How do I display script names like Latin, Cyrillic, Arabic?
Use Intl.DisplayNames to convert script codes into human-readable names for writing systems in any language.
Introduction
A script is a writing system. Latin is the script used for English, French, and Spanish. Cyrillic is the script used for Russian, Bulgarian, and Ukrainian. Arabic is the script used for Arabic, Persian, and Urdu. Scripts differ from languages because the same language can be written in multiple scripts. Serbian uses both Cyrillic and Latin scripts. Users in Serbia choose which script they prefer.
When you build language selectors, font pickers, or text input controls, you need to display script names so users can identify writing systems. The Intl.DisplayNames API converts script codes into localized, human-readable names without requiring you to maintain translation tables.
Understanding scripts and languages
Scripts and languages are not the same thing. A language is what people speak. A script is how people write it.
English is a language. Latin is a script. English uses the Latin script, but so do dozens of other languages including Spanish, French, German, Vietnamese, and Turkish.
Serbian is a language that can be written in two scripts. Serbian written in Cyrillic looks like "Српски". Serbian written in Latin looks like "Srpski". Both represent the same language with the same words and grammar. The difference is only the writing system.
Chinese has two common script variants. Simplified Chinese uses simplified Han characters. Traditional Chinese uses traditional Han characters. The same sentence appears differently depending on which script you use.
This distinction matters when building interfaces. A Serbian user might prefer Cyrillic text over Latin text. A Chinese user needs to choose between Simplified and Traditional characters. Your interface needs to display script names so users can make these choices.
The problem with hardcoding script names
You can create a lookup table that maps script codes to script names.
const scriptNames = {
Latn: "Latin",
Cyrl: "Cyrillic",
Arab: "Arabic",
Hans: "Simplified Chinese",
Hant: "Traditional Chinese"
};
console.log(scriptNames.Latn);
// "Latin"
This approach only works for English speakers. Users who speak other languages see English script names they may not understand. You need translations for every language you support.
const scriptNames = {
en: {
Latn: "Latin",
Cyrl: "Cyrillic",
Arab: "Arabic"
},
es: {
Latn: "latino",
Cyrl: "cirílico",
Arab: "árabe"
},
fr: {
Latn: "latin",
Cyrl: "cyrillique",
Arab: "arabe"
}
};
This becomes unmaintainable quickly. Every new language requires a complete set of translations. Every new script requires entries in every language. You need a better solution.
Using Intl.DisplayNames to get script names
The Intl.DisplayNames constructor creates a formatter that converts script codes into human-readable names. You specify a locale and set the type to "script".
const names = new Intl.DisplayNames(["en"], { type: "script" });
console.log(names.of("Latn"));
// "Latin"
The first argument is an array of locale identifiers. The second argument is an options object where type: "script" tells the formatter you want script names. The of() method takes a script code and returns its name.
Script codes follow the ISO 15924 standard. Each script has a four-letter code with the first letter capitalized and the remaining three letters lowercase. Latin is Latn. Cyrillic is Cyrl. Arabic is 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"
The formatter handles all the complexity of maintaining script name translations. You only need to provide the script code.
Common script codes
The ISO 15924 standard defines codes for over 160 scripts. These are the most commonly used codes.
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"
Latin covers most Western European languages. Cyrillic covers Russian, Bulgarian, Ukrainian, and other Slavic languages. Arabic covers Arabic, Persian, and Urdu. Han covers Chinese, with Hans for Simplified Chinese and Hant for Traditional Chinese. Hangul covers Korean. Hiragana and Katakana are Japanese scripts.
Displaying script names in different locales
Script names localize based on the display locale. Create formatters with different locales to see names in different languages.
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"));
// "ラテン文字"
Each formatter returns the script name in its display locale. This handles all the complexity of maintaining script name translations.
The same pattern works for any script code.
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"));
// "阿拉伯文"
The formatter applies the correct linguistic conventions for each language automatically.
Building a script selector for language variants
Some languages offer users a choice of scripts. Serbian can be written in Cyrillic or Latin. Chinese can be written in Simplified or Traditional characters. You need to display these options so users can choose.
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" }
// ]
This function returns script options in the user's interface language. Serbian users see options for Cyrillic and Latin scripts. Chinese users see options for Simplified and Traditional scripts. The names appear in the language the user understands.
Displaying full locale identifiers with script information
Locale identifiers can include script codes to distinguish between writing systems. The format is language-script-region, like sr-Cyrl-RS for Serbian written in Cyrillic in Serbia or zh-Hans-CN for Simplified Chinese in China.
When you display these locale identifiers, extract the script code and convert it to a readable name.
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)"
This pattern makes locale identifiers human-readable by combining language names with script names. Users see "Serbian (Cyrillic)" instead of "sr-Cyrl".
Creating a font selector with script names
Font selection interfaces often group fonts by the scripts they support. You need to display script names so users understand which scripts each font covers.
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"]
// }
// ]
This creates a list of fonts with their supported scripts in human-readable form. Users can choose fonts based on the writing systems they need.
Showing available input methods by script
Operating systems and browsers provide input methods for different scripts. A Japanese input method converts Latin characters into Hiragana, Katakana, or Kanji. A Chinese input method converts Pinyin into Simplified or Traditional Chinese characters. You can display available input methods with their script names.
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: "ヘブライ文字" }
// ]
This displays input method names in the user's language. Users see "Hiragana" when the interface is in English and "ひらがな" when the interface is in Japanese.
Understanding script code capitalization
Script codes follow a specific capitalization pattern. The first letter is uppercase. The remaining three letters are lowercase. Latn is correct. LATN, latn, and LaTn are not standard.
The of() method accepts script codes regardless of capitalization.
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"
The formatter handles all variations correctly. However, using the standard capitalization pattern makes your code more readable and consistent with the ISO 15924 standard.
Handling fallback locales
The Intl.DisplayNames constructor accepts an array of locales. If the first locale is not available, the formatter falls back to the next locale in the array.
const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "script" });
console.log(names.of("Latn"));
// "Latin"
The formatter tries "xx-XX" first, which does not exist, then falls back to "en". This ensures your code works even when the requested locale is unavailable.
You can check which locale the formatter actually uses with the resolvedOptions() method.
const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "script" });
console.log(names.resolvedOptions().locale);
// "en"
This shows the formatter resolved to English after the fallback.
Building a multilingual content management system
Content management systems that support multiple scripts need to show which scripts are available for each piece of content. You can display script names to help content editors choose the correct version.
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" }
// ]
This pattern helps content editors see which script versions exist and navigate between them.
Browser support
The Intl.DisplayNames API with script type support is available in all modern browsers. It has been supported since March 2021 across major browsers including Chrome, Firefox, Safari, and Edge.
You can check if the API is available before using it.
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");
}
For older browsers, you need to provide a fallback or use a polyfill. A simple fallback uses a hardcoded mapping of script codes to names.
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"
This ensures your code works even in browsers without Intl.DisplayNames support, though you lose the automatic localization features.