I've got a single MySQL table with mixed english/french content in each row. I'm trying to figure how to migrate this into a proper i18n configured Drupal site.

I can get Migrate to import the content into one language, but I want it to import it into both languages. There are 901 rows, so it should ultimately create 1802 nodes that are linked.

I just can't figure out how to set up the Migrate module to loop through twice & link the nodes.

EDIT: I used this and was able to merge the two:

public function postImport() {

// $ii should really be determined by $count_query
$ii = 2000;
for ($i = 1; $i < $ii; $i++) {
  // Confirm SQL in phpMyAdmin to verify
  $query = "SELECT n.nid, tid.field_bv_transfer_id_value
    FROM {field_revision_field_bv_transfer_id} tid
    INNER JOIN node n ON tid.entity_id = n.nid
    WHERE tid.field_bv_transfer_id_value = $i;";
  $result = db_query($query);

  // Reset for each import
  $currentRowCount = $current_translateid = 0;
  foreach ($result as $record) {
    if ($currentRowCount % 2 == 0) {
      $node = node_load($record->nid);
      $node->pathauto_perform_alias = FALSE;
      $node->tnid = $record->nid;
      $current_translateid = $record->nid;
    } else {
      $node = node_load($record->nid);
      $node->pathauto_perform_alias = FALSE;
      $node->tnid = $current_translateid;


  • 1
    I don't think you should use the postImport to add your translated nids, that'll mess up the migrate mapping (i.e. you won't be able to roll it back). Doing it as two separate migrate scripts within the same group would be the right way to do it, and using the 'sourceMigration' method allows you to add the tnid to the second migration to solve the question of linking the translations together.
    – Alan Dixon
    Feb 13, 2015 at 14:27

3 Answers 3


You can create two migrations, both with the same mapping (except for the nids), but one saves nodes in English and the second in French.

  • 1
    True, but how do I link the two? I've got some rough code here, but I know it's possible to do it all at once. pastebin.com/ap1P5DGY I think that the docs here are missing something for me - drupal.org/node/1132582 - in the prepareRow() what's returned? The linking can be done with postImport(). Feb 11, 2012 at 23:41
  • I have to do some migrate stuff tomorrow so I will take a look. I think you need to look at the mapping between the two migrations which has a record of the nids that have been imported and the original content id.
    – acouch
    Feb 15, 2012 at 23:48

In prepareRow() you return either true or false. This determines whether that row is processed in that particular migration (and counted even).

This way, you can detect the language for each row, and only return TRUE for rows that contain content in the specific language for that migration.

so you could do something like:

public function prepareRow($row){
  $return = FALSE
  if ($row->lang == "fr"){
   $return = TRUE;
  // Only rows with a source 'lang' value of 'fr' are processed
  return $return;

An even more performant way to do this, if you are going to do the dual migrations thing, would be to add a condition() to each source query (if you are using MigrateSourceSQL), such as ->condition('lang','en','=').


(The following applies to Drupal 7 — I don't know about Drupal 6 or before.)
I presume you want to define the relation of translation between English and French nodes. To do that, first, each node should have the defined language, as defined in prepareRow():

$this->addFieldMapping('language', 'language_code');
$row->lang_dest = 'fr'; // or "en", depending on the row.

Second, you need to somehow define the tnid of the source-node to be its own nid and the tnid of the translation node to be the nid of the source node. Note you can choose the random language for the source node, and so it is even acceptable to mix up the source-language between different contents. The question is how.
(Note I think that is all you need, but I might be wrong. I followed the steps in the second case below, and succeeded.)

If you explicitly specify the node-number (=nid) of each row in your migration, then it is easy, because you know which row corresponds to which nid, even before you import those nodes. So, you can just set tnid of each row as such. Obviously, you must be careful not to conflict the imported nid with any of the existing nids in the Drupal contents.

If you let Drupal decide the nid of each imported row, then it is more tricky. I did with the 2 steps. First, I imported all the source-language rows, adding a custom field to identify it as the source node for the later use. Second, I imported the translated-language rows and set up all the tids of both source and translated-language nodes. These two steps can be completely different modules, but are perhaps handier if you define those two as separate classes in the same (migration) group in the varialbe $api in your Your_ModuleName.migrate.inc.

For the second step for the translated language, I wrote as follows. In short, it finds the source-language node with the SQL query, based on the custom field field_original_html_filename, which was defined when it was imported.

// In prepareRow()
//   Set up tnid, obtaining the nid from the node already imported.
    $this->addFieldMapping('tnid', 'row_tnid');
    $field_name = 'field_original_html_filename';
    $query = sprintf("SELECT n.entity_id FROM {field_data_%s} n WHERE n.%s_value = '%s'",
                     $field_name, $field_name, $fbasename_trans);     // entity_id == nid of Node
    $result = db_query($query);
    $nid_trans = $result->fetchCol()[0];
    $row->row_tnid = $nid_trans;      // In my case, it is guaranteed there is only one candidate.

// In prepare()
//   Forcibly set up (Change) tnid of the node already imported.
  public function prepare(&$node, $row) {
    if (isset($node->tnid) && ($source = node_load($node->tnid))) {
      $node->translation_source = $source;

That is all. I am not surprised if there would be an easier or better way, but it worked for me. Anyway an advantage to set up the translations during migration is you can always rollback. As a reference my entire migration code (for 2 languages, from the static HTML files) is available at GitHub:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.