Last updated November 5, 2012. Created by Jose Reyero on April 2, 2011.
Edited by frederickjh, Daniel_KM, Kristen Pol. Log in to edit this page.

If you want some of your module's strings and objects to be translatable using i18n API, there are some functions that need to be implemented.

Menu translation

Any module that displays a menu needs to provide a hook for i18n to be able to translate/localize it.

<?php
// If $tree is the menu tree to be displayed
if (function_exists('i18n_menu_localize_tree')) {
 
$tree = i18n_menu_localize_tree($tree);
}
?>

Multilingual variables

In order for your module's variables to be translatable they must be declared using Variable module hooks. If variables are declared using hook_variable_info() everything else will be handled automatically.

String translation

Internationalization module provides translation services for user-defined strings. It also tracks source strings and keeps them up-to-date using a 'string key' that is a string with these elements "textgroup:object_type:object_key:property_name".

Some examples of these 'string keys' are:

taxonomy:term:12:name
node:type:story:name

Strings of core modules

Most Drupal core 'textgroups' and strings like Taxonomy terms, Vocabularies, Menu items or Content type names are already defined and tracked by i18n modules so the only thing you need is the below translating functions.

1. Get / translate strings.

For translating these strings, the module must call this function before displaying them:

<?php
/**
* Translate or update user defined string.
*
* @param $name
*   Textgroup and location glued with ':'.
* @param $string
*   String in default language. Default language may or may not be English.
* @param $options
*  Optional parameters like 'langcode' if language code is different from the page request one.
*
* @return $string
*   Translated string, $string if not found
*/
function i18n_string_translate($name, $string, $options) {
   .....
}
?>

For strings that have an input format you must use the function i18nstrings_text() that takes care of applying the correct filters if there is a translation.

<?php
/**
* Get filtered translation
*
* This function is intended to return translations for strings that have an input format.
* If no format passed, the default one will be used
*
* @param $name
*   Full string id
* @param $default
*   Default string to return if not found, already filtered
* @param $options
*   Optional parameters, like 'format'
*/
function i18n_string_text($name, $default, $options) {
  ....
}
?>
2. Create translation sets (taxonomy example)

To translate a string, you should use two functions, depending on which i18n mode you use:

  • Localize mode: i18n_string_translation_update()

    <?php
      $result_translation
    = i18n_string_translation_update(
        array(
    'taxonomy', 'term', $tid, 'name'), // Path where to store source and translation.
       
    $translated_string,
       
    $language,
       
    $source_string
     
    );
    ?>
  • Translate mode: i18n_translation_set_create()

    <?php
     
    // 1. Load or create a translation set.
     
    $translation_set = ($source_term->i18n_tsid) ?
       
    i18n_translation_set_load($source_term->i18n_tsid) :
       
    i18n_translation_set_create('taxonomy_term', $vocabulary->machine_name);
     
    // 2. Add the full source term in this set.
     
    $translation_set->add_item($source_term, $source_term->language);
     
    // 3. Add the full translated term in this set.
     
    $translation_set->add_item($translated_term, $translated_term->language);
     
    $translation_set->save(TRUE);
    ?>

Strings for other contents and modules

When a module defines its own strings, it must use some additional functions to allow these string to be tracked and updated.

1. Define the textgroup by implementing hook_i18n_string_info(). This is how it looks (i18n_taxonomy module)

<?php
/**
* Implements hook_i18n_string_info()
*/
function i18n_taxonomy_i18n_string_info() {
 
$groups['taxonomy'] = array(
   
'title' => t('Taxonomy'),
   
'description' => t('Vocabulary titles and term names for localizable vocabularies.'),
   
'format' => FALSE, // This group doesn't have strings with format
   
'list' => FALSE, // This group cannot list all strings
   
'refresh callback' => 'i18n_taxonomy_i18n_string_refresh',
  );
  return
$groups;
}
?>
2. Use i18n_string_update() whenever the source string changes.

<?php
/**
* Update / create translation source for user defined strings.
*
* @param $name
*   Textgroup and location glued with ':'.
* @param $string
*   Source string in default language. Default language may or may not be English.
* @param $options
*   Array with aditional options. The most important one is the 'format' parameter when the string has an input format.
*/
function i18n_string_update($name, $string, $options = array()) {
  ....
}
?>

The format parameter is to be used when the string is to be formatted with Input filters before displaying. If so, filter permissions will be properly handled by i18nstrings. Only users that can access the Input format will be able to translate that strings. These strings must be translated using i18nstrings_text() instead of i18nstrings().

3. Optional refresh callback function as defined in hook_locale()

<?php
/**
* Update / create translation source for user defined strings.
*
* @return $bool
*   TRUE if update successful, FALSE if not.
*/
 
function mymodule_locale_refresh() {
    ...
   
// code to call i18n_string_update() for every string used by this module
   
...
    return
TRUE; // Update completed with no issues
 
}
?>

The refresh function should call i18n_string_update() for every string that is to be translated in your module. The refresh interface will remove all strings defined by your module and only re-list the strings added by the refresh callback function.

Again, all Drupal core textgroups and strings will be defined and handled by i18n_string when edited using the default pages for it. So you just need to implement these other functions when your module uses specific user defined strings.

The easiest way to have the strings translated without adding a new module dependency is to create a wrapper for your module which just translates the strings when i18n_string module is enabled.

<?php
function mymodule_translate($name, $string, $langcode = NULL) {
  return
function_exists('i18n_string') ? i18n_string($name, $string, array('langcode' => $langcode)) : $string;
}
?>

Or, if your module has its own textgroup, we can have a default parameter for that textgroup and same some code. Note we still keep the 'textgroup' parameter just in case we need to translate other module's strings, like for occasionally translating a taxonomy term or a content type name.

<?php
function mymodule_translate($name, $string, $langcode = NULL, $textgroup = 'mytextgroup') {
  return
function_exists('i18n_string') ? i18n_string($textgroup . ':' . $name, $string, array('langcode' => $langcode)) : $string;
}
?>

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

Where do i put

Menu translation
Any module that displays a menu needs to provide a hook for i18n to be able to translate/localize it.

<?php
// If $tree is the menu tree to be displayedif
(function_exists('i18n_menu_localize_tree')) {
 
$tree = i18n_menu_localize_tree($tree);
}
?>

At the beginning of this page you wrote about menu translation and gave and example of how to provide a hook,
I want to ask about Taxonomy translation, i have a module that shows my taxonomy terms always in English, and not in other languages although i have translated all the terms. When i checked the code of the module i found it is using this

<?php
$options
= taxonomy_allowed_values($field);
?>

Does i18n hook automatically such calls?, or the code of the module should be like the one provided for menu translation, because when i changed the code to:

<?php
if (function_exists('i18n_taxonomy_allowed_values')) {
 
$options = i18n_taxonomy_allowed_values($field);
}else{
$options = taxonomy_allowed_values($field);
}
?>

it was working well, but the owner of the module told me i am in the wrong way and i should not do that;

could you please tell us of how should i solve this problem, is there any other correct way of doing that?

Thanks.