Chrome 扩展开发完全指南 / 第 14 章:国际化(i18n)
第 14 章:国际化(i18n)
国际化(Internationalization,简称 i18n)让扩展能够支持多种语言和地区。通过 Chrome 的 i18n API,你可以轻松实现界面多语言切换,让全球用户都能舒适地使用你的扩展。
14.1 i18n 基础
14.1.1 manifest.json 配置
{
"name": "__MSG_appName__",
"description": "__MSG_appDescription__",
"default_locale": "en"
}
__MSG_keyName__:引用_locales中的消息键default_locale:默认语言代码,当用户语言不可用时回退
14.1.2 目录结构
_locales/
├── en/ # 英语(默认)
│ └── messages.json
├── zh_CN/ # 简体中文
│ └── messages.json
├── zh_TW/ # 繁体中文
│ └── messages.json
├── ja/ # 日语
│ └── messages.json
└── ko/ # 韩语
└── messages.json
⚠️ 注意:目录名使用下划线(
zh_CN),而非连字符(zh-CN)。
14.2 消息文件
14.2.1 消息格式
{
"appName": {
"message": "My Extension",
"description": "The display name of the extension"
},
"appDescription": {
"message": "A powerful browser extension for productivity",
"description": "The description of the extension in the Web Store"
},
"welcomeMessage": {
"message": "Hello, $USERNAME$! Welcome back.",
"description": "Greeting message with user name",
"placeholders": {
"username": {
"content": "$1",
"example": "John"
}
}
},
"itemCount": {
"message": "You have $COUNT$ items",
"description": "Shows the number of items",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"openSettings": {
"message": "Open Settings",
"description": "Button text to open settings page"
}
}
消息字段说明
| 字段 | 必需 | 说明 |
|---|---|---|
message | ✅ | 消息文本 |
description | ❌ | 对翻译者的说明 |
placeholders | ❌ | 占位符定义 |
14.2.2 中文消息文件
{
"appName": {
"message": "我的扩展",
"description": "扩展的显示名称"
},
"appDescription": {
"message": "一款强大的浏览器效率工具",
"description": "Chrome Web Store 中的扩展描述"
},
"welcomeMessage": {
"message": "你好,$USERNAME$!欢迎回来。",
"description": "带用户名的问候语",
"placeholders": {
"username": {
"content": "$1",
"example": "小明"
}
}
},
"itemCount": {
"message": "您有 $COUNT$ 个项目",
"description": "显示项目数量",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"openSettings": {
"message": "打开设置",
"description": "打开设置页面的按钮文本"
}
}
14.3 使用 i18n API
14.3.1 JavaScript 中获取消息
// 获取消息
const appName = chrome.i18n.getMessage('appName');
console.log(appName); // "我的扩展" 或 "My Extension"
// 带占位符的消息
const welcome = chrome.i18n.getMessage('welcomeMessage', ['小明']);
console.log(welcome); // "你好,小明!欢迎回来。"
// 多个占位符
const message = chrome.i18n.getMessage('itemCount', ['5']);
console.log(message); // "您有 5 个项目"
// 获取当前语言
const lang = chrome.i18n.getUILanguage();
console.log(lang); // "zh-CN"
14.3.2 HTML 中使用
<!-- 在 manifest.json 中使用 -->
<!-- "name": "__MSG_appName__" -->
<!-- 在 HTML 中使用(需手动替换或使用 JS) -->
<h1 data-i18n="appName"></h1>
<p data-i18n="appDescription"></p>
<button data-i18n="openSettings"></button>
<script>
// 自动翻译页面中所有带 data-i18n 属性的元素
document.querySelectorAll('[data-i18n]').forEach(element => {
const key = element.getAttribute('data-i18n');
const message = chrome.i18n.getMessage(key);
if (message) {
element.textContent = message;
}
});
// 翻译 input placeholder
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
const key = element.getAttribute('data-i18n-placeholder');
const message = chrome.i18n.getMessage(key);
if (message) {
element.placeholder = message;
}
});
// 翻译 title 属性
document.querySelectorAll('[data-i18n-title]').forEach(element => {
const key = element.getAttribute('data-i18n-title');
const message = chrome.i18n.getMessage(key);
if (message) {
element.title = message;
}
});
</script>
14.4 国际化工具类
// lib/i18n.js
class I18nManager {
constructor() {
this.currentLocale = chrome.i18n.getUILanguage();
this.messages = {};
}
// 获取消息,支持回退
getMessage(key, substitutions = []) {
const message = chrome.i18n.getMessage(key, substitutions);
return message || key; // 回退到键名
}
// 批量获取消息
getMessages(keys) {
const result = {};
for (const key of keys) {
result[key] = this.getMessage(key);
}
return result;
}
// 翻译整个 DOM
translatePage(root = document) {
// 翻译文本内容
root.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
const text = this.getMessage(key);
if (text) el.textContent = text;
});
// 翻译 placeholder
root.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
const key = el.getAttribute('data-i18n-placeholder');
el.placeholder = this.getMessage(key);
});
// 翻译 title
root.querySelectorAll('[data-i18n-title]').forEach(el => {
const key = el.getAttribute('data-i18n-title');
el.title = this.getMessage(key);
});
// 翻译 aria-label
root.querySelectorAll('[data-i18n-aria]').forEach(el => {
const key = el.getAttribute('data-i18n-aria');
el.setAttribute('aria-label', this.getMessage(key));
});
}
// 格式化日期(根据地区)
formatDate(date, options = {}) {
const defaults = {
year: 'numeric',
month: 'long',
day: 'numeric',
...options
};
return new Intl.DateTimeFormat(this.currentLocale, defaults).format(date);
}
// 格式化数字
formatNumber(number, options = {}) {
return new Intl.NumberFormat(this.currentLocale, options).format(number);
}
// 格式化货币
formatCurrency(amount, currency = 'CNY') {
return new Intl.NumberFormat(this.currentLocale, {
style: 'currency',
currency
}).format(amount);
}
// 相对时间
formatRelativeTime(value, unit) {
try {
return new Intl.RelativeTimeFormat(this.currentLocale, {
numeric: 'auto'
}).format(value, unit);
} catch {
return `${value} ${unit}s ago`;
}
}
}
// 单例导出
const i18n = new I18nManager();
export default i18n;
14.5 支持语言列表
常用语言代码:
| 语言 | 代码 | 目录名 |
|---|---|---|
| 英语 | en | en |
| 简体中文 | zh-CN | zh_CN |
| 繁体中文 | zh-TW | zh_TW |
| 日语 | ja | ja |
| 韩语 | ko | ko |
| 法语 | fr | fr |
| 德语 | de | de |
| 西班牙语 | es | es |
| 葡萄牙语 | pt | pt_BR / pt_PT |
| 俄语 | ru | ru |
| 阿拉伯语 | ar | ar |
14.6 业务场景
场景一:自动语言检测与提示
async function suggestLanguage() {
const userLang = chrome.i18n.getUILanguage();
const { preferredLang } = await chrome.storage.sync.get('preferredLang');
if (!preferredLang && !userLang.startsWith('zh')) {
// 用户使用非中文系统,但仍可提示
const available = ['en', 'zh_CN', 'ja', 'ko'];
const bestMatch = available.find(l => userLang.startsWith(l.split('_')[0]));
if (bestMatch && bestMatch !== 'en') {
chrome.notifications.create('lang-suggest', {
type: 'basic',
iconUrl: 'icons/icon-128.png',
title: 'Language Available',
message: `This extension is available in your language. Check Settings.`
});
}
}
}
场景二:右键菜单国际化
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'searchWeb',
title: chrome.i18n.getMessage('contextMenuSearch'),
contexts: ['selection']
});
chrome.contextMenus.create({
id: 'saveToCollection',
title: chrome.i18n.getMessage('contextMenuSave'),
contexts: ['selection', 'link', 'image']
});
});
14.7 注意事项
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 消息不显示 | 键名拼写错误或文件缺失 | 检查 messages.json |
| 占位符无效 | placeholders 配置错误 | 确认 $1 格式正确 |
| 目录名错误 | 使用了连字符而非下划线 | zh_CN 而非 zh-CN |
| 翻译不更新 | 缓存问题 | 重新加载扩展 |
| manifest 中不生效 | 格式应为 __MSG_key__ | 检查双下划线格式 |