大家都知道在 orchard 中做国际化的时候,直接在代码或 view 中用 T 包上要进行国际化得字符串就行了,
之后 orchard 就会根据你设置得语言文化,寻找到相应得 po 文件来进行 localization。
在 Controller 中你通常定义一个属性,public Localizer T { get; set; } 这里 T 是一个委托。
之后在构造函数中注入一个 NullLocalizer.Instance ,但当你打开 NullLocalizer 时,你发现这个类并没有做什么具体的工作,那国际化究竟是怎么做的呢?
事实上你删掉构造函数中的 T = NullLocalizer.Instance; 不会有任何问题,我也不知道 NullLocalizer 有什么用处??
public static class NullLocalizer { static NullLocalizer () { _instance = (format, args) => new LocalizedString((args == null || args.Length == 0) ? format : string.Format(format, args)); } static readonly Localizer _instance; public static Localizer Instance { get { return _instance; } } }
下面让我们看看 orchard 中国际化是怎么做的:
具体的类都定义在 Orchard.Framework\Localization\ 下面
首先来看 LocalizationModule 类:
protected override void Load(ContainerBuilder builder) { builder.RegisterType<Text>().As<IText>().InstancePerDependency(); } protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) { // 通过反射 ,查看你是否定义个一个 public 的 ,名字叫 T 的 ,类型为 Localizer 的委托
// 所以 T 必须是 public 的 名字也不能随便变
var userProperty = FindUserProperty(registration.Activator.LimitType); if (userProperty != null) { var scope = registration.Activator.LimitType.FullName; registration.Activated += (sender, e) => { // 这里给 registration 这个事件注册了很多个方法 var localizer = _localizerCache.GetOrAdd(scope, key => LocalizationUtilities.Resolve(e.Context, scope)); // 得到一个 Localizer 对象 userProperty.SetValue(e.Instance, localizer, null); // 把得到的 Localizer 对象赋给 T ,实际传入的是 Text 类中 Get 方法 // 实际上 AdminMenu 的 T 也是在这里绑定的, 当你需要一个 Localizer 的实例时这个事件就会被触发,给你的 T 绑定一个方法 (Get) }; } } private static PropertyInfo FindUserProperty(Type type) { return type.GetProperty("T", typeof(Localizer)); }
下面我们来看看 Text 中的 Get 方法,这才是 T 实际绑定的方法
public LocalizedString Get(string textHint, params object[] args) { // textHint 就是 T 包裹的字符串 Logger.Debug("{0} localizing '{1}'", _scope, textHint); var workContext = _workContextAccessor.GetContext(); var currentCulture = workContext.CurrentCulture; // 得到当前的语言文化,第一次会从数据库中的 Settings_SiteSettingsPartRecord 这个表中读取,随会缓存起来
// 调用 DefaultLocalizedStringManager 中的 GetLocalizedString 方法返回一个本地化的字符串
var localizedFormat = _localizedStringManager.GetLocalizedString(_scope, textHint, currentCulture); return args.Length == 0 ? new LocalizedString(localizedFormat, _scope, textHint, args)// 把返回的字符串包装成 LocalizedString 对象,以便显示在页面上 : new LocalizedString(string.Format(GetFormatProvider(currentCulture), localizedFormat, args), _scope, textHint, args);
}
下面我们来看 DefaultLocalizedStringManager 中的 GetLocalizedString 方法
public string GetLocalizedString(string scope, string text, string cultureName) { var culture = LoadCulture(cultureName); // 得到当前语言文化 string scopedKey = (scope + "|" + text).ToLowerInvariant(); if (culture.Translations.ContainsKey(scopedKey)) { return culture.Translations[scopedKey]; // 如果当前 scope 中有 po 文件,直接取出翻译后的字符串 } string genericKey = ("|" + text).ToLowerInvariant(); if (culture.Translations.ContainsKey(genericKey)) { return culture.Translations[genericKey]; // 当前工程中找不到 po 文件,再找其他地方,找到则取出翻译后的字符串 } return GetParentTranslation(scope, text, cultureName); }
orchard 中有一系列的查找路径去找到相应的 po 文件:
const string CoreLocalizationFilePathFormat = "~/Core/App_Data/Localization/{0}/orchard.core.po"; const string ModulesLocalizationFilePathFormat = "~/Modules/{0}/App_Data/Localization/{1}/orchard.module.po"; const string ThemesLocalizationFilePathFormat = "~/Themes/{0}/App_Data/Localization/{1}/orchard.theme.po"; const string RootLocalizationFilePathFormat = "~/App_Data/Localization/{0}/orchard.root.po"; const string TenantLocalizationFilePathFormat = "~/App_Data/Sites/{0}/Localization/{1}/orchard.po";
而且 orchard 中做国际化也非常方便:
1) 在要需要国际化的类中定义一个叫 T 的委托 public Localizer T { get; set; } (view 中直接用 T 包裹就行)
2) 用 T 包裹需要本地化的字符串。
3) 准备好 po 文件,按照指定的格式书写,放到指定的位置,orchard 就能找到并取出了。 po 文件必须是 utf-8 编码,否则会出现乱码,orchard 会用 utf-8 来解析 po 文件
下面我简单翻译 BlogPostAdminController 中的两句提示信息:
把这个 po 文件放到 :\Modules\Orchard.Blogs\App_Data\Localization\zh-CN\orchard.module.po 就行了
有关 po 文件的格式 和 放置的地方请参考官方网站: http://docs.orchardproject.net/Documentation/Creating-global-ready-applications