zoukankan      html  css  js  c++  java
  • ABP中的本地化处理(下)

     在上篇文章中我们的重点是讲述怎样通过在Domain层通过PreInitialize()配置ILocalizationConfiguration中的Sources(IList<ILocalizationSource>)集合,并且在调用L方法中配置LocalizationSourceName从而找到之前配置的ILocalizationSource对象,这一节主要来讲述ILocalizationSource中的GetString(string name)方法,通过这个方法来体会ABP中的设计思想。

      还是和之前一样我们首先来看看ILocalizationSource这个接口的定义。

        /// <summary>
        /// A Localization Source is used to obtain localized strings.
        /// </summary>
        public interface ILocalizationSource
        {
            /// <summary>
            /// Unique Name of the source.
            /// </summary>
            string Name { get; }
    
            /// <summary>
            /// This method is called by ABP before first usage.
            /// </summary>
            void Initialize(ILocalizationConfiguration configuration, IIocResolver iocResolver);
    
            /// <summary>
            /// Gets localized string for given name in current language.
            /// Fallbacks to default language if not found in current culture.
            /// </summary>
            /// <param name="name">Key name</param>
            /// <returns>Localized string</returns>
            string GetString(string name);
    
            /// <summary>
            /// Gets localized string for given name and specified culture.
            /// Fallbacks to default language if not found in given culture.
            /// </summary>
            /// <param name="name">Key name</param>
            /// <param name="culture">culture information</param>
            /// <returns>Localized string</returns>
            string GetString(string name, CultureInfo culture);
    
            /// <summary>
            /// Gets localized string for given name in current language.
            /// Returns null if not found.
            /// </summary>
            /// <param name="name">Key name</param>
            /// <param name="tryDefaults">
            /// True: Fallbacks to default language if not found in current culture.
            /// </param>
            /// <returns>Localized string</returns>
            string GetStringOrNull(string name, bool tryDefaults = true);
    
            /// <summary>
            /// Gets localized string for given name and specified culture.
            /// Returns null if not found.
            /// </summary>
            /// <param name="name">Key name</param>
            /// <param name="culture">culture information</param>
            /// <param name="tryDefaults">
            /// True: Fallbacks to default language if not found in current culture.
            /// </param>
            /// <returns>Localized string</returns>
            string GetStringOrNull(string name, CultureInfo culture, bool tryDefaults = true);
    
            /// <summary>
            /// Gets all strings in current language.
            /// </summary>
            /// <param name="includeDefaults">
            /// True: Fallbacks to default language texts if not found in current culture.
            /// </param>
            IReadOnlyList<LocalizedString> GetAllStrings(bool includeDefaults = true);
    
            /// <summary>
            /// Gets all strings in specified culture.
            /// </summary>
            /// <param name="culture">culture information</param>
            /// <param name="includeDefaults">
            /// True: Fallbacks to default language texts if not found in current culture.
            /// </param>
            IReadOnlyList<LocalizedString> GetAllStrings(CultureInfo culture, bool includeDefaults = true);
        }
    

      这个接口默认有四个实现:DictionaryBasedLocalizationSource、MultiTenantLocalizationSource、ResourceFileLocalizationSource、NullLocalizationSource这里我们重点来将我们项目中用到的DictionaryBasedLocalizationSource,这个我们也先来看看源码,热庵后再来做进一步的分析。

    /// <summary>
        /// This class is used to build a localization source
        /// which works on memory based dictionaries to find strings.
        /// </summary>
        public class DictionaryBasedLocalizationSource : IDictionaryBasedLocalizationSource
        {
            /// <summary>
            /// Unique Name of the source.
            /// </summary>
            public string Name { get; }
    
            public ILocalizationDictionaryProvider DictionaryProvider { get; }
    
            protected ILocalizationConfiguration LocalizationConfiguration { get; private set; }
    
            private ILogger _logger;
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="name"></param>
            /// <param name="dictionaryProvider"></param>
            public DictionaryBasedLocalizationSource(string name, ILocalizationDictionaryProvider dictionaryProvider)
            {
                Check.NotNullOrEmpty(name, nameof(name));
                Check.NotNull(dictionaryProvider, nameof(dictionaryProvider));
    
                Name = name;
                DictionaryProvider = dictionaryProvider;
            }
    
            /// <inheritdoc/>
            public virtual void Initialize(ILocalizationConfiguration configuration, IIocResolver iocResolver)
            {
                LocalizationConfiguration = configuration;
    
                _logger = iocResolver.IsRegistered(typeof(ILoggerFactory))
                    ? iocResolver.Resolve<ILoggerFactory>().Create(typeof(DictionaryBasedLocalizationSource))
                    : NullLogger.Instance;
    
                DictionaryProvider.Initialize(Name);
            }
    
            /// <inheritdoc/>
            public string GetString(string name)
            {
                return GetString(name, CultureInfo.CurrentUICulture);
            }
    
            /// <inheritdoc/>
            public string GetString(string name, CultureInfo culture)
            {
                var value = GetStringOrNull(name, culture);
    
                if (value == null)
                {
                    return ReturnGivenNameOrThrowException(name, culture);
                }
    
                return value;
            }
    
            public string GetStringOrNull(string name, bool tryDefaults = true)
            {
                return GetStringOrNull(name, CultureInfo.CurrentUICulture, tryDefaults);
            }
    
            public string GetStringOrNull(string name, CultureInfo culture, bool tryDefaults = true)
            {
                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;
                    }
                }
    
                if (!tryDefaults)
                {
                    return null;
                }
    
                //Try to get from same language dictionary (without country code)
                if (cultureName.Contains("-")) //Example: "tr-TR" (length=5)
                {
                    ILocalizationDictionary langDictionary;
                    if (dictionaries.TryGetValue(GetBaseCultureName(cultureName), out langDictionary))
                    {
                        var strLang = langDictionary.GetOrNull(name);
                        if (strLang != null)
                        {
                            return strLang.Value;
                        }
                    }
                }
    
                //Try to get from default language
                var defaultDictionary = DictionaryProvider.DefaultDictionary;
                if (defaultDictionary == null)
                {
                    return null;
                }
    
                var strDefault = defaultDictionary.GetOrNull(name);
                if (strDefault == null)
                {
                    return null;
                }
    
                return strDefault.Value;
            }
    
            /// <inheritdoc/>
            public IReadOnlyList<LocalizedString> GetAllStrings(bool includeDefaults = true)
            {
                return GetAllStrings(CultureInfo.CurrentUICulture, includeDefaults);
            }
    
            /// <inheritdoc/>
            public IReadOnlyList<LocalizedString> GetAllStrings(CultureInfo culture, bool includeDefaults = true)
            {
                //TODO: Can be optimized (example: if it's already default dictionary, skip overriding)
    
                var dictionaries = DictionaryProvider.Dictionaries;
    
                //Create a temp dictionary to build
                var allStrings = new Dictionary<string, LocalizedString>();
    
                if (includeDefaults)
                {
                    //Fill all strings from default dictionary
                    var defaultDictionary = DictionaryProvider.DefaultDictionary;
                    if (defaultDictionary != null)
                    {
                        foreach (var defaultDictString in defaultDictionary.GetAllStrings())
                        {
                            allStrings[defaultDictString.Name] = defaultDictString;
                        }
                    }
    
                    //Overwrite all strings from the language based on country culture
                    if (culture.Name.Contains("-"))
                    {
                        ILocalizationDictionary langDictionary;
                        if (dictionaries.TryGetValue(GetBaseCultureName(culture.Name), out langDictionary))
                        {
                            foreach (var langString in langDictionary.GetAllStrings())
                            {
                                allStrings[langString.Name] = langString;
                            }
                        }
                    }
                }
    
                //Overwrite all strings from the original dictionary
                ILocalizationDictionary originalDictionary;
                if (dictionaries.TryGetValue(culture.Name, out originalDictionary))
                {
                    foreach (var originalLangString in originalDictionary.GetAllStrings())
                    {
                        allStrings[originalLangString.Name] = originalLangString;
                    }
                }
    
                return allStrings.Values.ToImmutableList();
            }
    
            /// <summary>
            /// Extends the source with given dictionary.
            /// </summary>
            /// <param name="dictionary">Dictionary to extend the source</param>
            public virtual void Extend(ILocalizationDictionary dictionary)
            {
                DictionaryProvider.Extend(dictionary);
            }
    
            protected virtual string ReturnGivenNameOrThrowException(string name, CultureInfo culture)
            {
                return LocalizationSourceHelper.ReturnGivenNameOrThrowException(
                    LocalizationConfiguration,
                    Name,
                    name,
                    culture,
                    _logger
                );
            }
    
            private static string GetBaseCultureName(string cultureName)
            {
                return cultureName.Contains("-")
                    ? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal))
                    : cultureName;
            }
        }
    

      在分析这个类之前,我们先来看看这个类的构造函数,分别传入了一个string类型的name和ILocalizationDictionaryProvider对象,这个name按照ABP的注释表示的是资源的名称,这个是不能够重名的,另外一个参数是ILocalizationDictionaryProvider一个对象,这个对象用于处理本地化文件是XML格式还是Json格式,每种格式都会实现这个接口,然后分别处理对应格式的数据,这个接口稍后会进行分析。

      在这个类构造完成以后会调用其中的Initialize(ILocalizationConfiguration configuration, IIocResolver iocResolver)方法,用于向当前对象中传递本地化配置信息以及IIocResolver对象。并在这里面调用ILocalizationDictionaryProvider的Initialize方法。那么我们就来具体分析一下这个ILocalizationDictionaryProvider接口吧。

      首先看接口定义

    /// <summary>
        /// Used to get localization dictionaries (<see cref="ILocalizationDictionary"/>)
        /// for a <see cref="IDictionaryBasedLocalizationSource"/>.
        /// </summary>
        public interface ILocalizationDictionaryProvider
        {
            ILocalizationDictionary DefaultDictionary { get; }
    
            IDictionary<string, ILocalizationDictionary> Dictionaries { get; }
    
            void Initialize(string sourceName);
            
            void Extend(ILocalizationDictionary dictionary);
        }
    

      这个里面又定义了一个ILocalizationDictionary的接口,我们先来看看在我们的项目中添加的JsonEmbeddedFileLocalizationDictionaryProvider这个类里面的实现,这个类首先继承自一个LocalizationDictionaryProviderBase的抽象基类。

     public abstract class LocalizationDictionaryProviderBase : ILocalizationDictionaryProvider
        {
            public string SourceName { get; private set; }
    
            public ILocalizationDictionary DefaultDictionary { get; protected set; }
    
            public IDictionary<string, ILocalizationDictionary> Dictionaries { get; private set; }
    
            protected LocalizationDictionaryProviderBase()
            {
                Dictionaries = new Dictionary<string, ILocalizationDictionary>();
            }
    
            public virtual void Initialize(string sourceName)
            {
                SourceName = sourceName;
            }
    
            public void Extend(ILocalizationDictionary dictionary)
            {
                //Add
                ILocalizationDictionary existingDictionary;
                if (!Dictionaries.TryGetValue(dictionary.CultureInfo.Name, out existingDictionary))
                {
                    Dictionaries[dictionary.CultureInfo.Name] = dictionary;
                    return;
                }
    
                //Override
                var localizedStrings = dictionary.GetAllStrings();
                foreach (var localizedString in localizedStrings)
                {
                    existingDictionary[localizedString.Name] = localizedString.Value;
                }
            }
        }
    

      这个抽象基类里面主要定义一些常用的公共的属性以及一个公用的Extend方法。

    /// <summary>
        /// Provides localization dictionaries from JSON files embedded into an <see cref="Assembly"/>.
        /// </summary>
        public class JsonEmbeddedFileLocalizationDictionaryProvider : LocalizationDictionaryProviderBase
        {
            private readonly Assembly _assembly;
            private readonly string _rootNamespace;
    
            /// <summary>
            /// Creates a new <see cref="JsonEmbeddedFileLocalizationDictionaryProvider"/> object.
            /// </summary>
            /// <param name="assembly">Assembly that contains embedded json files</param>
            /// <param name="rootNamespace">
            /// <para>
            /// Namespace of the embedded json dictionary files
            /// </para>
            /// <para>
            /// Notice : Json folder name is different from Xml folder name.
            /// </para>
            /// <para>
            /// You must name it like this : Json**** and Xml****; Do not name : ****Json and ****Xml
            /// </para>
            /// </param>
            public JsonEmbeddedFileLocalizationDictionaryProvider(Assembly assembly, string rootNamespace)
            {
                _assembly = assembly;
                _rootNamespace = rootNamespace;
            }
    
            public override void Initialize(string sourceName)
            {
                var allCultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures);
                var resourceNames = _assembly.GetManifestResourceNames().Where(resouceName =>
                    allCultureInfos.Any(culture => resouceName.EndsWith($"{sourceName}.json", true, null) ||
                                                   resouceName.EndsWith($"{sourceName}-{culture.Name}.json", true,
                                                       null))).ToList();
                foreach (var resourceName in resourceNames)
                {
                    if (resourceName.StartsWith(_rootNamespace))
                    {
                        using (var stream = _assembly.GetManifestResourceStream(resourceName))
                        {
                            var jsonString = Utf8Helper.ReadStringFromStream(stream);
    
                            var dictionary = CreateJsonLocalizationDictionary(jsonString);
                            if (Dictionaries.ContainsKey(dictionary.CultureInfo.Name))
                            {
                                throw new AbpInitializationException(sourceName + " source contains more than one dictionary for the culture: " + dictionary.CultureInfo.Name);
                            }
    
                            Dictionaries[dictionary.CultureInfo.Name] = dictionary;
    
                            if (resourceName.EndsWith(sourceName + ".json"))
                            {
                                if (DefaultDictionary != null)
                                {
                                    throw new AbpInitializationException("Only one default localization dictionary can be for source: " + sourceName);
                                }
    
                                DefaultDictionary = dictionary;
                            }
                        }
                    }
                }
            }
    
            protected virtual JsonLocalizationDictionary CreateJsonLocalizationDictionary(string jsonString)
            {
                return JsonLocalizationDictionary.BuildFromJsonString(jsonString);
            }
        }  

      这个类的构造函数传入的是:Assembly assembly, string rootNamespace这个我们看命名就应该知道,第一个参数指的是包含嵌入式json file的程序集,第二个代表当前根命名空间。在这个类里面重点是Initialize方法,这个方法会循环遍历所有json格式的本地化的文件,然后读取每一个Json文件中定义的JsonString,然后通过JsonLocalizationDictionary类中的静态方法将JsonString反序列化为一个JsonLocalizationFile对象,我们也来看看这段代码的实现。

    /// <summary>
        ///     This class is used to build a localization dictionary from json.
        /// </summary>
        /// <remarks>
        ///     Use static Build methods to create instance of this class.
        /// </remarks>
        public class JsonLocalizationDictionary : LocalizationDictionary
        {
            /// <summary>
            ///     Private constructor.
            /// </summary>
            /// <param name="cultureInfo">Culture of the dictionary</param>
            private JsonLocalizationDictionary(CultureInfo cultureInfo)
                : base(cultureInfo)
            {
            }
    
            /// <summary>
            ///     Builds an <see cref="JsonLocalizationDictionary" /> from given file.
            /// </summary>
            /// <param name="filePath">Path of the file</param>
            public static JsonLocalizationDictionary BuildFromFile(string filePath)
            {
                try
                {
                    return BuildFromJsonString(File.ReadAllText(filePath));
                }
                catch (Exception ex)
                {
                    throw new AbpException("Invalid localization file format! " + filePath, ex);
                }
            }
    
            /// <summary>
            ///     Builds an <see cref="JsonLocalizationDictionary" /> from given json string.
            /// </summary>
            /// <param name="jsonString">Json string</param>
            public static JsonLocalizationDictionary BuildFromJsonString(string jsonString)
            {
                JsonLocalizationFile jsonFile;
                try
                {
                    jsonFile = JsonConvert.DeserializeObject<JsonLocalizationFile>(
                        jsonString,
                        new JsonSerializerSettings
                        {
                            ContractResolver = new CamelCasePropertyNamesContractResolver()
                        });
                }
                catch (JsonException ex)
                {
                    throw new AbpException("Can not parse json string. " + ex.Message);
                }
    
                var cultureCode = jsonFile.Culture;
                if (string.IsNullOrEmpty(cultureCode))
                {
                    throw new AbpException("Culture is empty in language json file.");
                }
    
                var dictionary = new JsonLocalizationDictionary(CultureInfo.GetCultureInfo(cultureCode));
                var dublicateNames = new List<string>();
                foreach (var item in jsonFile.Texts)
                {
                    if (string.IsNullOrEmpty(item.Key))
                    {
                        throw new AbpException("The key is empty in given json string.");
                    }
    
                    if (dictionary.Contains(item.Key))
                    {
                        dublicateNames.Add(item.Key);
                    }
    
                    dictionary[item.Key] = item.Value.NormalizeLineEndings();
                }
    
                if (dublicateNames.Count > 0)
                {
                    throw new AbpException(
                        "A dictionary can not contain same key twice. There are some duplicated names: " +
                        dublicateNames.JoinAsString(", "));
                }
    
                return dictionary;
            }
        }
    

      当然这里面也会做一些类似于Key不能重复等一系列的操作,具体的实现我们来看代码。到这里为止我们已经明白了到底通过怎样的方式来获取到Json文件中的所有的信息并最终保存在JsonLocalizationDictionary,有了这个Dictionary我们就能够顺理成章的通过Key值来获取对应的value了。

      最后,点击这里返回整个ABP系列的主目录。

  • 相关阅读:
    复选框与全选框的选中状态的联动
    用localStorage在页面间传值
    移动端页面输入法挡住input输入框的解决方法
    input[type=file]上传图片及转为base64码以及预览
    截取url参数
    图片加载完成再执行事件
    每天记录一点点
    图片懒加载
    @ font-face 引入本地字体文件
    vue 仿zTree折叠树
  • 原文地址:https://www.cnblogs.com/seekdream/p/10945084.html
Copyright © 2011-2022 走看看