Resolvers de locale personalizados
Personaliza cómo se detectan y persisten los locales proporcionando archivos de resolver personalizados.
Por defecto, el compilador utiliza persistencia de locale basada en cookies. Los resolvers personalizados te permiten implementar estrategias alternativas como localStorage, parámetros de URL, búsqueda en base de datos o detección por subdominio.
Cómo funciona
Crea archivos opcionales en el directorio .lingo/:
.lingo/locale-resolver.server.ts— Detección de locale del lado del servidor.lingo/locale-resolver.client.ts— Detección y persistencia de locale del lado del cliente
Si estos archivos no existen, el compilador utiliza la implementación predeterminada basada en cookies.
Resolver de locale del servidor
Crea .lingo/locale-resolver.server.ts para detección de locale personalizada del lado del servidor:
// .lingo/locale-resolver.server.ts
export async function getServerLocale(): Promise<string> {
// Your custom logic
return "en";
}
Ejemplo: encabezado 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;
}
Ejemplo: búsqueda en base de datos
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";
}
Ejemplo: detección por subdominio
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";
}
Resolver de locale del cliente
Crea .lingo/locale-resolver.client.ts para detección y persistencia de locale personalizada del lado del cliente:
// .lingo/locale-resolver.client.ts
export function getClientLocale(): string {
// Detect locale
return "en";
}
export function persistLocale(locale: string): void {
// Save locale preference
}
Ejemplo: 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();
}
Ejemplo: parámetros de 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();
}
Ejemplo: estrategia combinada
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();
}
Tipos de TypeScript
Ambos resolvers están completamente tipados:
// Server resolver
export async function getServerLocale(): Promise<string>;
// Client resolver
export function getClientLocale(): string;
export function persistLocale(locale: string): void;
Integración con setLocale
La función setLocale() de useLingoContext() llama automáticamente a tu persistLocale() personalizado:
{/* CODE_PLACEHOLDER_c47ff3da0ecefafc26f5d81ca95d3b2c */
Consideraciones de SSR
Para frameworks SSR (Next.js, Remix, etc.):
- El resolver del servidor se ejecuta en cada solicitud
- El resolver del cliente se ejecuta en el navegador después de la hidratación
- Asegura la consistencia entre la detección del servidor y del cliente
Patrón común: El servidor lee desde cookie/header, el cliente persiste en cookie/localStorage.
Implementación predeterminada
Si no se proporcionan resolvers personalizados, el compilador usa este predeterminado:
// 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();
}
Preguntas frecuentes
¿Necesito ambos resolvers de servidor y cliente? No. Proporciona solo lo que necesites personalizar. Los archivos faltantes usan el comportamiento predeterminado.
¿Puedo usar resolvers personalizados con aplicaciones SPA? Sí. Solo el resolver del cliente es relevante para aplicaciones SPA. El resolver del servidor es para SSR.
¿Funciona esto con Vite? Sí. El resolver del cliente funciona de forma idéntica. El resolver del servidor es específico de Next.js (para SSR).
¿Cómo pruebo los resolvers personalizados?
- Crea archivos de resolver
- Implementa tu lógica
- Ejecuta el servidor de desarrollo
- Prueba el cambio de locale con tu persistencia personalizada
¿Puedo acceder a APIs específicas de Next.js? Sí. Importa utilidades de Next.js (headers, cookies, etc.) directamente en tus archivos de resolver.
¿Qué pasa si getServerLocale devuelve un locale inválido?
El compilador recurre a sourceLocale si el locale devuelto no está en targetLocales.
Ejemplos por caso de uso
Enrutamiento basado en subdominio
Servidor:
const host = (await headers()).get("host") || "";
const locale = host.split(".")[0]; // es.example.com → es
return supportedLocales.includes(locale) ? locale : "en";
Cliente:
const locale = window.location.hostname.split(".")[0];
return supportedLocales.includes(locale) ? locale : "en";
Preferencias de usuario respaldadas por base de datos
Servidor:
const session = await getSession();
const user = await db.user.findUnique({
where: { id: session.userId },
});
return user.locale || "en";
Cliente:
// After user changes locale in UI
await fetch("/api/user/locale", {
method: "POST",
body: JSON.stringify({ locale }),
});
window.location.reload();
Enrutamiento basado en rutas (/en/about, /es/about)
Servidor:
const pathname = (await headers()).get("x-pathname") || "/";
const locale = pathname.split("/")[1];
return supportedLocales.includes(locale) ? locale : "en";
Cliente:
const locale = window.location.pathname.split("/")[1];
return supportedLocales.includes(locale) ? locale : "en";
Próximos pasos
- Cambio de idioma — Implementar selectores de idioma
- Integración con frameworks — Patrones específicos de frameworks
- Mejores prácticas — Estrategias recomendadas de detección de idioma