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-admincecentraladmincentral, 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.

...

magnolia-i18n in magnolia_main would contain  contains the API and some implementationthe underlying. Key generators etc would live near their counterparts. (i.e in _ui mostly)

I would simply use The main package is info.magnolia.i18ni18nsystem as a package name. However, if this ends up only being usable within the context of magnolia_ui, I would move it there instead of _main, and reflect that in the module and package names.

API

  • @I18nable annotation. "Internationalizable": marks any object as a candidate for translations. Used on interfaces/classes such as FieldDefinition. Is inherited.
  • @I18nText annotation. Marks a String as to be translated.
  • I18nKeyGenerator<T> interface. Implementations generate translation keys for <T>.

Implementation "details"

  • A Guice module which intercepts the creation of objects annotated with @I18nable and proxies them
  • Said proxy intercepts method calls annotated with @I18nText and returns translated values

Update tools and tasks

To migrate our own modules, we can write a tool which:

  • TranslationService
  • I18nizer. Transforms/decorate a given object and internationalizes it.
  • I18nParentable. This is an interface that allows setting a "parent" on an object. This interface should not be used by client code.

Implementation "details"

  • Tried to implement a Guice module that would do this transparently; also tried to implement this at Node2Bean level. Neither worked - didn't find a way to "swap" instances in Guice (i.e replace bean created by n2b by a proxy)
  • I18nizer thus needs to be invoked explicitely. We have one implementation based on ProxyToys which creates a proxy. It intercepts methods that return other @I18nable objects (by returning one instance, a collection, or a map where value are of an annotated type) and decorate those. Those proxies in turn intercept method calls annotated with @I18nText and returns translated values by delegating to i18nKeyGenerator and TranslatorService.
  • I18nizer also "injects" the I18nParentable interface and sets the parent object while intercepting calls.

Update tools and tasks

To migrate our own modules, we can write a tool which:

  • Starts up a repo and a specific (or several) component managers
  • Load up a translation file we want to migrate (or all its language counter parts)
  • Imports a bootstrap file we want to migrate
  • This should instantiate a whole bunch of forms etc
  • Go through these one by one
    • Go through each @I18nText property of the object
      • Does it correspond to a key currently existing in the translation file ?
        • yes: replace key in translation file by deduced key,
  • Starts up a repo and a specific (or several) component managers
  • Load up a translation file we want to migrate (or all its language counter parts)
  • Imports a bootstrap file we want to migrate
  • This should instantiate a whole bunch of forms etc
  • Go through these one by one
    • Go through each @I18nText property of the object
      • Does it correspond to a key currently existing in the translation file ?
        • yes: replace key in translation file by deduced key, remove property from jcr
        • no: add key in translation file
      • If it's a key but it doesn't have an existing translation
        • add deduced key to translation file (instead of configured key), remove property from jcr
      • If it's not a key, i.e current configuration has an "hardcoded" text
        • warn, blow up, panic, ...?
        • add deduced key to translation file (instead of configured key), remove property from jcr
    • Track unused keys in translation files
    • Track possible duplicates
  • Re-export file to replace bootstrap file
  • Re-export translation file(s)

...

(tick) Validate Concepts

(tick) Validate Roadmap

(tick) API

(error)(tick) Finalize module name, location, and package name.

...

(error) Replace MessagesManager with a cleaner impl

(error) Migration and updates

Topics to validate or research

...

(error) Migration and updates

Known bugs or problems:

  • Jira
    serverMagnolia - Issue tracker
    keyMAGNOLIA-5315
  • Jira
    serverMagnolia - Issue tracker
    keyMAGNOLIA-5317

 

Topics to validate or research

...

  • A form can be used in different contexts. It should be translatable according to context.
    • TBD: details, examples
    Where do we pass the user context (i.e locale)
    • Can we set it at injection time ?
    • Do we add an interface for retrieving the locale-to-use ? (Which UiContext could implement for example)

Existing uses and inconsistencies to fix for 5.1

...