こんにちは, или добрый день по-японски. Как бы не был популярен английский язык, всё же пользователям комфортнее и привычнее в родной языковой среде. Поэтому далее мы пошагово рассмотрим процесс адаптации консольного приложения для Linux на Kotlin/Native к русской и английской локали. Поможет нам в этом старый-добрый GNU gettext. В итоге убедимся, что это совсем не страшно. Заодно посмотрим интеграцию с библиотеками на C, которая значительно расширяет возможности Kotlin/Native. Что напишем: переводчик количественных числительных на японский язык. Что ожидается от читателя: знание языков программирования Kotlin, C, базовый уровень знакомства с ОС Linux (в частности Bash). Что понадобится в процессе: любой дистрибутив Linux, любая версия IntelliJ IDEA, установленный пакет gettext-devel или аналогичный. Где-то полгода медленно и печально изучаю японский язык по учебнику “Minna no Nihongo”: частью ради перевода текста песен, частью просто из интереса к культуре. Позже решил перейти с системной разработки на прикладную, с десктопа на мобильные платформы, соответственно с C++/Qt/STL на Kotlin/JVM/Android SDK. Теперь хочу два этих занятия совместить, написав для себя программы для помощи в изучении японского. Конечно, уже много готовых, но NIH-синдром ведь не что-то плохое, правда? Раньше я не задумываюсь использовал бы связку Qt/QML/C++: она позволяет быстро и эффективно решать в общем-то любые задачи и на любой платформе. Однако Qt всё больше поворачивается спиной к Open Source, вот и решил пора валить попробовать что-то другое. И тут в процессе изучения Kotlin узнал про 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 Translation Project. Состоит из: правила оформления исходного кода для последующей интернационализации утилиты для генерации текстовых файлов с локализуемыми строками кроссплатформенная нативная библиотека для извлечения переводов в runtime правила дистрибуции бинарных файлов с переводами Интернационализация в 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. На мой взгляд многовато для такого маленького проекта и такой конфигурации. Тут можно вспомнить о преимуществах скриптовых языков, которые компилировать не надо. Теперь посмотрим свойства исполняемого файла для отладочной и релизной конфигураций: Тут всё в порядке, бинарники скромные по размеру и без каких-либо сторонних зависимостей. Она не работает, по крайней мере в Community Edition. Просто игнорируются точки останова, хотя судя по выводу команды file отладочные символы в исполняемом файле есть.   Насколько я понял, соответствующий плагин доступен только в платной редакции IDE, которая пока для меня избыточна. Прошу поправить, если ошибаюсь. Так что для написания демо пришлось обходиться printf-driven отладкой, ну мне не привыкать после Android AOSP. В случае нашего проекта нужно лишь все локализуемые строки "обернуть" в вызов функции gettext(). Для краткости можно сделать синоним этой функции, например tr(), это общепринятая практика. Понадобится ещё служебный код для работы с POSIX-локалями, он находится в файле Locale.kt. Также мы должны заранее определиться с количеством поддерживаемых языков: для демо это будут только английский и русский, плюс числительные на японском для любого языка. Кстати, посмотреть названия локалей можно с помощью команды locale -a. Я написал Bash-скрипты для генерации с нуля и обновления файлов gettext: create_localization.sh update_localization.sh Далее вкратце опишу основные шаги, которые они выполняют. Генерируем pot-файл ("шаблон"), который содержит базовую информацию о программе и собственно строки нуждающиеся в переводе. Генерируем po-файл (текстовый перевод): Генерируем mo-файл (бинарный перевод): К сожалению, 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++ Builder P.S.: дальше планирую использовать Kotlin/Native уже в рамках кроссплатформенных библиотек. Если будет интерес, могу доработать демо, например портировать на Windows.