Basic babel support working w/UI language selection

This commit is contained in:
kdmukai
2022-04-30 06:57:39 -05:00
parent 5e007e5010
commit 91e1b42570
13 changed files with 307 additions and 14 deletions
+1
View File
@@ -4,3 +4,4 @@ src/seedsigner.egg-info/
.nova
.vscode
src/seedsigner/models/settings_definition.json
.mo
+71
View File
@@ -0,0 +1,71 @@
# Babel Multilanguage Support
### "Wrapping" text for translation
All you have to do in your code is wrap each piece of English text with the `gettext` shorthand `_()`:
* Wrap python strings: `error="No device was selected."` becomes `error=_("No device was selected.")`
* Use `.format()` to wrap strings with variable injections:
```
mystr = f"My dad's name is {dad.name} and my name is {self.name}."
mystr = _("My dad's name is {} and my name is {}").format(dad.name, self.name)
```
If there are a lot of variables to inject, placeholder names can be used:
```
mystr = _("My dad's name is {dad_name} and my name is {my_name}").format(dad_name=dad.name, my_name=self.name)
```
### Rescanning for text that needs translations
Re-generate the `messages.pot` file:
```
pybabel extract -N -F babel/babel.cfg -o babel/messages.pot .
```
This will rescan all wrapped text, picking up new strings as well as updating existings strings that have been edited.
Then run `update`:
```
pybabel update -N --no-wrap -i babel/messages.pot -d src/seedsigner/resources/babel
# Or target a specific language code:
pybabel update -N --no-wrap -i babel/messages.pot -d src/seedsigner/resources/babel -l es
```
_note: the `-N` flag prevents babel from trying to use fuzzy matching to re-use existing translations for new strings. The fuzzy matching does not seem to do what we would want so we keep it disabled._
Any newly wrapped text strings will be added to each `messages.po` file. Altered strings will be flagged as needing review to see if the existing translations can still be used.
Once the next round of translations is complete, recompile the results:
```
pybabel compile -d src/seedsigner/resources/babel
# Or target a specific language code:
pybabel compile -d src/seedsigner/resources/babel -l es
```
### Adding support for another language
Assuming you have `extract`ed an updated `messages.pot`, all you have to do is generate the initial version of each new language file:
```
pybabel init -i babel/messages.pot -d src/seedsigner/resources/babel -l es
pybabel init -i babel/messages.pot -d src/seedsigner/resources/babel -l fr
pybabel init -i babel/messages.pot -d src/seedsigner/resources/babel -l de
```
Then `update` and `compile` as above.
The only other step is to add the new language to the LOCALES list in `SettingsDefinition`.
## For Packagers:
The *.mo files are not checked into the repository as (large) binary (large) objects ("BLOBs") should not be checked into git to reduce repo bloat. So they need to be generated after fetching the code:
```
pybabel compile -d src/seedsigner/resources/babel
```
In order to make that as transparent as possible, that procedure has been integrated into the setup-process. So it will be executed by:
```
python3 setup.py install
```
Unfortunately, it won't be executed by `pip3 install -e .` even though that has been propagated very long to be the developement-env installation procedure.
+1
View File
@@ -0,0 +1 @@
[python: src/**/**.py]
+38
View File
@@ -0,0 +1,38 @@
# Translations template for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-04-29 08:48-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.1\n"
#: src/seedsigner/views/view.py:132
msgid "Scan"
msgstr ""
#: src/seedsigner/views/view.py:133
msgid "Seeds"
msgstr ""
#: src/seedsigner/views/view.py:134
msgid "Tools"
msgstr ""
#: src/seedsigner/views/view.py:135
msgid "Settings"
msgstr ""
#: src/seedsigner/views/view.py:139
msgid "Home"
msgstr ""
+2
View File
@@ -1,7 +1,9 @@
Babel==2.10.1
embit==0.4.10
numpy==1.21.1
picamera==1.13
Pillow==8.2.0
pytz==2022.1
-e git+https://github.com/seedsigner/pyzbar.git@c3c237821c6a20b17953efe59b90df0b514a1c03#egg=pyzbar
qrcode==7.3.1
RPi.GPIO==0.7.0
+4
View File
@@ -1,5 +1,6 @@
import logging
import traceback
import gettext
from embit.descriptor import Descriptor
from embit.psbt import PSBT
@@ -82,6 +83,9 @@ class Controller(Singleton):
back_stack: BackStack = None
screensaver: ScreensaverScreen = None
# Babel initialization
gettext.install('messages', localedir='seedsigner/resources/babel')
@classmethod
def get_instance(cls):
+11
View File
@@ -1,3 +1,4 @@
import gettext
import json
import os
@@ -103,6 +104,16 @@ class Settings(Singleton):
os.remove(self.SETTINGS_FILENAME)
print(f"Removed {self.SETTINGS_FILENAME}")
# Special handling for babel
if attr_name == SettingsConstants.SETTING__LOCALE:
# `value` will be the language code (e.g. "en", "es", "de", etc)
os.environ['LANGUAGE'] = value
# Re-initialize babel with the new locale
gettext.install('messages', localedir='seedsigner/resources/babel')
print(f"Set LANGUAGE locale to {os.environ['LANGUAGE']}")
self._data[attr_name] = value
self.save()
+15 -9
View File
@@ -40,9 +40,17 @@ class SettingsConstants:
(COORDINATOR__SPECTER_DESKTOP, "Specter Desktop"),
]
LANGUAGE__ENGLISH = "en"
ALL_LANGUAGES = [
(LANGUAGE__ENGLISH, "English"),
LOCALE__CZECH = "cs"
LOCALE__ENGLISH = "en"
LOCALE__FRENCH = "fr"
LOCALE__GERMAN = "de"
LOCALE__SPANISH = "es"
ALL_LOCALES = [
(LOCALE__CZECH, "Czech"),
(LOCALE__ENGLISH, "English"),
(LOCALE__FRENCH, "French"),
(LOCALE__GERMAN, "German"),
(LOCALE__SPANISH, "Spanish"),
]
BTC_DENOMINATION__BTC = "btc"
@@ -137,7 +145,7 @@ class SettingsConstants:
# Individual SettingsEntry attr_names
SETTING__LANGUAGE = "language"
SETTING__LOCALE = "locale"
SETTING__WORDLIST_LANGUAGE = "wordlist_language"
SETTING__PERSISTENT_SETTINGS = "persistent_settings"
SETTING__COORDINATORS = "coordinators"
@@ -330,14 +338,12 @@ class SettingsDefinition:
settings_entries: List[SettingsEntry] = [
# General options
# TODO: Full babel multilanguage support! Until then, type == HIDDEN
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
attr_name=SettingsConstants.SETTING__LANGUAGE,
attr_name=SettingsConstants.SETTING__LOCALE,
display_name="Language",
type=SettingsConstants.TYPE__SELECT_1,
visibility=SettingsConstants.VISIBILITY__HIDDEN,
selection_options=SettingsConstants.ALL_LANGUAGES,
default_value=SettingsConstants.LANGUAGE__ENGLISH),
selection_options=SettingsConstants.ALL_LOCALES,
default_value=SettingsConstants.LOCALE__ENGLISH),
# TODO: Support other bip-39 wordlist languages! Until then, type == HIDDEN
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
@@ -0,0 +1,39 @@
# Czech translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-04-29 08:48-0500\n"
"PO-Revision-Date: 2022-04-29 13:53-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: cs\n"
"Language-Team: cs <LL@li.org>\n"
"Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.1\n"
#: src/seedsigner/views/view.py:132
msgid "Scan"
msgstr "Skenuj"
#: src/seedsigner/views/view.py:133
msgid "Seeds"
msgstr "Seedy"
#: src/seedsigner/views/view.py:134
msgid "Tools"
msgstr "Nástroje"
#: src/seedsigner/views/view.py:135
msgid "Settings"
msgstr "Nastavení"
#: src/seedsigner/views/view.py:139
msgid "Home"
msgstr "Nabídka"
@@ -0,0 +1,39 @@
# German translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-04-29 08:48-0500\n"
"PO-Revision-Date: 2022-04-29 13:49-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
"Language-Team: de <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.1\n"
#: src/seedsigner/views/view.py:132
msgid "Scan"
msgstr "Scan"
#: src/seedsigner/views/view.py:133
msgid "Seeds"
msgstr "Seeds"
#: src/seedsigner/views/view.py:134
msgid "Tools"
msgstr "Tools"
#: src/seedsigner/views/view.py:135
msgid "Settings"
msgstr "Einst."
#: src/seedsigner/views/view.py:139
msgid "Home"
msgstr "Start"
@@ -0,0 +1,42 @@
# Translations template for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
# Translators:
# Keith Mukai, 2022
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-04-29 08:48-0500\n"
"PO-Revision-Date: 2022-04-29 13:50+0000\n"
"Last-Translator: Keith Mukai, 2022\n"
"Language-Team: Spanish (https://www.transifex.com/the-seedsigner-project/teams/135832/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.1\n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/seedsigner/views/view.py:132
msgid "Scan"
msgstr "Escanear"
#: src/seedsigner/views/view.py:133
msgid "Seeds"
msgstr "Llaves"
#: src/seedsigner/views/view.py:134
msgid "Tools"
msgstr "Útiles"
#: src/seedsigner/views/view.py:135
msgid "Settings"
msgstr "Ajustes"
#: src/seedsigner/views/view.py:139
msgid "Home"
msgstr "Principal"
@@ -0,0 +1,39 @@
# French translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-04-29 08:48-0500\n"
"PO-Revision-Date: 2022-04-29 13:49-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fr\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.1\n"
#: src/seedsigner/views/view.py:132
msgid "Scan"
msgstr "Scan"
#: src/seedsigner/views/view.py:133
msgid "Seeds"
msgstr "Graines"
#: src/seedsigner/views/view.py:134
msgid "Tools"
msgstr "Outils"
#: src/seedsigner/views/view.py:135
msgid "Settings"
msgstr "Paramètres"
#: src/seedsigner/views/view.py:139
msgid "Home"
msgstr "Accueil"
+5 -5
View File
@@ -128,14 +128,14 @@ class MainMenuView(View):
from .tools_views import ToolsMenuView
from seedsigner.gui.screens import LargeButtonScreen
menu_items = [
(("Scan", FontAwesomeIconConstants.QRCODE), ScanView),
(("Seeds", FontAwesomeIconConstants.KEY), SeedsMenuView),
(("Tools", FontAwesomeIconConstants.SCREWDRIVER_WRENCH), ToolsMenuView),
(("Settings", FontAwesomeIconConstants.GEAR), SettingsMenuView),
((_("Scan"), FontAwesomeIconConstants.QRCODE), ScanView),
((_("Seeds"), FontAwesomeIconConstants.KEY), SeedsMenuView),
((_("Tools"), FontAwesomeIconConstants.SCREWDRIVER_WRENCH), ToolsMenuView),
((_("Settings"), FontAwesomeIconConstants.GEAR), SettingsMenuView),
]
screen = LargeButtonScreen(
title="Home",
title=_("Home"),
title_font_size=26,
button_data=[entry[0] for entry in menu_items],
show_back_button=False,