Оригинальная ссылка: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 направление текста и даже интерфейса противоположно китайской версии (Источник изображения)
Основные шаги
Django, как большой и всеобъемлющий фреймворк, предоставил набор мультиязычных решений, я провел небольшое сравнение и не смог найти библиотеку лучше официального решения под систему Django. Решение Django можно просто разделить на четыре шага:
- некоторая необходимая конфигурация
- Отметить текст для перевода в коде
- использовать
makemessages
команда для создания файла po - компилировать
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
$ brew install gettext
$ brew link --force gettext
После установки 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.
Расширенные возможности
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 Лицензионное соглашение с материковым КитаемЛицензия.