C# WinForm 国际化/本地化/多语化的方法与各种坑
2018-04-13 / 2018-04-13
分类:

巨硬提供了完整的 WinForm 国际化方案,操作起来也比较简单(坑也比较多),本文主要记录一下国际化中会遇到的一些问题。

基本的国际化方案

基本的国际化/本地化操作我就不再赘述了,主要涉及以下3点:

  • 窗体文本、图片等内容的国际化(窗体文本)
  • 内部提示文本、时间等区域化内容的国际化(代码文本)
  • 语言的动态切换(不退出程序动态切换语言)

可供参考的文章:

坑1:图片资源文件的重复使用

一个含有图标的窗体,VS在新建新的语言的资源文件(.resx)的时候会把图标也给复制一份放在里面,然后导致最后编译出来的资源 dll 体积很大。
个人的解决方案是:

不在属性窗口配置 Icon 。图标扔到一个资源文件里统一管理,然后直接修改窗体设计文件源码 FormMain.Designer.cs ,加上一行:

1
this.Icon = global::PasteEx.Properties.Resources.ico;

删除其他语言资源文件中的 Icon 节点。直接编辑源码,VS是用对应的资源管理器打开的,不好修改。图片内容是 base64 编码的,一下就能找到

1
2
3
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
...
</data>

具体可以参考我的这个提交 e2f5f94

坑2:无法修改语言 dll 的生成路径

这是很多使用过 WinForm 国际化方案的人都会说坑爹的地方,网上有很多相关讨论。

国际化后编译生成出来的程序目录下会出现和语言区域同名文件夹,内有相关的资源 dll 。

数量一多强迫症简直不能忍啊,如图:

Steam 挂卡软件的多语目录

stackoverflow 搜索一阵以后发现的几个比较靠谱的方案。

参考:Single-assembly multi-language Windows Forms deployment (ILMerge and satellite assemblies / localization) - possible?

  1. 使用 ILMerge 或者 ILRepack 把 dll 直接嵌入到主程序 exe 中。两个我都试过,直接合并,反编译看到的内部路径是错误的,exe 无法找到相关的资源 dll 。
  2. 使用 AppDomain.CurrentDomain.AssemblyResolve 方法,在找不到 dll 的时候重新指定 dll 位置进行加载。相关解决方案 Costura
  3. 重写 System.ComponentModel.ComponentResourceManager ,随心所欲的修改生成和加载 dll 的位置与方式,但是方法过于复杂,不推荐。
  4. 放弃嵌入到 exe 中,修改 dll 的存放目录,然后增加程序集的解析目录。参考:如何控制 C# 引用 DLL 的位置

我最后无奈的使用了方法 4 ,在 App.config 添加如下内容,然后把那些多语言资源文件夹全扔到了 Language 文件夹下。好歹是清爽了点。

1
2
3
4
5
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Language" />
</assemblyBinding>
</runtime>

然而又发现如果程序运行的当前目录下 *.exe.config 不存在的话,这个配置不起效。(我 Release 新版本的时候都是删除这个文件的)

只好采用了一个过时方法来增加程序集的解析目录,在窗体载入前加上这行代码就可以了(Program.cs)

1
2
#pragma warning disable 0618
AppDomain.CurrentDomain.AppendPrivatePath("Language");