mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
The core initializes asynchronously at launch, so for a moment the transfer lists look empty and the app feels stalled. Show a full-window overlay (centered spinner + "Starting…") as the top layer of the root stack while coreState.isInitialized is false; it fades out once the core is ready.
Localization
Single source of truth for every user-facing string, in strings.json.
A Bun CLI generates the platform-native files from it:
| Target | Output | Notes |
|---|---|---|
apple |
apple/VniDrop/Resources/Localizable.xcstrings |
one catalog, all languages nested |
kmp |
shared/src/commonMain/composeResources/values[-lang]/strings.xml |
one file per language |
Workflow
# From the repository root:
make check-localization # structural checks (run before committing)
make localization # regenerate .xcstrings + strings.xml from strings.json
make localization-migrate # one-time: rebuild strings.json from platform files
Never edit the generated .xcstrings / strings.xml by hand — edit strings.json and
regenerate. Regenerated output is deterministic (sorted keys), so diffs stay small.
strings.json format
{
"sourceLanguage": "en",
"supportedLanguages": ["en", "fr"],
"strings": {
"send_title": {
"context": "Send tab — screen title.",
"translations": { "en": "Send", "fr": "Envoyer" }
},
"send_selected_files_count": {
"context": "Send flow — number of files chosen before creating a transfer.",
"targets": ["kmp", "apple"],
"args": [{ "name": "count", "type": "int" }],
"plural": {
"en": { "one": "{count} file selected", "other": "{count} files selected" },
"fr": { "one": "{count} fichier sélectionné", "other": "{count} fichiers sélectionnés" }
}
}
}
}
Fields
context(required) — where the string appears and its purpose. Emitted as the.xcstringscomment and an XML comment; also the note translators see.targets(optional) —["kmp", "apple"]. Omit to mean all targets.args(optional) — ordered list of{ name, type },type∈string | int | double. Referenced in text as{name}.translations— flat text per language. Mutually exclusive withplural.plural— per language, per CLDR category (zero,one,two,few,many,other).otheris always required.
Placeholders
Write named tokens {count}, {name} in text. The generator converts them to the right
positional token per platform, using the declared type:
| type | Apple | Android/KMP |
|---|---|---|
string |
%N$@ |
%N$s |
int |
%N$d |
%N$d |
double |
%N$f |
%N$f |
A literal % in text is emitted as %% whenever the string has args.
Adding a language
Add its code to supportedLanguages, fill in translations / plural for each key, then
generate. KMP gets a new values-<lang>/strings.xml; Apple gets the language inside the
single catalog. validate warns about any key still missing that language.
Migration notes (from the initial import)
- Apple keys that were literal English strings (
"%@ · %@") were imported verbatim — rename them to semantic keys and update the Swift call sites. - Arg names default to
arg1,arg2… (a lone int arg becomescount). Rename for clarity; keep the{token}in text in sync. - Folding
transfer_file_count_one/_otherinto the plural keytransfer_file_countrequires switching the KMP call site fromRes.string.transfer_file_count_oneto the Compose plural API (pluralStringResource(Res.plurals.transfer_file_count, count, count)), and the Apple side to automatic plural inflection.