LocalePack
ChromeFirefoxEdge오페라사파리CWS 등록 페이지
Vue.jsReact
Next.jsi18nextReact Native
개발자 가이드성공 사례
Home/Guides/Chrome locale codes
August 8, 2026

Chrome extension locale codes: all 55

Chrome accepts exactly 55 locale codes as subfolder names inside _locales/. Anything else is ignored silently — no error, no warning, no log line. This page is the complete list, plus the two rules that cause almost every “my translations aren’t showing up” bug report.

Rule 1: underscores, not hyphens

BCP‑47 — the standard behind Accept-Language headers, HTML lang attributes, Intl locales, i18next, Rails, iOS and virtually every translation platform — writes language-region tags with a hyphen: pt-BR, zh-CN, en-US.

Chrome extension _locales folder names use an underscore: pt_BR, zh_CN, en_US. The language subtag is lowercase and the region subtag is uppercase, exactly as spelled in the table below.

From the chrome.i18n documentation: “If you use an unsupported locale, Google Chrome ignores it.” That is the entire failure mode. The extension still loads. The Web Store still accepts the ZIP. Users in that language simply see your default_locale strings forever, and nothing in DevTools tells you why.

Wrong — BCP‑47 hyphens. These four folders are dead weight in the ZIP:

_locales/
├── en/
│   └── messages.json
├── pt-BR/          ← ignored
│   └── messages.json
├── zh-CN/          ← ignored
│   └── messages.json
├── es-419/         ← ignored
│   └── messages.json
└── en-GB/          ← ignored
    └── messages.json

Right — underscores, uppercase region:

_locales/
├── en/
│   └── messages.json
├── pt_BR/
│   └── messages.json
├── zh_CN/
│   └── messages.json
├── es_419/
│   └── messages.json
└── en_GB/
    └── messages.json

Casing matters too. Chrome matches the folder name against its list literally:

✗_locales/pt-BR/
✓_locales/pt_BR/

Hyphen is the single most common cause of a silently ignored locale folder.

✗_locales/pt_br/
✓_locales/pt_BR/

The region subtag is uppercase. Lowercase is not on the supported list.

✗_locales/zh-Hans/
✓_locales/zh_CN/

Chrome has no script-subtag locales. Simplified Chinese is zh_CN.

Why this bites in CI, not on your laptop

Almost nothing upstream of your build emits underscores. Translation platforms export pt-BR.json. Your i18n library’s locale directory is named zh-CN. Your CI script copies the directory tree as-is into dist/. So a rename step is usually required, and it belongs in the build — not in a manual checklist someone forgets before a release:

# Rename BCP-47 directories to Chrome _locales names
for dir in dist/_locales/*-*/; do
  name=$(basename "$dir")
  # pt-BR -> pt_BR, zh-CN -> zh_CN, es-419 -> es_419
  mv "$dir" "dist/_locales/${name//-/_}"
done

That naive rename handles the common cases because Chrome’s region subtags are already uppercase in BCP‑47 form. It does not fix zh-Hans or pt, which have no direct Chrome equivalent — see the mapping notes below the table. The only reliable check is to compare your output folder names against the supported list and fail the build on a mismatch.

All 55 supported _locales codes

Every code below is valid as a folder name under _locales/ and as a value for default_locale in manifest.json. The last column marks the 52 locales LocalePack translates into.

CodeLanguageNative nameLocalePack
arArabicالعربية✓
amAmharicአማርኛ✓
bgBulgarianБългарски✓
bnBengaliবাংলা✓
caCatalanCatalà✓
csCzechČeština✓
daDanishDansk✓
deGermanDeutsch✓
elGreekΕλληνικά✓
enEnglishEnglish✓
en_AUEnglish (Australia)English (AU)—
en_GBEnglish (United Kingdom)English (UK)—
en_USEnglish (United States)English (US)—
esSpanishEspañol✓
es_419Spanish (Latin America)Español (LA)✓
etEstonianEesti✓
faPersianفارسی✓
fiFinnishSuomi✓
filFilipinoFilipino✓
frFrenchFrançais✓
guGujaratiગુજરાતી✓
heHebrewעברית✓
hiHindiहिन्दी✓
hrCroatianHrvatski✓
huHungarianMagyar✓
idIndonesianBahasa Indonesia✓
itItalianItaliano✓
jaJapanese日本語✓
knKannadaಕನ್ನಡ✓
koKorean한국어✓
ltLithuanianLietuvių✓
lvLatvianLatviešu✓
mlMalayalamമലയാളം✓
mrMarathiमराठी✓
msMalayBahasa Melayu✓
nlDutchNederlands✓
noNorwegianNorsk✓
plPolishPolski✓
pt_BRPortuguese (Brazil)Português (BR)✓
pt_PTPortuguese (Portugal)Português (PT)✓
roRomanianRomână✓
ruRussianРусский✓
skSlovakSlovenčina✓
slSlovenianSlovenščina✓
srSerbianСрпски✓
svSwedishSvenska✓
swSwahiliKiswahili✓
taTamilதமிழ்✓
teTeluguతెలుగు✓
thThaiไทย✓
trTurkishTürkçe✓
ukUkrainianУкраїнська✓
viVietnameseTiếng Việt✓
zh_CNChinese (Simplified)简体中文✓
zh_TWChinese (Traditional)繁體中文✓

55 codes total. LocalePack ships 52 of them; the 3 it does not are the English regional variants, which are a copywriting decision rather than a translation job — see below.

Notes on the list

  • •There is no bare zh and no bare pt. Chinese exists only as zh_CN and zh_TW; Portuguese only as pt_BR and pt_PT. If your source files are named zh or pt, you must decide which variant they represent before shipping.
  • •es_419 uses a numeric UN M.49 region code, not a country code. 419 is Latin America. It sits alongside es, which in practice serves European Spanish.
  • •fil (Filipino) is a three-letter code. It is not tl, and tl is not on the list.
  • •no is the only Norwegian code. Neither nb nor nn appears.
  • •ar, fa and he are right-to-left. Shipping the catalog is the easy half; your popup and options page need RTL layout handling to match.
  • •Several widely requested languages are simply absent — among them Urdu, Punjabi, Icelandic, Macedonian, Georgian, Armenian, Azerbaijani, Nepali, Sinhala, Khmer, Burmese and Lao. There is no way to add them: an unlisted folder name is ignored.

Copy-paste list of all 55 codes

Grab this for a build script, a validation config, or a spreadsheet:

ar am bg bn ca
cs da de el en
en_AU en_GB en_US es es_419
et fa fi fil fr
gu he hi hr hu
id it ja kn ko
lt lv ml mr ms
nl no pl pt_BR pt_PT
ro ru sk sl sr
sv sw ta te th
tr uk vi zh_CN zh_TW

As a shell array, so you can scaffold folders or validate an existing tree:

CHROME_LOCALES=(
  ar am bg bn ca
  cs da de el en
  en_AU en_GB en_US es es_419
  et fa fi fil fr
  gu he hi hr hu
  id it ja kn ko
  lt lv ml mr ms
  nl no pl pt_BR pt_PT
  ro ru sk sl sr
  sv sw ta te th
  tr uk vi zh_CN zh_TW
)

# Scaffold every folder you intend to ship
for code in "${CHROME_LOCALES[@]}"; do
  mkdir -p "_locales/$code"
done

# Or: fail the build if any existing folder is not on the list
for dir in _locales/*/; do
  code=$(basename "$dir")
  case " ${CHROME_LOCALES[*]} " in
    *" $code "*) ;;
    *) echo "Unsupported locale folder: $code" && exit 1 ;;
  esac
done
A validation loop like the one above is worth ten minutes of your time. Chrome will never tell you that _locales/pt-BR is wrong, so the check has to live in your pipeline. Pair it with a JSON validity check on each messages.json — see validating messages.json.

How Chrome picks a locale at runtime

The locale Chrome resolves for your extension follows the browser’s UI language, not the language of the page the user is looking at and not the operating system on its own. You can read the value your extension is running under:

// Synchronous, returns a string like "en-US" or "pt-BR"
const uiLanguage = chrome.i18n.getUILanguage();

// Asynchronous, returns the user's accept-languages list
const accepted = await chrome.i18n.getAcceptLanguages();
Note the shape of the return value: getUILanguage() gives you a BCP‑47 tag with a hyphen, while your folders use underscores. If you compare the two directly — for example to decide whether you shipped a catalog for the current user — convert first.

From there, Chrome selects a catalog by narrowing from the most specific match toward your default_locale. The practical model:

Browser UI language: pt-BR

  1. _locales/pt_BR/messages.json   ← exact match, if you shipped it
  2. _locales/pt/…                  ← base language — but "pt" is not a
                                       supported Chrome code, so for
                                       Portuguese this step cannot exist
  3. _locales/en/messages.json      ← default_locale, always the last resort

Result: getMessage() always returns a string, never throws.

Rather than lean on the exact intermediate steps, design so the outcome is the same either way. Two rules make the fallback question moot:

  • •Ship the base language alongside any regional variant. If you ship en_GB, also ship en. A user in Canada or Ireland then lands on real English regardless of how the narrowing works, instead of possibly dropping all the way through to default_locale. The same applies to es next to es_419.
  • •Keep default_locale complete. It is the floor of the chain and the one catalog that must contain every key. See default_locale rules and errors.
  • •Fallback works per key, not per file. A key present in pt_BR with an empty message value returns an empty string — it does not fall back. Omit untranslated keys instead of shipping empty ones.
Chrome resolves the locale once, when the extension loads. Changing the browser language mid-session does not re-run getMessage() against a different catalog for an already-running extension, so test locale changes with a fresh browser start rather than a page reload.

The English variants: en, en_AU, en_GB, en_US

English is the only language on the list with regional variants that are not about translation at all — nobody is translating English into English. They exist for spelling and vocabulary: colour vs color, organise vs organize, autumn vs fall, and formatting conventions your copy might hard code.

When en alone is enough

  • •Your UI is short — buttons, labels, a handful of tooltips. The spelling delta across variants is usually zero or one word.
  • •You have no dedicated copywriter. Three near-identical catalogs that drift apart are worse than one that is right.
  • •You are optimising for effort. Every extra variant is another file to keep in sync on every string change, forever.

When regional English earns its place

  • •Your extension has substantial prose — onboarding, an options page with explanations, error messages written in full sentences. Spelling differences become visible at that volume.
  • •You are selling to a specific market where the wrong spelling reads as careless, or where terminology genuinely differs (billing, legal, education, healthcare).
  • •You already maintain a US and a UK voice for a website or marketing site, so the copy exists and only needs porting.
If you do ship variants, ship en too and point default_locale at it. Setting default_locale to en_US and omitting en leaves every other English-speaking region depending on fallback behaviour you would rather not have to reason about.

This is why LocalePack ships 52 locales rather than 55: the three English variants are an editorial call about your own source copy, not something a translation step should invent for you.

Store listing languages are a different surface

The codes on this page govern strings inside your extension: the popup, the options page, context menu items, and the __MSG_key__ substitutions in manifest.json.

Your Chrome Web Store listing — the title, summary, detailed description and screenshots a user reads before installing — is localised separately, in the Developer Dashboard, with its own language set. Translating one does not translate the other, and the two lists are not identical. It is entirely possible to have a fully localised extension with an English-only store listing, which is the worse half to leave undone: the listing is what decides whether the install happens at all.

  • •_locales/ — in-product strings, shipped in the ZIP, codes from the table above.
  • •Store listing — marketing copy, entered per language in the Developer Dashboard, not part of your build.

The differences are covered in detail in store listing vs in-extension i18n.

Pre-release checklist

  • •Every folder under _locales/ is on the 55-code list — spelled with an underscore and an uppercase region subtag.
  • •default_locale is set, is on the list, and has a matching folder containing every key.
  • •Base language folders exist next to any regional variants (en with en_GB, es with es_419).
  • •No empty "message": "" values left behind by a partial translation pass.
  • •Each messages.json is valid JSON with no trailing commas and no comments.
  • •The ZIP you upload preserves the folder names — some archive tools normalise case. Unzip your own artifact and look before you submit.

Frequently asked questions

Why is my pt-BR folder ignored?

Because Chrome's supported locale for Brazilian Portuguese is pt_BR with an underscore, not pt-BR with a hyphen. The Chrome documentation states: "If you use an unsupported locale, Google Chrome ignores it." There is no error and no warning — the extension loads, the folder is skipped, and Brazilian users see your default_locale strings. Rename the folder to pt_BR and reload the extension.

Does Chrome support en-GB for extensions?

Yes, as en_GB. Chrome supports four English codes for _locales folders: en, en_AU, en_GB and en_US. All four use underscores. If you ship a regional variant, ship the base en folder as well so English speakers outside those three regions still get English rather than falling through to your default_locale.

How many locales does Chrome support for extensions?

Chrome accepts 55 locale codes as _locales subfolder names, covering roughly 50 languages plus regional variants for English, Spanish, Portuguese and Chinese. Any folder name outside that list is silently ignored. The complete list is in the table on this page.

Is there a plain zh or pt locale code?

No. Chinese is available only as zh_CN (Simplified) and zh_TW (Traditional), and Portuguese only as pt_BR and pt_PT. A folder named zh or pt is not on the supported list and will be ignored, so you have to decide which variant your files represent. There is also no zh_Hans or zh_Hant — Chrome's list has no script subtags.

What happens if a user's language is not in my _locales folder?

Chrome falls back to the catalog named by default_locale in your manifest, so chrome.i18n.getMessage() still returns a string rather than throwing or returning nothing. That makes default_locale the one catalog that must contain every key your extension uses. Fallback also works per key: a key that exists in a translated file with an empty message value returns an empty string instead of falling back.

Are Chrome Web Store listing languages the same as _locales codes?

No. The _locales codes control strings inside the extension. Your store listing — title, summary, description and screenshots — is localised separately in the Chrome Web Store Developer Dashboard with its own language set, and it is not part of your build. Translating one does not translate the other.

Related guides

  • •Chrome extension i18n: the _locales folder structure — where the folders live and what goes inside them.
  • •default_locale: rules and common errors — the exact Chrome load errors and how to fix each one.
  • •Store listing vs in-extension i18n — the two localisation surfaces and why both matter.

Every LocalePack folder is already a valid Chrome name

Upload your messages.json, pay once, download a _locales ZIP — no account, no subscription. The 52 output folders are named pt_BR, zh_CN, es_419 and so on: every one is on the list above, so there is no rename step and nothing gets silently ignored. Placeholders like $PLACEHOLDER$ and $1 come back intact.

Translate your messages.json into 52 locales →
← Back to Guides
LocalePack
가이드개인정보처리방침이용약관지원

© 2025 LocalePack. 모든 권리 보유.

이 프로젝트는 다음으로 번역되었습니다: LocalePack logoLocalePack