Store listing localization vs in-extension i18n (Chrome + AMO pitfalls)
Every browser extension has two public-facing layers of text: the store listing (what users see before they install) and the in-extension strings (what users see after they install). These are localised through completely different systems. Confusing the two is one of the most common extension i18n mistakes — and it leads to partial translations, broken store pages, and missed international users.
The two layers, explained
Here is the split in plain terms:
_locales/*/messages.jsonchrome.i18n.getMessage() / browser.i18n.getMessage()messages.json does not translate your store listing. Translating your store listing does not translate what users see inside the extension. They are completely independent.The __MSG_ bridge in Chrome
There is one exception to the separation: Chrome allows __MSG_key__ tokens in manifest.json fields. When Chrome reads your manifest, it substitutes these tokens with the matching key from the active locale’s messages.json:
// manifest.json
{
"manifest_version": 3,
"name": "__MSG_appName__",
"description": "__MSG_appDescription__",
"default_locale": "en",
"version": "1.0.0"
}
// _locales/en/messages.json
{
"appName": {
"message": "Tab Manager Pro",
"description": "Extension name displayed in Chrome toolbar and Web Store."
},
"appDescription": {
"message": "Manage, search, and organize your browser tabs effortlessly.",
"description": "Short description shown on the Chrome Web Store listing."
}
}
// _locales/de/messages.json
{
"appName": {
"message": "Tab Manager Pro",
"description": "Extension name displayed in Chrome toolbar and Web Store."
},
"appDescription": {
"message": "Verwalten, suchen und organisieren Sie Ihre Browser-Tabs mühelos.",
"description": "Short description shown on the Chrome Web Store listing."
}
}With this setup, Chrome pulls the extension name and description from messages.json and displays the correct locale in both:
- The Chrome toolbar (extension name, tooltip)
- The Chrome Web Store listing page (if no separate listing localisation is uploaded)
__MSG_appDescription__ as the listing description only if you have not entered a separate description in the Chrome Developer Dashboard for that locale. If you upload a per-locale listing in the dashboard, that dashboard text takes priority over the manifest substitution.Chrome Web Store listing localisation
The Chrome Developer Dashboard has a dedicated “Store Listing” tab where you can enter per-locale marketing text that is independent of your extension code:
What's controlled by the dashboard
- Detailed description — the long store page description (up to 16,000 characters)
- Screenshots — per-locale screenshots with localised UI
- Promotional tiles — small, large, and marquee promotional images
- Category & tags — store categorisation
What's controlled by __MSG_ (or entered directly)
- Extension name — from
__MSG_appName__in manifest, or a fixed string - Short description — from
__MSG_appDescription__in manifest, or a fixed string (max 132 characters)
The recommended approach: use __MSG_appName__ and __MSG_appDescription__ in your manifest so that the name and short description are automatically localised from messages.json. Then enter the detailed description in each locale separately in the dashboard. This minimises duplication while ensuring both layers are localised.
AMO (Firefox) listing localisation
AMO (addons.mozilla.org) works differently from Chrome in a few important ways:
AMO displays __MSG_ substitutions automatically
__MSG_appName__ and __MSG_appDescription__ from your messages.json files. AMO shows the resolved name and description for the visitor’s locale on the add-on listing page.AMO has a separate 'Describe' section
messages.json.AMO does NOT sync listing text from _locales
messages.json translations are incomplete or placeholder-mangled, the reviewer sees broken text. This can lead to review delays or rejection comments. Complete, accurate translations improve your chances of a smooth AMO review.Side-by-side: Chrome vs AMO vs Safari
| Aspect | Chrome | Firefox (AMO) | Safari |
|---|---|---|---|
| Name / short desc source | __MSG_ from messages.json | __MSG_ from messages.json | __MSG_ (in-extension only) |
| Detailed description source | Developer Dashboard | AMO developer hub | App Store Connect |
| Screenshots source | Developer Dashboard | AMO developer hub | App Store Connect |
| Store desc auto-syncs from _locales? | No (name/desc only via __MSG_) | No (name/desc only via __MSG_) | No |
| Locale code format (store) | en, de, ja, zh_CN | en-US, de, ja, zh-CN | en-US, de-DE, ja, zh-Hans |
| Listing preview before publish | Yes (Developer Dashboard) | Yes (AMO dev hub) | Yes (App Store Connect) |
Why both layers matter for international reach
Store listing localisation and in-extension localisation serve different stages of the user journey:
Discovery (store listing)
A developer or user searching the Chrome Web Store or AMO in German sees your extension name, description, and screenshots. If these are in English only, the listing ranks lower in local search results and the user is less likely to click.
Decision (store listing)
The user reads the detailed description and reviews screenshots. Localised marketing copy and screenshots showing a native-language UI dramatically increase install rates in non-English markets.
Usage (in-extension strings)
After install, the user interacts with the extension popup, options page, and content script UI. messages.json translations determine this experience. If the listing was localised but the extension itself is English-only, users feel misled.
Retention (both)
An extension with consistent localisation — store listing and in-extension UI both in the user's language — builds trust and reduces uninstalls. The user never encounters a jarring language switch.
The __MSG_ pattern: what it covers and what it doesn’t
Here is the exact scope of __MSG_key__ substitution in manifest fields:
| Manifest field | __MSG_ substitution? | Notes |
|---|---|---|
| name | ✓ Yes | Extension name in toolbar and store listing |
| short_name | ✓ Yes | Shorter name for space-constrained UI |
| description | ✓ Yes | Short store description (max 132 chars on Chrome) |
| action.default_title | ✓ Yes | Toolbar button tooltip text |
| version | ✗ No | Must be a literal version string |
| permissions | ✗ No | Array of permission strings, not localisable |
| content_scripts | ✗ No | Match patterns and script paths are not localisable |
| background.service_worker | ✗ No | Script path, not localisable |
In practice, most developers use __MSG_ for just two fields: name and description. These are the only two manifest fields that appear in the store listing.
Common pitfalls
Translating messages.json and assuming the store listing is done
The most common mistake. messages.json translations only affect what getMessage() returns at runtime, plus the name / description fields in manifest.json (via __MSG_). The detailed store description, screenshots, and promotional text must be entered separately in each store’s developer console.
Hardcoding name/description in manifest.json instead of using __MSG_
If your manifest has "name": "Tab Manager Pro" (a literal string), Chrome and Firefox will always show that English string regardless of locale — even though you have a perfectly good translation in _locales/de/messages.json. Use "name": "__MSG_appName__" to enable automatic locale switching.
Only localising the store listing, not in-extension strings
Some developers translate their Chrome Web Store description into multiple languages but leave the extension UI in English only. The user installs based on a localised listing, then opens the popup to find English-only text. This mismatch damages trust and increases uninstall rates.
Forgetting AMO listing text is separate from _locales
On AMO, the add-on name and short description come from __MSG_ substitution automatically. But the detailed “About this add-on” text is entered in the AMO developer hub’s “Describe” section. Many developers localise messages.json and expect AMO to pull the full description from it — it does not.
Using __MSG_ in fields where it's not supported
__MSG_ substitution only works in name, description, and short_name in the manifest. Placing it in action.default_title is also supported, but fields like permissions, content_scripts, or version do not support substitution.
Safari: App Store Connect is entirely separate
Safari Web Extensions ship via the App Store. __MSG_ substitution still works inside Safari for the toolbar and extension UI, but the App Store listing (app name, subtitle, description, keywords, screenshots) is managed entirely in App Store Connect. See the Safari localization article for details.
Recommended workflow
To localise both layers efficiently:
Set up __MSG_ in manifest.json
Use __MSG_appName__ and __MSG_appDescription__. This lets both the in-extension UI and the basic store listing draw from the same localised source.
Translate messages.json into your target locales
Upload your source messages.json to LocalePack, choose your target languages, and download the _locales ZIP. This handles all in-extension strings including appName and appDescription.
Enter detailed store listing copy per locale
In the Chrome Developer Dashboard, AMO developer hub, or App Store Connect, enter the longer marketing description for each locale you want to support. This text is independent of messages.json.
Upload localised screenshots
Take screenshots of your extension UI in each locale (the in-extension strings are already localised from step 2). Upload these per-locale screenshots in each store's developer console.
Quick reference: which text comes from where?
| Text the user sees | Source |
|---|---|
| Extension name in browser toolbar | __MSG_appName__ → messages.json "appName" |
| Extension name on store page | __MSG_appName__ (or dashboard override) |
| Short description on store page | __MSG_appDescription__ (or dashboard override) |
| Detailed store page description | Entered manually in store's developer console |
| Store screenshots | Uploaded per-locale in store's developer console |
| Popup text / options page labels | chrome.i18n.getMessage() → messages.json |
| Content script UI strings | chrome.i18n.getMessage() → messages.json |
| Toolbar button tooltip | __MSG_ or action.default_title → messages.json |
Localise your in-extension strings in minutes
LocalePack handles the messages.json side — including appName, appDescription, and all your in-extension strings with placeholder-safe translations. Upload, choose your target languages, pay once, and download a ready _locales ZIP. Then enter your store listing copy in each platform’s developer console to complete both layers.