Problem/Motivation

Setting the "Published (status)" target against a source (which is tampered as "Convert to Boolean", only alters the default/English language node and leaves the translated node as published.

Steps to reproduce

Preface: it is assumed you have Drupal 9.5.9 installed with all needed language modules set up, and at least 1 additional language installed, with the article content type set to be translatable.

1. Create a new feed type using the "Upload file" fetcher, and "JsonPath" parser, Node processor, and article content type
2. Add the mapping as stated (per the attached JSON example):
- "Published (status)" target to "status" source
- "Published (status)" target to "status" source, configured setting set to French
- "Title (title)" to "title_english"
- "Title (title)" to "title_french", configured setting set to French
- "Body (body): Text" to "type"
3. Context should be set to "$.items.*"
4. Save and edit tamper
- On "status -> Published: value" add "Convert to Boolean" tamper
- Truth set to "Active", False set to "Inactive"
- Submit changes
4. Create new feed based on the type just created.
5. Import items
6. Observe the 2 nodes imported with translations. The item with the "Inactive" status, is still published while english is unpublished

Other steps tried:
1. Setting language on all "Published" targets
2. Only having 1 "Published" target, in hopes it would apply to all

Proposed resolution

If "Published (status)" set as target, feeds should respect the language being set on and update accordingly.

Current workaround/solution is to not allow the published value to be translated so what happens on the default language, persists to the translation.

Issue fork feeds-3371339

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

nicole.harnish created an issue. See original summary.

nicole.harnish’s picture

Issue summary: View changes
waleedagel’s picture

Same problem.

bandana’s picture

StatusFileSize
new775 bytes

I think i have the same problem with regular node feeds from csv.
2 languages (default english, secondary swedish), published target (english) to csv source (status_en), published target (swedish) to csv source (status_sv).
On first import all swedish nodes are set to default publishing option for content type. On second import (if values are changed in csv file) all values are as expected.

This patch seems to fix the issue.

bandana’s picture

Status: Active » Needs review
francismak’s picture

Thank you for this patch. It is working properly. How we tested:

1. Single feed CSV import to Node, English and French, with column TITLE_EN, TITLE_FR, PUBLISHED_EN and PUBLISHED_FR
2. Configure the mapping of all columns, and set the relevant node languages
3. Use 1 for True, 0 for False
4. No tamper plugin used

Be careful when using the tamper plugin "Set value or default value" with "Only if empty" enabled.
It treats 0(zero) as empty.

benstallings’s picture

Version: 8.x-3.0-beta4 » 8.x-3.x-dev
Status: Needs review » Needs work

Claude Code says, regarding patch #4:

The root cause

Here's the exact sequence that causes the bug. Consider a new node being imported with English status = 0 (unpublished) and French status = 0:

Clear phase (runs first for all mappings):
1. Clear status on base entity → field emptied
2. Clear status on French translation → translation doesn't exist yet, skipped

Set phase:
1. setTarget for English status: array_merge([], [['value' => 0]]) → [['value' => 0]] → unpublished. Correct.
2. setTarget for French status: getEntityTarget() calls addTranslation('fr'), which inherits field values from the base entity — including the just-set status = 0, or potentially the field's default value (status = 1, published). Then: array_merge([['value' => ]], [['value' => 0]]) produces a 2-element array. For a single-cardinality field like status, setValue() takes only the first element — the inherited value wins, the import value is discarded.

The core problem: clearTarget can't clear a translation that doesn't exist yet, but setTarget creates the translation and then merges with its inherited values. The imported value ends up in second position and gets dropped by cardinality enforcement.

Why isNew() is the wrong fix

1. It breaks multi-mapping to the same field on new entities.

Consider two mappings that both target the body field on a new node (e.g., one for summary, one for value, or two sources appending to the same multi-value field):

- Clear phase: field cleared
- Set mapping 1: with isNew() skip → setValue([val1])
- Set mapping 2: with isNew() skip → setValue([val2]) — val1 is lost

Without the patch, the array_merge correctly accumulates: [val1] → [val1, val2].

2. It doesn't fix the same bug on existing entities with new translations.

When an existing entity gets a new translation added during import, isNew() returns FALSE, so the merge still happens and the same value-inheritance problem occurs.

3. isNew() checks the entity, but the problem is the translation.

A new entity and a new translation are different things. The patch conflates them.

What should be done instead

The real fix belongs in the translation creation flow. When getEntityTranslation() creates a new translation via addTranslation(), the inherited values for the target fields should be cleared before setTarget merges with them. Two possible approaches:

Option A: Have clearTarget in the map() method run after translations are created, not before. This is a larger refactor.

Option B: In setTarget(), check whether the field list was actually cleared for this specific entity target. For example, after getting the translation, check if it's newly created and clear the specific target field before merging:

  $entity_target = $this->getEntityTarget($feed, $entity);
  if (!empty($entity_target)) {
      $item_list = $entity_target->get($field_name);

      // Only merge with existing values if this entity target
      // was cleared during the clear phase.
      if ($entity_target === $entity || $entity_target->hasTranslation(...)) {
          $values = array_merge($item_list->getValue(), $values);
      }

      $item_list->setValue($values);
  }

Or most directly: track in getEntityTranslation() whether the translation was just created, and return that information to the caller so it can skip the merge.

Verdict

The bug is real, but this patch introduces a regression for multi-mapping on new entities while only partially fixing the translation issue. I'd reject this specific approach and look for a fix that targets translation-value-inheritance directly.

benstallings’s picture

Assigned: Unassigned » benstallings

benstallings’s picture

Assigned: benstallings » Unassigned
Status: Needs work » Needs review