new·Earn with mozg — 20% of every monthSend somebody here and take a fifth of every plan payment they make, for as long as they keep paying — not a bounty on the first invoice. Your handle is the link, the window is thirty days, and the commission lands on your balance the second they pay. Free to join: if you have signed in, you already have the link. mozg.sh/earnall news →
mozg.beta
Sign in

Better Auth · Plugins · all subjects

i18n plugin

29 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

English error messages default

Better Auth already provides English error messages by default, so you only need to provide translations for other languages.

i18n plugin detection option

Option: detection. Type: Array<"header" | "cookie" | "session" | "callback">. Default: ["header"]. An array of detection strategies to use, in priority order. The first strategy that returns a valid locale will be used.

i18n plugin localeCookie option

Option: localeCookie. Type: string. Default: "locale". The name of the cookie to read when using the "cookie" detection strategy.

i18n plugin installation package

The i18n plugin is installed from the package @better-auth/i18n.

i18n plugin userLocaleField option

Option: userLocaleField. Type: string. Default: "locale". The field name on the user object that stores their locale preference when using the "session" detection strategy.

i18n plugin getLocale option

Option: getLocale. Type: (ctx: GenericEndpointContext) => string | null | Promise<string | null>. A custom function to detect the locale when using the "callback" detection strategy. Receives the full endpoint context. Returns the locale code or null.

i18n plugin purpose

The i18n plugin allows you to translate error messages returned by Better Auth based on the user's locale. It supports multiple locale detection strategies including HTTP headers, cookies, session data, and custom callbacks.

i18n plugin basic setup example

Basic setup requires passing a translations object to the i18n() function with locale codes as keys and translation dictionaries as values: i18n({ translations: { fr: { USER_NOT_FOUND: "Utilisateur non trouvé", INVALID_EMAIL_OR_PASSWORD: "Email ou mot de passe invalide", INVALID_PASSWORD: "Mot de passe invalide", }, de: { USER_NOT_FOUND: "Benutzer nicht gefunden", INVALID_EMAIL_OR_PASSWORD: "Ungültige E-Mail oder Passwort", INVALID_PASSWORD: "Ungültiges Passwort", }, }, })

Built-in locales available

The package ships with ready-to-use translations for 22 languages: ar (Arabic), bn (Bengali), de (German), en (English), es (Spanish), fa (Persian/Farsi), fr (French), hi (Hindi), id (Indonesian), it (Italian), ja (Japanese), ko (Korean), nl (Dutch), pl (Polish), pt (Portuguese), ru (Russian), sv (Swedish), th (Thai), tr (Turkish), uk (Ukrainian), vi (Vietnamese), and zh (Chinese Simplified).

Import and use all built-in locales example

To use all built-in translations: import { betterAuth } from "better-auth"; import { i18n, locales } from "@better-auth/i18n"; export const auth = betterAuth({ plugins: [ i18n({ translations: locales }), ], });

Import subset of locales example

To keep bundle lean, import only needed locales: import { betterAuth } from "better-auth"; import { i18n, locales } from "@better-auth/i18n"; export const auth = betterAuth({ plugins: [ i18n({ translations: { en: locales.en, fr: locales.fr, }, }), ], });

Override specific messages example

Spread a built-in locale and override specific messages: import { betterAuth } from "better-auth"; import { i18n, locales } from "@better-auth/i18n"; export const auth = betterAuth({ plugins: [ i18n({ translations: { ...locales, fr: { ...locales.fr, USER_NOT_FOUND: "Membre introuvable", }, }, }), ], });

Add custom locale example

Mix built-in locales with custom ones: import { betterAuth } from "better-auth"; import { i18n, locales } from "@better-auth/i18n"; import type { TranslationDictionary } from "@better-auth/i18n"; const myLocale: TranslationDictionary = { USER_NOT_FOUND: "...", INVALID_EMAIL_OR_PASSWORD: "...", }; export const auth = betterAuth({ plugins: [ i18n({ translations: { ...locales, xx: myLocale, }, }), ], });

Error response format with translations

When an error occurs and a translation is available, the response includes the error code, the translated message, and the originalMessage in English. Example: { "code": "INVALID_EMAIL_OR_PASSWORD", "message": "Email ou mot de passe invalide", "originalMessage": "Invalid email or password" }

Default locale detection strategy

By default, the plugin detects the locale from the Accept-Language HTTP header.

Locale detection strategies configuration

Available detection strategies are: header (uses Accept-Language HTTP header), cookie (reads locale from a cookie), session (reads locale from authenticated user's stored preference), and callback (uses a custom function). Strategies are checked in the priority order specified in the detection array.

Header detection with Accept-Language parsing

The plugin automatically parses the Accept-Language header, including quality values. For example, 'Accept-Language: fr-CA, fr;q=0.9, en;q=0.8' will first try fr-CA (mapped to fr), then fr, then en.

Cookie-based detection configuration

To use cookie-based detection, add 'cookie' to the detection strategies. The cookie name is configured with the localeCookie option (default is 'locale').

Cookie-based detection example

i18n({ translations: { /* ... */ }, detection: ["cookie", "header"], localeCookie: "lang", })

Session-based detection configuration

To detect locale from the user's session, configure the user schema with a locale field and set detection to include 'session'. The userLocaleField option specifies which field on the user object stores the locale preference (default is 'locale').

Session-based detection example

export const auth = betterAuth({ user: { additionalFields: { locale: { type: "string", required: false }, }, }, plugins: [ i18n({ translations: { /* ... */ }, detection: ["session", "header"], userLocaleField: "locale", }), ], });

Custom locale detection callback

For advanced use cases, provide a getLocale function that receives the full endpoint context. The callback should return the locale code or null.

Custom detection callback example

i18n({ translations: { /* ... */ }, detection: ["callback", "header"], getLocale: (ctx) => { if (!ctx.request) return null; const url = new URL(ctx.request.url); return url.searchParams.get("lang"); }, })

Custom detection callback ctx.request caveat

The ctx.request could be undefined for non-HTTP calls when using the callback detection strategy.

i18n plugin translations option

Option: translations. Type: Record<string, Record<string, string>>. Required: Yes. A dictionary of translations keyed by locale code. Each locale contains a mapping of error codes to translated messages. Since Better Auth already provides English messages, you typically only need to provide translations for other languages.

i18n plugin defaultLocale option

Option: defaultLocale. Type: string. Default: "en". The fallback locale to use when no locale can be detected or the detected locale is not in the translations.

i18n plugin fallback behavior

If a translation is not found for a specific error code in the detected locale, the built-in English message is kept. If the detected locale is not in the translations dictionary, the plugin falls back to defaultLocale. If no locale can be detected from any strategy, the plugin falls back to defaultLocale (defaults to "en"). Non-error responses are never modified by this plugin.

i18n plugin error codes

The plugin translates error messages based on error codes. Common error codes include: USER_NOT_FOUND (User not found), INVALID_EMAIL_OR_PASSWORD (Invalid email or password), INVALID_PASSWORD (Invalid password), CREDENTIAL_ACCOUNT_NOT_FOUND (Credential account not found), EMAIL_NOT_VERIFIED (Email not verified), SESSION_EXPIRED (Session expired).

Automatic error message translation

The plugin automatically detects the user's locale and translates error messages accordingly. When an error is returned, the response includes both the translated message and the original English message.

Give your agent this brain