Kodėl t-eilutės¶
Keturi būdai įdėti reikšmę į verstiną pranešimą, palyginti ant to paties pranešimo. Visi keturi savo vietaženklius pavadina ir leidžia vertėjui juos perstatyti; jie skiriasi tuo, kas nutinka, kai vertimas klaidingas, kiek jūsų programos katalogas gali pasiekti ir kiek kainuoja juos perimti.
Pirmiausia eina lentelės, kad rastumėte jus dominančią eilutę ir perskaitytumėte tik už jos esantį skyrių.
Kiekvieną išverstą pranešimą paliečia trys šalys
Katalogas yra vertimų failas — .po, kol jį redaguoja žmonės, ir
sukompiliuotas į .mo, kad programa jį įkeltų (pamoka
pereina abu). Kiekvieną pranešimą paliečia trys šalys: programuotojas
parašo pirminę eilutę, vertėjas redaguoja katalogą — dažnai išorinėje
platformoje, toli nuo bet kokios kodo peržiūros — o programa veikimo
metu abu atvaizduoja kartu. Kiekvienas žemiau esantis formatavimo stilius
į tą patį klausimą atsako kitaip: kiek formatavimo kalbos katalogui
leidžiama valdyti? Pavyzdžiuose _ yra įprastas vertimo funkcijos
pavadinimas, o tr — šios bibliotekos.
Greta¶
Kai vertėjas suklysta. Katalogas pereina daugybę rankų, ir dauguma jame atsirandančių bėdų yra atsitiktinės:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| Vertimas praleidžia vietaženklį — kas atvaizduojama? | reikšmė tyliai dingsta | reikšmė tyliai dingsta | reikšmė tyliai dingsta | pirminis pranešimas, su įspėjimu (pagal nutylėjimą) |
| Vertimas prideda nežinomą vietaženklį — kas atvaizduojama? | išimtis | išimtis | vietaženklis lieka matomas kaip tekstas | pirminis pranešimas, su įspėjimu (pagal nutylėjimą) |
| Vertimas performatuoja vietaženklį — kas atvaizduojama? | tai, ko paprašė katalogas, arba išimtis, jei tipo raidė reikšmei nebetinka | tai, ko paprašė katalogas | $ eilutėmis neišreiškiama |
pirminis pranešimas, su įspėjimu |
| Ar vietaženkliai tikrinami atvaizdavimo metu? | ne | ne | ne | taip (žr. žemiau) |
Kokią galią turi katalogas. Vertimas yra duomenys iš už jūsų saugyklos ribų, ir kiekvienas stilius jam suteikia skirtingą galios kiekį:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| Iš kur ateina reikšmės? | iš aiškaus atvaizdžio | iš aiškių argumentų | iš kviečiančiojo vietinių ir globalių kintamųjų bei neprivalomo extras |
iš reikšmių, pagautų t-eilutės viduje |
| Ar katalogas gali pakeisti reikšmės formatavimą? | taip | taip | ne | ne |
| Ar katalogas gali siekti į objektų vidų (prieiga prie atributų)? | ne | taip | taip, su taškiniais vardais | ne |
| Kur gyvena „dabartinė kalba“? | ten, kur ją padeda programa | ten, kur ją padeda programa | kalbų kodų dėkle, esančiame bendrame programos objekte | ContextVar kintamajame, kiekvienai užduočiai ar užklausai atskirai |
Kiek kainuoja integravimas. Visa, kas aukščiau, yra nemokama, jei įrankiai tinka; štai kur jie gali netikti:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| Mažiausia Python versija | bet kuri | bet kuri | 3.10 | 3.14 |
| Branda | standartinė biblioteka | standartinė biblioteka | stabilus leidimas | alfa |
| Ar naudoja įprastus PO/MO katalogus? | taip | taip | taip | taip |
| Ar reikia savo pirminio kodo ištraukiklio? | ne | ne | ne | taip, šiuo metu |
| Kokią PO žymą Babel nustato, kad jau turimi įrankiai galėtų tikrinti? | python-format |
python-brace-format |
jokios | python-brace-format |
Dėl atvaizdavimo meto patikros: vienaskaitos pranešimai tikrinami dėl tikslaus vietaženklių sutapimo. Daugiskaitos pranešimai taip pat tikrinami — pagal sąjungos/sankirtos taisyklę, kuri leidžia tikslinės kalbos daugiskaitos formoms skirtis nuo pirminės kalbos; griežtesnė kiekvienos formos patikra vykdoma kompiliuojant katalogus (Ištraukimas).
Formato žymos eilutė kalba apie vietaženklius suprantantį tikrinimą, o ne apie
katalogų suderinamumą. jokios reiškia, kad standartiniai gettext įrankiai vis
tiek perskaito ir sukompiliuoja pranešimą, bet msgfmt --check-format neturi
jokios $ vietaženklių gramatikos, kurią galėtų pritaikyti.
Suderinamumas ir branda¶
Pirmosios dvi paskutinės lentelės eilutės yra tos, kurios nulemia perėmimą, todėl jas verta pasakyti aiškiai, o ne langeliais.
%-formatas ir .format() yra įmontuoti į Python ir jiems apskritai nereikia
jokios priklausomybės. flufl.i18n yra brandus paketas — išleistas
ir naudojamas produkcijoje — veikiantis su Python 3.10 ir naujesniais.
gettext-tstrings yra alfa ir reikalauja Python 3.14 ar naujesnio, nes
t-eilutės yra nauja 3.14 sintaksė — atgalinio perkėlimo nėra ir negali būti. Jo
specifikacija yra stabilioji dalis; Python API iki 1.0 dar gali
pasislinkti.
Ko nė vienas iš jų nekainuoja, tai katalogų suderinamumo. Visi keturi pagamina įprastus POT/PO/MO failus, kuriuos jau skaito kiekvienas PO redaktorius, vertimo platforma ir GNU gettext įrankis, tad žemiau aprašomas pasirinkimas yra atšaukiamas taip, kaip katalogų formatų keitimas nebūtų. Migracija apima esamo projekto perkėlimą.
Žemiau esantys skyriai kiekvieną kompromisą parodo išsamiai, po vieną būdą iš eilės.
%-formatas¶
Kas gali nutikti: sugadintas vietaženklis virsta veikimo meto išimtimi, nebent katalogo tikrinimas pagauna jį anksčiau.
Katalogo eilutė neša printf sintaksę, įskaitant pabaigoje esančią tipo raidę —
tą s viduje %(name)s — kurią lengva pražiūrėti ir lengva sugadinti:
>>> "Hello %(name)" % {"name": "Ada"} # the trailing "s" was deleted
Traceback (most recent call last):
...
ValueError: incomplete format
Vieno simbolio pataisa PO redaktoriuje virsta veikimo meto išimtimi, nebent
katalogo tikrinimas ją pagauna anksčiau. GNU msgfmt --check-format šitą
pagauna, bet tik pranešimams, pažymėtiems
python-format, ir tik jei katalogas pakeliui į jūsų programą iš tikrųjų
praeina pro msgfmt.
str.format¶
Tai pašalina pabaigos tipo raidę, išlaikant pavadintą, laisvai perstatomą vietaženklį. Tai, kas gali nutikti, persikelia į kitą mainų pusę: vertimas įgyja galią jūsų objektams.
str.format yra nedidelė reiškinių kalba, o jos iškvietimas eilutei reiškia
tos teisės perdavimą tai eilutei:
>>> "{name.__class__.__mro__}".format(name="Ada")
"(<class 'str'>, <class 'object'>)"
>>> settings.api_key = "sk-live-…"
>>> "{conf.api_key}".format(conf=settings)
'sk-live-…'
Dabar pakeiskite tas literalines eilutes tuo, ką grąžina _(). Jei
Hello {name} vertimas grįžta kaip {conf.api_key}, jo atvaizdavimas
išspausdina jūsų API raktą — ką nuskaityti, nusprendė katalogas, o ne jūsų
kodas. Katalogas nėra kodas, bet jis keliauja kaip duomenys: į vertimo
platformą, per kelias rankas, atgal kaip .po, sukompiliuojamas į .mo,
kartais visai iš už jūsų projekto ribų atsineštas. .format() kiekvienam to
kelio žingsniui suteikia prieigą prie perduodamų objektų atributų.
$ eilutės ir flufl.i18n¶
from flufl.i18n import initialize
_ = initialize("example")
name = "Ada"
print(_("Hello $name")) # Hello Ada — the value came from the caller's locals
Standartinės bibliotekos string.Template pateikia $name
interpoliacijos kalbą, tačiau pati nėra vertimo API. flufl.i18n
sujungia tą stilių su gettext katalogo paieška. Atkreipkite dėmesį, kad reikšmė
niekada neperduodama: flufl.i18n sudaro pakeitimų vardų erdvę iš
kviečiančiojo globalių ir vietinių kintamųjų — pranešimui prieinami visi
kintamieji, esantys iškvietimo vietoje. Neprivaloma extras atvaizdis
pirmenybę turi prieš abu. Jos vertėjui matoma sintaksė neturi nei pabaigos tipo
raidės, nei formato specifikacijos, o vietaženkliai lieka laisvai perstatomi.
Neprieinamas pakeitimas nesukelia klaidos. Kai name = "Ada", o
kviečiančiojo vardų erdvėje nėra nombre, katalogo vertimas Hello $nombre
atvaizduojamas kaip Hello $nombre: neišspręstas vietaženklis lieka matomas.
Ta dokumentuota elgsena išsaugo likusią išversto
pranešimo dalį, užuot sužlugdžiusi iškvietimą. Išimtys, kylančios sprendžiant
atributą ar konvertuojant reikšmę, vis tiek gali sklisti aukštyn.
flufl.i18n už plikąjį string.Template galingesnis vienu svarbiu aspektu.
Jo savas Template priima vietaženklius su taškais, tokius
kaip $settings.api_key, o jo vertėjas tuos kelius išsprendžia
pagal kviečiančiojo reikšmes. Išverstas vietaženklis gali įvardyti bet kurį
prieinamą kviečiančiojo vietinį ar globalų kintamąjį ir, su taškine sintakse,
keliauti per jo atributus. Tai patogu, kai pranešimui reikia atributo, tačiau kartu
kviečiančiojo rėmelis tampa katalogo pakeitimų vardų erdvės dalimi. Šis
palyginimas apibūdina flufl.i18n 6.0.0, o ne visus įmanomus
string.Template naudojimo būdus.
Jis atsako ir į klausimą, kurį kiti du formatavimo stiliai visiškai palieka
programai: kuri kalba yra dabartinė ir kaip ją pakeisti. Programos
objektas laiko kalbų dėklą, _.push(code) ir _.pop()
jį stumdo, with _.using(code): gali būti dedamas vienas į kitą, o
strategija pagal kalbos kodą suranda katalogą, tad pati programa
katalogo objektų niekada netvarko. Serveris, kuriam per vieną darbo vienetą
reikia parengti tekstą daugiau nei viena kalba — puslapį skaitytojui ir
pranešimą tam, kurio paskyroje nustatyta kitaip — ir yra tas atvejis, dėl
kurio visa tai egzistuoja.
Tas dėklas gyvena programos objekte, kuriuo dalijasi visas procesas. Todėl dvi persidengiančios užklausos naudoja tą patį dėklą, o blokai, kurie laike nėra griežtai vienas kitame, perduoda vienas kitam ne tą kalbą:
async def greet(code, delay):
with _.using(code):
await asyncio.sleep(delay)
return _("Hello $name")
async def main():
return await asyncio.gather(greet("fr", 0.01), greet("ja", 0.02))
>>> asyncio.run(main()) # "fr" entered first and left first, so it read "ja" off the top
['こんにちは Ada', 'Bonjour Ada']
Ši biblioteka tą pačią galimybę — susiejimai dedami vienas į kitą ir
atvyniojami lygiai taip pat — laiko ContextVar kintamajame, o ne bendrame
dėkle, todėl aukščiau parodytas persipynimas išsprendžiamas kiekvienai
užduočiai atskirai. Atitikmenys yra puslapyje
Kelios kalbos vienu metu. Ko ji
nepateikia, tai paieškos nuo kalbos kodo iki katalogo: jūs perduodate vertimų
objektą, o įprastu atveju tai vienas gettext.translation() iškvietimas, ir
perskaitytą katalogą kešuoja pati standartinė biblioteka.
t-eilutės¶
Katalogas vis dar mato Hello {name} ir lieka įprastu PO/MO katalogu.
Skirtumas yra tas, ką vertimui leidžiama pasakyti ir kas tai tikrina.
Ši biblioteka prieš atvaizduodama patikrina kiekvieną vertimą pagal pirminio
pranešimo vietaženklius ir priima tik plikus vardus, nieko daugiau. Prieš
t"Hello {name}":
| Vertimas, kuriame yra | atmetamas su |
|---|---|
{name.__class__.__mro__} |
placeholder {name.__class__.__mro__} must be a plain name, copied from the source message unchanged |
{name!r} |
placeholder {name} adds formatting; write {name} on its own, because the source message decides how the value is formatted |
{0} |
placeholder {0} must be a plain name, copied from the source message unchanged |
{nombre} |
translation does not match the source placeholders: {name} is missing; {nombre} is not in the source message |
Atmesta nereiškia sudužo: pagal nutylėjimą biblioteka užrašo įspėjimą ir atvaizduoja pirminį pranešimą, todėl blogas katalogas niekada nepargriauna programos — toks pat kontraktas, kokio laikosi pats gettext.
Formatavimas lieka ten, kur buvo parašytas — kode:
:,.2f niekada nepasiekia katalogo, todėl joks vertimas negali jo pakeisti ir
jokiam vertėjui nereikia į jį žiūrėti. Vis dėlto tai fiksuotas, o ne
lokalizuotas formatas — skaitmenų ir skirtukų parinkimas pagal kalbą yra
Babel darbas, atliekamas prieš iškvietimą.
Dar vienas skirtumas yra įrankiai: t-eilutės yra nauja sintaksė, todėl jų
ištraukimui į .pot šiuo metu reikia t-eilutes suprantančio ištraukiklio,
tokio kaip tas, kurį šis paketas pateikia Babel'iui.
Apribojimo kaina¶
Be Python versijos reikalavimo, viso to kaina yra viena taisyklė: interpoliacija turi būti paprastas vardas.
Tai tikras apribojimas — ir būtent tas pats apribojimas pagamina aukščiau išvardytas garantijas. Kartu su reikšmių susiejimu pirminiame kode ir vietaženklių tikrinimu veikimo metu jis neleidžia katalogo eilutėms skaičiuoti reiškinių ir išlaiko vietaženklių vardus prasmingus tam, kas juos verčia.
F-eilutės taip panaudoti apskritai neįmanoma — tuo metu, kai ją pamato bet kuri biblioteka, ji jau yra baigta eilutė, tad jos vertimas reiškia nuotrupos vertimą. T-eilutės (PEP 750) laiko statinį tekstą ir reikšmes atskirai, išlaikydamos į f-eilutes panašią sintaksę ir aiškų reikšmių susiejimą.
Kaip Python čia atsidūrė — du PEP'ai, parašyti su dešimties metų tarpu, ir standartinės bibliotekos diskusija, užsibaigusi be atsakymo — papasakota su šaltiniais puslapyje Ištakos.