t-string کیوں¶
قابلِ ترجمہ پیغام میں قدر ڈالنے کے چار طریقے، ایک ہی پیغام پر تقابل۔ چاروں اپنے پلیس ہولڈرز کو نام دیتے ہیں اور مترجم کو ان کی ترتیب بدلنے دیتے ہیں؛ فرق اس میں ہے کہ ترجمہ غلط ہو تو کیا ہوتا ہے، کیٹلاگ آپ کے پروگرام کے کتنے حصے تک پہنچ سکتا ہے، اور انہیں اپنانے کی قیمت کیا ہے۔
جدول پہلے آتے ہیں، تاکہ آپ اپنے مطلب کی سطر ڈھونڈ کر صرف اسی کے پیچھے والا حصہ پڑھ سکیں۔
ہر ترجمہ شدہ پیغام کو تین فریق چھوتے ہیں
کیٹلاگ ترجموں کی فائل ہے — جب تک انسان اسے مرتب کریں .po، اور
ایپلی کیشن کے لوڈ کرنے کے لیے .mo میں کمپائل شدہ
(ٹیوٹوریل دونوں سے گزارتا ہے)۔ ہر پیغام کو تین فریق
چھوتے ہیں: ڈویلپر ماخذ سٹرنگ لکھتا ہے، مترجم کیٹلاگ مرتب کرتا
ہے — اکثر کسی بیرونی پلیٹ فارم پر، ہر کوڈ ریویو سے کوسوں دور — اور
ایپلی کیشن رن ٹائم پر دونوں کو ملا کر رینڈر کرتی ہے۔ نیچے دیا گیا ہر
فارمیٹنگ اسلوب ایک ہی سوال کا الگ جواب دیتا ہے: فارمیٹ زبان کا کتنا حصہ
کیٹلاگ کے قابو میں آتا ہے؟ مثالوں میں _ ترجمہ کرنے والے فنکشن کا
روایتی نام ہے، اور tr اس لائبریری کا۔
پہلو بہ پہلو¶
جب مترجم سے غلطی ہو جائے۔ کیٹلاگ کئی ہاتھوں سے گزرتا ہے، اور اس میں جو کچھ خراب ہوتا ہے اس کا بیشتر حصہ اتفاقی ہوتا ہے:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| ترجمہ کوئی پلیس ہولڈر گرا دے — کیا رینڈر ہوتا ہے؟ | قدر خاموشی سے غائب ہو جاتی ہے | قدر خاموشی سے غائب ہو جاتی ہے | قدر خاموشی سے غائب ہو جاتی ہے | ماخذ پیغام، ایک وارننگ کے ساتھ (طے شدہ طور پر) |
| ترجمہ کوئی نامعلوم پلیس ہولڈر جوڑ دے — کیا رینڈر ہوتا ہے؟ | ایک استثنا | ایک استثنا | پلیس ہولڈر متن کی صورت میں نظر آتا رہتا ہے | ماخذ پیغام، ایک وارننگ کے ساتھ (طے شدہ طور پر) |
| ترجمہ کسی پلیس ہولڈر کو دوبارہ فارمیٹ کر دے — کیا رینڈر ہوتا ہے؟ | جو کیٹلاگ نے مانگا، یا استثنا اگر قسم کا حرف قدر پر پورا نہ اترے | جو کیٹلاگ نے مانگا | $-سٹرنگز میں یہ کہا ہی نہیں جا سکتا |
ماخذ پیغام، ایک وارننگ کے ساتھ |
| کیا رینڈر کے وقت پلیس ہولڈر جانچے جاتے ہیں؟ | نہیں | نہیں | نہیں | ہاں (نیچے دیکھیے) |
کیٹلاگ کے پاس کتنا اختیار ہے۔ ترجمہ آپ کی ریپازٹری سے باہر کا ڈیٹا ہے، اور ہر اسلوب اسے مختلف مقدار میں طاقت تھما دیتا ہے:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| قدریں کہاں سے آتی ہیں؟ | ایک صریح نقشے سے | صریح آرگیومنٹس سے | کال کرنے والے کے مقامی اور عالمی متغیرات سے، مع اختیاری extras |
t-string کے اندر پکڑی گئی قدروں سے |
| کیا کیٹلاگ بدل سکتا ہے کہ قدر کیسے فارمیٹ ہو؟ | ہاں | ہاں | نہیں | نہیں |
| کیا کیٹلاگ آبجیکٹس کے اندر پہنچ سکتا ہے (خصوصیات تک رسائی)؟ | نہیں | ہاں | ہاں، نقطے والے ناموں سے | نہیں |
| "موجودہ زبان" کہاں رہتی ہے؟ | جہاں ایپلی کیشن اسے رکھے | جہاں ایپلی کیشن اسے رکھے | مشترک ایپلی کیشن آبجیکٹ پر زبان کے کوڈوں کا ایک سٹیک | ContextVar، فی ٹاسک یا فی درخواست |
اتصال کی قیمت کیا ہے۔ اوپر کا سب کچھ مفت ہے بشرطیکہ اوزار موزوں ہوں؛ یہیں وہ نہ بھی ہو سکتے ہیں:
%(name)s |
.format() |
flufl.i18n $name |
t"…" |
|
|---|---|---|---|---|
| کم از کم Python | کوئی بھی | کوئی بھی | 3.10 | 3.14 |
| پختگی | معیاری لائبریری | معیاری لائبریری | مستحکم ریلیز | الفا |
| کیا عام PO/MO کیٹلاگ استعمال کرتا ہے؟ | ہاں | ہاں | ہاں | ہاں |
| کیا الگ ماخذ استخراج کار درکار ہے؟ | نہیں | نہیں | نہیں | ہاں، فی الحال |
| موجودہ اوزاروں کی توثیق کے لیے Babel کون سا PO نشان اخذ کرتا ہے؟ | python-format |
python-brace-format |
کوئی نہیں | python-brace-format |
رینڈر کے وقت کی جانچ کے بارے میں: واحد پیغامات میں پلیس ہولڈرز کی بالکل مطابقت جانچی جاتی ہے۔ جمع والے پیغامات بھی جانچے جاتے ہیں، اُس اتحاد/اشتراک کے اصول کے مقابل جو ہدف زبان کی جمع کی صورتوں کو ماخذ سے مختلف ہونے دیتا ہے؛ فی صورت زیادہ سخت جانچ اُس وقت چلتی ہے جب کیٹلاگ کمپائل ہوتے ہیں (استخراج)۔
فارمیٹ نشان والی سطر پلیس ہولڈر سے واقف توثیق کے بارے میں ہے، کیٹلاگ کی
مطابقت کے بارے میں نہیں۔ کوئی نہیں کا مطلب ہے کہ معیاری gettext اوزار
پیغام کو پھر بھی پڑھ اور کمپائل کر لیتے ہیں، مگر
msgfmt --check-format کے پاس $-پلیس ہولڈر کی کوئی گرامر نہیں کہ اسے
لاگو کرے۔
مطابقت اور پختگی¶
آخری جدول کی پہلی دو سطریں ہی وہ ہیں جو اپنانے کا فیصلہ کرتی ہیں، لہٰذا انہیں خانوں کے بجائے صاف الفاظ میں کہہ دینا بہتر ہے۔
%-فارمیٹ اور .format() Python میں بنے ہوئے ہیں اور انہیں کسی انحصار کی
ضرورت ہی نہیں۔ flufl.i18n ایک پختہ پیکیج ہے، ریلیز شدہ اور
پروڈکشن میں زیرِ استعمال، جو Python 3.10 اور اس کے بعد پر چلتا ہے۔
gettext-tstrings ایک الفا ہے اور اسے Python 3.14 یا اس سے نیا درکار
ہے، کیونکہ t-string 3.14 کی نئی نحو ہے — نہ کوئی بیک پورٹ ہے اور نہ ہو سکتا
ہے۔ اس کی تصریح اس کا مستحکم حصہ ہے؛ Python API میں 1.0 سے پہلے
اب بھی تبدیلی ہو سکتی ہے۔
جس چیز کی قیمت ان میں سے کوئی نہیں لیتا، وہ کیٹلاگ کی مطابقت ہے۔ چاروں عام POT/PO/MO فائلیں پیدا کرتے ہیں جنہیں ہر PO ایڈیٹر، ترجمے کا پلیٹ فارم اور GNU gettext کا اوزار پہلے ہی پڑھ لیتا ہے، لہٰذا نیچے کا انتخاب اُس طرح واپس بھی ہو سکتا ہے جیسے کیٹلاگ کی صورت بدلنے کا انتخاب نہیں ہو سکتا۔ منتقلی کسی موجودہ پروجیکٹ کو منتقل کرنے پر بات کرتی ہے۔
نیچے کے حصے ہر سمجھوتے کو تفصیل سے دکھاتے ہیں، ایک وقت میں ایک طریقہ۔
%-format¶
کیا خرابی ہو سکتی ہے: خراب شدہ پلیس ہولڈر دورانِ عمل استثنا بن جاتا ہے، اِلا یہ کہ کیٹلاگ کی توثیق اسے پہلے پکڑ لے۔
کیٹلاگ کی سٹرنگ printf کی نحو اٹھائے پھرتی ہے، بشمول آخر میں آنے والے قسم
کے حرف کے — %(name)s کا s — جسے نظرانداز کرنا بھی آسان ہے اور خراب کرنا
بھی:
>>> "Hello %(name)" % {"name": "Ada"} # the trailing "s" was deleted
Traceback (most recent call last):
...
ValueError: incomplete format
PO ایڈیٹر میں ایک حرف کی تدوین رن ٹائم استثنا بن جاتی ہے، اِلا یہ کہ کیٹلاگ
کی توثیق اسے پہلے پکڑ لے۔ GNU کا msgfmt --check-format اسے پکڑ ضرور لیتا
ہے، مگر صرف اُن پیغامات کے لیے جن
پر python-format کا نشان ہو، اور صرف اُس صورت میں جب کیٹلاگ آپ کی
ایپلی کیشن تک پہنچتے ہوئے واقعی msgfmt سے گزرے۔
str.format¶
یہ آخری قسم کا حرف ہٹا دیتا ہے اور پلیس ہولڈر کو نام والا اور آزادانہ قابلِ ترتیب رکھتا ہے۔ خرابی کا امکان تبادلے کی دوسری طرف چلا جاتا ہے: ترجمہ آپ کے آبجیکٹس پر اختیار پا لیتا ہے۔
str.format ایک چھوٹی اظہاری زبان ہے، اور کسی سٹرنگ پر اسے پکارنے کا مطلب
ہے اُس سٹرنگ کو یہ زبان استعمال کرنے کا حق دے دینا:
>>> "{name.__class__.__mro__}".format(name="Ada")
"(<class 'str'>, <class 'object'>)"
>>> settings.api_key = "sk-live-…"
>>> "{conf.api_key}".format(conf=settings)
'sk-live-…'
اب اُن ثابت سٹرنگز کی جگہ وہ رکھ دیجیے جو _() لوٹاتا ہے۔ اگر Hello
{name} کا ترجمہ {conf.api_key} کی صورت میں واپس آئے، تو اسے رینڈر کرنا
آپ کی API کلید چھاپ دے گا — یعنی کیٹلاگ نے، آپ کے کوڈ نے نہیں، طے کیا کہ کیا
پڑھا جائے۔ کیٹلاگ کوڈ نہیں ہے، مگر سفر وہ ڈیٹا کی طرح کرتا ہے: کسی ترجمے کے
پلیٹ فارم تک باہر، کئی ہاتھوں سے ہو کر، .po کی صورت میں واپس، .mo میں
کمپائل، اور کبھی کبھی آپ کے پروجیکٹ سے بالکل باہر سے لیا ہوا۔ .format() اُس
پورے سفر کے ہر مرحلے کو اُن آبجیکٹس پر خصوصیات تک رسائی دے دیتا ہے جو آپ
دیتے ہیں۔
$-سٹرنگز اور 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 $name والی
انٹرپولیشن زبان فراہم کرتا ہے، مگر وہ خود ترجمے کا API نہیں۔
flufl.i18n اسی اسلوب کو gettext کیٹلاگ کی تلاش کے ساتھ ملا
دیتا ہے۔ غور کیجیے کہ قدر کبھی دی ہی نہیں جاتی: flufl.i18n متبادلی
نیم اسپیس کال کرنے والے کے globals اور locals سے بناتا ہے — کال کی جگہ پر جو
بھی متغیرات موجود ہوں، پیغام کے لیے دستیاب ہیں۔ ایک اختیاری extras نقشہ
دونوں پر فوقیت رکھتا ہے۔ مترجم کو نظر آنے والی اس کی نحو میں نہ آخری قسم کا
حرف ہے نہ فارمیٹ اسپیسیفائر، اور پلیس ہولڈر آزادانہ قابلِ ترتیب رہتے ہیں۔
ناقابلِ حصول متبادل استثنا نہیں اٹھاتا۔ اگر name = "Ada" ہو اور کال کرنے
والے کے نیم اسپیس میں کوئی nombre نہ ہو، تو Hello $nombre والا کیٹلاگ
ترجمہ Hello $nombre ہی رینڈر ہوتا ہے: حل نہ ہونے والا پلیس ہولڈر نظر آتا
رہتا ہے۔ وہ دستاویزی رویہ کال کو ناکام کرنے کے بجائے
ترجمہ شدہ پیغام کا باقی حصہ محفوظ رکھتا ہے۔ کسی خصوصیت کو حل کرتے یا قدر کو
تبدیل کرتے وقت اٹھنے والے استثناء پھر بھی پھیل سکتے ہیں۔
flufl.i18n ایک متعلقہ پہلو سے سادہ string.Template سے زیادہ صلاحیت
رکھتا ہے۔ اس کا اپنا Template نقطے والے پلیس ہولڈر جیسے
$settings.api_key قبول کرتا ہے، اور اس کا مترجم ان راستوں کو
کال کرنے والے کی قدروں کے مقابل حل کرتا ہے۔ ترجمہ شدہ پلیس ہولڈر کال کرنے
والے کے کسی بھی دستیاب local یا global کا نام لے سکتا ہے اور، نقطے والی نحو
کے ساتھ، اس کی خصوصیات میں گھوم سکتا ہے۔ جب پیغام کو کوئی خصوصیت درکار ہو تو
یہ سہولت ہے، مگر ساتھ ہی کال کرنے والے کے فریم کو کیٹلاگ کے متبادلی
نیم اسپیس کا حصہ بھی بنا دیتا ہے۔ یہاں کا تقابل flufl.i18n 6.0.0 کا بیان
ہے، string.Template کے ہر ممکن استعمال کا نہیں۔
یہ اُس سوال کا جواب بھی دیتا ہے جو باقی دونوں فارمیٹنگ اسلوب پورے کا پورا
ایپلی کیشن پر چھوڑ دیتے ہیں: کون سی زبان اِس وقت فعال ہے، اور اسے کیسے بدلا
جائے۔ ایپلی کیشن آبجیکٹ زبانوں کا ایک سٹیک رکھتا ہے،
_.push(code) اور _.pop() اسے حرکت دیتے ہیں، with _.using(code): اسے
نیسٹ کرتا ہے، اور حکمتِ عملی کسی زبان کے کوڈ کے لیے کیٹلاگ ڈھونڈ
لیتی ہے تاکہ ایپلی کیشن کو خود کبھی کیٹلاگ آبجیکٹس سے واسطہ نہ پڑے۔ یہ سب اُس
سرور کے لیے ہے جسے کام کی ایک ہی اکائی کے دوران ایک سے زیادہ زبانوں میں متن
پیدا کرنا ہو — قاری کے لیے ایک صفحہ، اور ایسے شخص کے لیے اطلاع جس کا کھاتہ کسی
اور زبان پر مقرر ہو۔
سٹیک اُسی ایپلی کیشن آبجیکٹ پر رہتا ہے، جسے پورا پروسیس مشترک رکھتا ہے۔ چنانچہ دو باہم متداخل درخواستیں ایک ہی سٹیک بانٹتی ہیں، اور جو بلاک وقت کے اعتبار سے سختی سے نیسٹ نہ ہوں، وہ ایک دوسرے کو غلط زبان تھما دیتے ہیں:
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']
یہ لائبریری وہی صلاحیت برقرار رکھتی ہے — بندشیں اُسی طرح نیسٹ ہوتی اور کھلتی
ہیں — مگر مشترک سٹیک کے بجائے ContextVar میں، چنانچہ اوپر والا تداخل ہر ٹاسک
کے لیے الگ حل ہو جاتا ہے۔ اس کے مساوی طریقے
ایک ساتھ کئی زبانیں میں دیے گئے ہیں۔ جو
چیز یہ فراہم نہیں کرتی وہ زبان کے کوڈ سے کیٹلاگ تک کی تلاش ہے: ترجمہ آبجیکٹ آپ
خود دیتے ہیں، جو عام صورت میں gettext.translation() کی ایک کال ہوتی ہے، اور
تجزیہ شدہ کیٹلاگ معیاری لائبریری خود کیش کر لیتی ہے۔
t-strings¶
کیٹلاگ کو اب بھی Hello {name} ہی نظر آتا ہے اور وہ ایک عام PO/MO کیٹلاگ ہی
رہتا ہے۔ فرق یہ ہے کہ ترجمے کو کیا کہنے کی اجازت ہے، اور اسے جانچتا کون
ہے۔
یہ لائبریری رینڈر سے پہلے ہر ترجمے کو ماخذ پیغام کے پلیس ہولڈرز کے مقابل
جانچتی ہے، اور صرف خالی نام قبول کرتی ہے، اور کچھ نہیں۔ t"Hello {name}" کے
مقابل:
| ترجمہ جس پر مشتمل ہو | مسترد ہونے کی وجہ |
|---|---|
{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 |
مسترد ہونے کا مطلب کریش نہیں: طے شدہ طور پر لائبریری ایک وارننگ لاگ کرتی ہے اور ماخذ پیغام رینڈر کرتی ہے، لہٰذا خراب کیٹلاگ ایپلی کیشن کو کبھی نہیں گراتا — وہی معاہدہ جو خود gettext نبھاتا ہے۔
فارمیٹنگ وہیں رہتی ہے جہاں لکھی گئی تھی، یعنی کوڈ میں:
:,.2f کبھی کیٹلاگ تک نہیں پہنچتا، چنانچہ کوئی ترجمہ اسے بدل نہیں سکتا، اور
کسی مترجم کو اسے دیکھنا بھی نہیں پڑتا۔ البتہ یہ ایک ثابت فارمیٹ ہے، مقامی
نہیں — ہر زبان کے لیے ہندسے اور جداکار چننا
Babel کا کام ہے، کال سے پہلے۔
ایک اور فرق اوزار کا ہے: t-string نئی نحو ہے، اس لیے انہیں .pot میں نکالنے
کے لیے فی الحال ایسا استخراج کار درکار ہے جو t-string سے واقف ہو، جیسا کہ وہ
جو یہ پیکیج Babel کے لیے فراہم کرتا ہے۔
پابندی کی قیمت¶
Python کی شرط کے علاوہ، اس سب کی قیمت ایک ہی قاعدہ ہے: انٹرپولیشن کا سادہ نام ہونا لازم ہے۔
یہ واقعی ایک پابندی ہے، اور یہی وہ پابندی ہے جو اوپر کی ضمانتیں پیدا کرتی ہے۔ ماخذ کی طرف قدر کی بندش اور رن ٹائم پر پلیس ہولڈر کی جانچ کے ساتھ مل کر یہ کیٹلاگ کی سٹرنگز کو اظہاریے چلانے سے روکتی ہے اور پلیس ہولڈر کے ناموں کو اُس شخص کے لیے بامعنی رکھتی ہے جو ان کا ترجمہ کرتا ہے۔
f-string اس طرح استعمال ہو ہی نہیں سکتی — جب تک کوئی لائبریری اسے دیکھتی ہے، وہ پہلے ہی مکمل شدہ سٹرنگ بن چکی ہوتی ہے، لہٰذا اس کا ترجمہ کرنے کا مطلب ایک ٹکڑے کا ترجمہ کرنا ہے۔ t-string (PEP 750) ثابت متن اور قدروں کو الگ رکھتی ہے، جبکہ f-string جیسی نحو اور قدر کی صریح بندش برقرار رہتی ہے۔
Python یہاں تک کیسے پہنچا — دس برس کے فاصلے پر دو PEP، اور معیاری لائبریری کی وہ بحث جو بغیر جواب کے بند ہوئی — یہ حوالوں سمیت پس منظر میں بیان ہوا ہے۔