mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 18:39:55 +02:00
88 lines
3.5 KiB
Markdown
88 lines
3.5 KiB
Markdown
# Localization
|
|
|
|
Single source of truth for every user-facing string, in [`strings.json`](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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```jsonc
|
|
{
|
|
"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
|
|
`.xcstrings` comment 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 with `plural`.**
|
|
- **`plural`** — per language, per [CLDR category](https://cldr.unicode.org/index/cldr-spec/plural-rules)
|
|
(`zero`, `one`, `two`, `few`, `many`, `other`). `other` is 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 becomes `count`). Rename for clarity;
|
|
keep the `{token}` in text in sync.
|
|
- Folding `transfer_file_count_one` / `_other` into the plural key `transfer_file_count`
|
|
requires switching the KMP call site from `Res.string.transfer_file_count_one` to the
|
|
Compose plural API (`pluralStringResource(Res.plurals.transfer_file_count, count, count)`),
|
|
and the Apple side to automatic plural inflection.
|