¿Cómo puedo mostrar nombres de idiomas como English, Español, 日本語?

Utiliza Intl.DisplayNames para mostrar nombres de idiomas en sus escrituras nativas para selectores de idioma e interfaces internacionalizadas.

Introducción

Cuando construyes un selector de idiomas o muestras una lista de idiomas disponibles, necesitas mostrar cada nombre de idioma de una manera que los usuarios puedan reconocer. Un hablante de francés busca "Français", un hablante de español busca "Español", y un hablante de japonés busca "日本語". Los usuarios identifican su idioma por su escritura y ortografía nativa.

Codificar estas traducciones de forma estática no es escalable. Tendrías que mantener traducciones de cada nombre de idioma en todos los demás idiomas. La API Intl.DisplayNames resuelve este problema proporcionando nombres estandarizados y adaptados al idioma local para idiomas, países, escrituras y monedas.

El problema de codificar estáticamente los nombres de idiomas

Puedes crear un selector de idiomas codificando estáticamente los nombres de idiomas en un objeto.

const languageNames = {
  en: "English",
  es: "Spanish",
  fr: "French",
  de: "German",
  ja: "Japanese"
};

console.log(languageNames.en);
// "English"

Este enfoque tiene tres problemas. Primero, estos nombres solo funcionan para hablantes de inglés. Segundo, los usuarios no pueden reconocer su idioma cuando aparece en inglés. Un usuario japonés que busca su idioma busca caracteres japoneses, no la palabra "Japanese". Tercero, necesitas mantener manualmente traducciones para cada idioma en todos los demás idiomas.

const languageNames = {
  en: {
    en: "English",
    es: "Spanish",
    fr: "French",
    de: "German",
    ja: "Japanese"
  },
  es: {
    en: "Inglés",
    es: "Español",
    fr: "Francés",
    de: "Alemán",
    ja: "Japonés"
  }
  // ... más idiomas
};

Esto rápidamente se vuelve inmantenible. Necesitas una mejor solución.

Usando Intl.DisplayNames para obtener nombres de idiomas

El constructor Intl.DisplayNames crea un formateador que convierte códigos de idioma en nombres legibles para humanos. Especificas un locale y el tipo de nombres que quieres mostrar.

const names = new Intl.DisplayNames(["en"], { type: "language" });
console.log(names.of("en"));
// "English"

El primer argumento es un array de identificadores de locale. El segundo argumento es un objeto de opciones donde type: "language" le indica al formateador que quieres nombres de idiomas. El método of() toma un código de idioma y devuelve su nombre.

Puedes obtener nombres en cualquier idioma cambiando el locale.

const enNames = new Intl.DisplayNames(["en"], { type: "language" });
const esNames = new Intl.DisplayNames(["es"], { type: "language" });
const frNames = new Intl.DisplayNames(["fr"], { type: "language" });

console.log(enNames.of("es"));
// "Spanish"

console.log(esNames.of("es"));
// "español"

console.log(frNames.of("es"));
// "espagnol"

Cada formateador devuelve el nombre del idioma en su locale de visualización. Esto maneja toda la complejidad de mantener traducciones de nombres de idiomas.

Mostrar nombres de idiomas en su forma nativa

La mejor práctica para los selectores de idioma es mostrar cada idioma en su propio sistema de escritura. Los usuarios reconocen su idioma más rápido cuando lo ven escrito con caracteres familiares.

const names = new Intl.DisplayNames(["ja"], { type: "language" });
console.log(names.of("ja"));
// "日本語"

Para obtener el nombre nativo de cada idioma, crea un formateador utilizando el idioma de destino como la configuración regional de visualización.

function getNativeName(languageCode) {
  const names = new Intl.DisplayNames([languageCode], { type: "language" });
  return names.of(languageCode);
}

console.log(getNativeName("en"));
// "English"

console.log(getNativeName("es"));
// "español"

console.log(getNativeName("fr"));
// "français"

console.log(getNativeName("de"));
// "Deutsch"

console.log(getNativeName("ja"));
// "日本語"

console.log(getNativeName("ar"));
// "العربية"

console.log(getNativeName("zh"));
// "中文"

Este patrón funciona para cualquier código de idioma. El formateador devuelve el nombre en el sistema de escritura y forma que utilizan los hablantes nativos.

Comprender los formatos de códigos de idioma

El método of() acepta códigos de idioma en varios formatos. Puedes usar códigos de idioma básicos como "en" o identificadores de configuración regional completos como "en-US".

const names = new Intl.DisplayNames(["en"], { type: "language" });

console.log(names.of("en"));
// "English"

console.log(names.of("en-US"));
// "American English"

console.log(names.of("en-GB"));
// "British English"

console.log(names.of("zh"));
// "Chinese"

console.log(names.of("zh-Hans"));
// "Simplified Chinese"

console.log(names.of("zh-Hant"));
// "Traditional Chinese"

El formateador reconoce tanto códigos cortos como identificadores extendidos con subtags de región o escritura. Esto te permite distinguir entre variantes de idiomas.

Controlar cómo aparecen los nombres de idiomas

La opción languageDisplay controla el nivel de detalle en los nombres devueltos. Acepta dos valores.

El valor "standard" devuelve nombres completos que incluyen información del dialecto. Este es el valor predeterminado.

const names = new Intl.DisplayNames(["en"], {
  type: "language",
  languageDisplay: "standard"
});

console.log(names.of("en-US"));
// "American English"

console.log(names.of("en-GB"));
// "British English"

console.log(names.of("pt-BR"));
// "Brazilian Portuguese"

console.log(names.of("pt-PT"));
// "European Portuguese"

El valor "dialect" también devuelve nombres completos con información del dialecto. En la mayoría de los casos, produce la misma salida que "standard".

const names = new Intl.DisplayNames(["en"], {
  type: "language",
  languageDisplay: "dialect"
});

console.log(names.of("en-US"));
// "American English"

console.log(names.of("pt-BR"));
// "Brazilian Portuguese"

Para los selectores de idioma, el formato estándar ayuda a los usuarios a elegir la variante correcta cuando hay múltiples dialectos disponibles.

Obtener nombres de idiomas localizados para la interfaz de usuario

Cuando construyes una página de configuración o un selector de idioma, necesitas mostrar los nombres de los idiomas en el idioma actual de la interfaz del usuario. Crea un formateador utilizando la configuración regional del usuario.

function getLocalizedLanguageName(languageCode, userLocale) {
  const names = new Intl.DisplayNames([userLocale], { type: "language" });
  return names.of(languageCode);
}

// Interfaz de usuario en inglés
console.log(getLocalizedLanguageName("ja", "en"));
// "Japanese"

// Interfaz de usuario en francés
console.log(getLocalizedLanguageName("ja", "fr"));
// "japonais"

// Interfaz de usuario en español
console.log(getLocalizedLanguageName("ja", "es"));
// "japonés"

Este enfoque muestra nombres descriptivos en el idioma que el usuario entiende. Combina esto con nombres nativos para crear etiquetas híbridas como "日本語 (japonés)" que funcionan tanto para hablantes nativos como para otros.

Construyendo un selector de idioma con nombres nativos

Un caso de uso común es construir un menú desplegable o lista donde los usuarios seleccionan su idioma preferido. Muestra cada idioma en su forma nativa para que los usuarios puedan encontrar su opción rápidamente.

const supportedLanguages = ["en", "es", "fr", "de", "ja", "ar", "zh"];

function createLanguageOptions() {
  return supportedLanguages.map((code) => {
    const names = new Intl.DisplayNames([code], { type: "language" });
    const nativeName = names.of(code);
    return { code, name: nativeName };
  });
}

const options = createLanguageOptions();
console.log(options);

Esto produce un array de opciones de idioma con nombres nativos.

[
  { code: "en", name: "English" },
  { code: "es", name: "español" },
  { code: "fr", name: "français" },
  { code: "de", name: "Deutsch" },
  { code: "ja", name: "日本語" },
  { code: "ar", name: "العربية" },
  { code: "zh", name: "中文" }
]

Puedes renderizar estas opciones en HTML para crear un selector de idioma.

function renderLanguageSelector() {
  const options = createLanguageOptions();
  const select = document.createElement("select");
  select.id = "language-selector";

  options.forEach((option) => {
    const optionElement = document.createElement("option");
    optionElement.value = option.code;
    optionElement.textContent = option.name;
    select.appendChild(optionElement);
  });

  return select;
}

const selector = renderLanguageSelector();
document.body.appendChild(selector);

Esto crea un menú desplegable donde cada idioma aparece en su escritura nativa, facilitando a los usuarios la identificación de su idioma.

Creación de etiquetas de idioma híbridas

Algunas interfaces muestran tanto el nombre nativo como una traducción en el idioma del usuario. Esto ayuda a los usuarios que pueden no reconocer todos los sistemas de escritura y también hace que la interfaz sea más accesible.

function createHybridLabel(languageCode, userLocale) {
  const nativeNames = new Intl.DisplayNames([languageCode], {
    type: "language"
  });
  const localizedNames = new Intl.DisplayNames([userLocale], {
    type: "language"
  });

  const nativeName = nativeNames.of(languageCode);
  const localizedName = localizedNames.of(languageCode);

  if (nativeName === localizedName) {
    return nativeName;
  }

  return `${nativeName} (${localizedName})`;
}

// User interface is in English
console.log(createHybridLabel("ja", "en"));
// "日本語 (Japanese)"

console.log(createHybridLabel("ar", "en"));
// "العربية (Arabic)"

console.log(createHybridLabel("en", "en"));
// "English"

// User interface is in Spanish
console.log(createHybridLabel("ja", "es"));
// "日本語 (japonés)"

Este patrón combina los beneficios de reconocimiento de los nombres nativos con la claridad de las traducciones localizadas.

Manejo de locales de respaldo

El constructor Intl.DisplayNames acepta un array de locales. Si el primer locale no está disponible, el formateador recurre al siguiente locale en el array.

const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "language" });
console.log(names.of("fr"));
// "French"

El formateador intenta primero con "xx-XX", que no existe, y luego recurre a "en". Esto asegura que tu código funcione incluso cuando el locale solicitado no está disponible.

Puedes verificar qué locale está utilizando realmente el formateador con el método resolvedOptions().

const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "language" });
console.log(names.resolvedOptions().locale);
// "en"

Esto muestra que el formateador se resolvió a inglés después del respaldo.

Cómo diferentes locales formatean los nombres de idiomas

Cada locale tiene sus propias convenciones para los nombres de idiomas. El formateador aplica estas automáticamente.

const supportedLanguages = ["en", "es", "fr", "de", "ja"];

function showLanguageNamesInLocale(locale) {
  const names = new Intl.DisplayNames([locale], { type: "language" });
  return supportedLanguages.map((code) => ({
    code,
    name: names.of(code)
  }));
}

console.log(showLanguageNamesInLocale("en"));
// [
//   { code: "en", name: "English" },
//   { code: "es", name: "Spanish" },
//   { code: "fr", name: "French" },
//   { code: "de", name: "German" },
//   { code: "ja", name: "Japanese" }
// ]

console.log(showLanguageNamesInLocale("es"));
// [
//   { code: "en", name: "inglés" },
//   { code: "es", name: "español" },
//   { code: "fr", name: "francés" },
//   { code: "de", name: "alemán" },
//   { code: "ja", name: "japonés" }
// ]

console.log(showLanguageNamesInLocale("ja"));
// [
//   { code: "en", name: "英語" },
//   { code: "es", name: "スペイン語" },
//   { code: "fr", name: "フランス語" },
//   { code: "de", name: "ドイツ語" },
//   { code: "ja", name: "日本語" }
// ]

El formateador maneja automáticamente la capitalización, el sistema de escritura y las convenciones lingüísticas para cada idioma.

Compatibilidad con navegadores

La API Intl.DisplayNames está disponible en todos los navegadores modernos. Es compatible desde marzo de 2021 en los principales navegadores, incluyendo Chrome, Firefox, Safari y Edge.

Puedes verificar si la API está disponible antes de usarla.

if (typeof Intl.DisplayNames !== "undefined") {
  const names = new Intl.DisplayNames(["en"], { type: "language" });
  console.log(names.of("fr"));
} else {
  console.log("Intl.DisplayNames no es compatible");
}

Para navegadores más antiguos, necesitas proporcionar una alternativa o usar un polyfill. Una alternativa simple utiliza un mapeo codificado de códigos de idioma a nombres.

function getLanguageName(code, locale) {
  if (typeof Intl.DisplayNames !== "undefined") {
    const names = new Intl.DisplayNames([locale], { type: "language" });
    return names.of(code);
  }

  // Alternativa para navegadores antiguos
  const fallbackNames = {
    en: "English",
    es: "español",
    fr: "français",
    de: "Deutsch",
    ja: "日本語"
  };

  return fallbackNames[code] || code;
}

console.log(getLanguageName("es", "en"));
// "español" (o "Spanish" si ajustas la alternativa para localización)

Esto asegura que tu código funcione incluso en navegadores sin soporte para Intl.DisplayNames, aunque pierdes las características de localización automática.