Translating Manifest Fields and Store Listings
Localise an extension's name, description, action title and command descriptions through _locales and __MSG_ placeholders, and keep store listings in step with the shipped translations.
Table of Contents
An extension can have perfectly translated UI and still greet a German user with an English name in the toolbar, an English tooltip, an English keyboard-shortcut description on chrome://extensions/shortcuts, and an English store listing. Those strings do not live in your popup’s code — they live in the manifest and in the store dashboard, and each is translated through a different mechanism. This guide is part of internationalisation and accessibility.
Where user-visible strings actually live
Step-by-step
1. Declare a default locale and use placeholders
1{
2 "manifest_version": 3,
3 "default_locale": "en",
4 "name": "__MSG_extName__",
5 "short_name": "__MSG_extShortName__",
6 "description": "__MSG_extDescription__",
7 "action": { "default_title": "__MSG_actionTitle__" },
8 "commands": {
9 "save-page": {
10 "suggested_key": { "default": "Alt+Shift+S" },
11 "description": "__MSG_cmdSavePage__"
12 }
13 }
14}
Execution context: parsed at install and on every browser locale change. default_locale is mandatory once _locales exists — without it the manifest fails to load. The browser substitutes __MSG_key__ in the fields it supports; placeholders in unsupported fields are shown literally, which is how you discover them.
2. Provide the messages
1// _locales/en/messages.json
2{
3 "extName": { "message": "Reader — save and read later", "description": "Extension name in the store and toolbar. Max ~45 characters." },
4 "extShortName": { "message": "Reader", "description": "Short name where space is tight. Max 12 characters." },
5 "extDescription": { "message": "Save articles, quotes and links to read later, on any device.", "description": "Shown on the extensions page. Max 132 characters." },
6 "actionTitle": { "message": "Save this page to Reader", "description": "Toolbar button tooltip." },
7 "cmdSavePage": { "message": "Save the current page", "description": "Shown on the keyboard shortcuts page." }
8}
Execution context: read by the browser for the user’s locale, falling back to default_locale per key. The description fields are for translators, not users — stating the length limit there prevents the most common translation bug, a name that is clipped mid-word in the toolbar.
3. Add locales, and let missing keys fall back
1// _locales/de/messages.json
2{
3 "extName": { "message": "Reader – speichern und später lesen" },
4 "extDescription": { "message": "Artikel, Zitate und Links speichern und später lesen – auf jedem Gerät." },
5 "actionTitle": { "message": "Diese Seite in Reader speichern" },
6 "cmdSavePage": { "message": "Aktuelle Seite speichern" }
7}
Execution context: read by the browser for German-locale users. extShortName is absent here, so it falls back to English — a partial locale is valid, which means a new locale can ship incrementally. The directory name must be a supported locale code (de, pt_BR, zh_CN — underscores, not hyphens).
4. Respect the length limits per field
| Field | Practical limit | What happens past it |
|---|---|---|
name | 45 characters | Store rejects; toolbar truncates earlier |
short_name | 12 characters | Truncated in app launchers |
description | 132 characters | Store rejects the package |
command description | ~60 characters | Wraps awkwardly on the shortcuts page |
German and Finnish translations routinely run 30% longer than English, so a 40-character English name leaves too little room. Check lengths in CI rather than in review:
1import { readdirSync, readFileSync } from "node:fs";
2const LIMITS = { extName: 45, extShortName: 12, extDescription: 132 };
3for (const loc of readdirSync("_locales")) {
4 const msgs = JSON.parse(readFileSync(`_locales/${loc}/messages.json`, "utf8"));
5 for (const [key, max] of Object.entries(LIMITS)) {
6 const len = msgs[key]?.message?.length ?? 0;
7 if (len > max) throw new Error(`${loc}/${key} is ${len} chars (max ${max})`);
8 }
9}
Execution context: Node, in CI. A store rejection for an over-long description in one locale blocks the whole release, so this is worth failing the build over.
5. Translate the store listing separately — and keep it in step
The store listing is not generated from _locales. In the Chrome Web Store dashboard, add each language under Store listing and enter the title, summary and description; screenshots can also be localised. Firefox’s AMO has an equivalent per-locale listing editor.
A practical way to keep the two aligned is to keep the listing text in the repository next to the manifest strings and copy from there, so a translator updates both in one change:
1store/
2 en/listing.md # title, summary, full description
3 de/listing.md
4_locales/
5 en/messages.json
6 de/messages.json
Execution context: the repository. Nothing automates the dashboard entry for you, but a single source file per language means the listing and the installed extension stop drifting apart — and drift here is visible to users before they install.
Choosing which locales to ship
Every added locale is a permanent maintenance cost: each new string needs translating before release, or it falls back to English and the UI becomes a mixture. That cost is worth paying where your users are, and the store dashboard’s installation statistics by language show exactly where that is.
A sensible progression is to ship the manifest strings and the store listing first for your top few languages — they are short, change rarely, and determine whether a user installs at all — and to translate the full UI only once a locale’s share justifies keeping it complete. A German listing and name with an English popup is a better experience than an English listing that a German user never clicks.
Right-to-left locales add layout work on top of translation, which is covered in supporting RTL locales in extension pages. Plan for it before adding Arabic or Hebrew rather than after.
Cross-browser variation
- Chrome / Edge:
__MSG_substitution works inname,short_name,description,action.default_title, command descriptions and a few other fields. The store listing is localised in the developer dashboard per language. - Firefox: the same
_localesformat and placeholders. AMO listings are localised in the AMO developer hub; Firefox also shows the manifest description inabout:addons. - Safari:
_localesis supported for the extension. The App Store listing belongs to the containing app and is localised in App Store Connect, alongside the app’s ownLocalizable.strings. - All three: locale directory names use underscores (
pt_BR), and an unrecognised directory name is silently ignored — a typo produces a locale that never loads.
Verification
- Switch the browser’s UI language to one of your locales, restart, and check the toolbar tooltip, the name on the extensions page and the shortcuts page.
- Confirm the resolved strings from any extension context:
1[chrome.i18n.getUILanguage(), chrome.i18n.getMessage("extName"), chrome.i18n.getMessage("extShortName")];
2// ["de", "Reader – speichern und später lesen", "Reader"]
Execution context: any extension page or the service worker. The short name falling back to English here confirms the fallback works as designed.
- Run the length check in CI across every locale.
- Search the extensions page and shortcuts page for a literal
__MSG_— any hit is a placeholder in an unsupported field or a missing key in the default locale.
FAQ
Can I change the language without changing the browser’s?
Not for manifest fields — the browser resolves them from its own UI language. Your own UI can offer a language switch by loading messages manually, but the name, tooltip and shortcut descriptions will follow the browser.
Why does my extension show __MSG_extName__?
The key is missing from the default_locale file, or default_locale is not set. The default locale must contain every key the manifest references.
Do I need to translate the store listing if the extension UI is translated?
The listing is what users see before installing, so it matters more than the UI for acquisition. Translate it first.
Related
- Localising extension UI with the i18n API — the runtime half of the same system.
- Supporting RTL locales in extension pages — layout for right-to-left languages.
- Generating a manifest per browser target — keeping placeholders intact through the build.
- Internationalisation and accessibility — the parent guide.