.net core在Linux本地化Localization的一次填坑
使用ABP框架开发.net core程序已经有一段时间了,因为之前部署在windows服务器上,使用一直很正常。自从前段时间切换服务器上了Linux的Centos服务器,发现之前中文的语言变成了英文,一直找不到原因。
因为之前windows是好的,所以肯定是环境问题,最直接的就是Linux本地化引起的。如果对Linux本地化命令不了解的,可以看一下这里。
果然是en_US,尝试切换使用 LANG=zh_CN
成功了,这里得要全局设置使用 export LANG=zh_CN ,否则其他session 不会生效。现在好了,从新访问,但还是不行。既然Linux系统的本地化已经设置好了,但程序还是不行,只能从代码入手。
在 Abp.Localization.Dictionaries.DictionaryBasedLocalizationSource 的 GetStringOrNull 中发现,
var cultureName = culture.Name; var dictionaries = DictionaryProvider.Dictionaries; //Try to get from original dictionary (with country code) ILocalizationDictionary originalDictionary; if (dictionaries.TryGetValue(cultureName, out originalDictionary)) { var strOriginal = originalDictionary.GetOrNull(name); if (strOriginal != null) { return strOriginal.Value; } }
它是通过DictionaryProvider.Dictionaries 的字典类型,找到我们预定的字体语言集
但是,通过
ILocalizationDictionaryProvider DictionaryProvider = ((IDictionaryBasedLocalizationSource)LocalizationManager.GetSource(CmsIdentityConsts.LocalizationSourceName)).DictionaryProvider;
发现DictionaryProvider.Dictionaries 只有一个en的类型,也就是我们默认第一个的英文字体包,为什么没有了我们定义的中文和繁体包了呢?
通过追查代码在Abp.Localization.Dictionaries.Xml.XmlEmbeddedFileLocalizationDictionaryProvider 的 Initialize 中发现,它的代码如下:
var allCultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures);
var resourceNames = _assembly.GetManifestResourceNames().Where(resouceName =>
allCultureInfos.Any(culture => resouceName.EndsWith($"{sourceName}.xml", true, null) ||
resouceName.EndsWith($"{sourceName}-{culture.Name}.xml", true,
null))).ToList();
主要是这个CultureInfo.GetCultures(CultureTypes.AllCultures) 函数,它的结果发现如下:
截取部分,发现并没有zh-CN,取而代之的是zh-Hans-CN。而我们判断当前线程是什么语言,使用的是Thread.CurrentThread.CurrentUICulture 或者 CultureInfo.CurrentUICulture 或者 CultureInfo.CurrentCulture 这三个函数,分别获得的结果是
这两个对不上,是不是很奇怪,为什么CultureInfo.GetCultures(CultureTypes.AllCultures) 和CultureInfo.CurrentCulture 这两个结果尽然不一致。那为什么我们在windows时候是好的呢,再在windows下执行如下:
发现原来windows是有的,linux就没有了。这就解释了为什么windows是好的,而Linux则出问题。网上查了好多资料,说是zh-CN过时了,不推荐,新的标准的zh-Hans-CN,反正都是没什么用的。
通过代码发现它是通过拼接,得到我们定义的语言集的 {sourceName}-{culture.Name}.xml , 那我们不是改以下文件的命名就可以了,说干就干。
果然,现在加载的时候能够获得三个语言包了。但是zh-Hans-CN和zh-CN的名字对不上怎么办?尝试设置线程
var currentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("zh-Hans-CN"); Thread.CurrentThread.CurrentCulture = currentCulture; Thread.CurrentThread.CurrentUICulture = currentCulture.GetConsoleFallbackUICulture();
很不幸,三个函数中只有最后一个才能获得:
上ABP官网论坛和网上找了半天,别人都没有说这里有问题,摒着不改源代码,相信广大码农的原则下,自我排查,是不是还有别的方法。灵感一现,文件名对应着语言的包名,XML里面不是还定义了一个参数么,把它改成zh-CN会不会好使:
再访问网站
结果,哈哈成功了!证明它通过当前线程查找的语言包是和内部XML定义的一致,完美解决!
Docker部署
说白了,在linux下运行,那Docker是必不可少的。但是直接运行在ubuntu 上面的镜像是没有语言包的。
sudo docker run -i -t <id> /bin/bash root@<id>:/# locale LANG= LANGUAGE= LC_CTYPE="POSIX" LC_NUMERIC="POSIX" LC_TIME="POSIX" LC_COLLATE="POSIX" LC_MONETARY="POSIX" LC_MESSAGES="POSIX" LC_PAPER="POSIX" LC_NAME="POSIX" LC_ADDRESS="POSIX" LC_TELEPHONE="POSIX" LC_MEASUREMENT="POSIX" LC_IDENTIFICATION="POSIX" LC_ALL=
或者
root@4acd55cd86a7:/# locale -a C C.UTF-8 POSIX
显然是没有语言包。需要重构建镜像入手Dockerfile文件如下
FROM ubuntu:trusty RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 CMD ["/bin/bash"]
这样就可以了,但是如果直接在ubuntu 上构建不行
需要如下处理,简单说就是安装语言包locales 命令
FROM ubuntu RUN apt-get update RUN apt-get install -y locales locales-all ENV LC_ALL en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US.UTF-8
大功告成!