Résolveurs de locale personnalisés
Personnalisez la détection et la persistance des locales en fournissant des fichiers de résolveur personnalisés.
Par défaut, le compilateur utilise la persistance de locale basée sur les cookies. Les résolveurs personnalisés vous permettent d'implémenter des stratégies alternatives comme localStorage, les paramètres d'URL, la recherche en base de données ou la détection de sous-domaine.
Fonctionnement
Créez des fichiers optionnels dans le répertoire .lingo/ :
.lingo/locale-resolver.server.ts— Détection de locale côté serveur.lingo/locale-resolver.client.ts— Détection et persistance de locale côté client
Si ces fichiers n'existent pas, le compilateur utilise l'implémentation par défaut basée sur les cookies.
Résolveur de locale côté serveur
Créez .lingo/locale-resolver.server.ts pour une détection de locale personnalisée côté serveur :
// .lingo/locale-resolver.server.ts
export async function getServerLocale(): Promise<string> {
// Your custom logic
return "en";
}
Exemple : en-tête Accept-Language (Next.js)
import { headers } from "next/headers";
export async function getServerLocale(): Promise<string> {
const headersList = await headers();
const acceptLanguage = headersList.get("accept-language");
// Parse accept-language: "en-US,en;q=0.9,es;q=0.8"
const locale = acceptLanguage
?.split(",")[0]
?.split("-")[0]
?.trim() || "en";
return locale;
}
Exemple : recherche en base de données
import { cookies } from "next/headers";
import { db } from "@/lib/db";
export async function getServerLocale(): Promise<string> {
const cookieStore = await cookies();
const sessionToken = cookieStore.get("session")?.value;
if (!sessionToken) return "en";
// Query user preferences from database
const user = await db.user.findUnique({
where: { sessionToken },
select: { preferredLocale: true },
});
return user?.preferredLocale || "en";
}
Exemple : détection de sous-domaine
import { headers } from "next/headers";
export async function getServerLocale(): Promise<string> {
const headersList = await headers();
const host = headersList.get("host") || "";
// Extract subdomain: es.example.com → es
const subdomain = host.split(".")[0];
// Map subdomain to locale
const localeMap: Record<string, string> = {
es: "es",
de: "de",
fr: "fr",
};
return localeMap[subdomain] || "en";
}
Résolveur de locale côté client
Créez .lingo/locale-resolver.client.ts pour une détection et persistance de locale personnalisées côté client :
// .lingo/locale-resolver.client.ts
export function getClientLocale(): string {
// Detect locale
return "en";
}
export function persistLocale(locale: string): void {
// Save locale preference
}
Exemple : localStorage
export function getClientLocale(): string {
if (typeof window === "undefined") return "en";
// Check localStorage
const stored = localStorage.getItem("user-locale");
if (stored) return stored;
// Fall back to browser language
return navigator.language.split("-")[0] || "en";
}
export function persistLocale(locale: string): void {
if (typeof window === "undefined") return;
localStorage.setItem("user-locale", locale);
// Optionally reload page to apply new locale
window.location.reload();
}
Exemple : paramètres d'URL
export function getClientLocale(): string {
if (typeof window === "undefined") return "en";
// Check URL parameter: ?lang=es
const params = new URLSearchParams(window.location.search);
const urlLocale = params.get("lang");
if (urlLocale) return urlLocale;
// Fall back to localStorage
return localStorage.getItem("locale") || "en";
}
export function persistLocale(locale: string): void {
if (typeof window === "undefined") return;
// Update URL parameter
const url = new URL(window.location.href);
url.searchParams.set("lang", locale);
window.history.replaceState({}, "", url.toString());
// Also save to localStorage
localStorage.setItem("locale", locale);
// Reload to apply new locale
window.location.reload();
}
Exemple : stratégie combinée
export function getClientLocale(): string {
if (typeof window === "undefined") return "en";
// Priority 1: URL parameter
const params = new URLSearchParams(window.location.search);
const urlLocale = params.get("lang");
if (urlLocale) return urlLocale;
// Priority 2: localStorage
const stored = localStorage.getItem("locale");
if (stored) return stored;
// Priority 3: Browser language
const browserLocale = navigator.language.split("-")[0];
const supportedLocales = ["en", "es", "de", "fr"];
if (supportedLocales.includes(browserLocale)) {
return browserLocale;
}
// Priority 4: Default
return "en";
}
export function persistLocale(locale: string): void {
if (typeof window === "undefined") return;
// Save to localStorage
localStorage.setItem("locale", locale);
// Update URL
const url = new URL(window.location.href);
url.searchParams.set("lang", locale);
window.history.replaceState({}, "", url.toString());
// Reload page
window.location.reload();
}
Types TypeScript
Les deux résolveurs sont entièrement typés :
// Server resolver
export async function getServerLocale(): Promise<string>;
// Client resolver
export function getClientLocale(): string;
export function persistLocale(locale: string): void;
Intégration avec setLocale
La fonction setLocale() de useLingoContext() appelle automatiquement votre persistLocale() personnalisé :
import { useLingoContext } from "@lingo.dev/compiler/react";
function MyComponent() {
const { setLocale } = useLingoContext();
// Calls your persistLocale() under the hood
setLocale("es");
}
Considérations SSR
Pour les frameworks SSR (Next.js, Remix, etc.) :
- Le résolveur serveur s'exécute à chaque requête
- Le résolveur client s'exécute dans le navigateur après l'hydratation
- Assurez la cohérence entre la détection serveur et client
Modèle courant : le serveur lit depuis un cookie/en-tête, le client persiste dans un cookie/localStorage.
Implémentation par défaut
Si aucun résolveur personnalisé n'est fourni, le compilateur utilise cette configuration par défaut :
// Default server resolver
export async function getServerLocale(): Promise<string> {
const cookies = await import("next/headers").then((m) => m.cookies());
return cookies().get("locale")?.value || "en";
}
// Default client resolver
export function getClientLocale(): string {
return document.cookie
.split("; ")
.find((row) => row.startsWith("locale="))
?.split("=")[1] || "en";
}
export function persistLocale(locale: string): void {
document.cookie = `locale=${locale}; path=/; max-age=31536000`;
window.location.reload();
}
Questions fréquentes
Ai-je besoin des résolveurs serveur et client ? Non. Fournissez uniquement ce que vous devez personnaliser. Les fichiers manquants utilisent le comportement par défaut.
Puis-je utiliser des résolveurs personnalisés avec des applications SPA ? Oui. Seul le résolveur client est pertinent pour les applications SPA. Le résolveur serveur est pour le SSR.
Cela fonctionne-t-il avec Vite ? Oui. Le résolveur client fonctionne de manière identique. Le résolveur serveur est spécifique à Next.js (pour le SSR).
Comment tester les résolveurs personnalisés ?
- Créez les fichiers de résolveur
- Implémentez votre logique
- Lancez le serveur de développement
- Testez le changement de locale avec votre persistance personnalisée
Puis-je accéder aux API spécifiques à Next.js ? Oui. Importez les utilitaires Next.js (headers, cookies, etc.) directement dans vos fichiers de résolveur.
Que se passe-t-il si getServerLocale retourne une locale invalide ?
Le compilateur revient à sourceLocale si la locale retournée n'est pas dans targetLocales.
Exemples par cas d'usage
Routage basé sur les sous-domaines
Serveur :
const host = (await headers()).get("host") || "";
const locale = host.split(".")[0]; // es.example.com → es
return supportedLocales.includes(locale) ? locale : "en";
Client :
const locale = window.location.hostname.split(".")[0];
return supportedLocales.includes(locale) ? locale : "en";
Préférences utilisateur stockées en base de données
Serveur :
const session = await getSession();
const user = await db.user.findUnique({
where: { id: session.userId },
});
return user.locale || "en";
Client :
// After user changes locale in UI
await fetch("/api/user/locale", {
method: "POST",
body: JSON.stringify({ locale }),
});
window.location.reload();
Routage basé sur le chemin (/en/about, /es/about)
Serveur :
const pathname = (await headers()).get("x-pathname") || "/";
const locale = pathname.split("/")[1];
return supportedLocales.includes(locale) ? locale : "en";
Client :
const locale = window.location.pathname.split("/")[1];
return supportedLocales.includes(locale) ? locale : "en";
Prochaines étapes
- Changement de langue — Implémenter des sélecteurs de langue
- Intégration de framework — Modèles spécifiques aux frameworks
- Bonnes pratiques — Stratégies recommandées de détection de langue