Documentación

Internacionalización

Cómo Motoko Base gestiona locales, enrutamiento, catálogos de mensajes y SEO para productos SaaS multilingües.

Abrir enChatGPT (se abre en una pestaña nueva)Claude (se abre en una pestaña nueva)Cursor (se abre en una pestaña nueva)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

LocaleCódigoPrefijo URL marketingEjemplo
Inglés (predeterminado)en(ninguno)/, /docs, /terms
Españoles/es/es/docs, /es/terms
Alemánde/de/de/docs
Francésfr/fr/fr/docs
Portugués brasileñopt-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       →  en

Primera 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

CapaUbicaciónContiene
Estructurasrc/features/marketing/marketing-structure.tsIDs, hrefs, refs tech, orden
Copymessages/{locale}/marketing.jsonTitulares, 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:

  1. Escribe el MDX en inglés
  2. Crea archivos hermanos por locale
  3. Lista el slug una vez en meta.json del 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

SuperficieControlEfecto
Navbar marketingSelector de idiomaNavega a URL con prefijo + actualiza cookie
Ajustes → App → AparienciaSelector de idiomaGuarda en BD via updateLanguagePreferenceAction, cookie, refresca UI
Primera visita /Accept-LanguageRedirect ú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.