آموزش بین المللی سازی (Internationalization) در جنگو (Django)
جنگو در ابتدا درست در ایالات متحده ی میانی توسعه یافت – در معنای واقعی کلمه، در لارنس، کنزاس، کمتر از 40 مایلی مرکز جغرافیای قاره ایالات متحده. همانند اغلب پروژه های منبع باز (open source)، جنگو در بین مردم سرتاسر زمین بزرگ شد. همانطور که جامعه جنگو به طور افزاینده ای متنوع می شدند، internationalization و localization به طور افزاینده ای دارای اهمیت شد. به دلیل آن که بسیاری از توسعه دهندگان درک مبهمی از این دوره ها دارند، به طور اختصار آنها را تعریف می کنیم.
internationalization به پردازش طراحی برنامه ها برای استفاده ی بالقوه از هر مکان رجوع می کند. این شامل علامت گذاری متن (از قبیل المان های UI و پیام های خطا) برای ترجمه ی آینده، abstract کردن نمایش تاریخ ها و زمان ها به طوری که استاندارهای مختلف محلی ممکن observed باشه، تهیه ی پشتیبانی برای منطقه های زمانی مختلف، و به طور کلی اطمینان از اینکه کد حاوی هیچ فرضی در باره ی محل آن کاربران نباشد. اغلب "internationalization" را به صورت اختصار I18N می بینید. ("18" به تعداد حروف حذف شده بین حرف اول "I" و حرف آخر "N" می باشد.)
localization به پردازش ترجمه ی واقعی یک برنامه ی internationalize شده برای استفاده در یک محل خاص رجوع می کند. گاهی اوقات "localization" به صورت اختصار L10N می بینید.
خود جنگو به طور کامل internationalize شده می باشد؛ تمام رشته ها برای ترجمه علامت گذاری شده اند، و تنظیمات نمایش مقادیر local-dependent مانند تاریخ ها و زمان ها را کنترل می کنند. جنگو همچنین دارای بیشتر از 50 فایل localization می باشد. در صورتی که زبان مادری شما انگلیسی نمی باشد، یک شانس خوب وجود دارد که جنگو قبلا به زبان اصلی شما ترجمه شده است.
فریم ورک همسان internationalization برای این localization ها برای شما جهت استفاده از کد و template های خودتان در دسترس می باشند.
جهت استفاده از این فریم ورک یا چارچوب، نیاز به اضافه کردن حداقل hook ها به کد پایتون و template های خود دارید. این hook ها رشته های ترجمه نام دارند. آن ها به جنگو می گویند، "این متن باید به زبان کاربر ترجمه شود، در صورتی که یک ترجمه برای این متن در آن زبان در دسترس باشد."
فریم ورک یا چارچوب جنگو (Django) مراقب استفاده از این hook ها برای ترجمه ی برنامه های وب می باشد، بسیار سریع، بر طبق تنظیمات زبان کاربر.
اساسا، جنگو دو کار را انجام می دهد:
- به توسعه دهندگان و نویسندگان template اجازه می دهد که بخش های برنامه ی خودشان که باید قابل ترجمه باشد را تعیین کنند.
- از آن اطلاعات جهت ترجمه ی برنامه های وب برای کاربران خاص بر طبق زبان مورد ترجیح استفاده می کند.
نکته
دستگاه ترجمه ی جنگو از GNU gettext (http://www.gnu.org/software/gettext/) از طریق ماژول gettext که در پایتون ارائه شده است استفاده می کند.
در صورتی که به internationalization نیازی نداشته باشید:
hook های internationalization جنگو به طور پیشفرض فعال می باشند، که موجب کمی بار اضافی می شود. در صورتی که از internationalization استفاده نمی کنید، باید درون فایل تنظیمات USE_I18N = False را قرار دهید. در صورتی که USE_I18N مقدار False باشد، جنگو بهینه سازی هایی را برای بارگذاری نشدن دستگاه internationalization انجام می دهد.
همچنین شاید بخواهید 'django.core.context_processors.i18n' از تنظیم TEMPLATE_CONTEXT_PROCESSORS حذف کنید.
سه مرحله برای internationalize کردن برنامه جنگو به قرار زیر می باشند:
- رشته های ترجمه را در کد پایتون خود را template قرار دهید.
- ترجمه هایی برای آن رشته های بدست آورید، در هر زبانی که برای پشتیبانی می خواهید.
- locale middleware را در تنظیمات جنگو خود فعال کنید.
هر کدام از این مراحل را به تفصیل پوشش خواهیم داد.
1. نحوه ی تعیین رشته های ترجمه
رشته های ترجمه "این متن باید ترجمه شده باشد" تعیین می کنند این رشته ها می توانند در کد پایتون و template های شما ظاهر شوند. علامت گذاری رشته های ترجمه بر عهده ی شما می باشد؛ سیستم تنها می تواند رشته هایی را که می شناسد ترجمه کند.
در کد پایتون
ترجمه استاندارد
تعیین یک رشته ی ترجمه با استفاده از تابع ugettext() می باشد. به صورت قرار است که این تابع به صورت یک نام مستعار کوتاه تر یعنی "_" import شود.
در مثال زیر، متن "Welcome to my site." به صورت رشته ی ترجمه علامت گذاری شده است:
from django.utils.translation import ugettext as _
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
واضح است، می توانید بدون استفاده از نام مستعار کد فوق را بنویسید. مثال زیر برابر با مثال فوق می باشد:
from django.utils.translation import ugettext
def my_view(request):
output = ugettext("Welcome to my site.")
return HttpResponse(output)
ترجمه با مقدار محاسبه شده کار می کند. مثال زیر برابر با دو مثال قبلی می باشد:
def my_view(request):
words = ['Welcome', 'to', 'my', 'site.']
output = _(' '.join(words))
return HttpResponse(output)
ترجمه با متغیرها نیز کار می کند. برای بار دیگر، مثال زیر برابر با مثال های قبلی می باشد:
def my_view(request):
sentence = 'Welcome to my site.'
output = _(sentence)
return HttpResponse(output)
(اخطار در مورد استفاده از متغیرها یا مقادیر محاسبه شده، همانطور که در دو مثال قبلی مشاهده کردید، این است که مزیت تشخیص رشته ی ترجمه یعنی django-admin.py makemessages، قادر به یافتن این رشته ها نخواهد بود. در مورد makemessage کمی بعد بیشتر توضیح خواهیم داد.)
رشته هایی که به _() یا ugettext() ارسال می کنید، می توانند placeholder هایی دریافت کنند، که درون زبان پایتون وجود دارند. مثال:
def my_view(request, m, d):
output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
return HttpResponse(output)
این تکنیک به ترجمه های زبان خاص اجازه می دهد متن placeholder را دوباره چیدمان کنند. برای مثال، یک ترجمه ی انگلیسی ممکن است "Today is November 26." باشد، هنگام ترجمه ی اسپانیایی ممکن است "Hoy es 26 de Noviembre." باشد – با placeholder ها (ماه و روز) با موقعیت های عوض شدهی آنها.
به همین دلیل، شما باید از رشته های نام گذاری شده (مانند %(day)s) به جای رشته های موضعی (مانند، %s یا %d) هر زمان که بیشتر از یک پارامتر وجود دارد استفاده کنید. در صورتی که از رشته های موضعی استفاده می کنید قادر نمی باشد placeholder متن را دوباره چیدمان کنید.
علامت گذاری رشته ها به صورت No-Op
از تابع django.utils.translation.ugettext_noop() جهت علامت گذاری یک رشته به صورت یک رشته ی ترجمه بدون ترجمه کردن آن استفاده کنید.
در صورتی که رشته های ثابتی دارید که باید در ریشه ی زبان ذخیره شده باشند چرا که آن ها در سرتاسر سیستم ها و کاربران رد و بدل می شوند – مانند رشته های درون یک پایگاه داده – ولی باید در آخرین نقطه ی ممکن ترجمه شوند، مانند زمانی که رشته به کاربر نشان داده می شود، از این استفاده کنید.
ترجمه ی Lazy
از تابع django.utils.translation.ugettext_lazy() جهت ترجمه ی رشته های به صورت lazy استفاده کنید – زمانی که مقدار در دسترس است، به جای هنگامی که تابع ugettext_lazy() فراخوانی شده است.
برای مثال، جهت ترجمه ی مدل help_text کد زیر را عمل کنید:
from django.utils.translation import ugettext_lazy
class MyThing(models.Model):
name = models.CharField(help_text=ugettext_lazy('This is the help text'))
در مثال فوق، ugettext_lazy() یک بازگشت lazy به رشته را ذخیره می کند – نه ترجمه ی واقعی را. خود ترجمه، هنگامی که رشته در یک رشته ی context استفاده شده باشد، مانند render کردن template در سایت مدیر جنگو، انجام خواهد شد.
نتیجه فراخوانی یک ugettext_lazy() می تواند هر جایی که شما یک رشته ی یونیکد (یک شیء از نوع unicode) در پایتون استفاده می کند استفاده شده باشد. در صورتی که سعی کنید در جایی که انتظار یک bytestring (شیء str) از آن استفاده کنید، هیچ چیز آن طوری که انتظار آن می رود کار نخواهد کرد، چون یک شیء uggettext_lazy() نحوه ی تبدیل خودش به یک bytestring را نمی داند. نمی توانید از یک رشته ی یونیکد درون یک bytestring استفاده کنید، بنابراین این رفتار عادی پایتون می باشد. برای مثال:
# This is fine: putting a unicode proxy into a unicode string.
u"Hello %s" % ugettext_lazy("people")
# This will not work, since you cannot insert a unicode object
# into a bytestring (nor can you insert our unicode proxy there)
"Hello %s" % ugettext_lazy("people")
در صورتی که همواره خروجی شبیه به "hello <django.utils.functional...>" مشاهده می کند، باید سعی به درج نتیجه ی ugettext_lazy() به درون یک bytestring کنید. آن یک اشکال در کد شما می باشد.
در صورتی که نام طولانی ugettext_lazy را نمی پسندید، می توانید از نام مستعار "_" (خط تیره)، مانند زیر استفاده کنید:
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
همواره از ترجمه های lazy در مدل های جنگو استفاده کنید. نام های فیلد و نام جدول باید برای ترجمه علامت گذاری شده باشند(در غیر این صورت، آن ها درون رابط مدیر ترجمه نخواهند شد). این بدین معنی است که نوشتن صریح option های verbose_name و verbose_name_plural در کلاس Meta، به جای اعتماد به تعیین پیشفرض verbose_name و verbose_name_plural جنگو توسط نگاه به نام کلاس مدل:
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(_('name'), help_text=_('This is the help text'))
class Meta:
verbose_name = _('my thing')
verbose_name_plural = _('mythings')
جمع بندی
برای تعیین پیام های جمع بندی شده از تابع django.utils.translation.ungettext() استفاده کنید. مثال:
from django.utils.translation import ungettext
def hello_world(request, count):
page = ungettext('there is %(count)d object',
'there are %(count)d objects', count) % {
'count': count,
}
return HttpResponse(page)
ungettext سه آرگومان دریافت می کند: رشته ی ترجمه ی تک، رشته ی ترجمه ی جمع و تعداد شیء ها (که به زبان های ترجمه به صورت متغیر count ارسال شده است).
در کد Template
ترجمه در template های جنگو از دو تگ template و یک syntax کمی متفاوت تر از کد پایتون استفاده می کند. برای دادن دسترسی به این تگ ها به template خود، {% load i18n %} را نزدیک بالای template خود قرار دهید.
تگ template {% trans %} هر دوی رشته ی ثابت (احاطه شده درون دابل کتیشن یا تک کتیشن) یا محتوای متغیر را ترجمه می کند:
<title>{% trans "This is the title." %}</title>
<title>{% trans myvar %}</title>
در صورتی که آپشن noop موجود باشد، variable lookup همچنان اتفاه می افتد ولی ترجمه انجام نمی شود. در دست ترجمه ....
<title>{% trans "myvar" noop %}</title>
امکان ترکیب یک متغیر template درون یک رشته ی داخل {% trans %} امکان پذیر نمی باشد. در صورتی که ترجمه های شما نیازمند رشته هایی با متغیرها (placeholder ها) باشد، از {% blocktrans %} استفاده کنید:
{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}
جهت ترجمه ی یک عبارت template – تصور کنید، از فیلترهای template استفاده می کند – نیاز چسباندن عبارت به متغیر محلی برای استفاده درون بلاک ترجمه می باشد:
{% blocktrans with value|filter as myvar %}
This will have {{ myvar }} inside.
{% endblocktrans %}
در صورتی که نیاز به چسباندن بیشتر از یک عبارت داخل یک تگ blocktrans دارید، قسمت ها را با and جدا کنید:
{% blocktrans with book|title as book_t and author|title as author_t %}
This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}
جهت جمع بستن، هر دو فرم های تک و جمع را با تگ {% plural %} تعیین کنید، که درون {% blocktrans %} و {% endblocktrans %} ظاهر می شوند. مثال:
{% blocktrans count list|length as counter %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktrans %}
به طور داخلی، تمام بلاک و ترجمه های درون خطی از فراخوانی مناسب ugettext / ungettext استفاده می کنند.
هر RequestContext دارای دسترسی به سه متغیر ترجمه ی خاص می باشد:
- LANGUAGE لیستی از تاپل ها می باشد که در المان اول کد زبان و دومی نام زبان (ترجمه شده به locale فعال فعلی) می باشد.
- LANGUAGE_CODE زبان مورد ترجیح کاربر فعلی به صورت یک رشته می باشد. مثال: en-us. (به بخش "نحوه ی پی بردن جنگو به زبان مورد ترجیح" در همین آموزش مراجعه کنید.)
- LANGUAGE_BIDI جهت locale فعلی می باشد. در صورتی که True باشد، یک زبان راست به چپ، مانند: فارسی، عربی می باشد. در صورتی که False باشد یک زبان چپ به راست مانند : انگلیسی، فرانسوی و آلمانی می باشد.
در صورتی که از RequestContext extension استفاده نمی کنید، می توان آن مقادیر را با سه تگ زیر بدست آورد:
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_current_language_bidi as LANGUAGE_BIDI %}
این تگ ها همچنین به یک {% load i18n %} نیاز دارند
{% some_special_tag _("Page not found") value|yesno:_("yes,no") %}
نکته
در این مثال، زیر ساخت ترجمه، رشته ی "yes,no" را ارسال می کند، نه رشته های فردی "yes" و "no". رشته ی ترجمه شده نیازمند علامت کاما "," می باشد، به طوری که تجزیه کننده ی کد فیلتر نحوه ی جدا کردن آرگومان ها را بداند. برای مثال، یک مترجم آلمانی ممکن است رشته ی "yes,no" را به صورت "ja,nein" ترجمه کند (نگهداشتن کاما دست نخورده).
کار با شیء های ترجمه ی Lazy
استفاده از ugettext_lazy() و ungettext_lazy() برای علامت گذاری رشته ها در مدل ها و توابع یک عمل رایج می باشد. هنگامی که با این شیء ها در جای دیگر کد خود کار می کنید، باید مطمئن باشید که به طور تصادفی آن ها را رشته ها تبدیل نمی کنید، زیرا آن ها باید در آخرین زمان ممکن تبدیل شوند (در دست ترجمه ...). این موضوع ایجاب می کند که از چند تابع کمکی استفاده کنید.
متصل کردن رشته ها: string_concat()
join های رشته ی استاندارد پایتون (''.join([...])) بر روی لیست های حاوی شیء های ترجمه ی lazy کار نخواهند کرد. در عوض، می توانید از django.utils.translation.string_concat() استفاده کنید، که یک شیء lazy ایجاد می کند که محتویات آن را به هم وصل کرده و آن ها را تنها هنگامی که نتیجه در یک رشته شامل شده باشد، به هم متصل می کند. برای مثال:
from django.utils.translation import string_concat
# ...
name = ugettext_lazy(u'John Lennon')
instrument = ugettext_lazy(u'guitar')
result = string_concat([name, ': ', instrument])
در این مورد، ترجمه ی lazy در result تنها زمانی که خود result در یک رشته استفاده شده باشد به رشته ها تبدیل می شود (معمولا زمان render شدن template).
دکوریتور allow_lazy()
جنگو بسیاری توابع سودمند (به ویژه در django.utils) ارائه می کند که یک رشته را به صورت اولین آرگومان دریافت می کنند و کاری را بر روی آن رشته انجام می دهند. این توابع توسط فیلترهای template همچنین به طور مستقیم در کد دیگر استفاده می شوند.
در صورتی که توابع مشابه خود را بنویسید و با ترجمه های سر و کار داشته باشید، زمانی که اولین آرگومان یک شیء ترجمه ی lazy باشد با مشکل چه باید بکنم رو به رو می شوید. شما نمی خواهید آن را بلا فاصله به یک رشته تبدیل کنید، چرا که ممکن است تابع را بیرون از view نیز استفاده کنید (در دست ترجمه ...).
برای موارد شبیه به این، از دکوریتور django.utils.functional.allow_lazy() استفاده کنید. این دکوریتور تابع اصلاح می کند، به طوری که اگر با یک ترجمه lazy به عنوان اولین آرگومان فراخوانی شود، ارزیابی تابع تا زمانی که نیاز باشد به یک رشته تبدیل شود به تاخیر می افتد.
برای مثال:
from django.utils.functional import allow_lazy
def fancy_utility_function(s, ...):
# Do some conversion on string 's'
# ...
fancy_utility_function = allow_lazy(fancy_utility_function, unicode)
در دست ترجمه ....
استفاده از این دکوریتور بدین معناست که شما می توانید تابع خود را نوشته و فرض کنید که ورودی یک رشته ی مناسب می باشد، سپس پشتیبان برای شیء های ترجمه ی lazy در پایان اضافه کنید.
2. نحوه ی ساختن فایل های زبان
هنگامی رشته های خود را برای ترجمه ی بعدی ضمیمه کردید، نیاز به نوشتن (بدست آوردن) خود ترجمه های زبان دارید. در زیر نحوه ی این عمل وجود دارد.
محدودیت های Locale
جنگو localize کردن برنامه ی شما را به داخل یک locale که خود جنگو ترجمه نکرده است را پشتیبانی نمی کند. در این مورد، فایل های ترجمه ی شما نادیده گرفته می شوند. در صورتی که برای این مورد تلاش کرده اید و جنگو از آن پشتیبانی کرده است، ناگذیر به یک مخلوط از رشته های ترجمه خود (از برنامه خود) رشته های انگلیسی (از خود جنگو) نگاه کنید. در صورتی که می خواهید یک locale برای برنامه ی خود را پشتیبیانی کنید که قبلا بخشی از جنگو نمی باشد، نیاز به ساختن حداقل یک ترجمه از هسته جنگو خواهید داشت.
فایل های پیام
اولین قدم برای یک زبان جدید ساختن یک فایل پیام می باشد. فایل پیام یک فایل متنی ساده برای نشان دادن یک زبان است، که حاوی تمام رشته های در دسترس برای ترجمه و چگونگی نمایش آن ها در زبان داده شده است. فایل های پیام دارای پسوند .po می باشند.
جنگو ابزاری به نام django‑admin.py makemessages ارائه می کند، که ساختن و نگهداری این فایل ها را خودکار می کند. جهت ساخت و به روز رسانی یک فایل پیام، دستور زیر را اجرا کنید:
django-admin.py makemessages -l de
de کد زبان برای فایل پیامی که می خواهید بسازید می باشد. کد زبان، در این مورد در قابل بندی locale می باشد. برای مثال، pt_BR برای برزیلی پرتغالی و de_AT برای اتریشی آلمانی می باشد.
اسکریپت باید از یکی از این سه مکان اجرا شود:
- دایرکتوری ریشه ی پروژه ی جنگوی شما.
- دایرکتوری ریشه ی app جنگوی شما.
این اسکریپت در سرتاسر درخت منبع پروژه یا درخت منبع برنامه اجرا می شود و تمام رشته های علامت گذاری شده برای ترجمه را بیرون می کشد. این اسکریپت یک فایل پیام در دایرکتوری locale/LANG/LC_MESSAGES می سازد (یا بروز رسانی می کند). در مثال de، فایل locale/de/LC_MESSAGES/django.po خواهد بود.
به طور پیشفرض django-admin.py makemessages هر فایلی که دارای پسوند .html باشد را بررسی می کند. در مواردی که می خواهید آن پیشفرض را override کنید، از آپشن –extension یا –e برای تعیین پسوندهای فایل جهت بررسی استفاده کنید:
django-admin.py makemessages -l de -e txt
چندین پسوند را با کاما و / یا با استفاده از –e یا ––extension چندین بار جدا کنید:
django-admin.py makemessages -l de -e html,txt -e xml
زمان ساختن کاتالوگ های ترجمه ی جاوا اسکریپت (که کمی بعد در این آموزش از کتاب پوشش داده خواهد شد،) نیاز به استفاده از دامنه ی خاص 'djangojs' دارید، نه –e js.
gettext نه؟
در صورتی که فواید gettext را نصب ندارید، django‑admin.py makemessages فایل های خالی خواهد ساخت. اگر چنین است می توانید gettext را نصب کنید و یا فایل پیام (locale/en/LC_MESSAGES/django.po) انگلیسی را که فقط یک فایل خالی ترجمه است در صورت دسترس کپی کنید و از آن به عنوان نقطه شروع استفاده کنید.
با سیستم عامل ویندوز کار می کنید؟
در صورتی که از ویندوز استفاده می کنید و نیاز به نصب فواید GNU gettext برای عملکرد django‑admin makemessages دارید، به بخش "gettext در ویندوز" در همین فصل مراجعه کنید.
قالب بندی فایل های .po ساده می باشد. هر فایل .po حاوی یک قسمت کوچک از metadata، از قبیل اطلاعات تماس maintainer ترجمه، توده ی فایل یک لیست از پیام ها می باشد – ارتباط های ساده بین رشته های ترجمه و متن ترجمه شده ی واقعی برای زبان خاص.
برای مثال، در صورتی که app جنگو شما حاوی یک رشته ی ترجمه برای متن "Welcom to my site." مانند زیر می باشد:
_("Welcome to my site.")
... سپس django-admin.py makemessages یک فایل .po حاوی تکه کد های زیر را خواهد ساخت – یک پیام:
#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr ""
یک توضیح سریع:
- msgid رشته ی ترجمه می باشد، که در منبع وجود دارد. آن را تغییر ندهید.
- msgstr جایی است که ترجمه ی زبان خاص را در آن جا قرار می دهید. که با مقدار خالی شروع می کند، بنابراین تغییر دادن آن به عهده ی شما می باشد. اطمینان حاصل کنید که ترجمه ی شما درون کتیشن می باشد.
- برای راحتی، هر پیام به صورت شکلی از خط کامنت شروع شده با علامت "#" می باشد و در بالای خط msgid قرار گرفته است، filename و شماره خط از رشته ی ترجمه شده جمع شده اند.
پیام های طولانی یک مورد خاص می باشند. در آن جا، اولین رشته به طور مستقیم بعد از msgstr (یا msgid) یک رشته ی خالی می باشد. سپس خود محتوا خودش سرتاسر چند خط بعدی به صورت یک رشته در هر خط نوشته شده خواهند بود. آن رشته ها به صورت مستقیم متصل شده اند. space های عقبی درون رشته ها را فراموش نکنید؛ در غیر اینصورت، آن ها بدون فاصله به یکدیگر وصل می شوند!
جهت بررسی دوباره ی تمام کد منبع و template ها برای رشته های ترجمه ی جدید و به روز رسانی تمام فایل های پیام برای تمام زبان ها، دستور زیر را اجرا کنید:
django-admin.py makemessages -a
کامپایل فایل های Message
بعد از این که فایل پیام خود را ایجاد نمودید – و هر بار که تغییراتی در آن انجام دهید – نیاز به کامپایل آن درون یک فرم موثرتر خواهید داشت، جهت استفاده با gettext. این کار را با مزیت django‑admin.py compilemessages انجام دهید.
این ابزار سرتاسر تمام فایل های .po در دسترس اجرا می شود و فایل های .mo را ایجاد می کند که فایل های باینری بهینه شده برای استفاده توسط gettext می باشند. درون دایرکتوری همسانی که دستور django‑admin.py makemessages را اجرا کردید، دستور django-admin.py compilemessages را مانند زیر اجرا کنید:
django-admin.py compilemessages
همین. ترجمه های شما آماده برای استفاده می باشند.
3. طریقه ی پی بردن جنگو به ترجیح زبان
هنگامی ترجمه های خود را آماده می کنید – یا، در صورتی که تنها می خواهید از ترجمه های موجود در خود جنگو استفاده کنید – تنها نیاز خواهید داشت ترجمه را برای app خود فعال کنید.
در پشت صحنه، جنگو دارای یک مدل خیلی منعطف از تصمیم گیری در مورد زبانی که باید استفاده شود می باشد – installation‑wide برای یک کاربر خاص، یا هر دو.
برای قرار دادن preference زبان installation-wide، LANGUAGE_CODE را قرار دهید. جنگو از این زبان به صورت ترجمه ی پیشفرض استفاده می کند – تلاش نهایی در صورتی که مترجم دیگری یک ترجمه پیدا کند.
در صورتی که بخواهید جنگو را با زبان مادری خود اجرا کنید، و یک فایل زبان برای زبان شما در دسترس می باشد، نیاز به قرار دادن LANGUAGE_CODE دارید.
در صورتی که می خواهید برای هر کاربر منحصر به فرد زبان مورد ترجیح خودش را مشخص کنید، از LocaleMiddleware استفاده کنید. LocaleMiddleware انتخاب زبان بر اساس داده ی درخواست را فعال می کند. LocaleMiddleware محتوا را برای کاربر سفارشی می کند.
جهت استفاده از LocaleMiddlewareT، 'django.middleware.locale.localeMiddleware' را به تنظیم MIDDLEWARE_CLASSES اضافه کنید. به دلیل آن که ترتیب middleware مهم می باشد، باید دستور العمل های زیر را دنبال کنید:
- اطمینان حاصل کنید آن اولین middleware نصب شده می باشد.
- باید بعد از SessionMidedleware بیاید، زیرا LocaleMiddleware از داده session استفاده می کند.
- در صورتی که از CacheMiddleware استفاده می کند، LocaleMiddleware را بعد از آن قرار دهید.
به عنوان مثال، MIDDLEWARE_CLASSES ممکن است شبیه به زیر باشد:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
)
(برای اطلاعات بیشتر درباره ی middleware، به مبحث middleware مراجعه کنید.)
LocaleMiddleware تلاش می کند preference زبان کاربر را توسط الگوریتم زیر تعیین کند:
- ابتدا، به دنبال کلید django_language در session کاربر فعلی می گردد.
- در صورت نبود، به دنبال یک کوکی می گردد.
- در صورت نبود، به HTTP هدر Accept-Language نگاه می کند. این هدر توسط مرورگر شما ارسال شده است و به سرور زبانی را که شما ترجیح می دهید را می گوید. جنگو برای هر زبان در هدر تلاش می کند تا یک را با ترجمه های در دسترس بایبد.
- در صورت نبود، از تنظیم سراسری LANGUAGAE_CODE استفاده می کند.
نکته ها:
- در هر کدام از این مکان ها، preference زبان، انتظار می رود در قالب بندی استاندارد زبان باشد، به صورت یک رشته. برای مثال، برزیلی پرتغالی، pt-br می باشد.
- در صورتی که زبان پایه در دسترس باشد ولی sublanguage مشخص نباشد، جنگو از زبان پایه استفاده می کند. برای مثال، در صورتی که کاربرد de-at تعیین کرده باشد (اتریشی آلمانی) ولی جنگو تنها دارای de در دسترس باشد، جنگو از de استفاده می کند.
- تنها زبان های لیست شده در تنظیم LANGUAGES می توانند انتخاب شوند. در صورتی که می خواهید انتخاب زبان را به یک زیر مجموعه از زبان های تهیه شده محدود کنید (چرا که برنامه ی شما تمام آن زبان ها را تهیه نمی کند)، LANGUAGE را به یک لیستی از زبان ها محدود کنید. برای مثال:
LANGUAGES = ( ('de', _('German')), ('en', _('English')), )
مثال فوق زبان هایی را که برای انتخاب خودکار به آلمانی و انگلیسی در دسترس می باشند را محدود می کند (و هر sublanguage، مانند de-ch یا en-us).
- در صورتی که یک تنظیم LANGUAGE سفارشی تعریف می کند، همانطور که در bullet قبلی توضیح داده شد، علامت گذاری زبان ها به صورت رشته های ترجمه صحیح می باشد – ولی استفاده از یک تابع ugettext() ساختگی، نه یکی در django.utils.translation. هرگز نباید django.utils.translation را درون فایل تنظیمات import کنید، چرا که آن ماژول در خودش وابسته به تنظیمات می باشد، و موجب یک import دایره ای می شود.
راهکار استفاده از یک تابع ugettext() ساختگی می باشد. در زیر یک نمونه از فایل تنظیمات می باشد:
ugettext = lambda s: s LANGUAGES = ( ('de', ugettext('German')), ('en', ugettext('English')), )
با این ترتیب، django‑admin.py makemessages همچنان این رشته های علامت گذاری شده برای ترجمه را پیدا می کند، ولی ترجمه در زمان اجرا اتفاق نمی افتد – بنابراین باید بخاطر داشته باشید که زبان ها را در ugettext() واقعی در هر کدی که در زمان اجرا از LNAGUAGE استفاده می کند wrap کنید.
- LocalMiddleware می تواند تنها زبان هایی تهیه شده توسط جنگو برای ترجمه را انتخاب کند. در صورتی که می خواهید ترجمه هایی را برای برنامه ی خود تهیه کنید که قبلا در ترجمه های منبع جنگو قرار داده نشده اند، شما می خواهید حداقل اصول اولیه ترجمه ها را برای آن زبان تهیه کنید. برای مثال، جنگو ID های پیام فنی برای ترجمه ی قالب بندی های تاریخ و زمان استفاده می کند – بنابراین نیاز به حداقل آن ترجمه های برای سیستم، جهت عملکرد درتس خواهید داشت.
نقطه ی شروع خوب کپی فایل انگلیسی .po و ترجمه ی حداقل پیام های فنی می باشد – شاید پیام های validation، همچنین.
ID های فنی پیام به سادگی تشخیص داده شده اند؛ آن ها تماما حروف بزرگ می باشند. شما ID پیام را به صورت پیام های دیگر ترجمه نمی کنید، شما local گوناگون صحیح در مقدار انگلیسی تهیه شده تهیه می کنید. برای مثال، با DATETIME_FORMAT (یا DATE_FORMATE یا TME_FORMAT)، این قالبندی رشته است که می خواهید برای زبان خود استفاده کنید. قالب بندی جهت قالب بندی رشته هایی که توسط تگ template، now استفاده شده اند یکسان است.
هنگامی که LocalMiddleware، preference کاربر را تعیین می کند، این preference را به صورت request.LANGUAGE_CODE برای هر HttpRequest قابل دسترس می کند. احساس آزادی کندی برای خواند این مقدار در کد view خود. در زیر یک مثال ساده وجود دارد:
def hello_world(request):
if request.LANGUAGE_CODE == 'de-at':
return HttpResponse("You prefer to read Austrian German.")
else:
return HttpResponse("You prefer to read another language.")
توجه داشته باشید که، با ترجمه استاتیک (بدون middleware)، زبان دز settings.LANGUAGE_CODE می باشد، در حالی که با ترجمه ی پویا (middleware)، در request.LANGUAGE_CODE می باشد.
استفاده از ترجمه ها در پروژه ی خودتان
جنگو توسط الگوریتم زیر به دنبال ترجمه می گردد:
- ابتدا، به دنبال دایرکتوری locale در دایرکتوری برنامه (در دست ترجمه ...). در صورتی که یک ترجمه برای زبان انتخاب شده پیدا کند، ترجمه نصب خواهد شد.
- سپس، به دنبال دایرکتوری locale در دایرکتوری پروژه می گردد. در صورتی که یک ترجمه پیدا کند، ترجمه نصب خواهد شد.
- در پایان، پایگاه ترجمه ی تهیه شده توسط جنگو را در django/conf/locale را بررسی می کند.
می توانید برنامه هایی که شامل ترجمه های خودشان باشند را بنویسید، و می توانید پایگاه ترجمه ها را در مسیر پروژه ی خود override کنید. یا، می توانید تنها یک پروژهی بزرگ خارج از چندین app ساخته و تمام ترجمه ها را درون یک پروژه ی بزرگ فایل پیام قرار دهید. انتخاب با خودتان می باشد.
تمامی مخازن (repositories) دارای ساخت به روش یکسان می باشند، آن ها:
- $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
- $PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
- تمام مسیرهای لیست شده در LOCALE_PATHS در فایل تنظیمات شما (در دست ترجمه ...).
- $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)
جهت ساخت فایل های پیام، از ابزار همسان django-admin.py makemessages همانطور که فایل های پیام جنگو با آن بود استفاده می کنید. تنها نیاز است د رجای مناسب باشید – در دایرکتوری conf/locale ((در دست ترجمه ...)) یا locale/ ((در دست ترجمه ...)) دایرکتوری قرار دارند. همچنین از دستور همسان django-admin.py compilemessages برای تولید فایل های باینری django.mo که توسط gettext استفاده می شوند استفاده می شود.
همچنین می توانید django-admin.py compilemessages --settings=path.to.settings را برای ایجاد پردازش کامپایلر تمام دایرکتوری های موجود در تنظیم LOCALE_PATHS اجرا کرد.
فایل های پیام برنامه برای پی بردن کمی پیچیده می باشند – آن ها نیاز به LocaleMiddleware دارند. در صورتی که از middleware استفاده نمی کنید، تنها فایل های پیام جنگو و فایل های پیام پروژه پردازش خواهند شود.
(در دست ترجمه ...). در صورتی که برنامه ها نیاز به تحویل داده شدن به کاربران دیگر داشته و در پروژه های دیگر استفاده خواهد شد، ممکن است بخواهید ترجمه های app-specific را استفاده کنید. ولی استفاده از ترجمه های app‑specific و ترجمه های پروژه می تواند مشکلات مرموزی با makemessages زتولید کند: makemessages از تمام دایرکتوری های زیر مسیر فعلی عبور کرده و بنابراین ممکن است ID های پیام را درون فایل پیام پروژه قرار دهد که قبلا در فایل های پیام برنامه وجود داشته اند.
ساده ترین راه ذخیره کردن برنامه هایی است که بخشی از پروژه نیستند (و بنابراین ترجمه های خودشان را حمل می کنند) خارج از درخت پروژه. django-admin.py makemessages در سطح پروژه تنها رشته هایی که پروژه ی صریح شما متصل هستند را ترجمه می کند و نه رشته هایی که به طور مستقل توزیع شده هستند.
view تغییر مسیر set_language
برای راحتی، جنگو یک view به نام django.views.i18n.set_language ارائه می کند، که یک preference زبان کاربر را قرار می دهد و به صفحه ی قبلی تغییر مسیر می دهد.
این view را توسط اضافه کردن خط زیر به URLconf فعال کنید:
(r'^i18n/', include('django.conf.urls.i18n')),
(توجه داشته باشید که این مثال view را در /i18n/setlang/ قابل دسترس می کند.)
view انتظار دارد توسط روش POST فراخوانی شود، با یک پارامتر langage در درخواست. در صورتی که پشتیبانی session فعال شده باشد، view انتخاب زبان در session کاربر را ذخیره می کند. در غیر اینصورت، زبان انتخاب در یک کوکی که به طور پیشفرض با نام django_language می باشد را ذخیره می کند. (نام می تواند از طریق تنظیم LANGUAGE_COOKIE_NAME تغییر کند.)
بعد از تنظیم انتخاب زبان، جنگو با الگوریتم زیر کاربر را تغییر مسیر می دهد:
- جنگو به دنبال یک پارامتر next در داده POST می گردد.
- در صورتی که وجود نداشته باشد، یا خالی باشد، جنگو URL درون هدر Referrer را امتحان می کند.
- در صورتی که خالی باشد – تصور کنید، در صورتی که یک مرورگر مانع آن هدر شود – سپس کاربر به / (ریشه ی سایت) تغییر مسیر داده خواهد شد.
در زیر مثال کد HTML template وجود دارد:
<form action="/i18n/setlang/" method="post">
<input name="next" type="hidden" value="/next/page/" />
<select name="language">
{% for lang in LANGUAGES %}
<option value="{{ lang.0 }}">{{ lang.1 }}</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>
ترجمه ها و جاوا اسکریپت
اضافه کردن ترجمه ها به جاوا اسکریپت برخی مشکلات را موجب می شود:
- کد جاوا اسکریپت دارای دسترسی به انجام gettext نمی باشد.
- کد جاوا اسکریپت دارای دسترسی به فایل های .po یا .mo نمی باشد؛ آن ها نیاز به تحویل داده شدن توسط سرور را دارند.
- کاتالوگ های ترجمه برای جاوا اسکریپت باید در کوچکترین حد ممکن نگهداری شوند.
جنگو یک راهکار یکپارچه سازی شده برای این مشکلات تهی می کند: ترجمه ها را به جاوا استکریپت اراسل می کند، بنابراین می توانید gettext و غیره را از درون جاوا اسکریپت فراخوانی کنید.
javascript_catalog
راهکار اصلی برای این مشکلات، view ای با نام javascript_catalog می باشد، که یک کتابخانه ی کد جاوا اسکریپت با توابعی است که رابط gettext را تقلید کرده است، به اضافه ی یک آرایه از رشته های ترجمه. آن رشته های ترجمه گرفته شده از برنامه، پروژه یا هسته ی جنگو می باشد، مطابق آن چه را که شما در info_dict یا URL تعیین کرده اید.
به شکل زیر آن را مرتبط می کنید:
js_info_dict = {
'packages': ('your.app.package',),
}
urlpatterns = patterns('',
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
)
هر رشته در packages باید در syntax پایتون dotted-package باشد (قالب بندی همسان به صورت رشته ها در INSTALLED_APPS) و باید به یک پکیجی که حاوی دایرکتوری locale می باشد رجوع کند. در صورتی که چندین پکیج تعیین می کنید، تمام آن کاتالوگ ها در یک کاتالوگ ادغام می شوند. در صورتی که جاوا اسکریپتی دارید که از رشته های برنامه های مختلف استفاده می کند این مفید می باشد.
می توانید view را توسط قرار دادن پکیج هایی داخل الگوی URL پویا کنید:
urlpatterns = patterns('',
(r'^jsi18n/(?P<packages>\S )/$', 'django.views.i18n.javascript_catalog'),
)
با این موضوع، شما پکیج هایی را به صورت یک لیست از نام های پکیج محدود شده توسط علامت های ' 'در URL تعیین می کنید. این بویژه در صورتی که صفحات شما کدهایی از app های مختلف استفاده می کند و این اغلب تغییر می کند و نمی خواهید در یک فایل کاتالوگ بزرگ بکشید مفید است. به عنوان یک اقدام امنیتی، این مقادیر تنها django.conf یا هر پکیجی از تنظیم INSTALLED_APPS می باشند.
استفاده از کاتالوگ ترجمه ی جاوا استکریپت
جهت استفاده از کاتالوگ، تنها کافیست (در دست ترجمه ...):
<script type="text/javascript" src="/django/path/to/jsi18n/"></script>
این نحوه ای است که مدیر ترجمه ی کاتالوگ سرور را واکشی می کند. هنگامی که کاتالوگ بارگذاری شد، کد جاوا اسکریپت شما می تواند از رابط gettext استاندارد برای دسترسی به آن استفاده کند:
document.write(gettext('this is to be translated'));
همچنین یک رابط ngettext نیز وجود دارد:
var object_cnt = 1 // or 0, or 2, or 3, ...
s = ngettext('literal for the singular case',
'literal for the plural case', object_cnt);
و حتی یک تابع رشته ی interpolation:
function interpolate(fmt, obj, named);
interpolation syntax قرض گرفته شده از پایتون می باشد، بنابراین تابع interpolate هر دوی interpolation موضعی و نام گذاری شده را پشتیبانی می کند:
- interpolation موضعی: obj حاوی یک شیء آرایه ی جاوا اسکریپت می باشد که مقادیر المان ها به ترتیب در placeholder های متناظر ftm در ترتیب یکسانی که ظاهر شده اند قرار می گیرند:
fmts = ngettext('There is %s object. Remaining: %s', 'There are %s objects. Remaining: %s', 11); s = interpolate(fmts, [11, 20]); // s is 'There are 11 objects. Remaining: 20'
- interpolation های نام گذاری شده: این حالت توسط ارسال پارامتر boolean اختیاری name به صورت true انتخاب شده است. obj حاوی یک شیء جاوا اسکریپت یا آرایه ی associative می باشد. برای مثال:
d = { count: 10 total: 50 }; fmts = ngettext('Total: %(total)s, there is %(count)s object', 'there are %(count)s of a total of %(total)s objects', d.count); s = interpolate(fmts, d, true);
شما نباید بالا با رشته ی interpolation بروید: این همچنان جاوا اسکریپت می باشد، بنابراین، کد باید تعویض های تکرار شده ی regular-expression را ایجاد کند. این به همان سرعت رشته ی interpolation در جاوا نمی باشد، بنابراین آن را برای مورادی که واقعا به آن نیاز دارید نگه دارید (برای مثال، در رابطه با ngettet برای تولید جمع بندی های مناسب).
ساختن کاتالوگ های ترجمه ی جاوا اسکریپت
شما کاتالوگ های ترجمه را به همان روشی که کاتالوگ های ترجمه ی دیگر جنگو را ساخته و به روز رسانی می کردید – با ابزار django-admin.py makemessages – انجام می دهید. تنها تفاوت این است که نیاز به تهیه ی یک پارامتر –d djangojs مانند زیر خواهید داشت:
django-admin.py makemessages -d djangojs -l de
دستور فوق کاتالوگ ترجمه برای جاوا اسکریپت برای آلمانی را ساخته یا به روز رسانی می کند. بعد از به روز رسانی کاتالوگ های ترجمه، تنها کافیست دستور django-admin.py compilemessages همانند کاتالوگ های عادی جنگو اجرا کنید.
نکته هایی برای کاربران آشنا به gettext
در صورتی که gettext را می شناسید، ممکن است متوجه این تخصص های روش جنگو برای انجام ترجمه شده باشید:
- رشته ی دامنه، django یا djangojs است. این رشته ی دامنه برای فرق قائل شدن بین برنامه های مختلف می باشد، که داده ی آن ها در یک کتابخانه ی مشترک فایل پیام ذخیره می شود (معمولا /usr/share/locale/). دامنه ی جنگو برای پایتون و رشته های ترجمه ی template استفاده شده است و به کاتالوگ های سراسری ترجمه بارگذاری شده است. دامنه ی djangojs تنها برای کاتالوگ های ترجمه ی جاوا اسکریپت جهت اطمینان اینکه آن ها تا حد ممکن کوچک هستند استفاده شده است.
- جنگو از xgettext تنها استفاده نمی کند. از wrapper پایتون در اطراف xgettext و msgfmt استفاده می کند. این بیشتر برای راحتی است.
gettext در ویندوز
این تنها برای افرادی که می خواهند ID های پیام را استخراج کنند یا فایل های پیام را کامپایل کنند ضروری می باشد (.op). خود کار ترجمه تنها با ویرایش فایل های موجود از این نوع درگیر است، ولی در صورتی که می خواهید فایل های پیام خود را ایجاد کنید، یا می خواهید یک فایل پیام تغییر کرده را آزمون یا کامپایل کنید، نیاز به مزیت های gettext خواهید داشت:
- فایل های zip زیر را از http://sourceforge.net/projects/gettext دانلود کنید
- gettext-runtime-X.bin.woe32.zip
- gettext-tools-X.bin.woe32.zip
- libiconv-X.bin.woe32.zip
- 3 فایل را فولدر همسان از حالت zip خارج کنید (مانند C:\Program Files\gettext‑utils)
- PATH سیستم را به روز رسانی کنید:
- Control Panel > System > Advanced > Environment Variables
- در لیست System variables، بر روی Path کلید کنید، بر روی Edit کلیک کنید
- ;C:\Program Files\gettext‑utils\bin به انتهای فیلد Variable value اضافه کنید
همچنین می توانید از باینری های gettect نیز که در جای دیگر بدست آورده شده است استفاده کنید، مادامی که دستور xtgett –version به درستی کار می کند. برخی نسخه های باینری 0.14.4 این دستور را پشتیبانی نمی کنند. در صورتی که دستور xgettext –version وارد شده در یک command prompt ویندوز موجب یک پنجره ی popup شده است "xgettext.exe has generated errors and will be closed by Windows" برای استفاده از مزیت های ترجمه ی جنگو با پکیج gettext تلاش نکنید.