Skip to content
better-i18n.com
Sur cette page

Complete API reference for the Remix & Hydrogen SDK.

Architecture #

@better-i18n/remix is a thin wrapper around @better-i18n/core that adds:

  • Request-based locale detection via Accept-Language header parsing
  • Singleton pattern with TtlCache for efficient CDN fetching across requests
  • Server-side translation loading — translations are loaded server-side and passed as loader data, with optional react-i18next integration for useTranslation() hooks

createRemixI18n #

The main entry point. Creates a singleton i18n instance for server-side use.

TypeScript
import { createRemixI18n } from "@better-i18n/remix";

const i18n = createRemixI18n({
  projectId: "my-company/web-app",
  defaultLocale: "en",
});

RemixI18nConfig extends I18nCoreConfig from @better-i18n/core:

OptionTypeDefaultDescription
projectstringRequiredProject identifier in org/project format
defaultLocalestringRequiredFallback locale when detection fails
logLevel"debug" | "info" | "warn" | "error" | "silent""warn"Logging verbosity
cdnBaseUrlstring"https://cdn.better-i18n.com"CDN base URL (for self-hosted setups)
manifestCacheTtlMsnumber300000Cache TTL in milliseconds for CDN manifest (5 min)
fetchTimeoutnumber10000CDN fetch timeout in milliseconds
retryCountnumber1Number of retry attempts on CDN fetch failure

The returned object exposes these methods:

PropertyTypeDescription
configI18nCoreConfigResolved configuration object
detectLocale(request)(request: Request) => Promise<string>Detect locale from Accept-Language header
getMessages(locale)(locale: string) => Promise<Messages>Get all messages for a locale from CDN
getLocales()() => Promise<string[]>Get available locale codes from manifest
getLanguages()() => Promise<LanguageOption[]>Get language options with metadata

RemixI18n Interface #

Detect the user's preferred locale from the Accept-Language header.

TypeScript
const locale = await i18n.detectLocale(request);
// "tr" (if Accept-Language contains Turkish)

How it works:

  1. Fetches available locales from CDN manifest via getLocales()
  2. Parses Accept-Language header with parseAcceptLanguage()
  3. Matches against available locales with matchLocale()
  4. Falls back to defaultLocale if no match
ParameterTypeDescription
requestRequestStandard Web API Request object

Returns: Promise<string> — The matched locale code

Fetch all translation messages for a locale. Returns namespaced messages from the CDN, cached via TtlCache.

TypeScript
const messages = await i18n.getMessages("tr");
// {
//   common: { welcome: "Hos geldiniz", shop_now: "Alisverise Basla" },
//   products: { from: "Baslayan fiyat" },
//   footer: { about: "Hakkimizda" }
// }
ParameterTypeDescription
localestringLocale code (e.g., "en", "tr")

Returns: Promise<Messages> — Namespaced translation messages

Get available locale codes from the CDN manifest.

TypeScript
const locales = await i18n.getLocales();
// ["en", "tr", "es", "fr", "de"]

Returns: Promise<string[]> — Array of available locale codes

Get language options with full metadata, useful for building language switcher UIs.

TypeScript
const languages = await i18n.getLanguages();
// [
//   { code: "en", name: "English", nativeName: "English", flagUrl: "..." },
//   { code: "tr", name: "Turkish", nativeName: "Turkce", flagUrl: "..." },
// ]

Returns: Promise<LanguageOption[]> — Array of language metadata objects


Utility Functions #

Parse an RFC 5646 Accept-Language header into a priority-sorted list of language tags.

TypeScript
import { parseAcceptLanguage } from "@better-i18n/remix";

const languages = parseAcceptLanguage("tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7");
// ["tr-TR", "tr", "en-US", "en"]

// Handles null/undefined gracefully
parseAcceptLanguage(null);
// []
ParameterTypeDescription
headerstring | null | undefinedRaw Accept-Language header value

Returns: string[] — Language tags sorted by quality factor (highest first)

Parsing rules:

  • Splits on commas, extracts language tag and quality factor
  • Quality factor defaults to 1.0 if not specified
  • Wildcard * entries are ignored
  • Invalid quality values are treated as 1.0

Find the best matching locale from a parsed language list against available locales.

TypeScript
import { matchLocale } from "@better-i18n/remix";

matchLocale(["tr-TR", "tr", "en-US"], ["en", "tr", "es"]);
// "tr" (base language match: "tr-TR" → "tr")

matchLocale(["ja", "ko"], ["en", "tr", "es"]);
// null (no match)
ParameterTypeDescription
languagesstring[]Priority-sorted language tags (from parseAcceptLanguage)
availableLocalesstring[]Available locale codes in your project

Returns: string | null — Best matching locale, or null if none found

Matching strategy (in priority order):

PriorityStrategyExample
1Exact match"tr-TR" matches "tr-TR"
2Base language"tr-TR" matches "tr" (strips region subtag)
3Region expansion"tr" matches "tr-TR" (first available variant)

Types #

Translation messages dictionary. Namespaced at the top level.

TypeScript
import type { Messages } from "@better-i18n/remix";

const messages: Messages = {
  common: {
    welcome: "Welcome",
    goodbye: "Goodbye",
  },
  errors: {
    notFound: "Page not found",
  },
};

Type: Record<string, unknown>

Language metadata from the CDN manifest.

TypeScript
import type { LanguageOption } from "@better-i18n/remix";

// {
//   code: "tr",
//   name: "Turkish",
//   nativeName: "Turkce",
//   flagUrl: "https://cdn.better-i18n.com/flags/tr.svg"
// }
PropertyTypeDescription
codestringLocale code (e.g., "en", "tr")
namestring | undefinedEnglish language name
nativeNamestring | undefinedNative language name
flagUrlstring | null | undefinedURL to flag image
isDefaultboolean | undefinedWhether this is the default/source language

Configuration type for createRemixI18n(). Alias for I18nCoreConfig.

TypeScript
import type { RemixI18nConfig } from "@better-i18n/remix";

Extends I18nCoreConfig — see config options table in the createRemixI18n section above.

Instance type returned by createRemixI18n().

TypeScript
import type { RemixI18n } from "@better-i18n/remix";

Exposes detectLocale(), getMessages(), getLocales(), and getLanguages() — see return type table in the createRemixI18n section above.

The following types are re-exported for convenience:

TypeScript
import type {
  I18nCoreConfig,
  LanguageOption,
  Messages,
  Locale,
  LogLevel,
  ManifestLanguage,
  ManifestResponse,
} from "@better-i18n/remix";

msg() #

String extractor for non-hook contextsmeta() functions, loaders, and utility modules where React hooks cannot be called. For component translations, use useTranslation() (i18next) or useTranslations() (use-intl) instead.

TypeScript
import { msg } from "@better-i18n/remix";

const common = messages.common;
msg(common, "welcome", "Welcome"); // → "Hoş geldiniz" or "Welcome"
msg(common, "missing_key");        // → ""
msg(undefined, "key", "fallback"); // → "fallback"
ParameterTypeDefaultDescription
nsRecord<string, unknown> | undefinedRequiredNamespace object from messages
keystringRequiredTranslation key
fallbackstring""Fallback value if key is missing or not a string

Returns: string — The translation value, or fallback


React Entrypoint (@better-i18n/remix/react) #

Optional entrypoint for projects that want a use-intl-based React provider instead of i18next. Provides a RemixI18nProvider, translation hooks, and a built-in LanguageSwitcher component.

Wraps your app with use-intl's IntlProvider and a context for locale/languages state.

TSX
import { RemixI18nProvider } from "@better-i18n/remix/react";

<RemixI18nProvider
  locale={locale}
  messages={messages}
  languages={languages}
  timeZone="Europe/Istanbul"
>
  <Outlet />
</RemixI18nProvider>
PropTypeDefaultDescription
localestringRequiredCurrent locale code
messagesMessagesRequiredTranslation messages from CDN
languagesLanguageOption[][]Available languages for switcher
timeZonestringIANA timezone for date/time formatting
nowDateFixed "now" for SSR consistency
onError(error: Error) => voidno-opError handler for missing translations
childrenReactNodeRequiredChild components

All hooks must be used within a RemixI18nProvider.

HookReturn TypeDescription
useTranslations(namespace?)(key: string) => stringGet a translation function (from use-intl)
useFormatter()IntlFormatterDate, number, and list formatting (from use-intl)
useMessages()MessagesAccess all messages in context
useNow()DateCurrent "now" value (for SSR hydration)
useTimeZone()stringCurrent timezone
useLocale()stringCurrent locale code
useLanguages()LanguageOption[]Available language list
useRemixI18n(){ locale, languages }Raw context value
TSX
import { useTranslations, useLocale } from "@better-i18n/remix/react";

export default function Header() {
  const t = useTranslations("common");
  const locale = useLocale();

  return <h1>{t("welcome")}</h1>;
}

Pre-built <select> component for switching languages. Navigates to the locale-prefixed URL automatically.

TSX
import { LanguageSwitcher } from "@better-i18n/remix/react";

<LanguageSwitcher locale={locale} defaultLocale="en" />
PropTypeDefaultDescription
localestringRequiredCurrent locale code
defaultLocalestring"en"Default locale (no URL prefix)
renderOption(lang) => ReactNodeCustom option renderer

Accepts all standard <select> props (className, aria-label, etc.).

Accessible dropdown component with flag emojis, native language names, and keyboard navigation. A more feature-rich alternative to LanguageSwitcher.

TSX
import { LocaleDropdown } from "@better-i18n/remix/react";

<LocaleDropdown />

Reads locale and languages from RemixI18nProvider context. Selecting a locale navigates to the locale-prefixed URL using useNavigate().

PropTypeDefaultDescription
defaultLocalestring"en"Default locale (no URL prefix)
variant"styled" | "unstyled""styled"Styled or unstyled mode
showFlagbooleantrueShow flag emoji/image
showNativeNamebooleantrueShow native language name
showLocaleCodebooleantrueShow locale code
renderTrigger(ctx) => ReactNodeCustom trigger renderer
renderItem(ctx) => ReactNodeCustom item renderer

For full props reference, CSS custom properties, and custom rendering — see LocaleDropdown.


i18next Entrypoint (@better-i18n/remix/i18next) #

Optional entrypoint for projects using i18next / react-i18next. Converts CDN translations to i18next resource format.

Builds a complete i18next-compatible configuration from CDN translations and manifest. Fetches all locales and returns resources, supported languages, and i18next init options.

TypeScript
import { createRemixI18n } from "@better-i18n/remix";
import { buildI18nextConfig } from "@better-i18n/remix/i18next";

const i18n = createRemixI18n({ projectId: "acme/store", defaultLocale: "en" });
const config = await buildI18nextConfig({ i18n });

// config.resources      — { en: { common: {...}, nav: {...} }, tr: {...} }
// config.supportedLanguages — ["en", "tr", "es"]
// config.fallbackLanguage   — "en"
// config.languages          — [{ code: "en", name: "English", ... }, ...]
// config.i18nextOptions     — { lowerCaseLng: true, defaultNS: "translation", ... }
ParameterTypeDescription
options.i18nRemixI18nInstance from createRemixI18n()
options.i18nextOptionsRecord<string, unknown>Additional i18next init options (merged with defaults)

Returns: Promise<{ resources, supportedLanguages, fallbackLanguage, languages, i18nextOptions }>

Converts Better i18n CDN format to i18next resources format. Optionally accepts a locale list (defaults to all locales from CDN manifest).

TypeScript
import { loadResources } from "@better-i18n/remix/i18next";

const resources = await loadResources(i18n);
// { en: { common: { welcome: "Welcome" }, translation: { common: {...} } } }

CDN format: { "namespace": { "key": "value" } } i18next format: { "locale": { "namespace": { "key": "value" } } }

ParameterTypeDescription
i18nRemixI18nInstance from createRemixI18n()
localesstring[]Optional locale list (defaults to all from manifest)

Returns: Promise<Record<string, Record<string, Record<string, unknown>>>> — i18next-compatible resources