Dzisiaj krótki tekst, który może się przydać osobom przygotowującym tłumaczenie programu w Pythonie przy użyciu biblioteki gettext.
Zabrałem się niedawno za tłumaczenie kolejnej aplikacji, tym razem padło na wspominaną w ostatnim odcinku Programów Obadania Wartych apkę pogodową o nazwie Mousam.
Dość szybko natrafiłem na problem. Niektóre angielskie słowa, takie jak „hight”, „moderate” i „low” są używane w kilku miejscach i w zależności od kontekstu, należałoby przetłumaczyć używając różnych form gramatycznych.
Angielskie stringi są wpisane bezpośrednio w kod, a ich tłumaczeniem zajmuje się gettext na podstawie plików .po. Przykład:
def classify_uv_index(uv_index):
if uv_index <= 2:
return _("Low")
elif uv_index <= 5:
return _("Moderate")
elif uv_index <= 7:
return _("High")
Znak podkreślenia to wywołanie gettext z podanym stringiem do przetłumaczenia. Narzędzie o nazwie xgettext zbiera wszystkie te ciągi razem z informacją o ich położeniu (nazwa pliku i numer linii) w jeden plik mousam.pot, który służy jako podstawa do wszystkich tłumaczeń:
#: src/mousam.py:276 src/mousam.py:286 src/weatherData.py:120
#: src/weatherData.py:133 src/weatherData.py:142
msgid "High"
msgstr ""
#: src/mousam.py:277 src/mousam.py:287 src/weatherData.py:116
#: src/weatherData.py:129 src/weatherData.py:138
msgid "Low"
msgstr ""
Jak widać, każdy ze stringów z tego fragmentu jest użyty kilka razy, w dwóch różnych plikach.
To był pierwszy raz, gdy zajmowałem się tłumaczeniem już na etapie przygotowywania plików do przełożenia. Wszystkiego uczyłem się „po drodze” i najpierw miałem nadzieję, że różne wersji tłumaczenia tego samego ciągu można zdefiniować po prostu w pliku .po jeżeli będą miały podaną inną lokalizację.
Niestety przy budowaniu aplikacji wyskoczyły błędy duplikacji stringów i musiałem poszukać innego rozwiązania.
Zacząłem czytać dokumentację gettext i znalazłem informację o funkcji pgettext, pozwalającej na dodanie kontekstu dla ciągu do tłumaczenia. Przerobiłem odpowiednie fragmenty kodu zgodnie z tym, co wyczytałem, podając najpierw kontekst, a potem wersję angielską.
def classify_uv_index(uv_index):
if uv_index <= 2:
return gettext.pgettext("uvindex", "Low")
elif uv_index <= 5:
return gettext.pgettext("uvindex", "Moderate")
elif uv_index <= 7:
return gettext.pgettext("uvindex", "High")
Wygenerowałem xgettextem świeży plik mousam.pot, podając informację, jakich słów kluczowych ma szukać:
xgettext --keyword=_ --keyword=pgettext:1c,2 --output=po/mousam.pot -f po/POTFILES
Teraz w mousam.pot miałem już informacje o kontekście powtarzających się ciągów oznaczonych przez msgctxt:
#: src/weatherData.py:118
msgctxt "uvindex"
msgid "Low"
msgstr ""
#: src/weatherData.py:120
msgctxt "uvindex"
msgid "Moderate"
msgstr ""
#: src/weatherData.py:122
msgctxt "uvindex"
msgid "High"
msgstr ""
Na jego podstawie zaktualizowałem plik polskiego tłumaczenia przy użyciu:
msgmerge -D po -U pl.po mousam.pot
I mogłem już przetłumaczyć nowe stringi.
Po tych zmianach aplikacja budowała się bez problemów i odpalała z polskim tłumaczeniem. Niestety, wszystkie stringi, które w pl.po miały podany msgctxt wyświetlały się bez tłumaczenia, w wersji podanej w pliku .py.
Tu mnie wcięło na jakiś czas i szukałem pomocy w fediwersum i na discordzie. Nawet napisałem malutki programik, żeby sprawdzić, czy to, co robię w ogóle działa. Działało, a w tłumaczonej aplikacji już nie.
Przy okazji, żebym nie musiał w przyszłości wyszukiwać w dokumentacji: do wygenerowania plików .mo z tłumaczeniem używalnym przez aplikacje służy polecenie msgfmt po/pl.po -o po/pl/LC_MESSAGES/nazwa_apki.mo
.
Wreszcie na Polskim forum Pythona dostałem podpowiedź, że może brakować prawidłowego ustawienia opcji biblioteki gettext. Okazało się, że brakowało mi dwóch linijek:
gettext.bindtextdomain('mousam', localedir)
gettext.textdomain('mousam')
Wcześniej tego nie zauważyłem, bo w mousam.in są prawie identyczne linijki dla biblioteki locale (locale.bindtextdomain i locale.textdomain), a ja po pobieżnym rzucie okiem wziąłem je za te od gettextu. Po dodaniu i ponownym zbudowaniu aplikacji wszystko zaczęło działać i ciągi ze zdefiniowanym kontekstem były również przetłumaczone.
Zachęcony tym sukcesem przejrzałem jeszcze cały program, wyszukałem stringi, które nie były oznaczone jako przetłumaczalne i dodałem do nich wywołania gettext, zaktualizowałem plik .pot i przetłumaczyłem nowe ciągi.

Na koniec wysłałem wrzuciłem wszystkie te zmiany do forka Mousam, przygotowałem pull request i po zatwierdzeniu przez developera miałem robotę z głowy, a inne osoby tłumaczące ten program będą mogły wykorzystać to, co przygotowałem do poprawy swoich przekładów.
Dodaj komentarz