i18n for developers

LeoColomb edited this page Mar 2, 2014 · 7 revisions

This page explains how to make your code ready for translators to create translation files in their language. If you need to learn the basics on translating, also check YOURLS in your language.

i18n?!

i18n is a abbreviation for internationalization, or the process of making code ready for translation. It is called "i18n" because there are 18 letters between the i and the n. In our YOURLS case, it means marking strings which should be translated in a special way.

Hardcoded vs Translatable

The regular way of echoing or returning strings, in PHP, would be for instance:

echo "This is a message.";
return "another string";

In this example, the strings are hardcoded so you can't change them unless you edit the PHP file. Not very practical.

YOURLS uses a set of functions to indicate that strings should be translated if a translation file is available.

Marking strings for translation

The principle is the following:
yourls_i18n_function( $string, $textdomain )

where

  • yourls_i18n_function will be one i18n functions, see below
  • $string is the string to translate
  • $textdomain is a unique identifier, the "text domain", you'll use across your project

The above example would become:

// echo a string
yourls_e( 'This is a message', 'john-plugin' );

// return a string
$message = yourls__( 'another string', 'john-plugin' );

This will make 2 things happen:

  1. i18n functions return translated string if a translation file is found, or the original string otherwise.
  2. these functions also mark the strings as translatable: a utility like Poedit (see below) can generate a PO or POT file with these strings, so that translators can make their translation file.

Loading your text domain

Pick a unique name, preferably related to the project name. For instance if you're coding a plugin named Funny Redirection, the domain could be funny-redirection.

Your code must have the following function call:

yourls_load_custom_textdomain( $domain, $path_to_translation_files );

For instance in your plugin that would be:

// Trigger when all plugins are loaded
yourls_add_action( 'plugins_loaded', 'funny_redirection_load_textdomain' );

// load custom text domain
function funny_redirection_load_textdomain() {
    yourls_load_custom_textdomain( 'funny-redirection', dirname( __FILE__ ) . '/translations' );
}

This would tell YOURLS to look for translation files inside the translations directory of your plugin folder.

The translation file names must follow the following format: domain dash locale, eg funny_redirection-pt_BR.mo

i18n functions

YOURLS i18n functions are located in includes/functions-i18n.php. The main functions are:

  • yourls__( $string, $domain )
    Return a translated string

  • yourls_e( $string, $domain )
    Echo a translated string

  • yourls_n( $single, $plural, $number, $domain )
    Return the plural or single form based on the amount. Example:
    $msg = yourls_n( '1 tooth', '%s teeth', $number, 'funny-redirection' );

Escaping functions:

  • yourls_esc_html__( $string, $domain )
    Retrieves the translation of $string and escapes it for safe use in HTML output

  • yourls_esc_attr__( $string, $domain )
    Return a translated string, escaped for a use in an attribute (like title="")

There are also the corresponding xxxx_e() function to echo instead of return.

There are more complex functions you probably won't need for basic stuff, but reading the source of includes/functions-i18n.php will make your day a whole lot brighter anyway :)

Placeholders

Translatable strings cannot contain dynamic content. For instance,

  • while this will work: echo "We have $count short URL";
  • this won't: yourls_e( "We have $count short URL", "myplugin" );

During the parsing of your code to generate PO files, the PO generator will mark "We have $count short URL" as translatable. However in the application yourls_e() will be called with an argument like "We have 1337 short URL": YOURLS won't find a suitable translation of this string and will return it untranslated.

The solution is to use functions printf() (to echo) and sprintf() (to return).

Here is what the right solution of the short URL count problem will look like:

printf( yourls__( "We have %s short URL", "myplugin" ), $count );

Generate a POT file

Once all your code is ready for translation, generate a POT file. You'll need to configure your software (eg PoEdit) with these strings, which are the i18n functions used in YOURLS:

  • yourls__
  • yourls_e
  • yourls_s
  • yourls_se
  • yourls_esc_attr__
  • yourls_esc_html__
  • yourls_x
  • yourls_ex
  • yourls_esc_attr_x
  • yourls_esc_html_x
  • yourls_n:1,2
  • yourls_nx:1,2
  • yourls_n_noop:1,2
  • yourls_nx_noop:1,2

Configure also the parser to include comments for translators. In PoEdit: File, Preferences, Parsers, PHP: edit and in the Parser command add parameter --add-comments=/

You'll find plenty of guides on the net to show you how to use PoEdit. Also check the official YOURLS.pot

Get a few translators to work!

Your work is now ready to be translated. Ask a few Japanese / Brazilian / Czech friends to create translation files, and ship your code with all these translation files :)