Wix 安装部署教程(十三) -- 多语言安装包
这几天摸索WIX的多语言安装包(这里是Wix的setup 工程,不是Bundle),终于走通了,感谢网友uni的指点。WIX的多语言安装包能够根据系统环境自动切换界面语言,你也可以通过命令指定语言。下面我说一说步骤。共4步。
1.设置WixLocalization文件。
Wxl文件就相当于应用程序的资源文件。让我们根据不同的语言来编写不同的文本内容。
例如我们新建一个WixUI_zh-cn.wxl ,来处理简体中文。
<WixLocalization Culture="zh-cn" Codepage="936" xmlns="http://schemas.microsoft.com/wix/2006/localization">
<String Id="WixUIBack" Overridable="yes">上一步(&B)</String>
<String Id="WixUINext" Overridable="yes">下一步(&N)</String>
<String Id="WixUICancel" Overridable="yes">取消</String>
<String Id="WixUIFinish" Overridable="yes">完成(&F)</String>
<String Id="WixUIRetry" Overridable="yes">重试(&R)</String>
<String Id="WixUIIgnore" Overridable="yes">忽略(&I)</String>
<String Id="WixUIYes" Overridable="yes">是(&Y)</String>
<String Id="WixUINo" Overridable="yes">否(&N)</String>
<String Id="WixUIOK" Overridable="yes">确定</String>
<String Id="WixUIPrint" Overridable="yes">打印(&P)</String>
......
这些主要是用于UI界面上的元素,我们看Wix的窗口源码:下面的!(loc.WixUINext)和!(loc.WixUICancel) 和上面的Id为WixUINext和Id为WixUICancel的String 一一 对应。而属性Overridable,表示可以重写。因为默认是英文,可以重写的意思就是最新的String会覆盖之前的同名Id。
<Dialog Id="WelcomeDlg" Width="370" Title="!(loc.WelcomeDlg_Title)">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56"Height="17" Default="yes" Text="!(loc.WixUINext)" />
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
如果要添加自定义的内容,添加String就行,另外我们可以看见,这个wxl文件有Culture="zh-cn" Codepage="936"两个属性,这是和Product的Language属性和Codepage属性对应,而Product的Codepage属性不能使用!loc.Id 的方式,但Wix会根据wxl文件进行重载。Package的Languages属性和SummaryCodepage 也是一样对应。
<String Id="Lang">2052</String>
<String Id="Code">936</String>
下面是三种语言的Language和Codepage。
语言 语言-国家 Language Codepage
English en-us 1033 1252
Simplified Chinese zh-cn 2052 936
Traditional Chinese zh-tw 1028 950
同理我们分别添加英文和繁体的wxl。更多的源文件在源码的下面的目录可以找到:
WIX\wix-3.8\src\ext\UIExtension\wixlib
最终我们得到:
这个时候不同语言设置的工作我们已经完成。下面设置下属性--Build--Cultures to build,生成安装包。
但是我们看到这三个安装文件,这不是我们想要的。毕竟三个文件都是一样的大。 我们继续处理。将多个msi文件融合成一个。
2.生成mst文件
这里要用到transform files(变形文件?恩,就这么翻译吧) 一个变形文件(.mst)包含了两个msi文件的比较内容,在做Path的时候,也会用到它。既然是比较内容,就需要有一个文件作为基础,这次我们选英文。另外我们会用到一个wix工具:Torch.exe,它在Wix的安装目录bin下面可以找到。它的命令语法如下:
torch.exe [-?] [options] targetInput updatedInput -out outputFile [@responseFile]
示例:
torch.exe -t language "en-us\MyInstaller.msi""zh-cn\MyInstaller.msi" -out "transforms\zn-cn.mst"
别忘了用管理员身份执行,还要带上正确的路径。生成的mst文件再wix的bin目录,也就是和torch.exe的同级目录。如下图所示,操作正确会出现 Windows Installer XML Toolset。。。的字样。
同理我们生成zh-tw.mst和zh-cn.mst,要生成en-us.mst,就把中文和英文的位置换一下。 生成文件如下。
3.合并mst文件。
步骤2相当于我们拿到三个界面的资源文件,现在就是需要把他们合并成一个文件,也就是把mst嵌入到msi文件中。WIX本身没有提供工具做这个事情,但Windows 的SDK 有几个VBScript文件能执行数据库(MSI本身就是一种数据库文件)相关的任务。你需要从MSDN下载安装SDK,这些脚本包含在Win32的示例中。你也可以从下面的链接了解更多内容。
https://msdn.microsoft.com/en-us/library/aa372865(VS.85).aspx
而我们感兴趣的脚本是WixSubStg.vbs. win7系统,它的目录应该是在C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\sysmgmt\msi\scripts. 中。找到之后我们复制到安装工程的Debug目录下。
WixSubStg.vbs:
' Windows Installer utility to add a transform or nested database as a substorage
' For use with Windows Scripting Host, CScript.exe or WScript.exe
' Copyright (c) Microsoft Corporation. All rights reserved.
' Demonstrates the use of the database _Storages table
'
Option Explicit Const msiOpenDatabaseModeReadOnly = 0
Const msiOpenDatabaseModeTransact = 1
Const msiOpenDatabaseModeCreate = 3 Const msiViewModifyInsert = 1
Const msiViewModifyUpdate = 2
Const msiViewModifyAssign = 3
Const msiViewModifyReplace = 4
Const msiViewModifyDelete = 6 Const ForAppending = 8
Const ForReading = 1
Const ForWriting = 2
Const TristateTrue = -1 ' Check arg count, and display help if argument not present or contains ?
Dim argCount:argCount = Wscript.Arguments.Count
If argCount > 0 Then If InStr(1, Wscript.Arguments(0), "?", vbTextCompare) > 0 Then argCount = 0
If (argCount = 0) Then
Wscript.Echo "Windows Installer database substorage managment utility" &_
vbNewLine & " 1st argument is the path to MSI database (installer package)" &_
vbNewLine & " 2nd argument is the path to a transform or database to import" &_
vbNewLine & " If the 2nd argument is missing, substorages will be listed" &_
vbNewLine & " 3rd argument is optional, the name used for the substorage" &_
vbNewLine & " If the 3rd arugment is missing, the file name is used" &_
vbNewLine & " To remove a substorage, use /D or -D as the 2nd argument" &_
vbNewLine & " followed by the name of the substorage to remove" &_
vbNewLine &_
vbNewLine & "Copyright (C) Microsoft Corporation. All rights reserved."
Wscript.Quit 1
End If ' Connect to Windows Installer object
On Error Resume Next
Dim installer : Set installer = Nothing
Set installer = Wscript.CreateObject("WindowsInstaller.Installer") : CheckError ' Evaluate command-line arguments and set open and update modes
Dim databasePath:databasePath = Wscript.Arguments(0)
Dim openMode : If argCount = 1 Then openMode = msiOpenDatabaseModeReadOnly Else openMode = msiOpenDatabaseModeTransact
Dim updateMode : If argCount > 1 Then updateMode = msiViewModifyAssign 'Either insert or replace existing row
Dim importPath : If argCount > 1 Then importPath = Wscript.Arguments(1)
Dim storageName : If argCount > 2 Then storageName = Wscript.Arguments(2)
If storageName = Empty And importPath <> Empty Then storageName = Right(importPath, Len(importPath) - InStrRev(importPath, "\",-1,vbTextCompare))
If UCase(importPath) = "/D" Or UCase(importPath) = "-D" Then updateMode = msiViewModifyDelete : importPath = Empty 'substorage will be deleted if no input data ' Open database and create a view on the _Storages table
Dim sqlQuery : Select Case updateMode
Case msiOpenDatabaseModeReadOnly: sqlQuery = "SELECT `Name` FROM _Storages"
Case msiViewModifyAssign: sqlQuery = "SELECT `Name`,`Data` FROM _Storages"
Case msiViewModifyDelete: sqlQuery = "SELECT `Name` FROM _Storages WHERE `Name` = ?"
End Select
Dim database : Set database = installer.OpenDatabase(databasePath, openMode) : CheckError
Dim view : Set view = database.OpenView(sqlQuery)
Dim record If openMode = msiOpenDatabaseModeReadOnly Then 'If listing storages, simply fetch all records
Dim message, name
view.Execute : CheckError
Do
Set record = view.Fetch
If record Is Nothing Then Exit Do
name = record.StringData(1)
If message = Empty Then message = name Else message = message & vbNewLine & name
Loop
Wscript.Echo message
Else 'If adding a storage, insert a row, else if removing a storage, delete the row
Set record = installer.CreateRecord(2)
record.StringData(1) = storageName
view.Execute record : CheckError
If importPath <> Empty Then 'Insert storage - copy data into stream
record.SetStream 2, importPath : CheckError
Else 'Delete storage, fetch first to provide better error message if missing
Set record = view.Fetch
If record Is Nothing Then Wscript.Echo "Storage not present:", storageName : Wscript.Quit 2
End If
view.Modify updateMode, record : CheckError
database.Commit : CheckError
Set view = Nothing
Set database = Nothing
CheckError
End If Sub CheckError
Dim message, errRec
If Err = 0 Then Exit Sub
message = Err.Source & " " & Hex(Err) & ": " & Err.Description
If Not installer Is Nothing Then
Set errRec = installer.LastErrorRecord
If Not errRec Is Nothing Then message = message & vbNewLine & errRec.FormatText
End If
Wscript.Echo message
Wscript.Quit 2
End Sub
打开cmd 执行下面的命令:
WiSubStg.vbs "en-us\DIAViewSetup.msi" "transforms\zh-cn.mst" 2052
WiSubStg.vbs "en-us\DIAViewSetup.msi" "transforms\zh-tw.mst"
等于是在英文的安装包里面嵌入了简体和繁体的语言变形。 所以我们不必嵌入英文的变形。 完成了这个步骤还不够。我们还需要改变安装包Packages的Languages属性,告诉它支持的语言类型有哪些。而这需要用到另外一个脚本文件
WiLangId.vbs:也需要放于Debug目录下面。
' Windows Installer utility to report the language and codepage for a package
' For use with Windows Scripting Host, CScript.exe or WScript.exe
' Copyright (c) Microsoft Corporation. All rights reserved.
' Demonstrates the access of language and codepage values
'
Option Explicit Const msiOpenDatabaseModeReadOnly = 0
Const msiOpenDatabaseModeTransact = 1
Const ForReading = 1
Const ForWriting = 2
Const TristateFalse = 0 Const msiViewModifyInsert = 1
Const msiViewModifyUpdate = 2
Const msiViewModifyAssign = 3
Const msiViewModifyReplace = 4
Const msiViewModifyDelete = 6 Dim argCount:argCount = Wscript.Arguments.Count
If argCount > 0 Then If InStr(1, Wscript.Arguments(0), "?", vbTextCompare) > 0 Then argCount = 0
If (argCount = 0) Then
message = "Windows Installer utility to manage language and codepage values for a package." &_
vbNewLine & "The package language is a summary information property that designates the" &_
vbNewLine & " primary language and any language transforms that are available, comma delim." &_
vbNewLine & "The ProductLanguage in the database Property table is the language that is" &_
vbNewLine & " registered for the product and determines the language used to load resources." &_
vbNewLine & "The codepage is the ANSI codepage of the database strings, 0 if all ASCII data," &_
vbNewLine & " and must represent the text data to avoid loss when persisting the database." &_
vbNewLine & "The 1st argument is the path to MSI database (installer package)" &_
vbNewLine & "To update a value, the 2nd argument contains the keyword and the 3rd the value:" &_
vbNewLine & " Package {base LangId optionally followed by list of language transforms}" &_
vbNewLine & " Product {LangId of the product (could be updated by language transforms)}" &_
vbNewLine & " Codepage {ANSI codepage of text data (use with caution when text exists!)}" &_
vbNewLine &_
vbNewLine & "Copyright (C) Microsoft Corporation. All rights reserved."
Wscript.Echo message
Wscript.Quit 1
End If ' Connect to Windows Installer object
On Error Resume Next
Dim installer : Set installer = Nothing
Set installer = Wscript.CreateObject("WindowsInstaller.Installer") : CheckError ' Open database
Dim databasePath:databasePath = Wscript.Arguments(0)
Dim openMode : If argCount >= 3 Then openMode = msiOpenDatabaseModeTransact Else openMode = msiOpenDatabaseModeReadOnly
Dim database : Set database = installer.OpenDatabase(databasePath, openMode) : CheckError ' Update value if supplied
If argCount >= 3 Then
Dim value:value = Wscript.Arguments(2)
Select Case UCase(Wscript.Arguments(1))
Case "PACKAGE" : SetPackageLanguage database, value
Case "PRODUCT" : SetProductLanguage database, value
Case "CODEPAGE" : SetDatabaseCodepage database, value
Case Else : Fail "Invalid value keyword"
End Select
CheckError
End If ' Extract language info and compose report message
Dim message:message = "Package language = " & PackageLanguage(database) &_
", ProductLanguage = " & ProductLanguage(database) &_
", Database codepage = " & DatabaseCodepage(database)
database.Commit : CheckError ' no effect if opened ReadOnly
Set database = nothing
Wscript.Echo message
Wscript.Quit 0 ' Get language list from summary information
Function PackageLanguage(database)
On Error Resume Next
Dim sumInfo : Set sumInfo = database.SummaryInformation(0) : CheckError
Dim template : template = sumInfo.Property(7) : CheckError
Dim iDelim:iDelim = InStr(1, template, ";", vbTextCompare)
If iDelim = 0 Then template = "Not specified!"
PackageLanguage = Right(template, Len(template) - iDelim)
If Len(PackageLanguage) = 0 Then PackageLanguage = "0"
End Function ' Get ProductLanguge property from Property table
Function ProductLanguage(database)
On Error Resume Next
Dim view : Set view = database.OpenView("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductLanguage'")
view.Execute : CheckError
Dim record : Set record = view.Fetch : CheckError
If record Is Nothing Then ProductLanguage = "Not specified!" Else ProductLanguage = record.IntegerData(1)
End Function ' Get ANSI codepage of database text data
Function DatabaseCodepage(database)
On Error Resume Next
Dim WshShell : Set WshShell = Wscript.CreateObject("Wscript.Shell") : CheckError
Dim tempPath:tempPath = WshShell.ExpandEnvironmentStrings("%TEMP%") : CheckError
database.Export "_ForceCodepage", tempPath, "codepage.idt" : CheckError
Dim fileSys : Set fileSys = CreateObject("Scripting.FileSystemObject") : CheckError
Dim file : Set file = fileSys.OpenTextFile(tempPath & "\codepage.idt", ForReading, False, TristateFalse) : CheckError
file.ReadLine ' skip column name record
file.ReadLine ' skip column defn record
DatabaseCodepage = file.ReadLine
file.Close
Dim iDelim:iDelim = InStr(1, DatabaseCodepage, vbTab, vbTextCompare)
If iDelim = 0 Then Fail "Failure in codepage export file"
DatabaseCodepage = Left(DatabaseCodepage, iDelim - 1)
fileSys.DeleteFile(tempPath & "\codepage.idt")
End Function ' Set ProductLanguge property in Property table
Sub SetProductLanguage(database, language)
On Error Resume Next
If Not IsNumeric(language) Then Fail "ProductLanguage must be numeric"
Dim view : Set view = database.OpenView("SELECT `Property`,`Value` FROM `Property`")
view.Execute : CheckError
Dim record : Set record = installer.CreateRecord(2)
record.StringData(1) = "ProductLanguage"
record.StringData(2) = CStr(language)
view.Modify msiViewModifyAssign, record : CheckError
End Sub ' Set ANSI codepage of database text data
Sub SetDatabaseCodepage(database, codepage)
On Error Resume Next
If Not IsNumeric(codepage) Then Fail "Codepage must be numeric"
Dim WshShell : Set WshShell = Wscript.CreateObject("Wscript.Shell") : CheckError
Dim tempPath:tempPath = WshShell.ExpandEnvironmentStrings("%TEMP%") : CheckError
Dim fileSys : Set fileSys = CreateObject("Scripting.FileSystemObject") : CheckError
Dim file : Set file = fileSys.OpenTextFile(tempPath & "\codepage.idt", ForWriting, True, TristateFalse) : CheckError
file.WriteLine ' dummy column name record
file.WriteLine ' dummy column defn record
file.WriteLine codepage & vbTab & "_ForceCodepage"
file.Close : CheckError
database.Import tempPath, "codepage.idt" : CheckError
fileSys.DeleteFile(tempPath & "\codepage.idt")
End Sub ' Set language list in summary information
Sub SetPackageLanguage(database, language)
On Error Resume Next
Dim sumInfo : Set sumInfo = database.SummaryInformation(1) : CheckError
Dim template : template = sumInfo.Property(7) : CheckError
Dim iDelim:iDelim = InStr(1, template, ";", vbTextCompare)
Dim platform : If iDelim = 0 Then platform = ";" Else platform = Left(template, iDelim)
sumInfo.Property(7) = platform & language
sumInfo.Persist : CheckError
End Sub Sub CheckError
Dim message, errRec
If Err = 0 Then Exit Sub
message = Err.Source & " " & Hex(Err) & ": " & Err.Description
If Not installer Is Nothing Then
Set errRec = installer.LastErrorRecord
If Not errRec Is Nothing Then message = message & vbNewLine & errRec.FormatText
End If
Fail message
End Sub Sub Fail(message)
Wscript.Echo message
Wscript.Quit 2
End Sub
语法:
WiLangId.vbs "en-us\TestInstaller.msi" Package 1033,1028,2052
执行成功会出现下面的提示框:
这个时候,我们再点击英文的安装包。出现的是中文界面了。因为它已经根据我的系统环境自动做了选择。测试了繁体系统也是ok的。
4.用命令选择语言。
但是,这都还不是完整的多语言。有时候用户需要选择,比如他想再繁体系统中使用简体安装界面,而且我们的安装界面的多语言需要和我们应用程序的多语言同步起来。基于这个需求,我们还是需要在msi文件上包一个壳,先让用户去选择一门语言。那么我们怎么用命令来执行呢?
msiexec有命令:
msiexec /i en-us\DIAViewSetup.msi TRANSFORMS=transforms\zh-tw.mst
指定在简体系统中使用繁体界面:
这样在我们的C++外壳中就可以根据用户的选择也触发不同的界面。如果用户自己手动去安装,也起码可以根据他当前的系统自动切换语言。
小结:本文没有提供完整的安装包多语言解决方法。仅仅为大家提供了一种我走通了的方法,另外还有许可证书的部分,不同的语言,不同的许可证书,但路径又是不能通过loc的方式来处理的。需要自定义窗口来处理,后面有新的发现我会继续更新。希望对苦逼的WIX学习者带来帮助。
Wix 安装部署教程(十三) -- 多语言安装包的更多相关文章
- Wix 安装部署教程(十六) -- 自动生成多语言文件
因为持续集成需要,所有项目编译完之后生成一个多语言的安装包.之前生成mst文件都是手动操作,而且mst文件必须每次重新和新的安装包“关联”,否则中文的安装包去调用英文的资源的时候就会报类似于“类型转换 ...
- WIX 安装部署教程(六) 为你收集的七个知识点
前段时间整理5篇WIX(Windows Installer XML)的安装教程,但还不够完善,这里继续整理了七个知识点分享给大家.WIX最新版本3.8,点击下载 WIX安装部署(一)同MSBuild自 ...
- Wix 安装部署教程(十四) -- 多语言安装包之用户许可协议
在上一篇中,留下了许可协议的问题,目前已经解决.感谢网友武全的指点! 问题 一般我们是用WixVariable 来设定许可协议.如下所示: <WixVariable Id="WixUI ...
- Wix 安装部署教程(十五) --CustomAction的七种用法
在WIX中,CustomAction用来在安装过程中执行自定义行为.比如注册.修改文件.触发其他可执行文件等.这一节主要是介绍一下CustomAction的7种用法. 在此之前要了解InstallEx ...
- Wix 安装部署教程(九) --用WPF做安装界面
经常安装PC端的应用,特别是重装系统之后,大致分为两类.一类像QQ,搜狗输入法这样的.分三步走的:第一个页面可以自定义安装路径和软件许可.第二个页面显示安装进度条,第三个页面推荐其他应用.先不管人家怎 ...
- Wix 安装部署教程 -CustomAction的七种用法
在WIX中,CustomAction用来在安装过程中执行自定义行为.比如注册.修改文件.触发其他可执行文件等.这一节主要是介绍一下CustomAction的7种用法. 在此之前要了解InstallEx ...
- Wix 安装部署教程(十) --来,用WPF做个漂亮的安装界面
在上一篇中曾留下两个问题,.Net捆绑安装不触发以及路径选择的问题现在都已经解决,这段时间花的最多的地方还是WPF调样式上面,奈何WPF功力不够,暂时还是没有达到自己想要的效果.另外ViewModel ...
- Wix 安装部署教程(四) 添加安装文件及快捷方式
通过之前三篇的介绍,大家对wix的xml部署方式也应该有一些认识,今天天气不错,再来一发.主要介绍桌面,开始菜单,卸载等功能的如何添加.希望园友们支持! 一.如何添加文件 Demo打包程序很简单,就一 ...
- Wix 安装部署教程(十二) -- 自动更新WXS文件
上一篇分享了一个QuickWIX,用来对比两个工程前后的差异,但是这样还是很繁琐,而且昨天发现有Bug,目录对比有问题.这次改变做法,完全让程序自动去更新WXS文件,然后再用CCNet去自动编译,这样 ...
随机推荐
- cookie,session原理,以及如何使用chrome查看。
首先,先补充下chrome浏览器的使用. 1.1.php源码: <?php $cookieDomain = '.elf.com'; setcookie(, '/', $cookieDomain) ...
- qweb
qweb 是 odoo的模板系统, 在 odoo系统中, 它有不同的用途和实现, 一个是 web client 的 widget 的渲染引擎, 它是通过 javascript实现的,也是 最早引入到 ...
- Web程序的运行原理及流程(一)
自己做Web程序的开发也有两年多了 从最开始跟风学框架 到第一用上框架的欣喜若狂 我相信每个程序员都是这样过来的 在大学学习一门语言 学会后往往很想做一个实际的项目出来 我当时第一次做WEB项目看 ...
- git push --help
git-push(1) Manual Page NAME git-push - Update remote refs along with associated objects SYNOPSIS gi ...
- shell脚本编译安装LAMP环境
#filename lamp.sh#version Centos6.7;apache2.4.23;mariadb-5.5.40;php5.5.38#data 2016/09/28#mail 23853 ...
- juery与表单中name="nodeName"引起的冲突
引入jquery时,表单中如果有name="nodeName"的表单项,会有一些奇怪的冲突. 表单HTML代码如下: <form id="formAddEquipN ...
- c#对数据库访问完应关闭连接
1.对数据库的连接SqlConnection con = new SqlConnection(constr);使用完成后,应该至少应该close或dispose关闭.否则会导致数据库例如(SQl200 ...
- 总结一下使用react-native的一些入门级重要点
经过多方的协调沟通,我们决定至4.1日起,停止向新用户透出该服务:今年10.15以后,老用户也停止该服务(具体表现是网站上传APK-Android或者URL-iOS入口会下掉,已经发版的App,SDK ...
- shell 条件判断语句整理
常用系统变量 1) $0 当前程式的名称 2) $n 当前程式的第n个参数,n=1,2,…9 3) $* 当前程式的任何参数(不包括程式本身) 4) ...
- 用Ant实现Java项目的自动构建和部署
原文地址:http://tech.it168.com/j/2007-11-09/200711091344781.shtml 本文请勿转载! Ant是一个Apache基金会下的跨平台的构 ...