navree
Documentation

Localization

Front ends in several languages: <html lang dir> for right-to-left locales, API errors translated by their code with next-intl or @nuxtjs/i18n, and the data table's labels.

On this page

A front end on Navree speaks the user's language in two places: the text it owns (labels, buttons, empty states) and the refusals the API sends back. The packages leave your own text to the translation library you already use, next-intl in Next.js or @nuxtjs/i18n in Nuxt, and give you the two pieces they don't have: text direction and API errors keyed by code.

Right-to-left locales

direction(locale) is "rtl" for Arabic, Hebrew, Persian, Urdu, Pashto, Sindhi, Uyghur, Yiddish, Dhivehi, Sorani Kurdish, Kashmiri and Syriac, with or without a region (ar-AE), and follows a script subtag when there is one (pa-Arab is right to left, ku-Latn isn't). isRtl(locale) is the same as a boolean.

web/src/app/[locale]/layout.tsx

localeAttributes("ar-AE") is { lang: "ar-AE", dir: "rtl" }.

With dir="rtl" on <html>, layouts written with logical properties mirror on their own: Tailwind's ps-*, pe-*, ms-*, me-*, start-*, end-*, text-start and text-end, and the rtl: and ltr: variants for the rest (rtl:rotate-180 on a chevron). The data tables use logical properties throughout.

API errors by code

Every error from a Navree API carries a stable code (not_found, validation_failed, limit_reached, invoice_already_sent): see errors. The front end translates the code, not the English message. errorKey(error) names the message, errors.<code> (errors.network_error when the request got no answer, errors.unknown for anything else), and translateError(error, t) looks it up:

web/messages/fr.json
TSX

next-intl's t has t.has(key), which translateError uses to pick the code's message, then errors.unknown, then the English sentence.

OptionDefaultMeaning
has(key)the translator's t.hasWhether a key exists. Without one, the code's key is used as is.
prefixerrorsThe keys' prefix.
fallbackthe English sentence's own fallbackThe text when nothing better is known.

The message gets {status} and the API's {message} as values, for codes whose detail matters ("Your plan includes 5 seats."). Validation errors keep their field messages: applyFieldErrors puts them on the fields, and the API translates them itself when it runs localization.

The data table's labels

DataTable and <NavreeDataTable> take labels, so the table speaks the page's language, with numbers formatted for the locale:

TypeScript

Sending the locale to the API

The browser's Accept-Language header passes through the BFF to the API, which uses it to pick the language of its own messages. To send the app's chosen locale instead of the browser's, set the header on the typed client:

TypeScript

Fonts for other scripts

The theme's faces cover Latin, Latin Extended and Vietnamese, and Geist also Cyrillic. For Arabic or Devanagari, add a face that covers the script ahead of the system fallbacks, self-hosted from @fontsource like the others:

web/src/app/globals.css

The browser picks a face per character, so a page that mixes English names with Arabic text uses Geist for one and Noto Sans Arabic for the other.