Documentación
Internacionalización
Cómo Motoko Base gestiona locales, enrutamiento, catálogos de mensajes y SEO para productos SaaS multilingües.
Motoko Base incluye una base de internacionalización (i18n) completa construida con next-intl. Obtienes cinco locales, URLs de marketing conscientes del locale, shell del dashboard traducido, documentación y changelog localizados, y un patrón claro para extender traducciones a tus propias features.
Esta página explica cómo está cableada la i18n. Para instrucciones paso a paso al añadir copy, consulta Añadir traducciones.
Locales soportados
| Locale | Código | Prefijo URL marketing | Ejemplo |
|---|---|---|---|
| Inglés (predeterminado) | en | (ninguno) | /, /docs, /terms |
| Español | es | /es | /es/docs, /es/terms |
| Alemán | de | /de | /de/docs |
| Francés | fr | /fr | /fr/docs |
| Portugués brasileño | pt-BR | /pt-br | /pt-br/docs |
Las definiciones de locale están en src/i18n/routing.ts. Las etiquetas del selector de idioma provienen de localeLabels en el mismo archivo.
Modelo de enrutamiento híbrido
Motoko Base usa dos estrategias de enrutamiento a propósito:
Superficies con prefijo de locale (marketing)
Estas rutas viven bajo src/app/[locale]/:
- Landing page y secciones de marketing
- Páginas legales (
/terms,/privacy,/licence) - Documentación (
/docs) - Changelog (
/changelog)
El inglés usa URLs sin prefijo (localePrefix: 'as-needed'). Otros locales reciben un prefijo (/fr, /de, /es, /pt-br).
El selector de idioma de marketing (MarketingLanguageSwitcher) navega entre rutas equivalentes en cada locale manteniendo el contexto de la página actual.
Superficies de app sin prefijo
Estas rutas permanecen en rutas fijas sin segmento de locale:
- Dashboard y features demo (
/dashboard/...) - Flujos de auth (
/sign-in,/sign-up, olvidar/restablecer/verificar) - Rutas API, webhooks, redirects públicos (
/api,/r/[slug])
El locale activo se resuelve en tiempo de request — no desde la URL.
Resolución de locale
Cuando next-intl necesita un locale, src/i18n/resolve-locale.ts aplica esta prioridad:
Usuario autenticado → user_preferences.language (base de datos)
Invitado → cookie NEXT_LOCALE
Fallback → cabecera Accept-Language
Predeterminado → enPrimera visita a /: src/proxy.ts lee Accept-Language una vez. Si coincide con un locale soportado distinto del inglés, redirige a la URL de marketing con prefijo y establece la cookie. Después, las elecciones explícitas (selector o Ajustes) prevalecen.
Usuarios autenticados: guardar el idioma en Ajustes → App → Apariencia (/dashboard/settings/app/appearance) actualiza la base de datos via updateLanguagePreferenceAction, establece la cookie y refresca la UI vía LocalePreferenceSync.
Estructura de archivos
src/i18n/
├── routing.ts # Locales, prefijos, nombre de cookie
├── navigation.ts # Link, useRouter, usePathname conscientes del locale
├── request.ts # Config servidor next-intl (carga mensajes)
├── resolve-locale.ts # Resolución de locale para rutas app/auth
├── load-messages.ts # Import dinámico de catálogos
└── fumadocs.ts # Locales de contenido docs/changelog
messages/
├── en/
│ ├── index.ts # Agrega todos los namespaces
│ └── … # Un JSON por feature/área
├── es/
├── de/
├── fr/
└── pt-BR/
content/docs/ # MDX localizado vía sufijo dot (p. ej. page.de.mdx)
content/changelog/Los helpers de formato compartidos están en src/lib/i18n/format.ts. Úsalos en lugar de hardcodear "en-US" en llamadas Intl.
Catálogos de mensajes
Las traducciones son namespaces JSON tipados bajo messages/{locale}/. Cada index.ts exporta un objeto consumido por next-intl.
Usa un namespace por feature o área (auth, settings, billing, …). Mantén keys estables en todos los locales.
Marketing: estructura vs copy
| Capa | Ubicación | Contiene |
|---|---|---|
| Estructura | src/features/marketing/marketing-structure.ts | IDs, hrefs, refs tech, orden |
| Copy | messages/{locale}/marketing.json | Titulares, descripciones, FAQ, footer |
Los componentes de sección fusionan estructura + mensajes en useMarketingContent().
Usar traducciones en componentes
Componentes cliente
"use client";
import { useTranslations } from "next-intl";
export function MyPage() {
const t = useTranslations("overview");
return <h1>{t("welcome.title", { name: "Alex" })}</h1>;
}Componentes servidor y metadata
import { getTranslations } from "next-intl/server";
export async function generateMetadata() {
const t = await getTranslations("docs");
return { title: t("metaTitle") };
}Enlaces conscientes del locale
Importa helpers de @/i18n/navigation — no next/link — en páginas con prefijo de locale.
Docs y changelog localizados
El contenido usa Fumadocs i18n con el parser dot (page.de.mdx, …). Inglés es el archivo base (page.mdx).
Para añadir una página localizada:
- Escribe el MDX en inglés
- Crea archivos hermanos por locale
- Lista el slug una vez en
meta.jsondel folder
SEO
URLs localizadas reciben canonicals por locale, alternates hreflang y entradas en sitemap vía src/lib/seo/metadata.ts y src/app/sitemap.ts.
Controles de idioma
| Superficie | Control | Efecto |
|---|---|---|
| Navbar marketing | Selector de idioma | Navega a URL con prefijo + actualiza cookie |
| Ajustes → App → Apariencia | Selector de idioma | Guarda en BD via updateLanguagePreferenceAction, cookie, refresca UI |
Primera visita / | Accept-Language | Redirect único al mejor locale |
Próximos pasos
Añadir traducciones — Guía práctica para nuevas features y keys.
Estructura del proyecto — Dónde vive el código.
Configuración — Nav del dashboard y config del producto.