Наиболее распространённый способ переводов программного обеспечения и веб-сайтов на разные языки — с помощью библиотеки gettext.
Для тех, кто не в курсе, поясню как она работает:
- В коде программы/сайта, везде где вам нужно выводить некий текст, меняющийся в зависимости от языка, вы пишете не просто строковые константы, типа «Да» и «Нет», а используете некую специальную функцию вроде _(«Да»), _(«Нет»), или t(«Да»), t(«Нет»), или даже [`Да`], [`Нет`].
- Вы запускаете специальный скрипт, который сканирует весь ваш исходный код, находит такие вот специфические вызовы функции, и составляет из них *.po — файл.
- Вы с помощью некоего редактора, например poedit добавляете в файл *.po переводы всех строк которые там есть на некий язык, и сохраняете файл как, например, en_US.po, после чего генерируете соответствующий *.mo-файл, в данном случае en_US.mo.
- Вы указываете вашей программе/сайту путь к файлу перевода (например, берёте из сессии или из данных браузера язык, который выбран у пользователя, и исходя из этого, выбираете ru_RU.mo или en_US.mo
- Программа/сайт автоматически заменяет ваши t(«Да») и t(«Нет») на слова написанные на том языке, который был выбран.
Как же перевести po-файл автоматически, если качество перевода для вас не важно, но нужно чтобы сайт/приложение начало выглядеть как многоязычное для вашего заказчика?
Для демонстрации многоязычного интерфейса на языках, которых я не знаю (финский, немецкий), но также их не знает и заказчик, мне понадобилось создать файлы переводов для этих языков. В самом файле было около 1500 различных строк, и, начав переводить файл с помощью google translate (каждое слово вручную), я довольно быстро понял, что эта процедура затянется на несколько дней.
А если я всё равно перевожу строки через google translate, и, при этом, не вдумываюсь в их смысл, почему бы не сделать этот процесс автоматическим?
Инструменты
Для решения задачи был выбран Python3, библиотека для работы с *.po и *.mo файлами polib, и библиотека yandex.translate для работы с яндекс-переводчиком. Яндекс-переводчик был выбран в связи с тем, что его API гораздо проще в использовании нежели API гугл-транслейта. Также, для создания консольного скрипта, я использовал библиотеку click
Принцип действия
Для работы модулю требуется указать исходный файл *.po из которого нужно считать какой-то уже существующий перевод (то есть фразы и слова в нём должны иметь значения, пусть даже на том же самом языке). Также, нужно указать языки — с какого и на какой переводить. После чего можно, запустив перевод, и дождавшись его окончания, сохранить результат в виде *.po-файла, а также в *.mo-файл.
Код:
Основной класс для работы с переводом:
import polib from yandex_translate import YandexTranslate class Translator: """ Класс для перевода po-файлов """ def __init__(self, yandex_key, src_lang=None, dest_lang=None, src_po_file=None): """ Конструктор :param yandex_key: ключ для доступа к API яндекс-переводчика :param src_lang: исходный язык :param dest_lang: язык назначения :param src_po_file: исходный po-файл :return: """ self.yandex_key = yandex_key self.src_lang = src_lang self.dest_lang = dest_lang self.yandex_translate = YandexTranslate(self.yandex_key) if src_po_file is not None: self.open_po_fle(src_po_file) def open_po_fle(self, po_filename): """ Открыть po-файл :param po_filename: :return: """ self.po = polib.pofile(po_filename) def _translate_str(self, text, src_lang, dest_lang, return_src_if_empty_result=True, need_print=False): """ Перевести текстовую строку (приватный метод) :param text: текст, который нужно перевести :param src_lang: исходный язык :param dest_lang: конечный язык :param return_src_if_empty_result: вернуть исходную строку, если перевод не найден? :param need_print: Выводить на экран информацию о результате перевода (для отладки) :return: переведённая строка (или исходная, если перевод не найден, и return_src_if_empty_result==True) """ tr = self.yandex_translate.translate(text, "{}-{}".format(self.src_lang, self.dest_lang)) if tr['code'] == 200 and len(tr['text']) and tr['text'][0]: tr_text = tr['text'][0] else: tr_text = "" if need_print: print(text + " => " + tr_text) if not tr_text and return_src_if_empty_result: tr_text = text return tr_text def go_translate(self, src_lang=None, dest_lang=None, break_on=0): """ Запустить перевод po-файла :param src_lang: исходный язык :param dest_lang: конечный язык :param break_on: номер строки, на которой нужно остановить процесс, используется для отладки :return: """ if src_lang is None: src_lang = self.src_lang if dest_lang is None: dest_lang = self.dest_lang count = len(self.po) pos = 0 for item in self.po: if break_on and break_on == pos: break pos += 1 percent = int(pos * 100 / count) print("Percent:" + str(percent) + "%") if item.msgstr: item.msgstr = self._translate_str(item.msgstr, src_lang, dest_lang, True, True) if item.msgstr_plural: for num in item.msgstr_plural: item.msgstr_plural[num] = self._translate_str(item.msgstr_plural[num], True, True) def save_po_file(self, dest_po_file=None): """ Сохранить результирующий po-файл :param dest_po_file: Имя файла :return: """ if dest_po_file is None: dest_po_file = self.dest_po_file self.po.save(dest_po_file) def save_mo_file(self, dest_mo_file=None): """ Сохранить mo-файл :param dest_mo_file: имя файла :return: """ if dest_mo_file is None: dest_mo_file = self.dest_mo_file self.po.save_as_mofile(dest_mo_file) |
Для работы классу необходим API-ключ для яндекс-переводчика. Получить его можно здесь: https://tech.yandex.ru/keys/get/?service=trnsl
Установка питона, если у вас его ещё нет (от рута):
apt-get install python3 python3-pip |
Установка скрипта:
pip3 install PoTrans |
Сам код можно найти по адресу https://github.com/MihanEntalpo/PoTrans
После установки вам станет доступен консольный скрипт potrans
Использование в качестве консольного инструмента:
С помощью скрипта можно сделать 2 вещи:
1) Перевод:
Перевести файл ./ru_RU.po с русского на итальянский и сохранить результат в ./it_IT.po:
(длинная строка после ключа —key — это ключ API яндекс)
potrans translate -i ./ru_RU.po -il ru -ol it -o ./it_IT.po --key trnsl.1.1.20160516T111755Z.a0378f3552843fb4.a5ac4af82a585d1e3517f32c4c953f3f791ddb2a |
2) Перевод не указывая ключ:
Сначала положим в конфигурационный файл наш ключ:
echo "trnsl.1.1.20160516T111755Z.a0378f3552843fb4.a5ac4af82a585d1e3517f32c4c953f3f791ddb2a" > ~/.config/potrans.key |
И запускаем перевод уже без ключа key:
potrans translate -i ./ru_RU.po -il ru -ol it -o ./it_IT.po |
3) Перевод и сохранение результатов в *.po и *.mo файлы:
potrans translate -i ./ru_RU.po -il ru -ol it -o ./it_IT.po -om ./it.IT.mo |
4) Перевод файла с выводом всех обрабатываемых фраз и их переводов:
potrans translate --debug -i ./ru_RU.po -il ru -ol it -o ./it_IT.po |
5) Перевод файла с использование msgid если msgstr пуст (можно использовать если в исходном файле нет перевода):
potrans translate --usemsgid -i ./ru_RU.po -il ru -ol it -o ./it_IT.po |
6) Простая конвертация файла из *.po в *.mo без перевода
Сконвертировать файл ./ru_RU.po в ./ru_RU.mo
potrans convert -i ./ru_RU.po -o ./ru_RU.mo |
Использование в качестве библиотеки:
from PoTrans import Translator # Ключ API переводчика яндекса yandex_key = "trnsl.1.1.20160516T111755Z.a0378f3552843fb4.a5ac4af82a585d3e3517f32c4c953f3f791dfb2a" # Создаём объект переводчика t = Translator(yandex_key, src_lang="ru",dest_lang="de") # Загружаем po-файл t.open_po_file("./ru_ruMessages.po") # Запускаем перевод t.go_translate() # Сохраняем результат t.save_po_file("./de_deMessages.po") |
Другие библиотеки и инструменты
Справедливости ради, надо сказать, что я нашел несколько аналогичных модулей в репозитории (вот например: https://pypi.python.org/pypi/potranslate), но, все они меня не удовлетворили по одной из причин:
1) Скрипты в основном используют google-переводчик, для которого относительно сложно получить API-ключ (в сравнеии с процедурой получения ключа для яндекс-переводчика)
2) Некоторые были написаны давно, и API google-переводчика уже успело измениться, поэтому плагины теперь не работают.
3) Как показал анализ, многие из них не умеют работать с plural-ами, то есть переводить слова, у которых есть разные формы для разных количеств (например, единственное или множественное для английского языка, или 1,2,5 для русского)
4) Написаны они все поголовно на Python 2.7, а мне нужен был скрипт, написанный на Python 3
5) Перевод во всех этих модулях осуществляется из msgid в msgstr, то есть берётся исходная строка из msgid, переводится, и результат записывается в msgstr. В моём же случае берётся msgstr одного файла, а перевод записывается в msgstr другого, а также есть возможность, при отсутствии значений в msgstr брать их из msgid.
Для интересующихся, исходный код находится здесь
Если Вы заинтересованы в локализации ПО с помощью gettext, я рекомендую Вам использовать этот инструмент на базе web: https://poeditor.com/
Привет. Подскажи почему скрипт не отрабатывает…
Traceback (most recent call last):
File «C:/Users/pc/PycharmProjects/untitled/popo.py», line 6, in
translator.go_translate(«ru», «en»)
File «C:\Users\pc\PycharmProjects\untitled\PoTrans\potrans.py», line 86, in go_translate
item.msgstr = self._translate_str(item.msgid, src_lang, dest_lang, True, debug)
File «C:\Users\pc\PycharmProjects\untitled\PoTrans\potrans.py», line 45, in _translate_str
tr = self.yandex_translate.translate(text, «{}-{}».format(self.src_lang, self.dest_lang))
File «C:\Users\pc\PycharmProjects\untitled\yandex_translate\__init__.py», line 150, in translate
raise YandexTranslateException(status_code)
yandex_translate.YandexTranslateException: ERR_LANG_NOT_SUPPORTED
5) Перевод файла с использование msgid если msgstr пуст (можно использовать если в исходном файле нет перевода):
Согласно этому пункту установил True для msgid
Привет.
Судя по выдаче, яндекс утверждает что язык не поддерживается.
Ты похоже запускаешь свой скрипт popo.py, можешь показать его содержимое?
Так-то вроде из трейса следует что переводишь ты с русского на английский, однако, увидеть код было бы надёжнее.
Просто взял пример твоего кода
# Ключ генерировал
translator = Translator(key)
translator.open_po_fle(‘./en_EN.po’)
translator.go_translate(‘ru’, ‘en’)
translator.save_po_file(‘./ru_RU.po’)
translator.save_mo_file(‘./ru_RU.mo’)
Файл для проверки взял po-файл темы wordpress
Заработал. Translator(key, src_lang=»en»,dest_lang=»ru») из текста. Пример из документации не работает
Спасибо за публикацию, очень правильно все написано!