Home > Developer Guide > Translation
Translation for Developers
Overview
- CiviCRM strings are written in English, and localized via PHP Gettext.
- Anyone can contribute translations by making a free account on transifex.org.
- The translations are packaged and distributed via the Update Language Extension.
The ts() Function
All code with user-facing text must pass it through the ts() function, which takes the following arguments:
- A string literal (with no variables, line breaks, or concatenation).
- An optional array containing variables to substitute and/or a pluralized version of the string.
The function is available in PHP, Javascript, Smarty templates and Javascript templates:
PHP
-
For extensions, the ts function is provided by the civix-generated extensionUtil class and typically imported as
E.```php use CRM_Myextension_ExtensionUtil as E;
echo E::ts('Hello World!'); ```
-
In CiviCRM core (non-extension) code, the import can be skipped and the global
ts()function used directly:php $options = [ 1 => ts('One'), 2 => ts('Two'), 3 => ts('Three'), ]; -
Since extensions are more common, the rest of these examples will be formatted with
E::ts()assuming theuseimport is at the top of the file. -
Example with variable substitution:
php $message = E::ts('A new %1 named "%2" has been created.', [ 1 => $itemType, 2 => $itemTitle, ]);Note that variables should themselves be translated by your code before passing in, if appropriate.
-
If the string needs to be pluralized, use the singular form as the main string, and provide the count (integer) and plural (string) in the 2nd argument along with any placeholder values:
php $string = E::ts('1 item was created by %1', [ 'count' => $total, 'plural' => '%count items were created by %1', 1 => $userName, ]);
Javascript
- The ts function is imported into a closure via the
CRM.ts()function, which takes scope (extension name) and returns a function that always applies that scope to ts calls:
```js // This closure gets a local copy of jQuery, Lo-dash, and ts (function($, _, ts) {
let message = ts('Hello World!');
})(CRM.$, CRM._, CRM.ts('foo.bar.myextension')); ```
- Variable substitutions and pluralization works the same as in php
js
let message = ts('1 item has been updated by %1.', {
plural: '%count items have been updated by %1.',
count: getCount(),
1: getUserName(),
});
!!! note
CRM.ts is not the same as the global ts function. CRM.ts is a function that returns a function (javascript is wacky like that). Since your closure gives the local ts the same name as the global ts, it will be used instead.
!!! important
Your local variable must be named ts in order for the static code parser to extract translatable strings.
Javascript/HTML Templates
Any file ending in .html is assumed to use a javascript-based template language (e.g. Angular) and follow the same rules as javascript:
- Angular Example
```html
{{ ts('1 Record', {plural: '%count Records', count: getCount()) }}
```
- Angular Optimization
The curly-brace syntax supports an optimized one-time binding if the value never changes.
For static strings with no variables, add :: after the opening braces for better performance:
```html
{{:: ts('Simple Title') }}
```
Smarty Templates
- The strings hard-coded into templates should be wrapped in
{ts}...{/ts}tags. For example:
```smarty
{ts escape='html'}Hello World!{/ts}
```
- Variable substitution and pluralization follows the same rules:
```smarty
{ts escape='html' plural='%count items saved by %1' count=$count 1=$userName}1 item saved by %1{/ts}
```
- Escaping rules are especially important in html attributes where the translation might contain a quote mark that would break the markup.
smarty
<a title="{ts escape='html'}Open "Date Picker"{/ts}">
{ts escape='html'}Select Date{/ts}
</a>
- Inline scripts use the 'js' escaping rule:
```smarty
{literal}