Migracja¶
Jeśli Twój projekt już używa gettext, o możliwości przyjęcia tej biblioteki decydują wąskie pytania: czy unieważnia katalogi, które masz, czy potrafi współistnieć z kodem, którego nie jesteś gotów zmienić, i jak dużo przejścia musi się wydarzyć naraz. Odpowiedzi, od najkrótszej:
| Pytanie | Odpowiedź |
|---|---|
Czy istniejące pliki .po i .mo nadal działają? |
Tak. Te same pliki, te same narzędzia. |
| Czy stare i nowe wywołania mogą żyć w jednym pliku? | Tak, i jedno odwzorowanie ekstraktora obsługuje oba. |
| Czy msgid się zmienia? | Nie przy .format(). Tak przy %-formacie. |
| Czy cały projekt musi przejść naraz? | Nie. Jedno miejsce wywołania to poprawna zmiana. |
| A co z Jinją, szablonami Django, JavaScriptem? | Nietknięte, te same katalogi. |
Reszta tej strony to szczegóły stojące za każdą z tych odpowiedzi.
Z .format(): msgid się nie zmienia¶
To przypadek, w którym migracja nie kosztuje prawie nic. Komunikat
str.format i komunikat t-stringowy wyprowadzają ten sam klucz katalogu, bo
w obu przypadkach kluczem jest tekst z pozostawionym w nim {name}:
# Before
_("Hello {name}").format(name=name)
# After — the msgid is still "Hello {name}"
tr(t"Hello {name}")
Istniejące tłumaczenie zostaje więc przypięte. Zaczynając od katalogu zawierającego
zmień wywołanie, wyodrębnij ponownie i zaktualizuj:
$ pybabel extract -F babel.cfg -o locales/messages.pot .
extracting messages from app.py (encoding="utf-8")
writing PO template file to locales/messages.pot
$ pybabel update -i locales/messages.pot -d locales
updating catalog locales/ja/LC_MESSAGES/messages.po based on locales/messages.pot
Wpis, który wraca, różni się dwiema liniami metadanych i niczym więcej — komentarzem-znacznikiem identyfikującym go jako komunikat t-stringowy oraz numerem linii źródłowej:
Żadnej flagi fuzzy, żadnego ponownego tłumaczenia, w żadnym języku.
Komunikat renderuje się natychmiast:
$ pybabel compile -d locales
compiling catalog locales/ja/LC_MESSAGES/messages.po to locales/ja/LC_MESSAGES/messages.mo
$ python app.py
こんにちは Ada
update --check zgłosi katalogi jako nieaktualne
Ten komentarz-znacznik i przesunięte numery linii wystarczą, by
pybabel update --check uznał, że katalog wymaga wygenerowania na nowo,
bo porównuje cały wpis, a nie tylko tłumaczenie. Uruchom prawdziwe
pybabel update w tym samym commicie co zmiana kodu i zacommituj katalogi
razem z nią — to ten sam nawyk, o który prosi już
bramka CI.
Z %-formatu: msgid się zmienia, więc tłumaczenia stają się fuzzy¶
Składnia printf mieszka wewnątrz komunikatu, więc jej zastąpienie przepisuje
klucz katalogu. Nie da się tego obejść i jest to uczciwy koszt porzucenia
%(name)s:
pybabel update rozpoznaje nowy komunikat jako bliskiego krewnego usuniętego
i przenosi stare tłumaczenie, oznaczając je jako fuzzy:
#. gettext-tstrings
#: app.py:4
#, fuzzy, python-brace-format, python-format
msgid "Hello {name}"
msgstr "こんにちは %(name)s"
O tym stanie trzeba wiedzieć trzy rzeczy:
- Nic nie psuje się w czasie działania. Wpisy fuzzy są wyłączane ze
skompilowanego
.mo, więc aplikacja renderuje tekst źródłowy, dopóki człowiek nie potwierdzi pary — ta sama degradacja, przez którą przechodzi każdy przeredagowany komunikat. - CI pozostaje zielone, dopóki są fuzzy. Kontroler symboli zastępczych
pomija wpisy fuzzy, dokładnie tak jak
msgfmt --check-format, bo wpis, który nie może dotrzeć do czasu działania, nie powinien wywalać budowania. W chwili, gdy tłumacz zdejmie flagę, wpis jest sprawdzany jak każdy inny — więc%(name)spozostawione w potwierdzonym tłumaczeniu zostaje wychwycone właśnie wtedy, czyli w momencie, w którym zaczęłoby się renderować. - Stara flaga
python-formatjedzie razem i powinna zostać usunięta wraz z flagąfuzzy, inaczejmsgfmt --check-formatbędzie dalej stosował reguły printf do komunikatu w formacie klamrowym.
Dla nazwanych symboli printf edycja jest mechaniczna — %(name)s staje się
{name} i nic więcej się nie zmienia — więc duży katalog to skryptowe
przejście, po którym następuje przegląd tłumacza, a nie ponowne tłumaczenie.
Pozycyjne %s mechaniczne nie jest: nie ma nazwy do przeniesienia, a jej wybór
jest właśnie sensem tej zmiany.
Migracja może więc postępować w tempie, na jakie pozwala przegląd: nieprzekonwertowany wpis fuzzy to widoczny kawałek pracy w katalogu, a nie zepsute budowanie.
Stare i nowe wywołania współistnieją¶
Ekstraktor, który czyta t-stringi, czyta też zwykłe wywołania gettext, więc jedno odwzorowanie obsługuje plik w trakcie migracji:
from gettext_tstrings import tr
from myapp.i18n import _
name = "Ada"
print(_("Save changes"))
print(tr(t"Hello {name}"))
Oba komunikaty lądują w tym samym szablonie i tylko ten t-stringowy niesie komentarz-znacznik włączający dodatkowe sprawdzanie tej biblioteki:
#: app.py:5
msgid "Save changes"
msgstr ""
#. gettext-tstrings
#: app.py:6
#, python-brace-format
msgid "Hello {name}"
msgstr ""
Rozpoznaje _(), cztery standardowe nazwy gettext, aliasy tr() / ntr()
oraz odroczone lazy_gettext() / lazy_pgettext(). Własną funkcję pomocniczą
trzeba nazwać w odwzorowaniu.
W czasie działania oba style są tak samo niezależne: gettext.translation()
zwraca jeden obiekt tłumaczeń, a zarówno _, jak i punkty wejścia tej
biblioteki czytają z niego.
Co się nie przenosi¶
- Języki szablonów.
{% trans %}Jinji2, tagi szablonów Django i ich ekstraktory Babel działają dalej bez zmian i dalej zasilają te same katalogi PO. T-stringi to składnia Pythona; dotyczą źródeł Pythona. - Twoje pliki katalogów. Żadnej zmiany formatu, żadnego nowego pliku, żadnego kroku konwersji.
- Twoja platforma tłumaczeniowa. Wymiana
.pojest identyczna, a flagapython-brace-format, którą niesie komunikat t-stringowy, to ta sama flaga, którą niesie komunikat.format()— więc kontrola jakości symboli zastępczych działa dalej. - Kod spoza Pythona. Katalog JavaScriptu czy C w tym samym projekcie pozostaje nietknięty.
Lista kontrolna migracji¶
- Dodaj rozszerzenie
babeltam, gdzie działapybabel, i zmień odwzorowaniepythonwbabel.cfgna metodęgettext_tstrings— jedno odwzorowanie obsłuży wtedy oba style, a-kdalej działa dla zwykłych wywołań. - Przekonwertuj najpierw miejsca wywołań
.format(). Wyodrębnij ponownie, uruchompybabel updatei zacommituj katalogi razem z kodem; nie spodziewaj się wpisów fuzzy. - Konwertuj miejsca wywołań
%-formatu partiami, które da się przejrzeć, przepisując przeniesione symbole zastępcze i zdejmując flagifuzzyorazpython-format. - Napraw to, co odrzuca ograniczenie: interpolacja musi być prostą nazwą, więc
t"Hello {user.name}"staje się najpierw zmienną lokalną. To edycja miejsca wywołania, nie katalogu. - Włącz
strict = truew odwzorowaniu ekstraktora, gdy przejście się skończy, tak by komunikat, którego nie da się wyodrębnić, wywalał budowanie, zamiast znikać z szablonu. - Dodaj kontrolę czasu działania z W produkcji:
wyrenderuj jeden komunikat na każdy wysyłany język przez ścisły
Translator.
Kroki 2 i 3 to zwykłe commity. Nic na tej liście nie wymaga dnia przełomu.