I am creating/updating a node programmatically, i.e., with node_save($node);

What is the best practice for creating translations for an entity completely programmatically?

Comments

plach’s picture

See lines 367-375 of entity_translation.admin.inc:

<?php
  $translation = array(
    'translate' => $form_state['values']['translation']['translate'],
    'status' => $form_state['values']['translation']['status'],
    'language' => $form['#language'],
    'source' => $form['#source'],
  );

  $handler->setTranslation($translation, $form_state['values']);
  field_attach_update($form['#entity_type'], $form['#entity']);
?>

The call to field_attach_update is not necessary if you call node_save after setting the translation.

plach’s picture

Status: Active » Fixed

I guess this is fixed.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

pcambra’s picture

Here's what I did for adding translations to taxonomy terms programatically, just in case someone finds it useful

  $translation = array(
    'translate' => 0,
    'status' => 1,
    'language' => 'en', // here is the language you're translating to
    'source' => 'fr', // here is the source language
  );
  $term = taxonomy_term_load($tid); // I had to load the taxonomy term
  $handler = entity_translation_get_handler('taxonomy_term', $term); // get the translation handler in place
  // This is the array structure for the field you are translating, in my case the name was "name_field" and the language target, English. $value is the translated string.
  $values = array(
    'name_field' => array (
      'en' => array(
        '0' => array(
          'value' => $value,
         )
       )
     )
  );
  // Finally you set the translation and save the object.
  $handler->setTranslation($translation, $values);
  taxonomy_term_save($term);
Toxid’s picture

Thought I'd share my code for translating nodes as well, since I couldn't find a way to do it while creating a node, before calling node_save.

<?php

$translation = array(
  'translate' => 0,
  'status' => 1,
  'language' => 'sv', // here is the language you're translating to
  'source' => 'en', // here is the source language
);

$node = node_load($nid); // If you've called node_save($node) before, you'll have the node object already and you can omit this
$handler = entity_translation_get_handler('node', $node);	
$values['name_field']['sv']['0']['value'] = $sv_name; // You can write the array like this, I find it easier to read
$handler->setTranslation($translation, $values);
field_attach_update('node', $node);

?>

If it's possible to add a translation while creating a node, before calling node_save, please let me know.

nikosnikos’s picture

Thanks plach, pcambra and Toxid it saved me hours !

Here's how I did it :

$node = (object) array(
  'uid'     => $uid,
  'type'    => 'article',
  'title'   => $title,
  'body'    => array($lang_orig => array(0 => array(
    'value' => $body,
    'summary' => $summary,
    'format'  => $format_id,
  ))),
  'created' => $date,
  'changed' => $changed,
  'comment' => 1,
  'language' => $lang_orig,
  'revision' => 0,
);

$node->body[$new_lang][0] = array(
  'value' => $new_body,
  'summary' => $new_summary,
  'format'  => $format_id,
);

$node->title_field[$new_lang][0]['value'] = $new_title;

// Add other node fields translation

$handler = entity_translation_get_handler('node', $node);

$translation = array(
  'translate' => 0,
  'status' => 1,
  'language' => $new_lang,
  'source' => $node->language,
);

$handler->setTranslation($translation, $node);

node_save($node);
smartango’s picture

Component: Code » Base system
Status: Closed (fixed) » Active

does not work anymore.
I want to add a translation to a node, it just does nothing
this

drupal_set_message('title field . '.print_r($this->node->title_field,TRUE));

print all languages defined for all translation, but in the ui it is not present the translation (translate tab says 'add')

version: 7.x-1.0-beta1

plach’s picture

Is the $node->translations key defined and not empty?

smartango’s picture

this:
translations after . stdClass Object
(
[original] => en
[data] => Array
(
[en] => Array
(
[entity_type] => node
[entity_id] => 7
[language] => en
[source] =>
[uid] => 1
[status] => 1
[translate] => 0
[created] => 1348749201
[changed] => 1352392099
)

)

)

is the output of drupal_set_message('translations after . '.print_r($this->node->translations,TRUE)); (printed before and after setTranslation have the same content)

also I want to update original value, I am using:
$this->node->title_field[$language][0]['value'] = $title;

              $translation = array(
                      'translate' => 0,
                      'status' => 1,
                      'language' => $language, // here is the language you're translating to
                      'source' => $this->node->language, // here is the source language
              );
              $values = array(
                      'title_field' => array (
                              "$language" => array(
                                      '0' => array(
                                              'value' => $title,
                                      )
                              )
                      )
              );
              $handler->setTranslation($translation,$values);
...
field_attach_update('node',$this->node); // I would use node_save, I am in a class, thus $this->node, but now is on the same method

print_r($values):

values Array
(
[title_field] => Array
(
[it] => Array
(
[0] => Array
(
[value] => Prodotto con variantis
)

)

)

)

plach’s picture

Status: Active » Fixed

I am sorry, I don't know what troubles you are facing, but the way described in #6 should be the most correct.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

mike.coenen’s picture

Sorry to open this up again.

The suggestion of number 6 (nikosnikos) works great. The only thing that does not seem to work is the url alias not being created for the added translations. I have tried to debug the handler that gets returned but the only thing i can find is that it does set the pathauto = TRUE value in the node.

Anyone have an idea to get the aliases working? (It does work when creating a translation through the interface )

plach’s picture

Did you try setting pathauto = true?

mkalkbrenner’s picture

Just an important hint for everyone who tries add a translation to an entity programmatically.

The way described in #6 works if you create a new node.

If you want to add a translation to an already existing node you will run into the issue described in #7. In that case you have to tell the handler to replace the cached entity with the updated one by setting the optional third parameter to TRUE:

$handler = entity_translation_get_handler('node', $node, TRUE);
Smos’s picture

I do it a little different:

<?php
  $node = array(
    'type' => 'activity',
    'uid' => 1,
    'status' => 1,
    'comment' => 0,
    'promote' => 0,
    'language' => 'en',
  );

  $entity = entity_create('node', $node);

  $ewrapper = entity_metadata_wrapper('node', $entity);
  
  $ewrapper->title_field->set('English Title');
  $my_body_content = 'This is in English';
  $ewrapper->body->set(array('value' => $my_body_content));

  $handler = entity_translation_get_handler('node', $entity, TRUE);

  $translation = array(
    'translate' => 0,
    'status' => 1,
    'language' => 'nl',
    'source' => 'en',
  );

  // I want to change the value of the title and body field of the translation here but don't know how...

  $handler->setTranslation($translation, $entity);

  $ewrapper->save(true);
  entity_save('node', $entity);
?>

But I don't know how to change the title and body field of the translation without overwriting the original node. Does anybody know how?

torpy’s picture

For the title field at least, you need to have the title module installed: http://drupal.org/project/title

calculus’s picture

I face a similar problem to comment #12.

I'm trying to create translations of body field when a user creates a node. I'm using hook_field_attach_presave.

function hook_field_attach_presave($entity_type, $entity) {
  $entity->body[$target_lang][0] = array(
    'value' => 'some value',
    'summary' => '',
    'format'  => 'safe_html',
  );
			
  $handler = entity_translation_get_handler('node', $entity);
			
  $translation = array(
    'translate' => 0,
    'status' => 1,
    'language' => $target_lang,
    'source' => $original_lang,
  );

  $handler->setTranslation($translation, $entity);
}

Translation is being created (in edit form i have target-language's tab), url alias is being created for original content but not for translations. In fact if i edit translations i realize that path['pathauto'] is false.

Everything works right if i manually add a translation from UI. Generate automatic URL alias is ticked in edit translation form.

Do i miss something?

mvdve’s picture

Status: Closed (fixed) » Active

Same issue here (as #12 and #17). Pathauto is disabled on the second language.
In my case I import taxonomy terms with the migrate module.
After enabling pathauto everything works perfect.

The following code only enables the main entity. Pathauto is still disabled on the other languages.

$entity->path = array('pathauto' => FALSE);

When the entity function "Hide shared elements on translation forms" is enabled, the pathauto field is show on the translation.
It looks like this field is not shared between the two versions.

Is there a way to enable pathauto or to fix the "share" issue?

mvdve’s picture

Updated the code to be more simple and reliable. The pathauto update function activates the translated alias:

For the time being, I created an work around:

$translation = array(
        'translate' => 0,
        'status' => 1,
        'language' => 'en', // language to set
        'source' => 'nl',      // source language
    );
    $term = taxonomy_term_load($entity->tid);
    $handler = entity_translation_get_handler('taxonomy_term', $term, TRUE);
    $values['name_field']['en']['0']['value'] =   $name_en;
    $handler->setTranslation($translation, $values);
    taxonomy_term_save($term);
    
    // create url alias for translation
    pathauto_taxonomy_term_update_alias($term, 'update', $values = array ( 'language' => 'en')); // create path for translation

Hopefully this helps people with the same problem.

plach’s picture

Status: Active » Fixed

Tentatively marking fixed again.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

SebCorbin’s picture

Thanks to you guys I made an MigrateDestination that set a translate_copy property.
Later in hook_node_insert(), it automatically creates translations for each language activated using default values from the original language.

/**
 * Implements hook_node_insert().
 */
function importer_node_insert($node) {
  static $languages = array();
  if (empty($languages)) {
    $languages = array_keys(language_list());
  }
  // Automatically generate translation for nodes
  // marked as such during migration.
  if (!empty($node->translate_copy)) {
    $handler = entity_translation_get_handler('node', $node, TRUE);
    foreach ($languages as $langcode) {
      if (!isset($node->translations->data[$langcode]) && $langcode !== $node->language) {
        $values = array();
        $translation = array(
          'translate' => 0,
          'status'    => 1,
          'language'  => $langcode, // language to set
          'source'    => $node->language, // source language
        );

        // Copy field values
        foreach (field_info_instances('node', $node->type) as $instance) {
          $field_name = $instance['field_name'];
          $field = field_info_field($field_name);
          if ($field['translatable']) {
            $value = $node->{$field_name}[$node->language];

            // Append langcode for title field, not necessary but handy while viewing pages.
            if($field_name == 'title_field'){
              $value[0]['value'] .= " [$langcode]";
            }

            $values[$field_name][$langcode] = $value;
            $node->{$field_name}[$langcode] = $value;
          }
        }

        $handler->setTranslation($translation, $values);
      }
    }
    $handler->saveTranslations();
    field_attach_update('node', $node);
  }
}
dwb17’s picture

Issue summary: View changes

After googling for quite a while, and trying to find a working example of how to programmatically create a multilingual entity, I ended up at this post.
It seems that this is the best documentation around currently, so I though it would help others by adding an example code snippet that utilized Entity Metadata Wrapper(entity_metadata_wrapper) and Entity translation.

<?php
//Setup default values for the node
$default_values = array(
  'type' => 'project',
  'uid' => 1,
  'status' => 0,
  'promote' => 0,
  'language' => 'el' // source language
);

// Create the new project.
$node_entity = entity_create('node', $default_values);

$handler = entity_translation_get_handler('node', $node_entity); // get the translation handler in place

$node_wrapper = entity_metadata_wrapper('node', $node_entity);

// We must set the value of the node title, 
// even though we are using the title field(https://www.drupal.org/project/title)
$node_wrapper->title = 'Greek Title'; 

//Populate the fields now, per langauge
$node_wrapper->language('el')->title_field = 'Greek Title';
$node_wrapper->language('en')->title_field = 'English Title';

$node_wrapper->language('el')->field_subtitle = 'Greek subtitle';
$node_wrapper->language('en')->field_subtitle = 'English subtitle';

$translation = array(
  'translate' => 0,
  'status' => 1,
  'language' => 'en', // here is the language you're translating to
  'source' => 'el', // here is the source language
);

$handler->setTranslation($translation, $node_wrapper->value());
$node_wrapper->save();

// Create url alias for translation
// This is necessary in order for the translated node get a correct alias
pathauto_node_update_alias($node_wrapper->value(), 'update', $values = array ( 'language' => 'en'));
?>
gambry’s picture

To properly create the pathauto alias, the only cross-entitytype solution I found so far is:

if (module_exists('pathauto') && is_callable('pathauto_' . $entity_type . '_update_alias')) {
    call_user_func('pathauto_' . $entity_type . '_update_alias', $entity, 'update', array('language' => $langcode));
}

Where $langcode is your additional language code$entity is your entity and $entity_type is taxonomy_term, node, user, etc.

bendev’s picture

#24 works fine thanks

it can be interesting to note that it should be run (for instance) @hook_node_insert because hook_entity_presave is too ealry and the alias would get deleted afterwards.