# Add translation keys (https://docs.salla.dev/twilight/react/recipes/add-translation-keys)



Use this whenever your theme shows text of its own, so it reads correctly in Arabic and English.

**Goal:** `pages.faq.title` shows the right words in each language.

Strings live in `locales/ar.json` and `locales/en.json` as flat, dot-separated keys. The `localesDir` option of [`twilightReact()`](https://docs.salla.dev/twilight/react/local-development/vite-plugin) bundles every `*.json` file in that folder, and the root route hands the result to `TwilightProvider`. At runtime the store's own translations are looked up first and your theme keys are the fallback. See [Languages and RTL](https://docs.salla.dev/twilight/react/guide/languages-and-rtl) and [useTranslation](https://docs.salla.dev/twilight/react/hooks/use-translation).

### Add the key to both files [#add-the-key-to-both-files]

The dots are part of the key; do not nest objects.

```json title="locales/en.json"
{
  "pages.faq.title": "Frequently asked questions",
  "pages.faq.items_count": "{{count}} questions"
}
```

```json title="locales/ar.json"
{
  "pages.faq.title": "الأسئلة الشائعة",
  "pages.faq.items_count": "{{count}} أسئلة"
}
```

### Use it with a fallback [#use-it-with-a-fallback]

The second argument is the fallback text, shown if the key is missing.

```tsx title="app/components/FaqHeader.tsx"
import { useTranslation } from '@salla.sa/twilight-theme-engine/i18n';

export function FaqHeader({ count }: { count: number }) {
  const { t } = useTranslation();
  return (
    <header>
      <h1>{t('pages.faq.title', 'Frequently asked questions')}</h1>
      <p>{t('pages.faq.items_count', '{{count}} questions', { count })}</p>
    </header>
  );
}
```

### Confirm the wiring [#confirm-the-wiring]

A new theme already has it, so only check it is still there.

```ts title="vite.config.ts"
twilightReact({ localesDir: './locales' })
```

```tsx title="app/routes/__root.tsx"
import themeTranslations from 'virtual:twilight/theme-translations';

<TwilightProvider translations={themeTranslations} layout={ThemeLayout}>
```

## Check it worked [#check-it-worked]

Open the page at `/en/faq` and `/ar/faq`: the heading changes language. If you see the fallback in both, a JSON file is invalid (first trap).

<Callout type="warn" title="Traps">
  * **A JSON syntax error empties every translation.** The plugin catches the parse error and returns an empty module, so all `t()` calls silently show their fallback. Validate the files.
  * **A key in one language only** shows the fallback (or the raw key) after a language switch.
  * **Store translations win.** If the same key exists upstream, your theme value is ignored. Rename yours, following the `pages.<page>.<thing>` and `common.<group>.<thing>` pattern.
  * **Always pass a fallback.** Without it a missing key renders as the raw key.
  * **Without `translations={themeTranslations}` on `TwilightProvider`,** none of your files load.
  * **The file name is the locale code.** Add `fr.json` for French.
  * **Interpolation is `{{name}}` and values are not HTML-escaped.** Never pass untrusted markup.
</Callout>

## Next [#next]

* [Languages and RTL](https://docs.salla.dev/twilight/react/guide/languages-and-rtl): direction and language switching.
* [Add a page](https://docs.salla.dev/twilight/react/recipes/add-a-page): where most new strings come from.
