# osy translate — the request for any model, and its answer read back

> There is no platform model. `osy translate --to sv,de` brings `locales/sv.osy` and `locales/de.osy` in step with the source and writes `locales/request.json` — every text still to translate, with its context — and `locales/prompt.md`, the instructions to hand any model (or translator) with it. `osy translate --apply answer.json` checks every answer with the compile's own rules and writes the good ones into the files.

<!-- id: i18n-translate · area: i18n · stability: stable · html: https://osysharp.com/reference/i18n/translate/ -->

## Summary        {#summary}
A round trip in three commands: ask, translate anywhere, apply.

```text syntax
osy translate --to sv            # locales/sv.osy in step; locales/request.json + locales/prompt.md written
                                 # → hand both to a model, save its reply as answer.json
osy translate --apply answer.json
osy translate                    # later: re-sync every language after the source changed, and say what is left
```

## Signature      {#signature}
```text
osy translate [PATH] [--to <languages>] [--apply <answer.json>] [--json]
```

## Description    {#description}
**`--to sv,de`** adds each language the project does not speak yet and syncs every locale file it has
([A language's words — locales/<language>.osy](https://osysharp.com/reference/i18n/translations/)): new texts arrive as `null`, changed ones as stale with their old translation beside them,
removed ones are dropped. A text whose only change is the NAME of a placeholder (`{ToString}` now `{n}`) keeps its
translation, translated, with the placeholder renamed in it — and a compile serves a translation keyed under the old
name even before the file is synced, so upgrading the platform never turns a page back into English (when two texts
fit one old key, as `From {ToString}` does for two labels, only the sync can carry it). A placeholder is named after the
value the text shows — `n.ToString() + " fields"` is `{n} fields` — never after the method that formats it. Run in a KIT's directory it translates the kit's own words; in an
APP's, the app's words plus its kits' words that the kits do not translate into that language (a kit's translation keyed
under an old placeholder name counts).

**One file per owner.** In an app, the app's own words are kept in `locales/<lang>.osy` and each kit's in
`locales/<lang>.<Package>.osy` — `locales/sv.Osysharp.WorkQueues.osy` — so no file grows with every kit an app adds (one
file per language passed the compiler's per-file ceiling in an app using eighteen kits). A language already kept in
one file is split the first time `osy translate` runs, its translations kept; a file whose owner no longer has words
is removed. Every `locales/*.osy` is read as one language's words together.

**The request** (`locales/request.json`, format `osy-translation-request/1`) lists each text owed a translation with
everything a translator needs — which is the whole point: a word translated without knowing where it stands is how
`.rc` files went wrong.

```json syntax
{
  "id": "9f3c0a51e2b47d10", "language": "sv",
  "source": "{count} items in your bag", "role": "text", "what": "text on the page",
  "placeholders": [{ "name": "count", "filledWith": "count", "type": "int" }],
  "plural": "count", "pluralForms": ["one", "other"],
  "where": { "component": "BagPage", "route": "/bag", "file": "model/pages/bag.osy", "line": 12 },
  "beside": ["Your bag", "Check out"],
  "maxLength": 30,
  "previously": { "source": "…", "translation": "…" }
}
```

**The prompt** (`locales/prompt.md`) says what those fields mean, the rules (keep every `{placeholder}`, one text per
plural form, plain text, the language's own conventions, the source's tone), and the exact shape to answer in:

```json syntax
{ "format": "osy-translation-answer/1", "translations": [
  { "id": "…", "language": "sv", "text": "Till kassan" },
  { "id": "…", "language": "sv", "plural": { "one": "{count} vara", "other": "{count} varor" } } ] }
```

**`--apply`** checks each answer against its source before writing it — the placeholders, the plural forms the
language needs, no markup, not empty — and reports every one it refuses (the exit code is 2 when any was refused).
Nothing half-checked reaches a file, and the compile checks the file again anyway.

Both working files are regenerated on every run, and git-ignored.

## See also       {#see-also}
- [A language's words — locales/<language>.osy](https://osysharp.com/reference/i18n/translations/) — the file it writes
- [Which words are translated — the compiler finds every text a reader sees](https://osysharp.com/reference/i18n/extraction/) — where the texts come from
- [A count's plural forms — one text per CLDR category](https://osysharp.com/reference/i18n/plurals/) — plural categories per language
