quarkus-renarde
quarkus-renarde copied to clipboard
Add or test internationalisation
We should try to internationalise the sample TODO app and see if we have all the required bits from Qute/Quarkus in order to be able to set the language and get translations in the views.
https://www.playframework.com/documentation/1.4.x/guide12 can be used for inspiration, and for http://rivieradev.fr (code at https://github.com/FroMage/RivieraDEV/blob/master/app/controllers/Application.java#L55) we had these controller actions to change the language from the top menu:
public static void fr(String url) {
Lang.change("fr");
redirect(url);
}
public static void en(String url) {
Lang.change("en");
redirect(url);
}
So a way to change the current language from a Controller
would be great. I'm pretty sure this sets a language cookie that overrides the browser language headers when set.
Is a custom internationalization format preferred instead of using an existing one as GNU gettext format or XLIFF because of better integration in the Java or Quarkus world?
Qute already has https://quarkus.io/guides/qute-reference#type-safe-message-bundles so we have to check if that is what we want or make it simpler if it has to.
Ideas for improving type-safe messages/templates, and grouping them together.
Using records:
interface View {}
public class Application extends Controller {
// This defines a template
record Index(String name) implements View {
// This defines localisation with key `Application_Index_my_greeting`,
// available in the view as {i18n:my_greeting(name)} and outside as {i18n:Application_Index_my_greeting(name)}
void my_greeting(String name) {}
}
public View index() {
return new Index("Stef");
}
}
Extending the current type-safe messages:
public static class Application extends Controller {
@CheckedTemplate
public static class Templates {
public static native TemplateInstance index(String name);
@MessageBundle
interface index {
@Message("fr", "bla bla {name}")
@Message("en", "yada yada {name}")
String my_greeting(String name);
}
}
public TemplateInstance index() {
return Templates.index("Stef");
}
}
The current solution:
@MessageBundle
interface Messages {
@Message
String views_application_index_my_greeting(String name);
}
public class Application extends Controller {
@CheckedTemplate
public static class Templates {
public static native TemplateInstance index(String name);
}
public TemplateInstance index() {
return Templates.index("Stef");
}
}
A more nested version:
public class Application extends Controller {
@CheckedTemplate
public static class Templates {
public static native TemplateInstance index(String name);
@MessageBundle
interface index {
@Message
String my_greeting(String name);
}
}
public TemplateInstance index() {
return Templates.index("Stef");
}
}
I need to look at the records idea. It's quite interesting.
@FroMage Would it help in the mean time if we change the default name of a bundle declared in a nested class? Currently, it's always msg
. We could say that for a nested class the simple name of the class is used together with _msg
. So that you could use something like index_msg:my_greeting('Stef')
in the template. Or even Application_index_msg:my_greeting('Stef')
if we want to include the names of declaraing classes. I think that the last proposal makes sense. WDYT?
The reason why ATM my application has all keys prefixed with views.Controller.method.
is that I define them all in a single messages.properties
file. But this makes them hard to use in views, because each view could have a default that makes the views.Controller.method.
optional (since we know the view path).
I understand you proposal, and it makes a lot of sense to make them available as Application_index:my_greeting
, but mostly for accessing them outside the Application/index.html
view, no? For that one, an implicit msg:my_greeting
should be enough, no?
Also, in terms of IDE completion, probably it's better to make the prefix msg_Application_Index
, no? Any reason why we can't make it msg.Application.Index:my_greeting
?
I understand you proposal, and it makes a lot of sense to make them available as Application_index:my_greeting, but mostly for accessing them outside the Application/index.html view, no? For that one, an implicit msg:my_greeting should be enough, no?
Hm, the problem is that qute does not have a notion of view and controllers. Only Renarde does. And message bundles are "global objects". We could make it work using TemplateInstance
attributes though. Something like TemplateInstance.setAttribute("messageBundleSuffix", "Application_Index")
(Renarde could do this) and then msg
would become msg_Application_Index
when resolving the message. I will do some experiments...
Also, in terms of IDE completion, probably it's better to make the prefix msg_Application_Index, no?
It could be, yes.
Any reason why we can't make it
msg.Application.Index:my_greeting
?
A qute namespace can only consist of alphanumeric characters and underscores.
Hm, the problem is that qute does not have a notion of view and controllers
Well, it does for nested @CheckedTemplate
classes ;) So it would be a regular fit to follow that convention too.
Hm, the problem is that qute does not have a notion of view and controllers
Well, it does for nested
@CheckedTemplate
classes ;) So it would be a regular fit to follow that convention too.
It's not a view/controller per se. It's a method that defines a type-safe template. That's all.
Call it what you want, as long as the convention is the same, it works the same ;)
We could make it work using TemplateInstance attributes though. Something like TemplateInstance.setAttribute("messageBundleSuffix", "Application_Index") (Renarde could do this) and then msg would become msg_Application_Index when resolving the message. I will do some experiments...
Hm, but similar tricks would break message validation... We need something more like "local namespaces" or "namespace aliases". So that msg
can be translated to msg_Application_Index
when validating templates..
I was thinking we could first look up the msg:
namespace and if it doesn't exist, lookup msg_Application_index:
.
I was thinking we could first look up the
msg:
namespace and if it doesn't exist, lookupmsg_Application_index:
.
Ok, but when you use {msg:hello(name)}
in your template then we validate at build time that there is a message in the bundle msg
for key hello
that accepts one parameter (and if name has a type then the type is validated as well). So it's not as easy...
@FroMage In fact, I think that you can just ignore the msg
namespace because we generate an implementation of the message bundle interface and so you can just add a msg
parameter your @CheckedTemplate
and obtain the implementation via @Inject MyBundle
or MessageBundles.get(MyBundle.class)
and use {msg.hello(name)}
in the template. Methods will be validated as usual.
E.g.
@CheckedTemplate
class Templates {
TemplateInstance index(MyBundle msg);
}
and then something like:
Templates.index(MessageBundles.get(MyBundle.class)).render()