Communitygithub.com

Nelsutro/agent-skills

Internacionalización y localización de nivel profesional para aplicaciones web. Útil al agregar múltiples idiomas, traducir la interfaz de usuario, extraer cadenas hardcodeadas, manejar plurales/género/mensajes ICU, enrutamiento por configuración regional, diseños RTL, fechas/números/monedas localizados, hreflang/SEO o validar catálogos de traducción. Guía independiente del framework para React, Next.js, Vue, Svelte, Astro, Remix y JS puro, con ejemplos de next-intl, react-i18next, FormatJS y Lingui.

¿Qué es agent-skills?

Esta habilidad (skill) proporciona una guía completa y de nivel profesional para implementar internacionalización (i18n) y localización (l10n) en aplicaciones web modernas. Va más allá de una simple traducción, enfocándose en la arquitectura de i18n como parte fundamental del desarrollo. Cubre desde la auditoría inicial de código en busca de cadenas hardcodeadas hasta la configuración de enrutamiento por configuración regional, el manejo de plurales y género con ICU MessageFormat, el uso de `Intl` para formatear fechas, números y monedas, y la preparación para diseños RTL (de derecha a izquierda). La habilidad es independiente del framework, con ejemplos prácticos para React, Next.js, Vue, Svelte, Astro, Remix y JavaScript puro, e incluye referencias a bibliotecas populares como next-intl, react-i18next, FormatJS y Lingui. Su objetivo es ayudar a los desarrolladores a evitar anti-patrones comunes, establecer flujos de trabajo de traducción robustos y garantizar una experiencia de usuario coherente y de alta calidad en múltiples idiomas y regiones.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Nelsutro/agent-skills/tree/HEAD/web-i18n

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

i18n Web

La internacionalización es arquitectura, no una pasada de traducción al final. El objetivo es una interfaz de usuario que se renderice correctamente para cualquier configuración regional sin casos especiales: sin oraciones concatenadas, sin formatos hardcodeados, sin diseños que se rompan cuando el alemán es un 35% más largo o el árabe invierte la dirección.

Cuándo usar esta habilidad

Úsala cuando el usuario pida:

  • Agregar un idioma, un selector de idioma o enrutamiento por configuración regional
  • Traducir una aplicación, extraer cadenas o configurar un flujo de trabajo de traducción
  • Corregir errores de pluralización, género o "1 elemento / 2 elementos"
  • Dar formato a fechas, números, moneda, tiempo relativo o listas por configuración regional
  • Soporte RTL (árabe, hebreo, persa, urdu)
  • Agregar hreflang, metadatos localizados o mapas de sitio multilingües
  • Revisar una configuración i18n existente, claves faltantes o desviación en las traducciones
  • Elegir entre next-intl, react-i18next, FormatJS, Lingui o un TMS

Las referencias relacionadas están en references/ y los scripts en scripts/. Lee solo lo que la tarea necesite. Las rutas son relativas al directorio de esta habilidad (el runtime del agente lo imprime cuando se carga la habilidad).

No negociables

  1. Nunca construyas una oración concatenando fragmentos traducidos. Traduce el mensaje completo con marcadores de posición: "{count} mensajes nuevos", no t('nuevo') + ' ' + count + ' ' + t('mensajes').
  2. Nunca uses texto en inglés como clave. Las claves son identificadores estables (inbox.unreadCount), para que los cambios en el texto no rompan las búsquedas ni invaliden todas las traducciones.
  3. Toda cadena visible para el usuario debe estar externalizada, incluyendo aria-label, alt, title, placeholder, errores de validación, estados vacíos, notificaciones toast, correos electrónicos, notificaciones push y salida PDF.
  4. Nunca hardcodees formatos. Nada de toFixed(2) + '$', nada de MM/DD/YYYY manual, nada de nombres de mes en inglés, nada de suposiciones de 24h. Usa Intl.
  5. Una configuración regional es una etiqueta BCP 47, no un idioma. pt-BR y pt-PT, zh-Hans y zh-Hant, es-MX y es-ES son productos diferentes. Almacena la etiqueta completa.
  6. El estado de la configuración regional debe tener alcance de solicitud en el servidor. Nunca pongas la configuración regional actual en una variable mutable a nivel de módulo durante SSR; las solicitudes concurrentes filtrarán idiomas entre sí.
  7. Las traducciones son entrada no confiable si provienen de un TMS, comunidad o CMS. Nunca las pases a innerHTML/dangerouslySetInnerHTML sin sanitizar.

Flujo de trabajo

1. Auditar antes de modificar el código

  • Detecta el stack, el enrutador y la biblioteca i18n existente antes de proponer cualquier cosa.
  • Encuentra cadenas hardcodeadas:
    node scripts/scan-hardcoded.mjs src
    
    Trata la salida como candidatos, no como verdad absoluta: omite test fixtures, constantes, URLs y registros orientados al desarrollador.
  • Inventaria cada configuración regional actualmente enviada y dónde viven los catálogos.
  • Identifica las áreas de mayor riesgo primero: autenticación, pago, errores, correos electrónicos y texto legal.

2. Decidir la arquitectura (escríbela)

Responde estas preguntas antes de escribir código:

DecisiónValor por defectoNotas
Estrategia de configuración regionalPrefijo de ruta /es/Mejor SEO y capacidad de caché. Subdominios/ccTLD solo con una razón.
Configuración regional por defectoIdioma de origenNo redirijas automáticamente a visitantes nuevos a una configuración regional adivinada sin una opción para anularla.
Detección de configuración regionalURL → cookie → Accept-Language → valor por defectoLa URL gana para que los enlaces compartidos nunca cambien de idioma.
Formato de catálogoJSON por configuración regional por espacio de nombresCarga diferida de espacios de nombres; nunca envíes todas las configuraciones regionales al cliente.
Sintaxis de mensajesICU MessageFormatPortable entre bibliotecas y TMS; soporta plural/select/ordinal.
Estilo de clavefeature.component.elementEstable, buscable, sin texto en inglés.
Fuente de verdad de traduccionesTMS o repositorio, no ambosElige uno; sincroniza en una dirección en CI.
Fallback (retroceso)Cadena de configuración regional es-MX → es → enRegistra los fallbacks en desarrollo para detectar vacíos.

3. Externalizar y estructurar mensajes

  • Reemplaza cadenas con claves semánticas y mueve el texto al catálogo de la configuración regional base.
  • Usa una clave por oración completa. Si una oración contiene un enlace o una palabra en negrita, usa interpolación de texto enriquecido en lugar de dividirla.
  • Mantén los marcadores de posición con nombre ({userName}), nunca posicionales ({0}).
  • No pongas HTML en los catálogos a menos que la biblioteca soporte explícitamente etiquetas tipadas.
  • Agrupa por superficie de producto, no por página: checkout.json, auth.json, errors.json.

Consulta references/icu.md para reglas sobre plural, select, ordinal y texto enriquecido.

4. Configurar enrutamiento y renderizado por configuración regional

  • Deriva la configuración regional de la ruta, valídala contra la lista de soportadas y devuelve 404 para configuraciones regionales desconocidas en lugar de hacer un fallback silencioso.
  • Proporciona un selector de idioma que preserve la ruta actual y la cadena de consulta.
  • Establece <html lang="..."> y <html dir="..."> en el documento renderizado en el servidor.
  • Carga solo el catálogo de la configuración regional activa más el fallback por defecto.
  • Asegúrate de que el mismo catálogo esté disponible para el renderizado del servidor y del cliente; evita lógica de traducción duplicada o divergente.

Configuración específica del framework y errores comunes: references/frameworks.md. Enrutamiento, detección, hreflang y mapas de sitio: references/routing-seo.md.

5. Manejar valores sensibles a la configuración regional

Usa Intl (o el envoltorio del framework), nunca formato manual:

new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount)
new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeZone }).format(date)
new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(-3, 'day')
new Intl.ListFormat(locale, { type: 'conjunction' }).format(items)
new Intl.PluralRules(locale).select(count)
new Intl.Collator(locale).compare(a, b)
  • Almacena las marcas de tiempo en UTC; renderiza en la zona horaria del usuario, no la del servidor.
  • La moneda no es lo mismo que la configuración regional: un usuario francés puede pagar en USD.
  • Ordena/busca con Intl.Collator, no con < o localeCompare() sin una configuración regional.
  • Conversión de mayúsculas/minúsculas: toLocaleUpperCase(locale), porque la i turca se convierte en İ.

6. Diseñar para expansión y RTL

  • Asume un +30–40% de longitud de texto (alemán, finlandés, ruso) y evita botones de ancho fijo o etiquetas de una sola línea.
  • Deja que los contenedores crezcan, permite el ajuste de palabras y prueba con pseudo-localización:
    node scripts/pseudo-localize.mjs locales/en.json --out locales/en-XA.json
    
  • Usa propiedades CSS lógicas (margin-inline-start, padding-inline, inset-inline) en lugar de left/right.
  • Refleja iconos direccionales (flechas, chevrones, progreso), no logotipos, relojes, controles multimedia ni marcas de verificación.
  • Envuelve el contenido en línea generado por el usuario en <bdi> o dir="auto".

Lista de verificación RTL completa: references/rtl.md.

7. Configurar QA de traducción en CI

Como mínimo, haz que la compilación falle cuando los catálogos sean estructuralmente inválidos:

node scripts/check-locales.mjs locales --base en

Verifica claves faltantes/extra, valores vacíos, discrepancias en marcadores de posición y sintaxis ICU rota. Agrega compilación ICU a través de la CLI de tu biblioteca cuando esté disponible:

npx formatjs compile-folder --ast locales/en locales/compiled/en

Agrega pruebas para categorías plurales, salida de fecha/número con una zona horaria fija y renderizado RTL. Consulta references/testing.md.

Decisiones clave y trampas

Selección de biblioteca

NecesidadElegir
Next.js App Router, componentes del servidornext-intl
React SPA, ecosistema maduro, muchos pluginsreact-i18next
Primero ICU, independiente del framework, extracción CLIFormatJS (react-intl)
Catálogos en tiempo de compilación, runtime pequeño, seguridad de tiposLingui
Vuevue-i18n
Sveltesvelte-i18n o paraglide
Astroenrutamiento i18n integrado + tu biblioteca de mensajes
Vanilla / mínimoIntl + un cargador de catálogos pequeño

No introduzcas una segunda biblioteca i18n en una aplicación que ya tenga una. Extiende la configuración existente.

Pluralización

Las categorías plurales son específicas del idioma. El inglés tiene one/other; el árabe tiene zero/one/two/few/many/other; el japonés tiene solo other. Nunca escribas count === 1 ? ... : ....

"inbox.unread": "{count, plural, =0 {No hay mensajes sin leer} one {# mensaje sin leer} other {# mensajes sin leer}}"

Género y concordancia gramatical

Usa select en una variable de género y mantén el valor de la variable neutral en cuanto al idioma (female, male, neutral, unknown), no palabras localizadas:

"profile.greeting": "{gender, select, female {Bienvenida, {name}} male {Bienvenido, {name}} other {Bienvenido/a, {name}}}"

Si un idioma necesita concordancia en adjetivos, sustantivos o artículos, expón el género/caso gramatical como un argumento en lugar de manipular la cadena.

Texto enriquecido

No dividas "Lee nuestros " + link + " términos" en tres claves. Usa interpolación de etiquetas:

"signup.terms": "Acepto los <link>Términos del Servicio</link>"
t.rich('signup.terms', { link: (chunks) => <a href="/terms">{chunks}</a> })

SEO

  • Una URL indexable por configuración regional; nunca sirvas contenido traducido por máquina detrás de la misma URL.
  • hreflang auto-referenciado más x-default; URLs absolutas; incluye todas las configuraciones regionales.
  • Localiza <title>, meta descripción, etiquetas OG y datos estructurados.
  • No combines hreflang con noindex en páginas localizadas.

Detalles en references/routing-seo.md.

Caché

  • La configuración regional en la ruta hace que el almacenamiento en caché de CDN sea sencillo. Si negocias por encabezado, envía Vary: Accept-Language y espera fragmentación de caché.
  • No almacenes en caché la preferencia de idioma personalizada en una respuesta CDN compartida.
  • Mantén Content-Language coherente con la configuración regional renderizada.

Lista de verificación de revisión

Antes de declarar i18n como terminado:

  • No hay cadenas hardcodeadas visibles para el usuario en componentes, correos electrónicos o errores
  • No hay concatenación de oraciones; cada mensaje es una unidad traducible completa
  • Las claves son semánticas y estables; no hay texto en inglés como claves
  • Cada marcador de posición existe en cada configuración regional; la sintaxis ICU compila
  • Los plurales usan Intl.PluralRules/ICU, no count === 1
  • El género/concordancia se maneja mediante select, no sufijos "/a"
  • Fechas, números, moneda, tiempo relativo usan Intl con configuración regional/zona horaria explícita
  • <html lang> y dir son correctos en el primer pintado (SSR), no se establecen en un efecto
  • El selector de configuración regional preserva la ruta/consulta y funciona con enlaces profundos
  • La configuración regional desconocida devuelve 404, no un fallback silencioso
  • El diseño sobrevive a una expansión de +35% y RTL
  • hreflang, canónico, mapa del sitio y metadatos localizados son correctos
  • La validación del catálogo se ejecuta en CI
  • El comportamiento de fallback es intencional y observable

Anti-patrones

  • t('hola') + ', ' + nombre — oraciones fragmentadas
  • clave={textoEnIngles} — texto traducido como clave
  • count === 1 ? 'elemento' : 'elementos' — lógica plural solo en inglés
  • new Date().toLocaleDateString() sin configuración regional ni zona horaria
  • value.toFixed(2) para dinero
  • locale === 'ar' && { direction: 'rtl' } disperso por los componentes en lugar de un dir a nivel de documento
  • Establecer <html lang> dentro de useEffect — causa un destello y rompe los rastreadores
  • Cargar todas las configuraciones regionales en el paquete inicial
  • Redirigir automáticamente por IP sin forma de anularlo
  • Traducir nombres propios, nombres de productos, identificadores de código o URLs
  • Traducir cadenas con máquina y enviarlas sin revisar
  • Asumir que un solo archivo de catálogo escala más allá de unos pocos cientos de claves

Skills relacionados