Ruka hadi kwenye maudhui

Kwa nini t-strings

Njia nne za kuweka thamani ndani ya ujumbe unaotafsirika, zikilinganishwa kwenye ujumbe uleule. Zote nne huyapa vishika nafasi vyake majina na humruhusu mfasiri kuvipanga upya; hutofautiana katika kinachotokea tafsiri inapokuwa na kasoro, katika kiasi cha programu yako ambacho katalogi inaweza kukifikia, na katika gharama ya kuzitumia.

Majedwali huja kwanza, ili uweze kupata safu inayokuhusu na kusoma sehemu iliyo nyuma yake pekee.

Pande tatu hugusa kila ujumbe uliotafsiriwa

Katalogi ni faili la tafsiri — .po wakati binadamu wanalihariri, likikusanywa kuwa .mo ili programu ilipakie (mafunzo hupitia zote mbili). Pande tatu hugusa kila ujumbe: msanidi huandika mfuatano chanzo, mfasiri huhariri katalogi — mara nyingi kwenye jukwaa la nje, mbali na mapitio yoyote ya msimbo — na programu huvionyesha vyote viwili pamoja wakati wa utekelezaji. Kila mtindo wa uumbizaji hapa chini hujibu swali lilelile kwa njia tofauti: ni kiasi gani cha lugha ya umbizo ambacho katalogi hupewa kudhibiti? Katika mifano, _ ni jina la kawaida la kitendakazi cha kutafsiri, na tr ni la maktaba hii.

Ubavu kwa ubavu

Mfasiri anapofanya kosa. Katalogi husafiri kupitia mikono mingi, na mengi ya yanayoharibika ndani yake ni ya bahati mbaya:

%(name)s .format() flufl.i18n $name t"…"
Tafsiri huondoa kishika nafasi — nini huonyeshwa? thamani hutoweka kimyakimya thamani hutoweka kimyakimya thamani hutoweka kimyakimya ujumbe chanzo, pamoja na onyo (kwa chaguo-msingi)
Tafsiri huongeza kishika nafasi kisichojulikana — nini huonyeshwa? hitilafu hitilafu kishika nafasi hubaki kikionekana kama maandishi ujumbe chanzo, pamoja na onyo (kwa chaguo-msingi)
Tafsiri huumbiza upya kishika nafasi — nini huonyeshwa? kile katalogi ilichoomba, au hitilafu ikiwa herufi ya aina haiendani tena na thamani kile katalogi ilichoomba hakielezeki katika $-strings ujumbe chanzo, pamoja na onyo
Je, vishika nafasi hukaguliwa wakati wa kuonyesha? hapana hapana hapana ndiyo (ona hapa chini)

Mamlaka ambayo katalogi inayo. Tafsiri ni data inayotoka nje ya hazina yako, nao kila mtindo hupewa kiasi tofauti cha nguvu:

%(name)s .format() flufl.i18n $name t"…"
Thamani hutoka wapi? ramani iliyo wazi hoja zilizo wazi vigezo vya ndani na vya jumla vya kinachoita, pamoja na extras ya hiari thamani zilizonaswa ndani ya t-string
Je, katalogi inaweza kubadilisha jinsi thamani inavyoumbizwa? ndiyo ndiyo hapana hapana
Je, katalogi inaweza kufikia ndani ya vitu (ufikiaji wa sifa)? hapana ndiyo ndiyo, kwa majina yenye vitone hapana
"Lugha ya sasa" hukaa wapi? popote programu inapoiweka popote programu inapoiweka rundo la misimbo ya lugha kwenye kitu cha programu kinachoshirikiwa ContextVar, kwa kila kazi au ombi

Gharama ya kuunganisha. Kila kilicho hapo juu ni bure ikiwa zana zinafaa; hapa ndipo huenda zisifae:

%(name)s .format() flufl.i18n $name t"…"
Python ya chini kabisa yoyote yoyote 3.10 3.14
Ukomavu maktaba sanifu maktaba sanifu toleo thabiti alpha
Je, hutumia katalogi za kawaida za PO/MO? ndiyo ndiyo ndiyo ndiyo
Je, huhitaji kitoaji maalum cha chanzo? hapana hapana hapana ndiyo, kwa sasa
Ni bendera gani ya PO ambayo Babel huidokeza, ili zana zilizopo zithibitishe? python-format python-brace-format hakuna python-brace-format

Kuhusu ukaguzi wa wakati wa kuonyesha: jumbe za umoja hukaguliwa kwa ulinganifu kamili wa vishika nafasi. Jumbe za wingi hukaguliwa pia, dhidi ya kanuni ya muungano/mwingiliano inayoruhusu maumbo ya wingi ya lugha lengwa kutofautiana na ya chanzo; ukaguzi mkali zaidi wa kila umbo huendeshwa katalogi zinapokusanywa (Utoaji).

Safu ya bendera ya umbizo inahusu uthibitishaji unaotambua vishika nafasi, si uoanifu wa katalogi. hakuna humaanisha zana sanifu za gettext bado husoma na kukusanya ujumbe, lakini msgfmt --check-format haina sarufi ya kishika nafasi cha $ ya kuitumia.

Uoanifu na ukomavu

Safu mbili za kwanza za jedwali la mwisho ndizo huamua matumizi, hivyo zinastahili kusemwa waziwazi badala ya kama visanduku.

%-format na .format() zimejengwa ndani ya Python nazo hazihitaji utegemezi wowote. flufl.i18n ni kifurushi kilichokomaa, kilichotolewa na kinachotumika katika uzalishaji, kinachoendeshwa kwenye Python 3.10 na baadaye. gettext-tstrings ni alpha nayo huhitaji Python 3.14 au mpya zaidi, kwa sababu t-strings ni sintaksia mpya katika 3.14 — hakuna urudishaji nyuma wala hauwezi kuwepo. Ainisho lake ndiyo sehemu yake thabiti; API ya Python bado inaweza kuhama kabla ya 1.0.

Kile ambacho hakuna hata mmoja wao anakigharimu ni uoanifu wa katalogi. Zote nne huzalisha mafaili ya kawaida ya POT/PO/MO ambayo kila kihariri cha PO, kila jukwaa la tafsiri, na kila zana ya GNU gettext tayari husoma, hivyo chaguo lililo hapa chini linaweza kurudishwa nyuma kwa namna ambayo kubadilisha miundo ya katalogi lisingeweza. Uhamiaji hufafanua kuhamisha mradi uliopo.

Sehemu zilizo hapa chini huonyesha kila muamala kwa kina, njia moja baada ya nyingine.

%-format

_("Hello %(name)s") % {"name": name}

Kinachoweza kwenda vibaya: kishika nafasi kilichoharibika huwa hitilafu ya wakati wa utekelezaji, isipokuwa uthibitishaji wa katalogi ukikinasa kwanza.

Mfuatano wa katalogi hubeba sintaksia ya printf, ikiwa ni pamoja na herufi ya aina mwishoni — ile s ndani ya %(name)s — ambayo ni rahisi kuipuuza na rahisi kuiharibu:

>>> "Hello %(name)" % {"name": "Ada"}  # the trailing "s" was deleted
Traceback (most recent call last):
  ...
ValueError: incomplete format

Uhariri wa herufi moja katika kihariri cha PO huwa hitilafu ya wakati wa utekelezaji isipokuwa uthibitishaji wa katalogi uinase kwanza. GNU msgfmt --check-format huikamata hii kweli, lakini kwa jumbe zilizowekwa alama ya python-format tu, na tu ikiwa katalogi hupita kwelikweli kwenye msgfmt njiani kuelekea programu yako.

str.format

_("Hello {name}").format(name=name)

Huondoa herufi ya aina ya mwishoni huku ikihifadhi kishika nafasi chenye jina kinachoweza kupangwa upya kwa uhuru. Kinachoweza kwenda vibaya huhamia upande mwingine wa muamala: tafsiri hupata nguvu juu ya vitu vyako.

str.format ni lugha ndogo ya misemo, na kuiita juu ya mfuatano kunamaanisha kuukabidhi mfuatano huo haki ya kuitumia:

>>> "{name.__class__.__mro__}".format(name="Ada")
"(<class 'str'>, <class 'object'>)"
>>> settings.api_key = "sk-live-…"
>>> "{conf.api_key}".format(conf=settings)
'sk-live-…'

Sasa badilisha mifuatano hiyo halisi na chochote _() inachorudisha. Ikiwa tafsiri ya Hello {name} inarudi kama {conf.api_key}, kuionyesha huchapisha ufunguo wako wa API — katalogi, si msimbo wako, ndiyo iliyoamua kilichosomwa. Katalogi si msimbo, lakini husafiri kama data: nje hadi jukwaa la tafsiri, kupitia mikono kadhaa, kurudi kama .po, ikikusanywa kuwa .mo, mara nyingine ikiletwa kutoka nje ya mradi wako kabisa. .format() huipa kila hatua ya safari hiyo ufikiaji wa sifa za vitu unavyopitisha.

$-strings na flufl.i18n

from flufl.i18n import initialize

_ = initialize("example")

name = "Ada"
print(_("Hello $name"))  # Hello Ada — the value came from the caller's locals

string.Template ya maktaba sanifu hutoa lugha ya uingizaji ya $name, lakini yenyewe si API ya kutafsiri. flufl.i18n huunganisha mtindo huo na utafutaji wa katalogi ya gettext. Zingatia kwamba thamani haipitishwi kamwe: flufl.i18n hujenga nafasi ya majina ya ubadilishaji kutoka vigezo vya jumla na vya ndani vya kinachoita — vigezo vyovyote vilivyopo mahali pa wito vinapatikana kwa ujumbe. Ramani ya hiari ya extras hutangulia vyote viwili. Sintaksia yake inayomkabili mfasiri haina herufi ya aina ya mwishoni wala kiainishi cha umbizo, na vishika nafasi hubaki vinapangika upya kwa uhuru.

Ubadilishaji usiopatikana hauinui hitilafu. Kwa name = "Ada" na bila nombre katika nafasi ya majina ya kinachoita, tafsiri ya katalogi ya Hello $nombre huonyeshwa kama Hello $nombre: kishika nafasi kisichotatuliwa hubaki kikionekana. Tabia hiyo iliyoandikwa huhifadhi sehemu iliyobaki ya ujumbe uliotafsiriwa badala ya kuufanya wito ushindwe. Hitilafu zinazoinuliwa wakati wa kutatua sifa au kubadilisha thamani bado zinaweza kuenea.

flufl.i18n ina uwezo zaidi kuliko string.Template tupu kwa njia moja inayohusika. Template yake maalum hukubali vishika nafasi vyenye vitone kama $settings.api_key, na mfasiri wake hutatua njia hizo dhidi ya thamani za kinachoita. Kishika nafasi kilichotafsiriwa kinaweza kutaja kigezo chochote cha ndani au cha jumla kinachopatikana kwa kinachoita na, kwa sintaksia ya vitone, kupitia sifa zake. Hilo ni jambo la urahisi wakati ujumbe unahitaji sifa, huku pia likifanya fremu ya kinachoita kuwa sehemu ya nafasi ya majina ya ubadilishaji ya katalogi. Ulinganisho wa hapa unaelezea flufl.i18n 6.0.0, si kila matumizi yanayowezekana ya string.Template.

Pia hujibu swali ambalo mitindo mingine miwili ya kuumbiza huliachia programu kabisa: ni lugha ipi iliyo ya sasa, na jinsi ya kuibadilisha. Kitu cha programu hushikilia rundo la lugha, _.push(code) na _.pop() hulisogeza, with _.using(code): huweka kimoja ndani ya kingine, na mkakati hutafuta katalogi ya msimbo wa lugha ili programu isishughulike kamwe na vitu vya katalogi yenyewe. Seva inayopaswa kutoa maandishi kwa lugha zaidi ya moja ndani ya kipande kimoja cha kazi — ukurasa kwa msomaji, arifa kwa mtu ambaye akaunti yake imewekwa tofauti — ndicho kisa ambacho hii ipo kwa ajili yake.

Rundo hukaa kwenye kitu hicho cha programu, ambacho mchakato mzima hukishirikiana. Kwa hiyo maombi mawili yanayopishana hushiriki rundo moja, nayo vizuizi ambavyo havijawekwa ndani kwa ndani kwa ukamilifu kwa wakati hukabidhiana lugha isiyo sahihi:

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']

Maktaba hii huhifadhi uwezo uleule — ufungaji huwekwa ndani kwa ndani na kufunguka kwa namna ileile — ndani ya ContextVar badala ya rundo linaloshirikiwa, hivyo mpishano wa hapo juu hutatuliwa kwa kila kazi. Vilinganishi vyake viko kwenye Lugha kadhaa kwa wakati mmoja. Kile ambacho haitoi ni utafutaji wa katalogi kutokana na msimbo wa lugha: wewe hupitisha kitu cha translations, ambacho kwa hali ya kawaida ni wito mmoja wa gettext.translation(), nayo maktaba sanifu huhifadhi katalogi iliyochanganuliwa.

t-strings

tr(t"Hello {name}")

Katalogi bado huona Hello {name} na hubaki katalogi ya kawaida ya PO/MO. Tofauti ni kile ambacho tafsiri inaruhusiwa kusema, na nani anayekikagua.

Maktaba hii huthibitisha kila tafsiri dhidi ya vishika nafasi vya ujumbe chanzo kabla ya kuonyesha, nayo hukubali majina matupu na hakuna kingine. Dhidi ya t"Hello {name}":

Tafsiri iliyo na hukataliwa kwa
{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

Kukataliwa hakumaanishi kuanguka: kwa chaguo-msingi maktaba huandika onyo na kuonyesha ujumbe chanzo, hivyo katalogi mbaya kamwe haiiangushi programu — mkataba uleule ambao gettext yenyewe huutunza.

Uumbizaji hubaki pale ulipoandikwa, ndani ya msimbo:

amount = 1234.5
tr(t"Total: {amount:,.2f}")  # msgid is "Total: {amount}"

:,.2f haifiki kamwe kwenye katalogi, hivyo hakuna tafsiri inayoweza kuibadili, na hakuna mfasiri anayelazimika kuiangalia. Hata hivyo ni umbizo lisilobadilika, si lililotafsiriwa kwa eneo — kuchagua tarakimu na vitenganishi kwa kila lugha ni kazi ya Babel, kabla ya wito.

Tofauti nyingine ni zana: t-strings ni sintaksia mpya, hivyo kuzitoa ndani ya .pot kwa sasa kunahitaji kitoaji kinachotambua t-string, kama kile ambacho kifurushi hiki hukitoa kwa Babel.

Gharama ya kizuizi

Zaidi ya sharti la Python, bei ya haya yote ni kanuni moja: uingizaji lazima uwe jina tupu.

tr(t"Hello {user.name}")  # raises InvalidTemplateError at the call site
name = user.name  # compute it first
tr(t"Hello {name}")

Hilo ni kizuizi halisi, nacho ndicho kizuizi kilekile kinachozalisha dhamana zilizo hapo juu. Pamoja na ufungaji wa thamani upande wa chanzo na ukaguzi wa vishika nafasi wakati wa utekelezaji, huzuia mifuatano ya katalogi kutathmini misemo na huyafanya majina ya vishika nafasi yaendelee kuwa na maana kwa mtu anayeyatafsiri.

f-string haiwezi kutumika hivi hata kidogo — kufikia wakati maktaba yoyote inaiona tayari ni mfuatano uliokamilika, hivyo kuitafsiri kunamaanisha kutafsiri kipande. t-strings (PEP 750) hutenganisha maandishi tuli na thamani huku zikihifadhi sintaksia inayofanana na ya f-string na ufungaji wa thamani ulio wazi.

Jinsi Python ilivyofika hapa — PEP mbili zenye tofauti ya miaka kumi, na mjadala wa stdlib uliofungwa bila jibu — imesimuliwa pamoja na vyanzo katika Usuli.