关于 odoo-i18n
odoo-i18n 是面向开发者的技能,用于管理 Odoo 14–19 的翻译与本地化。它提供提取字符串、校验和报告 .po 文件,以及通过 Odoo CLI 导入或导出翻译的工作流。
主要功能
- 01提取可翻译字符串
- 02校验 .po 语法与内容
- 03报告翻译缺漏
- 04合并和转换 .po 文件
- 05通过 Odoo CLI 导入导出
- 06检查语言特定规则
适用场景
- 01为 Odoo 模块生成翻译文件
- 02发布前校验 .po 文件
- 03跟踪翻译覆盖缺口
- 04将翻译载入 Odoo 数据库
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ahmed-lakosha/ odoo-plugins --skill odoo-i18n安装后在 Claude Code 中输入/odoo-i18n确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
scripts/__init__.py_common.pyi18n_converter.pyi18n_extractor.pyi18n_reporter.pyi18n_validator.py详细介绍
Odoo i18n 技能
全面掌握 Odoo 14-19 的国际化(i18n)和本地化(l10n)。
关键翻译规则
- 绝对不要在
_()中使用 f-string——请使用%格式化:_('Record %s') % name - 绝对不要在
_()中拼接字符串——请将完整句子交给译者 - 类级别的字符串(selection 值、类属性)请使用
_lt() - 绝对不要将字段属性中的
string=包装在_()中——Odoo 会自动翻译 - 将
.po文件保存为 UTF-8 编码,不带 BOM - 阿拉伯语
.po文件的 Plural-Forms 标头中必须包含nplurals=6 - 编辑
.po文件后务必更新模块:-u module --stop-after-init
工作流 1:提取可翻译字符串
扫描 Odoo 模块中的所有可翻译字符串,并生成 .pot(模板)和 .po(语言)文件。
用法
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_extractor.py --module <path> --lang <code> [--output <dir>] [--no-pot] [--verbose]
| 参数 | 必填 | 说明 |
|---|---|---|
--module | 是 | Odoo 模块目录的路径 |
--lang | 是 | 目标语言代码(例如,ar、fr、tr) |
--output | 否 | 自定义输出目录(默认:module/i18n/) |
--no-pot | 否 | 跳过生成 .pot 模板 |
--verbose | 否 | 显示所有提取的字符串 |
提取内容
Python (*.py) -- _('...') 和 _lt('...'):
# Extracted:
raise UserError(_('Record %s not found') % name)
state = fields.Selection([('draft', _lt('Draft'))])
# NOT extracted: _(variable), _(f'Hello {name}')
XML (*.xml) -- string=、help=、placeholder= 属性;菜单/操作中的 name=;HTML 文本:
<field name="state" string="Status"/>
<h1>Welcome to our website</h1>
<record id="view_my_form" model="ir.ui.view">
JavaScript (*.js) -- _t('...') 和 _lt('...'):
const msg = _t("Save Changes"); // extracted
const msg = _t(someVariable); // NOT extracted
生成的文件结构
# Translation template for my_module
msgid ""
msgstr ""
"Project-Id-Version: Odoo Module my_module\n"
"Content-Type: text/plain; charset=UTF-8\n"
#: models/my_model.py:45
#, python-format
msgid "Record %s not found"
msgstr ""
替代方案:Odoo 内置提取器
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-export --modules=my_module --language=ar \
--output=my_module/i18n/ar.po --stop-after-init
Odoo CLI 提取器包含数据库字符串(模型名称、操作名称),而插件提取器不会提取这些内容。对于生产环境,内置提取器更为完整。
提取后
- 在翻译编辑器(Poedit、Virtaal 或文本编辑器)中打开
.po文件 - 填写所有
msgstr条目 - 验证:运行
i18n_validator.py --po-file path/to/ar.po - 检查覆盖率:运行
i18n_reporter.py --module path/ --lang ar - 加载到 Odoo 中:更新模块或使用导出/导入工作流
工作流 2:验证 .po 文件
验证 .po 文件中是否存在语法错误、编码问题、空翻译、模糊条目、格式说明符不匹配,以及阿拉伯语/RTL 特定问题。
用法
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_validator.py --po-file <path> [--lang <code>] [--strict] [--output <file>]
| 参数 | 必填 | 说明 |
|---|---|---|
--po-file | 是 | 要验证的 .po 文件路径 |
--lang | 否 | 用于语言特定检查的语言代码(从文件名自动检测) |
--strict | 否 | 将未翻译字符串视为错误,而非警告 |
--output | 否 | 将报告写入文件,而非标准输出 |
验证内容
语法: UTF-8 编码(无 BOM/Latin-1)、包含 Content-Type/charset/Language/MIME-Version 的标头、字符串引号格式正确、无解析错误。
翻译: 空 msgstr(未翻译)、需要审核的 fuzzy 标记、重复的 msgid、过时的(#~)条目。
格式说明符: %s/%d/%f 的数量必须匹配;必须包含所有 %(name)s 命名说明符:
# WRONG - missing second %s:
msgid "Invoice %s due on %s"
msgstr "fatura %s"
# CORRECT:
msgid "Invoice %s due on %s"
msgstr "fatura %s vadesi %s"
阿拉伯语特定检查(--lang ar): 是否包含阿拉伯字符、是否存在 Latin-1 导致的编码异常、方向控制字符、是否要求 nplurals=6、标记 BIDI 覆盖字符。
常见错误及修复方法
“Charset must be UTF-8”:
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_converter.py --action convert --po ar.po --output ar_fixed.po
“Fuzzy translation (needs review)” -- 确认后移除 fuzzy 标记:
# Before: # After:
#, fuzzy, python-format #, python-format
msgid "Record %s not found" msgid "Record %s not found"
msgstr "record not found" msgstr "record not found"
“Arabic should have nplurals=6”:
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
CI/CD 集成
- name: Validate Arabic translations
run: python odoo-i18n/scripts/i18n_validator.py --po-file my_module/i18n/ar.po --lang ar --strict
退出代码 0 = 通过(可能有警告),1 = 失败(发现错误)。
工作流 3:查找缺失的翻译
将模块源文件中的可翻译字符串与现有 .po 文件进行比较,并报告缺失或不完整的内容。
用法
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_reporter.py --module <path> --lang <code> [--format text|json|csv] [--output <file>] [--min-pct <N>]
| 参数 | 必需 | 描述 |
|---|---|---|
--module | 是 | Odoo 模块目录的路径 |
--lang | 是 | 要检查的语言代码(例如:ar、fr) |
--format | 否 | 输出格式:text(默认)、json、csv |
--output | 否 | 将报告写入文件,而不是写入标准输出 |
--min-pct | 否 | 如果完成率低于阈值,则退出代码为 1 |
了解报告
- 缺失:源代码中有此字符串,但
.po中没有条目 - .po 中为空:条目存在,但
msgstr为""(尚未翻译) - 模糊:自动匹配,需要人工审核;不会向用户显示(Odoo 会回退到源文本)
- 完成率 % = 已翻译 / 活跃字符串总数 * 100(非空、非模糊、非过期)
工作流:修复缺失的翻译
- 运行
i18n_reporter.py,找出缺漏 - 使用 Poedit、Virtaal 或文本编辑器打开
.po - 填写缺失的
msgstr条目 - 如果字符串在
.po中完全不存在,先运行i18n_extractor.py - 使用
i18n_validator.py验证 - 重新运行
i18n_reporter.py,确认覆盖情况
源代码变更后更新 .po
# 1. Re-extract new strings
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_extractor.py --module /path/ --lang ar --no-pot
# 2. Merge (preserves existing translations)
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_converter.py \
--action merge --base /path/i18n/ar.po --new /path/i18n/ar.po --output /path/i18n/ar_merged.po
# 3. Check remaining gaps
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_reporter.py --module /path/ --lang ar
工作流 4:通过 Odoo CLI 导出/导入
需要 PostgreSQL 和有效的数据库。
导出翻译
# Single module
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-export --modules=my_module --language=ar \
--output=my_module/i18n/ar.po --stop-after-init
# Export .pot template (no --language = empty msgstr)
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-export --modules=my_module \
--output=my_module/i18n/my_module.pot --stop-after-init
# Multiple modules / all installed
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-export --modules=mod1,mod2 --language=ar --output=combined_ar.po --stop-after-init
导入翻译
# Import .po
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-import=my_module/i18n/ar.po --language=ar --modules=my_module --stop-after-init
# Import and overwrite existing
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-import=my_module/i18n/ar.po --language=ar \
--modules=my_module --i18n-overwrite --stop-after-init
# Multiple languages
for lang in ar fr tr; do
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-import=my_module/i18n/${lang}.po --language=${lang} \
--modules=my_module --stop-after-init
done
加载语言
python odoo-bin -c conf/myproject.conf -d mydb --load-language=ar --stop-after-init
python odoo-bin -c conf/myproject.conf -d mydb --load-language=ar,fr,tr --stop-after-init
通过 shell 检查/激活:
# python odoo-bin shell -d mydb
langs = self.env['res.lang'].with_context(active_test=False).search([])
for lang in langs:
print(f"[{'ACTIVE' if lang.active else 'inactive'}] {lang.code}: {lang.name}")
# Activate
lang = self.env['res.lang'].with_context(active_test=False).search([('code', '=', 'ar')])
lang.active = True
self.env.cr.commit()
更新模块(最简单的重新加载方式)
python -m odoo -c conf/myproject.conf -d mydb -u my_module --stop-after-init
Odoo 17+ 特有事项
翻译在内部以 JSON 术语形式存储;导出格式仍为 .po。清除缓存:
# python odoo-bin shell -d mydb
self.env['ir.translation'].clear_caches()
self.env.cr.commit()
网站翻译
python odoo-bin -c conf/myproject.conf -d mydb \
--i18n-export --modules=website,my_theme_module --language=ar \
--output=website_ar.po --stop-after-init
翻译文件位置
module/i18n/
module.pot <- Template (not loaded by Odoo)
ar.po <- Arabic
ar_SA.po <- Saudi variant (overrides ar.po entries)
fr.po <- French
tr.po <- Turkish
工具:合并和清理 .po 文件
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_converter.py --action merge --base ar.po --new ar_new.po --output ar_merged.po
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_converter.py --action clean --po ar.po
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_converter.py --action stats --po ar.po
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_converter.py --action convert --po ar.po --output ar_fixed.po
支持的语言速查表
| 代码 | 语言 | 是否为 RTL? | 复数形式 |
|---|---|---|---|
ar / ar_SA / ar_AE | 阿拉伯语(变体) | 是 | 6 |
en / en_US | 英语 | 否 | 2 |
fr / fr_FR | 法语 | 否 | 2 |
tr | 土耳其语 | 否 | 2 |
de | 德语 | 否 | 2 |
es | 西班牙语 | 否 | 2 |
Odoo 版本差异
| 功能 | Odoo 14-15 | Odoo 16-17 | Odoo 18-19 |
|---|---|---|---|
| JS 翻译导入 | import { _t } from "web.core" | import { _t } from "@web/core/l10n/translation" | 与 16 相同 |
| Bootstrap 版本 | 4.x | 5.1.3(内置 RTL) | 5.1.3+ |
| 翻译存储 | ir.translation 表 | JSON 术语(内部) | JSON 术语 |
| 列表视图类型 | <tree> | <tree> | <list>(仅限 19) |
| 控制器类型 | type='json' | type='json' | type='jsonrpc'(仅限 19) |
| 可见性属性 | attrs={'invisible': ...} | attrs={'invisible': ...} | 内联 invisible="expr"(仅限 19) |
常见问题
翻译未显示:使用 -u module --stop-after-init 更新模块,然后清除浏览器缓存。
阿拉伯语文本乱码(mojibake):文件以 Latin-1 格式保存。运行 i18n_converter.py --action convert 修复编码。
格式说明符错误:确保 msgid 和 msgstr 中的 %s/%d/%(name)s 数量一致。
阿拉伯语用户在网站上看到英语:检查 (1) 是否已安装阿拉伯语,(2) 是否已设置用户语言,(3) 添加翻译后是否已更新模块。
模糊条目:打开 .po,核对翻译,并从已批准的条目中移除 #, fuzzy 标记。
可配置的品牌信息
生成的 .pot/.po 文件使用可配置的作者/邮箱。设置环境变量:
export ODOO_I18N_COPYRIGHT="Your Company"
export ODOO_I18N_BUGS_EMAIL="[email protected]"
详细参考(记忆文件)
如需了解本技能摘要之外的完整模式,插件还包含以下记忆文件:
memories/translation_patterns.md—— Python_()/_lt()模式、XML 属性翻译、JS_t()模式、.po 文件结构、电子邮件模板翻译memories/rtl_patterns.md—— RTL 激活、Bootstrap RTL、CSS 逻辑属性、SCSS 覆盖、RTL 中的 Flexbox、JS RTL 检测、QWeb 报告 RTL、常见 RTL 修复memories/language_codes.md—— 语言代码、日期/时间格式、数字格式、货币显示、伊斯兰历说明、时区参考