Локализация интерфейса и сообщений в приложениях .NET и Delphi

| рубрика: Заметки | автор: st
Метки:

Статья была опубликована в журнале "Мир ПК" №2-2008

Не так давно в журнале уже затрагивалась тема локализации (см. «Мир ПК», №6/07, с. 68). В своей статье «Локализация приложений в Delphi для Win32» Михаил Перов рассказал о различных инструментах — коммерческих и бесплатных. Вернуться к сюжету мне пришлось по двум причинам: во-первых, хотелось бы описать непосредственно суть проблемы, вызвавшей появление ряда дополнительных инструментов, и, во-вторых, показать на примере, что разработчики вполне могут решить задачу своими силами.

В действительности технология локализации предлагается поставщиком как для среды разработки Microsoft Visual Studio.NET 2003/2005 (подгружаемые ресурсы), так и для CodeGear Delphi (Data Translation Wizard).

Основное неудобство штатных средств проявляется прежде всего при вовлечении в цикл локализации других исполнителей. В самом деле, вряд ли разработчики смогут самостоятельно сделать корректный перевод текста на несколько языков. Просить же далекого от программирования переводчика (а в заказных проектах зачастую ими являются ключевые пользователи) «набить текст», пользуясь средой Visual Studio или Delphi, не самая лучшая идея. Также всегда есть неприятные, хотя и преодолимые проблемы при локализации уже существующих приложений, где такая возможность не была предусмотрена изначально.

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

Локализация для .NET

Примечание: см. также более общий подход к локализации для .NET и Mono gettext for .NET/Mono.

Ресурсы редактируются в удобной для переводчика табличной форме в среде MS Excel (рис. 1). Выбор Excel позволяет привлечь к переводу практически любого пользователя, однако при большом количестве задействованных людей, которым рассылаются файлы при децентрализованной работе, могут возникнуть другие проблемы: корректное слияние изменений, версии. Их можно решить, например, используя файл, размещенный на сервере SharePoint: в этом случае пользователь сам синхронизирует сделанные изменения через веб-сервис. При работе внутри сети предприятия возможно простое разделение доступа к файлу на сетевом диске или в системе управления исходным кодом (Visual SourceSafe, SVN и др.). То есть данная схема вполне может масштабироваться до небольших групп переводчиков, работающих в автономном режиме.

Рис. 1. Файл со строковыми ресурсами

Цифрами отмечены:

  • 1 и 2 — таблица должна содержать как минимум две колонки: «Message» (идентификатор) и «Neutral» (значение по умолчанию). Идентификатор должен быть уникален для каждой строки.
  • 3 — таблица должна иметь название «Messages».
  • 4 — для локализации можно добавлять неограниченное число колонок с заголовком в виде «язык-страна», соответствующим значениям культур, поддерживаемых платформой.NET.
  • Пустые ячейки: будет использовано значение из колонки «Neutral», соответственно эта колонка должна быть заполнена всегда.

Далее, исходный Excel-файл обрабатывается утилитой, генерирующей ресурсные файлы для платформы .NET. Файлы включаются в проект разработчиком, компилируются как встроенные ресурсы (embedded resource) и затем с использованием небольшой вспомогательной библиотеки классов загружаются в приложение с минимальным написанием программного кода (рис.2).

Рис. 2. Схема локализации для .NET

Чтобы избежать коллизий при наличии нескольких исходных файлов, с которыми работают разные сотрудники, рекомендуется придерживаться именования, соответствующего проекту, например <project name>.Messages.xls.

Для генерации используется утилита ResourceMaker, конвертирующая файл Excel во множество файлов .NET с расширениями *.resx. Для обеспечения минимального уровня автоматизации утилита запускается из командной строки с параметрами:

Arbinada.Utils.ResourceMaker.exe <имя файла Excel> <Каталог генерации (проекта)>

Например:

Arbinada.Utils.ResourceMaker.exe "C:\Arbinada\Arbinada.MyApp\Arbinada.MyApp.messages.xls" "C:\Arbinada\Arbinada.MyApp" "Messages"

Рекомендуется использовать имя файла ресурсов (BaseName) «Messages», чтобы в дальнейшем при использовании строковых ресурсов в приложении уменьшить количество необходимого для этого кода.

Утилита генерирует один или более файлов *.resx для каждого определенного вами языка в формате: resx для значений по умолчанию (neutral), .ru-Ru.resx для русского, .fr-Fr.resx для французского и т.д.

Далее вам нужно включить эти ресурсы в проект приложения и перестроить его, после чего они будут готовы к использованию.

Для удобной работы с ресурсами применяется небольшая библиотека Arbinada.Utils.Localizer. Вам необходимо скомпилировать эту сборку и добавить на нее ссылку в вашем проекте. Чтобы просто извлечь строку по ее идентификатору, используйте следующий код:

using System.Reflection;
using Arbinada.Utils.Localizer;

/* Где-то в коде */
string myMessage = ResExtractor.GetMessage(
  Assembly.GetExecutingAssembly(),
  "MyResourceStringId");
/* Если имя ресурсного файла (BaseName) нестандартное,
   то вам нужно всякий раз указывать его явно */
string myMessage2 = ResExtractor.GetMessage(
  Assembly.GetExecutingAssembly(),
  "MyMessagesFile", "MyResourceStringId");

Чтобы избежать написания большого количества однообразного кода, для локализации визуальных элементов (текст — labels, опции — checkboxes и т. д.) в ваших веб-формах или пользовательских элементах управления (user controls) вы можете использовать класс WebFormLocalizer внутри события Page_Load:

private void Page_Load (object sender, System. EventArgs e)
{
   WebFormLocalizer localizer = new WebFormLocalizer (
      Assembly.GetExecutingAssembly(),
      this,
      MyMessages,   // параметр не нужен, если используется файл “Messages”
      DirThemesCtl_ // префикс идентификатора уровня формы, ресурс ищется по строке
                      // <Префикс — ID формы> + <WebControl.ID>
    );
    
}

Этот фрагмент программы меняет текст (caption) у элементов веб-формы по их идентификатору (WebControl. ID) на соответствующий, найденный в ресурсах. В прилагаемом к статье примере поддерживаются элементы типов: Label, CheckBox, LinkButton, Button и DataGrid.

Для DataGrid используется трехуровневая сигнатура для идентификатора ресурса:

<Префикс — ID формы> + + "_" + <Суффикс — ID колонки>.

Поддерживаются BoundColumn и TemplateColumn. Для ButtonColumn суффикс может принимать значения Edit, Cancel и Update. Более подробно вы можете посмотреть в исходном тексте файла WebFormLocalizer.cs.

Локализация для Delphi

Технология не имеет принципиальных отличий, она была успешно адаптирована для Delphi 2007 Win32 (рис.3).

Рис.3. Схема локализации для Delphi

Как видно из приведенной схемы, непосредственной компиляции в ресурсы Windows-приложения не происходит. Вместо этого мы используем XML-файлы, содержащие метаинформацию об используемых языках и собственно переведенные строки (рис.4, 5).

Рис.4. Метаинформация об используемых языках локализации

Рис.5. Строковые ресурсы

Вместо написания нескольких строк текста выбираем в палитре компонент TA3FormLocalizer и в качестве источника назначаем ему LocalizerStore (рис.6).

Рис.6. Локализация формы в Delphi. Просто "кидаем компонент".

В самом приложении необходимо только проинициализировать хранилище локализованных данных.

{
  Читаем данные из директории Localization по заданным SourceFileName и SelectedLocale.
  Например, если свойство SourceFileName = 'SampleApp', а SelectedLocale = 'ru-RU',
  то будет загружен файл: .\Localization\SampleApp.ru-RU.xml
}
procedure TFormMain.FormCreate(Sender: TObject);
begin
   LocalizerStore.SelectedLocale.Parse(TLocaleInfo.GetSystemLocale);
   LocalizerStore.LoadData;
end;

После запуска приложение показывает информацию на выбранном языке (в примере используются текущие настройки локализации Windows для пользователя).

Рис.7. Локализованное Delphi-приложение

Взяв исходные тексты (файл A3Localization. pas), вы сможете изучить имеющиеся возможности по локализации широкого круга VCL-компонентов (стандартные элементы управления форм, сетки, компоненты доступа к БД TDataSet с локализацией на уровне названий полей, TActionList и другие). При необходимости в подсистему легко добавить недостающую функциональность, например по локализации ваших собственных компонентов, на основе механизма информации о типах времени выполнения (RTTI — runtime type information), не меняя статической связанности модулей.

Исходные тексты

Zip-архивы, содержащие примеры локализации для веб-приложения .NET и Delphi-приложения под Windows, вы можете скачать по ссылке в конце страницы или на прилагаемом к журналу диске.

Сергей Тарасов, октябрь 2005. Декабрь 2007 (с добавлениями).

Package icon localizer_delphi.zip

Package icon localizer_dotnet.zip