default_locale: rules and common errors
default_locale is a single field in manifest.json, but getting it wrong — or omitting it — prevents your extension from loading entirely. This article covers every rule Chrome enforces, the exact error messages you will see, and how to fix each one.
The fundamental rule
Chrome enforces a strict coupling between default_locale and the _locales directory. The rule has two sides:
If _locales/ exists → default_locale is required
Create the _locales directory and omit default_locale and Chrome will refuse to load your extension.
If default_locale is declared → _locales/ must exist
Declare default_locale without creating the _locales folder and you get the same result: the extension fails to load.
If neither exists → fully non-i18n extension
An extension without _locales/ and without default_locale is perfectly valid. It just cannot use chrome.i18n.getMessage() or __MSG_ manifest substitutions.
Error: “default_locale is required”
This is the most common mistake. You add a _locales/ folder with translated files but forget to add default_locale to the manifest.
Chrome DevTools error:
Could not load extension from ‘/path/to/my-extension’.
Manifest file is invalid.
default_locale is required if _locales/ is present.Fix — add default_locale to manifest.json:
{
"manifest_version": 3,
"name": "__MSG_appName__",
"default_locale": "en" ← add this
}The value must be a locale code that has a corresponding folder inside _locales/. If default_locale is "en", there must be a _locales/en/messages.json file.
Error: “_locales/ directory missing”
The opposite mistake: you declare default_locale but either never created _locales/ or accidentally deleted it.
Chrome DevTools error:
Could not load extension from ‘/path/to/my-extension’.
Manifest file is invalid.
_locales directory is missing or inaccessible.Fix:
# Create the required folder structure
mkdir -p _locales/en
# Then add _locales/en/messages.json with at least one key
echo '{ "appName": { "message": "My Extension" } }' > _locales/en/messages.jsonError: default_locale folder is missing from _locales/
A subtler variant: you have default_locale declared and a _locales/ folder, but the folder for the declared default locale is absent. For example, default_locale is "en" but only _locales/de/ exists.
Could not load extension from ‘/path/to/my-extension’.
Catalog file is missing for locale en.Fix:
_locales/
├── en/ ← must match default_locale exactly
│ └── messages.json
└── de/
└── messages.jsonError: invalid locale code in default_locale
Chrome only accepts locale codes from its approved list. An unrecognised value in default_locale will fail validation.
"default_locale": "en-US""default_locale": "en_US"Hyphens are not allowed. Chrome uses underscores.
"default_locale": "english""default_locale": "en"Full language names are not valid. Use the two-letter ISO code.
"default_locale": "pt-BR""default_locale": "pt_BR"Region codes use underscore, not hyphen.
_locales/ must match default_locale exactly — same casing, same separator. If default_locale is "pt_BR", the folder must be named pt_BR, not pt_br or PT_BR.What default_locale controls at runtime
Beyond validation, default_locale defines the fallback chain Chrome uses when a locale is unavailable:
User locale: zh_TW (Traditional Chinese)
Chrome lookup order:
1. _locales/zh_TW/messages.json ← specific variant
2. _locales/zh/messages.json ← parent language (if zh_TW not found)
3. _locales/en/messages.json ← default_locale (if zh also not found)
Result: always returns a string, never throws.This means you can ship a small set of locales and rely on default_locale for the rest. Users whose language is not translated will see your source strings rather than a broken experience.
"message": "", Chrome will return an empty string — it will not fall back to the default locale for that key. Always omit untranslated keys entirely, or remove the locale file altogether, rather than leaving empty message strings.How the Chrome Web Store validates default_locale
The Web Store runs the same manifest validation as Chrome itself when you upload a ZIP. Additionally, it checks that:
- •
default_localematches a folder in_locales/. - •Every
_locales/*/messages.jsonfile is valid JSON. - •Every key used in
__MSG_key__manifest substitutions exists in thedefault_localemessages.json.
Upload failures from these checks result in a rejection email with a message like “Your item was found to have errors” and a reference to the manifest field at fault.
Removing i18n from an existing extension
If you decide to remove i18n support from an extension that previously had it, you must remove both sides of the pair together:
# 1. Remove default_locale from manifest.json
{
"manifest_version": 3,
"name": "My Extension", ← plain string, not __MSG_appName__
"description": "...",
"version": "1.0.0"
// no default_locale
}
# 2. Delete the _locales/ directory entirely
rm -rf _locales/default_locale but leave the _locales/ folder on disk, Chrome will throw the “default_locale is required” error again. Both must go together.Quick reference
| Situation | Result |
|---|---|
| _locales/ present + default_locale declared | Valid — i18n is active |
| Neither _locales/ nor default_locale | Valid — non-i18n extension |
| _locales/ present, default_locale missing | Error: default_locale is required |
| default_locale declared, _locales/ missing | Error: _locales directory missing |
| default_locale folder absent from _locales/ | Error: Catalog file is missing for locale {code} |
| default_locale value uses hyphen (e.g. "en-US") | Error: invalid locale code |
| __MSG_key__ references key missing from default_locale | Web Store rejection / runtime empty string |
| Translated file has empty message string | Returns empty string — does not fall back |
Generate a validated _locales ZIP in minutes
LocalePack validates your source messages.json on upload — catching format errors before translation — and outputs a _locales ZIP with correctly named folders ready to drop into your extension. No manifest errors, no rejected uploads. Pay once, no subscription.