From f9a59209547d03b6adaef372c9e965bb2f752e8f Mon Sep 17 00:00:00 2001 From: Turtle-Hwan Date: Tue, 4 Aug 2026 02:04:47 -0700 Subject: [PATCH] fix(i18n): stop repairCatalog crashing on un-bootstrapped locale keys (#11728) en.json carries ~190 keys per locale that the locale catalogs have not been bootstrapped with yet, so every repair-locale-catalog run threw a TypeError before doing any work. Skip missing leaves instead. Split out of #11728 so the crash fix can land without the catalog regeneration, which still needs native-speaker review. Co-authored-by: Turtle-Hwan Co-authored-by: Orca --- ...ale-repair-catalog-missing-leaves.test.mjs | 30 +++++++++++++++++++ config/scripts/locale-translation-policy.mjs | 5 ++++ 2 files changed, 35 insertions(+) create mode 100644 config/scripts/locale-repair-catalog-missing-leaves.test.mjs diff --git a/config/scripts/locale-repair-catalog-missing-leaves.test.mjs b/config/scripts/locale-repair-catalog-missing-leaves.test.mjs new file mode 100644 index 000000000..65e142e51 --- /dev/null +++ b/config/scripts/locale-repair-catalog-missing-leaves.test.mjs @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest' + +import { repairCatalog } from './locale-translation-policy.mjs' + +// Regression: en.json routinely carries keys a locale catalog has not been bootstrapped with yet +// (~190 per locale at the time of writing), which crashed the whole repair run before it did any work. +describe('repairCatalog with un-bootstrapped keys', () => { + const enCatalog = { + auto: { + lib: { agent: { catalog: { '760bc6883d': 'Codex' } } }, + components: { untranslated: 'Continue', nested: { alsoMissing: 'Refresh' } } + } + } + + const translatedOnly = () => ({ auto: { lib: { agent: { catalog: { '760bc6883d': '사본' } } } } }) + + it('skips leaves the locale catalog is missing instead of throwing', () => { + for (const locale of ['ko', 'ja', 'zh', 'es']) { + const localeCatalog = translatedOnly() + expect(() => repairCatalog(enCatalog, localeCatalog, locale), locale).not.toThrow() + expect(localeCatalog.auto.components, locale).toBeUndefined() + } + }) + + it('still repairs the leaves that are present', () => { + const localeCatalog = translatedOnly() + expect(repairCatalog(enCatalog, localeCatalog, 'ko')).toBe(1) + expect(localeCatalog.auto.lib.agent.catalog['760bc6883d']).toBe('Codex') + }) +}) diff --git a/config/scripts/locale-translation-policy.mjs b/config/scripts/locale-translation-policy.mjs index cd579edb3..50a490dc1 100644 --- a/config/scripts/locale-translation-policy.mjs +++ b/config/scripts/locale-translation-policy.mjs @@ -551,6 +551,11 @@ export function repairCatalog(enCatalog, localeCatalog, locale) { for (const leaf of leaves) { const current = leaf.key.split('.').reduce((cursor, part) => cursor?.[part], localeCatalog) + // Why: en.json carries keys the locale catalog has not been bootstrapped with yet; repair only + // rewrites values that already exist, so skip instead of crashing on undefined. + if (typeof current !== 'string') { + continue + } const next = repairTranslatedValue({ key: leaf.key, enValue: leaf.value,