[Программирование, *nix, Локализация продуктов, Kotlin, Изучение языков] Интернационализация и локализация приложения на Kotlin/Native

Автор Сообщение
news_bot ®

Стаж: 6 лет 7 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
22-Мар-2021 19:33

こんにちは, или добрый день по-японски.Как бы не был популярен английский язык, всё же пользователям комфортнее и привычнее в родной языковой среде.Поэтому далее мы пошагово рассмотрим процесс адаптации консольного приложения для Linux на Kotlin/Native к русской и английской локали.Поможет нам в этом старый-добрый GNU gettext.В итоге убедимся, что это совсем не страшно.Заодно посмотрим интеграцию с библиотеками на C, которая значительно расширяет возможности Kotlin/Native.Что напишем: переводчик количественных числительных на японский язык.Что ожидается от читателя: знание языков программирования Kotlin, C, базовый уровень знакомства с ОС Linux (в частности Bash).Что понадобится в процессе: любой дистрибутив Linux, любая версия IntelliJ IDEA, установленный пакет gettext-devel или аналогичный.
МотивацияСобственно почему Kotlin/Native и причём тут японскийГде-то полгода медленно и печально изучаю японский язык по учебнику “Minna no Nihongo”: частью ради перевода текста песен, частью просто из интереса к культуре.Позже решил перейти с системной разработки на прикладную, с десктопа на мобильные платформы, соответственно с C++/Qt/STL на Kotlin/JVM/Android SDK.Теперь хочу два этих занятия совместить, написав для себя программы для помощи в изучении японского. Конечно, уже много готовых, но NIH-синдром ведь не что-то плохое, правда?Раньше я не задумываюсь использовал бы связку Qt/QML/C++: она позволяет быстро и эффективно решать в общем-то любые задачи и на любой платформе.Однако Qt всё больше поворачивается спиной к Open Source, вот и решил пора валить попробовать что-то другое.И тут в процессе изучения Kotlin узнал про Kotlin/Native.Соответственно, первая программа-помощник будет именно на нём.Что такое Kotlin/NativeИзначально Kotlin выступал в качестве "более лучшей" (С) Java, и целиком опирался на платформу JVM.Однако затем компания JetBrains, разработчик Kotlin, решила адаптировать набирающий популярность язык и под другие платформы.В частности, под веб (Kotlin/JS) и под "натив" (Kotlin/Native).Kotlin/Native появился в марте 2017 года, кстати ровно четыре года назад.Он позволяет компилировать код на Kotlin в нативный код с помощью LLVM, без зависимости от виртуальной машины JVM и других библиотек.Далее мои субъективные впечатления по опыту разработки демонстрационного приложения, так что буду рад исправлениям и дополнениям.Из плюсов:
  • исходный код под пермиссивной открытой лицензией (Apache-2.0 license)
  • работа на всех поддерживаемых LLVM платформах, в частности iOS и десктопы
  • генерирует единственный исполняемый файл без сторонних зависимостей
  • низкое потребление системных ресурсов
  • доступны все базовые "вкусности" Kotlin вроде коллекций и функционального программирования
  • есть прозрачный interop с языком C
  • как следствие: доступны low-level функции платформы, в частности POSIX
Из минусов:
  • стандартная библиотека весьма бедная по сравнению с огромной JVM, многое придётся писать с нуля (пример: оставьте только пункт Native тут)
  • нет стабильной библиотеки для GUI, хотя есть некоторые привязки через C interop (GTK, libui)
  • слабый инструментарий по сравнению с той же Android Studio, например нет той же локализации
  • относительно долгое время сборки
Больше о Kotlin/Native можно почитать тут, а примеры доступны на Github.ТерминологияИнтернационализация (i18n): подготовка приложения к локализации, обычно выполняется разработчиками.Локализация (l18n): Процесс перевода и адаптации контента приложения для конкретных языков, обычно выполняется переводчиками.Важно: "контент" тут это не только строки, но и направление текста, формат даты, чисел и так далее. В данной статье ограничусь только строками.Что такое GNU gettextЭтот пакет из нескольких утилит, библиотек и регламентов.Является частью GNU Translation Project.Состоит из:
  • правила оформления исходного кода для последующей интернационализации
  • утилиты для генерации текстовых файлов с локализуемыми строками
  • кроссплатформенная нативная библиотека для извлечения переводов в runtime
  • правила дистрибуции бинарных файлов с переводами
Почему GNU gettextИнтернационализация в Kotlin/JVM для Android использует средства Android SDK, в частности строковые ресурсы, и завязана на JVM.  Поэтому для Kotlin/Native эти средства недоступны.В Qt есть собственный инструментарий, но его не получится использовать вне Qt, тем более с отличным от C++ языком.Поэтому остаётся GNU gettext:
  • универсальный (поддерживается множество языков программирования)
  • кроссплатформенный (Win/Mac/Linux, есть Android/iOS версия)
  • стабильный в силу почтенного возраста
  • с подробной документацией
  • со вспомогательными приложениями
Демонстрационный проектСуть: консольная программа пока только под Linux, чтобы не переусложнять код.Функционал: читает натуральное число из аргумента командной строки или stdin, и переводит его в количественное числительное на японском.Число может содержать специфичные для локали разделители тысяч, которая программа выводит при запуске.Скачать проект можно на Github и затем открыть в IntelliJ IDEA.Характеристики исполняемых файловДля начала время полной сборки: ~18 сек на конфигурации Ryzen 3900X + 32GB DDR4-3600 + NVM-E SSD. На мой взгляд многовато для такого маленького проекта и такой конфигурации.Тут можно вспомнить о преимуществах скриптовых языков, которые компилировать не надо.Теперь посмотрим свойства исполняемого файла для отладочной и релизной конфигураций:Размеры скомпилированных исполняемых файлов
$ file build/bin/native/debugExecutable/JapaneseNumeralTranslator.kexe
build/bin/native/debugExecutable/JapaneseNumeralTranslator.kexe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.16, BuildID[xxHash]=a0971dbf76e9db60, with debug_info, not stripped
$ ldd build/bin/native/debugExecutable/JapaneseNumeralTranslator.kexe
  linux-vdso.so.1 (0x00007fff890d7000)
  libdl.so.2 => /lib64/libdl.so.2 (0x00007f348e47a000)
  libm.so.6 => /lib64/libm.so.6 (0x00007f348e334000)
  libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f348e312000)
  libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f348e2f7000)
  libc.so.6 => /lib64/libc.so.6 (0x00007f348e12c000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f348e4a0000)
  libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f348e112000)
  libutil.so.1 => /lib64/libutil.so.1 (0x00007f348e10b000)
  libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f348e0d1000)
  librt.so.1 => /lib64/librt.so.1 (0x00007f348e0c6000)
$ file build/bin/native/releaseExecutable/JapaneseNumeralTranslator.kexe
build/bin/native/releaseExecutable/JapaneseNumeralTranslator.kexe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.16, BuildID[xxHash]=c76aff5e0db3fdae, not stripped
$ ldd build/bin/native/releaseExecutable/JapaneseNumeralTranslator.kexe
  linux-vdso.so.1 (0x00007ffff69c2000)
  libdl.so.2 => /lib64/libdl.so.2 (0x00007f41ad9dd000)
  libm.so.6 => /lib64/libm.so.6 (0x00007f41ad897000)
  libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f41ad875000)
  libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f41ad85a000)
  libc.so.6 => /lib64/libc.so.6 (0x00007f41ad68f000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f41ada03000)
  libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f41ad675000)
  libutil.so.1 => /lib64/libutil.so.1 (0x00007f41ad66e000)
  libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f41ad634000)
  librt.so.1 => /lib64/librt.so.1 (0x00007f41ad629000)
$ ls -lh build/bin/native/debugExecutable/JapaneseNumeralTranslator.kexe
-rwxr-xr-x. 1 eraxillan eraxillan 1.8M Mar  7 13:24 build/bin/native/debugExecutable/JapaneseNumeralTranslator.kexe
$ ls -lh build/bin/native/releaseExecutable/JapaneseNumeralTranslator.kexe
-rwxr-xr-x. 1 eraxillan eraxillan 529K Mar  7 13:24 build/bin/native/releaseExecutable/JapaneseNumeralTranslator.kexe
Тут всё в порядке, бинарники скромные по размеру и без каких-либо сторонних зависимостей.Отладка проектаОна не работает, по крайней мере в Community Edition.Просто игнорируются точки останова, хотя судя по выводу команды file отладочные символы в исполняемом файле есть.  Насколько я понял, соответствующий плагин доступен только в платной редакции IDE, которая пока для меня избыточна.Прошу поправить, если ошибаюсь.Так что для написания демо пришлось обходиться printf-driven отладкой, ну мне не привыкать после Android AOSP.ИнтернационализацияВ случае нашего проекта нужно лишь все локализуемые строки "обернуть" в вызов функции gettext().Для краткости можно сделать синоним этой функции, например tr(), это общепринятая практика.
import kotlinx.cinterop.*
import platform.linux.*
import platform.posix.*
fun tr(key: String): String = gettext(key)?.toKString() ?: ""
Понадобится ещё служебный код для работы с POSIX-локалями, он находится в файле Locale.kt.Также мы должны заранее определиться с количеством поддерживаемых языков: для демо это будут только английский и русский, плюс числительные на японском для любого языка.Кстати, посмотреть названия локалей можно с помощью команды locale -a.Перевод локализуемых строкЯ написал Bash-скрипты для генерации с нуля и обновления файлов gettext: Далее вкратце опишу основные шаги, которые они выполняют.Генерируем pot-файл ("шаблон"), который содержит базовую информацию о программе и собственно строки нуждающиеся в переводе.
# Extract all tr() wrapped strings to po/jnt.pot file
xgettext --keyword=tr --language=java \
    --add-comments --sort-output \
    --copyright-holder='Alexander Kamyshnikov <axill777@gmail.com>' \
    --package-name='Japanese numeral translator' \
    --package-version='1.0' \
    --msgid-bugs-address='axill777@gmail.com' \
    -o po/jnt.pot --files-from=KT_FILES
Генерируем po-файл (текстовый перевод):
# Generate locale sources
# NOTE: --no-translator option is a workaround to supress email input request
msginit --no-translator --input=po/jnt.pot --locale=en_US.UTF-8 --output po/en_US/jnt.po
msginit --no-translator --input=po/jnt.pot --locale=ru_RU.UTF-8 --output po/ru_RU/jnt.po
Генерируем mo-файл (бинарный перевод):
# Generate locale binary files
msgfmt --output-file=po/en_US/jnt.mo po/en_US/jnt.po
msgfmt --output-file=po/ru_RU/jnt.mo po/ru_RU/jnt.po
Развертывание бинарных файлов с переводамиК сожалению, Kotlin/Native не поддерживает ресурсы, так что "упаковать" mo-файлы в исполняемый файл пока не выйдет.На это есть соответствующий баг.Думаю, функционал ресурсов можно реализовать и вручную дополнительной задачей в Gradle, это пожалуй тема для отдельной статьи.Для тестового проекта положим mo-файлы рядом с приложением, где не нужны права суперпользователя и где gettext сможет их найти.Для релиза приложение следует упаковать в RPM/DEB-пакет, а mo-файлы установить в директорию /usr/share/locale.ИтогКак видите, процесс несложен, по крайней мере при наличии готового кода и скриптов.В процессе разработки нужно лишь периодически вызывать update_localization.sh, переводить новые строки, и снова вызывать этот скрипт для генерации mo-файлов.ありがとう ございます, или спасибо за внимание!ИсточникиОпределения взяты из документации Django Почему интернационализация и локализация имеют значениеKotlin Native: следите за файламиDxGetText — GNU Gettext for Delphi and C++ BuilderP.S.: дальше планирую использовать Kotlin/Native уже в рамках кроссплатформенных библиотек. Если будет интерес, могу доработать демо, например портировать на Windows.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_programmirovanie (Программирование), #_*nix, #_lokalizatsija_produktov (Локализация продуктов), #_kotlin, #_izuchenie_jazykov (Изучение языков), #_kotlin, #_gettext, #_localization, #_internationalization, #_linux, #_japanese, #_programmirovanie (
Программирование
)
, #_*nix, #_lokalizatsija_produktov (
Локализация продуктов
)
, #_kotlin, #_izuchenie_jazykov (
Изучение языков
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 05-Окт 16:27
Часовой пояс: UTC + 5