Frappe_Claude_Skill_Package frappe-core-translation

install
source · Clone the upstream repo
git clone https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/OpenAEC-Foundation/Frappe_Claude_Skill_Package "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/source/core/frappe-core-translation" ~/.claude/skills/openaec-foundation-frappe-claude-skill-package-frappe-core-translation && rm -rf "$T"
manifest: skills/source/core/frappe-core-translation/SKILL.md
source content

Frappe Translation / i18n

Deterministic patterns for translating Frappe apps across v14, v15, and v16.


Quick Reference

TaskPythonJavaScript
Translate string
_("Hello")
__("Hello")
With substitution
_("Hello {0}").format(name)
__("Hello {0}", [name])
With context
_("Change", context="Coins")
__("Change", null, "Coins")
Lazy (module-level)
_lt("Pending")
[v15+]
N/A
Check RTL
frappe.utils.is_rtl()
frappe.utils.is_rtl()

Decision Tree

Need to translate a string?
├── In Python (.py)?
│   ├── Inside a function/method → _("text {0}").format(val)
│   ├── Module-level constant [v15+] → _lt("text")
│   └── Module-level constant [v14] → define inside function or use lazy
├── In JavaScript (.js)?
│   └── ALWAYS → __("text {0}", [val])
├── In Jinja template (.html)?
│   └── {{ _("text") }}
├── In Vue (.vue)?
│   └── __("text") in <script>, {{ __("text") }} in <template>
└── DocType label/description/option?
    └── Auto-extracted — no _() needed

Where do translations live?
├── v14 → apps/{app}/{app}/translations/{lang}.csv
├── v15+ → apps/{app}/{app}/locale/{lang}/LC_MESSAGES/{app}.po
└── User overrides → Translation DocType (highest priority)

Need to extract untranslated strings?
├── v14 → bench --site {site} get-untranslated {lang} {output}
└── v15+ → bench generate-pot-file --app {app}

Translation Priority (Highest First)

PrioritySourceScope
1Translation DocType (user overrides)Per-site
2MO files (
locale/{lang}/.../{app}.mo
)
Per-app [v15+]
3CSV files (
translations/{lang}.csv
)
Per-app
4Parent language (e.g.,
pt
for
pt-BR
)
Fallback

Version Differences

Featurev14v15v16
_()
/
__()
YesYesYes
_lt()
lazy translation
NoYesYes
CSV translationsYesYes (legacy)Yes (legacy)
PO/MO (gettext)NoYesYes
bench generate-pot-file
NoYesYes
Babel JS extractorNoYesYes
Type hints on
_()
NoNoYes

Auto-Extracted Strings (No _() Needed)

These are extracted automatically by the framework:

  • DocType labels and descriptions
  • Select field options (each option line)
  • Workflow states and actions
  • Print Format labels
  • Report column labels
  • Notification subjects (not body)
  • Dashboard chart labels

String Extraction Rules

File TypeExtractorWhat It Finds
.py
Babel (AST)
_("...")
,
_lt("...")
calls
.js
Babel tokenizer [v15+] / regex [v14]
__("...")
calls
.html
Regex
{{ _("...") }}
in Jinja
.vue
Same as JS
__("...")
in script/template
.json
DocType parserLabels, descriptions, options

CRITICAL: Extractors work on the AST/tokens. They CANNOT extract dynamically constructed strings. See Anti-Patterns.


Anti-Patterns (NEVER Do These)

PatternWhy It BreaksCorrect Form
_(f"Hello {name}")
f-string not extractable
_("Hello {0}").format(name)
_("Hello " + name)
Concatenation fragments
_("Hello {0}").format(name)
_("Welcome %s") % name
Old-style not extractable
_("Welcome {0}").format(name)
__(`Hello ${name}`)
Template literal not extractable
__("Hello {0}", [name])
_(" Hello ")
Leading/trailing spaces trimmed
_("Hello")
_("item" if x else "items")
Ternary inside _()
_("item") if x else _("items")
_(variable)
Variable not extractable
_("Known String")

Full anti-pattern catalog with code examples: references/anti-patterns.md


CSV Translation File Format

Location:

apps/{app}/{app}/translations/{lang}.csv

"source","translation","context"
"Hello","Hallo",""
"Change","Wisselgeld","Coins"
"Change","Wijziging","Amendment"
  • ALWAYS use UTF-8 encoding (no BOM)
  • ALWAYS quote all fields with double quotes
  • Context column is optional but MUST be present (empty string if unused)
  • No hooks registration needed — auto-discovered from
    translations/
    directory

PO/MO Files [v15+]

Location:

apps/{app}/{app}/locale/{lang}/LC_MESSAGES/{app}.po

# Generate POT template
bench generate-pot-file --app {app}

# Migrate existing CSV to PO
bench migrate-csv-to-po --app {app}

# Compile PO to MO (required for runtime)
bench compile-po-to-mo --app {app}

PO files follow standard GNU gettext format. Use any PO editor (Poedit, Weblate, Transifex).


Bench Commands

CommandVersionPurpose
bench --site {site} get-untranslated {lang} {output.csv}
AllExport untranslated strings
bench update-translations {lang} {untranslated.csv} {translated.csv}
AllImport translations
bench generate-pot-file --app {app}
v15+Generate .pot template
bench migrate-csv-to-po --app {app}
v15+Convert CSV to PO format
bench compile-po-to-mo --app {app}
v15+Compile PO to binary MO

RTL Support

Hardcoded RTL languages:

ar
(Arabic),
he
(Hebrew),
fa
(Persian/Farsi),
ps
(Pashto)

# Python
if frappe.utils.is_rtl():
    # Apply RTL-specific logic
// JavaScript
if (frappe.utils.is_rtl()) {
    // Apply RTL-specific logic
}
  • Frappe auto-applies
    dir="rtl"
    to the
    <html>
    element
  • ALWAYS use logical CSS properties (
    margin-inline-start
    not
    margin-left
    ) for RTL compatibility
  • Bootstrap RTL stylesheet is auto-loaded when RTL language is active

Custom App Translation Workflow

Adding translations to your custom app:

  1. Write translatable strings using
    _()
    /
    __()
    with positional placeholders
  2. Extract untranslated strings:
    • v14:
      bench --site {site} get-untranslated {lang} untranslated.csv
    • v15+:
      bench generate-pot-file --app {app}
  3. Translate the extracted strings (manually or via PO editor)
  4. Place translations:
    • CSV:
      apps/{app}/{app}/translations/{lang}.csv
    • PO:
      apps/{app}/{app}/locale/{lang}/LC_MESSAGES/{app}.po
  5. Compile (v15+ PO only):
    bench compile-po-to-mo --app {app}
  6. Clear cache:
    bench --site {site} clear-cache

Reference Files

FileContents
references/api-reference.mdFull Python _() and JS __() API with all signatures and edge cases
references/csv-and-bench.mdCSV format spec, bench commands, PO/MO workflow, custom app setup
references/anti-patterns.mdComplete anti-pattern catalog with failing and corrected examples