i18n module integration

Last updated on
14 October 2016

There is a i18n controller for integration (exportable) entities with the i18n module. However, the controller requires some additional code for your entity type to be activated. It's best practice to create a small integration module for that, which then can depend upon i18n. In that module you basically have to:

  • Activate the i18n integration controller.
  • Declare translatable properties.
  • Implement CRUD hooks to notify i18n module of updates

Once, you've done that you still have to take care of using the translation in the right place, e.g. with the provided helper entity_i18n_string(). It's recommended to use classed entity objects in order to be able to leverage the simpler to use Entity::getTranslation() method though.


As example, we have a short look at the i18n integration provided by profile2:

The profile2_i18n.info file looks like this:

name = Profile2 translation
description = Translate profile2 types.
dependencies[] = profile2
dependencies[] = i18n_string
package = Multilingual - Internationalization
core = 7.x

Then the module file activates the entity i18n integration controller...

 * Implements hook_entity_info_alter().
function profile2_i18n_entity_info_alter(&$info) {
  // Enable i18n support via the entity API.
  $info['profile2_type']['i18n controller class'] = 'EntityDefaultI18nStringController';

...and declares some properties as translatable:

 * Implements hook_entity_property_info_alter().
function profile2_i18n_entity_property_info_alter(&$info) {
  // Mark some properties as translatable, but also denote that translation
  // works with i18n_string.
  foreach (array('label') as $name) {
    $info['profile2_type']['properties'][$name]['translatable'] = TRUE;
    $info['profile2_type']['properties'][$name]['i18n string'] = TRUE;

What remains to be done is informing the i18n module about updates:

 * Implements hook_profile2_type_insert().
function profile2_i18n_profile2_type_insert($profile_type) {
  i18n_string_object_update('profile2_type', $profile_type);

 * Implements hook_profile2_type_update().
function profile2_i18n_profile2_type_update($profile_type) {
  // Account for name changes.
  if ($profile_type->original->type != $profile_type->type) {
    i18n_string_update_context("profile2:profile2_type:{$profile_type->original->type}:*", "profile2:profile2_type:{$profile_type->type}:*");
  i18n_string_object_update('profile2_type', $profile_type);

 * Implements hook_profile2_type_delete().
function profile2_i18n_profile2_type_delete($profile_type) {
  i18n_string_object_remove('profile2_type', $profile_type);

That's it. Using translation usually has to be done in the main module, not depending on i18n - e.g. as done here:

    return t('My @profile-label', array('@profile-label' => drupal_strtolower($type->getTranslation('label'))));

Further notes:

  • In case your integration needs are more complex you can override the EntityDefaultI18nStringController and provide a custom i18n_string_object_wrapper, e.g. Rules does so.
  • Other example implementations can be found in Rules link, here or here.