API Intl.Collator
Ordena y compara cadenas correctamente en diferentes idiomas
Introducción
La ordenación de cadenas en JavaScript parece sencilla hasta que te encuentras con texto internacional. La comparación predeterminada de cadenas utiliza valores de punto de código Unicode, produciendo resultados incorrectos para muchos idiomas. La API Intl.Collator proporciona una comparación de cadenas adaptada al idioma que respeta las reglas de ordenación cultural y maneja correctamente los caracteres especiales.
Por qué falla la ordenación predeterminada
Considera ordenar una lista de nombres alemanes:
const names = ["Zoe", "Ava", "Ärzte", "Änder"];
console.log(names.sort());
// ["Ava", "Zoe", "Änder", "Ärzte"]
Este resultado es incorrecto para los hablantes de alemán. En alemán, los caracteres con diéresis como ä deberían ordenarse cerca de su letra base a, no al final. El problema radica en que JavaScript compara valores de punto de código Unicode, donde Ä (U+00C4) viene después de Z (U+005A).
Diferentes idiomas tienen diferentes reglas de ordenación. El sueco ordena ä al final del alfabeto, el alemán lo ordena cerca de a, y el francés trata los caracteres acentuados de manera diferente. La comparación binaria ignora estas convenciones culturales.
Cómo funciona la colación de cadenas
La colación es el proceso de comparar y ordenar cadenas según reglas específicas del idioma. El Algoritmo de Colación Unicode define cómo comparar cadenas analizando caracteres, diacríticos, mayúsculas y minúsculas, y puntuación por separado.
Al comparar dos cadenas, una función de colación devuelve un número:
- Valor negativo: la primera cadena va antes que la segunda
- Cero: las cadenas son equivalentes para el nivel de sensibilidad actual
- Valor positivo: la primera cadena va después de la segunda
Este patrón de comparación de tres vías funciona con Array.sort y permite un control preciso sobre qué diferencias importan.
Uso de localeCompare para ordenación básica adaptada al idioma
El método localeCompare proporciona comparación de cadenas adaptada al idioma:
const names = ["Zoe", "Ava", "Ärzte", "Änder"];
console.log(names.sort((a, b) => a.localeCompare(b, "de")));
// ["Ava", "Änder", "Ärzte", "Zoe"]
Esto produce una ordenación correcta en alemán. El primer parámetro especifica el idioma, y localeCompare maneja las reglas culturales automáticamente.
Puedes pasar opciones como tercer parámetro:
const items = ["File10", "File2", "File1"];
console.log(items.sort((a, b) =>
a.localeCompare(b, "en", { numeric: true })
));
// ["File1", "File2", "File10"]
La opción numeric permite una ordenación natural donde "2" viene antes que "10". Sin ella, "10" se ordenaría antes que "2" porque "1" viene antes que "2".
El problema de rendimiento con localeCompare repetido
Cada llamada a localeCompare procesa la configuración de localización desde cero. Al ordenar arrays grandes, esto crea una sobrecarga significativa:
// Ineficiente: procesa la localización para cada comparación
const sorted = items.sort((a, b) => a.localeCompare(b, "de"));
Ordenar 1000 elementos requiere aproximadamente 10000 comparaciones. Cada comparación recrea la configuración de localización, multiplicando el costo de rendimiento. Esta sobrecarga se vuelve notable en interfaces de usuario con grandes conjuntos de datos.
Uso de Intl.Collator para una comparación eficiente de cadenas
Intl.Collator crea un objeto de comparación reutilizable que procesa la configuración de localización una sola vez:
const collator = new Intl.Collator("de");
const sorted = items.sort((a, b) => collator.compare(a, b));
La instancia del collator almacena la configuración de localización y las reglas de comparación. El método compare utiliza estas reglas precomputadas para cada comparación, eliminando la sobrecarga de inicialización repetida.
Las mejoras de rendimiento oscilan entre el 60% y el 80% al ordenar arrays grandes en comparación con llamadas repetidas a localeCompare.
Acceso directo al método compare
Puedes pasar el método compare directamente a sort:
const collator = new Intl.Collator("de");
const sorted = items.sort(collator.compare);
Esto funciona porque compare está vinculado a la instancia del collator. El método recibe dos cadenas y devuelve el resultado de la comparación, coincidiendo con la firma que Array.sort espera.
Entendiendo los niveles de sensibilidad
La opción sensitivity controla qué diferencias de caracteres importan durante la comparación. Hay cuatro niveles:
Sensibilidad base
La sensibilidad base ignora acentos y mayúsculas:
const collator = new Intl.Collator("en", { sensitivity: "base" });
console.log(collator.compare("a", "a")); // 0
console.log(collator.compare("a", "á")); // 0
console.log(collator.compare("a", "A")); // 0
console.log(collator.compare("a", "b")); // -1
Solo difieren las letras base. Este nivel funciona bien para búsquedas aproximadas donde los usuarios podrían no escribir los acentos correctamente.
Sensibilidad a los acentos
La sensibilidad a los acentos considera los acentos pero ignora las mayúsculas y minúsculas:
const collator = new Intl.Collator("en", { sensitivity: "accent" });
console.log(collator.compare("a", "a")); // 0
console.log(collator.compare("a", "á")); // -1
console.log(collator.compare("a", "A")); // 0
console.log(collator.compare("á", "A")); // 1
Los caracteres acentuados y no acentuados difieren. Las versiones en mayúsculas y minúsculas de la misma letra coinciden.
Sensibilidad a mayúsculas y minúsculas
La sensibilidad a mayúsculas y minúsculas considera las mayúsculas pero ignora los acentos:
const collator = new Intl.Collator("en", { sensitivity: "case" });
console.log(collator.compare("a", "a")); // 0
console.log(collator.compare("a", "á")); // 0
console.log(collator.compare("a", "A")); // -1
console.log(collator.compare("á", "Á")); // -1
Las diferencias de mayúsculas y minúsculas importan pero los acentos se ignoran. Este nivel es menos común en la práctica.
Sensibilidad a variantes
La sensibilidad a variantes considera todas las diferencias:
const collator = new Intl.Collator("en", { sensitivity: "variant" });
console.log(collator.compare("a", "a")); // 0
console.log(collator.compare("a", "á")); // -1
console.log(collator.compare("a", "A")); // -1
console.log(collator.compare("á", "Á")); // -1
Este es el valor predeterminado para la ordenación. Cada diferencia de caracteres produce un resultado de comparación distinto.
Elegir la sensibilidad según el caso de uso
Diferentes escenarios necesitan diferentes niveles de sensibilidad:
- Ordenar listas: Utilizar sensibilidad a variantes para mantener un orden estricto
- Buscar contenido: Utilizar sensibilidad básica para coincidir independientemente de acentos o mayúsculas
- Filtrar opciones: Utilizar sensibilidad a acentos cuando las mayúsculas no deben importar
- Búsqueda sensible a mayúsculas: Utilizar sensibilidad a mayúsculas cuando los acentos no deben importar
La opción usage proporciona configuraciones de sensibilidad predeterminadas para escenarios comunes.
Usar usage para modos de ordenación y búsqueda
La opción usage optimiza el comportamiento del collator para ordenación o búsqueda:
// Optimizado para ordenación
const sortCollator = new Intl.Collator("en", { usage: "sort" });
// Optimizado para búsqueda
const searchCollator = new Intl.Collator("en", { usage: "search" });
El uso de sort establece por defecto la sensibilidad a variantes, asegurando que cada diferencia produzca un orden consistente. El uso de search optimiza para encontrar coincidencias, típicamente utilizando una sensibilidad más relajada.
Para búsquedas insensibles a mayúsculas y acentos:
const collator = new Intl.Collator("en", {
usage: "search",
sensitivity: "base"
});
const items = ["Apple", "Äpfel", "Banana"];
const matches = items.filter(item =>
collator.compare(item, "apple") === 0
);
console.log(matches); // ["Apple"]
Este patrón permite la coincidencia aproximada donde los usuarios no necesitan escribir caracteres exactos.
Habilitando la ordenación numérica para un orden natural
La opción numérica trata los números incrustados como valores numéricos:
const collator = new Intl.Collator("en", { numeric: true });
const files = ["File1", "File10", "File2"];
console.log(files.sort(collator.compare));
// ["File1", "File2", "File10"]
Sin la ordenación numérica, "File10" se ordenaría antes que "File2" porque la cadena "10" comienza con "1". La ordenación numérica analiza las secuencias de números y las compara matemáticamente.
Esto produce un orden natural que coincide con las expectativas humanas para nombres de archivos, números de versión y listas numeradas.
Manejo de números decimales con ordenación numérica
La ordenación numérica tiene una limitación con los números decimales:
const collator = new Intl.Collator("en", { numeric: true });
const values = ["1.5", "1.10", "1.2"];
console.log(values.sort(collator.compare));
// ["1.2", "1.5", "1.10"]
El punto decimal se trata como puntuación, no como parte del número. Cada segmento entre signos de puntuación se ordena por separado. Para la ordenación de números decimales, analiza los valores a números y utiliza la comparación numérica.
Controlando el orden de mayúsculas y minúsculas con caseFirst
La opción caseFirst determina si las letras mayúsculas o minúsculas se ordenan primero:
// Mayúsculas primero
const upperFirst = new Intl.Collator("en", { caseFirst: "upper" });
console.log(["a", "A", "b", "B"].sort(upperFirst.compare));
// ["A", "a", "B", "b"]
// Minúsculas primero
const lowerFirst = new Intl.Collator("en", { caseFirst: "lower" });
console.log(["a", "A", "b", "B"].sort(lowerFirst.compare));
// ["a", "A", "b", "B"]
El valor predeterminado es false, que utiliza el orden predeterminado del idioma. Esta opción no tiene efecto cuando la sensibilidad es base o accent porque esos niveles ignoran las mayúsculas y minúsculas.
Ignorando la puntuación durante la comparación
La opción ignorePunctuation omite los signos de puntuación durante la comparación:
const collator = new Intl.Collator("en", { ignorePunctuation: true });
console.log(collator.compare("hello", "he-llo")); // 0
console.log(collator.compare("hello", "hello!")); // 0
Esta opción es true por defecto para el tailandés y false para otros idiomas. Úsala cuando la puntuación no deba afectar al orden o coincidencia de cadenas.
Especificación de tipos de ordenación para reglas específicas de idioma
Algunos locales admiten múltiples tipos de ordenación para clasificaciones especializadas:
// Ordenación china pinyin
const pinyin = new Intl.Collator("zh-CN-u-co-pinyin");
// Ordenación de guía telefónica alemana
const phonebook = new Intl.Collator("de-DE-u-co-phonebk");
// Agrupación de emoji
const emoji = new Intl.Collator("en-u-co-emoji");
El tipo de ordenación se especifica en la cadena de locale utilizando la sintaxis de extensión Unicode. Los tipos comunes incluyen:
pinyin: Ordenación china por pronunciación romanizadastroke: Ordenación china por número de trazosphonebk: Ordenación de guía telefónica alemanatrad: Reglas de ordenación tradicionales para ciertos idiomasemoji: Agrupa emojis por categoría
Consulta Intl.supportedValuesOf para ver los tipos de ordenación disponibles en tu entorno.
Reutilización de instancias de collator en toda tu aplicación
Crea instancias de collator una vez y reutilízalas en toda tu aplicación:
// utils/collation.js
export const germanCollator = new Intl.Collator("de");
export const searchCollator = new Intl.Collator("en", {
sensitivity: "base"
});
export const numericCollator = new Intl.Collator("en", {
numeric: true
});
// En tus componentes
import { germanCollator } from "./utils/collation";
const sorted = names.sort(germanCollator.compare);
Este patrón maximiza el rendimiento y mantiene un comportamiento de comparación consistente en todo tu código.
Ordenación de arrays de objetos por propiedad
Utiliza el collator en una función de comparación que acceda a propiedades de objetos:
const collator = new Intl.Collator("de");
const users = [
{ name: "Zoe" },
{ name: "Änder" },
{ name: "Ava" }
];
const sorted = users.sort((a, b) =>
collator.compare(a.name, b.name)
);
Este enfoque funciona para cualquier estructura de objeto. Extrae las cadenas a comparar y pásalas al collator.
Comparación del rendimiento entre Intl.Collator y localeCompare
Intl.Collator proporciona mejor rendimiento al ordenar grandes conjuntos de datos:
// Más lento: recrea la configuración de locale para cada comparación
const slow = items.sort((a, b) => a.localeCompare(b, "de"));
// Más rápido: reutiliza la configuración de locale precomputada
const collator = new Intl.Collator("de");
const fast = items.sort(collator.compare);
Para arrays pequeños (menos de 100 elementos), la diferencia es insignificante. Para arrays grandes (miles de elementos), Intl.Collator puede ser un 60-80% más rápido.
Existe una excepción en navegadores basados en V8 como Chrome. localeCompare tiene una optimización para cadenas que solo contienen ASCII utilizando tablas de búsqueda. Al ordenar cadenas puramente ASCII, localeCompare puede tener un rendimiento comparable a Intl.Collator.
Saber cuándo usar Intl.Collator versus localeCompare
Usa Intl.Collator cuando:
- Ordenes arrays grandes (cientos o miles de elementos)
- Ordenes repetidamente (el usuario alterna el orden, listas virtuales)
- Construyas utilidades de comparación reutilizables
- El rendimiento sea importante para tu caso de uso
Usa localeCompare cuando:
- Hagas comparaciones puntuales
- Ordenes arrays pequeños (menos de 100 elementos)
- La simplicidad sea más importante que el rendimiento
- Necesites comparación en línea sin configuración
Ambas APIs admiten las mismas opciones y producen resultados idénticos. La diferencia es puramente sobre rendimiento y organización del código.
Verificar opciones resueltas
El método resolvedOptions devuelve las opciones reales utilizadas por el comparador:
const collator = new Intl.Collator("de", { sensitivity: "base" });
console.log(collator.resolvedOptions());
// {
// locale: "de",
// usage: "sort",
// sensitivity: "base",
// ignorePunctuation: false,
// collation: "default",
// numeric: false,
// caseFirst: "false"
// }
Esto ayuda a depurar el comportamiento de la ordenación y entender los valores predeterminados. El idioma resuelto puede diferir del idioma solicitado si el sistema no admite el idioma exacto.
Verificar soporte de idiomas
Comprueba qué idiomas son compatibles en el entorno actual:
const supported = Intl.Collator.supportedLocalesOf(["de", "fr", "xx"]);
console.log(supported); // ["de", "fr"]
Los idiomas no compatibles se revierten al predeterminado del sistema. Este método ayuda a detectar cuando tu idioma solicitado no está disponible.
Soporte de navegadores y entornos
Intl.Collator ha sido ampliamente compatible desde septiembre de 2017. Todos los navegadores modernos y versiones de Node.js lo soportan. La API funciona de manera consistente en todos los entornos.
Algunos tipos de ordenación y opciones pueden tener soporte limitado en navegadores más antiguos. Prueba la funcionalidad crítica o consulta las tablas de compatibilidad de MDN si estás dando soporte a entornos antiguos.
Errores comunes a evitar
No crear un nuevo collator para cada comparación:
// Incorrecto: crea el collator repetidamente
items.sort((a, b) => new Intl.Collator("de").compare(a, b));
// Correcto: crear una vez, reutilizar
const collator = new Intl.Collator("de");
items.sort(collator.compare);
No asumir que la ordenación predeterminada funciona para texto internacional:
// Incorrecto: falla con caracteres no ASCII
names.sort();
// Correcto: usar ordenación adaptada al idioma
names.sort(new Intl.Collator("de").compare);
No olvidar especificar la sensibilidad para búsquedas:
// Incorrecto: la sensibilidad variante requiere coincidencia exacta
const collator = new Intl.Collator("en");
items.filter(item => collator.compare(item, "apple") === 0);
// Correcto: sensibilidad base para coincidencias aproximadas
const collator = new Intl.Collator("en", { sensitivity: "base" });
items.filter(item => collator.compare(item, "apple") === 0);
Casos de uso prácticos
Utiliza Intl.Collator para:
- Ordenar contenido generado por usuarios (nombres, títulos, direcciones)
- Implementar funciones de búsqueda y autocompletado
- Construir tablas de datos con columnas ordenables
- Crear listas filtradas y opciones de menús desplegables
- Ordenar nombres de archivos y números de versión
- Navegación alfabética en listas de contactos
- Interfaces de aplicación multilingües
Cualquier interfaz que muestre texto ordenado a los usuarios se beneficia de la ordenación adaptada al idioma. Esto asegura que tu aplicación se sienta nativa y correcta independientemente del idioma del usuario.