i18n
Reglas
- Next.js App Router: usar
next-intl — hook useTranslations(), NextIntlClientProvider, middleware para detección de locale
- React SPA: usar
react-i18next — hook useTranslation(), i18n.init() con i18next-http-backend para carga perezosa
- Enrutamiento con locale:
/en/about, /fr/about — usar middleware para detectar el encabezado Accept-Language y redirigir
- Archivos de traducción: JSON por locale por namespace —
messages/en/common.json, messages/fr/common.json
- División de namespaces: un namespace por característica/página —
auth.json, dashboard.json, settings.json — cargar solo lo que necesita la página
- Pluralización: usar formato de mensaje ICU —
{count, plural, one {# elemento} other {# elementos}}
- Interpolación:
t('saludo', { nombre }) — nunca concatenar cadenas traducidas
- Fecha/número/moneda: usar
Intl.DateTimeFormat, Intl.NumberFormat — nunca hardcodear formatos
- Soporte RTL: usar
dir="rtl" en <html>, propiedades lógicas de CSS (margin-inline-start no margin-left), probar con árabe/hebreo
Patrones
import createMiddleware from "next-intl/middleware";
export default createMiddleware({
locales: ["en", "fr", "de", "ar"],
defaultLocale: "en",
});
import { useTranslations } from "next-intl";
function ProductCard({ price, date }: { price: number; date: Date }) {
const t = useTranslations("products");
const format = useFormatter();
return (
<div>
<h2>{t("title")}</h2>
<p>{format.number(price, { style: "currency", currency: "USD" })}</p>
<p>{format.dateTime(date, { dateStyle: "medium" })}</p>
</div>
);
}
Evitar
- Hardcodear cadenas visibles para el usuario — toda cadena debe pasar por
t()
- Concatenación de cadenas para texto traducido — usar interpolación o formato ICU
- Cargar todos los locales de una vez — cargar de forma perezosa por locale y namespace
- CSS
left/right para el diseño — usar propiedades lógicas para compatibilidad RTL
- Almacenar el locale solo en localStorage — usar locale basado en URL para SEO y capacidad de compartir