15. Translations

WebApp provides a multilanguage interface. The language can be switched in settings. We use GNU gettext for translating. In your JS code just rememer to translate each string label using _(your string label):

Example:

/**
 * Create all buttons which should be added by default the the `Actions` {@link Ext.ButtonGroup ButtonGroup}.
 * This will create {@link Ext.ButtonGroup ButtonGroup} element with Spreed setup button.
 *
 * @return {Array} The {@link Ext.ButtonGroup ButtonGroup} elements which should be added
 * in the Actions section of the {@link Ext.Toolbar Toolbar}.
 * @private
 */
createActionButtons : function()
{
        return [{
                xtype : 'button',
                text : _('Setup Meeting'), // button text translation
                tooltip : {
                        title : _('Setup Meeting'), //tooltip title translation
                        text : _('Setup meeting with provided details')  //tooltip text translation
                },
                iconCls : 'icon_spreed_setup',
                handler : this.setUpMeeting,
                scope : this
        }];
},

15.1. Gettext

Information about the GNU gettext project can be found at http://www.gnu.org/software/gettext/

GNU gettext allows us to supply the entire WebApp in the language of the user’s choosing. It also allows the developer to implement plural translations and context-based translations.

With plural translations you can have gettext handle whether you should use a singular or plural translation.

PHP:

<?php echo sprintf(ngettext('Deleted %s file','Deleted %s files', $count), $count); ?>

JavaScript:

ngettext('Deleted %s file','Deleted %s files', count).sprintf(count);

Context-based translations allows you to translate the same word in English, but different in other languages. You add a context in which you specify where you use the translatable text and gettext can distinquish the different strings.

PHP:

<?php
        echo pgettext('Menu', 'Open');
        echo pgettext('Dialog.toolbar', 'Open');
?>

JavaScript:

pgettext('Menu', 'Open');
pgettext('Dialog.toolbar', 'Open');

For developers it is also possible to add commentary for the translators. You can do this by adding the following comment to the code just above the gettext function.

PHP:

<?php
        /* TRANSLATORS: Comment
        * Extra comment
        */
        _('English text');

        // TRANSLATORS: Comment
        // Extra comment
        _('English text');
?>

JavaScript:

/* # TRANSLATORS: Comment
* # Extra comment */
_('English text');

// # TRANSLATORS: Comment
// # Extra comment
_('English text');

For the extraction of the translations from the JavaScript files gettext offers the utility xgettext. However, this utility cannot read a JavaScript file. To make it work we set the language to Python (yes, that is not a joke). The downside of this is that comments directed at the translators need to be formatted a certain way. Python does not understand the JavaScript comments so we need to add a # on every line preceeding the gettext function. Also when you are using the multiline comment (/* */) the last line must contain the # character.

15.2. Server-Side

On the server-side the PHP has by default some gettext functions implemented already. It still misses a few though. That is why the of the WebApp server-side implements the following functions: pgettext, npgettext, dpgettext and dcpgettext.

More information about the default PHP functions can be found at http://php.net/gettext/.

15.2.1. Implemented Functions on the Server-Side

PHP misses the context gettext functions and that is what the WebApp implements. The $msgctxt argument supplies the context of the translation. The $msgid is the translatable string. The different functions implement the context together with the normal gettext function, plural, domain and category gettext functions.

<?php
        pgettext($msgctxt, $msgid);
        npgettext($msgctxt, $msgid, $msgid_plural, $num);
        dpgettext($domain, $msgctxt, $msgid);
        dcpgettext($domain, $msgctxt, $msgid, $category);
?>

15.3. Client-Side

15.3.1. Reading Translations and Passing it to the Client

The language class is the server-side PHP class that reads the binary .mo file in the language folder of the WebApp and returns a list of those translations. The client requests the page index.php?load=translations.js which includes the file client/translations.js.php. In this file the Translations class is defined and the translations are added inside this global Javascript object.

The plural-forms are defined inside the “first defintion”. This first defition is an empty string with the value containing information like the following.

msgid ""
msgstr ""
"Project-Id-Version: Weblang\n"
"POT-Creation-Date: 2015-07-16 16:16:06+0100\n"
"PO-Revision-Date: 2015-07-16 16:16:06+0100\n"
"Last-Translator: Weblang automatic translator <foo@bar.foobar>\n"
"Language-Team: Kopano Team <development@kopano.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;"

As you can see the last line defines the plural forms. Based on this string gettext will understand what singular or plural form it needs to use. In English there are only two, but in other languages you might have a different one when dealing with zero. Some other languages make it even more complex with different cases for numbers ending with one, except for eleven.

15.3.2. Implemented Functions on the Client-Side

The client-side implements the same function we have on the server-side. Except for the functions that implement the gettext category (the c-functions). The msgid argument is the translatable string. The msgctxt is the context argument.

_(key, domain)
dgettext(domain, msgid)
ngettext(msgid, msgid_plural, count)
dngettext(domain, msgid, msgid_plural, count)
pgettext(msgctxt, msgid)
dpgettext(domain, msgctxt, msgid)
npgettext(msgctxt, msgid, msgid_plural, count)
dnpgettext(domain, msgctxt, msgid, msgid_plural, count)

The Translations object will use the plural forms string that was extracted from the binary .mo file and passed on to the client. It is put into a function to be called when dealing with a plural translation.