feat(i18n): English source-string overrides + clear translation cache

Add locale/translations_en/{frappe,erpnext}/en.po so we can rewrite English
source strings we are not happy with. English is not special-cased anywhere in
Frappe's runtime, so setup_locale() picks these up with no changes: it derives
the locale from the translations_<lang> folder name, copies each en.po into the
target app and compiles it to sites/assets/locale/en/LC_MESSAGES/<app>.mo.
Both files ship empty (header only) for now.

Also clear the merged-translation cache once all .mo files are written.
bench migrate clears that cache *before* running after_migrate hooks, and it
refills during migration (patches and syncs call _() under lang "en"), so the
.mo files we write afterwards stayed invisible until someone ran
`bench clear-cache` by hand. This affected az/ru too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali 2026-07-13 10:54:44 +00:00
parent e2f48bfd36
commit dee31d739b
3 changed files with 71 additions and 0 deletions

View File

@ -59,12 +59,31 @@ def setup_locale():
# would otherwise keep serving a stale .mo.
_compile_mo(app_name, lang)
_clear_translation_cache()
print(
f"[az_locale] Locale setup complete: {copied} file(s) copied, "
f"{merged} extras merged, {skipped} skipped"
)
def _clear_translation_cache():
"""Drop the cached merged-translation dict so the fresh .mo files are picked up.
`bench migrate` clears this cache *before* running after_migrate hooks, and the
cache refills during migration (patches and syncs call _() under lang "en").
Since we write the .mo files afterwards, they would stay invisible until someone
ran `bench clear-cache` by hand.
"""
try:
import frappe.translate
frappe.translate.clear_cache()
print("[az_locale] Cleared translation cache")
except Exception as e:
print(f"[az_locale] Warning: could not clear translation cache ({e})")
def _compile_mo(app_name, lang):
"""Compile the app's <lang>.po into the runtime .mo under sites/assets/locale/.

View File

@ -0,0 +1,26 @@
#
# English overrides for the "erpnext" app.
#
# This file is NOT a translation — it rewrites English source strings we are not
# happy with. It is copied to apps/erpnext/erpnext/locale/en.po by setup_locale()
# and compiled into sites/assets/locale/en/LC_MESSAGES/erpnext.mo on migrate.
#
# Rules:
# - msgid must match the source string byte for byte (case, punctuation,
# %s placeholders). Look them up in apps/erpnext/erpnext/locale/main.pot.
# - An empty msgstr means "no override" — the original string is used.
# - An override applies to that string everywhere. To narrow it down, add a
# msgctxt line above the msgid.
# - Keep this file small: the whole English dict ships in every session's boot.
#
# Example:
# msgid "Customer"
# msgstr "Client"
#
msgid ""
msgstr ""
"Project-Id-Version: erpnext\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@ -0,0 +1,26 @@
#
# English overrides for the "frappe" app.
#
# This file is NOT a translation — it rewrites English source strings we are not
# happy with. It is copied to apps/frappe/frappe/locale/en.po by setup_locale()
# and compiled into sites/assets/locale/en/LC_MESSAGES/frappe.mo on migrate.
#
# Rules:
# - msgid must match the source string byte for byte (case, punctuation,
# %s placeholders). Look them up in apps/frappe/frappe/locale/main.pot.
# - An empty msgstr means "no override" — the original string is used.
# - An override applies to that string everywhere. To narrow it down, add a
# msgctxt line above the msgid.
# - Keep this file small: the whole English dict ships in every session's boot.
#
# Example:
# msgid "Submit"
# msgstr "Post"
#
msgid ""
msgstr ""
"Project-Id-Version: frappe\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"