27 мая 2008 г.

Ruby. GetText. Интернационализация приложений

Преамбула


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


Выход есть. Велосипед уже изобретен и успешно ездяет :-) По опыту работы с системой GetText в других приложениях я был весьма обрадован ее наличием в Ruby и в частности поддержкой ее в системе Ruby on Rails. Незначительное замечание: система gettext написана полностью на Ruby и не являеться портом распространенной системы GNUGetText, хоть и полностью с ней совместима по формату.


Для начала нужно установить гем gettext. На данный момент самым свежим был 1.90, более ранние версии имели серьезные проблемы в парсере и отлавливали не все строки ресурсов для локализации.



gem install gettext


Для начала определимся с понятием языкового домена. Проще говоря, это имя файла в который будет использоватся для перевода. Обычно это название приложения. Например: blog


Дополним дерево каталогов нашего приложения следующими структурами:


/locale/ru/LC_MESSAGES


/locale/uk/LC_MESSAGES


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


/po/ru


/po/ru


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


Немаловажно упомянуть также pot файл. Это основной файл языкового домена. Он будет создан автоматически и размещен в каталоге /po. В него будут извлекаться все новые ресурсы и затем мержится с каждым файлом po из языковых подкаталогов. Не стоит волноваться за сохранность уже переведенных ресурсов. Извлечение в pot файл и дальнейший мерж(merg) его с po файлами построены таким образом, что дополняют новыми ресурсами из pot файла и не затрагивают уже переведенных.


Для начала создадим две rake-задачи, для облегчения нашей работы в будущем.


/lib/tasks/gettext.rake


desc "Update pot/po files."

task :updatepo do

require 'gettext/utils'

GetText.update_pofiles("blog", Dir.glob("{app,lib,bin}/**/*.{rb,erb,rjs}"), "blog 1.0.0")

end



desc "Create mo-files"

task :makemo do

require 'gettext/utils'

GetText.create_mofiles(true, "po", "locale")

end


Первая задача предназначена для извлечения ресурсных строк и мержа po файлов. Вторая для непостредственной из компиляции. Хочу заметить, что работая с этой этой системой в других средах разработки все операции приходилось делать вручную. Использование системы соглашений в RoR позволяет выполнять задачи извлечение и компиляции практически "в одно касание". Тоесть, если pot файл отсутствует, то он будет создан. Если присутсвуют языковые каталоги, и в них существуют po файлы, то они будут смержены и соответсвенно будут созданы и откомпилированы mo файлы.


Дополним файл /config/environment.rb строкой


require 'gettext/rails'


 


В файл app/controllers/application.rb добавим слудеющий код.


class ApplicationController < ActionController::Base


# Выбираем локаль

GetText.locale = "ru"


# Инициализируем систему доменом

init_gettext "blog"


# Устанавливаем кодировку

before_filter :set_charset



def set_charset

headers['Content-Type'] = "text/html;charset=utf-8"

end

end


Ну вот основные приготовления закончены. После вышеперечисленных действий в наше распоряжени поступила функция _() (нижнее подчеркивание) и все строки к которым она будет применена подлежат извлечению и локализации. Кстати говоря система дополнительно извлекает из базы название сущностей и их свойств.


Напишем где нибудь в коде <%= _("Hello world!!!") %> и выполним задачу


rake updatepo


В каталоге /po система содаст файл языкового домена с расширением pot. В дальнейшем вам вообще не понадобиться трогать этот файл. Кроме случаев создания новой языковой локали, которая заключается в копировании файла pot в каталог /po/{lang}/LC_MESSAGES изменяя только расшинение на po.


Итак скопируем переименованный blog.pot в файл /po/ru/LC_MESSAGES/blog.po


Теперь мы готовы началь перевод. Открываем blog.po любым редатором, например poEdit. Этот редактор кросплатформный и с открытым исходным кодом. Находим в списке ресурс "Hello wordl!!!" и в нижнем окне пишем его перевод: "Привет мир!!!".


После закрытия файла достаточно просто выполнить задачу:


rake makemo


Это приведет к компиляции переведенного po файла в mo файл.


На этом все. В следующий раз я обясню свою мотивацию в выборе системы перевода. Если конечно вы сами не поймете и не проникнетесь красотой простоты решения :-)



8 мая 2008 г.

Redmine, Trac, Mantis ...

В каждой мало-мальски уважающей организации существует так называемый "корпоративный" сайт, общая база знаний или просто доска объявлений. Вникать и рассуждать о надобности таких ресурсов не имеет смысла и да и ставиться за цель. Они, однозначно, нужны, как место для концентрации корпоративных знаний, снижения коефициента искажения информации при внутрикорпоративном общении, средство корпоративной отчетности да и вообще показывают общую картину процесов в компании. Одним из основных достоинств, я считаю, есть единое хранилище информации для сотрудников только приступивших к работе в компании или над новым проектом.
В данный момент речь не идет о таких монстрах как MS Project, Jira и т.д. так как в большинстве своем они либо избыточны либо сами по себе не предназначены для тех целей к которым их хотят принудить.
Речь пойдет о свободнораспростраямых WEB ориентированных системах трекинга: Trac, Mantis и Redmine.
В разное время я работал с каждой из них и хочу выделить несколько критериев по которым высказать свое мнение о системах вцелом.
Изначально все системы имеют общий набор атрибутов: OpenSource, кросплатформность, наличие системы сопровожения заданий и разпределения их между пользователями.

1. Легкость установки
Для установки Mantis необходим Apache с поддержкой PHP и MySQL. Хотя существует версия включающая уже настроенную для использования среду.

Об установке Trac ходят легенды :-) Сложности возникают на этапе интеграции Apache, Python и SVN из-за несовместимости бинарных дистрибутивов. Настройка проектов и интеграция с SVN осуществляеться в командном режиме. Та же ситуация с натройкой пользователей. Гораздо проще утановить из репозитариев, но все равно оставляеть неприятный осадок. Trac может быть запущен и без apache на собственном сервере, это слегка разбавляет ситуацию.

Redmine - производит впечатление "коробочного" продукта. Для работы требуеться лиш установленный интерпритатор языка Ruby и для начала библиотека для работы с базой SQLlite и пакет драйвера для работы с этой базой. Незначительные манипуляции по установке начальных установок и генерации базы позволяет поднять систему на собственном встроенном сервере. Не лишним будет сказать, что этих самых серверов существует как минимум три. Так, что эдинственным сторонним продуктом во всей системе, можно сичитьть только СУРБД.

2. Наличие Wiki
Mantis не поддерживает встроенной системы Wiki.
Trac содержит Wiki.
Redmine содержит Wiki.

3. Мультиязычность
Mantis поддерживает локализацию.
Локализация Trac "прибита гвоздями". Тоесть изначально он не имеет подсистемы локализации.
Redmine - идет включает подсистему локализации не только на уровне приложения, но и на уровне отдельніх пользовательских аккаунтов. Русский и украинский идут в поставке.

4. Интеграция с системами контроля версий
Mantis не содержит интеграции с ситемами контроля версий.
Trac интегрирован с SVN.
Redmine - интегрирован с SVN, CVS, Darks, Bazaar, Mercurial, Git. Причем настройка заметно проще чем в Trac.

5. Идивидуальные вкусности и перспективы
Собственно этот пункт призван быть развязкой. Как видно Mantis являеться явным аутсайдером и основная схватка пойдет между Trac и Redmine.
Хотя и так уже понятно кто победит :-) Вобщем-то это уже даже не борьба вышеупомянутых систем, а скорее сообщиств Ruby и Python.
Вобщем я стаю на сторону Redmine. Простота установки, локализация, простота настройки, поддержка Wiki и систем контроля версий стали основными причинами для моего выбора. Миграция существующих проектов c Trac прошла отлично, если не считать поплЫвшие таблици в Wiki.
Вообще довольно интересно наблюдать за развитием языка Ruby, так стремительно вышедшего на сцену и думаю уже обогнавшего своих основных конкурентов PHP и Python и даже замахнувшегося на Java :-)