Thanks!
Here is the localization module.
import glob
import json
from dash.dependencies import Input, Output, State, ALL
from app import app
id_to_key = {}
def id_to_str(id):
if isinstance(id, str):
return id
tmp = [f'"{key}":"{id[key]}"' for key in sorted(id)]
return f"{{{','.join(tmp)}}}"
def register(ids):
id_to_key.update(ids)
def translate(key=None, id=None, lang="en-us"):
if key or id:
key = key if key else id_to_key[id_to_str(id)]
try:
return languages[lang][key]
except KeyError:
try:
print(f"Key '{key}' undefined for lang '{lang}'")
return languages["en-us"][key]
except KeyError:
raise KeyError(f"Key '{key}' undefined for default lang 'en-us'")
else:
raise ValueError()
def read_index():
with open("lang/index.json", "r", encoding="utf8") as f:
index = json.load(f)
return index
def read_lang(filename):
with open(filename, "r", encoding="utf8") as f:
lang = json.load(f)
return {
k: v.replace("[", "\[")
.replace("]", "\]")
.replace(">", "\>")
.replace("sub\>", "sub>")
.replace("sup\>", "sup>")
for k, v in lang.items()
}
def initialize_langs(index):
langs = {}
for k in index:
langs[k] = {}
for filename in glob.glob(f"lang/{k}/*.json"):
langs[k].update(read_lang(filename))
return langs
lang_index = read_index()
languages = initialize_langs(lang_index)
@app.callback(
Output({"type": "text", "file": ALL, "index": ALL}, "children"),
Input("language_dropdown", "value"),
State({"type": "text", "file": ALL, "index": ALL}, "id"),
State("debug", "data"),
prevent_initial_call=True,
)
def update_translation(lang, text_ids, debug):
if debug:
for id in text_ids:
id = id_to_str(id)
if id not in id_to_key:
print(id)
return [translate(id=id, lang=lang) for id in text_ids]
Every string is stored as a pair “key: my_translation” in a bunch of JSON files organized as follows:
lang
├─ index.json
├─ en-us
| ├─ file1.json
| ├─ file2.json
| ├─ ...
├─ fr-fr
| ├─ file1.json
| ├─ file2.json
where index.json
looks like this:
{
"en-us": "English",
"fr-fr": "Français"
}
To initialize a string, I use the function translate(key, lang=lang)
and associate an ID in the format {"type": "text", "file": "whatever", "index": "whatever"}
for the callback update_translation
.
To register a string with an ID that does not match this format, I use the function register
. These strings won’t be translated by the callback update_translation
.
Hope it’s clear! There may be an easier way, though.