Vadovas¶
Šis puslapis yra veikimo meto žinynas: viskas, ką jūsų programos kodas daro su šia biblioteka, kai katalogai jau egzistuoja. Jei dar nematėte viso ciklo — pažymėti, ištraukti, išversti, sukompiliuoti, paleisti — pamoka jį pereina vieną kartą per penkias minutes; katalogų kūrimas ir tikrinimas aprašytas Ištraukime, o kaip komanda tą ciklą sukioja — atnaujinimo ciklai, CI, vertimo platformos — yra Realioje aplinkoje.
Kurią įėjimo vietą rinktis?¶
Paketas siūlo kelis būdus pranešimui išversti, nes programos kalbą susieja keliais skirtingais būdais. Rinkitės pagal tai, kaip jūsų programa nusprendžia, kokia kalba ji kalba:
| Jūsų padėtis | Naudokite |
|---|---|
| Viena kalba visam procesui — CLI, darbalaukio programa, scenarijus | Translator, kviečiamas kaip _ |
| Po kalbą kiekvienai užklausai ar asinchroninei užduočiai — žiniatinklio programa | use_translations() aplink darbą, tada tr() |
| Pranešimas, apibrėžtas importo metu — formos etiketė, enum, konstanta | lazy_gettext() arba lazy_pgettext() |
| Kiekis lemia formuluotę | ngettext() / npgettext() bet kuria iš aukščiau nurodytų formų |
| Šablono atvaizdavimas visai be katalogo | compile_template() |
Visa, kas žemiau, yra tie penki dalykai ta pačia tvarka.
Katalogo susiejimas¶
Rekomenduojama forma atkartoja gettext klasėmis grįstą naudojimą: vieną kartą
susieti standartinį vertimo objektą ir naudoti iškviečiamą apdorotoją kaip _.
import gettext
from gettext_tstrings import Translator
translations = gettext.translation("messages", localedir="locales", languages=["ja"])
_ = Translator(translations)
name = "Ada"
print(_(t"Hello {name}")) # こんにちは Ada
n = 3
print(_.ngettext(t"One file", t"{n} files", n)) # picks the right plural form for n
filename = "report.txt"
print(_.pgettext("button", t"Open {filename}")) # "button" disambiguates homonyms
Modulio lygmens funkcijos laikosi standartinės bibliotekos pavadinimų ir jos tik pozicinių argumentų iškvietimo tvarkos:
from gettext_tstrings import gettext, ngettext, npgettext, pgettext
gettext(t"Hello {name}", translations=translations)
ngettext(t"One file", t"{n} files", n, translations=translations)
pgettext("button", t"Open {filename}", translations=translations)
npgettext("inbox", t"One message", t"{n} messages", n, translations=translations)
tr ir ntr yra tikslūs gettext ir ngettext sinonimai.
Kalba pagal užklausą¶
Žiniatinklio karkasas kalbą parenka kiekvienai užklausai. Susiekite užklausos vertimus su dabartiniu kontekstu, ir kiekvienas modulio lygmens iškvietimas išsispręs į tą kalbą — saugiai net ir lygiagrečioms užklausoms:
from gettext_tstrings import tr, use_translations
def handle(request):
name = request.user.display_name
translations = load_translations(request.locale)
with use_translations(translations):
return render(tr(t"Hello {name}"))
set_translations(translations) susieja be with bloko — karkasams, kurie
patys tvarko užklausos gyvavimo ciklą; get_translations() nuskaito dabartinį
susiejimą. Aiškiai nurodytas translations= argumentas visada nugali
kontekstą, o nesusietas kontekstas grįžta prie standartinės bibliotekos
globaliai įdiegtų gettext funkcijų. Išnagrinėti Flask ir ASGI tarpinės
programinės įrangos pavyzdžiai yra puslapyje
Realioje aplinkoje.
Atidėtas vertimas¶
T-eilutė savo reikšmes pagauna iš karto, o tai netinka eilutei, apibrėžtai importavimo metu — formos etiketei, išvardijimo reikšmei, modulio konstantai — kuri turi būti atvaizduota ta kalba, kuri aktyvi jos panaudojimo metu.
from gettext_tstrings import lazy_gettext, lazy_pgettext, use_translations
SAVE = lazy_gettext(t"Save changes") # defined once, at import
OPEN = lazy_pgettext("button", t"Open file")
with use_translations(japanese):
assert str(SAVE) == "変更を保存" # rendered here, in this language
LazyString atvaizduojama per str(), format() ir f-eilutes, o lygybėje
prilygsta savo atvaizduotam tekstui.
Tyčia neturi maišos
LazyString tekstas priklauso nuo aktyvios kalbos, todėl maiša pasikeistų
perjungus kalbą ir tyliai sugadintų bet kurią ją laikančią aibę ar žodyną.
Jei reikia rakto, pirma iškvieskite str().
strict nusprendžiama ten, kur pranešimas parašomas, o ne ten, kur jis
atvaizduojamas:
Atidėta eilutė atvaizduojama ten, kur galiausiai panaudojama — šablone, formoje,
žurnalo įraše — o ta vieta retai žino, ar tai testų paleidimas, ar reali
aplinka. Perdavus strict=True apibrėžimo vietoje, tas pats
garsiai CI, atlaidžiai realioje aplinkoje
pasirinkimas galioja ir eilutei, kuri atvaizduojama ne savo kvietimo vietoje.
Daugiskaitos formos priklauso nuo veikimo meto skaičiaus, todėl jas
atvaizduokite iš karto su ngettext ten, kur skaičius žinomas.
Kelios kalbos vienu metu¶
Vienai užklausai neretai reikia daugiau nei vienos kalbos: puslapis atvaizduojamas skaitytojui, o kartu į eilę statomas pranešimas paskyrai, kuriai nustatyta kita kalba, arba santrauka, cituojanti kiekvieną dalyvį jo paties kalba. Susiejimai dedami vienas į kitą, o išėjus iš vidinio bloko atkuriamas išorinis.
with use_translations(reader):
page = tr(t"Hello {name}")
with use_translations(recipient):
notice = tr(t"Hello {name}") # the recipient's language
footer = tr(t"Hello {name}") # the reader's again
Einant per gavėjų sąrašą darbą atlieka atidėtos eilutės: pranešimas parašomas vieną kartą, importavimo metu, ir atvaizduojamas po kartą kiekvienai kalbai.
SUBJECT = lazy_gettext(t"Your order shipped")
for user in users:
with use_translations(load_translations(user.locale)):
send(user.email, str(SUBJECT))
Susiejimas yra ContextVar, o ne dėklas, laikomas bendrame objekte, todėl
persidengiančios užklausos negali pasigriebti viena kitos kalbos — įskaitant
atvejį, kai jos iš savo blokų išeina ta pačia tvarka, kuria į juos įėjo:
būtent šį persipynimą dėklas apdoroja klaidingai. Įkelti katalogą kiekvienai
kalbai pigu: gettext.translation() kiekvieną .mo perskaito vieną kartą ir
dalija kopijas, kurios naudoja tą patį perskaitytą katalogą.
Ar darbinė gija paveldi susiejimą, priklauso nuo darinio
Plika threading.Thread ar ThreadPoolExecutor.submit pradeda arba nuo
iškvietėjo konteksto kopijos, arba nuo tuščio, o kuris iš jų —
sys.flags.thread_inherit_context: laisvų gijų dariniuose jis pagal
nutylėjimą teisingas, visur kitur — klaidingas. Todėl tas pats kodas su
3.14t atvaizduoja susietą kalbą, o su 3.14 — proceso globalų katalogą.
Perduokite kontekstą, užuot pasikliovę numatytąja elgsena:
asyncio.to_thread tai už jus jau padaro.
Lokalę atitinkančios reikšmės¶
Ši biblioteka sprendžia, kur reikšmė atsiranda išverstame pranešime. Pačios
reikšmės ji nelokalizuoja. {amount:,.2f} yra Python formato specifikacija su
fiksuota elgsena — kablelis kas tris skaitmenis ir taškas prieš dešimtaines —
ir ji pagamina tuos pačius simbolius, kad ir kokia kalba būtų pranešimas:
Vokiečiai tą skaičių rašo 1.234,50, prancūzai 1 234,50, o hindi kalboje
1234567 grupuojamas kaip 12,34,567, o ne 1,234,567. Skaičiai, valiutos,
datos, laikai ir matavimo vienetai priklauso Babel. Pirma
suformatuokite reikšmę, tada įdėkite jau baigtą eilutę:
from babel.numbers import format_currency
total = format_currency(amount, "EUR", locale=locale)
tr(t"Your order comes to {total}")
Skaičiuojamame pranešime skaičius atlieka du darbus — parenka daugiskaitos formą ir pasirodo tekste — o lokalizuojamas tik antrasis. Atrankai palikite žalią kiekį, o rodymui perduokite suformatuotą eilutę:
from babel.numbers import format_decimal
shown = format_decimal(n, locale=locale)
_.ngettext(t"One file", t"{shown} files", n)
Formatavimas prieš iškvietimą yra ir tai, kas laiko formato specifikaciją už katalogo ribų: vertėjas mato baigtą teksto gabalą, o ne skaičių su nurodymais, kaip jį atvaizduoti.
Kas nutinka, kai katalogas klaidingas¶
Jei vertimo vietaženkliai neatitinka pirminių — trūkstamas, nežinomas ar performatuotas laukas, prasprūdęs pro patikrą iš ranka taisyto MO, tiekėjo katalogo ar konvejerio, praleidžiančio tikrintuvą — pagal nutylėjimą atvaizduojamas pirminis pranešimas, o ne keliama klaida. Tai atkartoja paties gettext kontraktą, kad blogas katalogas niekada nesulaužo programos.
Kai Hello {name} išverstas kaip こんにちは {nombre}, atvaizdavimas pavyksta,
o į gettext_tstrings žurnalą patenka vienas įspėjimas:
WARNING gettext_tstrings: invalid translation for msgid 'Hello {name}'; using
source text: translation does not match the source placeholders: {name} is
missing; {nombre} is not in the source message
Įspėjimas suveikia po kartą kiekvienam pranešimui ir šablonui, o ne kiekvieną atvaizdavimą, todėl sugadintas katalogo įrašas neužtvindo žurnalo.
Testams ir CI galite pasirinkti garsų lūžimą:
strict = Translator(translations, strict=True)
tr(t"Hello {name}", translations=translations, strict=True)
Ta pati paieška tada kelia klaidą, nešančią tą patį sakinį, tik be dalies „using source text“:
>>> strict(t"Hello {name}")
Traceback (most recent call last):
...
gettext_tstrings.errors.InvalidTranslationError: translation does not match the
source placeholders: {name} is missing; {nombre} is not in the source message
Šie pranešimai parašyti tam, kas gali dėl jų ką nors padaryti, o katalogo
atveju tai dažniau vertėjas nei programuotojas — todėl ten, kur vietaženklis
atrodo esantis, bet jo nėra, pranešimas paaiškina kodėl, o ne pakartoja, kad jo
trūksta. Viso pločio skliaustai, padvigubintas {{name}}, nematomas nedalus
tarpas, kirilicos raidė tarp lotyniškų: kiekvienas turi savo formuluotę, o
sąrašas su pavyzdžiais yra puslapyje
Vertėjams. Tas puslapis parašytas
taip, kad jį būtų galima perduoti tam, kas redaguoja .po.
Šablono atvaizdavimas be katalogo¶
compile_template atveria tą pačią mechaniką vienu lygmeniu žemiau: ji paverčia
t-eilutę jos msgid'u ir susietų reikšmių rinkiniu bei atvaizduoja bet kurį jai
paduotą šabloną.
from gettext_tstrings import compile_template
name = "Ada"
compiled = compile_template(t"Hello {name}")
compiled.msgid # "Hello {name}"
compiled.placeholders # ("name",)
compiled.render("こんにちは {name}") # "こんにちは Ada"
render tikrina pagal tas pačias taisykles ir neatitikus visada kelia
klaidą. Nuolaidaus režimo čia nėra: nuolaidumas egzistuoja tam, kad katalogo
paieška galėtų nusileisti iki pirminio teksto, o šablonas, kurį patys perdavėte,
neturi nuo ko nusileisti.
Sauga ir apimtis¶
Šitaip galima:
O šitaip tyčia neleidžiama:
Pirma apskaičiuokite prasmingą reikšmę:
Šis apribojimas duoda stabilius katalogo raktus, suteikia vertėjams naudingus vardus ir neleidžia išverstai eilutei tapti reiškinių kalba.
Garantija apima struktūrą ir formatavimą: vertimas niekada nėra apskaičiuojamas ir niekada negali pridėti prieigos prie atributų, iškvietimų, konversijų ar formato specifikacijų. Du dalykai lieka kviečiančiojo atsakomybe — lygiai kaip ir su standartinės bibliotekos gettext: atvaizduotos išvesties ekranavimas pagal jos paskirties vietą (HTML, apvalkalas, terminalas) ir katalogo vientisumas, nes priešiškas katalogas gali kartoti vietaženklį, kad išpūstų išvesties dydį, o tai būdinga bet kokiam vietaženkliais grįstam i18n.