Categorías: Labs

El API de internacionalización de JavaScript

Esta es una traducción del artículo original publicado en el blog de Mozilla Hacks. Traducción por Giovanni Céspedes.

Firefox 29 fue lanzado hace años, por lo que este artículo lleva pendiente mucho tiempo. Sin embargo, yo quería hacer una pausa por un segundo para hablar del API de internacionalización incluida por primera vez en en esa versión para escritorio (¡y pasando todas las pruebas!). Norbert Lindenberg escribió la mayor parte de la implementación, yo la revisé y ahora lo mantengo. (El trabajo de Makoto Kato podrá traerlo para Android, b2g puede tomar más tiempo debido a algunos obstáculos específicos de b2g. Mantente atento.)

¿Qué es la internacionalización?

Internacionalización (i18n para abreviar — i, 18 caracteres, n) es el proceso de escribir aplicaciones de manera que les permitan ser fácilmente adaptables para audiencias de diferentes lugares, usando una variedad de lenguajes. Es fácil cometer estos errores inadvertidamente asumiendo que los usuarios vienen de sólo un lugar y hablan un idioma, especialmente si ni siquiera sabes que has hecho una suposición.

function formatDate(d)
{
  // Todos usan mes/día/año...¿cierto?
  var month = d.getMonth() + 1;
  var date = d.getDate();
  var year = d.getFullYear();
  return month + "/" + date + "/" + year;
}
 
function formatMoney(amount)
{
  // Todo el dinero es dólares con dos dígitos de fracción...¿cierto?
  return "$" + amount.toFixed(2);
}
 
function sortNames(names)
{
  function sortAlphabetically(a, b)
  {
    var left = a.toLowerCase(), right = b.toLowerCase();
    if (left > right)
      return 1;
    if (left === right)
      return 0;
    return -1;
  }
 
  // Los nombres siempre se ordenan alfabéticamente...¿cierto?
  names.sort(sortAlphabetically);
}

El soporte de i18n en JavaScript ha sido históricamente pobre

El formateo con soporter i18n en el JS tradicional usa los distintos métodos toLocaleString(). Las cadenas resultantes contienen cualquier detalle que la implementación decide ofrecer: no hay manera de escoger (¿necesitas el día de la semana en ese formato de fecha? ¿el año es relevante? Incluso si fueran incluidos los datos correctos, el formato puede estar equivocado, por ejemplo decimal en vez de porcentaje. Y no puedes escoger la localización.

En cuanto al ordenamiento, JS no ofrece casi ninguna función útil de comparación de texto sensible a la localización (colación).  localeCompare() existe pero con una interfaz muy incómoda e inadecuada para usar con sort. Y éste tampoco permite la elección de localización o un ordenamiento específico.

Estas limitaciones son suficientemente malas que — ¡esto me dio una gran sorpresa cuando lo aprendí! — las aplicaciones web serias que necesitan capacidades del i18s (más comúnmente, los sitios financieros con despliegue de dinero) encapsulan los datos, los envían a un servidor, hacen que el servidor realice la operación, y lo envían de vuelta al cliente. Viajes redondos a un servidor solo para darle formato a montos de dinero. Vaya.

Una nueva API de Internacionalización JS

La nueva API de internacionalización ECMAScript mejora en gran medida las capacidades de i18n de JavaScript. Proporciona todos los detalles que uno podría desear para formatear fechas, números y ordenamiento de texto. La localización es seleccionable, con opción de respaldo si la localización no está disponible. Las solicitudes de formato pueden especificar los componentes particulares a incluir. Se admiten formatos personalizados para porcentajes, cifras significativas, y moneda. Numerosas opciones de colación se exponen para su uso en la colación de texto. Y si te preocupas por el rendimiento, el trabajo por adelantado para seleccionar una localización y las opciones de proceso se puede hacer una sola vez, en vez de que se realice cada vez que se realiza una operación dependiente de la localización.

Dicho esto, el API no es una panacea. El API es solamente “el mejor esfuerzo”. Las salidas precisas casi nunca están definidas, deliberadamente. Una aplicación puede legalmente tener soporte solamente para la localización oj, o podría pasar por alto (casi todas) las opciones de formato proporcionadas. La mayoría de las implementaciones tendrán soporte de alta calidad para muchas localizaciones, pero no está garantizado (en particular en los sistemas de recursos limitados como el móvil).

Bajo el capó, la implementación de Firefox depende de la librería International Components for Unicode (ICU), que a su vez depende de la colección de datos de localización Unicode Common Locale Data Repository (CLDR). Nuestra implementación es self-hosted: la mayor parte de la implementación sobre ICU está escrita en JavaScript. Golpeamos algunos baches en el camino (no hemos alojado algo tan grande antes), pero nada muy grande.

La interface Intl

La API i18n vive en el objeto global Intl. Intl contiene tres constructores: Intl.Collator, Intl.DateTimeFormat, y Intl.NumberFormat. Cada constructor crea un objeto exponiendo la operación correspondiente, guardando eficientemente la configuración de localización y las opciones de operación. La creación de uno de estos objetos sigue este patrón:

var ctor = "Collator"; // o los otros
var instance = new Intl[ctor](locales, options);

locales es una cadena que especifica una sola etiqueta de lenguaje o un objeto tipo arreglo que contiene múltiples etiquetas de idioma. Las etiquetas de idioma son cadenas como en (inglés en general), de-AT (el alemán usado en Austria), o zh-Hant-TW (el chino utilizado en Taiwán, con la escritura tradicional china). Las etiquetas de lenguaje pueden incluir también una “extensión Unicode”, de la forma -u-key1-value1-key2-value2..., donde cada llave es una “llave de extensión”. Los distintos constructores los interpretan especialmente.

options es un objeto cuyas propiedades (o su ausencia, mediante el valor undefined) determina cómo es el procedimiento del formateador o comparador. Su interpretación exacta está determinada por cada constructor individual.

Teniendo en cuenta la información y las opciones de configuración local, la aplicación intentará producir el comportamiento más cercano que pueda para llegar al “ideal”. Firefox es compatible con más de 400 lugares para colación y más de 600 regiones para fecha/hora y formato de número, por lo que es muy probable (pero no garantizado) que las regiones que te interesan son compatibles.

Intl generalmente no provee garantía para un determinado comportamiento. Si la región requerida no tiene soporte, Intl permite intentar con el mejor comportamiento posible. Incluso si la región tiene soporte, el procedimiento no es rígidamente especificado. Nunca asumas que un conjunto particular de opciones corresponde a un comportamiento particular. La redacción del formato general (que abarca todos los componentes requeridos) puede variar a través de los navegadores, o incluso a través de versiones de navegadores. Los formatos de los componentes individuales no están especificados: un día en formato short-format puede ser “S”, “Sa”, o “Sat”. El API Intl no tiene la intención de tener un comportamiento especificado de manera exacta.

Formato Fecha/tiempo

Opciones

Las opciones de propiedades primarias para el formato fecha/tiempo son:

weekday, era
"narrow", "short", o "long". (era se refiere a las divisiones normalmente más largas de los años en un sistema de calendario: AC/DC, la era japonesa actual, u otros.)
month
"2-digit", "numeric", "narrow", "short", o "long"
year
day
hour, minute, second
"2-digit" o "numeric"
timeZoneName
"short" o "long"
timeZone
"UTC" (insensible a capitalización) da formato con respecto al UTC. Valores como "CEST" y "America/New_York" no tienen que tener soporte, y no funcionan actualmente en Firefox.

Los valores no muestran un formato particular: recuerda, el API Intl casi nunca especifica el procedimiento exacto. Pero la intención está en que "narrow", "short", y "long" produzcan salidas del tamaño correspondiente — “S” o “Sa”, “Sat”, y “Saturday”, por ejemplo. (Tal vez las salidas sean ambiguas: Saturday y Sunday, ambos pueden producir “S”.) "2-digit" y "numeric" muestran cadenas numéricas de dos dígitos o largas cadenas numéricas: “70” y “1970”, por ejemplo.

Las opciones utilizadas a fin de cuentas son generalmente aquellas que fueron solicitadas. Sin embargo, si no se especifica ningún requerimiento weekday/year/month/day/hour/minute/second, entonces year/month/day se añadirán a tus opciones proporcionadas.

Más allá de estas opciones básicas, aquí hay algunas opciones especiales:

hour12
Especifica si las horas estarán en el formato de 12 horas o 24 horas (hora militar). El valor predeterminado es típicamente dependiente de la región. (Detalles tales como si la medianoche está basada en cero o doce y si los ceros a la izquierda también están presentes depende de la región.)

Existen también dos propiedades especiales, localeMatcher (que puede ser "lookup" o "best fit") y formatMatcher (que puede ser "basic" o "best fit"), cada uno con "best fit" por defecto. Esto afecta cómo la región y el formato correcto son elegidos. El uso de casos como estos son un tanto esotéricos, por lo que probablemente deberían ser ignorados.

Opciones centradas en localización

DateTimeFormat también permite dar formato usando calendarios y sistemas de numeración personalizados. Estos detalles son efectivamente parte de la localización, por lo que están especificados en la extensión Unicode en la etiqueta de idioma.

Por ejemplo, el tailandés como lengua de Tailandia tiene la etiqueta de idioma th-TH. Recordemos que una extensión Unicode tiene el formato-u-key1-value1-key2-value2.... La llave del sistema de calendario es ca, y la llave de sistema de numeración es nu. El sistema de numeración tailandés tiene el valor de thai, y el sistema de calendario chino tiene e valor chinese. Así que para dar formato para las fechas de esta manera, damos la siguiente extensión Unicode que contiene estos dos pares clave/valor en el extremo de la etiqueta de idioma: th-TH-u-ca-chinese-nu-thai.

Para más información sobre los diferentes sistemas de calendario y numeración, ver la documentación completa de DateTimeFormat.

Ejemplos

Después de crear un objeto DateTimeFormat, el siguiente paso es usarlo para formatear fechas a través de la función format(). Convenientemente, esta funciones una función bound: no tienes que llamar directamente a DateTimeFormat. Luego le proporcionas un objeto timestamp o Date.

Poniendo todo junto, aquí tienes algunos ejemplos de cómo crear opciones de DateTimeFormat para un uso particular, con el comportamiento actual de FireFox.

var msPerDay = 24 * 60 * 60 * 1000;
 
// July 17, 2014 00:00:00 UTC.
var july172014 = new Date(msPerDay * (44 * 365 + 11 + 197));

Vamos a dar formato a una fecha en el idioma inglés de Estados Unidos. Vamos a incluir mes/día/año de dos dígitos, más dos dígitos de horas/minutos, y una zona horaria corta para aclarar la hora. (El resultado sería obviamente diferente en otra zona horaria.)

var options =
  { year: "2-digit", month: "2-digit", day: "2-digit",
    hour: "2-digit", minute: "2-digit",
    timeZoneName: "short" };
var americanDateTime =
  new Intl.DateTimeFormat("en-US", options).format;
 
print(americanDateTime(july172014)); // 07/16/14, 5:00 PM PDT

O vamos a hacer algo similar para Portugués-idealmente cómo es usado en Brasil, pero en un apuro el de Portugal funciona. Vamos a dar un formato más largo, con todo el año y el mes deletreado, pero hecho con UTC para la portabilidad.

var options =
  { year: "numeric", month: "long", day: "numeric",
    hour: "2-digit", minute: "2-digit",
    timeZoneName: "short", timeZone: "UTC" };
var portugueseTime =
  new Intl.DateTimeFormat(["pt-BR", "pt-PT"], options);
 
// 17 de julho de 2014 00:00 GMT
print(portugueseTime.format(july172014));

¿Qué tal un formato compacto UTC para un horario semanal de trenes suizo? Vamos a tratar los idiomas oficiales de más a menos populares para elegir el que sea más probablemente legible.

var swissLocales = ["de-CH", "fr-CH", "it-CH", "rm-CH"];
var options =
  { weekday: "short",
    hour: "numeric", minute: "numeric",
    timeZone: "UTC", timeZoneName: "short" };
var swissTime =
  new Intl.DateTimeFormat(swissLocales, options).format;
 
print(swissTime(july172014)); // Do. 00:00 GMT

O vamos a intentar una fecha en un texto descriptivo de un cuadro en un museo japonés, utilizando el calendario japonés con año y era:

var jpYearEra =
  new Intl.DateTimeFormat("ja-JP-u-ca-japanese",
                          { year: "numeric", era: "long" });
 
print(jpYearEra.format(july172014)); // 平成26年

Y para algo completamente diferente, una fecha para su uso en tailandés como se utiliza en Tailandia – pero utilizando el sistema de numeración de Tailandia y calendario chino. (Implementaciones de calidad como la de Firefox tratarían th-TH como th-TH-u-ca-buddhist-nu-latn, imputando el sistema de calendario budista típico de Tailandia y los números latinos 0-9.)

var options =
  { year: "numeric", month: "long", day: "numeric" };
var thaiDate =
  new Intl.DateTimeFormat("th-TH-u-nu-thai-ca-chinese", options);
 
print(thaiDate.format(july172014)); // ๒๐ 6 ๓๑

Dejando de lado el sistema de calendario y numeración, es relativamente simple. Sólo tienes que elegir tus componentes y longitudes.

Formato de numeración

Opciones

Las propiedades de opciones principales para el formato de número son las siguientes:

style
"currency", "percent", o "decimal" (por defecto) para dar formato a un valor de ese tipo.
currency
Un código de moneda de tres letras, por ejemplo USD o CHF. Requerido si style es "currency", de lo contrario carece de sentido.
currencyDisplay
"code", "symbol", o "name", por defecto "symbol""code" utilizará el código de moneda de tres letras en la cadena formateada. "symbol" usaría un símbolo de moneda como $ o £. "name" normalmente utiliza una versión deletreada de la moneda.
minimumIntegerDigits
Un número entero del 1 a 21 (inclusivo), con 1 por defecto. La cadena resultante es rellenada por delante con ceros hasta que su componente entero contenga por lo menos esta cantidad de dígitos. (Por ejemplo, si éste valor fuera 2, formatear 3 podría producir “03”.)
minimumFractionDigits, maximumFractionDigits
Un número entero de 0 a 20 (inclusive). La cadena resultante tendrá al menos minimumFractionDigits y no más de maximumFractionDigits, dígitos fraccionarios. El valor por defecto mínimo es (dependiente de la moneda generalmente 2, raramente 0 o 3) si style es "currency", de lo contrario 0. El máximo valor por defecto es 0 para porcentajes, 3 para decimales y dependiente de la moneda para montos monetarios.
minimumSignificantDigits, maximumSignificantDigits
Un números entero de 1 a 21 (inclusive). Si está presente, esto sobreescribe los controles de dígitos enteros/fraccionarios anteriores para determinar las cifras significativas de mínimos/máximos en la cadena con formato de número, en acuerdo con el número de lugares decimales requeridos para especificar con precisión el número. (Nota que los dígitos significativos en un múltiplo de 10 pueden ser ambiguos, como en “100” con sus uno, dos o tres dígitos significativos).
useGrouping
Booleano (por defecto true) determinando si la cadena con formato contiene separadores de agrupación (por ejemplo “,” como separador de miles en inglés).

NumberFormat también reconoce la esotérica propiedad localeMatcher, en su mayoría ignorable.

Opciones centradas en localización

Al igual que DateTimeFormat soporta sistemas de numeración personalizados en la extensión de Unicode usando la llave de nu, así también lo hace NumberFormat. Por ejemplo, la etiqueta de idioma de chino en China es zh-CN. El valor para el sistema de numeración decimal Han es hanidec. Para dar formato a los números con estos sistemas, usamos una extensión de Unicode en la etiqueta de idioma: zh-CN-u-nu-hanidec.

Para obtener información sobre cómo especificar los diferentes sistemas de numeración, lee la documentación completa de NumberFormat.

Ejemplos

Los objetos NumberFormat tienen una función format al igual que los objetos DateTimeFormat. Y como allí, la función format es una función bound que se puede utilizar en el aislamiento del NumberFormat.

Aquí algunos ejemplos de cómo crear opciones NumberFormat para usos específicos, con el comportamiento de Firefox. Primero vamos a formatear algo de dinero para su uso en chino como en China, específicamente utilizando números decimales Han (en vez de los más comunes números latinos). Selecciona el estilo "currency", utiliza el código para el renminbi chino (yuan), agrupación por defecto, con el habitual número de dígitos fraccionarios.

var hanDecimalRMBInChina =
  new Intl.NumberFormat("zh-CN-u-nu-hanidec",
                        { style: "currency", currency: "CNY" });
 
print(hanDecimalRMBInChina.format(1314.25)); // ¥ 一,三一四.二五

O vamos a dar formato a un precio de gasolina al estilo de Estados Unidos, con su peculiar 9 en las milésimas, para el uso en inglés como se utiliza en los Estados Unidos.

var gasPrice =
  new Intl.NumberFormat("en-US",
                        { style: "currency", currency: "USD",
                          minimumFractionDigits: 3 });
 
print(gasPrice.format(5.259)); // $5.259

O vamos a intentar un porcentaje en árabe, diseñado para su uso en Egipto. Asegúrate que el porcentaje tenga al menos dos dígitos fraccionarios. (Ten en cuenta que éste y todos los otros ejemplos pueden aparecer con diferentes pedidos en contexto RTL (lectura de derecha a izquierda), por ejemplo  ٤٣٫٨٠٪ en vez de ٤٣٫٨٠٪.)

var arabicPercent =
  new Intl.NumberFormat("ar-EG",
                        { style: "percent",
                          minimumFractionDigits: 2 }).format;
 
print(arabicPercent(0.438)); // ٤٣٫٨٠٪

O supongamos que estamos usando formato persa en Afganistán, y queremos que al menos dos dígitos enteros y no más de dos dígitos fraccionarios.

var persianDecimal =
  new Intl.NumberFormat("fa-AF",
                        { minimumIntegerDigits: 2,
                          maximumFractionDigits: 2 });
 
print(persianDecimal.format(3.1416)); // ۰۳٫۱۴

Por último, vamos a dar formato a una cantidad de dinares bareinís, para el árabe en Bahrein. Inusualmente en comparación a la mayoría de las divisas, lo dinares bareinís se dividen en milésimas (fils), por lo que nuestro número tendrá tres lugares. (Otra vez ten en cuenta que el ordenamiento visual aparente puede variar).

var bahrainiDinars =
  new Intl.NumberFormat("ar-BH",
                        { style: "currency", currency: "BHD" });
 
print(bahrainiDinars.format(3.17)); // د.ب.‏ ٣٫١٧٠

Colación

Opciones

Las principales opciones de colación son las siguientes:

usage
"sort" o "search" (por defecto "sort"), especificando el uso de este Collator. (Un objeto tipo search puede que considere más cadenas como equivalentes que un objeto de tipo sort.)
sensitivity
"base", "accent", "case", o "variant". Esto determina qué tan sensible es el objeto a los caracteres que tienen la misma “letra base” pero tienen diferentes acentos/diacríticos y/o capitalización. (Las letras base varían según la localización: “a” y “ä” tienen la misma letra base en alemán pero son distintas en sueco.) La sensibilidad "base" solo considera la letra base, ignorando las modificaciones (así que en alemán “a”, “A”, y “ä” son consideradas la misma). "accent" considera la letra base y los acentos, pero ignora la capitalización (en alemán “a” y “A” son la misma, pero “ä” es diferente a ambas). "case" considera la letra base y la capitalización, pero ignora los acentas (en alemán “a” y “ä” son iguales, pero “A” es diferente a ambas). Finalmente, "variant" considera la letra base, acentos, y capitalización (en alemán “a”, “ä” y “A” todas difieren). Si usage es "sort", el valor por defecto es "variant"; de otro modo depende de la localización.
numeric
Booleano (por defecto false) que determina si números completos en cadenas deben ser considerados a la hora de ordenar. Por ejemplo, el ordenamiento numérico puede producir  "F-4 Phantom II", "F-14 Tomcat", "F-35 Lightning II"; ordenamiento no-numérico podría producir "F-14 Tomcat", "F-35 Lightning II", "F-4 Phantom II".
caseFirst
"upper", "lower", o "false" (por defecto). Determina cómo se considera la capitalización al ordenar: "upper" coloca letras mayúsculas primero ("B", "a", "c"), "lower" coloca las minúsculas primero ("a", "c", "B"), y "false" ignora la capitalización por completo ("a", "B", "c"). (Nota: Firefox actualmente ignora esta propiedad.)
ignorePunctuation
Booleano (por defecto false) que determina si se debe ignorar la puntuación cuando se realiza la comparación (por ejemplo, para que "biweekly" y "bi-weekly" sean equivalentes).

Y está la misma propiedad localeMatcher que probablemente puedes ignorar.

Opciones centradas en localización

La principal opción Collator de las extensiones Unicode de localización es co, la cual selecciona el tipo de ordenamiento para llevar a cabo: libreta de teléfonos (phonebk), diccionario (dict), y muchos otros.

Adicionalmente, la llaves kn y kf pueden, opcionalmente, duplicar las propiedades numeric y caseFirst del objeto options. Pero no está garantizado que se soportan en la etiqueta de idioma, y options es mucho más claro que los componentes de etiqueta de idioma. Así que lo mejor es sólo ajustar estas opciones a través de options.

Estos pares de llave y valor están incluidos en la extensión de Unicode del mismo modo para que se han incluido DateTimeFormat y NumberFormat; consulta esas secciones sobre cómo especificar una etiqueta de idioma.

Ejemplos

Los objetos Collator tienen una propiedad de función compare. Esta función acepta dos argumentos x y y y retorna un número menor a cero si x compara como menor que y, 0 si x compara igual que y, o un número mayo a cero si x compara mayor que y. Como con las funciones format, compare es una función bound que se puede extraer para uso independiente.

Vamos a intentar ordenando algunos apellidos alemanes, para su uso en alemán como en Alemania. Existen dos órdenes diferentes en alemán, guía telefónica y diccionario. El tipo guía telefónica destaca el sonido, y es como si “ä”, “ö” y así sucesivamente se ampliaron a “ae”, “oe”, y así sucesivamente antes de ordenar.

var names =
  ["Hochberg", "Hönigswald", "Holzman"];
 
var germanPhonebook = new Intl.Collator("de-DE-u-co-phonebk");
 
// si ordenamos ["Hochberg", "Hoenigswald", "Holzman"]:
//   Hochberg, Hönigswald, Holzman
print(names.sort(germanPhonebook.compare).join(", "));

Algunas palabras alemanas conjugan con diéresis extras, por lo que en los diccionarios es sensato ordenar ignorando diéresis (excepto cuando se ordenan palabras que difieren sólo por diéresis: schon antes de schön).

var germanDictionary = new Intl.Collator("de-DE-u-co-dict");
 
// si ordenamos ["Hochberg", "Honigswald", "Holzman"]:
//   Hochberg, Holzman, Hönigswald
print(names.sort(germanDictionary.compare).join(", "));

O vamos a ordenar una lista de versiones de Firefox con errores varios (diferentes capitalizaciones, aleatorios acentos y marcas diacríticas, guiones extra), en inglés como se utiliza en los Estados Unidos. Queremos ordenar respetando el número de versión, así que incluímos ordenamiento numérico, no considerando los dígitos carácter por carácter.

var firefoxen =
  ["FireFøx 3.6",
   "Fire-fox 1.0",
   "Firefox 29",
   "FÍrefox 3.5",
   "Fírefox 18"];
 
var usVersion =
  new Intl.Collator("en-US",
                    { sensitivity: "base",
                      numeric: true,
                      ignorePunctuation: true });
 
// Fire-fox 1.0, FÍrefox 3.5, FireFøx 3.6, Fírefox 18, Firefox 29
print(firefoxen.sort(usVersion.compare).join(", "));

Por último, vamos a hacer una búsqueda de cadena localizada que ignora la capitalización y los acentos, otra vez en inglés en los Estados Unidos.

// Las comparaciones funcionan con las formas compuestas y descompuestas.
var decoratedBrowsers =
  [
   "A\u0362maya",  // A͢maya
   "CH\u035Brôme", // CH͛rôme
   "FirefÓx",
   "sAfàri",
   "o\u0323pERA",  // ọpERA
   "I\u0352E",     // I͒E
  ];
 
var fuzzySearch =
  new Intl.Collator("en-US",
                    { usage: "search", sensitivity: "base" });
 
function findBrowser(browser)
{
  function cmp(other)
  {
    return fuzzySearch.compare(browser, other) === 0;
  }
  return cmp;
}
 
print(decoratedBrowsers.findIndex(findBrowser("Firêfox"))); // 2
print(decoratedBrowsers.findIndex(findBrowser("Safåri")));  // 3
print(decoratedBrowsers.findIndex(findBrowser("Ãmaya")));   // 0
print(decoratedBrowsers.findIndex(findBrowser("Øpera")));   // 4
print(decoratedBrowsers.findIndex(findBrowser("Chromè")));  // 1
print(decoratedBrowsers.findIndex(findBrowser("IË")));      // 5

Cabos sueltos

Puede ser útil determinar si hay soporte para alguna operación para localizaciones particulares, o para determinar si una localización es compatible. Intl provee funciones supportedLocales() en cada constructor, y funciones resolvedOptions() en cada prototipo, para exponer esta información.

var navajoLocales =
  Intl.Collator.supportedLocalesOf(["nv"], { usage: "sort" });
print(navajoLocales.length > 0
      ? "El ordenamiento en navajo tiene soporte"
      : "El ordenamiento en navajo no tiene soporte");
 
var germanFakeRegion =
  new Intl.DateTimeFormat("de-XX", { timeZone: "UTC" });
var usedOptions = germanFakeRegion.resolvedOptions();
print(usedOptions.locale);   // de
print(usedOptions.timeZone); // UTC

Comportamiento heredado

Las funciones ES5 estilo toLocaleString y localeCompare previas no tenían una semántica específica, no aceptaban opciones particulares y eran en gran parte inútiles. Así que el API i18n las reformula en términos de operaciones Intl. Cada método acepta ahora acepta adicionalmente argumentos locales y options, interpretados como los constructores Intl. (Excepto que para toLocaleTimeString y toLocaleDateString, se utilizan componentes predeterminados diferentes si no se utilizan opciones.)

Para uso breve donde el comportamiento exacto no importa, está bien utilizar los métodos antiguos. Pero si necesitas más control o var a dar formato o comparar muchas veces, es mejor usar directamente las primitivas Intl.

Conclusión

La internacionalización es un tema apasionante, cuya complejidad está limitada sólo por la variada naturaleza de la comunicación humana. El API de internacionalización trata una porción pequeña pero muy útil de esa complejidad, haciéndolo más fácil producir aplicaciones web sensibles a la localización. ¡Vé a usarlo!

(Y un agradecimiento especial a Norbert Lindenberg, Anas El Husseini, Simon Montagu, Gary Kwong, Guo Shu-yu, Ehsan Akhgari, el pueblo de #mozilla.de y quien pueda haber olvidado (¡lo sentimos!) que dieron comentarios en este artículo o me ayudaron en la producción y criticando los ejemplos.)

The following two tabs change content below.

jorgev

Add-ons Developer Relations Lead at Mozilla
Jorge trabaja para el equipo de complementos de Mozilla, y se dedica a Mozilla Hispano y Mozilla Costa Rica en su tiempo libre. Actualmente está encargado del blog de Mozilla Hispano Labs.