Cómo normalizar identificadores de configuración regional a formato estándar

Convierte identificadores de configuración regional a formato canónico con mayúsculas correctas y ordenación de componentes

Introducción

Los identificadores de configuración regional pueden escribirse de muchas formas diferentes mientras se refieren al mismo idioma y región. Un usuario podría escribir EN-us, en-US o en-us, y los tres representan inglés americano. Al almacenar, comparar o mostrar identificadores de configuración regional, estas variaciones crean inconsistencia.

La normalización convierte los identificadores de configuración regional a una forma canónica estándar. Este proceso ajusta el uso de mayúsculas de los componentes, ordena las palabras clave de extensión alfabéticamente y produce una representación consistente en la que puedes confiar en toda tu aplicación.

JavaScript proporciona métodos integrados para normalizar identificadores de configuración regional automáticamente. Esta guía explica qué significa la normalización, cómo aplicarla en tu código y cuándo los identificadores normalizados mejoran tu lógica de internacionalización.

Qué significa la normalización para los identificadores de configuración regional

La normalización transforma un identificador de configuración regional en su forma canónica según el estándar BCP 47 y las especificaciones Unicode. La forma canónica tiene reglas específicas para mayúsculas, ordenación y estructura.

Un identificador de configuración regional normalizado sigue estas convenciones:

  • Los códigos de idioma están en minúsculas
  • Los códigos de escritura están en mayúscula inicial con la primera letra en mayúscula
  • Los códigos de región están en mayúsculas
  • Los códigos de variante están en minúsculas
  • Las palabras clave de extensión están ordenadas alfabéticamente
  • Los atributos de extensión están ordenados alfabéticamente

Estas reglas crean una única representación estándar para cada configuración regional. No importa cómo un usuario escriba un identificador de configuración regional, la forma normalizada es siempre la misma.

Comprender las reglas de normalización

Cada componente de un identificador de configuración regional tiene una convención específica de mayúsculas en la forma canónica.

Uso de mayúsculas y minúsculas en idiomas

Los códigos de idioma siempre usan letras minúsculas:

en (correct)
EN (incorrect, but normalizes to en)
eN (incorrect, but normalizes to en)

Esto se aplica tanto a códigos de idioma de dos letras como de tres letras.

Uso de mayúsculas y minúsculas en sistemas de escritura

Los códigos de sistema de escritura usan mayúscula inicial, donde la primera letra es mayúscula y las tres letras restantes son minúsculas:

Hans (correct)
hans (incorrect, but normalizes to Hans)
HANS (incorrect, but normalizes to Hans)

Los códigos de sistema de escritura comunes incluyen Latn para latino, Cyrl para cirílico, Hans para caracteres han simplificados y Hant para caracteres han tradicionales.

Uso de mayúsculas y minúsculas en regiones

Los códigos de región siempre usan letras mayúsculas:

US (correct)
us (incorrect, but normalizes to US)
Us (incorrect, but normalizes to US)

Esto se aplica a los códigos de país de dos letras utilizados en la mayoría de los identificadores de configuración regional.

Ordenación de extensiones

Las etiquetas de extensión Unicode contienen palabras clave que especifican preferencias de formato. En la forma canónica, estas palabras clave aparecen en orden alfabético por su clave:

en-US-u-ca-gregory-nu-latn (correct)
en-US-u-nu-latn-ca-gregory (incorrect, but normalizes to first form)

La clave de calendario ca viene antes que la clave de sistema de numeración nu alfabéticamente, por lo que ca-gregory aparece primero en la forma normalizada.

Uso de Intl.getCanonicalLocales para normalizar

El método Intl.getCanonicalLocales() normaliza los identificadores de configuración regional y los devuelve en forma canónica. Este es el método principal para la normalización en JavaScript.

const normalized = Intl.getCanonicalLocales("EN-us");
console.log(normalized);
// ["en-US"]

El método acepta un identificador de configuración regional con cualquier uso de mayúsculas y minúsculas y devuelve la forma canónica con el uso correcto de mayúsculas y minúsculas.

Normalización de códigos de idioma

El método convierte los códigos de idioma a minúsculas:

const result = Intl.getCanonicalLocales("FR-fr");
console.log(result);
// ["fr-FR"]

El código de idioma FR se convierte en fr en la salida.

Normalización de códigos de sistema de escritura

El método convierte los códigos de escritura a mayúsculas y minúsculas:

const result = Intl.getCanonicalLocales("zh-HANS-cn");
console.log(result);
// ["zh-Hans-CN"]

El código de escritura HANS se convierte en Hans, y el código de región cn se convierte en CN.

Normalización de códigos de región

El método convierte los códigos de región a mayúsculas:

const result = Intl.getCanonicalLocales("en-gb");
console.log(result);
// ["en-GB"]

El código de región gb se convierte en GB en la salida.

Normalización de palabras clave de extensión

El método ordena las palabras clave de extensión alfabéticamente:

const result = Intl.getCanonicalLocales("en-US-u-nu-latn-hc-h12-ca-gregory");
console.log(result);
// ["en-US-u-ca-gregory-hc-h12-nu-latn"]

Las palabras clave se reordenan de nu-latn-hc-h12-ca-gregory a ca-gregory-hc-h12-nu-latn porque ca viene antes que hc y hc viene antes que nu alfabéticamente.

Normalización de múltiples identificadores de configuración regional

El método Intl.getCanonicalLocales() acepta un array de identificadores de localización y los normaliza todos:

const locales = ["EN-us", "fr-FR", "ZH-hans-cn"];
const normalized = Intl.getCanonicalLocales(locales);
console.log(normalized);
// ["en-US", "fr-FR", "zh-Hans-CN"]

Cada configuración regional en el array se convierte a su forma canónica.

Eliminación de duplicados

El método elimina los identificadores de configuración regional duplicados después de la normalización. Si varios valores de entrada se normalizan a la misma forma canónica, el resultado contiene solo una copia:

const locales = ["en-US", "EN-us", "en-us"];
const normalized = Intl.getCanonicalLocales(locales);
console.log(normalized);
// ["en-US"]

Las tres entradas representan la misma configuración regional, por lo que la salida contiene un único identificador normalizado.

Esta deduplicación es útil al procesar entradas de usuario o fusionar listas de configuraciones regionales de múltiples fuentes.

Manejo de identificadores no válidos

Si algún identificador de configuración regional en el array no es válido, el método lanza un RangeError:

try {
  Intl.getCanonicalLocales(["en-US", "invalid", "fr-FR"]);
} catch (error) {
  console.error(error.message);
  // "invalid is not a structurally valid language tag"
}

Al normalizar listas proporcionadas por el usuario, valide o capture errores para cada configuración regional individualmente para identificar qué identificadores específicos no son válidos.

Uso de Intl.Locale para normalización

El constructor Intl.Locale también normaliza los identificadores de configuración regional al crear objetos de configuración regional. Puedes acceder a la forma normalizada a través del método toString().

const locale = new Intl.Locale("EN-us");
console.log(locale.toString());
// "en-US"

El constructor acepta cualquier uso válido de mayúsculas y minúsculas y produce un objeto de configuración regional normalizado.

Acceso a componentes normalizados

Cada propiedad del objeto de configuración regional devuelve la forma normalizada de ese componente:

const locale = new Intl.Locale("ZH-hans-CN");

console.log(locale.language);
// "zh"

console.log(locale.script);
// "Hans"

console.log(locale.region);
// "CN"

console.log(locale.baseName);
// "zh-Hans-CN"

Las propiedades language, script y region utilizan todas el uso correcto de mayúsculas y minúsculas para la forma canónica.

Normalización con opciones

Cuando creas un objeto de configuración regional con opciones, el constructor normaliza tanto el identificador base como las opciones:

const locale = new Intl.Locale("EN-us", {
  calendar: "gregory",
  numberingSystem: "latn",
  hourCycle: "h12"
});

console.log(locale.toString());
// "en-US-u-ca-gregory-hc-h12-nu-latn"

Las palabras clave de extensión aparecen en orden alfabético en la salida, aunque el objeto de opciones no especifique ningún orden particular.

Por qué importa la normalización

La normalización proporciona consistencia en toda tu aplicación. Cuando almacenas, muestras o comparas identificadores de configuración regional, usar la forma canónica previene errores sutiles y mejora la fiabilidad.

Almacenamiento consistente

Al almacenar identificadores de configuración regional en bases de datos, archivos de configuración o almacenamiento local, las formas normalizadas previenen la duplicación:

const userPreferences = new Set();

function saveUserLocale(identifier) {
  const normalized = Intl.getCanonicalLocales(identifier)[0];
  userPreferences.add(normalized);
}

saveUserLocale("en-US");
saveUserLocale("EN-us");
saveUserLocale("en-us");

console.log(userPreferences);
// Set { "en-US" }

Sin normalización, el conjunto contendría tres entradas para la misma configuración regional. Con normalización, contiene correctamente una.

Comparación fiable

Comparar identificadores de configuración regional requiere normalización. Dos identificadores que difieren solo en el uso de mayúsculas y minúsculas representan la misma configuración regional:

function isSameLocale(locale1, locale2) {
  const normalized1 = Intl.getCanonicalLocales(locale1)[0];
  const normalized2 = Intl.getCanonicalLocales(locale2)[0];
  return normalized1 === normalized2;
}

console.log(isSameLocale("en-US", "EN-us"));
// true

console.log(isSameLocale("en-US", "en-GB"));
// false

La comparación directa de cadenas de identificadores no normalizados produce resultados incorrectos.

Visualización consistente

Al mostrar identificadores de configuración regional a los usuarios o en salidas de depuración, las formas normalizadas proporcionan un formato consistente:

function displayLocale(identifier) {
  try {
    const normalized = Intl.getCanonicalLocales(identifier)[0];
    return `Current locale: ${normalized}`;
  } catch (error) {
    return "Invalid locale identifier";
  }
}

console.log(displayLocale("EN-us"));
// "Current locale: en-US"

console.log(displayLocale("zh-HANS-cn"));
// "Current locale: zh-Hans-CN"

Los usuarios ven identificadores de configuración regional correctamente formateados independientemente del formato de entrada.

Aplicaciones prácticas

La normalización resuelve problemas comunes al trabajar con identificadores de configuración regional en aplicaciones reales.

Normalización de entrada de usuario

Cuando los usuarios introducen identificadores de configuración regional en formularios o ajustes, normaliza la entrada antes de almacenarla:

function processLocaleInput(input) {
  try {
    const normalized = Intl.getCanonicalLocales(input)[0];
    return {
      success: true,
      locale: normalized
    };
  } catch (error) {
    return {
      success: false,
      error: "Please enter a valid locale identifier"
    };
  }
}

const result = processLocaleInput("fr-ca");
console.log(result);
// { success: true, locale: "fr-CA" }

Esto asegura un formato consistente en tu base de datos o configuración.

Construcción de tablas de búsqueda de configuración regional

Al crear tablas de búsqueda para traducciones o datos específicos de configuración regional, usa claves normalizadas:

const translations = new Map();

function addTranslation(locale, key, value) {
  const normalized = Intl.getCanonicalLocales(locale)[0];

  if (!translations.has(normalized)) {
    translations.set(normalized, {});
  }

  translations.get(normalized)[key] = value;
}

addTranslation("en-us", "hello", "Hello");
addTranslation("EN-US", "goodbye", "Goodbye");

console.log(translations.get("en-US"));
// { hello: "Hello", goodbye: "Goodbye" }

Ambas llamadas a addTranslation usan la misma clave normalizada, por lo que las traducciones se almacenan en el mismo objeto.

Fusión de listas de configuración regional

Al combinar identificadores de configuración regional de múltiples fuentes, normalízalos y elimina duplicados:

function mergeLocales(...sources) {
  const allLocales = sources.flat();
  const normalized = Intl.getCanonicalLocales(allLocales);
  return normalized;
}

const userLocales = ["en-us", "fr-FR"];
const appLocales = ["EN-US", "de-de"];
const systemLocales = ["en-US", "es-mx"];

const merged = mergeLocales(userLocales, appLocales, systemLocales);
console.log(merged);
// ["en-US", "fr-FR", "de-DE", "es-MX"]

El método elimina duplicados y normaliza el uso de mayúsculas y minúsculas en todas las fuentes.

Creación de interfaces de selección de configuración regional

Al construir menús desplegables o interfaces de selección, normaliza los identificadores de configuración regional para su visualización:

function buildLocaleOptions(locales) {
  const normalized = Intl.getCanonicalLocales(locales);

  return normalized.map(locale => {
    const localeObj = new Intl.Locale(locale);
    const displayNames = new Intl.DisplayNames([locale], {
      type: "language"
    });

    return {
      value: locale,
      label: displayNames.of(localeObj.language)
    };
  });
}

const options = buildLocaleOptions(["EN-us", "fr-FR", "DE-de"]);
console.log(options);
// [
//   { value: "en-US", label: "English" },
//   { value: "fr-FR", label: "French" },
//   { value: "de-DE", label: "German" }
// ]

Los valores normalizados proporcionan identificadores consistentes para el envío de formularios.

Validación de archivos de configuración

Al cargar identificadores de configuración regional desde archivos de configuración, normalízalos durante la inicialización:

function loadLocaleConfig(config) {
  const validatedConfig = {
    defaultLocale: null,
    supportedLocales: []
  };

  try {
    validatedConfig.defaultLocale = Intl.getCanonicalLocales(
      config.defaultLocale
    )[0];
  } catch (error) {
    console.error("Invalid default locale:", config.defaultLocale);
    validatedConfig.defaultLocale = "en-US";
  }

  config.supportedLocales.forEach(locale => {
    try {
      const normalized = Intl.getCanonicalLocales(locale)[0];
      validatedConfig.supportedLocales.push(normalized);
    } catch (error) {
      console.warn("Skipping invalid locale:", locale);
    }
  });

  return validatedConfig;
}

const config = {
  defaultLocale: "en-us",
  supportedLocales: ["EN-us", "fr-FR", "invalid", "de-DE"]
};

const validated = loadLocaleConfig(config);
console.log(validated);
// {
//   defaultLocale: "en-US",
//   supportedLocales: ["en-US", "fr-FR", "de-DE"]
// }

Esto detecta errores de configuración tempranamente y asegura que tu aplicación use identificadores normalizados válidos.

Normalización y coincidencia de configuración regional

La normalización es importante para los algoritmos de coincidencia de configuración regional. Al encontrar la mejor coincidencia de configuración regional para una preferencia de usuario, compara formas normalizadas:

function findBestMatch(userPreference, availableLocales) {
  const normalizedPreference = Intl.getCanonicalLocales(userPreference)[0];
  const normalizedAvailable = Intl.getCanonicalLocales(availableLocales);

  if (normalizedAvailable.includes(normalizedPreference)) {
    return normalizedPreference;
  }

  const preferenceLocale = new Intl.Locale(normalizedPreference);

  const languageMatch = normalizedAvailable.find(available => {
    const availableLocale = new Intl.Locale(available);
    return availableLocale.language === preferenceLocale.language;
  });

  if (languageMatch) {
    return languageMatch;
  }

  return normalizedAvailable[0];
}

const available = ["en-us", "fr-FR", "DE-de"];
console.log(findBestMatch("EN-GB", available));
// "en-US"

La normalización garantiza que la lógica de coincidencia funcione correctamente independientemente del uso de mayúsculas y minúsculas en la entrada.

La normalización no cambia el significado

La normalización solo afecta la representación de un identificador de configuración regional. No cambia qué idioma, escritura o región representa el identificador.

const locale1 = new Intl.Locale("en-us");
const locale2 = new Intl.Locale("EN-US");

console.log(locale1.language === locale2.language);
// true

console.log(locale1.region === locale2.region);
// true

console.log(locale1.toString() === locale2.toString());
// true

Ambos identificadores se refieren al inglés americano. La normalización simplemente garantiza que se escriban de la misma manera.

Esto es diferente de operaciones como maximize() y minimize(), que agregan o eliminan componentes y pueden cambiar la especificidad del identificador.

Compatibilidad con navegadores

El método Intl.getCanonicalLocales() funciona en todos los navegadores modernos. Chrome, Firefox, Safari y Edge proporcionan compatibilidad completa.

Node.js admite Intl.getCanonicalLocales() a partir de la versión 9, con compatibilidad completa en la versión 10 y posteriores.

El constructor Intl.Locale y su comportamiento de normalización funcionan en todos los navegadores que admiten la API Intl.Locale. Esto incluye versiones modernas de Chrome, Firefox, Safari y Edge.

Resumen

La normalización convierte los identificadores de configuración regional a su forma canónica aplicando reglas estándar de uso de mayúsculas y minúsculas y ordenando las palabras clave de extensión. Esto crea representaciones consistentes que puedes almacenar, comparar y mostrar de manera confiable.

Conceptos clave:

  • La forma canónica usa minúsculas para idiomas, mayúsculas iniciales para escrituras y mayúsculas para regiones
  • Las palabras clave de extensión se ordenan alfabéticamente en la forma canónica
  • El método Intl.getCanonicalLocales() normaliza identificadores y elimina duplicados
  • El constructor Intl.Locale también produce salida normalizada
  • La normalización no cambia el significado de un identificador de configuración regional
  • Usa identificadores normalizados para almacenamiento, comparación y visualización

La normalización es una operación fundamental para cualquier aplicación que trabaje con identificadores de configuración regional. Previene errores causados por el uso inconsistente de mayúsculas y minúsculas y garantiza que tu lógica de internacionalización maneje los identificadores de configuración regional de manera confiable.