Translations
Frappe includes a built-in translation system that makes applications
multilingual. It automatically extracts translatable text from your
application, stores translations for different languages, and displays
the appropriate translation based on the user’s selected language.
inside the
_() function in Python or __() inJavaScript.
How the Translation System Works
The translation workflow consists of three stages: extracting
translatable strings, translating them into different languages, and
displaying the translated text within the application.
Step 1: Extracting Translatable Strings
Many strings, such as DocType field labels and descriptions, are
automatically detected from JSON files. Strings written directly in
Python or JavaScript files must be explicitly marked for translation.
Example in Python:
message = _("You don't have permissions to access this file")
During extraction, Frappe scans supported files such as
.py, .js, and .json and collects
every string wrapped in translation functions.
Step 2: Translating Extracted Strings
After extraction, all strings are exported into language-specific CSV
files. Community contributors translate these strings through the
Frappe translation portal, and verified translations are merged into
the framework.
Community Contribution:
Translation updates are regularly reviewed and merged into the Frappe
codebase, making improvements available to all users.
Step 3: Displaying Translated Strings
Translations are stored as key-value pairs where the original text acts
as the key and the translated version becomes the value.
{
"About": "के बारे में",
"Academic Year": "शैक्षणिक वर्ष",
"1 week": "1 सप्ताह"
}
When the application encounters a translated string wrapped in
__() or _(), it searches the translation
dictionary and returns the appropriate language if available.
Best Practices for Writing Translatable Strings
Following these guidelines ensures the translation parser correctly
identifies every string and produces accurate translations.
1. Always Use Literal Strings
Only literal strings can be extracted. Variables or expressions cannot
be parsed by the translation engine.
Correct
message = _("Document submitted successfully")
frappe.msgprint(message)
Incorrect
message = "Document submitted successfully"
frappe.msgprint(_(message))
2. Use Positional Placeholders for Variables
Variables should always be inserted using positional placeholders such
as {0}. Avoid formatting the string before passing it to
the translation function.
Python
_("Welcome {0}, get started with ERPNext.")
.format(full_name)
JavaScript
__("Welcome {0}, get started with ERPNext.",
[full_name])
3. Don’t Split Strings
Keep complete sentences together. Avoid concatenating translated
strings or writing multiline text.
Correct
_("You have {0} subscribers.")
.format(count)
Incorrect
_("You have ")
+ count +
_(" subscribers.")
4. Handle Singular and Plural Separately
Languages have different pluralization rules, so singular and plural
messages should be translated independently.
if invoice_count > 1:
msg = _("You have {0} pending invoices").format(invoice_count)
else:
msg = _("You have {0} pending invoice").format(invoice_count)
5. Avoid Leading or Trailing Spaces
Spaces surrounding translated strings may be removed during the
translation process. Add spaces outside the translation function if
needed.
Correct
msg = " " + _("You have pending invoices") + " "
6. Provide Context When Needed
Some words have multiple meanings depending on where they are used.
Context helps translators provide accurate translations.
JavaScript
__("Change", null, "Coins")
Python
_("Change", context="Switch")
Adding a New Language
Frappe allows developers to add support for additional languages by
exporting untranslated strings, translating them, and importing the
translated files back into the application.
Step 1: Export Untranslated Strings
bench --site [sitename] get-untranslated [language] [output-file]
This command exports all untranslated strings into a file that can be
translated.
Step 2: Translate the File
Translate every string while preserving the original order of entries.
The translated file should match the exported file line by line.
Step 3: Import the Translations
bench update-translations
[language]
[source-file]
[translated-file]
Bench generates a new [language].csv file inside the
translations directory of each application.
Step 4: Register the Language
Add the new language entry to the
frappe/geo/languages.json file so it becomes available in
the application.
Step 5: Commit Your Changes
Commit the generated translation CSV files together with the updated
language configuration and push the changes to your repository.
Best Practice:
Test your application after importing translations to verify that all
strings appear correctly and that placeholders remain intact.