Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial l10n / multilanguage support #620

Open
wants to merge 224 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
224 commits
Select commit Hold shift + click to select a range
91e1b42
Basic babel support working w/UI language selection
kdmukai Apr 30, 2022
632c212
Experimental support for character sets the default fonts can't display
kdmukai Apr 30, 2022
23080e7
Bugfixes, locale-specific font sizing, Japanese initial tests
kdmukai May 1, 2022
0887e27
Interim commit; initial `ScreenshotRenderer`, basic integration in py…
kdmukai May 6, 2022
f7ae848
Early experiments with Arabic and Hebrew; now up 232 translatable str…
kdmukai May 7, 2022
c306133
Bugfixes
kdmukai May 7, 2022
001f65e
Early Russian support
kdmukai May 8, 2022
1ac2509
Bugfixes, translator notes
kdmukai May 8, 2022
a2d5bba
Numerous TRANSLATOR_NOTE clarifications; proper pluralization
kdmukai May 9, 2022
69c2bd6
Bugfix, missing translation strings, prepping screenshot generator fo…
kdmukai May 10, 2022
bffefcb
Merge branch 'main' into initial_multilanguage
kdmukai May 10, 2022
3df2fb1
bugfix; enhanced screenshot generator output
kdmukai May 10, 2022
8bedaa2
Translation updates; dynamic sizing TopNav titles; more complex scree…
kdmukai May 21, 2022
5f875fa
Adds --locale option to screenshot generator; DE translation update
kdmukai May 25, 2022
4198927
Better handling of the PSBTOverview loading spinner
kdmukai May 26, 2022
8d4fb29
Merge remote-tracking branch 'upstream/dev' into initial_multilanguage
kdmukai May 26, 2022
bb9ae3a
Update screen.py
kdmukai May 26, 2022
1225c5b
More bulletproofing around the loading screen spinner
kdmukai May 26, 2022
af460fe
RU update
kdmukai May 26, 2022
077e1d5
Update tools_screens.py
kdmukai May 26, 2022
c33d149
Adding missing translation strings; Initial KR support
kdmukai May 29, 2022
6b59b23
ES update
kdmukai May 31, 2022
cd7c193
CS update
kdmukai May 31, 2022
3e608d9
SettingsQR compatibility fix; minor bugfix on LargeIconStatusScreen
kdmukai Jun 2, 2022
bab74d2
DE update
kdmukai Jun 12, 2022
fdf98a2
Merge branch 'dev' into initial_multilanguage
kdmukai Jun 28, 2022
d201fca
Merge branch 'dev' into initial_multilanguage
kdmukai Oct 23, 2022
5bdf2bd
Create README.md
kdmukai Oct 23, 2022
3220352
notes from first pass
Nov 19, 2022
5754c2c
removed russian messages.mo
Nov 20, 2022
32d9c9f
Merge branch 'dev' into kdmukai/initial_multilanguage
Nov 20, 2022
cb63484
an iteration i18n-izing literals and fstrings
Nov 20, 2022
ec3e19b
reviewed kdmukais _() changes, added some, updated my notes
Nov 20, 2022
4ece024
bugfix: getter() for localized font-size
Nov 21, 2022
04959d9
translator notes added to changes in commit cb63484
Nov 21, 2022
993fb37
done w/ kdmukai code-review from last weeks i18n_notes
Nov 21, 2022
c387597
Prefixing /README.md as branch docs
Nov 24, 2022
f2c8405
editing README.md as branch docs
Nov 24, 2022
152f9e3
Merge branch 'i18n' into kdmukai/initial_multilanguage
Nov 29, 2022
eef0358
Merge branch 'dev' into kdmukai/initial_multilanguage
Dec 1, 2022
c130d99
oh la la, la la, la la !
Dec 1, 2022
fb55840
oh la la
Dec 1, 2022
808411c
l10n catalogs updated after merge dev
Dec 1, 2022
4c416c8
dont translate sig-or-script-types yet
Dec 1, 2022
4a8a04c
ok, now translate sig/script-types
Dec 1, 2022
4b0dc31
Merge branch 'dev' into kdmukai/initial_multilanguage
Dec 2, 2022
4df5757
postmerge bugfix on poorly resolved conflict
Dec 2, 2022
8ca3edb
i18n/l10n settings i18n/l10n-ized too
Dec 8, 2022
2f18588
Merge branch 'dev' into kdmukai/initial_multilanguage
Dec 8, 2022
a4c784e
french l10n revisited
Dec 12, 2022
eab4942
i18n/l10n iter: RemoveableMicroSd/BIP85-ChildSeeds
Dec 13, 2022
3d0395e
working w/o halting
Dec 13, 2022
6de246f
message catalog cleanup
Dec 13, 2022
1b94005
added en locale and translated langs
Dec 13, 2022
e433b50
gettext() from instance, not class
Dec 13, 2022
bf5561f
added views for more screenshots
Dec 13, 2022
14174c6
fr/es by nmhs
Dec 13, 2022
1e45c45
exclude line-num from message catalogs
Dec 14, 2022
760b157
messages.po catalogs included
Dec 14, 2022
9a4063a
l10n by nmhs
Dec 14, 2022
9d4f469
access font sizes w/ getter() for i18n
Dec 14, 2022
2ff0c90
discard-seed and seedqr-format screenshots in tests
Dec 15, 2022
1e283bd
tiny effort to i18n-ize embit strings
Dec 18, 2022
79857b6
i18n special keys (del,space)
Dec 18, 2022
e13f5dc
One last screen, and this exploration is hereby paused!
Dec 20, 2022
fe5f0a5
Merge branch 'dev' into kdmukai/initial_multilanguage
Jan 11, 2023
5b6961e
updated readme
Jan 12, 2023
0f7ea65
resolves Pillow 10 deprecation warning
Jan 13, 2023
d17b21f
Merge branch 'dev' into kdmukai/initial_multilanguage
Feb 8, 2023
9f07504
merge cleanup
Feb 8, 2023
0f44a29
some locales disabled
Feb 8, 2023
4834a20
i18n msgid extractions
Feb 8, 2023
3de70c1
Merge branch 'dev' into kdmukai/initial_multilanguage
Feb 16, 2023
04da43b
(i18n, fr-l10n) iteration
Feb 16, 2023
e477a89
Merge branch 'dev' into kdmukai/initial_multilanguage
Feb 22, 2023
298878a
works for me as of 0.6.0
Feb 22, 2023
08e3039
Merge branch 'dev' into kdmukai/initial_multilanguage
Mar 3, 2023
577e33a
MainMenuView now in view.py
Mar 12, 2023
14d7754
i18n for view.py
Mar 22, 2023
23337fd
compiled gettext *.mo in repo; cost=-20K/locale
Jun 25, 2023
110ffb8
Merge branch 'dev' into kdmukai/initial_multilanguage
Jul 16, 2023
5a1bf4c
resolved merge-conflict errors
Jul 27, 2023
681d3d4
Merge branch 'v0.7.0' into kdmukai/initial_multilanguage
Oct 7, 2023
3f3e144
Post v0.7.0 merge bug-fixes: components, generator, view
Oct 9, 2023
59ecc61
partial i18n at v0.7.0
Oct 9, 2023
0d4df24
l10n msgids updated w/o new translations
Oct 9, 2023
250813c
Multilanguage Spanish: Reviewed all text strings
enteropositivo Feb 24, 2024
e08037f
supporting enteropositivos es work: l10n-fr, screenshots, messages ca…
Feb 24, 2024
56a15ec
Fixes screenshot generator to work for generating all languages at once
kdmukai Sep 21, 2024
328da28
Removes babel *.mo binaries
kdmukai Sep 21, 2024
8ab7350
Update settings_definition.py
kdmukai Sep 21, 2024
128181d
Add `locale` to SettingsQR tests
kdmukai Sep 21, 2024
b0299f7
No need to have an "en" babel dir when English is the string source
kdmukai Sep 21, 2024
65f99b8
Revert Toast changes
kdmukai Sep 21, 2024
1c75cca
Merge commit 'refs/pull/3/head' of github.com:jdlcdl/seedsigner into …
Sep 30, 2024
ed8591f
Merge commit 'refs/pull/4/head' of github.com:jdlcdl/seedsigner into …
Sep 30, 2024
e1d8fb6
Merge commit 'refs/pull/5/head' of github.com:jdlcdl/seedsigner into …
Sep 30, 2024
dd06d10
Merge pull request #7 from jdlcdl/kdmukai/initial_multilanguage
kdmukai Oct 2, 2024
0dd3eb8
Updated babel files
kdmukai Sep 28, 2024
b785cc9
setup.cfg for pybabel integration with setuptools
kdmukai Sep 28, 2024
22be02f
Update setup.cfg
kdmukai Oct 3, 2024
9d81d06
remove locale-specific messages.po from repo
kdmukai Oct 11, 2024
6a81e9d
Additional translation strings; separate Babel deps
kdmukai Oct 11, 2024
2690cdd
Remove unnecessary *.po file copy
kdmukai Oct 11, 2024
b5e2b25
Merge pull request #8 from kdmukai/pr_7_with_merges
kdmukai Oct 11, 2024
a25fb4e
Add seedsigner-translations as a submodule
kdmukai Oct 11, 2024
f2548b2
Remove line numbers in messages.pot
kdmukai Oct 11, 2024
543f15b
Merge pull request #9 from kdmukai/initial_multilanguage_submodule
kdmukai Oct 11, 2024
41d58db
Merge commit 'bf12291be1eba8c6427cd5cf741ee7fa491a61a1' into initial_…
kdmukai Oct 11, 2024
fd45717
Merge commit '7fd0ebc871476f3357c9d5d525788eb1643f47a0' into initial_…
kdmukai Oct 11, 2024
f5b6aae
Merge commit 'b5d244a8c7aa35fdc8ac3bf8d41741f15d41f400' into initial_…
kdmukai Oct 11, 2024
8862bf5
Merge commit '16cb15ec0a6846901c5e17b5c77147a64a4bc121' into initial_…
kdmukai Oct 11, 2024
a11d91f
Merge commit '0c83196828497d2b4308a370b85ffeda97b47efb' into initial_…
kdmukai Oct 11, 2024
1d33af4
Merge commit 'f7ec9ff221a5e0b8398c701b45441044688b0e9d' into initial_…
kdmukai Oct 11, 2024
cb4348a
Merge commit 'facf7efd9ff417177576066def67f4afc5a287a7' into initial_…
kdmukai Oct 11, 2024
ac564ce
Merge commit 'a109c80c92d7267e243595a80bcee212b4679fd0' into initial_…
kdmukai Oct 11, 2024
e623bef
Merge commit '39d11d978e7fed2e99f4075d75049154c3469c21' into initial_…
kdmukai Oct 11, 2024
8bf46f7
Merge commit '87452cdf8f7e17323e3cc50f329739b3892ba052' into initial_…
kdmukai Oct 11, 2024
9519e6c
Merge commit 'a73cae6ae80a81962a67fd5ef2e3ec9cf68700bd' into initial_…
kdmukai Oct 11, 2024
fdf951c
Merge commit '213ca2b49a8f6426f24284564b2e67d6f0464c0c' into initial_…
kdmukai Oct 11, 2024
2c27de2
Merge commit '558e6c3e4e82494c8150ddcc33cff679350f55f9' into initial_…
kdmukai Oct 11, 2024
bf22113
Merge commit '5667cee489d627f4bf3da18fb5587e09ce26a708' into initial_…
kdmukai Oct 11, 2024
333b524
Merge commit '5bfad17e12d5ee34aa572d943a08049c71b8aba7' into initial_…
kdmukai Oct 11, 2024
fd4e0a4
Merge commit 'a14aa2f08972e72478f5d3a40a9c023205b8c299' into initial_…
kdmukai Oct 11, 2024
b5a47e9
Merge commit 'db16fa77f87914c0a51d96f2b5ea9fcc4d7ddf0e' into initial_…
kdmukai Oct 11, 2024
5929ecb
Merge commit '69cb6ac7fffc2fa1f2c7775eaf6762f0d5805fed' into initial_…
kdmukai Oct 11, 2024
39cf75e
Merge commit 'c12f64e1ecfa9fe33ba78b9c8e0dcc0f3a673c6f' into initial_…
kdmukai Oct 11, 2024
8e99e70
Merge commit '8e50f597d76958d3043e308cab82702a36a172aa' into initial_…
kdmukai Oct 11, 2024
7b7a69b
Merge commit '82bbd7d673d6f2726e2a976048bf8371d84ac7c4' into initial_…
kdmukai Oct 11, 2024
aae5124
Merge commit 'a446b67adb27f2d1d1c323db5c636269026d967e' into initial_…
kdmukai Oct 11, 2024
18b449c
Merge commit '1b9fc3f7642e292be0b2915fd2c90dc1ca35d96b' into initial_…
kdmukai Oct 11, 2024
5b88ce4
Merge commit 'b17677cfdd120ddaf2fb6b0a8ea3a822a8cd816a' into initial_…
kdmukai Oct 11, 2024
a47af33
Merge commit 'c7738c114c5675c164260676e39c1a7fc9d43db6' into initial_…
kdmukai Oct 11, 2024
c9c2c26
Merge commit 'e5d313360d3b9977eebe4f483b435f7b800d4e49' into initial_…
kdmukai Oct 11, 2024
310042d
Merge commit 'adbf5bc1967c8e91a3fa4cf2c7c6e9a4cb7342a4' into initial_…
kdmukai Oct 12, 2024
84ea603
Merge commit '62807fd6744165b672170cc3c3f030fe90ae41e2' into initial_…
kdmukai Oct 12, 2024
f0a5110
Merge commit 'cdf7fc202729a35945ae955d53b0bfe9e592e02a' into initial_…
kdmukai Oct 12, 2024
76d2e2e
Merge commit '59409bd76cd3be64de9854fb8272b7e111edd990' into initial_…
kdmukai Oct 12, 2024
576f847
Merge commit '9aa95e8c9283d85475f24519abce38d454cd5297' into initial_…
kdmukai Oct 12, 2024
ca59bac
Merge commit 'c94725e7fa6f4f9ef6c3c8fd33e11e454d9a5900' into initial_…
kdmukai Oct 12, 2024
d33bc0a
Merge commit 'c5e602f5a4fd9ccbce8c37a15f08b0bea787672d' into initial_…
kdmukai Oct 12, 2024
7ccdf56
Merge commit '2b42c436d4fc15552d63c6261386c0b0ffbae90b' into initial_…
kdmukai Oct 12, 2024
c464607
Merge commit '153af5125c0a20b2288df924e4fd7886ea323aad' into initial_…
kdmukai Oct 12, 2024
49c4556
Merge commit '49c4a4338a54b44248c7e68e26c3b5706e0eab64' into initial_…
kdmukai Oct 12, 2024
a14af18
Merge commit 'b07292f0e8f47a5c0564262865f80ded44313bc5' into initial_…
kdmukai Oct 12, 2024
df8fdac
Merge commit 'd973007c469799b22b703f1349e220eb93811bf4' into initial_…
kdmukai Oct 12, 2024
142b5b6
Merge commit 'dec01b292ac990ac32409088e73eca2edc1ef637' into initial_…
kdmukai Oct 12, 2024
e3e9b8c
Merge commit '724033ce66a97065a460b50d136073fec4c5a335' into initial_…
kdmukai Oct 24, 2024
62c84a3
Merge commit 'ad6f55ddbc15a285a82631a1ea0dfeb6db61641a' into initial_…
kdmukai Oct 24, 2024
6bddee3
Merge commit '3e0b69b2d31f012b9bdfc1f40ea5c19c972d72a0' into initial_…
kdmukai Oct 24, 2024
7bc936a
Merge commit '20acf302f6cb9092122770087d3bd7248a346820' into initial_…
kdmukai Oct 24, 2024
146db1d
Merge commit '0f2677291815184cc4f75b1b42cbbc385d82fa75' into initial_…
kdmukai Oct 24, 2024
bc12e68
Merge commit 'e16c853e6ea54ed6d697b0e6f710f221331d2c44' into initial_…
kdmukai Oct 24, 2024
0c75ae6
Merge commit '80ad7d610ac71e13ea472309dd1878c0804b7270' into initial_…
kdmukai Oct 24, 2024
1a29e7d
Merge commit '2c96e20afc23c9cf33117d0f6a738ec02435bcd0' into initial_…
kdmukai Oct 24, 2024
3b1e744
Merge commit '0f329f6fb568f1c7aaae6288ce33e38c566f3d1d' into initial_…
kdmukai Oct 24, 2024
ae77de5
Merge commit '0f329f6fb568f1c7aaae6288ce33e38c566f3d1d' into initial_…
kdmukai Oct 24, 2024
2cd5377
Merge commit '07f0bbf847d8ca41c5bc526d2c81a807c75a95d9' into initial_…
kdmukai Oct 24, 2024
e789e05
Merge commit '13e1d607054f38127be9d76d94c489e2efd773d8' into initial_…
kdmukai Oct 24, 2024
bea5d6c
Merge commit '5e4f6697318d0e4ba7998b15d30fae265cf6b4ed' into initial_…
kdmukai Oct 24, 2024
318587c
Merge commit '037702444f7957bf6354f82a185c35e0b3d03ad7' into initial_…
kdmukai Oct 24, 2024
4de6f58
Merge commit '34322ee04fa0ea3f3bc026118b1b714411ab0866' into initial_…
kdmukai Oct 24, 2024
a75343f
Merge commit '100a9740e70e3c7119eab0f8b0ad262f72a35b7e' into initial_…
kdmukai Oct 24, 2024
02ba581
Merge commit 'dc4c2c877e186ddf383b008942e4169738f9ff8d' into initial_…
kdmukai Oct 24, 2024
929fbed
Merge commit '794c25cfaf261b8f8f7df39e7f72d118b766787b' into initial_…
kdmukai Oct 24, 2024
50e230f
Merge commit '520a74df8640e31763aac12ba5242afddc82a9ff' into initial_…
kdmukai Oct 24, 2024
52652c7
Merge commit 'e50f23468ba671ff0ae7d973a14b3363eb454e16' into initial_…
kdmukai Oct 24, 2024
83f2dff
Merge commit 'ec68481869516376b77e06187975da399e329041' into initial_…
kdmukai Oct 24, 2024
3167d24
Merge commit 'cd3e0f3c2a3b16a2bd3fb844cb7028b6dec0e8b4' into initial_…
kdmukai Oct 24, 2024
69c8364
Merge commit 'c2a4a80d7fa2707e46e9e5e8e92469e1d1fb59c7' into initial_…
kdmukai Oct 24, 2024
9376bdb
Merge commit '0c477e27f558a55556b09575eda9d218f7f05e35' into initial_…
kdmukai Oct 24, 2024
2caa7ad
Merge commit '8cd8a5be2d9e6973db3b855541c65a0f2a6fdedf' into initial_…
kdmukai Oct 24, 2024
f64f394
Full l10n integration; Initial possible v0.8.5
kdmukai Nov 1, 2024
a353e26
cleanup
kdmukai Nov 1, 2024
e4c4cb4
import cleanup/isolation
kdmukai Nov 2, 2024
d412be2
Large refactor to add `ButtonOption` and `mark_for_translation`
kdmukai Nov 3, 2024
a213032
minor bugfix
kdmukai Nov 3, 2024
47044a0
docs update
kdmukai Nov 3, 2024
82df4bd
Coordinating latest to Transifex
kdmukai Nov 3, 2024
7920bbd
Minor text / translation fix
kdmukai Nov 3, 2024
47fe9e6
Screenshot generator improved integration with pytest
kdmukai Nov 5, 2024
f65ea6d
Update to match latest translations
kdmukai Nov 5, 2024
14ccab7
bugfix
kdmukai Nov 5, 2024
87003b4
misc TODO note
kdmukai Nov 5, 2024
e98eea8
Remove duplicate merge artifacts; additional screenshots
kdmukai Nov 5, 2024
c5dae84
Remove legacy Raspi OS Power Off
kdmukai Nov 5, 2024
5472add
test suite bugfixes
kdmukai Nov 5, 2024
7ee89c8
Minor cleanup
kdmukai Nov 5, 2024
28d21bb
Merge pytest results and screenshot generator coverage
kdmukai Nov 5, 2024
2cd3523
Adding `gui` modules back to coverage; basic l10n tests
kdmukai Nov 5, 2024
547b01d
Explanatory comment for legacy setup.py
kdmukai Nov 6, 2024
ae96508
Fix test runner CI integration
kdmukai Nov 6, 2024
03274aa
Adds `coverage` convenience script
kdmukai Nov 6, 2024
ab02028
Experiment to add coverage to test runner
kdmukai Nov 6, 2024
c57ae0a
Github Action coverage bugfix
kdmukai Nov 6, 2024
f5a359a
Fixes / improvements to CI coverage
kdmukai Nov 6, 2024
6199288
Coverage CI bugfix
kdmukai Nov 6, 2024
bb9dbf8
More CI fix attempts
kdmukai Nov 6, 2024
7989378
CI fix
kdmukai Nov 6, 2024
4a8145f
Coverage CI fix
kdmukai Nov 6, 2024
174597d
CI bugfix
kdmukai Nov 6, 2024
8eb886d
CI test runner bugfix, cleanup
kdmukai Nov 6, 2024
3e068b0
Updated coverage docs, script
kdmukai Nov 6, 2024
a47097d
Filter to get local coverage to match CI results
kdmukai Nov 6, 2024
7a8609f
better attempt at unifying CI and local results
kdmukai Nov 6, 2024
fae400d
Integration w/seedsigner-screenshots as a submodule
kdmukai Nov 6, 2024
0eeb751
Syncing submodules to latest repo updates.
kdmukai Nov 7, 2024
8559eaf
bugfix
kdmukai Nov 8, 2024
29cb0c7
bugfix
kdmukai Nov 8, 2024
6d19db1
l10n omission fix
kdmukai Nov 8, 2024
44e24bf
Adds `ScrollableTextLine`, scrolling `TopNav` titles
kdmukai Nov 8, 2024
ed53b90
Force `status_headline` to be one line
kdmukai Nov 8, 2024
3fade06
Move commit to match current screenshots
kdmukai Nov 8, 2024
80ea2c8
Modernize OpeningSplash, add to screenshots
kdmukai Nov 8, 2024
917a578
Interim commit
kdmukai Nov 12, 2024
72f945d
Minor existing bugfix
kdmukai Nov 12, 2024
e34ef66
cleanup, temp bugfix
kdmukai Nov 18, 2024
fff1a9a
Update settings_definition.py
kdmukai Nov 18, 2024
30bc020
removing screenshots from default submodule fetch
kdmukai Nov 18, 2024
71bc222
bugfix. Don't use "`_`" as a placeholder var!!!
kdmukai Nov 21, 2024
425acfb
Adds screenshots for passphrase entry, SeedQR transcription, addr ver…
kdmukai Dec 5, 2024
fcacbbb
Fix for when running screenshot generation for multiple locales
kdmukai Dec 6, 2024
6ee36a4
README typo fix
kdmukai Dec 11, 2024
e8f26b1
Screenshot generator fixes, refactor
kdmukai Dec 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:

steps:
- uses: actions/checkout@v3
with:
# Needs to also pull the seedsigner-translations repo
submodules: recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
Expand All @@ -45,16 +48,20 @@ jobs:
--cov=seedsigner \
--cov-append \
--cov-branch \
--cov-report term \
--cov-report html \
--cov-report html:./artifacts/cov_html \
--cov-report xml \
--durations 5 \
-vv
- name: Generate screenshots
run: |
python -m pytest tests/screenshot_generator/generator.py
python -m pytest tests/screenshot_generator/generator.py \
--color=yes \
--cov=seedsigner \
--cov-append \
--cov-branch \
--cov-report html:./artifacts/cov_html \
-vv
cp -r ./seedsigner-screenshots ./artifacts/
- name: Coverage report
run: coverage report
- name: Archive CI Artifacts
uses: actions/upload-artifact@v3
with:
Expand Down
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ src/seedsigner.egg-info/
.vscode
src/seedsigner/models/settings_definition.json
.idea
*.mo
.coverage
seedsigner-screenshots
.coverage*

*.po
*.mo
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "src/seedsigner/resources/seedsigner-translations"]
path = src/seedsigner/resources/seedsigner-translations
url = https://github.com/SeedSigner/seedsigner-translations.git
branch = dev
[submodule "seedsigner-screenshots"]
path = seedsigner-screenshots
url = https://github.com/SeedSigner/seedsigner-screenshots.git
branch = dev
update = none
316 changes: 316 additions & 0 deletions l10n/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
# Localization (l10n) Developer Notes

## High-level overview
1. Python code indicates text that needs to be translated.
1. Those marked strings are extracted into a master `messages.pot` file.
1. That file is uploaded [Transifex](https://app.transifex.com/seedsigner/seedsigner).
1. Translators work within Transifex on their respective languages.
1. Completed translations are downloaded as `messages.po` files for each language.
1. Python "compiles" them into `messages.mo` files ready for use.
1. The `*.po` and `*.mo` files are written to the [seedsigner-translations](https://github.com/SeedSigner/seedsigner-translations) repo.
1. That repo is linked as a submodule here as `seedsigner.resources.seedsigner-translations`.
1. Python code retrieves a translation on demand.


## "Wrapping" text for translation
Any text that we want to be presented in multiple languages needs to "wrapped".

The CORE CONCEPT to understand is that wrapping is used in TWO different contexts:
1. Pre-translation: This is how we identify text that translators need to translate. Any wrapped string literals will appear in translators' Transifex UI.
2. Post-translation: Return the locale-specific translation for that source string (defaults to the English string if no translation is found).

We have three techniques to wrap code, depending on which of the above contexts we're in and where we are in the code:


#### Technique 1: `ButtonOption`
Most `View` classes will render themselves via some variation of the `ButtonListScreen` which takes a `button_data` list as an input. Each entry in
`button_data` must be a `ButtonOption`. The first argument for `ButtonOption` is the `button_label` string. This is the English string literal that
is displayed in that button. If you look at `setup.cfg` you'll see that `ButtonOption` is listed as a keyword in `extract_messages`. That means
that the first argument for `ButtonOption` -- its `button_label` string -- will be marked for translation (by default the `extract_messages`
integration will only look at the first argument of any method listed in `keywords`).

```python
class SomeView(View):
# These string literals will be marked for translation
OPTION_1 = ButtonOption("Option 1!")
OPTION_2 = ButtonOption("Option 2!")

def run(self):
button_data = [self.OPTION_1, self.OPTION_2]

# No way for `extract_messages` to know what's in `some_var`; won't be marked for
# translation unless it's specified elsewhere.
some_var = some_value
button_data.append(ButtonOption(some_var))
```

These `ButtonOption` values are generally specified in class-level attributes, as in the above example. Classes in python are imported once, after
which class-level attributes are never reinterpreted again; **the value at import time for a class-level attribute is its value for the duration of
the program execution.**

This means that we must assume that `ButtonOption.button_label` strings are ALWAYS the original English string. This is crucial because the English
values are the lookup keys for the translations:

* `ButtonOption.button_label` = "Hello!" in the python code.
* Run the code, the class that contains our `ButtonOption` as a class-level attribute is imported.
* Regardless of language selection, that `ButtonOption` will always return "Hello!".
* `Screen` then uses "Hello!" as a key to find the translation "¡Hola!".
* User sees "¡Hola!".

IF `ButtonOption` were wired to return the translated string, we'd have a problem:
* User sets their language to Spanish and enables persistent settings.
* Launch SeedSigner. At import time the `button_label`'s value is translated to "¡Hola!".
* User sees "¡Hola!" in the UI. All good.
* User changes language to English (or any other language).
* Now the `Screen` must find the matching string in a different translation file.
* But the `button_label` value was fixed at import time; it's still providing "¡Hola!" as the lookup key.
* Since all the translation files map English -> translation, no such "¡Hola!" match exists in any translation file.
* So the translation falls back to just displaying the unmatched key: "¡Hola!"

tldr: `ButtonOption` marks its `button_label` English string literal for translation, but NEVER provides a translated value.

---

#### Technique 2: `seedsigner.helpers.l10n.mark_for_translation`
You'll see that `mark_for_translation` is imported as `_mft` for short.

As far as translations are concerned, `_mft` serves the same purpose as `ButtonOption`. The only difference is that `_mft` is for all other
(non-`button_data`) class-level attributes.

```python
from seedsigner.helpers.l10n import mark_for_translation as _mft

@classmethod
class SomeView(View):
title: str = _mft("Default Title")
text: str = _mft("My default body text")

def run(self):
self.run_screen(
SomeScreen,
title=self.title,
text=self.text
)
```

In general we try to avoid using `_mft` at all, but some class-level attributes just can't be avoided.

---

#### Technique 3: `gettext`, aka `_()`
This is the way you'll see text wrapping handled in the vast majority of tutorials.

```python
from gettext import gettext as _

my_text = _("Hello!")

# Specify Spanish
os.environ['LANGUAGE'] = "es"
print(my_text)
>> ¡Hola!

# Specify English
os.environ['LANGUAGE'] = "en"
print(my_text)
>> Hello!
```

This approach marks string literals for translation AND retrieves the translated text.

We do the same in SeedSigner code, but only when the string literal is in a part of the code that is dynamically evaluated:

```python
from gettext import gettext as _

class SomeView(View):
def __init__(self):
# Mark string literal for translation AND dynamically retrieve its translated value
self.some_var = _("I will be dynamically fetched")
```

Though note that there are times when we use `_()` only for the retrieval side:

```python
from seedsigner.helpers.l10n import mark_for_translation as _mft

class SomeView(View):
message = _mft("Hello!") # mark for translation, but always return "Hello!"

def run(self):
self.run_screen(
SomeScreen,
message=self.title
)

# elsewhere...
@dataclass
class SomeScreen(Screen):
message: str = None

def __post_init__(self):
message_display = TextArea(
text=_(self.message) # The _() wrapping here now retrieves the translated value, if one is available
)
```

---

## Basic rules
* English string literals in class-level attributes should be wrapped with either `ButtonOption` (for `button_data` entries) or `_mft` (for misc class-level attrs) so they'll be picked up for translation.
* English string literals anywhere else should be wrapped with `_()` to be marked for translation AND provide the dynamic translated value.
* In general, don't go out of your way to translate text before passing it into `Screen` classes.
* The `Screen` itself should do most of the `_()` calls to fetch translations for final display.
* Minor risk of double-translation weirdness otherwise.

Mark for translation in the `View`. Retrieve translated values in the `Screen`. Pass final display text into the basic gui `Component`s.

---

## Provide translation context hints
In many cases the English string literal on its own does not provide enough context for translators to understand how the word is being used.

For example, is "change" referring to altering a value OR is it the amount coming back to you in a transaction?

Whenever necessary, add explanatory context as a comment. This applies to all three ways of marking strings for translation.

The `extract_messages` command is explictly looking for the exact string: `# TRANSLATOR_NOTE:` in comments.

```python
class SeedAddressVerificationView(View):
# TRANSLATOR_NOTE: Option when scanning for a matching address; skips ten addresses ahead
SKIP_10 = ButtonOption("Skip 10")
```

Note that the comment MUST be on the preceding line of executable code for it to work:

```python
class SettingsConstants
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High <-- ✅ Correct way to add context
density_low = _mft("Low")

ALL_DENSITIES = [
(DENSITY__LOW, density_low),
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High <-- ❌ Note will NOT be picked up
(DENSITY__MEDIUM, "Medium"),
(DENSITY__HIGH, "High"),
]
```

```python
# TRANSLATOR_NOTE: Refers to the user's change output in a psbt
some_var = _("change")
```

---

## `_()` Wrapping syntax details
* Use `.format()` to wrap strings with variable injections. Note that `.format()` is OUTSIDE the `_()` wrapping.
```python
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)
```

The translators will only see: "My dad's name is {} and my name is {}" in Transifex. Often the English string literal is
basically incomprehensible on its own so always provide an explanation for what is being injected:

```python
# TRANSLATOR_NOTE: Address verification success message (e.g. "bc1qabc = seed 12345678's receive address #0.")
text = _("{} = {}'s {} address #{}.").format(...)
```

If there are a lot of variables to inject, placeholder names can be used (TODO: how does Transifex display this?):
```python
mystr = _("My dad's name is {dad_name} and my name is {my_name}").format(dad_name=dad.name, my_name=self.name)
```
* Use `ngettext` to dynamically handle singular vs plural forms based on an integer quantity:
```python
n = 1
print(ngettext("apple", "apples", n))
>> apple

n = 5
print(ngettext("apple", "apples", n))
>> apples
```

Transifex will ask translators to provide the singular and plural forms on a language-specific basis (e.g. Arabic as THREE plural forms!).

---

## Set up localization dependencies
```bash
pip install -r l10n/requirements-l10n.txt
```

Make sure that your local repo has fetched the `seedsigner-translations` submodule. It's configured to add it in src/seedsigner/resources.
```bash
# Need --remote in order to respect the target branch listed in .gitmodules
git submodule update --remote
```


### Pre-configured `babel` commands
The `setup.cfg` file in the project root specifies params for the various `babel` commands discussed below.

You should have already added the local code as an editable project in pip:
```bash
# From the repo root
pip install -e .
```


### Rescanning for text that needs translations
Re-generate the `messages.pot` file:

```bash
python setup.py extract_messages
```

This will rescan all wrapped text, picking up new strings as well as updating existings strings that have been edited.

_TODO: Github Action to auto-generate messages.pot and fail a PR update if the PR has an out of date messages.pot?_


### Making new text available to translators
Upload the master `messages.pot` to Transifex. It will automatically update each language with the new or changed source strings.

_TODO: Look into Transifex options to automatically pull updates?_


### Once new translations are complete
The translation file for each language will need to be downloaded via Transifex's "Download for use" option (sends you a `messages.po` file for that language).

This updated `messages.po` should be added to the seedsigner-translations repo in l10n/`{TARGET_LOCALE}`/LC_MESSAGES.


### Compile all the translations
The `messages.po` files must be compiled into `*.mo` files:

```bash
python setup.py compile_catalog

# Or target a specific language code:
python setup.py compile_catalog -l es
```

### Unused babel commands
Transifex eliminates the need for the `init_catalog` and `update_catalog` commands.


## Keep the seedsigner-translations repo up to date
The *.po files for each language and their compiled *.mo files should all be kept up to date in the seedsigner-translations repo.

_TODO: Github Actions automation to regenerate / verify that the *.mo files have been updated after *.po changes._

---

## Generate screenshots in each language
Simply run the screenshot generator:

```bash
pytest tests/screenshot_generator/generator.py

# Or target a specific language code:
pytest tests/screenshot_generator/generator.py --locale es
```
Loading
Loading