Создание многоязыкового инсталлятора для Windows с помощью WiX

  • Tutorial
logoВ этой статье я поделюсь с вами практическим опытом, полученным за много лет создания инсталляторов в Фаматек. Под катом — теоретические выкладки и практические инструкции, как безболезненно и «по феншую», совместимым с «Windows Logo Testing» способом создать инсталлятор, предлагающий пользователю выбрать язык установки и устанавливающий продукт на выбранном языке. При этом используются исключительно бесплатные решения.

Зачем нужен многоязыковой инсталлятор?


Прежде чем перейти к технической части, я немного растекусь мыслью по древу и расскажу, зачем нужны все эти шаманские танцы с бубном. Итак, как выглядит типичный инсталлятор Windows приложения? Это .msi или .exe файл, который при запуске интересуется, куда и что будем ставить, по возможности ставит это, создает ярлыки где сможет и запускает установленную программу — дабы пользователь мог ее сразу же использовать. Знакомое зрелище, не правда ли? Так инсталлятор выглядит до тех пор, пока перед разработчиком не встает во всей своей красе задача сделать локализованную версию. Русскую, немецкую, китайскую… Ибо пользователи очень хотят.

И если с самой программой, как правило, проблем не возникает, то инсталлятор ставит разработчика перед довольно интересным выбором. Какие есть варианты?

  1. Самое простое решение — это сделать кучку инсталляторов, каждый из которых будет обладать пользовательским интерфейсом на нужном языке и устанавливать программу с необходимыми файлами и настройками, чтобы она также отображала свой интерфейс на том же языке. К сожалению, у способа есть небольшой минус: по мере увеличения количества локализаций управлять 30+ инсталляторами становится не очень удобно. Плюс всплывает забавный момент: если пользователь нашел программу через поиск, то он с большей вероятностью скачает ее, если рядом с ней написано, что она поддерживает его язык. Увы, английский знают далеко не все.
  2. Можно сделать один инсталлятор и автоматически выбирать язык программы при старте. Так поступает, например, 64-битная версия популярной утилиты 7-zip. Увы, у такого способа тоже есть ряд недостатков. Во-первых, не знающие английского пользователи просто-напросто могут не справиться с интерфейсом самого инсталлятора. Во-вторых, программа выбирает язык при старте, часто основываясь на настройках системы, которые не всегда соответствуют тому, что пользователь хочет на самом деле. В результате программа часто не угадывает и показывает интерфейс не на том языке, что отрицательно сказывается на такой тонкой материи, как user experience.
  3. Хорошим решением будет предложить пользователю выбрать язык инсталлятора и программы при старте инсталлятора. Но тут нас поджидают чисто технологические трудности: предпочтительный для Windows тип инсталляторов, MSI, такую возможность не поддерживает в принципе, а использование Innosetup / Nsis ведет к другой проблеме — при сертификации программы на «Windows Logo» компания Microsoft настоятельно просит инсталлятор именно в виде MSI. Плюс инсталляторы форм-фактора MSI очень любят крупные компании, использующие их для автоматического обновления и развертывания программ на тысячи компьютеров одним кликом.


Честно сделать MSI, который говорил бы с пользователем на выбранном им языке, нельзя. Но можно применить «военную хитрость» и воспользоваться техникой, которую вы все видели в инсталляторах таких немаленьких компаний как Adobe или DrWeb: инсталлятор представляет из себя .exe файл, который при запуске предлагает выбрать язык, затем извлекает из себя .msi, проводит с ним ряд странных манипуляций, и дальше установка идет на выбранном языке. Самый популярный вид «военной хитрости» — это орденоносная и всячески заслуженная программа InstallShield. Она и MSI сделает, и в .exe его упакует, и меню выбора языка сама приделает. Все в ней хорошо, кроме цены — порядка пяти тысяч долларов за рабочее место. Не то, чтобы очень дорого (у нас есть парочка лицензий), но масштабируемость страдает. Ни в open source такой инсталлятор не положишь, ни фрилансеру честно на доработку не отдашь. Но можно обойтись без InstallShield, исключительно с помощью бесплатных программ, шаманского бубна и пары заклинаний.

Образцово-показательная многоязыковая программа



Для начала сделаем простую многоязыковую программу, которую будем ставить. Наиболее простой и наглядный способ — это использовать бесплатный фреймворк Qt, в котором многоязычность сделана из коробки очень хорошо. Скачиваем и устанавливаем все для сборки программы здесь. Устанавливаем переменную окружения QTDIR на папку, в которую установилось «bin\qmake.exe» (для последней версии Qt при установке по умолчанию это будет «C:\QtSDK\Desktop\Qt\4.7.3\mingw») и переменную окружения MINGWDIR на папку, в которую установилось «bin\mingw32-make.exe» (для последней версии Qt при установке по умолчанию это будет «C:\QtSDK\mingw»). Эти переменные окружения нужны моему скрипту сборки, чтобы корректно все собрать одним кликом :).

Саму программу я уже подготовил: забираем исходники тут, распаковываем куда-нибудь и запускаем батник с говорящим названием «build.bat». Этот батник соберет из исходников демонстрационную программу и положит результат в "%TEMP%\multilang_test". Если зайти в эту папку, то мы увидим исполняемый файл нашей программы, несколько библиотек Qt и файл локализации на русский с глубокомысленным расширением «qm». Запустив программу и воспользовавшись верхним меню, мы можем на лету переключать язык между английским и русским. Для того, чтобы инсталлятор мог указать программе, на каком языке она будет говорить, при первом запуске предусмотрен специальный ключ реестра, «HKCU\Software\my_company\multilang_app\lang_cur», который можно установить в значение 0 для английского языка интерфейса и 1 для русского:

image

… и «калечный» многоязыковой инсталлятор



После того, как программа получилась, можно сделать к ней инсталлятор. Для сборки инсталлятора мы воспользуемся бесплатной программой WiX от Microsoft (кстати, если мне не изменяет память, это первый проект Microsoft, который был передан в open source). Скачать и установить его можно отсюда (требует наличия .net framework). Также нам потребуется программа для создания вышеупомянутого bootstrapper — для этих целей мы воспользуемся бесплатной программой dotnetinstaller, скачать и установить которую можно здесь. Для dotnetinstaller необходимо установить переменную окружения DNIDIR на папку, в которую установилось «dotnetinstaller.exe» (для установки по умолчанию это будет «C:\Program Files\dotnetinstaller\bin»).

Для начала нам нужно сделать MSI. WiX создает MSI из XML-описания, которое достаточно простое и подробно рассмотрено в ряде статей на хабре. Дабы не углубляться в детали, я подготовил проект инсталлятора: забираем исходники тут, распаковываем куда-нибудь и запускаем батник с говорящим названием «build.bat», результатом работы которого будет кучка промежуточных файлов и финал эволюции — файл "%TEMP%\multilang_test\multilang_install.exe" — собранный мультиязыковой инсталлятор, запустив который мы увидим сначала меню выбора языка, а затем — интерфейс инсталлятора на выбранном языке и установленную программу также на выбранном языке:



Как такое получается? Рассмотрим последовательность магических действий, приводящую к результату, не намного уступающему Adobe. Сначала мы создаем английский и русский инсталляторы в формате MSI — для этого достаточно указать линкеру WiX ключ командной строки "-cultures:en-US" для английского языка и "-cultures:ru-RU" для русского (см. батник сборки).

Получившиеся MSI инсталляторы мы хотим распаковывать и запускать из нашего bootstrapper'а в зависимости от выбранного языка. В принципе, ничто не мешает нам поместить в bootstrapper оба MSI как есть. Но это будет не лучшее решение, так как каждый MSI сдержит ВСЕ файлы нашей программы и различаются они только строчками текста и тем, какое значение писать в реестр для установки языка по умолчанию. Для небольших программ и двух языков это не критично, но если файлы нашей программы весят хотя бы сотню мегабайт, а локализована она на три десятка языков, то создавать инсталляционный пакет весом в три гигабайта мы, наверное, не захотим. Поэтому хранить в bootstrapper'е мы будем только один MSI, английский, а для остальных мы будем хранить так называемую «трансформу» — .MST файл, который содержит разницу между английским инсталлятором и локализованным. Такие файлы как правило имеют небольшой размер, и их можно «применить» к английскому MSI во время инсталляции чтобы «превратить» его в локализованный. Для создания MST файла используется программа «torch», входящая в состав WiX. Любопытные снова изучают содержимое .bat файла.

Теперь у нас есть один большой MSI файл и маленький MST для превращения его в русскоязычный. Можно класть в bootstrapper? Теоретически можно, практически же, если положить файлы как есть, то опять получим проблемы. На этот раз на этапе удаления программы. Удаление установленных через MSI программ вообще происходит достаточно забавным способом: Windows во время установки сохраняет MSI в своей папке (кстати, одна из причин отчаянного роста папки Windows), а во время удаления использует сохраненный MSI. Проблемы с нашими файлами заключается в том, что MST Windows сохранять не умеет, и, установив русскую версию приложения с использованием комбинации MSI и MST файла, мы получим потенциальные проблемы при удалении через Add/Remove Programs: Windows попытается найти MST, участвовавший при установке, не сможет этого сделать и сильно обидится. Дабы не огорчать Windows, MST файлы можно положить внутрь MSI и применять прямо из него. Для этого существует специальный скрипт на вижуал бейсике от Microsoft, доступный тут и предусмотрительно положенный мною в архив с исходниками инсталлятора. Применение этого скрипта (любопытные опять могут заглянуть в батник) помещает MST в MSI, и, чтобы запустить такой MSI в русском режиме, используется что-то вроде:

msiexec /i multilang_install_en.msi TRANSFORMS=:1049


Итак, все готово — можно собирать bootstrapper. Как уже говорилось, для этого существует специально обученная open source программа со странным названием dotnetinstaller. Вообще она предназначается для создания инсталляторов, тянущих за собой .NET Framework, но также может создавать меню выбора языков и в зависимости от выбранного языка извлекать из себя и запускать MSI с разными ключами командной строки. Так же как и WiX, dotnetinstaller принимает исходник в формате XML (уже созданный исходник проекта прилагается). Проект в целом достаточно прост: в нем я указываю dotnetinstaller, что нужно сделать меню с двумя языками, для английского вызывать MSI как он есть, а для русского — с примененной трансформой.

Итог



Использованные в моих примерах исходники в кодировке UTF-8 полностью прокомментированы на русском и могут служить примером минимальной многоязыковой программы и минимального инсталлятора к ней. К сожалению, как видно из вышеописанных заклинаний, MSI изначально не рассчитан на создание многоязыковых инсталляторов «все-в-одном», поэтому создание таких инсталляторов — достаточно сложный процесс. Так что, если у вас нет острой необходимости сертифицироваться на «Windows Logo» и иметь MSI для корпоративных клиентов, используйте InnoSetup или NSIS — там задача создания многоязыкового инсталлятора решается намного проще.

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

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

И еще один итог



Результаты применения всего вышеперечисленного можно посмотреть в нашей бесплатной программе Advanced IP Scanner для сканирования локальных сетей. Многоязыковой инсталлятор, динамический выбор языка интерфейса и прочее по феншую. Комментарии по сканеру также принимаются :).

  • +23
  • 14.9k
  • 3
Фаматек
69.87
Company
Support the author
Share post

Comments 3

    0
    Интересно и полезно. Спасибо
      +1
      А вот таким способом нельзя сейчас воспользоваться?
        +1
        Можно. Более того, запихивание MST в MSI как у меня описано — это как раз от данного способа.
        К сожалению, у него есть ряд минусов:
        1. Шибко недокументированный, может поменяться в любой момент, может быть нарушен любой надстройкой над операционкой.
        2. Использует настройку «standards and formats», которая, к сожалению, не всегда корректно отображает нужный пользователю язык :(. Юзабилити гласит, что предлагать выбрать язык надо (выбрав по умолчанию какой мы считаем нужным). Иначе будут недовольные пользователи — а даже десяток недовольный пользователей могут воплями «втф у меня ваша программа установилась не на русском а на немецком!?!» сильно испортить карму O_O.

      Only users with full accounts can post comments. Log in, please.