Haz tu pregunta y obtén un resumen del documento referenciando esta página y el proveedor AI de tu elección
El contenido de esta página ha sido traducido con una IA.
Ver la última versión del contenido original en inglésSi tienes una idea para mejorar esta documentación, no dudes en contribuir enviando una pull request en GitHub.
Enlace de GitHub a la documentaciónCopiar el Markdown del documento a la portapapeles
Formato de Mensajes ICU: la sintaxis y los puntos conflictivos
ICU MessageFormat es una sintaxis de cadenas que permite que una traducción contenga su propia lógica condicional: plurales, formas según el género, formato de números y fechas. Existe porque la gramática pertenece al traductor, no al desarrollador que escribe if (count === 1). Este artículo cubre la sintaxis, las partes dependientes del idioma que rompen las implementaciones ingenuas y cómo el ecosistema JS lo gestiona.
Tabla de contenidos
El problema, en concreto
Este es el código que casi todo el mundo escribe primero:
Copiar el código al portapapeles
Esto funciona en inglés y falla en prácticamente todos los demás idiomas:
- Ruso y polaco necesitan tres o cuatro formas, no dos.
- Japonés solo necesita una, y el espacio concatenado es incorrecto.
- Árabe necesita seis formas, y el número mismo debe mostrarse en el sistema numérico de la locale.
- Francés coloca un espacio de no separación antes de cierta puntuación, que tu
+ " "acaba de romper.
El problema de fondo es que la oración se ha dividido en fragmentos. Un traductor ve item e items sin contexto y sin la capacidad de reordenar la frase. ICU MessageFormat soluciona esto manteniendo la oración completa en una sola cadena traducible y proporcionando al traductor operadores condicionales.
Argumentos simples
La unidad básica es un marcador de posición entre llaves simples:
Copiar el código al portapapeles
Pasas { name: "Alice" } al formatear y obtienes Hello, Alice!. Las llaves son los únicos caracteres especiales; para imprimir una llave literal, debes envolverla entre comillas simples: '{'.
Esa es toda la funcionalidad de "interpolación". Todo lo demás en ICU se construye sobre ella.
Plural
plural selecciona una rama según un valor numérico:
Copiar el código al portapapeles
Tres aspectos fundamentales que debes conocer:
#se reemplaza por el valor formateado decount, adaptado a la locale, por lo que1234se convierte en1,234enen-USy1.234enes-ES.otheres obligatorio. Toda implementación de ICU lanzará un error o fallará en la validación sin él. Es el valor de respaldo cuando ninguna categoría coincide.=0,=1, … coinciden con valores exactos y se evalúan antes de las categorías CLDR. Úsalos para textos especiales ("No hay mensajes"), no como un sustituto deone.
Copiar el código al portapapeles
offset
offset:n resta n del valor antes de seleccionar la categoría y sustituir #. Sirve para patrones como "A Alice y a otras 3 personas les gustó esto":
Copiar el código al portapapeles
Con count: 4, # renderiza 3. offset es muy útil pero no todos los entornos lo soportan igual de bien, así que conviene verificar tu runtime antes de depender de él.
Las categorías de plural dependen del idioma
Aquí es donde la gente suele equivocarse. Los nombres de categoría zero, one, two, few, many, other no son casillas universales que se completan para cada idioma. Cada locale utiliza un subconjunto, definido por las reglas de plural CLDR, y las reglas son gramaticales, no intuitivas.
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Idioma | Tag | Categorías utilizadas | Total |
|---|---|---|---|
| Japonés | ja | other | 1 |
| Chino | zh | other | 1 |
| Inglés | en | one, other | 2 |
| Alemán | de | one, other | 2 |
| Francés | fr | one, many, other | 3 |
| Checo | cs | one, few, many, other | 4 |
| Polaco | pl | one, few, many, other | 4 |
| Ruso | ru | one, few, many, other | 4 |
| Árabe | ar | zero, one, two, few, many, other | 6 |
| Galés | cy | zero, one, two, few, many, other | 6 |
Dos consecuencias que suelen sorprender:
oneno significa "1". En ruso,onecubre 1, 21, 31, 101: cualquier número que termine en 1 excepto los terminados en 11. En francés,0entra dentro deone.- Añadir una categoría al texto original en inglés no tiene efecto alguno. El mensaje en inglés solo necesita
oneyother; la traducción al polaco necesita cuatro ramas, y esa estructura reside en la cadena polaca, no en la inglesa. Cualquier formato que obligue a todas las locales a compartir la misma estructura de claves causará fricción aquí.
Puedes comprobar el comportamiento de tu runtime sin instalar nada:
Copiar el código al portapapeles
Intl.PluralRules incluye datos CLDR en todos los navegadores modernos y en Node. Cualquier biblioteca que ofrezca pluralización CLDR casi con certeza está llamando a esta API por debajo.
select y selectordinal
select permite ramificar en función de una cadena de texto arbitraria: un género, un rol, un estado o un nivel de plan.
Copiar el código al portapapeles
Las claves se comparan literalmente y other también es obligatorio aquí. select es la herramienta idónea cuando la estructura de una oración depende de un valor enumerado, ya que los idiomas discrepan sobre qué valores afectan su gramática.
selectordinal tiene la misma forma que plural pero utiliza las reglas de plurales ordinales, que corresponden a una tabla diferente de las cardinales:
Copiar el código al portapapeles
El inglés utiliza cuatro categorías ordinales (1st, 2nd, 3rd, 4th) a pesar de utilizar solo dos cardinales. Esa asimetría es la razón exacta por la que ambos operadores están separados.
Argumentos de números, fechas y horas
ICU puede formatear el valor que interpola:
Copiar el código al portapapeles
La forma moderna es el skeleton, introducido con ICU 60 y marcado por el prefijo ::. Los skeletons son mucho más expresivos que los nombres de estilo tradicionales:
Copiar el código al portapapeles
El soporte de skeletons varía según el runtime. FormatJS los implementa al completo, mientras que otros entornos solo aceptan las formas clásicas number, currency o date, long. Verifica la compatibilidad de :: en tu entorno antes de pasar a producción.
Anidamiento y límites de legibilidad
ICU es componible. Una rama de plural puede contener un select, que a su vez puede contener otro plural:
Copiar el código al portapapeles
Este es el ejemplo clásico de ICU y también el principal argumento contra el anidamiento profundo. A partir de dos niveles, los traductores empiezan a cometer errores de llaves y los editores TMS dejan de ser útiles. Anida como máximo dos niveles; si necesitas un tercero, divide la frase en dos mensajes independientes.
Cómo gestionan ICU las bibliotecas de JS
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Biblioteca | Soporte ICU | Lo que realmente escribes |
|---|---|---|
| react-intl (FormatJS) | Nativo, completo | Cadenas ICU, incluyendo skeletons y etiquetas de texto enriquecido |
| next-intl | Nativo | Cadenas ICU, mediante intl-messageformat de FormatJS |
| i18next | Requiere plugin | Sufijos de clave key_one / key_other y {{name}}; ICU vía i18next-icu |
| vue-i18n | Parcial / propio | Interpolación {name} y ramas de plural separadas por barras |
Angular ($localize) | Subconjunto | ICU plural / select dentro de plantillas, extraído a XLIFF |
Algunas aclaraciones para interpretar la tabla con precisión:
- La sintaxis predeterminada de i18next no es ICU, y no por ello es peor. Los sufijos (
item_one,item_few) se corresponden con las categorías deIntl.PluralRulesy son a menudo más fáciles de editar para los traductores en JSON plano. Peroselecty el anidamiento complejo no forman parte de este modelo, por lo que requieresi18next-icuo gestionar la lógica en el código. - Los plurales con barras de vue-i18n utilizan por defecto una función de reglas por locale, no las categorías CLDR. Funciona, pero la regla vive en la configuración de la app en vez de en los datos.
- FormatJS es la implementación de referencia en JS. Cuando se menciona "ICU MessageFormat" en un contexto de JavaScript, normalmente se alude a lo que FormatJS acepta.
- El soporte completo de ICU tiene un coste en el bundle. El parser y el manejo de skeletons añaden alrededor de 10 KB de JavaScript comprimido. Consulta por qué ICU no está hecho para JavaScript.
Cómo lo resuelve Intlayer
Intlayer no utiliza un DSL en cadenas de texto. Los operadores condicionales son funciones dentro de un archivo de declaración de contenido, por lo que la estructura está tipada y cada locale declara únicamente las categorías que su gramática necesita:
Copiar el código al portapapeles
Copiar el código al portapapeles
La correspondencia con los conceptos de ICU es directa:
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Concepto ICU | Intlayer |
|---|---|
{name} | insert("Hello {{name}}") o autodetección |
{count, plural, …} | plural({ one, few, many, other }) |
{value, select, …} | select({ draft, published, fallback }) |
rama de género en select | gender({ male, female, fallback }) |
rama booleana en select | cond({ true, false }) |
| rangos numéricos (no-CLDR) | enu({ "0": …, ">5": …, fallback: … }) |
{n, number, ::currency/EUR} | useCurrency()(1234.5, { currency: "EUR" }) |
plural delega la selección de categorías en Intl.PluralRules, por lo que la tabla CLDR anterior se aplica sin modificaciones. El formateo se mantiene independiente: números, fechas, monedas y listas se manejan mediante hooks de formato en lugar de incrustarse en el mensaje.
Límites transparentes:
- Intlayer requiere un paso de compilación: el compilador extrae las declaraciones durante el build. Si buscas JSON plano cargado en tiempo de ejecución, se trata de un modelo diferente.
pluralno admite anidar unt()dentro de sus ramas por el momento: envuelvespluraldentro det(), y no al revés.- El ecosistema es más reciente que el de i18next, con menos integraciones directas con TMS o respuestas en foros.
Si provienes de una base de código que ya contiene cadenas ICU reales, el adaptador de compatibilidad react-intl las analiza directamente: plural, select, selectordinal, # y los argumentos tradicionales number, date, time. Los skeletons y la opción offset: no están cubiertos por ese analizador, así que conviene revisar esos mensajes al migrar. El adaptador de i18next resuelve la forma con sufijos (key_one, key_male) mediante Intl.PluralRules.
Errores habituales
- Codificar la lógica de plural en JS.
count === 1 ? a : bproduce resultados incorrectos para 8 de los 10 idiomas de la tabla anterior. Una vez que el operador ternario está en el código, ningún traductor puede arreglarlo. - Concatenar fragmentos traducidos. El orden de las palabras, las concordancias gramaticales y la separación antes de los signos de puntuación dependen de la locale. Mantén la oración completa.
- Omitir
other. Es obligatorio según la especificación, no una convención optativa. La mayoría de los analizadores rechazarán el mensaje y el resto no mostrará nada. - Asumir que tus categorías se extrapolan. Que el archivo original en inglés use
oneyotherno implica que el polaco tenga dos ramas. Deja que cada locale declare las suyas. Consulta la declaración de contenido por locale. - Usar
=1donde correspondíaone.=1coincide solo con el número 1 exacto. En ruso, 21 necesitaone, y=1nunca se activará para ese caso. - Colocar
#fuera de una rama de plural. Solo tiene un significado especial dentro depluraloselectordinal. En cualquier otro lugar es una simple almohadilla. - Olvidar que
#ya está formateado. Si necesitas el número sin formato, interpola el argumento por su nombre.
Para profundizar
Comentarios
Aún no hay comentarios. Sé el primero en compartir tus pensamientos.
