Многоязычное руководство по Django (i18n)

задняя часть внешний интерфейс Windows Django
Многоязычное руководство по Django (i18n)

Оригинальная ссылка:OC от UE.com/Django_Suddenly…

Обзор

В последнее время компания готовится к расширению зарубежного бизнеса, поэтому необходимо добавить в систему DjangoИнтернационализация и локализацияслужба поддержки. общее сокращение от интернационализацииi18n, что означает, что i и n в интернационализации состоят из 18 букв; аббревиатура локализацииL10n, что указывает на то, что в словах «л» и «н» в «Локализации» 10 букв. Интересно, что строчные i и прописные L обычно используются для предотвращения путаницы.

Проще говоря: i18n должен создать основу для интернационализации, а L10n — адаптироваться к различным регионам. Возьмем простой пример:

i18n:

datetime.now().strftime('%Y/%m/%d')  # before i18n
datetime.now().strftime(timeformat)  # after i18n

L10n:

timeformat = {
    'cn': '%Y/%m/%d',
    'us': '%m/%d/%Y',
    'fr': '%d/%m/%Y',
    ...
}

Более конкретное определение см.W3Cобъяснение.

Область применения i18n очень широка, включая несколько языков, часовые пояса, денежные единицы, числа в единственном и множественном числе, кодировки символов и даже порядок чтения текста (RTL)и т.д. Эта статья посвящена толькоМногоязычный для i18nаспект.

使用阿拉伯语的 windows 系统,来源

↑ В арабской системе Windows направление текста и даже интерфейса противоположно китайской версии (Источник изображения)

Основные шаги

Django, как большой и всеобъемлющий фреймворк, предоставил набор мультиязычных решений, я провел небольшое сравнение и не смог найти библиотеку лучше официального решения под систему Django. Решение Django можно просто разделить на четыре шага:

  1. некоторая необходимая конфигурация
  2. Отметить текст для перевода в коде
  3. использоватьmakemessagesкоманда для создания файла po
  4. компилироватьcompilemessagesкоманда для компиляции файла mo

Рассмотрим подробно ниже

Шаг 1: Конфигурация

Сначала добавьте это содержимое в settings.py

LOCALE_PATHS = (
    os.path.join(__file__, 'language'),
)
MIDDLEWARE = (
    ...
    'django.middleware.locale.LocaleMiddleware',
    ...
)
LANGUAGES = (
    ('en', 'English'),
    ('zh', '中文'),
)

LOCALE_PATHS: укажите расположение файлов, созданных на третьем и четвертом шагах ниже. В более старых версиях Django необходимо вручную создать этот каталог.

LocaleMiddleware: позволяет Django распознавать и выбирать подходящий язык.

LANGUAGES: указывает, какие языки может предоставить этот проект.

Шаг 2: Отметьте текст

Раньше не было необходимости в нескольких языках, поэтому все писали китайский напрямую в соответствующем коде AJAX, например вот так:

return JsonResponse({"msg": "内容过长", "code": 1, "data": None})

Теперь, когда вам нужно несколько языков, вам нужно сообщить Django, какой контент нужно перевести. Для приведенного выше примера это записывается так:

from django.utils.translation import gettext as _

return JsonResponse({"msg": _("内容过长"), "code": 1, "data": None})

использовать здесьgettextФункция упаковывает исходную строку, чтобы Django мог вернуть соответствующую строку для текущего языка. Обычно используется одно подчеркивание_Улучшить читаемость.

Поскольку почти все интерфейсы и серверы нашей компании используют AJAX, мы не очень часто используем функцию шаблона Django (кстати, многоязычный инструмент, используемый в нашем интерфейсе,i18next). Но здесь я также записываю метод разметки шаблона Django:

<title>{% trans "This is the title." %}</title>
<title>{% trans myvar %}</title>

вtransТег сообщает Django, что содержимое в квадратных скобках необходимо перевести. Более конкретное использование см.официальная документация.

третий шаг:makemessages

Перед выполнением этого шага, пожалуйста, пройдитеxgettext --versionУбедитесь, что он у вас установленGNU gettext. GNU gettext — это стандартная библиотека i18n L10n, а Django и многоязычные модули многих других языков и библиотек вызывают GNU gettext, поэтому некоторые из следующих функций Django на самом деле благодаря GNU gettext. Если он не установлен, вы можете установить его следующим способом:

ubuntu:

$ apt update
$ apt install gettext

macOS:

$ brew install gettext
$ brew link --force gettext

windows

После установки GNU gettext выполните следующую команду в проекте Django.

$ python3 manage.py makemessages --local en

Затем сгенерированные файлы можно найти:language/en/LC_MESSAGES/django.po. введите вышеуказанную командуenЗамените другими языками, вы можете создавать разные языкиdjango.poдокумент. Содержание, вероятно, такое:

#: path/file.py:397
msgid "订单已删除"
msgstr ""

...

Джанго найдетgettextВсе строки, обернутые функцией, начиная сmsgidхранится в видеdjango.po. каждыйmsgidследующееmsgstrзначит ты хочешь этогоmsgidперевести на что. Изменив этот файл, вы можете указать Django, что переводить. Это также объясняется аннотациямиmsgidКакая строка какого файла появляется.

В этом файле было обнаружено несколько интересных особенностей:

  • Django поместит один и тот же файл в несколько файлов.msgidсгруппированы вместе. «Редактируй один раз, переводи везде»
  • Если более поздний исходный кодmsgidудалить, затем выполнить сноваmakemessagesПосле команды этоmsgidИ егоmsgstrбудет продолжать сохраняться в виде комментариевdjango.poсередина.
  • Поскольку строка в исходном коде — это просто так называемый идентификатор, я могу писать строки без реального смысла в исходном коде, например_("ERROR_MSG42"), затем переведите «ERROR_MSG42» на китайский и английский языки.
  • В этом файле будут сохранены заполнители для строк шаблона. Например, вы можете использовать именованные заполнители, чтобы использовать разные последовательности заполнителей на разных языках. Пример приведен ниже:

py file:

_('Today is {month} {day}.').format(month=m, day=d)
_('Today is %(month)s %(day)s.') % {'month': m, 'day': d}

po file:

msgid "Today is {month} {day}."
msgstr "Aujourd'hui est {day} {month}."

msgid "Today is %(month)s %(day)s."
msgstr "Aujourd'hui est %(day)s %(month)s."

четвертый шаг:compilemessages

модифицированныйdjango.poПосле файла выполните следующую команду:

$ python3 manage.py compilemessages --local en

Django вызовет программу в соответствии сdjango.poскомпилировать файл с именемdjango.moдвоичные файлы, местоположение иdjango.poто же место. Это файл, который будет прочитан при выполнении программы.

После выполнения вышеуказанных четырех шагов измените языковые настройки браузера, и вы увидите другой вывод Django.

Chrome 的语言设置

↑ Языковые настройки Chrome.

Расширенные возможности

i18n_patterns

Иногда мы хотим иметь возможность выбирать разные языки через URL-адрес. Это имеет много преимуществ, например, язык данных, возвращаемых одним и тем же URL-адресом, должен быть согласованным. Документация Django использует этот подход:

Упрощенный китайский: https://docs.djangoproject.com/zh-hans/2.0/

Английский: https://docs.djangoproject.com/en/2.0/

Конкретный метод заключается в добавлении в URL<slug:slug>

urlpatterns = ([
    path('category/<slug:slug>/', news_views.category),
    path('<slug:slug>/', news_views.details),
])

Для получения дополнительной информации, пожалуйста, обратитесь к Django.официальная документация.

Как Django решает, какой язык использовать

мы говорили об этом раньшеLocaleMiddlewareМожет решить, какой язык использовать. Конкретно,LocaleMiddlewareнаходится в следующем порядке (по убыванию приоритета):

  • i18n_patterns
  • request.session[settings.LANGUAGE_SESSION_KEY]
  • request.COOKIES[settings.LANGUAGE_COOKIE_NAME]
  • request.META['HTTP_ACCEPT_LANGUAGE'], т.е. в HTTP-запросеAccept-Language header
  • settings.LANGUAGE_CODE

Наша компания предпочитает помещать информацию о языке в файлы cookie. Когда пользователь вручную выбирает язык, внешний интерфейс может напрямую изменять файлы cookie, не запрашивая интерфейс в фоновом режиме. Пользователи, которые не установили язык вручную, не имеют этого файла cookie, следуйте настройкам браузера. сказатьsettings.LANGUAGE_COOKIE_NAMEЗначение по умолчаниюdjango_language, интерфейсы не хотят появляться в их кодеdjango, так что я вsettings.pyдобавлено вLANGUAGE_COOKIE_NAME = app_language😂.

Вы также можете пройтиrequest.LANGUAGE_CODEПолучить его вручную в представленииLocaleMiddlewareКакой язык был выбран. Вы даже можете пройтиactivateФункция вручную указывает язык, используемый текущим потоком:

from django.utils.translation import activate

activate('en')

ugettext

В эпоху Python2 для того, чтобы различать строки Unicode и строки байтов, существуютugettextиgettextдве функции. В Python3 из-за унификации кодировки строкugettextиgettextэквивалентны. Чиновники говорят, что в будущем он может быть списан.ugettext, но на данный момент (Django 2.0),ugettextЕще не заброшен.

gettext_lazy

Вот пример, чтобы увидеть интуитивноgettext_lazyиgettextразница

from django.utils.translation import gettext, gettext_lazy, activate, get_language

gettext_str = gettext("Hello World!")
gettext_lazy_str = gettext_lazy("Hello World!")

print(type(gettext_str))
# <class 'str'>
print(type(gettext_lazy_str))
# <class 'django.utils.functional.lazy.<locals>.__proxy__'>

print("current language:", get_language())
# current language: zh
print(gettext_str, gettext_lazy_str)
# 你好世界! 你好世界!

activate("en")

print("current language:", get_language())
# current language: en
print(gettext_str, gettext_lazy_str)
# 你好世界! Hello World!

gettextФункция возвращает строку, ноgettext_lazyТо, что возвращается, является прокси-объектом. Когда этот объект используется, он решает, на какой текст переводить, в зависимости от языка в текущем потоке.

Эта функция особенно полезна в моделях Django. Потому что код, определяющий строку в моделях, будет выполняться только один раз. В последующих запросах эта так называемая строка имеет разные представления в зависимости от языка.

from django.utils.translation import gettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

class YourThing(models.Model):
    kind = models.ForeignKey(
        ThingKind,
        on_delete=models.CASCADE,
        related_name='kinds',
        verbose_name=_('kind'),
    )

Модифицировать исходный код с помощью AST/FST

Поскольку проект нашей компании очень большой, рабочая сила добавляется к каждой строке_( ... )слишком громоздко. Поэтому я пытаюсь найти автоматизированный способ.

В начале я выбрал встроенный PythonastМодуль (Абстрактное синтаксическое дерево). Основная идея черезastНайдите все строки в проекте и добавьте к этим строкам_( ... ). Наконец, измененное синтаксическое дерево снова преобразуется в код.

Однако из-заastПоддержка информации о формате не очень хороша, и после изменения кода легко вызвать путаницу в формате. Поэтому я нашел улучшенный способ под названием FST (Full Syntax Tree). Моя любимая библиотека FST:redbaron. Основной код выглядит следующим образом:

root = RedBaron(original_code)

for node in root.find_all("StringNode"):
    if (
        has_chinese_char(node)
        and not is_aleady_gettext(node)
        and not is_docstring(node)
    ):
        node.replace("_({})".format(node))

modified_code = root.dumps()

Я вставил полный кодGistВыше, потому что это одноразовый скрипт, он пишется по желанию, и вы можете на него ссылаться.

использоватьredbaronВ процессе также были обнаружены некоторые проблемы, и они записаны здесь: Самая большая проблемаredbaron Техническое обслуживание остановлено! Поэтому некоторые новые синтаксисы, такие как f-string в Python 3.6, не поддерживаются. затем эта библиотека иastПо сравнению со стандартной библиотекой скорость работы очень низкая, и каждый раз, когда я запускаю этот скрипт, мой компьютер издает звук, похожий на звук двигателя самолета. Третий момент заключается в том, что он производит какое-то странное форматирование:

до исправления:

OutStockSheet = {
    1: '未出库',
    2: '已出库',
    3: '已删除'
}

После модификации ('已删除'Правая скобка переходит на следующую строку):

OutStockSheet = {
    1: _('未出库'),
    2: _('已出库'),
    3: _('已删除'
)}

Последний пункт можно решить форматированием, проблема не большая.

utf8 vs utf-8

Некоторые файлы py в проекте устарели и используются в начале файла.# coding: utf8знак. Для Python utf8 — это псевдоним для utf-8, так что проблем нет. Когда Django вызывает GNU gettext, он использует параметр для указания кодировки как utf-8, но GNU также считывает флаг кодировки в файле и имеет более высокий приоритет. К сожалению, utf8 — неизвестная кодировка для GNU gettext, поэтому GNU gettext понизится до ASCII и сообщит об ошибке, когда встретит китайские символы (глупо!):

$ python3 manage.py makemessages --local en
...
xgettext: ./path/filename.py:1: Unknown encoding "utf8". Proceeding with ASCII instead.
xgettext: Non-ASCII comment at or before ./path/filename.py:26.

Поэтому мне нужно поставить# coding: utf8изменить на# coding: utf-8, либо просто удалите эту строку, так как Python3 уже по умолчанию использует кодировку utf-8.

Суммировать

Django (и стоящий за ним GNU gettext) очень универсален в своих многоязычных возможностях, таких как работа с числами в единственном и множественном числе.ngettext, который обрабатывает полисемиюpgettext. Используйте переведенный текст в ответе HTTP, но оставьте текст перед переводом в журнале.gettext_noop.

В этой статье в основном рассказывается о функциях, которые я использовал на практике, и ямах, с которыми я столкнулся, в надежде помочь вам понять основы использования многоязычности Django. Комментарии приветствуются 👏.


知识共享协议

Этот документ принимаетCreative Commons Attribution-Некоммерческое использование-Без производных 2.5 Лицензионное соглашение с материковым КитаемЛицензия.