ISO 639-1 Cheatsheet - Language Code Reference

Localization breaks silently when the language you detect doesn't match the one you ship. Grouped by region, each two-letter code pairs its English and native name so you pick without confusing zh/zh-CN or pt/pt-BR. Use it to set the lang attribute, build Accept-Language negotiation, and store a canonical locale key. After reading you assign locale codes that browsers and backends agree on.

Reference·49 commands·Last updated 2026-07-21
isoLanguage Codesi18n

Typical Use Case

For developers building multilingual sites, locale routing and language negotiation. ISO 639-1 defines the two-letter language codes (zh, ja, en), which in practice combine with region subtags into BCP 47 tags such as zh-CN, en-US and pt-BR. This sheet covers 39 common languages plus their regional variants, along with four canonical Accept-Language header forms and four HTML lang attribute forms. The failure mode here is not an exception but silent degradation: a wrong lang attribute makes screen readers apply the wrong pronunciation rules to the whole page.

Asian Languages 12

zh
Chinese
zh-CN
Chinese (Simplified), Mainland China
zh-TW
Chinese (Traditional), Taiwan
zh-HK
Chinese (Traditional), Hong Kong
ja
Japanese
ko
Korean
vi
Vietnamese
th
Thai
id
Indonesian
ms
Malay
hi
Hindi
bn
Bengali

European Languages 22

en
English
en-US
English (United States)
en-GB
English (United Kingdom)
fr
French
de
German
es
Spanish
it
Italian
pt
Portuguese
pt-BR
Portuguese (Brazil)
ru
Russian
pl
Polish
nl
Dutch
sv
Swedish
da
Danish
no
Norwegian
fi
Finnish
el
Greek
tr
Turkish
cs
Czech
hu
Hungarian
ro
Romanian
uk
Ukrainian

Middle East & African Languages 5

ar
Arabic
he
Hebrew
fa
Persian
sw
Swahili
af
Afrikaans

Other Languages 2

la
Latin
eo
Esperanto

HTTP Accept-Language Example 4

Accept-Language: zh-CN,zh;q=0.9
Simplified Chinese preferred
Accept-Language: en-US,en;q=0.9
American English preferred
Accept-Language: ja,en;q=0.8
Japanese preferred, English as fallback
Accept-Language: *
Accept any language

HTML lang 4

<html lang="zh-CN">
Simplified Chinese page
<html lang="zh-TW">
Traditional Chinese page
<html lang="en">
English page
<html lang="ja">
Japanese page

Parameter matrix

ParameterEffectExample
bare language code (zh / ja / en)declare only the language, with no region preferencezh :: Chinese
language + region (zh-CN / en-GB)distinguish writing systems and word choice within one languagezh-CN :: Simplified Chinese (Mainland China)
<html lang="">declare the page language, driving screen reader pronunciation and translate prompts<html lang="zh-CN">
Accept-Language headerlet the client express preference; q values rank them and the server picks a variantAccept-Language: zh-CN,zh;q=0.9
wildcard *accept anything, which belongs last as a fallback rather than firstAccept-Language: *

Common pitfalls

SymptomA screen reader pronounces the Chinese page with pinyin or American English.

CauseThe lang attribute says zh rather than zh-CN, so the engine guesses a pronunciation rule.

FixWrite the exact tag that matches the content: zh-CN for Simplified, zh-TW or zh-HK for Traditional.

SymptomEnglish users in every region end up with the same spelling.

CauseThe locale key is just en and never splits en-US from en-GB, which differ in dates, spelling and word choice.

FixStore full tags in routes (en-GB / en-US) and fall back to bare en only for language-only matching.

SymptomThe Accept-Language header has q values, yet the server ignores the second preference.

CauseA q value must follow its language segment immediately, in the fixed form language;q=weight with weight between 0 and 1.

FixOrder by preference and write it as zh-CN,zh;q=0.9,en;q=0.8.

SymptomTraditional Chinese users get Simplified copy (or the reverse) with no visible error.

Causezh-HK, zh-TW and zh-CN are three separate tags, so a bundle keyed only on zh cannot tell them apart.

FixMaintain zh-CN and zh-TW bundles separately, and only map zh-HK to zh-TW when the difference genuinely does not matter.

Troubleshooting

  1. 1Check the language preference the request actually carries

    curl -sI https://example.com | grep -i accept-language

    No output means the request omitted the header, so the server can only fall back to a default.

  2. 2Verify the language tag the page declares

    curl -s https://example.com | grep -o "<html lang=[^>]*>"

    It should match the actual copy; a Traditional page needs lang="zh-TW" or lang="zh-HK".

Command Examples

Resolving language tags to readable names

node -e " const d = new Intl.DisplayNames(['en'], { type: 'language' }); console.log(['zh-CN', 'ja', 'pt-BR', 'en-GB', 'ar'].map(c => c + '=' + d.of(c)).join(' ')); "

Intl.DisplayNames reads the CLDR database, which confirms whether a tag is standard-recognized; an invalid tag throws RangeError, so it doubles as a validator.

Output

zh-CN=Chinese (China)  ja=Japanese  pt-BR=Brazilian Portuguese  en-GB=British English  ar=Arabic

Splitting language and region, then expanding likely subtags

node -e " for (const t of ['zh', 'zh-CN', 'zh-TW', 'en', 'pt-BR']) { const l = new Intl.Locale(t); console.log(t, 'language=' + l.language, 'region=' + (l.region || '-'), 'max=' + l.maximize().toString()); } "

maximize() turns zh into zh-Hans-CN, which is direct evidence that bare zh is presumed simplified. zh-TW expands to zh-Hant-TW, pinning script and region together.

Output

zh language=zh region=- max=zh-Hans-CN
zh-CN language=zh region=CN max=zh-Hans-CN
zh-TW language=zh region=TW max=zh-Hant-TW
en language=en region=- max=en-Latn-US
pt-BR language=pt region=BR max=pt-Latn-BR

Sorting Accept-Language into a fallback order by q

node -e " const h = 'zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7'; const parsed = h.split(',').map(s => { const p = s.trim().split(';q='); return { tag: p[0], q: p[1] ? +p[1] : 1 }; }); console.log(parsed.sort((a, b) => b.q - a.q).map(x => x.tag + '(' + x.q + ')').join(' > ')); "

Treat a missing q as 1, then sort descending to get the fallback chain; the server simply picks the first tag it has a translation for.

Output

zh-CN(1) > zh(0.9) > en(0.8) > ja(0.7)

The same number formats differently per language

node -e " console.log(new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(1234.5)); console.log(new Intl.NumberFormat('ja-JP').format(1234567.891)); "

German uses dots for thousands and a comma for decimals, Japanese the reverse. The region tag alone reshapes the output.

Output

1.234,50 €
1,234,567.891

Common Pitfalls

  • A wrong lang attribute never errors, but screen readers speak the wrong language and translation prompts plus search indexing decisions follow suit.
  • Never put the * wildcard first in Accept-Language: it matches immediately and swallows every specific preference after it, making negotiation meaningless.
  • Traditional-Chinese users receiving simplified copy — from writing zh-TW as zh or omitting the tag entirely — sees a page that looks perfectly fine.
  • Sharing one English string across all regions concentrates en-US versus en-GB spelling mismatches onto a handful of pages; automatic conversion will not cover them.
  • Join the two-letter code and the region with a hyphen (zh-CN). An underscore or slash makes the tag invalid and takes the q ordering down with it.

Tips

  • ISO 639-1 is two-letter codes; ISO 639-2 is three-letter (more detailed).
  • Language tags can carry a region suffix, e.g. zh-CN (Simplified), zh-TW (Traditional).
  • The Accept-Language request header negotiates content language; the server returns the matching version.
  • The HTML lang attribute helps search engines and screen readers detect language.
  • i18n libraries (i18next, vue-i18n) use these codes as locale identifiers.

FAQ

Which of zh, zh-CN, zh-TW and zh-HK should I use?

Choose by content, not by audience. Use zh-CN for simplified copy, zh-TW or zh-HK for traditional, and plain zh when you only declare a language without caring about region. The catch is that bare zh carries no script subtag, so BCP 47 likely-subtag rules resolve it to Hans — meaning zh is not the neutral "Chinese" anyone assumes, and a traditional-Chinese user served zh gets simplified text. zh-TW and zh-HK share the same script and differ only in regional conventions such as currency and date format.

Does the difference between en and en-US actually matter?

It depends on whether your content varies by region. Spelling (colour vs color), date format, currency symbol and quoting conventions all follow the region, so en-US or en-GB is required there. For region-neutral technical content, plain en suffices and saves a maintenance point. In practice most multilingual sites default to en-US, accepting that a few British phrasings need manual adjustment.

How exactly are q values in Accept-Language ordered?

q runs from 0 to 1, larger means more preferred, and an omitted q defaults to 1; ties fall back to header order. The syntax must be the tag followed by a semicolon and then q, as in zh-CN;q=0.9. Any other arrangement, such as putting q ahead of the tag or separating it with a comma, is ignored by parsers and silently drops the second preference. Once sorted descending the order is your fallback chain: zh-CN;q=0.9,zh;q=0.8 prefers an exact match and degrades to the bare language tag.

What breaks when the HTML lang attribute is wrong?

Nothing throws — it just fails silently. Screen readers pick pronunciation rules from lang and read Chinese with an English voice when it is wrong, browsers decide whether to offer translation based on it, search engines use it to decide whether alternate versions are needed, and spellcheck plus word breaking follow it too. It is the one declaration that produces entirely wrong behaviour without any error, so keep it centralized in the layout rather than scattered per page.

Can a language code be part of a URL route?

Yes, and it is the most common use of this table, but mind case and separators. Keep route segments lowercase (/pt-br, /zh-cn) because URL case sensitivity varies by server, and an uppercase form can 404 behind Nginx or a CDN. Normalize to a canonical form before comparing instead of matching the raw request string. Also reserve a default branch without a language prefix, or search engines cannot reach the root path.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us