mistralys/application-localization

PHP and Javascript localization library.

1.4.1 2021-08-10 11:40 UTC

This package is auto-updated.

Last update: 2022-06-10 18:09:38 UTC


README

Build Status Scrutinizer Code Quality

Application Localization

PHP and Javascript localization library, written in PHP. It is a simple but powerful localization layer that stores translated strings in ini files.

Highlights

  • Easy to adjust to an application's files structure
  • Built-in translator UI that can be integrated into an existing UI
  • Clientside translations with auto-generated include files
  • Translations with or without translation context information for translators

Installation

Simply require the package via composer:

composer require mistralys/application-localization

Also see the Packagist page.

Configuration

1) Adding locales

The native application locale should be english. Any additional locales can be added like this:

use AppLocalize\Localization;

Localization::addAppLocale('de_DE');
Localization::addAppLocale('fr_FR');

Note: The package has not been tested with non-latin scripts, like Sinic (Chinese, Japanese, Korean, Vietnamese) or Brahmic (Indian, Tibetan, Thai, Lao).

2) Adding file source folders

This defines in which folders to search for PHP or Javascript files. These files will be analyzed to find all places where there are translation function calls.

Register each folder to look in like this:

use AppLocalize\Localization;

$source = Localization::addSourceFolder(
    'source-slug', // Must be unique: used in file names and to access the source programmatically
    'Source label', // Human readable label
    'Group label', // Group labels are used to group sources in the UI
    '/path/to/ini/files/', // The localization files will be stored here
    '/path/to/source/files/' // The PHP and JavaScript source files to search through are here
);

Excluding files and folders

For performance reasons, it is recommended to exclude any files or folders that do not need to be analyzed. The Javascript analysis in particular still has issues with minified library files like jQuery or Bootstrap, so they should definitely be excluded.

To exclude folders or files by name:

$source->excludeFolder('foldername');
$source->excludeFile('jquery'); // any file with "jquery" in its name
$source->excludeFile('jquery-ui.min.js'); // by exact file name match

Note: No need to specify the absolute path or file name, as long as the name is unique.

3) Main configuration settings

Note: This must be done last, after the locales and sources have been defined.

use AppLocalize\Localization;

Localization::configure(
    '/path/to/analysis/cache.json', // Where the text information cache may be saved
    '/path/to/javascript/includes/' // Where the clientside files should be stored (Optional)
);

If no path is specified for the clientside includes, they will be disabled.

4) Select the target locale

The locale is english by default, and you can switch the locale anytime using this:

use AppLocalize\Localization;

Localization::selectAppLocale('de_DE');

Note: Your application logic must handle the decision of which locale to use.

5) Include the client libraries (optional)

The localization library automatically creates the necessary javascript include files in the folder you specified in step 3). In your application, include the following files to enable the translation functions:

  • locale-xx.js
  • md5.min.js
  • translator.js

Where xx is the two-letter ISO code of the target language. There will be one for each of the locales you added.

Once the javascript include files have been written, they are only refreshed whenever texts are updated in the localization editor UI. We recommend using a cache key (see below).

Using a cache key to update libraries

The libraries cache key is an arbitrary string that can be set. Whenever this changes, the javascript include files are refreshed automatically. A good way to keep them up to date is to use your application's version number as cache key.

$myAppVersion = 'v1.5.1';

Localization::setClientLibrariesCacheKey($myAppVersion);

Refreshing the libraries is then done automatically with each release of your application.

Using the translation functions

Serverside setup

To use the translation functions, you have to add use statements for those you need:

use function AppLocalize\t;
use function AppLocalize\pt;
use function AppLocalize\pts;
use function AppLocalize\tex;
use function AppLocalize\ptex;
use function AppLocalize\ptexs;

The t() function

Be it serverside or clientside, you may use the t() function to have texts automatically translated to the target locale. Simply wrap any of the native english texts in the function call.

PHP:

$text = t('Text to translate here');

JavaScript:

var text = t('Text to translate here');

The pt() and pts() functions

Note: This is only available serverside.

These are the same as t(), except that they echo the translated string. This is handy for templates for example:

<title><?php pt('Page title') ?></title>

The pts() function adds a space after the translated string, so that you do not have to manually add spaces when chaining several strings:

<div>
    <?php
        pts('First sentence here.');
        pts('Second sentence here.');
    ?>
</div>   

Using placeholders

The translation functions accept any number of additional parameters, which are injected into the translated string using the sprintf PHP function. This means you can use placeholders like this:

$amount = 50;
$text = t('We found %1$s entries.', $amount);

Clientside, you may use the same syntax:

var amount = 50;
var text = t('We found %1$s entries.', amount);

When using placeholders, we recommend systematically using numbered placeholders like %1$s or %02d. Primarily because the order of placeholders often changes in translated texts, but also to avoid confusion for whoever does the translating.

Providing translation context information

In some cases, knowing in which context a text is used will be critical to correctly translate it. The context flavored translation functions offer a second parameter dedicated to adding this context information.

  • t() -> tex()
  • pt() -> ptex()
  • pts() -> ptexs()
use function AppLocalize\ptex;

// Context information comes directly after the text.
ptex(
    'Text to translate', 
    'Context explanation for translators.'
);

// Placeholder values come after the context information.
ptex(
    '%1$s records', 
    'The placeholder contains the amount of records.', 
    42
);

The context text must be a string, just like the text to translate. Linebreaks and string concatenation are allowed, but no variables or functions, since this extracted offline, instead of being evaluated at runtime.

Hint: It is possible to use basic HTML tags for formatting.

Tips & best practices

Split sentences

The number of translatable texts in a typical application can grow very quickly. Whenever possible, try to split the texts into manageable chunks by splitting longer texts into smaller sentences. This has the added benefit of being able to reuse some of these text chunks in other places.

Use numbered placeholders

Even if the syntax is more cumbersome than a simple %s, using numbered placeholders is critical to allow for different sentence structures depending on the language. A placeholder placed at the end of a sentence may have to be moved to the beginning of the text in another language. Using numbered placeholders makes this easy.

Note: placeholders are highlighted in the localization UI, so that complex texts stay readable.

Put HTML tags in placeholders

While tags like <strong> or <em> are harmless choices to include in texts to translate, it should still be avoided. HTML is in the layout domain, and on principle should not be handled by translators.

So ideally, a text with markup should look like this:

use function AppLocalize\t;

$text = t('This is %1$sbold%2$s text.', '<strong>', '</strong>');

This way, the application can decide not only which tag to use, but also add classes to it if needed in the future - without having to touch any of the translated texts.

Same procedure for text links for example:

use function AppLocalize\t;

$textWithLink = t(
    'Please refer to the %1$sdocumentation%2$s for further details.',
    '<a href="http://...">',
    '</a>'
);

Template texts

To use a translated text as a template to re-use multiple times, simply replace the placeholders with placeholders.

Sounds strange? Look at this example:

use function AppLocalize\t;

$template = t('Entry number %1$s', '%1$s');

Translated to german, the text in the variable $template would look like this:

Eintrag Nummer %1$s

This means you can now use the template multiple times without calling the translation function each time, with the sprintf PHP function:

for($i=1; $i <= 10; $i++)
{
    echo sprintf($template, $i);
}

Going further

The Application Utils package has a string builder class used for concatenating strings, and which supports this package out of the box. Building complex sentences is easy with this, including in an HTML context.

Example:

use function AppUtils\sb;

$html = (string)sb()
  ->bold(t('Easy string concatenation'))
  ->nl()
  ->t('For more information, see here:')
  ->link(t('Application Utils'), 'https://github.com/Mistralys/application-utils');

Events

When the active locale is changed

The LocaleChanged event is triggered when a different locale is selected at runtime. It is possible to add a listener to this event, and react to locale changes.

Here is an example:

use AppLocalize\Localization;
use AppLocalize\Localization_Event_LocaleChanged;

Localization::addEventListener(Localization::EVENT_LOCALE_CHANGED, 'listenerFunction');

function listenerFunction(Localization_Event_LocaleChanged $event) : void
{
    // do something
}

Examples

To run the example editor UI, simply run a composer update in the package folder, and open the example folder in your browser (provided the package is in your webserver's webroot).

Origins

There are other localization libraries out there, but this was historically integrated in several applications. It has been moved to a separate package to make maintaining it easier. It has no pretension of rivalry with any of the established i18n libraries.