Translation
The translation solution is based on gettext module that is a part of
the standard library.
Translation files are located in smartreport/locale.
So far the process is manual, via CLI, but it should be easy to implement that into automatic pipelines.
How to add translation to the code
Install internationalization tools
Although it's not mandatory it provides helpful CLI tools, and access to the Localization feature.
pip install .[i18n]
Wrap strings in the code with special function
Strings should be wrapped with _() function.
A code example can be found below. Keep in mind that only the strings that should be translated are wrapped in this
function. Things like: dict keys, paths, etc. should remain untouched.
path = "config.ini"
try:
with open(path, "r") as f:
config = f.read()
_info_txt = f"File '{path}' has been loaded."
print(_info_txt)
except FileNotFoundError:
print("File not found")
from smartreport.engine.translations import _
path = "config.ini"
try:
with open(path, "r") as f:
config = f.read()
_info_txt = _("File '{path}' has been loaded.").format(path=path)
print(_info_txt)
except FileNotFoundError:
print(_("File not found"))
Important
The f-strings has to be replaced with .format() notation. e.g.
_("Your score: {score}").format(score=score)or_("Your score: {}").format(score)That is because the translation has to happen before the variable replacement.
_()function returns a string that is then updated with.format()method.
Extract all the strings to the template file
Run the CLI command from the root folder
pybabel extract -o smartreport/locale/base.pot smartreport
This will inspect all the files in smartreport folder
retrieve all wrapped strings and create a template file smartreport/locale/base.pot (parameter -o)
out of it, that can be used later to create language-specific translation files
Initialize language-specific sub-folders
To create an initial translation file for a new language you can use the following command
pybabel init -l pl_PL de_DE -i smartreport/locale/base.pot -d smartreport/locale -D smartreport
It will create sub-folders for Polish (pl_PL) and German (de_DE) languages (parameter -l)
based on smartreport/locale/base.pot template file (parameter -i)
inside smartreport/locale folder (parameter -d)
each with smartreport.po translation file (parameter -D)
Update language-specific sub-folders
If your template file has changed, you can update language-specific files with following command
pybabel update -i smartreport/locale/base.pot -d smartreport/locale -D smartreport
It will update smartreport.po translation files (parameter -D)
if sub-folders of inside smartreport/locale folder (parameter -d)
based on smartreport/locale/base.pot template file (parameter -i)
Those files (with .po extension) can be then imported into the external translation tool like Applanga or Poedit, as a starting point for translations.
Translation
.po files starts with an optional header that is followed with list of pairs
#: report/asset/report_asset_utils.py:20
msgid "Timestamp Unknown"
msgstr "Czas nieznany"
#: report/asset/report_asset_utils.py:26
msgid "Asset Name"
msgstr "Nazwa urządzenia"
Lines that starts with msgid defines identifiers and are the same in .pot template file
and all the translation files, while lines starting with msgstr defines actual translation.
Example above means that whenever there is a piece of code _("Timestamp Unknown")
it will be evaluated to string "Czas nieznany"
When done with translation replace the .po files in corresponding sub-folder.
Compilation
Last step is to compile the human-readable .po files to machine-readable .mo files.
It can be done with:
pybabel compile -d smartreport/locale -D smartreport -f
It will compile all the smartreport.po translation files (parameter -D)
located inside smartreport/locale folder sub-folders (parameter -d)
into machine-friendly format *.mo
How to use the translations in runtime
Import the set_translation_language() and run it each time you want to change the translation language.
This will import the default (en_US) language, and register the _ = gettext.gettext function into the global
namespace.
Example
from smartreport.engine.translations import set_translation_language
from smartreport.datatypes.measurement import JobMeasurement
from smartreport.report.measurement import MeasurementReport
measurement = JobMeasurement.read_json("./RawData7268.json")
for lang in ("pl_PL", "en_US", "it_IT"):
set_translation_language(language=lang)
measurement_report = MeasurementReport.use_word_output(measurement=measurement)
measurement_report.make_report(expert_mode=True, language=lang)
measurement_report.export_document(f"RawDataReport7268_{lang}.docx")