Add translation keys

Add Arabic and English strings to your theme and use them in components.

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() 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 and useTranslation.

Add the key to both files

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

locales/en.json
{
  "pages.faq.title": "Frequently asked questions",
  "pages.faq.items_count": "{{count}} questions"
}
locales/ar.json
{
  "pages.faq.title": "الأسئلة الشائعة",
  "pages.faq.items_count": "{{count}} أسئلة"
}

Use it with a fallback

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

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

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

vite.config.ts
twilightReact({ localesDir: './locales' })
app/routes/__root.tsx
import themeTranslations from 'virtual:twilight/theme-translations';

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

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).

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.

Next

On this page