_locales folder at your project root and validates that each declared locale has a messages.json. It emits locale JSON assets into the browser-specific build. During development, it picks up edits to any locale file without a full restart.
Template example
action-locales

_locales support.
Locale capabilities
Expected structure
default_locale in manifest.json should map to an existing _locales/<default>/messages.json.
Legacy layout: _locales next to the manifest
The project root is the canonical place for _locales, even when your manifest lives in a subfolder such as src/. A _locales folder next to a nested manifest still builds. The compiler emits a LocalesLayoutWarning that asks you to move it to the root. Browsers read locales from the extension root, so the root placement matches what ships.
Sample locales declaration in manifest.json
Here is how to declare locales in manifest.json:
_locales folder:
Sample messages.json file
Example messages.json file for translations:
Output path
Extension.js emits locale JSON files under:Development behavior
- Extension.js adds locale JSON files to compilation dependencies and watches them.
- Locale changes trigger extension reload behavior (hard reload), not component-style hot module replacement (HMR).
- Extension.js fails validation with actionable diagnostics when required locale files are missing or invalid.
Validation behavior
Extension.js validates:- A
_localesfolder withoutdefault_localein the manifest fails the build, because browsers reject that combination - Existence of
_locales/<default>and itsmessages.json - JSON validity for every locale’s
messages.json __MSG_*__references in the manifest against default locale keys
__MSG_*__ scan:
- Predefined
@@messages, such as__MSG_@@ui_locale__, are exempt because the browser provides them. - The
@character is allowed inside message keys, matching Chrome’s grammar for message names.
Troubleshooting missing locale keys
If your manifest uses__MSG_extension_description__, ensure the default locale file contains extension_description:
Packaging behavior
When you build with--zip or --zip-source, Extension.js checks the default locale again at zip time. A manifest that declares default_locale without a matching messages.json produces a warning, because stores reject packages without their default locale.
Zip filenames come from the manifest name. A __MSG_*__ name resolves against the default locale’s messages.json, so the archive carries the translated name, not the placeholder.
Best practices
- Keep
messages.jsonkeys consistent across locales. - Update default locale first, then propagate keys to other locales.
- Validate locale JSON in continuous integration (CI) to catch malformed files before packaging.
- Keep
_localesat the project root, the placement that browsers and the packaging step read.
Next steps
- Understand update outcomes in dev update behavior.
- Continue with JSON in development.
- Learn about manifest development behavior.

