Last updated April 14, 2016. Created on March 16, 2014.
Edited by nicrodgers, Grimreaper, Kristen Pol, ronliskey. Log in to edit this page.

Using migrate-7.x-2.6-RC1.

The main purpose of this cookbook is to show how to import one content type in as many languages as you want using CSV import.

It assumes that you have one csv file per language, that each is in the csv format, and that the content for all its translations are at the same line. It also assumes you're using the Content Translation method of translation, and not entity translation.

Force using "" to avoid problem with language like russian chinese, japanese, arabic, etc...

Principle

  • You first import the content in your main language.
  • In this import, you set the tnid using the postImport method.
  • Then you import your content in the other language, and you set the tnid using the method gettnid which go in the main language migration table made by migrate to retrieve the tnid.

Structure of the module

my_module :

  • my_module.info
  • my_module.module
  • my_module.migrate.inc
  • my_module.install
  • includes
    • basic.inc
    • content_type_1.inc
    • content_type_2.inc
  • data_sources
    • Content_type_1
      • content_type_1.en.csv
      • content_type_1.fr.csv
      • ...
    • Content_type_2
      • content_type_2.en.csv
      • content_type_2.fr.csv
      • ...

Content_type_2 will be used in https://drupal.org/node/2218857.

my_module.info

name = my module
description = Module to import content from csv
core = 7.x

dependencies[] = migrate

files[] = my_module.migrate.inc
files[] = includes/basic.inc
files[] = includes/content_type_1.inc
files[] = includes/content_type_2.inc

my_module.module

The simplest part :)

my_module.migrate.inc

/**
 * Implements hook_migrate_api().
 */
function my_module_migrate_api() {

  module_load_include('inc', 'my_module', 'includes/basic');
  module_load_include('inc', 'my_module', 'includes/content_type_1');
  module_load_include('inc', 'my_module', 'includes/content_type_2');

  // Prepare migration per language
  $migrations = array();
  $language_list = language_list();

  foreach ($language_list as $language_prefix => $language) {
    // Need to remove '-'
    $language_prefix = str_replace('-', '_', $language_prefix);

    $migrations['ContentType1_' . $language_prefix] = array(
      'class_name' => 'ContentType1Migration',
      'group_name' => 'ContentType1Migrate',
      'language_prefix' => $language_prefix,
    );
    $migrations['ContentType2_' . $language_prefix] = array(
      'class_name' => 'ContentType2Migration',
      'group_name' => 'ContentType2Migrate',
      'language_prefix' => $language_prefix,
    );
  }

  $api = array(
    'api' => 2,
    'groups' => array(
      'ContentType1Migrate' => array(
        'title' => t('ContentType1 Migrations'),
      ),
      'ContentType2Migrate' => array(
        'title' => t('ContentType2 Migrations'),
      ),
    ),
    'migrations' => $migrations,
  );
  return $api;
}

my_module.install (optional)

/**
 * Implements hook_enable().
 */
function my_module_enable() {
  $language_list = language_list();

  foreach ($language_list as $language_prefix => $language) {
    // Need to remove '-'
    $language_prefix = str_replace('-', '_', $language_prefix);

    MigrationBase::registerMigration(
      'ContentType1Migration',
      'ContentType1_' . $language_prefix,
      array(
        'class_name' => 'ContentType1Migration',
        'group_name' => 'ContentType1Migrate',
        'language_prefix' => $language_prefix,
      )
    );
    MigrationBase::registerMigration(
      'ContentType2Migration',
      'ContentType2_' . $language_prefix,
      array(
        'class_name' => 'ContentType2Migration',
        'group_name' => 'ContentType2Migrate',
        'language_prefix' => $language_prefix,
      )
    );
  }
}

/**
 * Implements hook_disable().
 */
function my_module_disable() {
  $language_list = language_list();

  foreach ($language_list as $language_prefix => $language) {
    // Need to remove '-'
    $language_prefix = str_replace('-', '_', $language_prefix);

    Migration::deregisterMigration('ContentType1_' . $language_prefix);
    Migration::deregisterMigration('ContentType2_' . $language_prefix);
  }
}

basic.inc

abstract class my_module_Basic_Migration extends Migration {
  public function __construct($arguments) {
    parent::__construct($arguments);
    ini_set('auto_detect_line_endings', TRUE);
  }
}

content_type_1.inc

class ContentType1Migration extends my_module_Basic_Migration {
  public function __construct($arguments) {
    parent::__construct($arguments);
    $this->description = t('Import ContentType1 CSV-file');
    $columns = array(
      // "Source": ('Fieldname', 'Description')
      0 => array('csv_id', t('CSV id')),
      1 => array('title', t('Title')),
      ...
    );
    $this->source = new MigrateSourceCSV(
      DRUPAL_ROOT . '/' . drupal_get_path('module', 'my_module') . '/data_sources/Content_type_1/content_type_1.' . $arguments['language_prefix'] . '.csv',
      $columns,
      array(
        'header_rows' => 1,
        'embedded_newlines' => TRUE
      )
    );
    $this->destination = new MigrateDestinationNode('content_type_1');
    $this->map = new MigrateSQLMap($this->machineName,
      array(
        'csv_id' => array(
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => TRUE,
        )
      ),
      MigrateDestinationNode::getKeySchema()
    );
    if ($arguments['language_prefix'] != 'en') {
      $this->addSoftDependencies(array('ContentType1_en'));
    }

    // Mapped fields
    $this->addFieldMapping('title', 'title')
      ->defaultValue('');
    $this->addFieldMapping('language')
      ->defaultValue(str_replace('_', '-', $arguments['language_prefix']));
    // tnid
    if ($arguments['language_prefix'] != 'en') {
      $this->addFieldMapping('tnid', 'csv_id')
        ->sourceMigration('ContentType1_en');
    }
  }

  public function complete($entity, stdClass $row) {
    // Put the tnid for english content after it is created.
    if ($this->arguments['language_prefix'] == 'en') {
      $entity->tnid = $entity->nid;
      node_save($entity);
    }
  }
}

content_type_2.inc

Will be described in https://drupal.org/node/2218857.

Example files:

content_type_1.en.csv

"CSV ID","Title"
1,"Title 1"
2,"Title 2"
3,"Title 3"

content_type_1.fr.csv

"CSV ID","Title"
1,"Titre 1"
2,"Titre 2"
3,"Titre 3"

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

Comments

thtas’s picture

Here is my example of how to import both english and Japanese versions of the same field from the same CSV.
The english title is in the csv column "title" and a Japanese title is in column "titleJp"

This project had title module, entity_translation and translation modules installed


class CustomNodeMigration extends Migration {
  
  public function __construct($arguments) {
    parent::__construct($arguments);
    $this->path = '/tmp/custom_content.csv';
    $this->source = new MigrateSourceCSV($this->path, array(), array('header_rows' => 1, 'embedded_newlines' => 1));
    $this->destination = new MigrateDestinationNode('page');

    $this->map = new MigrateSQLMap($this->machineName,
      array(
        'id' => array(
          'type' => 'int',
          'not null' => TRUE,
          'description' => 'page ID',
          'alias' => 'm',
        )
      ),
      MigrateDestinationNode::getKeySchema()
    );
    
    $this->addFieldMapping('language')->defaultValue('en');
    
    //translatable title_field defined by title module
    //https://www.drupal.org/project/title
    $this->addFieldMapping('title_field', 'title');
    $this->addFieldMapping('title_field:language', 'title_language');
  }
  
  public function prepareRow($row) {
    if (parent::prepareRow($row) === FALSE) {
      return FALSE;
    }

    $row->title = array($row->title, $row->titleJp);
    $row->title_language = array('en', 'ja');
  
    return TRUE;
  }
  
  
  function complete($entity, stdClass $row) {
    
    //Save entity_translation information
    if(empty($entity->translations->data['ja'])) {
      $entity->translations->data['ja'] = array(
        'language' => 'ja',
        'status' => 1,
        'source' => $entity->translations->original,
      );
      $entity->tnid = $entity->nid;    
      node_save($entity);
    }
  }
}

nicrodgers’s picture

The patch at #929402-40: Add support for migrate module simplifies this somewhat.