mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(i18n): require stable IDs for translation reuse
This commit is contained in:
@@ -1526,17 +1526,6 @@ export async function checkNativeLocaleArtifacts(
|
||||
);
|
||||
}
|
||||
|
||||
function choosePreviousTranslation(
|
||||
source: string,
|
||||
entries: readonly { translated: string }[],
|
||||
): string | undefined {
|
||||
const first = entries[0]?.translated;
|
||||
if (!first?.trim() || first === source) {
|
||||
return undefined;
|
||||
}
|
||||
return entries.every((entry) => entry.translated === first) ? first : undefined;
|
||||
}
|
||||
|
||||
export async function syncNativeLocale(
|
||||
locale: string,
|
||||
entries: NativeI18nEntry[],
|
||||
@@ -1561,38 +1550,11 @@ export async function syncNativeLocale(
|
||||
// The first refresh creates the locale artifact.
|
||||
}
|
||||
const previousById = new Map(previous.entries.map((entry) => [entry.id, entry]));
|
||||
const previousBySource = new Map<
|
||||
string,
|
||||
Array<{ id: string; source: string; translated: string }>
|
||||
>();
|
||||
for (const entry of previous.entries) {
|
||||
const candidates = previousBySource.get(entry.source) ?? [];
|
||||
candidates.push(entry);
|
||||
previousBySource.set(entry.source, candidates);
|
||||
}
|
||||
const currentIds = new Set(entries.map((entry) => entry.id));
|
||||
const currentSourceCounts = new Map<string, number>();
|
||||
for (const entry of entries) {
|
||||
currentSourceCounts.set(entry.source, (currentSourceCounts.get(entry.source) ?? 0) + 1);
|
||||
}
|
||||
const reusableBySource = new Map(
|
||||
[...previousBySource].map(([source, candidates]) => {
|
||||
const isCompleteIdChurn =
|
||||
candidates.length === currentSourceCounts.get(source) &&
|
||||
candidates.every((candidate) => !currentIds.has(candidate.id));
|
||||
return [
|
||||
source,
|
||||
isCompleteIdChurn ? choosePreviousTranslation(source, candidates) : undefined,
|
||||
] as const;
|
||||
}),
|
||||
);
|
||||
const reusableById = new Map(
|
||||
entries.map((entry) => {
|
||||
const exact = previousById.get(entry.id);
|
||||
const translated =
|
||||
exact?.source === entry.source && exact.translated.trim()
|
||||
? exact.translated
|
||||
: reusableBySource.get(entry.source);
|
||||
exact?.source === entry.source && exact.translated.trim() ? exact.translated : undefined;
|
||||
return [entry.id, translated] as const;
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -663,11 +663,10 @@ describe("native app i18n inventory", () => {
|
||||
const moved = await syncNativeLocale("sv", movedEntries, {
|
||||
glossary: [],
|
||||
translationsDir,
|
||||
translate: async () => {
|
||||
throw new Error("source-stable ID churn must reuse translation memory");
|
||||
},
|
||||
translate: async (pending) =>
|
||||
new Map(pending.map((entry) => [entry.id, `moved:${entry.source}`])),
|
||||
});
|
||||
expect(moved).toEqual({ changed: true, translated: 0 });
|
||||
expect(moved).toEqual({ changed: true, translated: 4 });
|
||||
const movedArtifact = JSON.parse(await readFile(artifactPath, "utf8")) as {
|
||||
entries: Array<{ id: string; source: string; translated: string }>;
|
||||
};
|
||||
@@ -675,10 +674,10 @@ describe("native app i18n inventory", () => {
|
||||
movedEntries.map((entry) => entry.id),
|
||||
);
|
||||
expect(movedArtifact.entries.map((entry) => entry.translated)).toEqual([
|
||||
"Hej",
|
||||
"Begärans-ID: \\(requestId)",
|
||||
"${apps.size} totalt, ${visibleApps.size} visas",
|
||||
"Av \\(total) behörigheter har \\(granted) beviljats",
|
||||
"moved:Hello",
|
||||
"moved:Request ID: \\(requestId)",
|
||||
"moved:Showing ${visibleApps.size} of ${apps.size}",
|
||||
"moved:\\(granted) of \\(total) permissions granted",
|
||||
]);
|
||||
|
||||
const refreshed = await syncNativeLocale("sv", entries, {
|
||||
|
||||
Reference in New Issue
Block a user