Drupal 7 core, together with Drupal contrib (https://www.drupal.org/docs/7/modules), has the ability to ship a powerful platform for our digital requirements in the enterprise world. The multilingual feature is one of the prime reasons that Drupal is a distinguished and a preferred solution to achieve business goals. Drupal 8 includes the multilingual package in the core itself, while Drupal 7 requires some extra amount of effort to build this package as a whole to make Drupal multilingual ready. As you might know, Drupal 7 has two major ways which can help you achieve multilingual functionality: 1. Content Translation 2. Entity Translation
To be brief, Content Translation creates a new node when a node is translated and is the historic way to translate content. On the other hand, Entity Translation works together with the Field Translation module shipped under the Internationalization module to translate fields of a node and is the recommended way to internationalize comment. Also, don't forget, if you're using entity translation, you need to use the Title module to add support for translatable titles. (Read More: Quora: Difference between Content and Entity Translation Drupal Stackexchange: Difference between Content and Entity Translation ) The combination of these contrib modules helps us bring multilingual power to our Drupal Platform. General translation of Drupal content, including node, taxonomy, menu, built-in interface, configuration (variables), is achieved easily by using these modules. But Drupal is not just limited to these entities. As developers, we have the flexibility in Drupal to create custom entities as per our requirements, and hence it's our responsibility to extend all the Drupal features to these custom built entities together with maintaining the great UX that Drupal already has. But custom entity is not translation ready by default. So let's see how we can extend translation support for a custom entity in Drupal 7.
We need to add a language column to the custom entity table to save the original language of the entity. Something like this: 'language' => [ 'type' => 'varchar', 'description' => "Language", 'length' => 20, 'not null' => FALSE, 'default' => LANGUAGE_NONE, ],
Add this to the update hook_schema of the custom entity. And if the module already exists, provide hook_update to do so using db_add_field.
In order to achieve this we need to let our Drupal know of this new power of 'Multilingual Support' using Entity API. So, find hook_entity_info implemented for the custom entity in your custom module and update âentity keys' key of the entity info array to include âlanguage' as well:
'entity keys' => [ 'id' => 'id' , 'language' => 'language', ],
Also, additionally we need to provide more related information to the entity info array at top level (similar to âentity keys') as: 'uri callback' => 'entity_class_uri', 'translation' => [ 'locale' => TRUE, 'entity_translation' => [ 'class' => 'EntityTranslationDefaultHandler', 'default settings' => [ 'default_language' => LANGUAGE_NONE, 'hide_language_selector' => FALSE, ], // Base path of the custom entity. 'base path' => 'custom_entity/%custom_entity', // Translate path to be used. 'translate path' => 'custom_entity/%'custom_entity/translate', // Access Callback to check access to translate tab. 'access callback' => 'custom_entity_translation_tab_access', // This depends on the translate path used above. 'access arguments' => [1], 'edit form' => 'custom_entity', 'view path' => 'custom_entity/%custom_entity', 'edit path' => 'custom_entity/%custom_entity/edit', ], ],
We need to define access callback 'custom_entity_translation_tab_access' function declared as âaccess callback', which can be something like this: function 'custom_entity_translation_tab_access'($entity) { return entity_translation_tab_access('custom_entity', $entity); }
Important Note: Few keys defined above under translation might be incorrect or of no use, but this combination worked for me. For detailed information, refer to following links: https://www.drupal.org/project/entity_translation/issues/2442703 https://www.drupal.org/project/entity_translation/issues/2789133
The above 2 steps create a supportive backend to extend multilingual support to our custom entity. Now, let's start testing and building on top of this. So, the next step in the direction is to enable Entity Translation for this entity as well, similar to what we have for other entities at:admin/config/regional/entity_translation We can also configure default language and language selector for the entity on the entity add/edit form. But unfortunately, at the time of development, I faced a few issues regarding this, which most probably is because of entity translation module. So, I would recommend you use the following settings for your custom entity: Default Language: Current Language Hide Language Selector: Checked
Now some good news: Field Translation works perfectly fine here as well. Just go to manage fields via UI and edit the field and enable field translation for all the fields you want to translate. Nothing fancy over here.
This is a very important step, as we are about to extend already existing custom entity form for add/edit operation. Remember in step 2, we added translation key to entity info and declared translate path. This adds a translate tab (rather say local task menu) to the entity edit/view page.
// ADD language variable . $langcode = entity_language('custom_entity', $entity); if (empty($langcode)) { $langcode = LANGUAGE_NONE; } // Simply use the default language. $form['language'] = array( '#type' => 'value', '#value' => $langcode, ); // This is required by other translation modules. $form_state[âcustom_entity'] = $entity; field_attach_form('custom_entity', $entity, $form, $form_state, $langcode);
Note that field_attach_form has langcode parameter, which is responsible for updating field elements based on current language for the entity and the translations. Update the submit handler of the form as well. Add language property of the entity before entity save operation as follows: $entity->language = $form_state['values']['language']; $entity->save();
Great! Now we are good to test the addition and updating operation for custom entity, checking default values for the Add translation form of the entity. And if everything is working fine (basically storage operation is fine), then we are ready to move forward.This step is very crucial, from Multilingual support point of view. Anytime a field value is used either for display or any other activity, we generally use LANGUAGE_NONE or âund' to get the value, which is not correct. As we are using field_translation module, field values now are stored considering the language value as well. Generally, field values in any entity are stored under LANGUAGE_NONE / âund' and hence we fetch the value in a similar fashion. $entity->field_sample[âund'][0][âvalue'] = âsample value'
But, enabling field translation stores the value separately for individual languages as: $entity->field_sample['en'][0]['value'] = 'sample value'; $entity->field_sample['de'][0]['value'] = 'sample value';
Keys âen' an âde' represents language code for English and German Language in the above code. So, find all the places in the custom codebase where the wrong way has been used and instead use field_language function.
So, custom entity translation works fine till the last step, but the whole mood is spoiled when we come to know that Title module does not support custom entity translation. https://www.drupal.org/project/title/issues/2718771 Now what? In Drupal, we have a solution for everything, if not contrib then definitely custom :D and anyway we are already transforming our custom entity. So, let's start extending translation support to the title of a custom entity. We are basically going to implement the same functionality of the Title module ourselves (independent of the title module).
Title module replaces the original title with title_field, in fact, it adds a field named âtitle_field' to the entity (say node) and uses field translation on top of that. So, similarly, we will add a field for the same purpose and enable field translation for that field. Looks good? If yes, go ahead and add title field manually using field UI with whatever machine name you want. For eg: field_custom_entity_title
Now we need to update all the possible calls to the original title and reroute that so that whenever a label is asked we try to send translated title as per current language. One quick possible way can be using hook_entity_load and update entity's title property based on language asked or current language. But this will not work as if you edit any custom entity then you will never be able to get actual value of title for the edit forms and will end up creating a mess. So, we will have to go stepwise. Firstly let's create a method in our Custom Entity Controller to get translated title /** * Get translated title based on field_custom_entity_title. */ public function getTranslatedTitle($entity, $langcode = NULL) { global $language; if (empty($langcode)) { $langcode = $language->language; } $translated_title = $entity->title; if (field_info_field('field_custom_entity_title')) { if (!empty($entity->field_custom_entity_title[$langcode][0]['value'])) { $translated_title = $entity->field_custom_entity_title[$langcode][0]['value']; } } return $translated_title; }
Now we need to update entity label callback as defined in hook_entity_info to use this function to return label, something like this: return entity_get_controller('custom_entity')->getTranslatedTitle($entity);
The above 2 steps makes it possible to get translated title for our custom entity using entity metadata wrapper.
So now wherever we have directly used $entity->title it's time to update that and instead use entity_metadata_wrapper or entity_get_controller & getTranslatedTitle to get appropriate title as per language asked or current language. Check: https://www.drupal.org/docs/7/api/entity-api/entity-metadata-wrappers for entity_metadata_wrapper usage Now, as far as custom code is concerned we are good and are supporting translations for our custom entity. But, it might be possible that other contrib modules are not using entity_api and instead are fetching title in a manner that the original title is sent instead of the translated one. In that case, we will have to identify this by performing kind of smoke testing and create a patch to fix this by using entity_api and entity_metadata_wrapper. I was able to find 2 such relevant issues
Few contrib module use the label of entity keys from hook_entity_info to get label directly from custom entity table which creates a problem and hence we can propose to use entity_api whenever available to fetch title.
If this content did not answer your questions, try searching or contacting our support team for further assistance.
Fri Sep 12 2025 08:21:49 GMT+0000 (Coordinated Universal Time)