Alpha
The Lingo.dev Compiler is in alpha. It is unstable, not recommended for production use, and APIs may change between releases.
Custom locale resolvers let you override how the Lingo.dev Compiler detects and persists the user's locale. By default, the compiler uses cookie-based persistence configured via the localePersistence option. For more control - URL-based routing, header detection, localStorage, or any custom logic - create resolver files in the .lingo/ directory.
Resolver files#
The compiler looks for two optional files:
| File | Environment | Exports |
|---|---|---|
.lingo/locale-resolver.server.ts | Server-side (SSR, RSC) | resolveLocale(request: Request): string |
.lingo/locale-resolver.client.ts | Client-side (browser) | resolveLocale(): string and persistLocale(locale: string): void |
If a resolver file exists, the compiler uses it instead of the default cookie-based behavior. If only one file exists, the other environment falls back to the default.
Server-side resolver#
The server resolver receives the incoming Request object and returns a locale code string:
// .lingo/locale-resolver.server.ts
export function resolveLocale(request: Request): string {
const url = new URL(request.url);
// Check URL path prefix: /es/about -> "es"
const pathLocale = url.pathname.split("/")[1];
const supportedLocales = ["en", "es", "de", "fr", "ja"];
if (supportedLocales.includes(pathLocale)) {
return pathLocale;
}
// Fall back to Accept-Language header
const acceptLanguage = request.headers.get("Accept-Language");
if (acceptLanguage) {
const preferred = acceptLanguage.split(",")[0].split("-")[0];
if (supportedLocales.includes(preferred)) {
return preferred;
}
}
return "en";
}Client-side resolver#
The client resolver has two functions: one to read the current locale and one to persist a locale change:
// .lingo/locale-resolver.client.ts
export function resolveLocale(): string {
// Check URL path prefix
const pathLocale = window.location.pathname.split("/")[1];
const supportedLocales = ["en", "es", "de", "fr", "ja"];
if (supportedLocales.includes(pathLocale)) {
return pathLocale;
}
// Fall back to localStorage
const stored = localStorage.getItem("locale");
if (stored && supportedLocales.includes(stored)) {
return stored;
}
return "en";
}
export function persistLocale(locale: string): void {
localStorage.setItem("locale", locale);
// Navigate to the locale-prefixed URL
const path = window.location.pathname.replace(/^\/[a-z]{2}/, "");
window.location.href = `/${locale}${path}`;
}Common resolver patterns#
Route by URL path prefix (/es/about, /de/pricing):
// .lingo/locale-resolver.server.ts
export function resolveLocale(request: Request): string {
const url = new URL(request.url);
const locale = url.pathname.split("/")[1];
const supported = ["en", "es", "de", "fr"];
return supported.includes(locale) ? locale : "en";
}// .lingo/locale-resolver.client.ts
export function resolveLocale(): string {
const locale = window.location.pathname.split("/")[1];
const supported = ["en", "es", "de", "fr"];
return supported.includes(locale) ? locale : "en";
}
export function persistLocale(locale: string): void {
const path = window.location.pathname.replace(/^\/[a-z]{2}/, "");
window.location.href = `/${locale}${path}`;
}The resolveLocale function must return a locale code that matches one of your configured targetLocales or sourceLocale. Returning an unsupported locale code causes the compiler to fall back to the source locale.
