پرش به محتویات

دام‌ها

این وب‌گاه به سی‌وپنج زبان ترجمه شده است، و هر یک از آن نسخه‌ها با گرداندنِ همان چرخه‌ای ساخته شده که این مستندات آموزش می‌دهد. این به معیارهای صنعت پیکره‌ای کوچک است و با این حال برای افتادن در بیشترِ دام‌هایی که i18n را از آنچه به نظر می‌رسد دشوارتر می‌کنند کافی بود.

هر بخش زیر چیزی است که این‌جا واقعاً پیش آمد، آن‌گاه چه شکلی داشت، و این‌که مرزِ میان آنچه کتابخانه به‌جای شما بررسی می‌کند و آنچه بر عهدهٔ داوری شما می‌ماند کجا می‌افتد.

تغییر نام یک متغیر، یک جمله را دوباره به ترجمه می‌فرستد

msgid کلید کاتالوگ است، و نامِ درون‌یابی‌شده درونِ آن جای دارد. بردنِ یک ثابت به دامنهٔ ماژول و بزرگ‌نویسی‌اش آن‌گونه که سبک پایتون می‌خواهد — از author به AUTHOR — پیامِ Copyright © 2026 {author} · MIT License را به پیامی بدل کرد که هیچ کاتالوگی هرگز ندیده بود. هر ترجمهٔ آن خط، در هر زبان، دوباره از چرخهٔ fuzzy می‌گذشت؛ آن هم برای تغییر نامی که چیزی از آنچه خواننده می‌بیند عوض نمی‌کرد.

کتابخانه جلوی شما را نمی‌گیرد: هر دو املا نامِ جای‌نگهدارِ معتبرند. کاری که می‌کند این است که نام را ارزندهٔ پاسداری می‌سازد — درون‌یابی باید یک نام ساده باشد، پس آنچه در کلید کاتالوگ می‌نشیند واژه‌ای است که مترجم می‌تواند بخواند، نه یک عبارت.

حالت آینه‌ای‌اش بنا به ساخت امن است. تبدیل‌ها و مشخصه‌های قالب جزء msgid نیستند؛ پس سفت‌کردنِ {amount:,.2f} به {amount:,.0f} هیچ کلیدی را عوض نمی‌کند و هیچ ترجمه‌ای را در هیچ کجا باطل نمی‌کند.

nplurals=2 به معنای دو رشتهٔ متفاوت نیست

ترکی، مجاری، فارسی و بنگالی همگی دو صورت جمع اعلام می‌کنند، و در هر چهار زبان دو صورتِ یک پیام شمارشی به‌حق همان یک رشتهاند — اسم پس از عدد مفرد می‌ماند، پس {n} sayfa هم برای یک صفحه درست است و هم برای ده صفحه. بازبینی که این تکرار را «تصحیح» کند ترجمه را می‌شکند.

خطای وارونه هم به همان اندازه آسان است. صورت سومِ لتونیایی تنها برای صفر وجود دارد؛ صورت دومِ اسلوونیایی مثنّی است، برای دقیقاً دو؛ و صورت آخرِ رومانیایی واژهٔ de را می‌طلبد که دو صورت نخستش نباید آن را داشته باشند. پرکردن آن خانه‌ها با یک مفرد و یک جمع کاتالوگی می‌سازد که تنها برای شمارش‌هایی نادرست است که هیچ‌کس آزمایش‌شان نمی‌کند.

بدتر آن‌که ترتیبِ خانه‌ها دلالتی نیست. ولزی پنج صورتش را چنان نمایه‌گذاری می‌کند که msgstr[0] حالت عام است و msgstr[1] مفرد. پرکردن‌شان به ترتیب بدیهی، مفرد را جایی می‌گذارد که هر پیام بی‌شمارش آن را خواهد یافت.

کتابخانه هیچ‌یک از این‌ها را بر عهده نمی‌گیرد، و نکته همین است: قاعدهٔ جمعِ زبان مقصد در سرآیند کاتالوگ خودش زندگی می‌کند، و قاعدهٔ اجتماع/اشتراک می‌گذارد ترجمه صورت‌هایی بیشتر یا کمتر از مبدأ داشته باشد. آنچه کتابخانه بررسی می‌کند تنها چیزی است که بی‌دانستنِ زبان می‌توان بررسی کرد — این‌که هر صورت جای‌نگهدارهای لازمش را نگه دارد.

دو صورت می‌توانند به دلیلی یکسان باشند

ایرلندی پنج صورت جمع دارد، و در گزارش ساختِ همین وب‌گاه چند تای آن‌ها یکسان نوشته شده‌اند. این لغزشِ کپی‌ولصق نیست: leathanach با l آغاز می‌شود، و هیچ‌یک از دو تغییر آغازینی که اعداد ایرلندی برمی‌انگیزند روی l نوشته نمی‌شود. صورت‌ها همچنان کار واقعی می‌کنند — ریشه میان leathanach و leathanaigh در نوسان است و شمارش‌های بالای ده به مفرد بازمی‌گردند — اما هیچ اسمی به معنای «صفحه» این تقابل را نشان نمی‌داد.

هر بررسی‌ای که صورت‌های تکراری را مشکوک بشمارد، ایرلندیِ درست را نشانه خواهد گرفت. تنها بازبینِ این کار انسانی است که زبان را می‌داند.

یک پیام تنها با یک شمارش می‌تواند مطابقت کند

گزارش ساختِ این وب‌گاه می‌گوید چند صفحه رندر شد و چقدر طول کشید. نوشتنش به صورت «Rendered {n} pages in {seconds} seconds» بی‌آزار به نظر می‌رسد و ترجمه‌پذیر نیست: gettext یک صورت را بر پایهٔ یک شمارش برمی‌گزیند، و آن شمارش n است. واژهٔ seconds می‌بایست با عددی مطابقت می‌کرد که ماشین جمع هرگز آن را نمی‌بیند.

چاره آن است که کمیت دوم را به‌جای واژه یک نماد یکا کنید، و نمادهای یکا خودشان بومی‌سازی می‌شوند: کاتالوگ‌های این وب‌گاه s و с و ث و שנ׳ و mp را با خود دارند، و تایپوگرافی فرانسوی و اسپانیایی و سوئدی پیش از نماد فاصله می‌خواهد، جایی که انگلیسی نمی‌خواهد. هیچ‌یک از این‌ها کارِ کتابخانه نیست — اما توجه به این‌که یک پیام به دو مطابقت نیاز دارد هست، و تنها ابزارِ آن نوشتنِ پیام به شکلی دیگر است.

ویرایش یک جملهٔ انگلیسی، دستور زبانِ زبان‌های دیگر را ویرایش می‌کند

صفحهٔ خانه پیش‌تر می‌گفت «all ten language editions». برداشتن آن عدد — ویرایشی یک‌واژه‌ای در انگلیسی، که چون آن عدد مدام کهنه می‌شد انجام شد — نهادی جمع را مفرد کرد. اسپانیایی، ایتالیایی، پرتغالی، روسی، اوکراینی، یونانی، هلندی و عبری همگی ناچار شدند فعل را دوباره مطابقت دهند؛ چند زبان صفت مفعولی را هم باید عوض می‌کردند.

ویرایشی در مبدأ که در انگلیسی بی‌اهمیت خوانده می‌شود، در پایین‌دست بی‌اهمیت نیست. علامت‌زدنش به fuzzy، که کاری است که pybabel update می‌کند، سازوکاری است که به هر مترجم فرصت توجه‌کردن می‌دهد.

تفاوت‌های نامرئی از هر کپی‌ولصقی جان به در می‌برند

راهنما تشخیصی را نقل می‌کند که (nаme) در آن هست — یک escapeِ عمدی، چون نویسه‌ای که نامش را می‌برد аی سیریلی است که هیچ خواننده‌ای آن را از لاتین بازنمی‌شناسد. مترجمان این وب‌گاه پنج بار جداگانه، در پنج زبان مختلف، آن escape را به نویسهٔ واقعی بدل کردند و هر بار صفحه‌ای ساختند که درست به نظر می‌رسید و نادرست بود.

این یکی را کتابخانه واقعاً می‌گیرد، و دلیلِ این‌که تشخیص‌ها این شکل را دارند هم همین است: جای‌نگهداری که حروفش نظام‌های نوشتاری را می‌آمیزد دو بار گزارش می‌شود، یک بار خوانا و یک بار escape‌شده، چون صورت escape‌شده تنها املایی است که آن دو را از هم جدا می‌کند. فاصلهٔ نشکن درون آکولادها هم به همین دلیل با کدنقطه چاپ می‌شود. بررسی‌کنندهٔ کاتالوگ پیام را پیش از آن‌که بتواند منتشر شود رد می‌کند.

ناتهی یعنی ترجمه‌شده نیست

کاتالوگی که با کپی‌کردنِ msgidهایش در msgstrها داربست شده باشد از هر بررسی ساده‌لوحانه‌ای می‌گذرد: چیزی تهی نیست، چیزی fuzzy نیست، و مجموعهٔ پیام‌ها دقیقاً می‌خواند. یکی از نسخه‌های زبانی این وب‌گاه چند ساعت به همان حال منتشر شد. هشت صفحه از نسخه‌ای دیگر هم که کپی‌های بایت‌به‌بایتِ مبدأ انگلیسی بودند همین‌طور — و آن‌ها از بررسی‌ای که بلوک‌های کد را میان دو صفحه مقایسه می‌کند می‌گذرند، چون همان یک فایل‌اند.

هیچ‌کدام چیزی نیست که یک کتابخانهٔ ترجمه بتواند ببیند. هر دو ارزان آزمایش می‌شوند، اما نه با الزام‌کردنِ این‌که هر مدخل با مبدأش فرق کند: ‏OK، نام محصول‌ها، نام اشخاص، سرواژه‌ها و شناسه‌های کد همگی به خودشان ترجمه می‌شوند، و بررسی‌ای که این را ممنوع کند تا ابد مثبت کاذب می‌سازد.

به‌جایش نرخ را بسنجید — بر روی یک کاتالوگ کامل یا یک صفحهٔ کامل — و پرت‌ها را به دست انسان بسپارید. آزمون خودِ این وب‌گاه دقیقاً همین کار را می‌کند: سطرهای نثرِ هر نسخهٔ زبانی را با مبدأ انگلیسی مقایسه می‌کند و بالای ۲۵٪ یکسان مردود می‌شود. آن نسخهٔ جعلی روی ۸۷٪ نشسته بود؛ هر ترجمهٔ اصیل میان ۴٪ و ۸٪ می‌نشیند، که همان دنبالهٔ کوچکِ سطرهایی است که به‌حق منطبق می‌شوند، مثل نشانی‌ها و خروجی نقل‌شدهٔ برنامه. این دو جمعیت چنان از هم دورند که آستانه لازم نیست دقیق باشد.

کاتالوگ تنها چیز ترجمه‌شده نیست

دو شکست این‌جا هیچ ربطی به gettext نداشتند.

ترجمهٔ یک عنوان، لنگری را که از آن ساخته می‌شود عوض می‌کند؛ پس هر پیوندِ میان‌صفحه‌ای به آن بخش می‌شکند — بی‌صدا، و تنها در همان زبان. این وب‌گاه لنگر انگلیسی را روی هر عنوان سنجاق می‌کند، و آزمونی فهرست مورد انتظار را از صفحهٔ انگلیسی استخراج می‌کند.

و مولدِ وب‌گاه ترجمه‌های رابط کاربری را برای شصت‌وهشت زبان می‌فرستد که سواحیلی و ایرلندی در میان‌شان نیستند. بی‌آن ترجمه، ساخت به انگلیسی تنزل نمی‌کند؛ includeِ قالب شکست می‌خورد و آن نسخهٔ زبانی اصلاً ساخته نمی‌شود. دو فایل از فایل‌های خودِ این مخزن برای پرکردن همین شکاف وجود دارند.

ابزارهای شما هم باگ دارند

گام CI که این مستندات برای گرفتنِ کاتالوگ‌های کهنه توصیه می‌کند، یعنی pybabel update --check، نمی‌تواند این کار را برای هیچ پروژه‌ای انجام دهد که از pgettext یا npgettext استفاده می‌کند. روی Babel 2.18.0 هر کاتالوگی را که msgctxt دارد، در هر اجرا، کهنه گزارش می‌کند. مقایسه از راه Catalog.is_identical می‌گذرد که هر پیام را با کلیدی که زیر آن ذخیره شده جست‌وجو می‌کند — و برای یک پیام بافتاری آن کلید جفتِ (id, context) است که Catalog.get نمی‌پذیردش. جست‌وجو چیزی برنمی‌گرداند و کاتالوگ‌ها هرگز برابر مقایسه نمی‌شوند:

>>> from babel.messages.catalog import Catalog
>>> c = Catalog(locale="ja")
>>> c.add("Guide", "ガイド", context="navigation")
<Message 'Guide' (flags: [])>
>>> c.is_identical(c)
False

این‌جا با تلاش برای استفاده از آن پیدا شد، به بالادست گزارش شد، و بررسی جایگزین در صفحهٔ محیط عملیاتی آمده است.

درسِ عام همان درس ناخوشایند است: دروازه‌ای که همیشه قرمز است بدتر از نبودِ دروازه است، چون تیم خاموشش می‌کند. پیش از آن‌که به بررسی CI خود برای شکست‌دادن اعتماد کنید، بسنجید که واقعاً می‌تواند قبول شود.

کتابخانه برای چیست، در یک سطر

بیشترِ این صفحه داوری‌ای است که هیچ ابزاری نمی‌تواند بر عهده بگیرد. آنچه یک ابزار می‌تواند بکند این است که تضمین کند ترجمه نتواند ساختار جمله‌ای را که ترجمه می‌کند عوض کند — نتواند مقداری را بیندازد، مقداری بسازد، مقداری را دوباره قالب‌بندی کند، یا به شیءهای شما دست‌درازی کند — و بتواند این را در جمله‌ای بگوید که کسی که باید درستش کند بتواند بر پایه‌اش کاری بکند. تمامِ آنچه این کتابخانه وعده می‌دهد همین است، و بقیهٔ این وب‌گاه چگونگی وفای به آن است.