(5) Orchard 开发之 Localization and NullLocalizer

  大家都知道在 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 中的两句提示信息:

33

把这个 po 文件放到 :\Modules\Orchard.Blogs\App_Data\Localization\zh-CN\orchard.module.po 就行了

22    11

 

有关 po 文件的格式 和 放置的地方请参考官方网站: http://docs.orchardproject.net/Documentation/Creating-global-ready-applications

posted @ 2013-01-15 21:50  LeslieFang  阅读(916)  评论(1编辑  收藏  举报