I18n Best Practices

I18n is a well supported platform feature. Every native or custom field label is translatable, as well as custom tabs and many more textual content. A full list of all (out-of-the-box) supported translatable objects and cultures can be reviewed from here.

Administrators are even able to deploy translation packages by utilizing the metadata API or overriding supplied translations included in Salesforce.

Concluding: Salesforce provides really extensive support for every I18n and L10n related requirement.

But what if you plan to deep-dive into application development and face a reasonable amount of APEX classes, controllers and/or visualforce-components and -pages containing lot of textual contents which are to be translated as well?

This article tries to cover best-practices in translating every aspect of textual content that may occur in your custom codings.


Custom Labels „Custom Labels“ are small pieces of data holding a (unique) name, a (short) discription, some kind of taxonomy for better organizing and sorting, a source language (depending on your user´s current locale settings) and – or course – a source translation text.

In contrast to other salesforce (native/custom) objects, custom labels rely on an internal data structure that is translatable per-default, so if you create a new custom label, it is automatically configured with (default) source language and can be translated in any other language by utilizing the translation workbench.

Custom labels fulfill the need for translatable text contents which can be referenced in any custom code, may it be APEX code or visualforce pages or components.

You may create up to 5.000 custom labels per organization, including up to 1.000 characters per label.

Generally, in source codes custom labels are always referenced by their (unique) names, so the source translation text may absolutely be a human readable sentence (in contrast to other I18n concepts of other frameworks which rely on so called „source translation tokens“ which are both source content and address, e.g. „my_site.my_homepage.headine“, and are not connected to any language/locale at all, so in fact you are not translating human readable texts but computer readable „addresses“).


Creating custom labels

create custom label salesforce

Creating a new custom label is as easy as creating any other platform´s recordset. Simply go to your organization´s setup, then navigate to „App Setup“ => „Create“ => „Custom Labels“ => „New Custom Label“.

Fill in your new custom label´s attributes as shown above and hit the „Save“-button. Voila — your first custom label has been created and stored in your organization´s database.

After creating your custom label, you may directly navigate to the translation workbench („Administration Setup“ => „Translation Workbench“ => „Translate“), choose a target language and „Custom Label“ by using the comboboxes and start translating.

But — are you a pen-pusher/accredited interpreter or a bloody skilled APEX programmer? I suggest you pass the writing job to those guys that have fun with it and move on to the  parts that really interest you.


Using Custom Labels in your custom application

Referencing a custom label in APEX code is not quite difficult. Labels are provided by the „Label“ struct residing within the global „System“ namespace. Thus you can de-reference every label by typing

As a shortcut, you may omit the „System“ namespace (similar to browser-based javascript where each object is bound to the global „window“ namespace):

Relating to our concrete sample label created above, you would type:


Note that the (unique) name of the custom label reflects some namespace-equivalent address hints for better organization:

ns“ refers to a „global“ namespace prefix reflecting e.g. your company name. So for, the namespace could imaginably state „sf“.

myPage“ may refer to the name of a visualforce-page the label is related to.

headline“ refers to the positioning and the purpose of the label within „myPage“.

It is best practice to use expressive naming conventions including namespaces for your custom labels. (Though the sample is a little bit confusing, because „myPage“ merely refers to a visualforce-page than APEX-code, but it will fit our needs for now and for demo purposes only. A real world Custom Label would be e.g. named „sf_mypageController_unexpectedErrorMessage“ or something like that.)

So far — easy as that and no news for experienced salesforce developers.


Generating messages from within APEX controllers

As you will probably recall, there are several ways of passing messages from APEX action controllers to visualforce pages:

(Not regarding the possibility of utilizing custom getter methods to directly inject text contents into visualforce pages, but this is not exactly the purpose of getter methods. But sometimes, for instance if you have to dynamically build text ressources from different data sources, you will probably rely on this possibility. Nevertheless the scope of this document is internationalization, not content management.)

Sobject.addError() and Sobject.field.addError() will mainly used within APEX-triggers to display custom error messages on forms and form fields. Notable is that you have always call these methods on the „original references“ obtained by e.g. or trigger.newMap, not on post-loaded „fullData“-instances, even if they contain the same salesforce ID and thus reference the same recordset in the database.

If you use these methods within custom APEX controllers or controller extensions, you should keep in mind that each call to Sobject.addError() is directly added to the ApexPages.PageMessage array and not bound to any form (because there is no direct link/relationship from Sobject to any form you place on the page, in contrast to these CRUD default salesforce edit Sobject pages.)

Calls to Sobject.field.addError() can be displayed on a visualforce-page directly under a related <apex:inputField>-component, but only, if the references Sobject.field instance is the same instance that recieves the custom error message.


Making APEX messages I18n-ready

It is best practice to use I18n-ready custom labels everytime you generate a message text which purpose is to be displayed somewhere on a visualforce page (may it be within <apex:pageMessages>-component or on a <apex:inputField>-component). So instead of writing for instance

you should always create a Custom Label and use it like this:

But what if you want to inject dynamic content into your freshly internationalized error message? Imagine a use case in which you have to display an upper boundary that is not allowed to be exceeded by the editor? Like this one:

In legacy salesforce APEX/platform API versions (including Summer ’12), it was not possible to create translated strings and substitute nested dynamic variables in APEX-code. So until then, the only solution to provide I18n with placeholders in APEX-Code was to write custom code which performs some regex magick.

For instance, ths small piece of APEX code could be a solution for this problem:

Assuming this (static) method is placed in an APEX class called ‚I18n‘, you are now able to format your page message in a slightly modified way:

And finally using a custom label:

Did you notice the String.valueOf() static method call? This is needed because the I18n-method only supports String mapped to String arguments. You will probably devine a code smell — I18n-capability is provided, but L10n has went by the board at this moment — your currency field will not properly be displayed (no currency string like „EUR“, wrong number format, ommitting „0“ decimals, no leading „0“ and so on).

Luckily, with current API version > Winter ’13 (v27.0 i think), lots of additional methods had been introduced. A special String (static) method is String.format() which implements a subset of the functionality that the visualforce-component <apex:formatText> had already provided for years. Using String.format(), your code changes to:

while your custom label´s content has to be changed to:

Regard the name change of the variable placeholder, instead of a (human readable) name you now have to use index numbers (starting by 0 ) for each argument in the paramter list.

This can become slightly annoying, because if your translator has to work on larger texts, he will probably face problems to concieve the meaning of each single placeholder. But that evaluates to a full-fledged SEP-field and hopefully will not affect us.

Nevertheless a real, major disadvantage of String.format() that it´s second argument only accept Lists of type String, so no L10n-support here also. It is NOT able to format Date, Datetime any number, decimal/currency objects. This stands in contrast to the <apex:outputText> visualforce-component, which indeed has basic L10n support (not speaking of the almighty <apex:outputField>-component.

Conclusion: There are several ways to maintain I18n-Strings within APEX classes, even variable placeholders may be utilized. It is up to you if you use the custom solution (advantage: human readable variable placeholder) names or the freshly introduced String.format() method.

Hopefully, salesforce decides to provide more features for String.format() like formatting capabilities for complex objects, like Decimal, Date, Datetime and Sobject.field in the near future. Relying on that, you will be right if you use String.format() today, this will probably save some migration efforts in the future.


I18n in visualforce pages

Luckily, I18n and L10n support in visualforce pages is a litte bit more mature. The main components for displaying I18ed and L10ed content are

Rarely used seems is <apex:pageMessages /> with which you can generate a ApexPages.PageMessage directly from a visualforce page (but why should man do so?).

Some basic rules apply when enriching visualforce pages with translations:

  • Use custom labels wherever you write custom text. Always wrap custom labels with <apex:outputText>
  • Never use a custom label for labeling an sObject´s input field (use field describe results and <apex:outputLabel> for that)
  • Use <apex:outputField> as much as possible for displaying Sobject field values.
  • Use <apex:outputText> only to display text which comes from a custom label.


Use custom labels wherever you write custom text

Custom labels are translatable. Never insert static text content into visualforce-pages (or -components), but use a Custom Label instead. When displaying the content of a Custom Label, use <apex:outputText> component as a wrapper. <apex:outputText> may take a list of parameters as <apex:param> child elements. These parameters will be used as replacements for Custom Label variable placeholders, similar to the String.format() method described above. Additionally, <apex:outputText> provides basiv L10n-Support. Take the following example:

This is the content of the custom label. Now see how this label can be referenced in any visualforce page:

Even better, in contrast to String.format(), the amount variable can be properly formatted:

Never use a custom label for labeling an Sobject´s field input control

Use <apex:outputLabel> in combination with the salesaforce Schema-„Reflection“ capabilities. Example:

As you will see, the label is properly displayed in a human readable way (and even translatable by utilizing the translation workbench!)

Use <apex:outputField> as much as possible for displaying Sobject field value

Don´t try to use custom formatting for sobject fields, especially currency, date and datetime fields. You will suffer serious L10n-mayham!

Instead of reinventing the wheel, use <apex:outputField>! This magic component knows exactly how to deal with Sobject.field instances and can format their content´s accordingly.

Use <apex:outputText> only to display text which comes from a custom label

Only use apex:outputText if you have static text content (which comes from a Custom Label created by you, of course) which cannot be replaced by any other text sources like Sobject names or Sobject field labels. Use exacly as much Custom Labels needed to enrich your user interfaces with information the user needs, not more, nor less. Note that each custom label has to be maintained and translated for each language in your organization! And it must be included in any deployment package that is maintained by your system administrators.

The following two tabs change content below.

Johannes Heinen

Johannes ist einer der langjährigen Senior Entwickler bei Aptly und hat neben weitreichenden Salesforce Kenntnissen auch einen Satz Kreativität, um Produkte je nach individuellen Anforderungen selbst zu kreieren.

Neueste Artikel von Johannes Heinen (alle ansehen)

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.