Chrome Extension Localization: The Complete i18n Guide (2026)

AppBooster Team · · 5 min read
World map with connected language pins

Why Localize Your Chrome Extension?

The Chrome Web Store serves users in 200+ countries. Extensions available in multiple languages see dramatically higher install rates — Chrome automatically shows your extension in the user’s preferred language when translations exist.

Localization (i18n) isn’t just translation. It includes adapting date formats, number formats, text direction, and cultural conventions. Chrome provides a built-in i18n system through messages.json files that makes this straightforward.


Setting Up the File Structure

Each supported locale needs its own messages.json file under the _locales directory:

my-extension/
├── _locales/
│   ├── en/
│   │   └── messages.json
│   ├── es/
│   │   └── messages.json
│   ├── fr/
│   │   └── messages.json
│   ├── ja/
│   │   └── messages.json
│   └── ar/
│       └── messages.json
├── manifest.json
└── popup.html

Set the default locale in your manifest:

{
  "manifest_version": 3,
  "name": "__MSG_extensionName__",
  "description": "__MSG_extensionDescription__",
  "default_locale": "en"
}

Message File Format

Each messages.json follows a structured format:

{
  "extensionName": {
    "message": "Tab Manager Pro",
    "description": "The display name of the extension"
  },
  "extensionDescription": {
    "message": "Organize, search, and manage your browser tabs efficiently",
    "description": "Brief description shown in the Chrome Web Store"
  },
  "greeting": {
    "message": "Welcome, $USER$!",
    "description": "Greeting shown when the popup opens",
    "placeholders": {
      "user": {
        "content": "$1",
        "example": "John"
      }
    }
  },
  "tabCount": {
    "message": "You have $COUNT$ open tabs",
    "description": "Shows the number of open tabs",
    "placeholders": {
      "count": {
        "content": "$1",
        "example": "42"
      }
    }
  }
}

Field Reference

FieldRequiredPurpose
messageYesThe translated string
descriptionNoContext for translators
placeholdersNoNamed variable substitutions
placeholders.contentYes (if placeholder used)The replacement value or $1, $2 reference
placeholders.exampleNoExample value for translators

Using Messages in Your Extension

In JavaScript

// Simple message
const name = chrome.i18n.getMessage('extensionName');

// With substitutions
const greeting = chrome.i18n.getMessage('greeting', ['John']);
const tabInfo = chrome.i18n.getMessage('tabCount', ['42']);

In HTML (via CSS)

<p class="greeting" data-i18n="greeting"></p>
// Auto-translate all elements with data-i18n attribute
document.querySelectorAll('[data-i18n]').forEach((el) => {
  const key = el.getAttribute('data-i18n');
  el.textContent = chrome.i18n.getMessage(key);
});

In manifest.json

Use the __MSG_messageName__ pattern:

{
  "name": "__MSG_extensionName__",
  "description": "__MSG_extensionDescription__",
  "action": {
    "default_title": "__MSG_browserAction__"
  }
}

In CSS

body {
  direction: __MSG_@@bidi_dir__;
  font-family: __MSG_@@font_family__;
}

Predefined Messages

Chrome provides several built-in messages you can use without defining them:

MessageExample ValuePurpose
@@extension_idabcdefghij...Extension’s unique ID
@@ui_localeen_USCurrent UI locale
@@bidi_dirltr or rtlText direction
@@bidi_reversed_dirrtl or ltrOpposite direction
@@bidi_start_edgeleft or rightStarting edge
@@bidi_end_edgeright or leftEnding edge
@@font_family'Segoe UI', ...Recommended font

Advanced Placeholder Patterns

Multiple Placeholders

{
  "downloadProgress": {
    "message": "Downloading $FILENAME$ ($CURRENT$ of $TOTAL$)",
    "placeholders": {
      "filename": { "content": "$1", "example": "report.pdf" },
      "current": { "content": "$2", "example": "3" },
      "total": { "content": "$3", "example": "10" }
    }
  }
}

Static Content Placeholders

For non-translatable content like brand names or HTML:

{
  "welcomeMessage": {
    "message": "Welcome to $APP_NAME$",
    "placeholders": {
      "app_name": {
        "content": "ExtensionBooster",
        "example": "ExtensionBooster"
      }
    }
  }
}

Escaping Dollar Signs

Use $$ for literal dollar signs:

{
  "priceDisplay": {
    "message": "Price: $$$AMOUNT$",
    "placeholders": {
      "amount": { "content": "$1", "example": "9.99" }
    }
  }
}

Result: Price: $9.99


RTL (Right-to-Left) Support

For Arabic, Hebrew, and other RTL languages:

body {
  direction: __MSG_@@bidi_dir__;
}

.sidebar {
  float: __MSG_@@bidi_start_edge__;
}

.content {
  margin-__MSG_@@bidi_start_edge__: 250px;
}
// Detect RTL in JavaScript
const isRtl = chrome.i18n.getMessage('@@bidi_dir') === 'rtl';
if (isRtl) {
  document.documentElement.setAttribute('dir', 'rtl');
}

Localization Best Practices

  1. Write descriptions for every message — Translators need context to produce accurate translations.
  2. Use named placeholders$USER_NAME$ is clearer than $1 for translators.
  3. Avoid concatenating translated strings — Word order varies across languages. Use placeholders instead.
  4. Test with long strings — German text averages 30% longer than English. Ensure your UI accommodates expansion.
  5. Include example in placeholders — Shows translators what the final output looks like.
  6. Don’t hardcode strings — Extract every user-facing string, including error messages and tooltips.

Supported Locales

Chrome supports 50+ locales. The most impactful ones to translate first:

LocaleLanguage% Chrome Users
enEnglish~25%
zh_CNChinese (Simplified)~15%
esSpanish~8%
pt_BRPortuguese (Brazil)~5%
jaJapanese~4%
deGerman~4%
frFrench~3%
koKorean~3%

What’s Next

Localization multiplies your extension’s reach with relatively low effort. Start with your top 5 languages, use descriptive placeholders, and test with RTL layouts.

Want to see how your localized extension performs across different markets? ExtensionBooster provides analytics and market insights to help you grow globally.

Share this article

Build better extensions with free tools

Icon generator, MV3 converter, review exporter, and more — no signup needed.

Related Articles