# Conventions template

Every locale's `AGENTS.md` fills the table below. The rationale per row is the _why_ a translator needs once; the per-locale file states the _what_ for its language.

The framework enforces every row via per-locale data under [`packages/ui/src/i18n/tests/locales/<locale>/style.ts`](../../packages/ui/src/i18n/tests/locales/). When the test fires, the failure message names the rule and points at this file.

## The 14-row conventions table

| Row | Surface                  | What it controls                                                                                                             | Why                                                                                                                       |
| --- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| 1   | Pronoun (informal you)   | `du` / `tu` / `you` — never `Sie` / `vous`                                                                                   | One narrator across locales; formal pronouns put distance between Tale and the reader.                                    |
| 2   | Quotation marks (prose)  | EN: `"…"` / DE: `„…"` (low-9 + high-9) / DE-CH: `«…»` / FR: `« text »` with NBSP                                             | The local typographic convention is what native readers expect; ASCII straight quotes in German prose mark a translation. |
| 3   | Quotation marks (JSON)   | ASCII `"` in every locale                                                                                                    | JSON syntax requires it; escaping curly quotes is a tooling burden no one needs.                                          |
| 4   | Apostrophe (prose)       | EN/DE: ASCII `'` / FR: typographic `’`                                                                                       | French prose uses the typographic apostrophe (`l'équipe`); English and German never.                                      |
| 5   | Apostrophe (JSON & code) | ASCII `'` in every locale                                                                                                    | Same reason as row 3.                                                                                                     |
| 6   | Dates (prose)            | EN: ISO or "Month D, YYYY" / DE / DE-CH: `19.04.2026` / FR: `19/04/2026`                                                     | National date conventions. ISO is universal in code/frontmatter.                                                          |
| 7   | Dates (code / ISO)       | `2026-04-19` everywhere                                                                                                      | ISO 8601; appears in frontmatter and code blocks.                                                                         |
| 8   | Time (wall clock)        | EN: 12-hour (`9 am`) / DE / DE-CH / FR: 24-hour (`09:00`)                                                                    | Local convention.                                                                                                         |
| 9   | Decimal separator        | EN: `.` / DE / FR: `,` / DE-CH: `.` (Swiss exception)                                                                        | Thousand-separator pairs with it; mixing the pair is a tell.                                                              |
| 10  | Thousands separator      | EN: `,` / DE: `.` or thin space / DE-CH: `'` (apostrophe: `1'000`) / FR: NNBSP (` `, U+202F)                                 | Same as row 9.                                                                                                            |
| 11  | Currency                 | EN: `$100` (USD, prefix) / DE / FR: `100 €` (suffix, NBSP) / DE-CH: `CHF 100` (prefix)                                       | Local currency + symbol placement; check rejects symbols from other locales.                                              |
| 12  | Percent                  | EN: `5%` / DE / DE-CH / FR: `5 %` with NBSP                                                                                  | NBSP between number and `%` in the metric-using locales; check rejects regular space and missing space.                   |
| 13  | Spelling (ß)             | DE: `ß` after long vowels (`Straße`, `groß`, `schließen`) / DE-CH: never (`Strasse`, `gross`, `schliessen`)                  | Swiss German never uses ß; the Duden rule for ß in DE is well-defined.                                                    |
| 14  | Dashes                   | EN: em-dash unspaced (`a—b`) / DE / DE-CH / FR: em-dash spaced (`a — b`); en-dash for number ranges everywhere (`2010–2020`) | English uses tight em-dashes; the metric languages space them. Number ranges use en-dash universally.                     |

## Why these rules and not others

The list is the smallest set that catches the surface bugs that break the _feel_ of native text. Punctuation, currency, and spelling reads native or it doesn't. Beyond row 14 are conventions reviewers catch: sentence flow, idiomatic phrasing, register beyond pronouns. The check stops where reviewer judgment starts.

## Adding a convention row

Adding a row means:

1. Extend the `LocaleStyleConfig` type in [`packages/ui/src/i18n/tests/locales/types.ts`](../../packages/ui/src/i18n/tests/locales/types.ts) with the new field.
2. Add the value to every locale's `style.ts` (the type-checker forces this).
3. Add a check module under [`packages/ui/src/i18n/tests/checks/`](../../packages/ui/src/i18n/tests/checks/) that reads the field and emits findings.
4. Add a planted fixture under each applicable locale's `planted/` folder.
5. Append a row to the table above with the row's rationale.

The meta-test ([`packages/ui/src/i18n/tests/__meta__/meta.test.ts`](../../packages/ui/src/i18n/tests/__meta__/meta.test.ts)) verifies every check is registered and every locale config has its concern fields.
