# A language's words — locales/<language>.osy

> A project's translations are checked-in source: one `locales/<language>.osy` per language, a `translations sv { }` block of `role "source" => "translation";` entries with their context above them. `osy translate` writes and keeps them in step; you own the words. The compile refuses a translation with the wrong placeholders, a missing plural form, or markup — at its line.

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

## Summary        {#summary}
One file per language per project — an app's or a kit's — at `locales/<language>.osy`. It is SOURCE: it rides every
compile, test and deploy with the rest of the project, a kit's archive carries its own, and a mistake in it is a compile
error at its line. `osy translate` writes it ([osy translate — the request for any model, and its answer read back](https://osysharp.com/reference/i18n/translate/)); the words are yours to edit.

## Signature      {#signature}
```osy
translations sv {
  /// Checkout · /checkout · a button's label · at most ~16 characters
  button "Check out" => "Till kassan";
  /// BagPage · /bag · placeholders: {count} = count (int) — {count} is a count
  text "{count} items in your bag" => { one: "{count} vara i din kasse", other: "{count} varor i din kasse" };
  text "Open" key "shop-status-open" => "Öppet";
  title "Your shopping bag" => null was "Your bag" => "Din kasse";
}
```

## Description    {#description}
**An entry** is keyed exactly as a message is ([Which words are translated — the compiler finds every text a reader sees](https://osysharp.com/reference/i18n/extraction/)): its ROLE, its source TEXT and, rarely, a `key`.
Its value is a text, one text per plural category (`{ one: …, other: … }`, [A count's plural forms — one text per CLDR category](https://osysharp.com/reference/i18n/plurals/)), or `null` — not
translated yet, so the reader sees the source text. The `///` lines above it are its context — where it is, what it
is, how long it may be, what its placeholders hold, what sits beside it — written by `osy translate` for whoever
translates the file cold.

**Kept in step, never regenerated.** A text the file translates keeps its translation. A new text arrives as `=> null`.
A text whose source changed (so its identity did) is matched to the entry it replaced — the same role in the same
component — and written STALE: `=> null was "old source" => "old translation"`, so the old words are right there as a
hint. A text no longer in the source is dropped.

**What the compile refuses**, at the translation's line, on the server and in `osy validate` alike:

| refusal | why |
|---|---|
| a placeholder the source does not have, or a source placeholder left out | the hole would show as `{name}` or the value would vanish |
| plural forms for a text with no count | nothing chooses between them |
| a plural form the language lacks, or one it needs missing | Swedish has `one` and `other`; Polish `one`, `few`, `many`, `other` |
| the same text translated twice in one language | which one wins would be an accident |
| markup — `<b>`, `<img …>`, `<!--` | a translation is TEXT, and is always shown as text |

**Precedence.** A kit ships `locales/*.osy` of its own words; an app's file carries its own words and, for a language
a kit does not speak, the kit's words too (`osy translate` puts them there, marked with the kit's name). By identity,
the app's entry wins — so an app overrides a kit's word by translating the same text and role itself.

**The languages an app speaks** are its source language (English, unless `app.SourceLanguage` says otherwise —
[app.SourceLanguage — the language the app is written in](https://osysharp.com/reference/i18n/source-language/)) and every language it has its OWN file for; a kit's extra languages are not offered on their
own, because the app's words would still be in its source language.

## Examples       {#examples}
```osy title="a page and its Swedish — checked against each other at compile time" test app=i18n-translations
[Route("/")]
[AllowAnonymous]
component Home() {
  int count = 2;
  action Pay() { Navigation.Go("/"); }
  render {
    Stack {
      Text("Your bag is empty.");
      Text($"{count} items in your bag");
      Button("Check out", onPress: Pay);
    }
  }
}

translations sv {
  text "Your bag is empty." => "Din kasse är tom.";
  text "{count} items in your bag" => { one: "{count} vara i din kasse", other: "{count} varor i din kasse" };
  button "Check out" => "Till kassan";
}
```

## See also       {#see-also}
- [osy translate — the request for any model, and its answer read back](https://osysharp.com/reference/i18n/translate/) — how the file is written and filled
- [A count's plural forms — one text per CLDR category](https://osysharp.com/reference/i18n/plurals/) — the forms a count takes
- [Which words are translated — the compiler finds every text a reader sees](https://osysharp.com/reference/i18n/extraction/) — which texts have entries
