Skip to content
Twilight React Playground
ثيم رائدaren

useTranslation

hookBeginnerserverbrowserlive demo

Gives a component the translate function t, the page language, its text direction and the language name.

import { useTranslation, I18nContextValue } from '@salla.sa/twilight-theme-engine/i18n';

In plain words

A storefront speaks the shopper's language. Instead of writing "Cart" in a component, you write a key such as common.titles.cart, and t() (short for translate) returns the text for that key in the page's language: "سلة المشتريات" on /ar pages, "Shopping Cart" on /en pages.

useTranslation() is a hook: a function whose name starts with use, called at the top of a component. Besides t, it tells you the page's locale ('ar'), whether its text runs right-to-left (isRTL), and the language's name.

The texts come from two places: Salla's own messages, shared by every store, and your theme's locales/*.json files for the words only your theme uses.

Signature

function useTranslation(ns?: string | string[]): {
  t: TFunction;              // t(key) · t(key, 'Default text') · t(key, { name: 'Sara' })
  i18n: i18n;                // the i18next instance of the nearest I18nProvider
  ready: boolean;
  locale: string;            // 'ar'
  direction: 'ltr' | 'rtl';
  isRTL: boolean;
  isLTR: boolean;
  languageName: string;      // 'العربية', 'English', otherwise the code upper-cased
}

interface I18nContextValue {
  locale: string;
  direction: 'ltr' | 'rtl';
  isRTL: boolean;
  isLTR: boolean;
  languageName: string;
}

Try it live

t() from useTranslation(), reading Salla's messages and the playground's own locales/*.json for this page's language.Try this: pick common.titles.cart, then set the namespace to theme: only the theme files are searched and the key comes back. Then switch the ar / en pill (top bar, or the ☰ menu on a phone).
Storefront canvas · en · LTR

Hello from the theme's own translations

locale
en
direction
ltr (isRTL: false)
languageName
English
ready
true
Controls
Picks the plural form of playground.items.
The second argument of t(): shown for a missing key.
useTranslation() with no argument, or useTranslation("theme").
What a theme writes
import { useTranslation } from '@salla.sa/twilight-theme-engine/i18n';

export function Label() {
  const { t } = useTranslation();
  return <span>{t('playground.greeting')}</span>;
}

Example

app/components/cart/CartHeading.tsx
import { useTranslation } from '@salla.sa/twilight-theme-engine/i18n';

export function CartHeading({ count }: { count: number }) {
  const { t, locale, direction } = useTranslation();
  return (
    <h1 lang={locale} dir={direction} className="cart-heading">
      {t('common.titles.cart', 'Cart')} <small>({count})</small>
    </h1>
  );
}

How it behaves

  • It returns react-i18next's own useTranslation(ns) result (t, i18n, ready) spread together with the engine's language context, I18nContextValue.

  • t(key) searches two i18next namespaces in order: app, Salla's shared messages for the page language (downloaded by rootBeforeLoad, see translations), then theme, your locales/*.json (see Theme translations). When both have the key, Salla's text wins.

  • Keys are split on ., and a dotted key is also looked up whole, so nested JSON ({ "common": { "titles": { "cart": "…" } } }) and flat keys ("common.titles.cart": "…") both resolve.

  • A missing key returns the key itself, or the default you pass as the second argument. i18next's {{name}} interpolation is on, with escapeValue: false; React still escapes the text when it renders it.

  • ready is always true: every message is in memory before the page renders, and the instance is created with react: { useSuspense: false }, so nothing suspends.

  • locale, direction, isRTL, isLTR and languageName come from the instance's language through getLanguageInfo, so they describe the nearest I18nProvider: the page's, unless you nest one.

  • A key that names a group rather than a text, such as t('common.titles'), returns the sentence "key 'common.titles (ar)' returned an object instead of string."

  • Outside TwilightProvider it does not throw: the language fields are the context's Arabic defaults (ar, rtl, العربية), and t uses the last instance react-i18next registered, or returns keys when there is none.

Gotchas

  • t(key, { count }) does not pick a plural form from a Salla-style translation such as {0} no items|{1} one item|[2,*] :count items: i18next looks for key_one / key_other variants, finds none, and returns the whole string. Fix: formatSallaPlural(t(key), count) from @salla.sa/twilight-theme-engine/utils (see formatSallaPlural).

  • Salla's messages use Laravel placeholders, which i18next does not fill: t('common.elements.search_about', { word }) returns "Search for (:word)". Fix: replace the placeholder yourself, t('common.elements.search_about').replace(':word', word), as the engine's CartSummary does with :amount.

  • Choosing a namespace by prefix does not work: the instance has nsSeparator: false, so t('theme:lookbook.title') treats the whole string as a key and returns it. useTranslation('theme') does select a namespace, but then searches only your files, and Salla keys such as common.titles.cart come back as keys. Fix: call useTranslation() with no argument, and pass { ns: 'theme' } to one t call when you need your own text for a key Salla also has.

  • t(key, { lng: 'en' }) on an Arabic page still returns Arabic: the instance holds only the page language's messages (createI18nInstance loads <locale>.trans alone) and falls back to that language. Fix: link to the other language, or render a subtree with a nested I18nProvider.

  • t('some.key') || 'Default' never shows the default: a missing key returns the key, which is a non-empty string. Fix: pass the default as the second argument, t('some.key', 'Default').

Related

Source and docs