3afa2fcdf3
In the GUIs, on the language-select screen, show e.g.
Czech (100%), Danish (13%), Dutch (54%)
instead of
Czech, Danish, Dutch
- we count the source strings when creating the .pot PO-template file
and add an "X-Electrum-SourceStringCount" header to it, in the push_locale.py script that uploads the .pot file to crowdin.
- later, when we run electrum-locale/update.py to download the translations in .po files, these files will also contain the same header.
- then when the build_locale.sh script compiles those .po files, we can read the header and use it to populate a new "stats.json" file
that we place in electrum/locale/locale/ and bundle in the all release binaries/distributables.
- stats.json also includes the number of translated strings for each lang
- at runtime we simply read stats.json and use the values to calculate the percentages
- a prior implementation did not pre-calc stats.json but did all calculations at runtime (by opening all .mo translations)
- however that was deemed to slow, hence the build-time pre-calc
- runtime calc took 40 ms on my laptop, so I guess it could easily take 10x that on an old phone
- just as we have always been very tolerant of any locale files or even the whole locale/ dir missing, we also tolerate stats.json missing
73 lines
2.6 KiB
Python
Executable File
73 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (C) 2026 The Electrum developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file LICENCE or http://www.opensource.org/licenses/mit-license.php
|
|
#
|
|
#
|
|
# This generates a 'stats.json' file containing some statistics about translation completeness.
|
|
|
|
import gettext
|
|
import glob
|
|
import json
|
|
import os
|
|
|
|
PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
|
LOCALE_DIR = os.path.join(PROJECT_ROOT, "electrum", "locale", "locale")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
catalog_size = {} # type: dict[str, int]
|
|
source_string_count = None
|
|
# - calc stats
|
|
files_list = glob.glob(f"{LOCALE_DIR}/*/LC_MESSAGES/*.mo")
|
|
for fname in files_list:
|
|
lang_code = os.path.basename(os.path.dirname(os.path.dirname(fname)))
|
|
try:
|
|
t = gettext.translation('electrum', LOCALE_DIR, languages=[lang_code])
|
|
except OSError as e:
|
|
raise Exception(f"cannot find or parse .mo file matching {fname!r}") from e
|
|
# calc catalog size of translated strings
|
|
catalog_size[lang_code] = len(t._catalog)
|
|
# same SourceStringCount header should be present in all .mo files:
|
|
t_info = t.info()
|
|
try:
|
|
ss_cnt = int(t_info["x-electrum-sourcestringcount"])
|
|
except Exception as e:
|
|
raise Exception(
|
|
f"missing or malformed 'x-electrum-sourcestringcount' header, for {lang_code!r}.\n"
|
|
f"found {t_info}"
|
|
) from e
|
|
if source_string_count is None:
|
|
source_string_count = ss_cnt
|
|
elif source_string_count != ss_cnt:
|
|
raise Exception(
|
|
f"inconsistent 'x-electrum-sourcestringcount' headers! "
|
|
f"prev_cnt={source_string_count}, new_cnt={ss_cnt} (for lang={lang_code})")
|
|
# - convert to json data. example:
|
|
# {
|
|
# "source_string_count": 9999,
|
|
# "translations": {
|
|
# "de_DE": {
|
|
# "string_count": 400,
|
|
# },
|
|
# ...
|
|
# }
|
|
# }
|
|
json_data = {
|
|
"source_string_count": source_string_count,
|
|
"translations": {},
|
|
}
|
|
for lang_code in catalog_size:
|
|
json_data["translations"][lang_code] = {}
|
|
json_data["translations"][lang_code]["string_count"] = catalog_size[lang_code]
|
|
# - write json to disk
|
|
with open(f"{LOCALE_DIR}/stats.json", "w", encoding="utf-8") as f:
|
|
json_str = json.dumps(
|
|
json_data,
|
|
indent=4,
|
|
sort_keys=True
|
|
)
|
|
f.write(json_str)
|
|
print(f"done. created file '{LOCALE_DIR}/stats.json'")
|