You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 11 Next »

Rationale

Magnolia 5.0 went slightly backwards - we lost some translations. The mechanisms are still in place, but we "forgot" to implement them; many dialogs are configured with "hardcoded" labels. Some code, too.

More importantly, the way we apply i18n through the UI is currently inconsistent or inexistent. Most new constructs (apps, action bars, ...) have no notion of i18n. Blindly applying the same slightly dated concept we've been using up to 4.5 would blow the configuration out of proportion. Below are a few suggestions to explore and make this simpler, smaller, or more consistent.

To explore

  • Generate key from app-dialog-field(.desc)
  • Generate i18nbasename?
  • (Updtask remove if match)
  • (Check contents)
  • Migrate existing translations
  • Chain bundles for simple defaults (chain with generated bundle names, sort of package fallback)
  • Process for contributing and integrating contributed translations

Existing uses

info.magnolia.ui.form.AbstractFormItem#getMessages - should not be public

info.magnolia.ui.form.AbstractFormItem#getMessage - should not be public

Suggestions

Differentiate between in-code-translations, vs in-config-translations. Translations in-configuration will require very little code change, but will require some work on update tasks and bootstrap files. IntelliJ (Eclipse too, probably) provides analysis tools to find out where i18n strings have been hardcoded. Attached a sample report executed on a couple of modules: i18-analysis.zip (main, ui, activation, cache, imaging, workflow). Note that the html-exported report is not very useable, but the in-app report is much more practical: Screen Shot 2013-07-31 at 14.21.14.png

In-code translations

I'm not sure we have a lot of those at the moment, but for things that don't need to be configured, we could. We could benefit from a tool like Localizer or GWT's i18n generator tool. These tools generate code (interfaces and impl) based on the keys found in a message bundle file. They are well thought out, in that they provide the correct methods depending on parameters found in the keys, for example. (generate a String getFileCount(int count) based on a file.count=There are {0} files message, for example). This is great for code completion and type-safety. However, their generated code isn't ioc-friendly, and tends to be laced with static dependencies. These tools also don't handle changes to the generated code well. Which means that if you need to add a message - or change its name, or change its "signature" - you need to edit the properties file, rather than the code.

If we have a lot of these, we could think about rolling out our own tool - or write code that behaves similarly.

Convention over configuration

We want to introduce a naming convention, both for basenames (i.e the location of the message files) and the key themselves.

To avoid a lot of redundant and verbose configuration, we could take this one step further and use the conventional name - if none is configured - to lookup a particular text. 

With some clever fallback mechanisms, this could make translations easier and simpler.

Suggestion for a fallback chain: given a bundle, the following keys are "tried". The first value to be found is used:

Field labels: – optional fallback to a key with a .label suffix to make things less verbose

<dialog-name>.<tab-name>.<field-name>.label
<dialog-name>.<tab-name>.<field-name>
<dialog-name>.<field-name>.label
<dialog-name>.<field-name>

Field descriptions - here we can't fallback to a key without the .desc suffix

<dialog-name>.<tab-name>.<field-name>.desc
<dialog-name>.<field-name>.desc

Tab labels:

<dialog-name>.<tab-name>.label (or .tablabel for explicitness?)

Action labels:

(warning) 99% of our dialogs have the same save/cancel actions. Those should be defaults. Labels should still be overridable on a dialog-per-dialog basis.

<dialog-name>.actions.<action-name>.label
<dialog-name>.actions.<action-name>

(lightbulb) I introduced the .actions portion here to avoid confusion with fields; consistency would dictate having a .fields or .tabs portion for field and tabs labels too, but that would downplay the conciseness.

Other elements tbd:

  • actions in dialog
  • actionbar in subapps
  • workbench/<view>/columns in content apps: column names
  • in workbench/view/columns, we also have formatterClass which should be locale-sensitive
  • app (label in app launcher, tab)
  • page templates
  • page components

(lightbulb) If fields or other elements need to be empty, like today, the label has to be configured as "empty", or the corresponding key.

(lightbulb) The "generated" key should reflect the real "location" of an element, not where it's inherited from. e.g the "save" action label of dialog foobar should first be looked up at <dialog-name>.actions.save before actions.save.

(lightbulb) If performance becomes a problem (with all the fallbacks and the chained message bundles), we could introduce a simple caching mechanism.
As a counter example, here's what we've been doing so far, taken from STK: dialogs.pages.faq.stkFAQHeader.tabMain.title.label
  • TBD - 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 ?)

MessagesManager

info.magnolia.cms.i18n.MessagesManager is the component that has been used to handled i18n up until Magnolia 4.5. It has its flaws, and should be rewritten.

i18nBasename

Up to Magnolia 4.5, the i18nBasename property was defined in a dialog definition (or further down). With 5.0, this property is moved one level down (into the form subnode).

However, actions definitions also need to be i18n'd. For apps, it would make sense to have i18nBasename defined at the app level (AppDescriptor). Dialogs however are not necessarily tied to an app (question) i.e they're used by the page-editor, so we wouldn't want to use the page-editor's i18nBasename.

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.

getI18nBasename is defined in too many places. It's inconsistent and unintuitive. Why the redundancy between info.magnolia.ui.dialog.Dialog#getI18nBasename and info.magnolia.ui.dialog.definition.DialogDefinition#getI18nBasename for example ?

getLabel() vs getI18nBasename() vs usages (question)

Each translatable item currently has a getLabel (or getWhatever) method, some have several (getDescription). They also have a getI18nBasename() method. This combination makes it unclear whose responsibility it is to actually translate (i.e call MessagesManager) the piece of text. Should it the the FieldDefinition itself ? The FieldFactory ? The FormBuilder ?

Would it help if we actually removed that stuff from the APIs and use an annotation to indicate which configured Strings need to be translated ?

In-config translations

A complete code review is required to identify all places with a direct String output (such as button labels, column headers, etc.). Such places have to be replaced with a proper i18n mechanism (MessagesManager.getWithDefault(key, defaultMsg), where the original String will be used as defaultMsg).

A complete code review is not necessary. A search for usages of the following (to be completed) should cover 95% of our bases.

  • info.magnolia.ui.form.field.definition.FieldDefinition#getLabel
  • info.magnolia.ui.form.field.definition.FieldDefinition#getDescription
  • info.magnolia.ui.form.definition.TabDefinition#getLabel
  • ...

Bootstrap files update

After the code review, the configuration (bootstrap) files have to be reviewed, and values of properties label and description (and alternatively others) will be replaced with a proper message key. Such message key (and value) will be added to the messages_en.properties file of the admincentral UI module (or the other module's messages file), and the path of the property + the key will be added to the list of changed properties (one list for each module, a properties file named i18n_properties_to_update.properties on classpath), which will be then used in the VersionHandlerto replace the values on the upgrade (using new UpdateI18nPropertiesTask).

Based on existing (or then existing) translation files, we should be able to write a script that does a search/replace into bootstrap files. And/or a (temporary) update task could help regenerating them.

Migration

Based on existing (or then existing) translation files, it should be easy to update content. (find i18nBasename, find values for "label" and "description" properties, reverse-lookup, replace by corresponding key)

  • No labels