From 263d2a2a91c46b7134a593d2da53efd68ffb49d0 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 26 Aug 2026 13:24:48 +0800 Subject: [PATCH] fix(i18n): include concatenated Apple UI strings (#129882) * fix(i18n): include concatenated Apple UI strings * test(i18n): cover multiline Apple modifiers --- scripts/apple-app-i18n.ts | 18 ++++++------------ test/scripts/apple-app-i18n.test.ts | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/scripts/apple-app-i18n.ts b/scripts/apple-app-i18n.ts index e6b81a5b86de..54a2ee4b5bdb 100644 --- a/scripts/apple-app-i18n.ts +++ b/scripts/apple-app-i18n.ts @@ -25,16 +25,6 @@ const IOS_SOURCE_PREFIXES = [ SHARED_CHAT_UI_SOURCE_PREFIX, "apps/shared/OpenClawKit/Sources/OpenClawKit/", ] as const; -const APPLE_CATALOG_KINDS = new Set([ - "conditional-branch", - "ui-call", - "ui-call-multiline", - "ui-localized-call", - "ui-localized-call-multiline", - "ui-modifier", - "ui-named-argument", - "ui-named-argument-multiline", -]); const IOS_CATALOG_EXCLUSIONS = new Set([ // Product names and preview-only single-character fixtures are intentionally verbatim. "OpenClaw", @@ -574,13 +564,17 @@ async function readOptionalFile(filePath: string): Promise { } } +function isAppleCatalogKind(kind: string): boolean { + return kind === "conditional-branch" || kind.startsWith("ui-"); +} + function isIosCatalogEntry(entry: NativeSourceEntry): boolean { return ( entry.surface === "apple" && entry.sites.some( (site) => IOS_SOURCE_PREFIXES.some((prefix) => site.path.startsWith(prefix)) && - APPLE_CATALOG_KINDS.has(site.kind), + isAppleCatalogKind(site.kind), ) && (!entry.source.includes("\\(") || isInflectedCountSource(entry.source)) && !IOS_CATALOG_EXCLUSIONS.has(entry.source) @@ -593,7 +587,7 @@ function isMacosCatalogEntry(entry: NativeSourceEntry): boolean { entry.sites.some( (site) => MACOS_SOURCE_PREFIXES.some((prefix) => site.path.startsWith(prefix)) && - APPLE_CATALOG_KINDS.has(site.kind), + isAppleCatalogKind(site.kind), ) && !entry.source.includes("\\(") && !MACOS_CATALOG_EXCLUSIONS.has(entry.source) diff --git a/test/scripts/apple-app-i18n.test.ts b/test/scripts/apple-app-i18n.test.ts index a5420266cf67..ef87e4910c8a 100644 --- a/test/scripts/apple-app-i18n.test.ts +++ b/test/scripts/apple-app-i18n.test.ts @@ -222,6 +222,22 @@ describe("Apple app i18n catalogs", () => { }); it("routes merged sites by coupled path and kind while preserving shipped translations", () => { + const coveredMacosEntries = [ + { kind: "ui-call-concatenated", source: "Call concatenated" }, + { + kind: "ui-localized-call-concatenated", + source: + "Older generated approvals are inactive because they were not tied to a working directory. Manual rules are unchanged.", + }, + { kind: "ui-modifier-concatenated", source: "Modifier concatenated" }, + { kind: "ui-modifier-multiline", source: "Modifier multiline" }, + { kind: "ui-named-argument-concatenated", source: "Named argument concatenated" }, + ].map(({ kind, source }, index) => ({ + id: `native.apple.concatenated.${index}`, + source, + surface: "apple", + sites: [{ kind, path: "apps/macos/Sources/OpenClaw/Example.swift" }], + })); const inventory = { version: 2, entries: [ @@ -243,6 +259,7 @@ describe("Apple app i18n catalogs", () => { { kind: "ui-call", path: "outside/Example.swift" }, ], }, + ...coveredMacosEntries, ], }; const existing = { @@ -278,6 +295,10 @@ describe("Apple app i18n catalogs", () => { }); expect(ios.catalog.strings?.["Do not catalog"]).toBeUndefined(); expect(macos.catalog.strings?.["Connect now"]).toBeDefined(); + expect(Object.keys(macos.catalog.strings ?? {})).toEqual( + expect.arrayContaining(coveredMacosEntries.map((entry) => entry.source)), + ); + expect(macos.catalog.strings?.["Do not catalog"]).toBeUndefined(); expect(ios.contradictions).toEqual([]); });