Quick links: Content - sections - sub sections
EN FR

Jelix has its own localization/internationalization mechanism. The setLocale and gettext functions from PHP are not used because of too much constraining to setup, and their configuration depends too much on how servers are configured.

Principles

Each text or string that you want to translate is associated to a key. These associations are placed in "property files". Each property file is associated to a language and a character encoding.

The following code retrieves the current language code into the lang variable.


$lang = jApp::config()->locale;

To be able to retrieve a string in the current language, simply call jLocale::get('the.key.of.the.string'), or in templates, use the syntax dedicated to locales (see the template page).

Configuration

In the configuration file of your application (mainconfig.ini.php), you have several parameters to configure the language and charset used in your application:

  • locale: indicates the language code of the lang to use in the application. It can be changed on the fly if needed. Values follow a specific syntax, similar to IETF language tags: a primary tag + "_" + a subtag. The primary tag is a lang code, and the subtag is the country code. By default, the locale is en_US.
  • charset: indicates the charset to use. By default it is UTF-8.
  • availableLocales: contains the list of language that your application supports.
  • fallbackLocale : indicates the locale to use if jLocale doesn't find the key in the current locale or similar locale.
  • There is also a section [langToLocale]. See below.

Property files

Property files are files containing translations. They follow strict conventions. They are located in the locales/ directory of modules. This directory has a specific organization. It has a sub-directories for each language. Example: locales/fr_FR/ for France's french, locales/en_US/ for United States english, and so on. In each language directory there are many property files, corresponding to the same language. However you can have different files containing same strings, but for different character encoding.

You can also store all properties files of all modules in a same main directory outside modules. It's then easy for people who translate to find files. The directory <yourapp>/app/locales/ should content one folder for each lang, and in these folders, one folder for each module, following this scheme: <yourapp>/app/locales/<lang>/<modules>/locales/<file.properties>. For example: myapp/app/locales/fr_FR/my_module/locales/example.properties.

You can then copy easily all files of <yourapp>/app/locales/<lang>/ in a new lang directory to translate to a new language.

There is also the possibility to redefine properties file by copying them into the <yourapp>/app/overloads/ directory. See the corresponding chapter.

File name

The name of the property files has the following structure: NAME.CHARSET.properties. NAME is a simple name which will be used in the locale selectors, and CHARSET corresponds to a character encoding. NAME can only use alphanumeric characters. Example with generic as NAME: generic.ISO-8859-1.properties, generic.UTF-8.properties, etc.

With the default configuration (locale=en_US and charset=UTF-8), locales/en_US/*.UTF-8.properties files will be used.

File content

The file content structure is quite simple. It's basically a key=translated string structure, with some improvements. There isn't real coding guidelines in the file, but you might organizing your locales using a 'generic.specific' scheme for the key names. You can't use double and single quotes to delimit your strings, new lines do this.

Keys can contain characters "a" to "z" (lowwercase/uppercase), and characters "_", "-", ".".

Here is an example for our english locale (en_US/generic.UTF-8.properties) file:


title.offlineElements = elements to check
title.onlineElements = online elements
buttons.save = Save
buttons.ok=Ok
buttons.cancel=Cancel
buttons.search=Search

And the equivalent in the french locale (fr_FR/generic.UTF-8.properties):


title.offlineElements = éléments à traiter
title.onlineElements = éléments en ligne
buttons.save = Enregistrer
buttons.ok=Valider
buttons.cancel=Annuler
buttons.search=Rechercher
Multi line

If the text is long and you want to write it in several lines, you can type an anti-slash (\) at the end of each line (excepted the last one of the text), to tell the parser to continue reading the translated string.


intro=this is a very very\
long text in\
several lines
message=this is a regular line

However, it doesn't insert a line break in the displayed string, but it inserts a space. If you want to insert a real line break, use \n or \r (\r\n on windows, \n on linux, \r on macs):


intro=this is a very very \
long text in\nseveral lines, but in\n one line\nin the source
Comments

You can also put some comments. They have to begin with a #. When the parser sees and #, the rest of the line is ignored. A comment can be at the beginning of a line, or in the middle of a line, or at the end of the line. If you want to use a # in a value, you have to escape it with an anti-slash (\#).

Whitespaces

Whitespaces before and after a value are ignored. If you want to put a value equal to a space, you have to use \w


nospace= #this is using a regular space
space= \w#this is using a \w space

The value of space will be ' ', and the value of nospace, an empty string.

HTML entities

Localized strings shouldn't contain HTML entities. First because a localized string could be used for other content than HTML, and second because jelix escapes html characters for you when necessary (i.e. the title of a page). So if the given string contains entities, this entities will be escaped and the browser won't identify them. To summarize, &copy; will become &amp;copy;.

If you want to use specific characters (~ or © etc), choose the corresponding charset and directly use these characters. They will be escaped for you. This is why it is a good idea to use the UTF-8 character encoding in your application, since it includes every existing character in any language.

If you really want to put some html in a localized string, and to use it in a jtpl template with the {jlocale} tag, a possibility is to put a ".html" at the end of the locale name as follow:


my.nice.title.of.my.paragraph.html = <strong>My nice title of my paragraph</strong>

Then, the {jlocale} tag in a template won't escape the content of the locale.

Retrieving a localized string

To retrieve a string, you have to use the get static method of jLocale. This method accepts a selector as the first argument, which has the following structure: <module>~<name>.<key>. The "<module>~" part is optional if the requested property file is in the current module.

For example, to retrieve the value of "buttons.save" in generic.UTF-8.properties in the "website" module:


  $string = jLocale:get("website~generic.buttons.save");

In a template, use this notation:


  <input type="button" value="{@website~generic.buttons.save@}" />

Localized string with parameters

It could be useful to have localized string with dynamic parameters. For example, you want to write this string:


   You will go on the http://www.jelix.org website and you will click on the 'documentation' section.

But you would like to use the same sentence with different websites or sections: you want to dynamically indicate the url of the web site and the name of the section. This is the purpose of parameters in the jLocale component.


  $string = jLocale::get("website~generic.sentence", array('http://www.jelix.org', 'documentation'));

And in the property file, you have to put a %s where you want to insert these dynamic values:


   sentence = You will go on the %s web site and you will click on the '%s' category.

Since the translated string is parsed by the sprintf php function, you should follow its syntax. This means that you use %d for a number, %s for a string, and so on. The order of the parameters is extremely important.


    $string = jLocale::get("website~generic.sentence", array('http://www.jelix.org', 'documentation'));
    //will give you You will go on the http://www.jelix.org website and you will click on the 'documentation' section.

    $string = jLocale::get("website~generic.sentence", array('documentation', 'http://www.jelix.org'));
    //will give you You will go on the documentation website and you will click on the 'http://www.jelix.org' section.

It may happen in some language that the translation causes a change in the order of parameters. But since the code is language generic, you won't pass the parameters in a different order. So you will use the syntax %x$s where x is the order id. For example, you could have this sentence in english:


   sentence = You will go on the %s web site and you will click on the '%s' section.

But in another language, the translator would like to change the order of the parameters: the first parameter in the sentence will be the section name, not the website url. So for example, in French, it could be translated like this:


   sentence = Cliquez sur la rubrique %2$s lorsque vous irez sur le site %1$s.

The first parameter will replace %1$s, the second parameter will replace %2$s, and so on.

Note that in template, you can't use the localeSelector syntax to indicate parameters. The only option left is to use the jLocale plugin:


   <p>{jlocale "website~generic.sentence", array('http://www.jelix.org', 'documentation')}</p>

Changing the language on the fly

Jelix provides a coordinator plugin supporting dynamic switching of languages: autolocale, located in lib/jelix/plugins/coord/. (see the documentation about coordinator plugins).

The autolocale plugin scans urls looking for a specific parameter indicating a language. If found, it switches your application and your user to this language by storing it in session. The locale configuration option is re-written by autolocale after being loaded from the configuration, so the configuration file remains untouched.

Even though the autolocale modifies the locale setting, you still get the current locale as usual:


$lang = jApp::config()->locale;

To use autolocale plugin, add the given section into app/system/mainconfig.ini.php or into the configuration file of one of entry points:


[coordplugin_autolocale]
enableUrlDetection = on
urlParamNameLanguage = lang

enableUrlDetection activate url-scanning. urlParamNameLanguage is the name of the url parameter defining a language code. You can then add a parameter on your urls to switch to other languages:


  <a href="?lang=fr_FR">français</a>
  <a href="?lang=en_US">english</a>

You can also give values without the country part of the code:


  <a href="?lang=fr">français</a>
  <a href="?lang=en">english</a>

Of course, you can rewrite your urls specifically for each language. You can have also automatic parameters for language in urls. See the corresponding documentation. In this case, and if all URLs have a "lang" or "locale" part, probably you don't need the autolocale plugin.

Note also that if the URL parameter does not contain a language code that is not listed into the availableLocales parameter of the main configuration, jLocale tries first to use the available locale that is near the given locale, and if it doesn't find one, jLocale will use the default locale of the application, indicated into locale, or at last try, the one indicated into fallbackLocale.

The last step is to activate the plugin. In Jelix configuration file, edit [coordplugins] section:


[coordplugins]
autolocale = 1

Auto detection of the language

The autolocale plugin also allows to detect automatically the language of the user, by checking the HTTP headers sent by the browser of the user when he visits your website for the first time.

If you want to enable this auto detection, you have to set the useDefaultLanguageBrowser parameter to on in the configuration of autolocale. It calls the method jLocale::getPreferedLocaleFromRequest(), that you can use of course in your own classes (controllers for example).

Localizing templates

There are situations where it would be better to localize an entire template than to create lots of locale strings.

To achieve this, just move your template in a sub-folder of its templates directory. As for locale files, there must be one sub-folder for each language (Example: templates/en_US/, templates/fr_FR/).

Supporting unknown or non default language code

With a lang code (like "en", "fr" etc..), Jelix can give the corresponding default language code ("en_US" for "en", "fr_FR" for "fr" etc). However it could not be the language code we want. For "en", you would want "en_GB" for example. Or you would want to support a lang that Jelix doesn't know (all known lang code are in the file Flib/jelix/core/lang_to_locale.ini.php@@).

To fix this problem, you have to fill the section [langToLocale] into the main configuration. The parameter key is the lang code, and the value is the language code.


[langToLocale]
en = en_GB
; or to have compatibility with old modules:
en = en_EN

If a key doesn't exists

Qhen jLocale doesn't find the key in a properties file corresponding to the wanted language, it will try to find it in a similar locale.

If it fails, it will try with the locale indicated into fallbackLocale.

It it still fails, jLocale::get() generates an exception. So you should always have all locales at least in one language, and indicate this language into fallbackLocale.

Installing translations packages

Jelix and some modules have been translated into several languages, via the web site Transifex. Some translations are not fully complete, and perhaps you won't find translations for your prefered language. You are then strongly encouraged to translate on Transifex.

The resulting properties files are available on https://download.jelix.org/jelix/langpacks/.

Download any package, unzip them into the directory <yourapp>/app/locales/ (probably you need to create it). Empty the temp directory, configure the application to support the new language (see above) and you could use Jelix modules with these translations.