diff --git a/.changeset/mean-cars-appear.md b/.changeset/mean-cars-appear.md
new file mode 100644
index 0000000000..a4c6f13cb7
--- /dev/null
+++ b/.changeset/mean-cars-appear.md
@@ -0,0 +1,5 @@
+---
+'@lit/localize-tools': patch
+---
+
+Fix an issue where running `extract` on an existing translation target would rewrite the "id" for placeholders signifying the expression index, which breaks translation targets where the expressions need to be reordered.
diff --git a/packages/localize-tools/src/formatters/xlb.ts b/packages/localize-tools/src/formatters/xlb.ts
index 42b22b471b..704e363d75 100644
--- a/packages/localize-tools/src/formatters/xlb.ts
+++ b/packages/localize-tools/src/formatters/xlb.ts
@@ -130,14 +130,13 @@ class XlbFormatter implements Formatter {
}
indent(messagesNode, 2);
messagesNode.appendChild(messageNode);
- let phIdx = 0;
for (const content of contents) {
if (typeof content === 'string') {
messageNode.appendChild(doc.createTextNode(content));
} else {
- const {untranslatable} = content;
+ const {untranslatable, index} = content;
const ph = doc.createElement('ph');
- ph.setAttribute('name', String(phIdx++));
+ ph.setAttribute('name', String(index));
ph.appendChild(doc.createTextNode(untranslatable));
messageNode.appendChild(ph);
}
diff --git a/packages/localize-tools/src/formatters/xliff.ts b/packages/localize-tools/src/formatters/xliff.ts
index bdc72f6193..58fe01ae6a 100644
--- a/packages/localize-tools/src/formatters/xliff.ts
+++ b/packages/localize-tools/src/formatters/xliff.ts
@@ -278,14 +278,11 @@ export class XliffFormatter implements Formatter {
*/
private encodeContents(doc: Document, contents: Message['contents']): Node[] {
const nodes = [];
- // We need a unique ID within each source for each placeholder. The index
- // will do.
- let phIdx = 0;
for (const content of contents) {
if (typeof content === 'string') {
nodes.push(doc.createTextNode(content));
} else {
- nodes.push(this.createPlaceholder(doc, String(phIdx++), content));
+ nodes.push(this.createPlaceholder(doc, content));
}
}
return nodes;
@@ -293,10 +290,12 @@ export class XliffFormatter implements Formatter {
private createPlaceholder(
doc: Document,
- id: string,
- {untranslatable}: Placeholder
+ {untranslatable, index}: Placeholder
): HTMLElement {
const style = this.xliffConfig.placeholderStyle ?? 'x';
+ // We need a unique ID within each source for each placeholder. The index
+ // will do.
+ const id = String(index);
if (style === 'x') {
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#x
const el = doc.createElement('x');
diff --git a/packages/localize-tools/testdata/extract-xliff-merge/goldens/foo.ts b/packages/localize-tools/testdata/extract-xliff-merge/goldens/foo.ts
index 66fa0d946f..f71b418288 100644
--- a/packages/localize-tools/testdata/extract-xliff-merge/goldens/foo.ts
+++ b/packages/localize-tools/testdata/extract-xliff-merge/goldens/foo.ts
@@ -7,7 +7,9 @@
import {msg, str} from '@lit/localize';
const who = 'World';
+const kind = 'Small';
msg(str`I am translated. Hello ${who}.`, {desc: 'Description of translated'});
+msg(str`I am translated with swapped order. Hello ${kind} ish ${who}.`);
msg('I am not translated', {desc: 'Description of not translated'});
msg('I am translated with a note', {desc: 'Happy note'});
msg('My note needs to be migrated', {desc: 'Existing note'});
diff --git a/packages/localize-tools/testdata/extract-xliff-merge/goldens/xliff/es-419.xlf b/packages/localize-tools/testdata/extract-xliff-merge/goldens/xliff/es-419.xlf
index b19084e9ba..f13d163dd4 100644
--- a/packages/localize-tools/testdata/extract-xliff-merge/goldens/xliff/es-419.xlf
+++ b/packages/localize-tools/testdata/extract-xliff-merge/goldens/xliff/es-419.xlf
@@ -8,6 +8,10 @@
Estoy traducido. Hola ${who}.Description of translated
+
+ I am translated with swapped order. Hello ${kind} ish ${who}.
+ Estoy traducido con orden intercambiada. Hola ${who} más o menos ${kind}.
+ I am translated with a noteEstoy traducido con una nota
diff --git a/packages/localize-tools/testdata/extract-xliff-merge/input/foo.ts b/packages/localize-tools/testdata/extract-xliff-merge/input/foo.ts
index 66fa0d946f..f71b418288 100644
--- a/packages/localize-tools/testdata/extract-xliff-merge/input/foo.ts
+++ b/packages/localize-tools/testdata/extract-xliff-merge/input/foo.ts
@@ -7,7 +7,9 @@
import {msg, str} from '@lit/localize';
const who = 'World';
+const kind = 'Small';
msg(str`I am translated. Hello ${who}.`, {desc: 'Description of translated'});
+msg(str`I am translated with swapped order. Hello ${kind} ish ${who}.`);
msg('I am not translated', {desc: 'Description of not translated'});
msg('I am translated with a note', {desc: 'Happy note'});
msg('My note needs to be migrated', {desc: 'Existing note'});
diff --git a/packages/localize-tools/testdata/extract-xliff-merge/input/xliff/es-419.xlf b/packages/localize-tools/testdata/extract-xliff-merge/input/xliff/es-419.xlf
index a74885ab25..f813d86940 100644
--- a/packages/localize-tools/testdata/extract-xliff-merge/input/xliff/es-419.xlf
+++ b/packages/localize-tools/testdata/extract-xliff-merge/input/xliff/es-419.xlf
@@ -7,6 +7,10 @@
I was translated. Hello ${who}.Estoy traducido. Hola ${who}.
+
+ I am translated with swapped order. Hello ${kind} ish ${who}.
+ Estoy traducido con orden intercambiada. Hola ${who} más o menos ${kind}.
+ I am translated with a noteEstoy traducido con una nota