Consider the following scenario:

(1) create two drupal sites, etsource.localhost and etdest.localhost
(2) on both, enable locale and language, go to admin/config/regional/language, and install and enable French in addition to English.
(3) on etsource, download and enable the features module
(4) on etsource, create a content type x
(5) on etsource, go to admin/structure/features/create, create a new feature with the content type x
(6) now put the module x in etsource and etdest, in the sites/all/modules folder
(7) enable x on both etsource and etdest
(8) on etdest, node/add/x, and enter "created with untranslatable field" as a title and body: this will be etdest/node/1
(9) on etsource and etdest, dowload and enable entity_translation
(10) go back to etsource.localhost/admin/structure/types/manage/x/fields/body, click "users may translate this field" and hit save
(11) go to http://etsouce.localhost/admin/structure/features/x/recreate, add entity_translation as a dependency and save
(12) put the new version of x on etdest
(13) on both source and dest, revert the feature
(14) on etdest, node/add/x, and enter "created with translatable field" as a title and body: this will be etdest/node/2
(15) update the feature again to reset the body field to untranslatable, and deploy.
(16) on etdest, create node/3 with the title and body "created with untranslatable field again".

node/1: you see the content
node/1/edit: you see the content in the body field
node/2: you don't the content
node/2/edit: you don't see the content in the body field
node/3: you see the content
node/3/edit: you see the content in the body field

When deploying new field translatability, an api function in entity_translation might be good, so we could do something like:

function x_update_7001() {
  // this function does not exist!

For now it seems that the function that does this is really tied to the batch process and the form, so there is no clear way to do this programmatically.


alberto56’s picture

In the above scenario, the database is in this state:

mysql> select entity_id, language, body_value, revision_id from field_data_body where entity_type = 'node';
| entity_id | language | body_value                                               | revision_id |
|         1 | und      | NODE 1 CHANGED                                           |           6 |
|         2 | en       | created with translatable field                          |           2 |
|         3 | und      | created, again, with untranslatable field NODE 3 CHANGED |           4 |

If the field should be LANGUAGE_NONE, we would need the function to update the language of line two of the above, it seems. That way, if a field is being modified during a deployment, our function could make sure the above table would be in a consistent state.

alberto56’s picture

To normalize the data in this table, I used the following code. What I have in mind for entity_translation_update_field_data() is a more robust version of this:

function MYMODULE_update_7021() {
  // I am deploying a new untranslatable version of body, because
  // I had previously made it translatable by error.
  features_revert(array('MYMODULE' => array('field')));

  // deploying it this way, though, does not change the data in the field_data_body
  // we need to to this here
  // see
  $query = db_select('field_data_body', 'f')
    ->fields('f', array('language', 'entity_id', 'body_value'))
    ->condition('f.entity_type', 'node', '=');
  $query->innerJoin('node', 'n', 'n.vid=f.revision_id');
  $result = $query->execute();
  $update = array();
  while($row = $result->fetchAssoc()) {
    if ($row['language'] != LANGUAGE_NONE) {
      if (!isset($update[$row['entity_id']])) {
        $update[$row['entity_id']] = $row['body_value'];
      else {
        $update[$row['entity_id']] .= '; ' . $row['language'] . ': ' . $row['body_value'];
  foreach ($update as $nid => $body) {
    $node = node_load($nid);
    $node->body[LANGUAGE_NONE][0]['value'] = $body;
bforchhammer’s picture

Yes, having something like entity_translation_update_field_data() would be useful for automatic data migration tasks. Ideally that function would also be used by the existing batch process in order to avoid duplicate code/logic.

However, until we get there, note that it should also be possible to use the batch process from within your update function by reusing the hook_update-batch sandbox. I had a very similar problem a while ago, see code below. Maybe it can help you avoid having to update the database yourself... Note that I wrote this about a year ago for the old alpha version of ET, so no guarantee that it still works. :-)

Also note that if you want to use this for multiple fields, I found that you had to use a separate hook_update for each field or otherwise the batch process would get stuck.

 * Enable multilingual property: field_article_title
function MYMODULE_update_7004(&$sandbox) {
  MYMODULE_features_enable_entity_translation('field_article_title', $sandbox);

 * Migrate existing field content from language "undefined" to entity language.
 * @param $field_name
 *   Field to enable entity translation on.
function MYMODULE_features_enable_entity_translation($field_name, &$sandbox) {
  $context = array('sandbox' => &$sandbox);
  module_load_include('inc', 'entity_translation', 'entity_translation.admin');
  entity_translation_translatable_batch(TRUE, $field_name, $context);
  $sandbox['#finished'] = $context['finished'];

When you start converting an existing site to use ET you probably also want to start using the title module; here's the snippet I used for migrating old node titles to title module ones.

 * Migrate title to field_title.
function MYMODULE_update_7003(&$sandbox) {
  MYMODULE_features_title_field_init('node', 'page', 'title', $sandbox);

 * Populate title field with existing title values.
function MYMODULE_features_title_field_init($entity_type, $bundle, $legacy_field, &$sandbox) {
  $context = array('sandbox' => &$sandbox);
  module_load_include('module', 'title');
  title_field_replacement_batch($entity_type, $bundle, $legacy_field, $context);
  $sandbox['#finished'] = $context['finished'];

Hope this helps. :)

alberto56’s picture

Thanks, very useful indeed!


alberto56’s picture

If one is updating a field from untranslatable to translatable one a single site, the confirmation text warns you that you will be losing data. The same is not true if you are deploying a change -- so it might be prudent to not implement the exact same code: for a deployment, perhaps keeping a copy of the translations might be a good idea in case the field is again set to translatable.

pbuyle’s picture


Starting from #3, I wrote a unique hook_update_N() to process all translatable fields, it can be found at