¿Cómo puedo mostrar nombres de scripts como Latino, Cirílico, Árabe?

Utiliza Intl.DisplayNames para convertir códigos de script en nombres legibles para sistemas de escritura en cualquier idioma.

Introducción

Un sistema de escritura es un conjunto de símbolos gráficos. El latino es el sistema utilizado para el inglés, francés y español. El cirílico es el sistema utilizado para el ruso, búlgaro y ucraniano. El árabe es el sistema utilizado para el árabe, persa y urdu. Los sistemas de escritura difieren de los idiomas porque el mismo idioma puede escribirse en múltiples sistemas. El serbio utiliza tanto el sistema cirílico como el latino. Los usuarios en Serbia eligen qué sistema prefieren.

Cuando construyes selectores de idioma, selectores de fuentes o controles de entrada de texto, necesitas mostrar los nombres de los sistemas de escritura para que los usuarios puedan identificar los sistemas de escritura. La API Intl.DisplayNames convierte los códigos de sistemas de escritura en nombres legibles y localizados sin requerir que mantengas tablas de traducción.

Entendiendo los sistemas de escritura e idiomas

Los sistemas de escritura y los idiomas no son lo mismo. Un idioma es lo que la gente habla. Un sistema de escritura es cómo la gente lo escribe.

El inglés es un idioma. El latino es un sistema de escritura. El inglés utiliza el sistema latino, pero también lo hacen docenas de otros idiomas, incluyendo el español, francés, alemán, vietnamita y turco.

El serbio es un idioma que puede escribirse en dos sistemas. El serbio escrito en cirílico se ve como "Српски". El serbio escrito en latino se ve como "Srpski". Ambos representan el mismo idioma con las mismas palabras y gramática. La diferencia está solo en el sistema de escritura.

El chino tiene dos variantes comunes de escritura. El chino simplificado usa caracteres Han simplificados. El chino tradicional usa caracteres Han tradicionales. La misma frase aparece de manera diferente dependiendo de qué sistema de escritura uses.

Esta distinción es importante al construir interfaces. Un usuario serbio podría preferir texto cirílico sobre texto latino. Un usuario chino necesita elegir entre caracteres simplificados y tradicionales. Tu interfaz necesita mostrar los nombres de los sistemas de escritura para que los usuarios puedan hacer estas elecciones.

El problema con codificar nombres de scripts

Puedes crear una tabla de búsqueda que mapee códigos de scripts a nombres de scripts.

const scriptNames = {
  Latn: "Latin",
  Cyrl: "Cyrillic",
  Arab: "Arabic",
  Hans: "Simplified Chinese",
  Hant: "Traditional Chinese"
};

console.log(scriptNames.Latn);
// "Latin"

Este enfoque solo funciona para hablantes de inglés. Los usuarios que hablan otros idiomas ven nombres de scripts en inglés que posiblemente no entiendan. Necesitas traducciones para cada idioma que soportes.

const scriptNames = {
  en: {
    Latn: "Latin",
    Cyrl: "Cyrillic",
    Arab: "Arabic"
  },
  es: {
    Latn: "latino",
    Cyrl: "cirílico",
    Arab: "árabe"
  },
  fr: {
    Latn: "latin",
    Cyrl: "cyrillique",
    Arab: "arabe"
  }
};

Esto se vuelve inmantenible rápidamente. Cada nuevo idioma requiere un conjunto completo de traducciones. Cada nuevo script requiere entradas en cada idioma. Necesitas una mejor solución.

Usando Intl.DisplayNames para obtener nombres de scripts

El constructor Intl.DisplayNames crea un formateador que convierte códigos de scripts en nombres legibles para humanos. Especificas una localización y estableces el tipo a "script".

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

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

Los códigos de scripts siguen el estándar ISO 15924. Cada script tiene un código de cuatro letras con la primera letra en mayúscula y las tres restantes en minúscula. Latino es Latn. Cirílico es Cyrl. Árabe es Arab.

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

console.log(names.of("Latn"));
// "Latin"

console.log(names.of("Cyrl"));
// "Cyrillic"

console.log(names.of("Arab"));
// "Arabic"

console.log(names.of("Hani"));
// "Han"

console.log(names.of("Hira"));
// "Hiragana"

console.log(names.of("Kana"));
// "Katakana"

El formateador maneja toda la complejidad de mantener las traducciones de nombres de scripts. Solo necesitas proporcionar el código del script.

Códigos de scripts comunes

El estándar ISO 15924 define códigos para más de 160 scripts. Estos son los códigos más utilizados comúnmente.

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

console.log(names.of("Latn"));
// "Latin"

console.log(names.of("Cyrl"));
// "Cyrillic"

console.log(names.of("Arab"));
// "Arabic"

console.log(names.of("Hebr"));
// "Hebrew"

console.log(names.of("Deva"));
// "Devanagari"

console.log(names.of("Thai"));
// "Thai"

console.log(names.of("Hani"));
// "Han"

console.log(names.of("Hans"));
// "Simplified Han"

console.log(names.of("Hant"));
// "Traditional Han"

console.log(names.of("Hang"));
// "Hangul"

console.log(names.of("Hira"));
// "Hiragana"

console.log(names.of("Kana"));
// "Katakana"

console.log(names.of("Beng"));
// "Bengali"

console.log(names.of("Grek"));
// "Greek"

El latino cubre la mayoría de los idiomas de Europa Occidental. El cirílico cubre el ruso, búlgaro, ucraniano y otros idiomas eslavos. El árabe cubre el árabe, persa y urdu. El han cubre el chino, con Hans para el chino simplificado y Hant para el chino tradicional. El hangul cubre el coreano. Hiragana y Katakana son scripts japoneses.

Mostrando nombres de scripts en diferentes localizaciones

Los nombres de scripts se localizan según la localización de visualización. Crea formateadores con diferentes localizaciones para ver los nombres en diferentes idiomas.

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

console.log(enNames.of("Latn"));
// "Latin"

console.log(esNames.of("Latn"));
// "latino"

console.log(frNames.of("Latn"));
// "latin"

console.log(jaNames.of("Latn"));
// "ラテン文字"

Cada formateador devuelve el nombre del script en su localización de visualización. Esto maneja toda la complejidad de mantener las traducciones de nombres de scripts.

El mismo patrón funciona para cualquier código de script.

const enNames = new Intl.DisplayNames(["en"], { type: "script" });
const deNames = new Intl.DisplayNames(["de"], { type: "script" });
const zhNames = new Intl.DisplayNames(["zh"], { type: "script" });

console.log(enNames.of("Cyrl"));
// "Cyrillic"

console.log(deNames.of("Cyrl"));
// "Kyrillisch"

console.log(zhNames.of("Cyrl"));
// "西里尔文"

console.log(enNames.of("Arab"));
// "Arabic"

console.log(deNames.of("Arab"));
// "Arabisch"

console.log(zhNames.of("Arab"));
// "阿拉伯文"

El formateador aplica automáticamente las convenciones lingüísticas correctas para cada idioma.

Creación de un selector de escritura para variantes de idioma

Algunos idiomas ofrecen a los usuarios una elección de sistemas de escritura. El serbio puede escribirse en cirílico o latino. El chino puede escribirse en caracteres simplificados o tradicionales. Necesitas mostrar estas opciones para que los usuarios puedan elegir.

function getScriptOptions(language, userLocale) {
  const names = new Intl.DisplayNames([userLocale], { type: "script" });

  if (language === "sr") {
    return [
      { code: "Cyrl", name: names.of("Cyrl") },
      { code: "Latn", name: names.of("Latn") }
    ];
  }

  if (language === "zh") {
    return [
      { code: "Hans", name: names.of("Hans") },
      { code: "Hant", name: names.of("Hant") }
    ];
  }

  return [];
}

console.log(getScriptOptions("sr", "en"));
// [
//   { code: "Cyrl", name: "Cyrillic" },
//   { code: "Latn", name: "Latin" }
// ]

console.log(getScriptOptions("zh", "en"));
// [
//   { code: "Hans", name: "Simplified Han" },
//   { code: "Hant", name: "Traditional Han" }
// ]

console.log(getScriptOptions("zh", "es"));
// [
//   { code: "Hans", name: "han simplificado" },
//   { code: "Hant", name: "han tradicional" }
// ]

Esta función devuelve opciones de escritura en el idioma de la interfaz del usuario. Los usuarios serbios ven opciones para escrituras cirílica y latina. Los usuarios chinos ven opciones para escrituras simplificada y tradicional. Los nombres aparecen en el idioma que el usuario comprende.

Visualización de identificadores de configuración regional completos con información de escritura

Los identificadores de configuración regional pueden incluir códigos de escritura para distinguir entre sistemas de escritura. El formato es idioma-escritura-región, como sr-Cyrl-RS para serbio escrito en cirílico en Serbia o zh-Hans-CN para chino simplificado en China.

Cuando muestres estos identificadores de configuración regional, extrae el código de escritura y conviértelo a un nombre legible.

function parseLocaleWithScript(locale) {
  const parts = locale.split("-");

  if (parts.length < 2) {
    return null;
  }

  const [language, script] = parts;

  if (script.length === 4) {
    return {
      language,
      script: script.charAt(0).toUpperCase() + script.slice(1).toLowerCase()
    };
  }

  return null;
}

function formatLocaleWithScriptName(locale, displayLocale) {
  const parsed = parseLocaleWithScript(locale);

  if (!parsed) {
    return locale;
  }

  const languageNames = new Intl.DisplayNames([displayLocale], {
    type: "language"
  });
  const scriptNames = new Intl.DisplayNames([displayLocale], { type: "script" });

  const languageName = languageNames.of(parsed.language);
  const scriptName = scriptNames.of(parsed.script);

  return `${languageName} (${scriptName})`;
}

console.log(formatLocaleWithScriptName("sr-Cyrl", "en"));
// "Serbian (Cyrillic)"

console.log(formatLocaleWithScriptName("sr-Latn", "en"));
// "Serbian (Latin)"

console.log(formatLocaleWithScriptName("zh-Hans", "en"));
// "Chinese (Simplified Han)"

console.log(formatLocaleWithScriptName("zh-Hant", "en"));
// "Chinese (Traditional Han)"

console.log(formatLocaleWithScriptName("sr-Cyrl", "es"));
// "serbio (cirílico)"

Este patrón hace que los identificadores de configuración regional sean legibles para humanos al combinar nombres de idiomas con nombres de escritura. Los usuarios ven "Serbian (Cyrillic)" en lugar de "sr-Cyrl".

Creación de un selector de fuentes con nombres de escritura

Las interfaces de selección de fuentes suelen agrupar las fuentes según los sistemas de escritura que soportan. Necesitas mostrar los nombres de los sistemas de escritura para que los usuarios entiendan qué escrituras cubre cada fuente.

function createFontOptions() {
  const fonts = [
    {
      name: "Arial",
      scripts: ["Latn", "Cyrl", "Grek", "Hebr", "Arab"]
    },
    {
      name: "Noto Sans CJK",
      scripts: ["Hans", "Hant", "Hira", "Kana", "Hang"]
    },
    {
      name: "Noto Sans Devanagari",
      scripts: ["Deva"]
    }
  ];

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

  return fonts.map((font) => ({
    name: font.name,
    scripts: font.scripts.map((code) => names.of(code))
  }));
}

console.log(createFontOptions());
// [
//   {
//     name: "Arial",
//     scripts: ["Latin", "Cyrillic", "Greek", "Hebrew", "Arabic"]
//   },
//   {
//     name: "Noto Sans CJK",
//     scripts: ["Simplified Han", "Traditional Han", "Hiragana", "Katakana", "Hangul"]
//   },
//   {
//     name: "Noto Sans Devanagari",
//     scripts: ["Devanagari"]
//   }
// ]

Esto crea una lista de fuentes con sus sistemas de escritura soportados en forma legible para humanos. Los usuarios pueden elegir fuentes basándose en los sistemas de escritura que necesitan.

Mostrar métodos de entrada disponibles por sistema de escritura

Los sistemas operativos y navegadores proporcionan métodos de entrada para diferentes sistemas de escritura. Un método de entrada japonés convierte caracteres latinos en Hiragana, Katakana o Kanji. Un método de entrada chino convierte Pinyin en caracteres chinos simplificados o tradicionales. Puedes mostrar los métodos de entrada disponibles con sus nombres de escritura.

function getInputMethods(userLocale) {
  const inputMethods = [
    { id: "latin-ime", script: "Latn" },
    { id: "japanese-ime", script: "Hira" },
    { id: "chinese-pinyin-simplified", script: "Hans" },
    { id: "chinese-pinyin-traditional", script: "Hant" },
    { id: "korean-ime", script: "Hang" },
    { id: "arabic-ime", script: "Arab" },
    { id: "hebrew-ime", script: "Hebr" }
  ];

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

  return inputMethods.map((method) => ({
    id: method.id,
    name: names.of(method.script)
  }));
}

console.log(getInputMethods("en"));
// [
//   { id: "latin-ime", name: "Latin" },
//   { id: "japanese-ime", name: "Hiragana" },
//   { id: "chinese-pinyin-simplified", name: "Simplified Han" },
//   { id: "chinese-pinyin-traditional", name: "Traditional Han" },
//   { id: "korean-ime", name: "Hangul" },
//   { id: "arabic-ime", name: "Arabic" },
//   { id: "hebrew-ime", name: "Hebrew" }
// ]

console.log(getInputMethods("ja"));
// [
//   { id: "latin-ime", name: "ラテン文字" },
//   { id: "japanese-ime", name: "ひらがな" },
//   { id: "chinese-pinyin-simplified", name: "簡体字" },
//   { id: "chinese-pinyin-traditional", name: "繁体字" },
//   { id: "korean-ime", name: "ハングル" },
//   { id: "arabic-ime", name: "アラビア文字" },
//   { id: "hebrew-ime", name: "ヘブライ文字" }
// ]

Esto muestra los nombres de los métodos de entrada en el idioma del usuario. Los usuarios ven "Hiragana" cuando la interfaz está en inglés y "ひらがな" cuando la interfaz está en japonés.

Entendiendo la capitalización de los códigos de script

Los códigos de script siguen un patrón específico de capitalización. La primera letra va en mayúscula. Las tres letras restantes van en minúscula. Latn es correcto. LATN, latn, y LaTn no son estándar.

El método of() acepta códigos de script independientemente de su capitalización.

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

console.log(names.of("Latn"));
// "Latin"

console.log(names.of("LATN"));
// "Latin"

console.log(names.of("latn"));
// "Latin"

console.log(names.of("LaTn"));
// "Latin"

El formateador maneja correctamente todas las variaciones. Sin embargo, usar el patrón de capitalización estándar hace que tu código sea más legible y consistente con el estándar ISO 15924.

Manejo de localizaciones alternativas

El constructor Intl.DisplayNames acepta un array de localizaciones. Si la primera localización no está disponible, el formateador recurre a la siguiente localización en el array.

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

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

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

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

Esto muestra que el formateador se resolvió a inglés después de la alternativa.

Construyendo un sistema de gestión de contenido multilingüe

Los sistemas de gestión de contenido que soportan múltiples scripts necesitan mostrar qué scripts están disponibles para cada pieza de contenido. Puedes mostrar nombres de scripts para ayudar a los editores de contenido a elegir la versión correcta.

function getContentVersions(contentId, userLocale) {
  const versions = [
    { script: "Latn", url: `/content/${contentId}/latn` },
    { script: "Cyrl", url: `/content/${contentId}/cyrl` },
    { script: "Arab", url: `/content/${contentId}/arab` }
  ];

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

  return versions.map((version) => ({
    script: version.script,
    name: names.of(version.script),
    url: version.url
  }));
}

console.log(getContentVersions("article-123", "en"));
// [
//   { script: "Latn", name: "Latin", url: "/content/article-123/latn" },
//   { script: "Cyrl", name: "Cyrillic", url: "/content/article-123/cyrl" },
//   { script: "Arab", name: "Arabic", url: "/content/article-123/arab" }
// ]

Este patrón ayuda a los editores de contenido a ver qué versiones de script existen y a navegar entre ellas.

Compatibilidad con navegadores

La API Intl.DisplayNames con soporte para el tipo script está disponible en todos los navegadores modernos. Ha sido 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: "script" });
  console.log(names.of("Latn"));
} else {
  console.log("Intl.DisplayNames is not supported");
}

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

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

  const fallbackNames = {
    Latn: "Latin",
    Cyrl: "Cyrillic",
    Arab: "Arabic",
    Hans: "Simplified Han",
    Hant: "Traditional Han"
  };

  return fallbackNames[code] || code;
}

console.log(getScriptName("Latn", "en"));
// "Latin"

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.