···1414- [Development Guidelines](#development-guidelines)
1515 - [Code Style](#code-style)
1616 - [Commit Messages](#commit-messages)
1717+ - [Localization (i18n)](#localization-i18n)
1718 - [Project Structure](#project-structure)
1819- [Need Help?](#need-help)
1920···112113- `refactor:` - Code changes that don't add features or fix bugs
113114- `test:` - Adding or updating tests
114115- `chore:` - Maintenance tasks
116116+117117+### Localization (i18n)
118118+119119+Kaneo uses [i18next](https://www.i18next.com/) with [react-i18next](https://react.i18next.com/) in the web app. We want user-facing copy to stay consistent, translatable, and easy to maintain.
120120+121121+#### Approach
122122+123123+- Use translation keys for all user-facing strings instead of hardcoded copy
124124+- In React components, call `t("namespace:key.path")` from `useTranslation()`
125125+- Translation files live in [`i18n/`](./i18n) and use locale-style filenames such as `en-US.json` and `de-DE.json`
126126+- [`i18n/en-US.json`](./i18n/en-US.json) is the source of truth for translation keys
127127+- Locale selection comes from the signed-in user's saved preference when available, otherwise from the browser locale
128128+129129+#### i18n commands
130130+131131+The root `package.json` includes scripts to keep locale files in sync:
132132+133133+| Command | Description |
134134+| ------- | ----------- |
135135+| `pnpm i18n:check [locale]` | Compares `en-US.json` with the other locale files and reports missing or extra keys. You can filter by locale, for example `pnpm i18n:check de-DE`. |
136136+| `pnpm i18n:check:fix [locale]` | Adds missing keys to other locale files using the English source text from `en-US.json`. |
137137+| `pnpm i18n:report` | Scans `.ts` and `.tsx` files in the React app and reports missing keys, unused locale keys, and dynamic translation calls that static analysis cannot verify. |
138138+| `pnpm i18n:report:fix` | Removes unused keys from `en-US.json` and the other locale files. |
139139+| `pnpm i18n:schema` | Generates [`i18n/schema.json`](./i18n/schema.json) from `en-US.json` for editor and tooling validation. |
140140+141141+#### Adding a new locale
142142+143143+1. Create a new file in [`i18n/`](./i18n) using a locale code filename such as `fr-FR.json`.
144144+2. Copy [`i18n/en-US.json`](./i18n/en-US.json) and translate the values.
145145+3. Register the locale in [`i18n/resources.ts`](./i18n/resources.ts) by updating `supportedLocales` and `resources`.
146146+4. If the locale should be selectable in the UI, add it to the language picker in [`apps/web/src/routes/_layout/_authenticated/dashboard/settings/account/preferences.tsx`](./apps/web/src/routes/_layout/_authenticated/dashboard/settings/account/preferences.tsx).
147147+5. Run `pnpm i18n:check`, `pnpm i18n:report`, and `pnpm i18n:schema` before opening your PR.
148148+149149+#### Adding translations
150150+151151+1. Add the new key to [`i18n/en-US.json`](./i18n/en-US.json) first.
152152+2. Use it in code with a static key:
153153+154154+```tsx
155155+const { t } = useTranslation();
156156+157157+return <p>{t("common:actions.close")}</p>;
158158+```
159159+160160+3. Use interpolation for dynamic values instead of concatenating translated strings:
161161+162162+```tsx
163163+t("projects:greeting", { name: userName });
164164+```
165165+166166+4. Keep translation keys static. Calls like `t(someVariable)` or ``t(`tasks:${key}`)`` cannot be validated by the i18n report and will be flagged.
167167+168168+#### Translation key conventions
169169+170170+- Use namespaces at the top level such as `common`, `settings`, and `tasks`
171171+- Use dot notation inside each namespace, for example `settings.preferencesPage.title`
172172+- Keep keys descriptive and stable
173173+- Put widely shared labels in `common`
174174+- Group feature-specific keys under the feature or component they belong to
175175+176176+#### Contribution notes
177177+178178+- If you change UI copy, update the locale files in the same PR
179179+- If you remove or rename translation keys, run `pnpm i18n:report:fix` to keep locale files clean
180180+- If a locale is not fully translated yet, leaving the English source text as a placeholder is fine
115181116182### Project Structure
117183