Extensions Translation¶
For developing a CiviCRM extension in a way that can be translated, all the best practices described in the Internationalisation for Developers page apply. This page describes special considerations that need to be taken into account for extensions.
See also: "Extension translation" wiki in the Translation project.
For translators: Translating strings on Transifex¶
There is a separate project on Transifex to translate extensions. Each extension has its own "resource". Therefore, when a translator joins a translation team, they can translate all extensions. We didn't see a need to separate each extension in a separate project, because each extension should have only one translation (.po
) file.
See: https://www.transifex.com/projects/p/civicrm_extensions/
Translation strings are sent to Transifex when the extension is "ready for automatic distribution". See: Publishing Extensions.
For administrators: Download translation files for extensions¶
The easiest way to download translations for extensions is to use the l10nupdate extension.
For developers: Correct usage of the E::ts()
function¶
In PHP, Smarty, and JS code, the convention is to perform translations using the E::ts()
helper function. This is the same as in core code — with the additional requirement that one must specify the "domain" so that the translation engine can use the correct dictionary (.mo
file) at run-time.
New in civix 17.08
E::ts()
was added to civix 17.08. The civix file may need to be regenerated. You can read more about it in the civix upgrade notes. Extensions may still use the old syntax using ts()
with the domain
argument.
PHP:
use CRM_Myextension_ExtensionUtil as E;
class CRM_Myextension_Form_Example {
function example() {
$string = E::ts('Hello, %1', array(
1 => $display_name,
));
}
}
Smarty templates:
{crmScope extensionKey='org.example.myextension'}
<p>{ts 1=$display_name}Hello, %1{/ts}</p>
{/crmScope}
Javascript:
(function ($, ts){
$('.foo').click(function greet(display_name) {
window.alert(ts('Hello, %1', {1: display_name}));
});
}(CRM.$, CRM.ts('org.example.myextension')));
Angular:
$scope.ts = CRM.ts('org.example.myextension');
Angular HTML templates:
<p>{{ts('Hello, %1', {1: display_name})}}</p>
For developers: Generate .po
and .mo
files for your extension¶
Sometimes you will want to generate the .po
and .mo
files for your extension during the development cycle, for testing (or for internal extensions, for production use).
Once your extension is stable you may want to consider publishing it on the Extensions Directory and getting it approved for in-app distribution you can then benefit from extension translation via Transifex where the Civi community can translate your extension and these translations can automatically be installed!
Here are the steps you'll need to follow:
Tools needed.
To generate the .po
and .mo
files you will need:
-
Download
civistrings
using the instructions in the readme. -
From the root directory of your extension do the following:
-
Create an
l10n
directory:mkdir l10n
-
Run CiviStrings
civistrings -o "l10n/<Extension Shortname>.pot" .
Note that thecivistrings
command format is:civistrings <OPTIONS> <PATH TO DIRECTORY CONTAINING EXTENSION>
-
Once you have your
pot
file copy this into the expected directory structure, for example to translate your extension into French copy thepot
file to<Extension Directory>/l10n/fr_FR/LC_MESSAGES/<Extension Shortname>.po
(Replacing the parts denoted by <> with the relevant information and noting carefully the filename change frompot
topo
.) -
Use your
po
editor to translate the strings in thepo
file you copied. -
If your editor has not generated the corresponding
mo
binary file you can do this manually using themsgfmt
command provided by GetText, as follows:
msgfmt <Extension Directory>/l10n/<Language>/LC_MESSAGES/<Extension Shortname>.po -o <Extension Directory>/l10n/<Language>/LC_MESSAGES/<Extension Shortname>.mo
Again, replacing the parts denoted by <> as appropriate.
Updating Strings
If you change your extension to add more E::ts
or similar calls you will need to go through the same process of generating the pot file then copying it to a po
file and editing that to then generate the mo file. Essentially start this process again!