mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 18:39:55 +02:00
Add localization/ — one strings.json is the source of truth for every
user-facing string, and a Bun CLI generates the platform files:
loc migrate rebuild strings.json from existing platform files (one-time)
loc validate structural checks (plural `other`, arg refs, coverage)
loc generate emit .xcstrings (Apple) + per-language strings.xml (KMP)
Canonical `{name}` placeholders are converted to each platform's positional
printf tokens using declared arg types. Plurals use CLDR categories and emit
native forms: xcstrings plural variations and Compose <plurals> blocks.
Migration folds the KMP `transfer_file_count_one`/`_other` pair into a single
plural key, so `transferFileCountResource` now returns the plural resource and
callers resolve it with pluralStringResource.
46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
/** Read/write Apple String Catalog (.xcstrings) — a single JSON file, all languages nested. */
|
|
|
|
export type XcState = "new" | "translated" | "needs_review" | "stale";
|
|
|
|
export interface XcStringUnit {
|
|
state: XcState;
|
|
value: string;
|
|
}
|
|
export interface XcVariations {
|
|
plural?: Record<string, { stringUnit: XcStringUnit }>;
|
|
}
|
|
export interface XcLocalization {
|
|
stringUnit?: XcStringUnit;
|
|
variations?: XcVariations;
|
|
}
|
|
export interface XcEntry {
|
|
comment?: string;
|
|
extractionState?: string;
|
|
localizations?: Record<string, XcLocalization>;
|
|
}
|
|
export interface XcCatalog {
|
|
sourceLanguage: string;
|
|
strings: Record<string, XcEntry>;
|
|
version: string;
|
|
}
|
|
|
|
export function parseXcstrings(json: string): XcCatalog {
|
|
return JSON.parse(json) as XcCatalog;
|
|
}
|
|
|
|
/** Stable, Xcode-compatible serialization: 2-space indent, keys sorted, trailing newline. */
|
|
export function renderXcstrings(cat: XcCatalog): string {
|
|
const sortDeep = (v: unknown): unknown => {
|
|
if (Array.isArray(v)) return v.map(sortDeep);
|
|
if (v && typeof v === "object") {
|
|
const out: Record<string, unknown> = {};
|
|
for (const k of Object.keys(v as Record<string, unknown>).sort()) {
|
|
out[k] = sortDeep((v as Record<string, unknown>)[k]);
|
|
}
|
|
return out;
|
|
}
|
|
return v;
|
|
};
|
|
return JSON.stringify(sortDeep(cat), null, 2) + "\n";
|
|
}
|