Ceļvedis¶
Šī lapa ir izpildlaika uzziņa: viss, ko ar šo bibliotēku dara jūsu lietotnes kods, tiklīdz katalogi pastāv. Ja vēl neesat redzējuši visu ciklu — atzīmēt, ekstrahēt, iztulkot, kompilēt, palaist —, pamācība to izstaigā vienreiz piecās minūtēs; katalogu izveidošana un validēšana ir aprakstīta Ekstrakcijā, bet tas, kā komanda tur ciklu griežamies — atjaunināšanas cikli, CI, tulkošanas platformas —, ir lapā Produkcijā.
Kuru ieejas punktu lietot?¶
Pakotne eksportē vairākus veidus, kā iztulkot ziņojumu, jo lietotnes valodu piesaista vairākos dažādos veidos. Izvēlieties pēc tā, kā jūsu programma izlemj, kurā valodā tā ir:
| Jūsu situācija | Lietojiet |
|---|---|
| Viena valoda visam procesam — CLI, darbvirsmas lietotne, skripts | Translator, izsaukts kā _ |
| Viena valoda katram pieprasījumam vai asinhronajam uzdevumam — tīmekļa lietotne | use_translations() ap darbu, tad tr() |
| Ziņojums, definēts importa laikā — formas uzraksts, enum, konstante | lazy_gettext() vai lazy_pgettext() |
| Formulējumu izlemj skaits | ngettext() / npgettext() jebkurā no augšminētajām formām |
| Raksta renderēšana bez jebkāda kataloga | compile_template() |
Viss tālāk ir šie pieci, tieši šādā secībā.
Kataloga piesaiste¶
Ieteicamā forma atspoguļo gettext klašu balstīto lietojumu: piesaistiet
standarta tulkojumu objektu vienreiz un lietojiet izsaucamo procesoru kā _.
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
Moduļa līmeņa funkcijas seko standarta bibliotēkas nosaukumiem un tās tikai pozicionālo argumentu izsaukšanas konvencijai:
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 un ntr ir precīzi gettext un ngettext aizstājvārdi.
Valoda katram pieprasījumam¶
Tīmekļa ietvars izvēlas valodu katram pieprasījumam. Piesaistiet pieprasījuma tulkojumus tekošajam kontekstam, un katrs moduļa līmeņa izsaukums atrisināsies uz šo valodu, droši arī vienlaicīgu pieprasījumu apstākļos:
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) piesaista bez with bloka — ietvariem, kas
paši pārvalda pieprasījuma dzīves ciklu; get_translations() nolasa tekošo
piesaisti. Skaidri norādīts arguments translations= vienmēr uzvar pār
kontekstu, bet nepiesaistīts konteksts atkāpjas uz standarta bibliotēkas
globāli instalētajām gettext funkcijām. Izstrādāti Flask un ASGI starpprogrammu
piemēri ir lapā Produkcijā.
Atliktā tulkošana¶
T-virkne notver savas vērtības nekavējoties, un tas ir nepareizi virknei, kas definēta importa laikā — formas uzrakstam, enum vērtībai, moduļa konstantei —, kurai jārenderējas tajā valodā, kas ir aktīva brīdī, kad to lieto.
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 renderējas caur str(), format() un f-virknēm, un salīdzinājumā
ir vienāds ar savu renderēto tekstu.
Apzināti nehešojams
LazyString teksts ir atkarīgs no aktīvās valodas, tāpēc hešs mainītos
līdz ar valodas pārslēgšanu un klusējot sabojātu jebkuru kopu vai vārdnīcu,
kas to tur. Ja jums vajadzīga atslēga, vispirms izsauciet str().
strict tiek izlemts tur, kur ziņojums ir uzrakstīts, nevis tur, kur tas
renderējas:
Atliktā virkne renderējas tur, kur tā galu galā tiek lietota — šablonā, formā,
žurnāla rindā —, un šī vieta reti zina, vai tas ir testa izpildījums vai
produkcija. Tieši strict=True padošana definīcijā ļauj to pašu izvēli —
skaļi CI, iecietīgi produkcijā —
attiecināt uz virkni, kas netiek renderēta savā izsaukuma vietā.
Daudzskaitļa formas ir atkarīgas no izpildlaika skaita, tāpēc renderējiet tās
nekavējoties ar ngettext tur, kur skaits ir zināms.
Vairākas valodas vienlaikus¶
Vienam pieprasījumam bieži vajag vairāk nekā vienu valodu: lapu, kas renderēta lasītājam un kas turklāt ierindo paziņojumu kontam, kuram iestatīta cita, vai kopsavilkumu, kas katru dalībnieku citē viņa paša valodā. Piesaistes iegulst viena otrā, un iekšējā bloka atstāšana atjauno ārējo.
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
Ejot cauri adresātu sarakstam, darbu paveic atliktās virknes: ziņojums ir uzrakstīts vienreiz, importēšanas laikā, un renderējas vienreiz katrā valodā.
SUBJECT = lazy_gettext(t"Your order shipped")
for user in users:
with use_translations(load_translations(user.locale)):
send(user.email, str(SUBJECT))
Piesaiste ir ContextVar, nevis steks, kas turēts uz koplietota objekta, tāpēc
pieprasījumi, kas pārklājas, nevar pārņemt cits cita valodu — arī tajā
gadījumā, kad tie savus blokus atstāj tādā secībā, kādā tajos iegāja, un
tieši šo pārklāšanos steks izdara nepareizi. Ielādēt katalogu katrai valodai ir
lēti: gettext.translation() parsē katru .mo vienreiz un izsniedz kopijas,
kas dala parsēto katalogu.
Vai darba pavediens manto piesaisti, ir atkarīgs no būvējuma
Kails threading.Thread vai ThreadPoolExecutor.submit sākas vai nu ar
izsaucēja konteksta kopiju, vai ar tukšu, un to, kurš no tiem, nosaka
sys.flags.thread_inherit_context — pēc noklusējuma patiess brīvpavedienu
būvējumos un aplams visur citur. Tāpēc viens un tas pats kods uz 3.14t
renderē piesaistīto valodu, bet uz 3.14 — procesa globālo katalogu.
Padodiet kontekstu, nevis paļaujieties uz noklusējumu:
asyncio.to_thread to jūsu vietā jau dara.
Lokāli ievērojošas vērtības¶
Šī bibliotēka izlemj, kur vērtība parādās iztulkotā ziņojumā. Tā nelokalizē
pašu vērtību. {amount:,.2f} ir Python formāta specifikācija ar fiksētu
uzvedību — komats ik pēc trim cipariem un punkts pirms decimāldaļas —, un tā
rada tās pašas rakstzīmes neatkarīgi no ziņojuma valodas:
Vācu valodā šo skaitli raksta 1.234,50, franču valodā 1 234,50, bet hindi
1234567 grupē kā 12,34,567, nevis 1,234,567. Skaitļi, valūtas, datumi,
laiki un mērvienības pieder Babel. Vispirms noformatējiet
vērtību, tad ielieciet gatavo virkni:
from babel.numbers import format_currency
total = format_currency(amount, "EUR", locale=locale)
tr(t"Your order comes to {total}")
Skaitāmā ziņojumā skaitlis dara divus darbus — tas izvēlas daudzskaitļa formu un parādās tekstā —, un lokalizēts tiek tikai otrais. Paturiet neapstrādāto skaitu izvēlei un padodiet noformatēto virkni attēlošanai:
from babel.numbers import format_decimal
shown = format_decimal(n, locale=locale)
_.ngettext(t"One file", t"{shown} files", n)
Formatēšana pirms izsaukuma ir arī tas, kas notur formāta specifikāciju ārpus kataloga: tulkotājs redz gatavu teksta gabalu, nevis skaitli plus norādījumus, kā to renderēt.
Kas notiek, kad katalogs ir kļūdains¶
Ja tulkojuma vietturi neatbilst avotam — trūkstošs, nezināms vai pārformatēts lauks, kas paslīdējis garām validācijai, no ar roku rediģēta MO, piegādātāja kataloga vai konveijera, kas izlaiž pārbaudītāju —, noklusējums ir renderēt avota ziņojumu, nevis izraisīt kļūdu. Tas atspoguļo paša gettext kontraktu, ka slikts katalogs nekad nesalauž lietotni.
Ja Hello {name} ir iztulkots kā こんにちは {nombre}, renderēšana izdodas un
uz gettext_tstrings žurnalizētāju aiziet viens brīdinājums:
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
Brīdinājums nostrādā vienreiz katram ziņojumam un rakstam, nevis vienreiz katrā renderēšanā, tāpēc sabojāts kataloga ieraksts nepārpludina žurnālu.
Testiem un CI varat izvēlēties skaļu kļūmi:
strict = Translator(translations, strict=True)
tr(t"Hello {name}", translations=translations, strict=True)
Tā pati meklēšana tad izraisa kļūdu, nesot to pašu teikumu bez puses par “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 ziņojumi ir rakstīti tam, kurš var rīkoties, un kataloga problēmas
gadījumā tas biežāk ir tulkotājs, nevis programmētājs — tāpēc tur, kur vietturis
izskatās klāt esošs, bet nav, ziņojums paskaidro, kāpēc, nevis atkārto, ka tā
trūkst. Pilnplatuma figūriekavas, dubultots {{name}}, neredzama nedalāmā
atstarpe, kirilicas burts latīņu burtu vidū: katram no tiem ir savs
formulējums, un tie kopā ar piemēriem ir uzskaitīti lapā
Tulkotājiem. Tā lapa ir rakstīta
tā, lai to varētu iedot cilvēkam, kurš rediģē .po.
Raksta renderēšana bez kataloga¶
compile_template atklāj to pašu mehānismu vienu līmeni zemāk: tas pārvērš
t-virkni par tās msgid plus piesaistītu vērtību kopu un renderē jebkuru rakstu,
ko tam padodat.
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 validē pēc tiem pašiem likumiem un neatbilstības gadījumā vienmēr
izraisa kļūdu. Šeit nav iecietīgā režīma: iecietība pastāv tāpēc, lai
kataloga meklēšana varētu degradēties uz avota tekstu, bet rakstam, ko esat
padevuši paši, nav no kā degradēties.
Drošība un tvērums¶
Šis ir derīgs:
Šie tiek noraidīti ar nolūku:
Vispirms aprēķiniet jēgpilnu vērtību:
Ierobežojums rada stabilas kataloga atslēgas, dod tulkotājiem noderīgus nosaukumus un neļauj iztulkotai virknei kļūt par izteiksmju valodu.
Garantija attiecas uz struktūru un formatējumu: tulkojums nekad netiek izvērtēts un nekad nevar pievienot piekļuvi atribūtiem, izsaukumus, konversijas vai formāta specifikācijas. Divas lietas paliek izsaucēja atbildībā tieši tāpat kā ar standarta bibliotēkas gettext — renderētās izvades atsoļošana tās saņēmējam (HTML, čaula, terminālis) un kataloga integritāte, jo naidīgs katalogs var atkārtot vietturi, lai uzpūstu izvades apjomu, un tas ir raksturīgi jebkurai uz vietturiem balstītai i18n.