quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.35k stars 2.56k forks source link

Update messages in Qute Message Bundle at run-time #41154

Open derari opened 3 weeks ago

derari commented 3 weeks ago

Description

I have translation strings stored in a mutable datasource, such as a database. If an entry changes, I want the updated value to be used by Qute Message Bundles without rebuilding or restarting the application.

Extra challenge: languages may be added at run-time

Implementation ideas

quarkus-bot[bot] commented 3 weeks ago

/cc @mkouba (qute)

derari commented 3 weeks ago

I did a little experiment here: https://github.com/derari/quarkus-dynamic-messages

I can put {!!} somewhere in the default message to enfore qute, and add an empty properties file for each language I need.

Then I provide a message locator inspired by the database blog post --> https://github.com/derari/quarkus-dynamic-messages/blob/main/src/main/java/org/acme/DynamicTemplates.java

and it works almost as I want it to

GET http://localhost:8080/hello/fr/Bob
GET http://localhost:8080/hello/set/fr/hello_name/Bonjour {name}!
GET http://localhost:8080/hello/fr/Bob

Just changing the default template does not work yet

GET http://localhost:8080/hello/set/_/hello_name/Welcome {name}!
GET http://localhost:8080/hello/en/Bob
GET http://localhost:8080/hello/fr/Bob
mkouba commented 3 weeks ago

First of all, Qute message bundles were not intended to work like this. With dynamic messages, you lose the possibility to validate the message templates which is one of the key features. Having said that, the message templates are just ordinary templates stored in the Engine. In theory, you can replace the message template manually using the injected Engine and calling Engine#putTemplate(messageKey, engine.parse(newMessageTemplate)).

The message key is generated and consist of the bundle name, locale and key derived from the method. You can see all the generated keys - just @Inject MessageBundleRecorder.BundleContext ctx and then ctx.getMessageTemplates().keySet() (this is not part of the public API though so it can change any time ;-).

I can put {!!} somewhere in the default message to enfore qute, and add an empty properties file for each language I need.

Yes, that's because when there's no qute syntax used in the message we don't use templates at all.

Then I provide a message locator inspired by the database blog post --> https://github.com/derari/quarkus-dynamic-messages/blob/main/src/main/java/org/acme/DynamicTemplates.java and it works almost as I want it to

Yes, this is a very similar approach to the Engine#putTemplate() method mentioned above.

Just changing the default template does not work yet

What exactly do you mean?

derari commented 3 weeks ago

First of all, Qute message bundles were not intended to work like this.

Yes, it feels very hacky to use it like this. But it also seems like the least-invasive solution.

Just changing the default template does not work yet

What exactly do you mean?

I figured it out. When a localized bundle is missing a message, the default message is copied. So changing the template for the default bundle does not change it for the localized bundle.


So my requirement is run-time configurable translatable messages, and I dont want to build something entirely new when we already have this nice message bundle system.

If I am the only one with this requirement then I guess I can work with approach, but maybe it would be interesting for others as well?