Mapping problem for multilingual terms with hierarchy.

Created on 12 April 2023, over 1 year ago
Updated 26 April 2023, over 1 year ago

Problem/Motivation

In a Drupal 10 website, I need to import nodes in two languages via Feeds. One of the node fields is a taxonomy term with max. 3 levels. Importing everything in one language works fine with the help of Feeds Tamper Term Hierarchy β†’ , but when trying to import them in both languages, things go south.

I've tried importing it with one importer, with mapping for the two languages in the same Feed Type, and tried as well to split them up. In the first case it creates new terms in French, but they get imported in the Dutch taxonomy list. When I split them up (with separate mapping for each language), the nodes don't link as translations.

The first option seems closest to the solution, but for now my only option is to manually edit and save more then 600 nodes. Help!

The only actual thing that needs to be translated, is the products description. All the other values are language independent or translated via the interface. But if I don't add the translated terms in my Json, no translation are created, so it seems.

Some more info: if I only add the Dutch taxonomy term and use two mappings for the description (for each language), the translated node is created, but as it has no taxonomy term, it results in this error when visiting the page:

Le site Web a rencontrΓ© une erreur inattendue. Veuillez essayer de nouveau plus tard.

Symfony\Component\Routing\Exception\InvalidParameterException: Parameter "taxonomy_term" for route "entity.taxonomy_term.canonical" must match "\d+" ("" given) to generate a corresponding URL. in Drupal\Core\Routing\UrlGenerator->doGenerate() (line 203 of core/lib/Drupal/Core/Routing/UrlGenerator.php).
Drupal\Core\Routing\UrlGenerator->getInternalPathFromRoute() (Line: 291)
Drupal\Core\Routing\UrlGenerator->generateFromRoute() (Line: 105)
Drupal\Core\Render\MetadataBubblingUrlGenerator->generateFromRoute() (Line: 198)
Drupal\Core\Template\TwigExtension->getPath() (Line: 241)
__TwigTemplate_e5b20beeb94972647e6e32e740c3ea71->doDisplay() (Line: 394)
Twig\Template->displayWithErrorHandling() (Line: 367)
Twig\Template->display() (Line: 379)
Twig\Template->render() (Line: 40)
Twig\TemplateWrapper->render() (Line: 53)
twig_render_template() (Line: 372)
Drupal\Core\Theme\ThemeManager->render() (Line: 433)
Drupal\Core\Render\Renderer->doRender() (Line: 204)
Drupal\Core\Render\Renderer->render() (Line: 238)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext() (Line: 239)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare() (Line: 128)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse() (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray()
call_user_func() (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch() (Line: 168)
Symfony\Component\HttpKernel\HttpKernel->handleRaw() (Line: 74)
Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 58)
Drupal\Core\StackMiddleware\Session->handle() (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass() (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle() (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 51)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle() (Line: 686)
Drupal\Core\DrupalKernel->handle() (Line: 19)

Steps to reproduce

- Setting up a taxonomy list with all used terms already in it (and translated).

- Create a Json file with this example structure. The values for both languages are in the same record. The final values for importing the term with hierarchy are "Categoriepad" and "Categoriepad_FR".

{
  "Products": [
{
      "ID": "1681241194-166",
      "Titel": "Chianti",
      "Beschrijving_NL": "Toonzaalmodel om mee te nemen.",
      "Beschrijving_FR": "Vertaling FR",
      "Prijs_nieuw": 149,
      "Prijs_oud": 704,
      "Hoofdcategorie": "Wonen",
      "Hoofdcategorie FR": "Habiter",
      "Subcategorie": "Tafels",
      "Subcategorie_FR": "Tables",
      "Sub-subcategorie": "Salontafels",
      "Sub-subcategorie_FR": "Tables de salon",
      "Categoriepad": "Wonen/Tafels/Salontafels",
      "Categoriepad_FR": "Habiter/Tables/Tables de salon",
      "Is_outlet": "Ja",
      "Afbeeldingen": "https://www.domain.be/sites/domain/files//product/image/20230103_105708.jpg",
      "Merk": "Editions",
      "Alt": "Chianti - Outlet",
      "Title": "Chianti - Outlet"
    }
  ]
}

- Create the feed. Feeds Tamper Term Hierarchy requires you to set the Term ID as reference in order for the hierarchy to work.

Here's the exported feed type:

uuid: 5b2a4a11-7b77-429f-b13f-6a00496907e7
langcode: nl
status: true
dependencies:
  config:
    - field.field.node.product.body
    - field.field.node.product.field_product_afbeeldingen
    - field.field.node.product.field_product_afmetingen
    - field.field.node.product.field_product_categorie
    - field.field.node.product.field_product_in_toonzaal
    - field.field.node.product.field_product_in_voorraad
    - field.field.node.product.field_product_is_outlet
    - field.field.node.product.field_product_materiaal
    - field.field.node.product.field_product_meerdere_kleuren
    - field.field.node.product.field_product_merk
    - field.field.node.product.field_product_oude_prijs
    - field.field.node.product.field_product_outletprijs
    - node.type.product
  module:
    - feeds_ex
    - feeds_tamper
    - node
third_party_settings:
  feeds_tamper:
    tampers:
      c32bdf45-e8ef-404a-892c-2d74f57b43e1:
        true_value: Ja
        false_value: Nee
        match_case: true
        no_match_value: false
        uuid: c32bdf45-e8ef-404a-892c-2d74f57b43e1
        plugin: convert_boolean
        source: stock
        weight: 0
        label: 'Convert to Boolean'
      2c3bc96f-4850-48cf-b183-319a3aa78eb1:
        true_value: Ja
        false_value: Nee
        match_case: true
        no_match_value: false
        uuid: 2c3bc96f-4850-48cf-b183-319a3aa78eb1
        plugin: convert_boolean
        source: in_toonzaal
        weight: 0
        label: 'Convert to Boolean'
      7095c7ff-095b-4de0-8668-baa4b82c3448:
        true_value: Ja
        false_value: Nee
        match_case: true
        no_match_value: false
        uuid: 7095c7ff-095b-4de0-8668-baa4b82c3448
        plugin: convert_boolean
        source: meerdere_kleuren
        weight: 0
        label: 'Convert to Boolean'
      dcebc3bc-9207-4036-aef8-1248d0265f58:
        uuid: dcebc3bc-9207-4036-aef8-1248d0265f58
        plugin: import_term_hierarchy
        source: categoriepad
        weight: 0
        label: 'Import Taxonomy Terms Hierarchy'
        delimiter: /
        vocabulary: tags
      3dbd7673-8d2f-4ba5-8c9a-1c82d4de31d1:
        separator: ;
        limit: null
        uuid: 3dbd7673-8d2f-4ba5-8c9a-1c82d4de31d1
        plugin: explode
        source: afbeeldingen
        weight: 0
        label: Explode
      177a34d6-3869-4e5d-8466-b64242185841:
        true_value: Ja
        false_value: Nee
        match_case: false
        no_match_value: false
        uuid: 177a34d6-3869-4e5d-8466-b64242185841
        plugin: convert_boolean
        source: is_outlet
        weight: 0
        label: 'Convert to Boolean'
label: Producten
id: csv
description: ''
help: ''
import_period: -1
fetcher: upload
fetcher_configuration:
  allowed_extensions: 'txt csv tsv xml opml json'
  directory: 'public://feeds'
parser: jsonpath
parser_configuration:
  context:
    value: '$.Products.*'
  source_encoding:
    - auto
  display_errors: false
  line_limit: 100
processor: 'entity:node'
processor_configuration:
  values:
    type: product
  langcode: nl
  insert_new: 1
  update_existing: 2
  update_non_existent: _delete
  skip_hash_check: false
  authorize: true
  revision: false
  expire: -1
  owner_feed_author: false
  owner_id: 2
custom_sources:
  titel:
    value: Titel
    label: Titel
    machine_name: titel
    type: json
  id:
    value: ID
    label: ID
    machine_name: id
    type: json
  beschrijving_nl:
    value: Beschrijving_NL
    label: Beschrijving_NL
    machine_name: beschrijving_nl
    type: json
  beschrijving_fra:
    value: Beschrijving_FR
    label: Beschrijving_FR
    machine_name: beschrijving_fra
    type: json
  meerdere_kleuren:
    value: Meerdere_kleuren
    label: Meerdere_kleuren
    machine_name: meerdere_kleuren
    type: json
  afbeeldingen_:
    value: 'Afbeeldingen.*'
    label: 'Afbeeldingen.*'
    machine_name: afbeeldingen_
    type: json
  alt:
    value: Alt
    label: Alt
    machine_name: alt
    type: json
  title:
    value: Title
    label: Title
    machine_name: title
    type: json
  subcategorie:
    value: Subcategorie
    label: Subcategorie
    machine_name: subcategorie
    type: json
  in_toonzaal:
    value: In_toonzaal
    label: In_toonzaal
    machine_name: in_toonzaal
    type: json
  stock:
    value: Stock
    label: Stock
    machine_name: stock
    type: json
  afwerking:
    value: Afwerking
    label: Afwerking
    machine_name: afwerking
    type: json
  merk:
    value: Merk
    label: Merk
    machine_name: merk
    type: json
  afmetingen:
    value: Afmetingen
    label: Afmetingen
    machine_name: afmetingen
    type: json
  categoriepad:
    value: Categoriepad
    label: Categoriepad
    machine_name: categoriepad
    type: json
  afbeeldingen:
    value: Afbeeldingen
    label: Afbeeldingen
    machine_name: afbeeldingen
    type: json
  is_outlet:
    value: Is_outlet
    label: Is_outlet
    machine_name: is_outlet
    type: json
  prijs_oud:
    value: Prijs_oud
    label: Prijs_oud
    machine_name: prijs_oud
    type: json
  prijs_nieuw:
    value: Prijs_nieuw
    label: Prijs_nieuw
    machine_name: prijs_nieuw
    type: json
  categoriepad_fr:
    value: Categoriepad_FR
    label: Categoriepad_FR
    machine_name: categoriepad_fr
    type: json
mappings:
  -
    target: nid
    map:
      value: id
    settings: {  }
    unique:
      value: '1'
  -
    target: field_product_meerdere_kleuren
    map:
      value: meerdere_kleuren
    settings:
      language: ''
  -
    target: field_product_in_toonzaal
    map:
      value: in_toonzaal
    settings:
      language: ''
  -
    target: field_product_in_voorraad
    map:
      value: stock
    settings:
      language: ''
  -
    target: field_product_materiaal
    map:
      target_id: afwerking
    settings:
      language: ''
      reference_by: name
      autocreate: '1'
  -
    target: title
    map:
      value: titel
    settings:
      language: nl
    unique: {  }
  -
    target: field_product_merk
    map:
      target_id: merk
    settings:
      language: ''
      reference_by: name
      autocreate: '1'
  -
    target: field_product_afmetingen
    map:
      value: afmetingen
    settings:
      language: nl
    unique: {  }
  -
    target: field_product_categorie
    map:
      target_id: categoriepad
    settings:
      language: nl
      reference_by: tid
      autocreate: 0
  -
    target: field_product_afbeeldingen
    map:
      target_id: afbeeldingen
      alt: alt
      title: title
    settings:
      language: nl
      reference_by: fid
      existing: '2'
      autocreate: 0
  -
    target: body
    map:
      value: beschrijving_nl
      summary: ''
    settings:
      language: nl
      format: plain_text
  -
    target: field_product_is_outlet
    map:
      value: is_outlet
    settings:
      language: ''
  -
    target: field_product_oude_prijs
    map:
      value: prijs_oud
    settings:
      language: null
    unique: {  }
  -
    target: field_product_outletprijs
    map:
      value: prijs_nieuw
    settings:
      language: null
    unique: {  }
  -
    target: title
    map:
      value: titel
    settings:
      language: fr
    unique: {  }
  -
    target: body
    map:
      value: beschrijving_fra
      summary: ''
    settings:
      language: fr
      format: plain_text
πŸ’¬ Support request
Status

Fixed

Component

Feeds Import (feature)

Created by

πŸ‡§πŸ‡ͺBelgium fbreckx Antwerp

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @fbreckx
  • πŸ‡§πŸ‡ͺBelgium fbreckx Antwerp
  • Status changed to Fixed over 1 year ago
  • πŸ‡³πŸ‡±Netherlands megachriz

    My first guess that this doesn't work is that Feeds Tamper Term Hierarchy doesn't have a configuration option for seleting a language for autocreating terms. So I assume it always creates terms in the default language.

    What I suggest to do instead is importing the terms separately, using a Taxonomy processor. With that you should be able to import the terms in multiple languages and also in a hierarchy, like so:

    If you can restructure the source as displayed above, you should be able to import terms hierarchically and have the Dutch and French terms linked to eachother.

    Mappings:
    name_nl > Name (default language, unique)
    name_fr > Name (french language)
    parent > Term Parents (reference by name, autocreate entities)

    That will result into this:
    Dutch:

    French:

    I also attached the feed type that I tested with as an example. I've used the CSV parser in there, but it could also work with the JSON parser.

    I hope this helps you further.

  • πŸ‡³πŸ‡±Netherlands megachriz

    Ow, I see you edited the issue summary while I wrote my response. Do you need further help or does my comment in #3 help you enough?

    When importing nodes, I think you would need to add mapping to taxonomy term twice, one for Dutch and one for French. For both, use the Dutch term as source. Also for both, referency by name. And I would suggest to turn off the option to autocreate entities.
    I haven't tried this yet though.

  • πŸ‡§πŸ‡ͺBelgium fbreckx Antwerp

    Hi Chris,

    Thanks for your quick response! I will try the above and get back to you as soon as possible.

    What I've just discovered: if I uncheck the option that users can translate the term field (when editing the node fields), then everything works as intended. It has a few other consequences on other stuff (especially with EVA), but the nodes are linked successfully, with translated terms!

    This is very confusing.

  • πŸ‡§πŸ‡ͺBelgium fbreckx Antwerp
  • πŸ‡³πŸ‡±Netherlands megachriz

    Btw, referencing by name is not an option, because referencing by Term ID is required by Feeds Tamper Term Hierarchy.

    If you import terms in the right hierarchy separately first (as explained in #3), then I think you don't need to use Feeds Tamper Term Hierarchy. You would just reference existing terms only on node imports. The hierarchy information would be no longer relevant, because it already exists then.

    This is very confusing.

    I have plans on documenting how to import translated content. Not sure if I'll get to that this year though. I first need to investigate myself again what all possibilities are. There is more than one way to import translated content.

  • πŸ‡§πŸ‡ͺBelgium fbreckx Antwerp

    Alright, I finally found the way to do this!

    First of all, I used only one importer. The body field and term field of the node was imported twice, like you said, with the Dutch hierarchical path as the source. I did use the Feeds Tamper Term Hierarchy plugin, so I set the the reference to Term ID.

    The result here was a bit unexpected; all Dutch nodes had the correct hierarchical term path, but their translations seemed to have skipped the root term. Strange! Luckily I found an option that let me 'Update Taxonomy Terms Parents'* at the content overview. After running this on all the French nodes, everything works like a charm!

    I totally forget where I found this plugin, it's not a seperate module, nor is it a patch. I'll update this post when I find it. Perhaps it comes with the CSHS module β†’ ?

  • Status changed to Fixed over 1 year ago
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024