Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • The dialogs prefix is not relevant and noisy. It was historically introduced to separate those labels from the page templates and page components names and description. Indeed, we're likely to have a stkFAQHeader.name somewhere. Currently leaning towards using separate message bundles. Or have an non-mandatory prefix/suffix (i.e there's a chance the component's title/name needs the same label as its first tab ?)
  • The pages.faq statement is arbitrary and is derived from the fact that the dialog in question happens to be configured under an arbitrary folder structure ( pages/faq/ )

Message bundles

i18nBasename is the property we use to look up a message bundle. This tells the system where the translation files are. It is called "basename" because i18n mechanisms typically append the locale to this and use the result as a path to a file (eg lookup <mybasename>_de_CH.properties, then <mybasename>_de.properties, then <mybasename>.properties - some even go as far as going up the path hierarchy (parent folders) of the basename until they find what they're looking for)

Up to Magnolia 4.5, the i18nBasename property was defined in a dialog definition (or further down). With 5.0, this exists in DialogDefinition, but also in one level below (in FormDefinition), and still exists in all elements below (TabDefinitionFieldDefinition, ...). The property is also present in ActionDefinition (members of DialogDefinition).

In 99% of the cases, the i18nBasename property set at dialog level should be enough. It is useful to keep the possibility to redefine it in actions, forms, tabs, and fields, but it should not be necessary. Defining i18nBasename at module level would be ideal - in terms of minimalizing redundancy anyway - but I'm not sure we'd have support for that right now. It'd be interesting to have i18nBasename in a module descriptor though. It would still be possible for individual components to override it if needed. We could also create a naming convention for i18nBasename as well (see below).

However, with the proposed key naming scheme (as well as with the existing informal one), message keys are distinct enough to consider dropping the need for specifying a message bundle. Proposal:
  • We don't need to specifiy i18nBasename anymore for translatable items. (but we can, at the very least to maintain backwards compatibility)
  • Every module will still have their own message bundle file(s); the system will chain and look for messages in all of these
    • We could imagine having a check that would warn, or even fail, when several bundles contains the same key(s).
    • However we still need to be able to override messages (for projects).
  • Global chains of message bundles - look into all known bundles
  • Basename helps grouping translation work - "I am now translating module X" - but that doesn't mean the basename has to be specified necessarily
  • Order of message bundles chain would need to be consistent and predictable

 

Message-bundles-naming-convention

Instead of having a huge fat bundle within ui-admincentral, every app should have its own bundle. The naming of an app-specific bundle should have the following pattern:

app-<app-name>-messages_<locale>.properties (e.g. app-security-messages_en.properties, app-contacts-messages_en.properties, etc. ).

It should be located directly under <module>/src/main/resources/mgnl-i18n/ (e.g. security-app/src/main/resources/mgnl-i18n/app-security-messages_en.properties)

 

Date formats and other localized items

We should make sure things like ColumnFormatter not only use the current user's locale, but also that this is indeed an "enabled" locale. A UI entirely in english but with a date formatted in french would be silly.

Exceptions

Exceptions are exceptions. They should not be translated. The message of an exception is targeted at developers and admins, and it is expected that they understand english. (if only to understand the meaning of the exception class name!)

If/when exceptions are currently reported to the user, we need to actually treat the exception. The below shows how not to do this.

Code Block
languagejava
titleIncorrect example
try {
   ... something that fails ...
} catch (SomeException e) {
   uiComponent.alert(e.getMessage());
} 

The below is what we should be aiming for (complete solution pending)

Code Block
languagejava
try {
   ... something that fails ...
} catch (SomeException e) {
   // if there is added value: log.error("sumfin happened: {}", e.getMessage(), e);
   // don't feel forced to bloat the logs, though.
 
   // if this is something the *user* should be informed about, and *can* do something about:
   uiComponent.alert(i18n.getMessage(e));
 
   // but really, is it ? Can a user do anything about a JCR Exception ?
   throw new RuntimeException(e); // did you need to catch it in the first place ?
} 

Of course, the question is - what IS i18n.getMessage(e) ?

This is TBD. An idea is to have an extra method to decorate exceptions, like we suggest for non-configured texts in 

Jira
serverMagnolia - Issue tracker
keyMAGNOLIA-5296
. A pattern we've used in the past (see login form) is to use the exception class simple name (IOException as opposed to java.io.IOException). I don't think we should try and use the message of the exception, because there is absolutely nothing that prevents anyone from injecting arbitrary text in there. However, some exceptions do have some sort of convention - a PathNotFoundException's message is usage the path that was indeed not found. If possible, I'd favor using explicit properties of the exception rather than the message (getPath() if it existed, in this case).

But: this would spread the use of I18nizer much further than it should (remember that one of the initial goals was to completely hide from the developers and have the decoration done by node2bean or Guice). Perhaps it could be "hidden" in uiComponent.handleException(e), and/or in a specific service. Need to think about the context, too (which would be lost if we use this last suggestion)

Implementation

Introducing a couple of concepts. The basic API will be in its own module in magnolia_main; it doesn't need to be in core, and will maybe not even depend on it. It could even be outside of magnolia_main if there is no dependency to core, but we currently lack a good location for such modules (wink)

...