Install
$ agentstack add skill-ahmed-lakosha-odoo-plugins-odoo-i18n ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Odoo i18n Skill
Provides deep expertise in Odoo internationalization (i18n) and localization (l10n) across Odoo 14-19.
Critical Translation Rules
- NEVER use f-strings inside
_()-- use%formatting:_('Record %s') % name - NEVER concatenate strings inside
_()-- give the translator the full sentence - Use
_lt()for class-level strings (selection values, class attributes) - NEVER wrap
string=field attributes in_()-- they are auto-translated by Odoo - Save
.pofiles as UTF-8 without BOM - Arabic
.pofiles must havenplurals=6in Plural-Forms header - Always update the module after editing
.pofiles:-u module --stop-after-init
Workflow 1: Extract Translatable Strings
Scans an Odoo module for all translatable strings and generates .pot (template) and .po (language) files.
Usage
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_extractor.py --module --lang [--output ] [--no-pot] [--verbose]
| Argument | Required | Description | |----------|----------|-------------| | --module | Yes | Path to the Odoo module directory | | --lang | Yes | Target language code (e.g., ar, fr, tr) | | --output | No | Custom output directory (default: module/i18n/) | | --no-pot | No | Skip generating .pot template | | --verbose | No | Show all extracted strings |
What Gets Extracted
Python (*.py) -- _('...') and _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= attributes; name= on menus/actions; HTML text:
Welcome to our website
JavaScript (*.js) -- _t('...') and _lt('...'):
const msg = _t("Save Changes"); // extracted
const msg = _t(someVariable); // NOT extracted
Generated File Structure
# 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 ""
Alternative: Odoo's Built-in Extractor
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
The Odoo CLI extractor includes database strings (model names, action names) that the plugin extractor does not. For production, the built-in extractor is more complete.
After Extraction
- Open the
.pofile in a translation editor (Poedit, Virtaal, or text editor) - Fill in all
msgstrentries - Validate: run
i18n_validator.py --po-file path/to/ar.po - Check coverage: run
i18n_reporter.py --module path/ --lang ar - Load into Odoo: update module or use export/import workflow
Workflow 2: Validate .po Files
Validates a .po file for syntax errors, encoding issues, empty translations, fuzzy entries, format specifier mismatches, and Arabic/RTL-specific problems.
Usage
python ${CLAUDE_PLUGIN_ROOT}/odoo-i18n/scripts/i18n_validator.py --po-file [--lang ] [--strict] [--output ]
| Argument | Required | Description | |----------|----------|-------------| | --po-file | Yes | Path to the .po file to validate | | --lang | No | Language code for language-specific checks (auto-detected from filename) | | --strict | No | Treat untranslated strings as errors instead of warnings | | --output | No | Write report to a file instead of stdout |
What is Validated
Syntax: UTF-8 encoding (no BOM/Latin-1), header with Content-Type/charset/Language/MIME-Version, properly quoted strings, no parse errors.
Translations: Empty msgstr (untranslated), fuzzy flags needing review, duplicate msgid, obsolete (#~) entries.
Format Specifiers: %s/%d/%f count must match; %(name)s named specifiers must all appear:
# 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"
Arabic-Specific (--lang ar): Arabic characters present, encoding artifacts from Latin-1, direction control chars, nplurals=6 required, BIDI overrides flagged.
Common Errors and Fixes
"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)" -- remove the fuzzy flag after verifying:
# 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=11 && n%100 --lang [--format text|json|csv] [--output ] [--min-pct ]
| Argument | Required | Description | |----------|----------|-------------| | --module | Yes | Path to the Odoo module directory | | --lang | Yes | Language code to check (e.g., ar, fr) | | --format | No | Output format: text (default), json, csv | | --output | No | Write report to a file instead of stdout | | --min-pct | No | Exit code 1 if completion below threshold |
Understanding the Report
- Missing: String in source code but NO entry in
.po - Empty in .po: Entry exists but
msgstris""(not yet translated) - Fuzzy: Auto-matched, needs human review; NOT shown to users (Odoo falls back to source)
- Completion % = Translated / Total Active Strings * 100 (non-empty, non-fuzzy, non-obsolete)
Workflow: Fixing Missing Translations
- Run
i18n_reporter.pyto identify gaps - Open
.poin Poedit, Virtaal, or text editor - Fill in missing
msgstrentries - If strings are entirely absent from
.po, runi18n_extractor.pyfirst - Validate with
i18n_validator.py - Re-run
i18n_reporter.pyto confirm coverage
Updating .po After Source Changes
# 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
Workflow 4: Export/Import via Odoo CLI
Requires PostgreSQL and a valid database.
Exporting Translations
# 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
Importing Translations
# 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
Loading Languages
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
Check/activate via 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()
Module Update (Simplest Reload)
python -m odoo -c conf/myproject.conf -d mydb -u my_module --stop-after-init
Odoo 17+ Specifics
Translations stored as JSON terms internally; export format still .po. Clear cache:
# python odoo-bin shell -d mydb
self.env['ir.translation'].clear_caches()
self.env.cr.commit()
Website Translations
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
Translation File Locations
module/i18n/
module.pot ` | `` | `` (19 only) |
| Controller type | `type='json'` | `type='json'` | `type='jsonrpc'` (19 only) |
| Visibility attrs | `attrs={'invisible': ...}` | `attrs={'invisible': ...}` | Inline `invisible="expr"` (19 only) |
---
## Common Issues
**Translations not showing**: Update the module with `-u module --stop-after-init`, then clear browser cache.
**Arabic text garbled (mojibake)**: File saved as Latin-1. Run `i18n_converter.py --action convert` to fix encoding.
**Format specifier error**: Ensure `%s`/`%d`/`%(name)s` count matches between msgid and msgstr.
**Website shows English for Arabic users**: Check (1) Arabic language installed, (2) user language set, (3) module updated after adding translations.
**Fuzzy entries**: Open .po, verify translation, remove `#, fuzzy` flag from approved entries.
---
## Configurable Branding
Generated .pot/.po files use configurable author/email. Set environment variables:
```bash
export ODOO_I18N_COPYRIGHT="Your Company"
export ODOO_I18N_BUGS_EMAIL="you@example.com"
Detailed Reference (Memory Files)
For comprehensive patterns beyond this skill summary, the plugin includes memory files:
memories/translation_patterns.md-- Python_()/_lt()patterns, XML attribute translation, JS_t()patterns, .po file structure, email template translationmemories/rtl_patterns.md-- RTL activation, Bootstrap RTL, CSS logical properties, SCSS overrides, Flexbox in RTL, JS RTL detection, QWeb report RTL, common RTL fixesmemories/language_codes.md-- Language codes, date/time formats, number formats, currency display, Hijri calendar notes, timezone reference
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ahmed-lakosha
- Source: ahmed-lakosha/odoo-plugins
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.