From c71e1b97d9345203cc8ba88aad3475d8bbe8faf2 Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:41:41 +0200 Subject: [PATCH] feat(l10n): advertise app localizations to iOS The per-app language picker in iOS Settings only appears when the built app declares multiple localizations. Add the planned languages to the Xcode project's knownRegions (so Xcode compiles their .lproj from the String Catalog), and have `generate` keep CFBundleLocalizations in Info.plist in sync with supportedLanguages (currently en, fr). --- apple/VniDrop/Resources/Info.plist | 5 +++++ localization/src/commands/generate.ts | 26 ++++++++++++++++++++++++++ localization/src/config.ts | 6 ++++++ 3 files changed, 37 insertions(+) diff --git a/apple/VniDrop/Resources/Info.plist b/apple/VniDrop/Resources/Info.plist index f19dd0c..a3c74ad 100644 --- a/apple/VniDrop/Resources/Info.plist +++ b/apple/VniDrop/Resources/Info.plist @@ -2,6 +2,11 @@ + CFBundleLocalizations + + en + fr + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion diff --git a/localization/src/commands/generate.ts b/localization/src/commands/generate.ts index 6cff0bd..b51fb90 100644 --- a/localization/src/commands/generate.ts +++ b/localization/src/commands/generate.ts @@ -6,6 +6,7 @@ import { mkdirSync } from "node:fs"; import { join } from "node:path"; import { + APPLE_INFO_PLIST, APPLE_XCSTRINGS, kmpValuesDir, KMP_RESOURCES, @@ -79,6 +80,29 @@ function buildAndroid(doc: StringsFile, lang: string): ParsedAndroid { return out; } +// ---- Apple Info.plist CFBundleLocalizations --------------------------------- + +/** + * Keep the app's CFBundleLocalizations in sync with supportedLanguages so iOS + * advertises the languages (and shows the per-app language picker in Settings). + * Replaces an existing array or inserts one right after the root . + */ +async function syncInfoPlistLocalizations(langs: string[]) { + const plist = await Bun.file(APPLE_INFO_PLIST).text(); + const items = langs.map((l) => `\t\t${l}`).join("\n"); + const block = `\tCFBundleLocalizations\n\t\n${items}\n\t\n`; + + const existing = /\t*CFBundleLocalizations<\/key>\s*[\s\S]*?<\/array>\n/; + const next = existing.test(plist) + ? plist.replace(existing, block) + : plist.replace(/(\n)/, `$1${block}`); + + if (next !== plist) { + await Bun.write(APPLE_INFO_PLIST, next); + console.log(`Updated CFBundleLocalizations in ${APPLE_INFO_PLIST}`); + } +} + // ---- entry ------------------------------------------------------------------ export async function generate() { @@ -87,6 +111,8 @@ export async function generate() { await Bun.write(APPLE_XCSTRINGS, renderXcstrings(buildXcstrings(doc))); console.log(`Wrote ${APPLE_XCSTRINGS}`); + await syncInfoPlistLocalizations(doc.supportedLanguages); + for (const lang of doc.supportedLanguages) { const dir = join(KMP_RESOURCES, kmpValuesDir(lang, doc.sourceLanguage)); mkdirSync(dir, { recursive: true }); diff --git a/localization/src/config.ts b/localization/src/config.ts index 8b9a6fd..ace576f 100644 --- a/localization/src/config.ts +++ b/localization/src/config.ts @@ -12,6 +12,12 @@ export const APPLE_XCSTRINGS = join( "apple/VniDrop/Resources/Localizable.xcstrings", ); +/** Apple app Info.plist — its CFBundleLocalizations is kept in sync with supportedLanguages. */ +export const APPLE_INFO_PLIST = join( + REPO_ROOT, + "apple/VniDrop/Resources/Info.plist", +); + /** KMP / Compose Multiplatform resources root; one values[-lang]/strings.xml per language. */ export const KMP_RESOURCES = join( REPO_ROOT,