Prusa-Firmware/lang/README.md
Yuri D'Elia 0d7680dbf7
New PO-based language translation support (#3471)
* lang: Add a PO language extractor with FW metadata support

Implement a straight-to-po language extractor which supports our custom
language requirements:

- _i/_I/ISTR for text string definitions
- _T for catalog translations (with back-reference support)
- //// EOL comments with:
  - MSG_ catalog entry name identifiers
  - c=X r=Y annotations for screen dimensioning checks
- Crude support for commented lines

All source locations are correctly referenced in the PO, with the
metadata colleted in the comment for further processing.

Several checks are implemented already during extraction:

- Correct catalog name assignment (no duplicates)
- Metadata checks for each entry

Further checks will be implemented by directly checking the translated PO file.

Requires "polib" and "regex" python modules.

* lang: Adapt lang-check to work directly on PO/POT files

* lang: Allow lang-extract to generate stable (pre-sorted) output directly

* lang: Further extend lang-extract consistency/error checking

- Do not parse inside preprocessor conditionals
- Distinguish between references and definitions
- Warn about missing references and definitions

* lang: lang-extract: warn about incorrect PROGMEM assignments

Check that ISTR is used along with PROGMEM_I1 in an attempt to spot
useless translated catalogs.

* lang: lang-extract: Improved handling of same-line translations

Correctly reference metadata on same-line translations.

* lang: lang-extract: Handle _O as a cat-ref

https://github.com/prusa3d/Prusa-Firmware/pull/3434

* lang: lang-extract: Warn about unused catalog definitions

* lang: lang-extract: Allow propagating translation comments via //

The definition:

    code //// definition [// comment]

will check [definition] as before, but blindly accumulate // comment.
The comment is then re-appended back into the PO files for translators
with the form:

    definition
    comment
    comment...

* lang: Fix incorrect display definitions

* lang: lang-extract: Check source encoding/charmap

* lang: Translate the degree symbol

* lang: Unbreak/cleanup DEBUG_SEC_LANG

* lang: Improve meaning of comment

* lang: Split charset conversions into an aux lib for future use

* lang: Implement lang-map.py to extract the translation symbol map

- Extracts the translatable symbol map for further use
- Computes a stable "language signature" from the map itself
- Optionally patches the binary update the symbols

* lang: Check for translation recoding problems

* lang: Implement a transliteration map to post-process translations

TRANS_CHARS is now used to replace unavailable symbols to the source
encoding, only while producing the language catalog.

* lang: Handle/check character replacements in lang-check

Filter the translation through TRANS_CHARS, so that the preview and
length check are performed correctly for expanding replacements such as
'ß' to 'ss'.

* lang: Implement lang-build.py to generate the final language catalog

* Cleanup .gitignore

* lang: Drop txt language files

* lang: Remove outdated translation scripts and obsolete docs

* lang: Update build scripts for new infrastructure

* lang: [no] Integrate accents from po/new/no.po

We now support accents natively

* lang: Remove redundant directory po/new/

* lang: Fix encoding of LCD characters in PO files

* lang: [hr] Fix wrapping in MSG_CRASH_DET_ONLY_IN_NORMAL

* lang: Sort and reformat PO files for further massaging

* lang: Switch to developer (dot) comments for PO metadata

* lang: Allow the IGNORE annotation to skip extraction

* lang: Fix missing/broken language metadata in sources

* lang: Add update-pot.sh and regenerate po/Firmware.pot

* lang: Add update-po.sh and refresh all PO files

* lang: Add summary documentation about the new translation workflow

* Add more ignored files

* CI: Add new required dependencies to travis

* lang: lang-build: Improve warning message

"referenced" was really meaning that data is being duplicated.

* lang: Respect the language order as defined in config.sh

This correctly splits normal and community-made entries during language
selection.

* lang: More typos in the documentation

* lang: Check for the maximum size of each language

Each table needs to fit within LANG_SIZE_RESERVED

* lang: Properly align _SEC_LANG to page boundaries

... instead of relying on _SEC_LANG_TABLE to calculate the offset

* lang: Build support for dual-language hex files

Detect the printer type by checking the current variant type.

On printers with no xflash (MK2*), generate one hex file for each
additional language file by patching the built-in secondary language
table during the build process

* lang: Mention lang-patchsec.py

* lang: Use color() instead of tput for clarity

* lang: Allow disabling terminal colors with NO_COLOR/TERM=dumb

* lang: Consistent use of redirection in config.sh

* lang: Stricter variant-type check for xflash support

* lang: Output size stats when building double-language hex files

* lang: Respect NO_COLOR in lang-check.py

* lang: Check for repeated/incorrect annotations

Catch errors such as "c=1 c=2"

* lang: Correct MSG_SLIGHT_SKEW/MSG_SEVERE_SKEW annotations

* lang: [it] Improve MSG_*_SKEW translation

* lang: Use INTLHEX instead of OUTHEX_P/S for configuration

We already have OUTHEX which is the compiled firmware.

Use INTLHEX for the final internationalized firmware, which is less
confusing. Also, assume it being a prefix for all generated hex
files, which reduces the number of variables set.

* lang: Move lang_map to lib.io for further use

* lang: lang-check: Accept a firmware map file to suppress unused string warnings

* lang: Use the map file to reduce useless warnings during fw-build

* lang: lang-check: Also suppress unused empty annotations

* lang: Fix MSG_MOVE_CARRIAGE_TO_THE_TOP_Z annotation

Refresh pot file

* lang: lang-check: Do not warn about same-word translations by default

Do not warn when one-word translations such as "No" result in "No" also
in other languages, since this is common in latin languages.

Allow to re-enable the warning with --warn-same

* lang: lang-build: Handle same-source/translation efficiently

* lang: [it] Explicitly add On/Off/Reset/Wizard to suppress warnings

Instead of displaying a warning, supress the warning and explicitly
translate each entry using english (which is the common/acceptable
word in these cases).

* lang: [it] Suppress more warnings

* lang: lang-check: Add intermediate "suggest" warning category

Warnings in the "suggest" category as shown as [S] as based on pure
speculation from the checking tool, such as the translation being
significantly shorter than the original.

As a result, they can be suppressed with --no-suggest

* lang: Return translation status from lang-check

- 0 if the translation only contains suggestions
- 1 if the translation contains warnings or errors

Check for the exit status in fw-build.sh, but do nothing at the moment
except printing a non-fatal error.

* lang: Remove "trim_trailing_whitespace=false" for po files

PO files got cleaned up/rewritten. We can now ensure they stay
consistent.

* lang: [sv] Re-integrate changes from 70c73cb

* lang: [no] Reintegrate changes from @pkg2000
2022-06-16 15:03:30 +02:00

5.2 KiB

Internationalization support

Multi-language support in the Prusa MK3 firmware is based on PO language files.

Firmware support is controlled by the LANG_MODE define in the configuration, which defaults to 1 (enabled). When LANG_MODE is set, the firmware can load additional languages, but these extra languages need to be "baked in" in the firmware image for flashing. This last step is performed using the tools in this directory.

Quick reference

Required tools

Python 3 with the regex, pyelftools and polib modules as well as gettext and dos2unix. On a debian-based distribution, install the required packages with:

sudo apt-get install python3-regex python3-pyelftools python3-polib gettext dos2unix

Main summary

Language files:

  • po/Firmware.pot: Main list of strings to translate (do not change this file manually - it is automatically generated)
  • po/Firmware_XY.po: Translations for "XY", where XY is the ISO639-1 language code.

PO files are simple text files that can be edited with any text editor, but are best handled using dedicated tools such as POEdit, Lokalize or Linguist.

High-level tools:

  • config.sh: Language selection/configuration
  • fw-build.sh: Builds the final multi-language hex file into this directory
  • fw-clean.sh: Cleanup temporary files left by fw-build.sh
  • update-pot.sh: Extract internationalized strings from the sources and place them inside po/Firmware.pot
  • update-po.sh: Refresh po file/s with new translations from the main pot file.

Lower-level tools:

  • lang-check.py: Checks a single po file for screen formatting issues.
  • lang-extract.py: Extract internationalized strings from source files.
  • lang-map.py: Extract and patch the translation symbol map in the firmware.
  • lang-build.py: Build a binary language catalog for a single po file.
  • lang-patchsec.py: Embed a single secondary language catalog in the firmware.

Building an internationalized firmware

This is accomplished by running fw-build.sh after building the firmware with LANG_MODE = 1. Language selection is done by modifying config.sh.

After running the script, the final Firmware-intl.hex will be generated in this directory.

This step is already performed for you when using build.sh or PF-build.sh. You can however re-run fw-build.sh to update just the language catalogs inside the image.

Updating an existing translation

Typo or incorrect translation in existing text

If you see a typo or an incorrect translation, simply edit po/Firmware_XY.po and make a pull request with the changes.

You can use the following command:

./lang-check.py po/Firmware_XY.po

to check for screen formatting issues in isolation, or use the --information flag:

./lang-check.py --information po/Firmware_XY.po

to preview all translations as formatted on the screen.

Missing translation without entry in po file

If some text is missing, but there is no reference text in the po file, you need to refresh the translation file by picking up new strings and annotations from the template.

Run ./update-po.sh po/Firmware_XY.po to propagate the new strings to your language. This will merge the new strings, update references/annotations as well as marking unused strings as obsolete.

Update the translations, then proceed as for typo or incorrect translation.

Fixing an incorrect screen annotation or english text

The screen annotations as well as the original english text is extracted from the firmware sources. Do not change the main pot file. The pot and po file contains the location of the annotation to help you fix the sources themselves.

Run ./update-pot.sh to regenerate po/Firmware.pot and verify that the annotation has been picked up correctly. You can stop here if you only care about the annotation.

Run ./update-po.sh po/Firmware_XY.po otherwise to propagate the annotation to your language, then proceed as for typo or incorrect translation.

Adding a new language

Each language is assigned a two-letter ISO639-1 language code.

The firmware needs to be aware of the language code. It's probably necessary to update the "Language codes" section in Firmware/language.h to add the new code as a LANG_CODE_XY define as well as add the proper language name in the function lang_get_name_by_code in Firmware/language.c.

It is a good idea to ensure the translation template is up-to-date before starting to translate. Run ./update-pot.sh to regenerate po/Firmware.pot if possible.

Copy po/Firmware.pot to po/Firmware_XY.po. The same language code needs to be used for the "Language" entry in the metadata. Other entries can be customized freely.

The new language needs to be explicitly added to the list of bundled languages in config.sh.

At this point the new language should be picked-up normally. See how to build an internationalized firmware and use lang-check.py for previewing the translation without having to perform a complete rebuild.

Internal details

TODO