Page History
...
- 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 astkFAQHeader.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 (TabDefinition
, FieldDefinition
, ...). 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).
- 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 asFieldDefinition
. 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 toi18nKeyGenerator
andTranslatorService
. I18nizer
also "injects" theI18nParentable
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,
- Does it correspond to a key currently existing in the translation file ?
- Go through each
- 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
- Does it correspond to a key currently existing in the translation file ?
- Track unused keys in translation files
- Track possible duplicates
- Go through each
- Re-export file to replace bootstrap file
- Re-export translation file(s)
...
Validate Concepts
Validate Roadmap
API
Finalize module name, location, and package name.
...
Replace MessagesManager with a cleaner impl
Migration and updates
Topics to validate or research
...
Migration and updates
Known bugs or problems:
Jira server Magnolia - Issue tracker key MAGNOLIA-5315 Jira server Magnolia - Issue tracker key MAGNOLIA-5317
Topics to validate or research
...
- A form can be used in different contexts. It should be translatable according to context.
- TBD: details, examples
- 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
...