gettext --- خدمات بین‌المللی‌سازی چندزبانه

کد منبع: Lib/gettext.py


ماژول gettext خدمات بین‌المللی‌سازی (I18N) و بومی‌سازی (L10N) را برای ماژول‌ها و برنامه‌های پایتون شما فراهم می‌کند. این ماژول هم از API فهرست پیام‌های gettext گنو و هم از یک API سطح بالاتر و مبتنی بر کلاس پشتیبانی می‌کند که ممکن است برای پرونده‌های پایتون مناسب‌تر باشد. رابطی که در زیر توضیح داده شده است، به شما امکان می‌دهد پیام‌های ماژول و برنامه خود را به یک زبان طبیعی بنویسید و فهرستی از پیام‌های ترجمه‌شده برای اجرا در زبان‌های طبیعی مختلف ارائه دهید.

همچنین برخی نکات درباره بومی‌سازی ماژول‌ها و برنامه‌های پایتون شما ارائه شده است.

API مربوط به GNU gettext

ماژول gettext، API زیر را تعریف می‌کند که بسیار شبیه به API gettext گنو است. اگر از این API استفاده کنید، ترجمه‌ی کل برنامه‌ی خود را به‌صورت سراسری تحت تأثیر قرار می‌دهید. معمولاً اگر برنامه‌ی شما تک‌زبانه باشد و انتخاب زبان به تنظیمات locale کاربر شما وابسته باشد، این همان چیزی است که می‌خواهید. اگر در حال localeسازی یک ماژول پایتون هستید، یا اگر برنامه‌ی شما نیاز دارد زبان‌ها را در حین اجرا تغییر دهد، احتمالاً بهتر است به‌جای آن از API مبتنی بر کلاس استفاده کنید.

gettext.bindtextdomain(domain, localedir=None)

دامنه‌ی domain را به پوشه‌ی locale با مسیر localedir مقید می‌کند. به‌طور مشخص‌تر، gettext پرونده‌های دودویی .mo را برای دامنه‌ی داده‌شده با استفاده از مسیر (در یونیکس) جستجو می‌کند: localedir/language/LC_MESSAGES/domain.mo، که در آن language به‌ترتیب در متغیرهای محیطی LANGUAGE، LC_ALL، LC_MESSAGES و LANG جستجو می‌شود.

اگر localedir حذف شود یا None باشد، اتصال جاری برای domain برگردانده می‌شود. [1]

gettext.textdomain(domain=None)

دامنه سراسری کنونی را تغییر دهید یا پرس‌وجو کنید. اگر domain برابر None باشد، دامنه سراسری کنونی برگردانده می‌شود، در غیر این صورت دامنه سراسری روی domain تنظیم می‌شود که برگردانده می‌شود.

gettext.gettext(message, /)

ترجمه localeسازی‌شده message را بر اساس دامنه سراسری، زبان و پوشه locale جاری برمی‌گرداند. این تابع معمولاً با نام _() در فضای نام محلی مستعار می‌شود (نمونه‌های زیر را ببینید).

gettext.dgettext(domain, message, /)

مانند gettext() است، اما پیام را در domain مشخص‌شده جستجو می‌کند.

gettext.ngettext(singular, plural, n, /)

مانند gettext()، اما شکل‌های جمع را در نظر می‌گیرد. اگر ترجمه‌ای پیدا شود، فرمول جمع را روی n اعمال می‌کند و پیام حاصل را برمی‌گرداند (برخی زبان‌ها بیش از دو شکل جمع دارند). اگر ترجمه‌ای پیدا نشود، در صورتی که n برابر ۱ باشد singular را برمی‌گرداند؛ در غیر این صورت plural را برمی‌گرداند.

فرمول جمع از سرآیند کاتالوگ گرفته می‌شود. این یک عبارت C یا Python است که یک متغیر آزاد n دارد؛ حاصل این عبارت، اندیس جمع در کاتالوگ است. برای سینتکس دقیق مورد استفاده در پرونده‌های .po و فرمول‌های زبان‌های مختلف، به مستندات GNU gettext مراجعه کنید.

gettext.dngettext(domain, singular, plural, n, /)

مانند ngettext() است، اما پیام را در domain مشخص‌شده جست‌وجو می‌کند.

gettext.pgettext(context, message, /)
gettext.dpgettext(domain, context, message, /)
gettext.npgettext(context, singular, plural, n, /)
gettext.dnpgettext(domain, context, singular, plural, n, /)

مشابه توابع متناظر بدون p در پیشوند (یعنی gettext()، dgettext()، ngettext()، dngettext())، اما ترجمه به زمینه پیام داده‌شده محدود می‌شود.

اضافه شده در نسخه‌ی 3.8.

توجه داشته باشید که GNU gettext همچنین یک متد dcgettext() تعریف می‌کند، اما این متد مفید تشخیص داده نشد و بنابراین در حال حاضر پیاده‌سازی نشده است.

در اینجا نمونه‌ای از استفاده رایج از این API آمده است:

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))

API مبتنی بر کلاس

API مبتنی بر کلاس ماژول gettext در مقایسه با API gettext GNU، انعطاف‌پذیری بیشتر و سهولت بیشتری به شما می‌دهد. این روش توصیه‌شده برای محلی‌سازی برنامه‌ها و ماژول‌های پایتون شما است. gettext یک کلاس GNUTranslations را تعریف می‌کند که تجزیه‌ی پرونده‌های با قالب .mo GNU را پیاده‌سازی می‌کند و متدهایی برای برگرداندن رشته‌ها دارد. نمونه‌های این کلاس همچنین می‌توانند خود را در فضای نام توکار به‌عنوان تابع _() نصب کنند.

gettext.find(domain, localedir=None, languages=None, all=False)

این تابع، الگوریتم استاندارد جستجوی پرونده .mo را پیاده‌سازی می‌کند. این تابع یک domain می‌گیرد، دقیقاً مشابه آنچه textdomain() می‌گیرد. localedir اختیاری مانند bindtextdomain() است. languages اختیاری فهرستی از رشته‌هاست که هر رشته یک کد زبان است.

اگر localedir داده نشود، از پوشه‌ی locale پیش‌فرض سیستم استفاده می‌شود. [2] اگر languages داده نشود، متغیرهای محیطی زیر جستجو می‌شوند: LANGUAGE، LC_ALL، LC_MESSAGES و LANG. اولین متغیری که مقداری غیرخالی برمی‌گرداند، برای متغیر languages استفاده می‌شود. متغیرهای محیطی باید حاوی فهرستی از زبان‌ها جداشده با دونقطه باشند که بر اساس دونقطه تفکیک می‌شود تا فهرست مورد انتظار از رشته‌های کد زبان تولید شود.

find() سپس زبان‌ها را بسط می‌دهد و عادی‌سازی می‌کند، سپس آن‌ها را پیمایش می‌کند و به دنبال پرونده موجودی می‌گردد که از این کامپوننت‌ها ساخته‌شده است:

localedir/language/LC_MESSAGES/domain.mo

find() اولین نام پرونده‌ای را که از این نوع وجود دارد، بازمی‌گرداند. اگر چنین پرونده‌ای پیدا نشود، None بازگردانده می‌شود. اگر all داده شود، فهرستی از همه‌ی نام پرونده‌ها را به ترتیبی که در فهرست زبان‌ها یا متغیرهای محیطی ظاهر می‌شوند، برمی‌گرداند.

gettext.translation(domain, localedir=None, languages=None, class_=None, fallback=False)

یک نمونه از *Translations بر اساس domain، localedir و languages برمی‌گرداند، که ابتدا به find() داده می‌شوند تا فهرستی از مسیرهای پرونده .mo مرتبط به دست آید. نمونه‌هایی با نام پرونده .mo یکسان در نهانگاه ذخیره می‌شوند. کلاس واقعی که نمونه‌سازی می‌شود، اگر ارائه شده باشد، class_ است؛ در غیر این صورت GNUTranslations است. سازنده‌ی کلاس باید تنها یک آرگومان file object بپذیرد.

اگر چندین پرونده یافت شوند، پرونده‌های بعدی به‌عنوان جایگزین (fallback) برای پرونده‌های قبلی استفاده می‌شوند. برای امکان تنظیم جایگزین از copy.copy() برای رونوشت هر شیء ترجمه از نهانگاه استفاده می‌شود؛ داده‌های واقعی نمونه همچنان با نهانگاه مشترک هستند.

اگر هیچ پرونده .mo یافت نشود، این تابع در صورتی که fallback نادرست باشد (که پیش‌فرض است)، استثنای OSError را پرتاب می‌کند و اگر fallback درست باشد، یک نمونه از NullTranslations را برمی‌گرداند.

تغییر یافته در نسخه‌ی 3.3: IOError در گذشته پرتاب می‌شد، اما اکنون نام مستعاری از OSError است.

تغییر یافته در نسخه‌ی 3.11: پارامتر codeset حذف شده است.

gettext.install(domain, localedir=None, *, names=None)

این کار تابع _() را در فضای نام توکارهای پایتون نصب می‌کند، بر اساس domain و localedir که به تابع translation() ارسال می‌شوند.

برای پارامتر names، لطفاً توضیح متد install() شیء ترجمه را ببینید.

همان‌طور که در زیر می‌بینید، معمولاً رشته‌های برنامه‌تان را که نامزد ترجمه هستند، با قرار دادن آن‌ها در فراخوانی تابع _() علامت‌گذاری می‌کنید، به این صورت:

print(_('This string will be translated.'))

برای سهولت، می‌خواهید تابع _() در فضای نام توکار پایتون نصب شود، تا به‌راحتی در تمام ماژول‌های برنامه شما در دسترس باشد.

تغییر یافته در نسخه‌ی 3.11: names اکنون یک پارامتر فقط کلیدواژه‌ای است.

کلاس NullTranslations

کلاس‌های ترجمه، در واقع ترجمه‌ی رشته‌های پیام پرونده منبع اصلی به رشته‌های پیام ترجمه‌شده را پیاده‌سازی می‌کنند. کلاس پایه‌ای که همه‌ی کلاس‌های ترجمه از آن استفاده می‌کنند، NullTranslations است؛ این کلاس، رابط پایه‌ای را فراهم می‌کند که می‌توانید برای نوشتن کلاس‌های ترجمه‌ی اختصاصی خود از آن استفاده کنید. متدهای NullTranslations به شرح زیر است:

class gettext.NullTranslations(fp=None)

یک شیء پرونده اختیاری به نام fp دریافت می‌کند که توسط کلاس پایه نادیده گرفته می‌شود. متغیرهای نمونه «محافظت‌شده» _info و _charset را که توسط کلاس‌های مشتق‌شده تنظیم می‌شوند، و همچنین _fallback را که از طریق add_fallback() تنظیم می‌شود، مقداردهی اولیه می‌کند. سپس اگر fp برابر None نباشد، self._parse(fp) را فراخوانی می‌کند.

_parse(fp)

این متد در کلاس پایه عملیات بی‌اثر (No-op) است، شیء پرونده fp را می‌گیرد و داده‌ها را از پرونده می‌خواند و کاتالوگ پیام آن را مقداردهی اولیه می‌کند. اگر قالب پرونده کاتالوگ پیام پشتیبانی‌نشده‌ای دارید، باید این متد را برای تجزیه قالب خود بازنویسی کنید.

add_fallback(fallback)

fallback را به‌عنوان شیء جایگزین (fallback) برای شیء ترجمه‌ی فعلی اضافه کنید. یک شیء ترجمه باید در صورتی که نتواند ترجمه‌ای برای یک پیام داده‌شده ارائه کند، به شیء جایگزین (fallback) مراجعه کند.

gettext(message, /)

اگر یک جایگزین (fallback) تنظیم شده باشد، gettext() به جایگزین ارجاع داده می‌شود. در غیر این صورت، message برگردانده می‌شود. در کلاس‌های مشتق‌شده بازنویسی می‌شود.

ngettext(singular, plural, n, /)

اگر یک جایگزین (fallback) تنظیم شده باشد، ngettext() را به جایگزین ارجاع می‌دهد. در غیر این صورت، اگر n برابر ۱ باشد، singular را برمی‌گرداند؛ در غیر این صورت plural را برمی‌گرداند. در کلاس‌های مشتق‌شده بازنویسی می‌شود.

pgettext(context, message, /)

اگر یک جایگزین (fallback) تنظیم شده باشد، pgettext() را به آن جایگزین (fallback) ارجاع می‌دهد. در غیر این صورت، پیام ترجمه‌شده را برمی‌گرداند. در کلاس‌های مشتق‌شده بازنویسی می‌شود.

اضافه شده در نسخه‌ی 3.8.

npgettext(context, singular, plural, n, /)

اگر یک جایگزین (fallback) تنظیم‌شده باشد، npgettext() را به آن جایگزین ارجاع می‌دهد. در غیر این صورت، پیام ترجمه‌شده را برمی‌گرداند. در کلاس‌های مشتق‌شده بازنویسی می‌شود.

اضافه شده در نسخه‌ی 3.8.

info()

یک دیکشنری شامل فراداده‌ی یافت‌شده در پرونده کاتالوگ پیام برمی‌گرداند.

charset()

کدگذاری پرونده کاتالوگ پیام را برمی‌گرداند.

install(names=None)

این متد gettext() را در فضای نام توکار نصب می‌کند و آن را به _ پیوند می‌دهد.

اگر پارامتر names داده شده باشد، باید دنباله‌ای حاوی نام توابعی باشد که می‌خواهید علاوه بر _() در فضای نام builtins نصب کنید. نام‌های پشتیبانی‌شده 'gettext'، 'ngettext'، 'pgettext' و 'npgettext' هستند.

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

import gettext
t = gettext.translation('mymodule', ...)
_ = t.gettext

این، _() را فقط در فضای نام سراسری ماژول قرار می‌دهد و بنابراین فقط بر فراخوانی‌های درون این ماژول تأثیر می‌گذارد.

تغییر یافته در نسخه‌ی 3.8: 'pgettext' و 'npgettext' افزوده شدند.

کلاس GNUTranslations

ماژول gettext یک کلاس اضافی مشتق‌شده از NullTranslations ارائه می‌دهد: GNUTranslations. این کلاس _parse() را بازنویسی می‌کند تا امکان خواندن پرونده‌های .mo با قالب GNU gettext در هر دو قالب بزرگ‌اندیان (big-endian) و کوچک‌اندیان (little-endian) فراهم شود.

GNUTranslations فراداده‌ی اختیاری را از کاتالوگ ترجمه تجزیه می‌کند. در GNU gettext مرسوم است که فراداده به‌عنوان ترجمه‌ی رشته‌ی خالی گنجانده شود. این فراداده به‌صورت جفت‌های key: value به سبک RFC 822 است و باید شامل کلید Project-Id-Version باشد. اگر کلید Content-Type یافت شود، از ویژگی charset برای مقداردهی اولیه‌ی متغیر نمونه‌ی «محافظت‌شده» _charset استفاده می‌شود، که اگر یافت نشود مقدار پیش‌فرض آن None خواهد بود. اگر کدگذاری charset مشخص شده باشد، تمام شناسه‌های پیام و رشته‌های پیام خوانده‌شده از کاتالوگ با استفاده از این کدگذاری به یونیکد تبدیل می‌شوند، در غیر این صورت ASCII فرض می‌شود.

از آنجا که شناسه‌های پیام نیز به‌صورت رشته‌های یونیکد خوانده می‌شوند، تمام متدهای *gettext() شناسه‌های پیام را به‌صورت رشته‌های یونیکد در نظر می‌گیرند، نه رشته‌های بایتی.

مجموعه‌ی کاملی از جفت‌های کلید/مقدار در یک دیکشنری قرار می‌گیرد و به‌عنوان متغیر نمونه «محافظت‌شده» _info تنظیم می‌شود.

اگر شماره جادویی (magic number) پرونده .mo نامعتبر باشد، شماره نسخه اصلی غیرمنتظره باشد، یا مشکلات دیگری هنگام خواندن پرونده رخ دهد، نمونه‌سازی از کلاس GNUTranslations ممکن است OSError را پرتاب کند.

class gettext.GNUTranslations

متدهای زیر نسبت به پیاده‌سازی کلاس پایه بازنویسی شده‌اند:

gettext(message, /)

شناسه‌ی message را در کاتالوگ جست‌وجو می‌کند و رشته‌ی پیام متناظر را به‌صورت یک رشته‌ی یونیکد برمی‌گرداند. اگر ورودی‌ای برای شناسه‌ی message در کاتالوگ وجود نداشته باشد و یک جایگزین (fallback) تنظیم شده باشد، جست‌وجو به متد gettext() مربوط به جایگزین (fallback) ارجاع داده می‌شود. در غیر این صورت، شناسه‌ی message برگردانده می‌شود.

ngettext(singular, plural, n, /)

جست‌وجوی صورت‌های جمع یک شناسه پیام را انجام می‌دهد. singular به‌عنوان شناسه پیام برای جست‌وجو در کاتالوگ استفاده می‌شود، در حالی که n برای تعیین اینکه کدام صورت جمع استفاده شود به کار می‌رود. رشته پیام بازگشتی یک رشته Unicode است.

اگر شناسه پیام در کاتالوگ پیدا نشود و یک جایگزین (fallback) مشخص شده باشد، درخواست به متد ngettext() آن جایگزین ارجاع داده می‌شود. در غیر این صورت، هنگامی که n برابر ۱ باشد، singular برگردانده می‌شود و در تمام موارد دیگر plural برگردانده می‌شود.

در اینجا یک مثال آمده است:

n = len(os.listdir('.'))
cat = GNUTranslations(somefile)
message = cat.ngettext(
    'There is %(num)d file in this directory',
    'There are %(num)d files in this directory',
    n) % {'num': n}
pgettext(context, message, /)

context و شناسه‌ی message را در کاتالوگ جست‌وجو می‌کند و رشته‌ی پیام متناظر را به‌صورت یک رشته‌ی یونیکد برمی‌گرداند. اگر هیچ ورودی‌ای در کاتالوگ برای شناسه‌ی message و context وجود نداشته باشد و یک جایگزین (fallback) تنظیم شده باشد، جست‌وجو به متد pgettext() مربوط به جایگزین (fallback) ارجاع داده می‌شود. در غیر این صورت، شناسه‌ی message برگردانده می‌شود.

اضافه شده در نسخه‌ی 3.8.

npgettext(context, singular, plural, n, /)

جست‌وجوی فرم‌های جمع برای یک شناسه پیام انجام می‌شود. singular به‌عنوان شناسه پیام برای جست‌وجو در کاتالوگ استفاده می‌شود، در حالی که n برای تعیین اینکه کدام فرم جمع استفاده شود به کار می‌رود.

اگر شناسه‌ی پیام برای context در کاتالوگ پیدا نشود، و یک جایگزین مشخص شده باشد، درخواست به متد npgettext() جایگزین ارجاع داده می‌شود. در غیر این صورت، هنگامی که n برابر ۱ باشد، singular برگردانده می‌شود، و در همه‌ی موارد دیگر plural برگردانده می‌شود.

اضافه شده در نسخه‌ی 3.8.

پشتیبانی از فهرست پیام‌های Solaris

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

سازنده‌ی Catalog

GNOME از نسخه‌ای از ماژول gettext نوشته‌ی James Henstridge استفاده می‌کند، اما این نسخه دارای API کمی متفاوتی است. کاربرد مستند آن چنین بود:

import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print(_('hello world'))

برای سازگاری با این ماژول قدیمی‌تر، تابع Catalog() نام مستعاری برای تابع translation() است که در بالا توضیح داده شد.

یکی از تفاوت‌های میان این ماژول و ماژول Henstridge: اشیای کاتالوگ او از دسترسی از طریق یک API نگاشت پشتیبانی می‌کردند، اما به نظر می‌رسد این قابلیت استفاده نشده است و بنابراین در حال حاضر پشتیبانی نمی‌شود.

بین‌المللی‌سازی برنامه‌ها و ماژول‌های شما

بین‌المللی‌سازی (I18N) به عملیاتی اشاره دارد که طی آن یک برنامه از چندین زبان آگاه می‌شود. بومی‌سازی (L10N) به تطبیق برنامه شما، پس از بین‌المللی‌سازی، با زبان محلی و عادات فرهنگی اشاره دارد. برای فراهم کردن پیام‌های چندزبانه برای برنامه‌های پایتون خود، باید مراحل زیر را انجام دهید:

  1. برنامه یا ماژول خود را با نمادگذاری ویژه‌ی رشته‌های قابل‌ترجمه آماده کنید

  2. اجرای بدنه‌ای از ابزارها بر روی پرونده‌های علامت‌گذاری‌شده‌ی شما برای تولید کاتالوگ‌های خام پیام

  3. ایجاد ترجمه‌های مختص هر زبان برای کاتالوگ‌های پیام

  4. از ماژول gettext استفاده کنید تا رشته‌های پیام به‌درستی ترجمه شوند

برای آماده‌سازی کد خود برای بین‌المللی‌سازی (I18N)، باید تمام رشته‌های موجود در پرونده‌های خود را بررسی کنید. هر رشته‌ای که باید ترجمه شود، باید با قرار دادن آن در _('...') علامت‌گذاری شود --- یعنی فراخوانی تابع _. برای مثال:

filename = 'mylog.txt'
message = _('writing a log message')
with open(filename, 'w') as fp:
    fp.write(message)

در این مثال، رشته 'writing a log message' به‌عنوان نامزدی برای ترجمه علامت‌گذاری شده است، اما رشته‌های 'mylog.txt' و 'w' علامت‌گذاری نشده‌اند.

چند ابزار برای استخراج رشته‌های موردنظر برای ترجمه وجود دارد. نسخه اصلی GNU gettext فقط از کد منبع C یا C++ پشتیبانی می‌کرد، اما نسخه گسترش‌یافته آن xgettext کد نوشته‌شده به چندین زبان، از جمله Python، را پویش می‌کند تا رشته‌های علامت‌گذاری‌شده به‌عنوان قابل‌ترجمه را پیدا کند. Babel یک کتابخانه بین‌المللی‌سازی Python است که شامل یک اسکریپت pybabel برای استخراج و کامپایل کاتالوگ‌های پیام است. برنامه‌ای از François Pinard به نام xpot کار مشابهی انجام می‌دهد و به‌عنوان بخشی از بسته po-utils او در دسترس است.

(پایتون همچنین شامل نسخه‌های پایتون خالص این برنامه‌ها نیز می‌شود که pygettext.py و msgfmt.py نامیده می‌شوند؛ برخی توزیع‌های پایتون این برنامه‌ها را برای شما نصب خواهند کرد. pygettext.py مشابه xgettext است، اما فقط کد منبع پایتون را می‌شناسد و نمی‌تواند زبان‌های برنامه‌نویسی دیگر مانند C یا C++ را پردازش کند. pygettext.py از یک رابط خط فرمان مشابه xgettext پشتیبانی می‌کند؛ برای جزئیات استفاده از آن، pygettext.py --help را اجرا کنید. msgfmt.py از نظر دودویی با GNU msgfmt سازگار است. با این دو برنامه، ممکن است برای بین‌المللی‌سازی برنامه‌های پایتون خود به بسته GNU gettext نیاز نداشته باشید.)

xgettext، pygettext و ابزارهای مشابه، پرونده‌های .po را تولید می‌کنند که کاتالوگ‌های پیام هستند. آن‌ها پرونده‌های ساختاریافته و قابل‌خواندن برای انسان هستند که هر رشته‌ی علامت‌گذاری‌شده در کد منبع را به‌همراه یک جای‌نگهدار برای نسخه‌های ترجمه‌شده‌ی این رشته‌ها در بر دارند.

سپس نسخه‌هایی از این پرونده‌های .po به‌صورت انفرادی به مترجمان انسانی تحویل داده می‌شوند تا ترجمه‌هایی برای هر زبان طبیعی پشتیبانی‌شده بنویسند. آن‌ها نسخه‌های کامل‌شده‌ی مختص هر زبان را به‌صورت یک پرونده <language-name>.po بازمی‌گردانند که با استفاده از برنامه‌ی msgfmt به یک پرونده کاتالوگ دودویی .mo قابل خواندن توسط ماشین کامپایل می‌شود. پرونده‌های .mo توسط ماژول gettext برای پردازش واقعی ترجمه در ران‌تایم استفاده می‌شوند.

روش استفاده شما از ماژول gettext در کدتان بستگی به این دارد که در حال بین‌المللی‌سازی یک ماژول هستید یا کل برنامه خود. دو بخش بعدی هر یک از این موارد را بررسی خواهند کرد.

بومی‌سازی ماژول شما

اگر در حال بومی‌سازی ماژول خود هستید، باید مراقب باشید که تغییرات سراسری ایجاد نکنید، مثلاً در فضای نام توکار. نباید از API مربوط به GNU gettext استفاده کنید، بلکه باید از API مبتنی بر کلاس استفاده کنید.

فرض کنید ماژول شما "spam" نام دارد و پرونده‌های .mo مربوط به ترجمه‌های مختلف زبان طبیعی ماژول در /usr/share/locale با قالب GNU gettext قرار دارند. آنچه باید در ابتدای ماژول خود بگذارید:

import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext

بومی‌سازی برنامه شما

اگر در حال بومی‌سازی برنامه خود هستید، می‌توانید تابع _() را به‌صورت سراسری در فضای نام توکار نصب کنید، معمولاً در پرونده راه‌انداز اصلی برنامه خود. این کار باعث می‌شود تمام پرونده‌های مختص برنامه شما بتوانند فقط از _('...') استفاده کنند، بدون این‌که لازم باشد آن را به‌صراحت در هر پرونده نصب کنید.

بنابراین در حالت ساده، تنها لازم است قطعه‌کد زیر را به پرونده راه‌انداز اصلی برنامه‌تان اضافه کنید:

import gettext
gettext.install('myapplication')

اگر نیاز دارید پوشه locale را تنظیم کنید، می‌توانید آن را به تابع install() بفرستید:

import gettext
gettext.install('myapplication', '/usr/share/locale')

تغییر زبان در لحظه

اگر برنامه شما نیاز دارد همزمان از زبان‌های بسیاری پشتیبانی کند، ممکن است بخواهید چندین نمونه ترجمه ایجاد کنید و سپس به‌صورت صریح بین آن‌ها جابه‌جا شوید، مانند این:

import gettext

lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])

# start by using language1
lang1.install()

# ... time goes by, user selects language 2
lang2.install()

# ... more time goes by, user selects language 3
lang3.install()

ترجمه‌های به‌تعویق‌افتاده

در بیشتر شرایط کدنویسی، رشته‌ها در همان جایی که کد شده‌اند ترجمه می‌شوند. با این حال، گاهی لازم است رشته‌ها را برای ترجمه علامت‌گذاری کنید، اما ترجمه واقعی را تا بعد به تعویق بیندازید. یک مثال کلاسیک:

animals = ['mollusk',
           'albatross',
           'rat',
           'penguin',
           'python', ]
# ...
for a in animals:
    print(a)

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

در اینجا یک روش برای مدیریت این وضعیت آمده است:

def _(message): return message

animals = [_('mollusk'),
           _('albatross'),
           _('rat'),
           _('penguin'),
           _('python'), ]

del _

# ...
for a in animals:
    print(_(a))

این روش کار می‌کند، زیرا تعریف ساختگی _() صرفاً رشته را بدون تغییر برمی‌گرداند. و این تعریف ساختگی به‌طور موقت هر تعریفی از _() در فضای نام توکار را می‌پوشاند (تا دستور del). البته اگر تعریف پیشینی از _() در فضای نام محلی دارید، مراقب باشید.

توجه داشته باشید که دومین استفاده از _()، «a» را به‌عنوان قابل‌ترجمه برای برنامه‌ی gettext شناسایی نخواهد کرد، زیرا پارامتر یک رشته‌ی لفظی نیست.

راه دیگر برای مدیریت این موضوع، استفاده از مثال زیر است:

def N_(message): return message

animals = [N_('mollusk'),
           N_('albatross'),
           N_('rat'),
           N_('penguin'),
           N_('python'), ]

# ...
for a in animals:
    print(_(a))

در این حالت، شما رشته‌های ترجمه‌پذیر را با تابع N_() علامت‌گذاری می‌کنید، که با هیچ تعریفی از _() تعارض نخواهد داشت. با این حال، باید به برنامه استخراج پیام خود یاد بدهید که به دنبال رشته‌های ترجمه‌پذیری بگردد که با N_() علامت‌گذاری شده‌اند. xgettext، pygettext، pybabel extract و xpot همگی این کار را از طریق استفاده از سوئیچ خط فرمان -k پشتیبانی می‌کنند. انتخاب N_() در اینجا کاملاً دلخواه است؛ می‌توانست به همین آسانی MarkThisStringForTranslation() باشد.

قدردانی‌ها

افراد زیر برای ایجاد این ماژول، کد، بازخورد، پیشنهادهای طراحی، پیاده‌سازی‌های پیشین و تجربه ارزشمند ارائه کردند:

  • Peter Funk

  • James Henstridge

  • Juan David Ibáñez Palomar

  • Marc-André Lemburg

  • مارتین فون لوویس

  • فرانسوا پینار

  • Barry Warsaw

  • گوستاوو نیمایر

پانویس‌ها