mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 23:18:25 +00:00
Basic babel support working w/UI language selection
This commit is contained in:
@@ -4,3 +4,4 @@ src/seedsigner.egg-info/
|
||||
.nova
|
||||
.vscode
|
||||
src/seedsigner/models/settings_definition.json
|
||||
.mo
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
[python: src/**/**.py]
|
||||
@@ -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 ""
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user