mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-10 04:39:56 +02:00
feat(l10n): single-source localization pipeline
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.
This commit is contained in:
43
localization/src/types.ts
Normal file
43
localization/src/types.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/** Canonical localization data model. See ../README.md for the full spec. */
|
||||
|
||||
/** Output targets a string can be emitted to. */
|
||||
export type Target = "kmp" | "apple";
|
||||
export const ALL_TARGETS: Target[] = ["kmp", "apple"];
|
||||
|
||||
/** Argument types, used to pick the right platform placeholder token. */
|
||||
export type ArgType = "string" | "int" | "double";
|
||||
|
||||
export interface Arg {
|
||||
/** Name referenced inside translations as `{name}`. */
|
||||
name: string;
|
||||
type: ArgType;
|
||||
}
|
||||
|
||||
/** CLDR plural categories. `other` is always required when `plural` is used. */
|
||||
export type PluralCategory = "zero" | "one" | "two" | "few" | "many" | "other";
|
||||
export type PluralForms = Partial<Record<PluralCategory, string>> & {
|
||||
other: string;
|
||||
};
|
||||
|
||||
export interface StringEntry {
|
||||
/** Where the string appears / its purpose. Emitted as xcstrings comment + xml comment. */
|
||||
context: string;
|
||||
/** Defaults to all targets when omitted. */
|
||||
targets?: Target[];
|
||||
/** Ordered positional args referenced as `{name}` in translations. */
|
||||
args?: Arg[];
|
||||
/** Flat (non-plural) translations, keyed by language. Mutually exclusive with `plural`. */
|
||||
translations?: Record<string, string>;
|
||||
/** Plural translations, keyed by language then CLDR category. Mutually exclusive with `translations`. */
|
||||
plural?: Record<string, PluralForms>;
|
||||
}
|
||||
|
||||
export interface StringsFile {
|
||||
sourceLanguage: string;
|
||||
supportedLanguages: string[];
|
||||
strings: Record<string, StringEntry>;
|
||||
}
|
||||
|
||||
export function targetsOf(entry: StringEntry): Target[] {
|
||||
return entry.targets ?? ALL_TARGETS;
|
||||
}
|
||||
Reference in New Issue
Block a user