Skip to content

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")