Merge pull request #38102 from ShrihariMahabal/remove-translations-from-boot

perf: remove translations from boot
This commit is contained in:
Shrihari Mahabal 2026-03-27 12:57:28 +05:30 committed by GitHub
commit 359a0ca763
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 53 additions and 6 deletions

View file

@ -342,10 +342,10 @@ def get_user_pages_or_reports(parent, cache=False):
def load_translations(bootinfo):
from frappe.translate import get_messages_for_boot
from frappe.translate import get_translation_version
bootinfo["lang"] = frappe.lang
bootinfo["__messages"] = get_messages_for_boot()
bootinfo["translations_version"] = get_translation_version()
def get_user_info():

View file

@ -5,7 +5,7 @@ import json
import frappe
from frappe.model.document import Document
from frappe.translate import MERGED_TRANSLATION_KEY, USER_TRANSLATION_KEY
from frappe.translate import MERGED_TRANSLATION_KEY, USER_TRANSLATION_KEY, change_translation_version
from frappe.utils import is_html, strip_html_tags
@ -46,3 +46,4 @@ class Translation(Document):
def clear_user_translation_cache(lang):
frappe.cache.hdel(USER_TRANSLATION_KEY, lang)
frappe.cache.hdel(MERGED_TRANSLATION_KEY, lang)
change_translation_version()

View file

@ -30,7 +30,9 @@ frappe.Application = class Application {
this.startup();
}
startup() {
async startup() {
// Wait for translations to be loaded before rendering any UI
if (frappe._translations_loaded) await frappe._translations_loaded;
frappe.realtime.init();
frappe.model.init();

View file

@ -98,6 +98,13 @@
frappe.boot = {{ frappe.utils.orjson_dumps(boot, default=frappe.json_handler) }}
// for backward compatibility of some libs
frappe.sys_defaults = frappe.boot.sysdefaults;
frappe._messages = {};
frappe._translations_loaded = fetch(
`/api/method/frappe.translate.get_boot_translations?v=${frappe.boot.translations_version}&lang=${frappe.boot.lang}`,
{credentials: "same-origin"}
).then(r => r.json()).then(data => {
frappe._messages = data.message || {};
}).catch(() => {});
</script>
{{ include_script('frappe-web.bundle.js') }}
{% endblock %}

View file

@ -19,7 +19,8 @@ from csv import reader, writer
import frappe
from frappe.query_builder import DocType, Field
from frappe.utils import cstr, get_bench_path, is_html, strip, strip_html_tags, unique
from frappe.utils import cstr, get_bench_path, get_build_version, is_html, strip, strip_html_tags, unique
from frappe.utils.caching import http_cache
REPORT_TRANSLATE_PATTERN = re.compile('"([^:,^"]*):')
CSV_STRIP_WHITESPACE_PATTERN = re.compile(r"{\s?([0-9]+)\s?}")
@ -28,6 +29,7 @@ CSV_STRIP_WHITESPACE_PATTERN = re.compile(r"{\s?([0-9]+)\s?}")
# Cache keys
MERGED_TRANSLATION_KEY = "merged_translations"
USER_TRANSLATION_KEY = "lang_user_translations"
TRANSLATION_VERSION_KEY = "translation_version"
def get_language(lang_list: list | None = None) -> str:
@ -132,6 +134,13 @@ def get_messages_for_boot():
return get_all_translations(frappe.local.lang)
@frappe.whitelist(allow_guest=True, methods=["GET"])
@http_cache(max_age=31536000)
def get_boot_translations(lang: str | None = None) -> dict[str, str]:
"""Return all translations for the current user's language."""
return get_all_translations(lang or frappe.local.lang)
def get_all_translations(lang: str) -> dict[str, str]:
"""Load and return the entire translations dictionary for a language from apps + user translations.
@ -241,6 +250,21 @@ def clear_cache():
frappe.cache.delete_value(
keys=["bootinfo", USER_TRANSLATION_KEY, MERGED_TRANSLATION_KEY],
)
change_translation_version()
def get_translation_version() -> str:
"""Return the current translation version from cache."""
version = frappe.cache.get_value(TRANSLATION_VERSION_KEY)
if version is None:
version = frappe.generate_hash(length=8)
frappe.cache.set_value(TRANSLATION_VERSION_KEY, version)
return f"{version}_{get_build_version()}"
def change_translation_version():
"""Generate a new random translation version to invalidate browser caches."""
frappe.cache.set_value(TRANSLATION_VERSION_KEY, frappe.generate_hash(length=8))
def get_messages_for_app(app, deduplicate=True):

View file

@ -52,9 +52,22 @@
if (!window.frappe) window.frappe = {};
frappe.boot = {{ frappe.utils.orjson_dumps(boot, default=frappe.json_handler) }};
frappe._messages = frappe.boot["__messages"];
frappe._messages = {};
frappe.csrf_token = "{{ csrf_token }}";
frappe._translations_loaded = fetch(
`/api/method/frappe.translate.get_boot_translations?v=${frappe.boot.translations_version}&lang=${frappe.boot.lang}`,
{
credentials: "same-origin",
headers: {
"X-Frappe-CSRF-Token": frappe.csrf_token,
"Accept": "application/json"
}
}
).then(r => r.json()).then(data => {
frappe._messages = data.message || {};
}).catch(() => {});
</script>