How to translate menu items with migrate? I'm importing menu items from CSV using migrate, migrate_tools, migrate_plus and migrate_source_csv.

This is an example of how I'm doing it for the original language:

source:
  plugin: csv
  path: 'private://init/links_gl.csv'
  delimiter: ','
  enclosure: '"'
  header_row_count: 1
  keys:
    - id
  column_names:
    0:
      id: id
    1:
      source_id: source_id
    2:
      parent_id: parent_id
    3:
      menu_name: menu_name
    4:
      weight: weight
    5:
      title: title

  constants:
    path: 'entity:node/'

ids:
  id:
    type: string

process:
  title: title
  menu_name: menu_name
  nid:
    plugin: bbaa_migration_lookup
    migration: init_contents_gl
    source: source_id
  'link/uri':
    plugin: bbaa_concat
    source:
      - constants/path
      - '@nid'
  parent:
    plugin: menu_link_parent
    source:
      - parent_id
      - '@menu_name'
  external:
    plugin: default_value
    default_value: 0
  expanded:
    plugin: default_value
    default_value: 1
  enabled:
    plugin: default_value
    default_value: 1
  langcode:
    plugin: default_value
    default_value: 'gl'
  weight: weight

destination:
  plugin: entity:menu_link_content
  bundle: menu_link_content
  no_stub: true


It's working but now I need to import another CSV file with translated menu items.

I created another migration configuration for the translated language and I tried adding this code:

  id:
    plugin: migration
    source: id
    migration: init_links_gl
  content_translation_source:
    plugin: default_value
    default_value: 'gl'
  langcode:
    plugin: default_value
    default_value: 'es'

destination:
  plugin: entity:menu_link_content
  translations: true
  no_stub: true


But it doesn't work.

How can I translate menu items with migrate?
I don't see anything obviously wrong in your code, but it would have been a better idea to post the 2 full examples (and ideally, you should try first a simple example that can be imported without custom plugins)

Note that for the second YML file (the translation) you don't need to import most of the stuff: you basically need to process the id, the parent, and the title, no need for nid or 'link/uri'.

As the documentation for this is scarce, I'm posting a working example (using JSON, not CSV).

YML for the first migration:

id: menu
label: JSON feed of menu links
migration_group: menu
source:
  plugin: url
  data_fetcher_plugin: file
  data_parser_plugin: json
  urls:
    - modules/custom/migrate_custom/artifacts/menu_en.json
  item_selector: data
  fields:
    -
      name: mlid
      label: 'Unique Id coming from API'
      selector: id
    -
      name: country_migration_id
      label: 'Unique Country Id coming from API. Used to create the relation with the nodes'
      selector: country_migration_id
    -
      name: name
      label: 'Country name'
      selector: name
    -
      name: parent_id
      label: 'Parent menu link id'
      selector: parent
  constants:
    path: 'entity:node/'
  ids:
    mlid:
      type: string
process:
  menu_name:
    plugin: default_value
    default_value: 'main'
  nid:
    plugin: migration_lookup
    migration:
      - country
    source_ids:
      country:
        - country_migration_id
  title:
    plugin: get
    source: name
    language: 'en'
  'link/uri':
    plugin: concat
    source:
      - constants/path
      - '@nid'
  parent:
    plugin: menu_link_parent
    source:
       - parent_id
       - 'main'
  external:
    plugin: default_value
    default_value: 0
  expanded:
    plugin: default_value
    default_value: 0
  enabled:
    plugin: default_value
    default_value: 1
  langcode:
    plugin: default_value
    default_value: 'en'
destination:
  plugin: entity:menu_link_content
  bundle: menu_link_content
  no_stub: true
migration_dependencies:
  required:
    - country
dependencies:
  enforced:
    module:
      - migrate_custom


YML file for translation: 

id: menu_ca
label: JSON feed of menu links (Catalan)
migration_group: menu
source:
  plugin: url
  data_fetcher_plugin: file
  data_parser_plugin: json
  urls:
    - modules/custom/migrate_custom/artifacts/menu_ca.json
  item_selector: data
  fields:
    -
      name: mlid
      label: 'Unique Id coming from API. Used to match the original language of the menu links'
      selector: id
    -
      name: name
      label: 'Country name'
      selector: name
    -
      name: parent_id
      label: 'Parent menu link id'
      selector: parent
  ids:
    mlid:
      type: string
process:
  menu_name:
    plugin: default_value
    default_value: 'main'
  id:
    plugin: migration_lookup
    migration:
      - menu
    source_ids:
      menu:
        - mlid
  title:
    plugin: get
    source: name
    language: 'ca'
  parent:
    plugin: menu_link_parent
    source:
       - parent_id
       - 'main'
  external:
    plugin: default_value
    default_value: 0
  expanded:
    plugin: default_value
    default_value: 0
  enabled:
    plugin: default_value
    default_value: 1
  langcode:
    plugin: default_value
    default_value: 'ca'
destination:
  plugin: entity:menu_link_content
  bundle: menu_link_content
  no_stub: true
  translations: true
migration_dependencies:
  required:
    - country
    - menu
dependencies:
  enforced:
    module:
      - migrate_custom


menu_en.json:

{
    "data": [{
        "id": "1_CHE",
        "country_migration_id": "CHE",
        "name": "Switzerland",
        "parent": 0
    }]
}


menu_ca.json:

{
    "data": [{
        "id": "1_CHE",
        "country_migration_id": "CHE",
        "name": "Suïssa",
        "parent": 0
    }]
}