PowerToys 本地化构建产物如何打包进 MSI 安装包?

PowerToys 本地化构建产物如何打包进 MSI 安装包? PowerToys 本地化构建产物如何打包进 MSI 安装包【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToysPowerToys 的界面文案通过resx/resw资源文件和lcl翻译文件管理流水线构建时会生成各语言的资源。C# 项目构建后会产出卫星资源程序集satellite DLL形如langId\ProjName.resources.dll这些 DLL 必须被声明进 WiX MSI 项目才能随安装包分发。关键点在于本地化资源文件只存在于流水线构建环境本地机器上并不存在因此整套资源打包逻辑被IsPipeline变量门控——只有流水线构建的 MSI 才会包含本地化资源。这篇文章说明这条打包链路的工作原理以及给一个新 C# 项目把卫星 DLL 加进 MSI 的具体修改点和验证方式。本地化构建产物从哪里来流水线的 localization 步骤在解决方案构建之前运行build-localization脚本该脚本用Localization.XLoc包扫描仓库根目录下所有LocProject.json为每个启用本地化的项目生成各语言的resx文件。每个本地化项目的项目根目录都有一个LocProject.json指明英文resx源文件位置、语言集合和输出路径例如 localization 文档 给出的示例src\path是文档中的示例路径实际使用时替换为你自己项目的路径{ Projects: [ { LanguageSet: Azure_Languages, LocItems: [ { SourceFile: src\\path\\resources\\Resources.resx, CopyOption: LangIDOnName, OutputPath: src\\path\\resources } ] } ] }生成结果会放在与LocProject.json同目录的loc文件夹中按语言分目录存放lcl文件英文原文加该语言的译文。之后Build PowerToys步骤用这些本地化resx构建各模块C# 项目随之产出卫星 DLL。各技术栈的产物形态不同决定了是否需要修改 MSIC#生成独立的卫星 DLL需要显式声明进 MSI本文主线C资源被编译进 dll/exe 本体本地化不产生额外文件MSI 无需额外改动UWP/WinUI资源进resources.pri文件未本地化的项目也有该文件MSI 同样无需额外改动可用命令验证 pri 内容。IsPipeline 门控为什么本地构建的 MSI 不含本地化资源Resources.wxs 中所有资源目录和Component声明都包在预处理指令里!-- Resource directories should be added only if the installer is built on the build farm -- ?ifdef env.IsPipeline? ... ?endif?该文件顶部定义了参与本地化的语言列表LocLanguageList包含ar-SA到zh-TW共 21 种语言qps-ploc等伪本地化语言处于注释状态随后用?foreach Language ...?为每种语言生成Resource$(var.IdSafeLanguage)$(var.ParentDirectory)形式的安装目录和组件。流水线侧的开关在 release.ymlIsPipeline: 1 # The installer uses this to detect whether it should pick up localizations本地构建时这个变量未定义WiX 预处理会直接跳过整个资源块。PowerToysInstallerVNext.wixproj 的 PreBuildEvent 也做了对应处理当IsPipeline未定义时本地构建会调用publish.cmd模拟流水线的 publish 步骤文档注释说明这是对 Settings/Launcher 发布流程的本地模拟再执行generateMonacoWxs.ps1生成 Monaco 的 WXS 片段。这样做的原因在 localization 文档 中写得很明确本地化resx只存在于流水线如果没有这个检查本地构建安装包项目会因为找不到卫星 DLL 而失败。把新 C# 项目的卫星 DLL 加进 MSI按以下顺序修改文档锚点指向旧版Product.wxs第 806/845–847 行附近当前仓库中这些逻辑位于 Resources.wxs以实际文件为准1. 确认项目的本地化已启用。即上面LocProject.json一步已完成且项目会把所有resx纳入构建.NET Core 项目自动包含其余框架需加EmbeddedResource IncludeProperties\Resources.*.resx /。2. 若卫星 DLL 落盘在一个新的安装目录把该目录 Id 加入 Resources.wxs 第 12 行?foreach ParentDirectory in ...?的目录列表中这样每种语言都会为该目录生成对应的Resource...子目录。3. 在ResourcesComponentGroup的?ifdef env.IsPipeline?块内为项目添加Component。localization 文档 给出的最小形式如下ProjName、ProjNameInstallFolder替换为你的项目名和目标目录 IdComponent IdProjName_$(var.IdSafeLanguage)_Component DirectoryResource$(var.IdSafeLanguage)ProjNameInstallFolder File IdProjName_$(var.IdSafeLanguage)_File Source$(var.BinX64Dir)modules\ProjName\$(var.Language)\ProjName.resources.dll / /Component对照仓库中已有的组件以 Awake 为例见 Resources.wxs完整写法还应包含两点稳定的 Component Guid每种语言在 foreach 开头定义一个CompGUIDPrefix每个组件在该前缀后追加 2 位十六进制后缀00、01、03……作为 GUID。新增组件时选一个该语言下未被占用的后缀KeyPath 注册表项现有组件均声明Software\Classes\powertoys\components下的RegistryValueKeyPathyes与文档的最小示例不同实际文件遵循这一模式Component IdAwake_$(var.IdSafeLanguage)_Component DirectoryResource$(var.IdSafeLanguage)INSTALLFOLDER Guid$(var.CompGUIDPrefix)1F RegistryKey Root$(var.RegistryScope) KeySoftware\Classes\powertoys\components RegistryValue Typestring NameAwake_$(var.IdSafeLanguage)_Component Value KeyPathyes / /RegistryKey File IdAwake_$(var.IdSafeLanguage)_File Source$(var.BinDir)\$(var.Language)\PowerToys.Awake.resources.dll / /Component4. 确认签名覆盖。流水线会对所有*.resources.dll签名新增的卫星 DLL 按命名规则自动纳入无需额外配置。5. 注意提交顺序。文档明确资源 DLL 必须等本地化团队完成 lcl 文件的初始提交后再加进 MSI 项目否则流水线上没有resx可用来生成 DLL构建会失败。C 与 UWP 项目的对应做法C不支持resx项目用rcresource.h管理字符串。做法是把可本地化的字符串表挪到resx中原文件重命名为ProjName.base.rc/resource.base.h并在 vcxproj 中设为不参与构建如None IncludeResources.resx /然后在 vcxproj 加构建事件在PrepareForBuild前用 convert-resx-to-rc.ps1 重新生成resource.h和ProjName.rc文档中的示例ProjName替换为实际项目名Target NameGenerateResourceFiles BeforeTargetsPrepareForBuild Exec LogStandardErrorAsErrorfalse Commandpowershell -NonInteractive -executionpolicy Unrestricted -NoProfile $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h ProjName.base.rc ProjName.rc / /Target生成的文件位于Generated Files目录引用resource.h时路径要写成Generated Files\resource.h。已有rc文件想转resx的可先用 convert-stringtable-to-resx.ps1 转换输入需满足IDS_ResName LResourceValue的格式。资源编译进二进制后MSI 侧不需要为 C 项目追加任何组件。UWP/WinUI项目使用resw把 csproj 中的PRIResource IncludeStrings\en-us\Resources.resw /改为通配PRIResource IncludeStrings\*\Resources.resw /构建与验证构建安装包详见 installer 文档一次性脚本打开Developer PowerShell for VS在仓库根目录运行tools\build\build-installer.ps1。首次运行请以管理员身份执行文档说明这是为了让 WiX 工具能移动wix.target并信任用于签名 MSIX 包的证书。手动构建PowerToys 使用 WiX v50.94 版本起从 WiX v3 迁移v3 工具链已不需要工具由构建过程经 dotnet tool 自动安装手动安装命令为dotnet tool install --global wix --version 5.0.2然后打开installer\PowerToysSetup.slnx选择Release构建文档说明只能以 Release 模式构建安装包产物在installer\PowerToysSetupVNext\x64\Release\或从管理员模式的Developer Command Prompt for VS执行git clean -xfd -e *exe -- .\installer\ MSBuild -t:restore .\installer\PowerToysSetup.slnx -p:RestorePackagesConfigtrue /p:Platformx64 /p:ConfigurationRelease MSBuild -t:Restore -m .\installer\PowerToysSetup.slnx /t:PowerToysInstallerVNext /p:ConfigurationRelease /p:Platformx64 MSBuild -t:Restore -m .\installer\PowerToysSetup.slnx /t:PowerToysBootstrapperVNext /p:ConfigurationRelease /p:Platformx64第一条git clean -xfd会删除installer\目录下的未跟踪文件保留 exe运行前确认该目录没有需要保留的本地修改产物。命令行方式生成的安装包位于\installer\PowerToysSetupVNext\{platform}\Release\MachineSetup。验证结果时的判断标准本地构建成功本身就是对 WXS 修改的语法验证但由于本地IsPipeline未定义本地 MSI 按设计不包含卫星 DLL 和资源目录——这是预期行为不是打包失败。含本地化资源的安装包只能由流水线产出IsPipeline: 1。UWP/WinUI 模块可在resources.pri所在目录用makepri.exe dump /if .\resources.pri导出resources.pri.xml检查末尾是否包含各语言的候选值。文档给出的是一个示例结果注意这是文档示例不是固定预期输出NamedResource nameGeneralSettings_RunningAsAdminText urims-resource://f4f787a5-f0ae-47a9-be89-5408b1dd2b47/Resources/GeneralSettings_RunningAsAdminText Candidate qualifiersLanguage-FR typeString ValueRunning as administrator/Value /Candidate Candidate qualifiersLanguage-EN-US isDefaulttrue typeString ValueRunning as administrator/Value /Candidate /NamedResource已知限制语言硬编码convert-resx-to-rc.ps1中各语言的AFX_TARG_*/LANG_*/SUBLANG_*值是写死的脚本第 50–77 行未来新增语言必须同步更新该脚本可用 Resource View 右键字符串表选Insert Copy自动生成代码来查语言代码。lcl 文件的防护CDPX 系统对 lcl 有 fail-safe 检查——若Val中的英文原文与英文Resources.resx不一致译文不会被拷贝进本地化resx改用英文原文避免加载过期翻译。LEGO 翻译 PR 冲突多个翻译 PR 可能改同一批 lcl 文件偶发坏合并会导致同一资源出现两个Tgt元素文档建议每个 LEGO PR 都跑构建农场本地化步骤报错时检查对应 resx/lcl。C# 构建告警带本地化资源构建时可能出现Referenced assembly mscorlib.dll targets a different processor警告文档标注这是 Visual Studio 的 bug不影响产物。卸载残留目录Resources.wxs 末尾的RemoveResourcesFolder组件为每种语言声明了Onuninstall的RemoveFolder用于卸载时清理各语言资源目录新增安装目录时同样需要考虑是否补对应的清理项。完成上述修改后下一步就是提 PR 跑流水线Build PowerToys步骤会用生成的本地化resx产出卫星 DLL安装器构建在IsPipeline: 1下把它们打进 MSI并进入签名环节。【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考