Neden t-string?¶
Çevrilebilir bir mesaja değer yerleştirmenin dört yolu, aynı mesaj üzerinde karşılaştırıldı. Dördü de yer tutucularını adlandırır ve bir çevirmenin onları yeniden sıralamasına izin verir; ayrıldıkları yerler şunlardır: bir çeviri yanlış olduğunda ne olur, programınızın ne kadarına katalog uzanabilir ve benimsemenin bedeli nedir.
Önce tablolar geliyor; böylece önemsediğiniz satırı bulup yalnızca onun arkasındaki bölümü okuyabilirsiniz.
Çevrilen her mesaja üç taraf dokunur
Katalog, çevirilerin dosyasıdır — insanlar düzenlerken .po,
uygulamanın yüklemesi için .mo biçimine derlenir
(öğretici ikisini de adım adım gösterir). Her mesaja üç
taraf dokunur: geliştirici kaynak dizgiyi yazar, bir çevirmen
kataloğu düzenler — çoğu zaman her tür kod incelemesinden uzakta, harici
bir platformda — ve uygulama ikisini çalışma zamanında birlikte render
eder. Aşağıdaki her biçimlendirme tarzı aynı soruyu farklı yanıtlar:
biçim dilinin ne kadarını kataloğun denetlemesine izin verilir?
Örneklerde _ çeviri işlevinin geleneksel adıdır; tr ise bu
kütüphaneninki.
Yan yana¶
Bir çevirmen hata yaptığında. Bir katalog birçok elden geçer ve içinde ters giden şeylerin çoğu kazadır:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| Bir çeviri bir yer tutucuyu düşürürse — ne render edilir? | değer sessizce kaybolur | değer sessizce kaybolur | değer sessizce kaybolur | kaynak mesaj, bir uyarıyla (varsayılan olarak) |
| Bir çeviri bilinmeyen bir yer tutucu eklerse — ne render edilir? | bir istisna | bir istisna | yer tutucu metin olarak görünür kalır | kaynak mesaj, bir uyarıyla (varsayılan olarak) |
| Bir çeviri bir yer tutucuyu yeniden biçimlendirirse — ne render edilir? | kataloğun istediği şey; ya da tür harfi artık değere uymuyorsa bir istisna | kataloğun istediği şey | $-dizgilerinde ifade edilemez |
kaynak mesaj, bir uyarıyla |
| Yer tutucular render anında denetlenir mi? | hayır | hayır | hayır | evet (aşağıya bakın) |
Kataloğun ne yetkisi var. Bir çeviri, deponuzun dışından gelen veridir ve her tarz ona farklı miktarda güç verir:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| Değerler nereden gelir? | açık bir eşlemeden | açık argümanlardan | çağıranın yerel ve global değişkenlerinden, artı isteğe bağlı extras |
t-string içinde yakalanan değerlerden |
| Katalog bir değerin nasıl biçimlendirileceğini değiştirebilir mi? | evet | evet | hayır | hayır |
| Katalog nesnelerin içine uzanabilir mi (öznitelik erişimi)? | hayır | evet | evet, noktalı adlarla | hayır |
| "Geçerli dil" nerede durur? | uygulama nereye koyarsa orada | uygulama nereye koyarsa orada | paylaşılan uygulama nesnesi üzerindeki bir dil kodu yığınında | bir ContextVar içinde, görev ya da istek başına |
Entegrasyonun bedeli. Yukarıdaki her şey, araçlar uyuyorsa bedavadır; uymayabileceği yer burasıdır:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| En düşük Python | herhangi biri | herhangi biri | 3.10 | 3.14 |
| Olgunluk | standart kütüphane | standart kütüphane | kararlı sürüm | alfa |
| Sıradan PO/MO katalogları kullanır mı? | evet | evet | evet | evet |
| Özel bir kaynak çıkarıcı gerekir mi? | hayır | hayır | hayır | evet, şimdilik |
| Babel hangi PO bayrağını çıkarsar, mevcut araçlar doğrulasın diye? | python-format |
python-brace-format |
hiçbiri | python-brace-format |
Render anındaki denetim üzerine: tekil mesajlar yer tutucuların birebir eşleşmesi bakımından denetlenir. Çoğul mesajlar da denetlenir; hedef dilin çoğul biçimlerinin kaynağınkinden farklı olmasına izin veren birleşim/kesişim kuralına karşı. Biçim başına daha katı denetim ise kataloglar derlenirken çalışır (Çıkarma).
Biçim bayrağı satırı, katalog uyumluluğunu değil, yer tutuculardan haberdar
doğrulamayı anlatır. hiçbiri, standart gettext araçlarının mesajı yine de
okuyup derlediği, ama msgfmt --check-formatın uygulayacak bir
$-yer-tutucu dil bilgisi olmadığı anlamına gelir.
Uyumluluk ve olgunluk¶
Son tablonun ilk iki satırı, benimsemeye karar veren satırlardır; bu yüzden hücreler olarak değil, açıkça söylemeye değerler.
%-format ve .format() Python'un içine gömülüdür ve hiçbir bağımlılık
gerektirmez. flufl.i18n, yayımlanmış ve üretimde kullanılan,
Python 3.10 ve sonrasında çalışan olgun bir pakettir. gettext-tstrings bir
alfadır ve Python 3.14 veya daha yenisini gerektirir; çünkü
t-string'ler 3.14'te gelen yeni bir sözdizimidir — bir geri uyarlaması yoktur
ve olamaz. Belirtimi onun kararlı parçasıdır; Python API'si 1.0
öncesinde hâlâ değişebilir.
Hiçbirinin bedeli olmayan şey, katalog uyumluluğudur. Dördü de her PO editörünün, çeviri platformunun ve GNU gettext aracının hâlihazırda okuduğu sıradan POT/PO/MO dosyaları üretir; dolayısıyla aşağıdaki seçim, katalog biçimlerini değiştirmenin olamayacağı bir şekilde geri alınabilirdir. Geçiş, var olan bir projeyi taşımayı anlatır.
Aşağıdaki bölümler her ödünleşmeyi, yöntem yöntem, ayrıntısıyla gösterir.
%-format¶
Ne ters gidebilir: bozulmuş bir yer tutucu, katalog doğrulaması onu önce yakalamazsa çalışma zamanı istisnasına dönüşür.
Katalog dizgisi, printf sözdizimini taşır; buna gözden kaçırması ve zarar
vermesi kolay olan sondaki tür harfi — %(name)s içindeki s — da dahildir:
>>> "Hello %(name)" % {"name": "Ada"} # the trailing "s" was deleted
Traceback (most recent call last):
...
ValueError: incomplete format
Bir PO editöründe yapılan tek karakterlik düzenleme, katalog doğrulaması onu
önce yakalamazsa çalışma zamanında bir istisnaya dönüşür. GNU
msgfmt --check-format bunu yakalar, ama yalnızca
python-format işaretli mesajlar için ve yalnızca katalog uygulamanıza giden
yolda gerçekten msgfmt'ten geçiyorsa.
str.format¶
Bu yöntem, adlandırılmış ve serbestçe yeniden sıralanabilir bir yer tutucuyu korurken sondaki tür harfini kaldırır. Ters gidebilecek olan şey, alışverişin öbür tarafına geçer: çeviri, nesneleriniz üzerinde güç kazanır.
str.format küçük bir ifade dilidir ve onu bir dizgi üzerinde çağırmak, o
dizgiye bu dili kullanma hakkını vermek demektir:
>>> "{name.__class__.__mro__}".format(name="Ada")
"(<class 'str'>, <class 'object'>)"
>>> settings.api_key = "sk-live-…"
>>> "{conf.api_key}".format(conf=settings)
'sk-live-…'
Şimdi bu değişmez dizgilerin yerine _() işlevinin döndürdüğü herhangi bir
şeyi koyun. Hello {name} mesajının çevirisi {conf.api_key} olarak geri
gelirse, onu render etmek API anahtarınızı yazdırır — neyin okunacağına
kodunuz değil, katalog karar vermiştir. Bir katalog kod değildir, ama veri
gibi yolculuk eder: bir çeviri platformuna gider, birkaç elden geçer, .po
olarak döner, .mo biçimine derlenir, bazen tamamen projenizin dışından
vendor'lanır. .format(), bu yolculuğun her aşamasına, aktardığınız nesneler
üzerinde öznitelik erişimi verir.
$-dizgileri ve flufl.i18n¶
from flufl.i18n import initialize
_ = initialize("example")
name = "Ada"
print(_("Hello $name")) # Hello Ada — the value came from the caller's locals
$name interpolasyon dilini standart kütüphanenin
string.Template sınıfı sağlar, ama bu sınıf kendi başına
bir çeviri API'si değildir. flufl.i18n, o tarzı gettext
katalog aramasıyla birleştirir. Değerin hiçbir zaman içeri aktarılmadığına
dikkat edin: flufl.i18n, yerine koyma ad alanını çağıranın global ve yerel
değişkenlerinden kurar — çağrı noktasında var olan değişkenler mesajın
kullanımına açıktır. İsteğe bağlı bir extras eşlemesi her ikisinin de önüne
geçer. Çevirmene dönük sözdiziminde sonda tür harfi ya da biçim belirteci
yoktur ve yer tutucular serbestçe yeniden sıralanabilir.
Çözülemeyen bir yerine koyma hata fırlatmaz. name = "Ada" varken ve
çağıranın ad alanında nombre yokken, katalogdaki Hello $nombre çevirisi
Hello $nombre olarak render edilir: çözülmemiş yer tutucu görünür kalır. Bu
belgelenmiş davranış, çağrıyı başarısız kılmak yerine
çevrilen mesajın geri kalanını korur. Bir özniteliği çözerken ya da bir değeri
dönüştürürken fırlatılan istisnalar yine de yayılabilir.
flufl.i18n, konumuzla ilgili bir noktada yalın bir string.Template'ten
daha yeteneklidir. Özel Template sınıfı
$settings.api_key gibi noktalı yer tutucuları kabul eder ve
translator bileşeni bu yolları çağıranın değerleri üzerinde
çözer. Çevrilmiş bir yer tutucu, erişilebilir herhangi bir çağıran yerel ya da
global değişkenini adlandırabilir ve noktalı sözdizimiyle onun
özniteliklerinde gezinebilir. Bu, bir mesajın bir özniteliğe ihtiyaç duyduğu
durumda kullanışlıdır; ama aynı zamanda çağıranın çerçevesini kataloğun
yerine koyma ad alanının parçası yapar. Buradaki karşılaştırma
flufl.i18n 6.0.0'ı anlatır, string.Template'in olası her kullanımını
değil.
Ayrıca diğer iki biçimlendirme tarzının tümüyle uygulamaya bıraktığı bir
soruyu da yanıtlar: hangi dil geçerlidir ve nasıl değiştirilir. Bir
uygulama nesnesi bir diller yığını tutar; _.push(code)
ile _.pop() yığını hareket ettirir, with _.using(code): iç içe geçmeyi
sağlar ve bir strateji bir dil koduna karşılık gelen kataloğu
bulur; böylece uygulama katalog nesneleriyle kendisi hiç uğraşmaz. Tek bir iş
birimi içinde birden fazla dilde metin üretmek zorunda olan bir sunucu — okur
için bir sayfa, dili farklı ayarlanmış birinin hesabı için bir bildirim — bu
düzeneğin var oluş nedenidir.
Yığın, tüm sürecin paylaştığı o uygulama nesnesinin üzerinde durur. Dolayısıyla üst üste binen iki istek tek bir yığını paylaşır ve zaman içinde tam olarak iç içe geçmeyen bloklar birbirine yanlış dili devreder:
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']
Bu kütüphane aynı yeteneği korur — bağlamalar aynı şekilde iç içe geçer ve
geri sarılır — ama onu paylaşılan bir yığında değil bir ContextVar içinde
tutar; böylece yukarıdaki geçişme görev başına çözülür. Eşdeğerleri
Aynı anda birden fazla dil başlığında.
Sağlamadığı şey, dil kodundan kataloğa uzanan aramadır: bir çeviri nesnesi
geçirirsiniz — yaygın durumda bu tek bir gettext.translation() çağrısıdır —
ve ayrıştırılmış kataloğu standart kütüphane önbelleğe alır.
t-string'ler¶
Katalog yine Hello {name} görür ve sıradan bir PO/MO kataloğu olarak kalır.
Fark, bir çevirinin ne söylemesine izin verildiği ve bunu kimin
denetlediğidir.
Bu kütüphane her çeviriyi render'dan önce kaynak mesajın yer tutucularına
karşı doğrular ve yalın adlar dışında hiçbir şeyi kabul etmez.
t"Hello {name}" karşısında:
| Şunu içeren bir çeviri | şu gerekçeyle reddedilir |
|---|---|
{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 |
Reddedilmek çökmek demek değildir: kütüphane varsayılan olarak bir uyarı günlükler ve kaynak mesajı render eder; böylece kötü bir katalog uygulamayı asla düşürmez — gettext'in kendi tuttuğu sözleşmenin aynısı.
Biçimlendirme, yazıldığı yerde, kodda kalır:
:,.2f kataloğa asla ulaşmaz; dolayısıyla hiçbir çeviri onu değiştiremez ve
hiçbir çevirmen ona bakmak zorunda kalmaz. Yine de bu sabit bir biçimdir,
yerelleştirilmiş bir biçim değil — basamakları ve ayırıcıları dile göre
seçmek çağrıdan önce Babel'in işidir.
Bir fark daha araç desteğindedir: t-string'ler yeni bir sözdizimi olduğundan,
onları bir .pot dosyasına çıkarmak şimdilik t-string'lerden anlayan bir
çıkarıcı gerektirir; bu paketin Babel için sağladığı gibi.
Kısıtlamanın bedeli¶
Python gereksiniminin ötesinde, tüm bunların bedeli tek bir kuraldır: bir interpolasyon yalın bir ad olmak zorundadır.
Bu gerçek bir kısıttır ve yukarıdaki güvenceleri üreten kısıtla aynıdır. Kaynak tarafında değer bağlama ve çalışma zamanında yer tutucu denetimiyle birlikte, katalog dizgilerinin ifade değerlendirmesini engeller ve yer tutucu adlarını onları çeviren kişi için anlamlı tutar.
Bir f-string bu şekilde hiç kullanılamaz — herhangi bir kütüphane onu gördüğünde çoktan bitmiş bir dizgidir; dolayısıyla onu çevirmek bir parçayı çevirmek demektir. t-string'ler (PEP 750) f-string benzeri sözdizimini ve açık değer bağlamayı korurken statik metinle değerleri ayrı tutar.
Python'un buraya nasıl geldiği — on yıl arayla iki PEP ve yanıtsız kapanan stdlib tartışması — kaynaklarıyla birlikte Arka Plan sayfasında anlatılıyor.