.net core webapi实现本地化

        现在项目中经常会要求支持本地化配置,实现本地化配置的方法有很多,例如微软官网提供了通过resx资源文件和PO文件实现本地化配置。但resx资源文件可扩展性不好,PO文件需要引入依赖包,本文主要介绍通过另一种可扩展的方式实现本地化。

        .net core内置了本地化配置接口IStringLocalizer,接口具体方法如下:

public interface IStringLocalizer
{
    /// <summary>
    /// Gets the string resource with the given name.
    /// </summary>
    /// <param name="name">The name of the string resource.</param>
    /// <returns>The string resource as a <see cref="LocalizedString"/>.</returns>
    LocalizedString this[string name] { get; }

    /// <summary>
    /// Gets the string resource with the given name and formatted with the supplied arguments.
    /// </summary>
    /// <param name="name">The name of the string resource.</param>
    /// <param name="arguments">The values to format the string with.</param>
    /// <returns>The formatted string resource as a <see cref="LocalizedString"/>.</returns>
    LocalizedString this[string name, params object[] arguments] { get; }

    /// <summary>
    /// Gets all string resources.
    /// </summary>
    /// <param name="includeParentCultures">
    /// A <see cref="System.Boolean"/> indicating whether to include strings from parent cultures.
    /// </param>
    /// <returns>The strings.</returns>
    IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures);
}

        this[string name]索引器提供通过名称获取本地化配置文本;

        this[string name, params object[] arguments]增加了参数arguments,用来做字符串占位符;

        GetAllStrings(bool includeParentCultures)获取所有配置文本。

        本地化配置修改好后,很少会修改。基于这个特点这里选择通过xml文件配置本地化语言,xml文件可扩展性较好,而且很方便后期在项目中扩展。实现主要包括两个对象:XmlLocalizerResource(xml资源对象)和XmlLocalizer(xml序列化对象)。

        XmlLocalizerResource通过加载指定目录下的所有xml文件,反序列到懒加载字典对象Lazy<IDictionary<string, IDictionary<string, string>>>中,主要代码如下:

using MyWebDemo.Localizer;
using MyWebDemo.Utils;

namespace MyWebDemo
{
	public class XmlLocalizerResource
	{
		private readonly IHostEnvironment _environment;
		private readonly Lazy<IDictionary<string, IDictionary<string, string>>> _respurces;

		public XmlLocalizerResource(IHostEnvironment environment)
		{
			_environment = environment;
			_respurces = new Lazy<IDictionary<string, IDictionary<string, string>>>(InitializeResources);
		}

		public IDictionary<string, IDictionary<string, string>> Value
		{
			get
			{
				return _respurces.Value;
			}
		}

		private IDictionary<string, IDictionary<string, string>> InitializeResources()
		{
			var infos = new Dictionary<string, IDictionary<string, string>>();
			var files = Directory.GetFiles(Path.Combine(_environment.ContentRootPath, "xml"), "*.xml", SearchOption.AllDirectories);

			foreach (var file in files)
			{
				var model = XmlUtil.Deserialize<XmlLocalizerModel>(file);

				if (model == null)
				{
					continue;
				}

				if (string.IsNullOrWhiteSpace(model.Culture))
				{
					throw new ArgumentException("The language type is required!");
				}

				if (infos.ContainsKey(model.Culture))
				{
					throw new ArgumentException($"The language({model.Culture}) resource file is duplicated!");
				}
				infos.Add(model.Culture, model.Texts.ToDictionary(p => p.Name, p => p.Value));
			}
			return infos;
		}
	}
}

        XmlLocalizer对象实现IStringLocalizer,具体代码如下:

using Microsoft.Extensions.Localization;
using System.Globalization;
using System.Text.RegularExpressions;

namespace MyWebDemo.Localizer
{
	public class XmlLocalizer : IStringLocalizer
    {
        private readonly XmlLocalizerCulture _cultureInfo;
        private readonly XmlLocalizerResource _resource;

        public XmlLocalizer(XmlLocalizerCulture cultureInfo, XmlLocalizerResource resource)
        {
            _cultureInfo = cultureInfo;
            
            _resource = resource;
        }

        public LocalizedString this[string name]
        {
            get
            {
                if (string.IsNullOrWhiteSpace(name))
                {
                    return new LocalizedString(name, name);
                }

                else if (!_resource.Value.ContainsKey(_cultureInfo.Name) || !_resource.Value[_cultureInfo.Name].ContainsKey(name))
                {
                    return new LocalizedString(name, GetDefaultString(name));
                }

                else
                {
                    return new LocalizedString(name, _resource.Value[_cultureInfo.Name][name]);
                }
            }
        }

        public LocalizedString this[string name, params object[] arguments]
        {
            get
            {
                if (string.IsNullOrWhiteSpace(name))
                {
                    return new LocalizedString(name, name);
                }

                else if (_resource.Value.ContainsKey(_cultureInfo.Name) || !_resource.Value[_cultureInfo.Name].ContainsKey(name))
                {
                    return new LocalizedString(name, GetDefaultString(name));
                }

                else
                {
                    return new LocalizedString(name, string.Format(_resource.Value[_cultureInfo.Name][name], arguments));
                }
            }
        }

        public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
        {
            if (includeParentCultures)
            {
                return _resource.Value.Values?.SelectMany(p => p.Select(t => new LocalizedString(t.Key, t.Value)))
                    ?? new List<LocalizedString>();
            }
            return _resource.Value.ContainsKey(_cultureInfo.Name)
                ? _resource.Value[_cultureInfo.Name].Select(p => new LocalizedString(p.Key, p.Value))
                : new List<LocalizedString>();
        }

        private string GetDefaultString(string name)
        {
            return Regex.Replace(name, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1], new CultureInfo(_cultureInfo.Name)));
        }
    }
}

        测试获取HelloWorld本地化文本,返回正常,但每次序列化时都需要注册IStringLocalizer,代码碎片化严重,不利于维护,考虑添加在项目控制器基类中添加IStringLocalizer属性,并在基类中调用IStringLocalizer接口内方法,提供给子类控制器使用,具体代码如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Primitives;
using MyWebDemo.Localizer;

namespace MyWebDemo.Controllers
{
	[ApiController]
	[Route("[controller]")]
	public class ApiController : ControllerBase
	{
		protected IStringLocalizer Localizer
		{
			get
			{
				return HttpContext.RequestServices.GetRequiredService<IStringLocalizer>();
			}
		}

		protected virtual string L(string name)
		{
			if (HttpContext.Request.Query.TryGetValue(XmlLocalizerCulture.Key, out StringValues val))
			{
				var cultureInfo = HttpContext.RequestServices.GetRequiredService<XmlLocalizerCulture>();

				cultureInfo.Name = val;
			}
			return Localizer[name].Value;
		}
	}
}

        考虑到实际项目中每次动态修改语言,所以需要支持动态配置功能,这里以sope生命周期注入XmlLocalizerCulture对象,属性包括语言名称Name,在项目初始化时默认为中文。在控制器基类中每次获取地址参数culture并赋值给XmlLocalizerCulture,实现动态配置功能。

        完整的代码已添加附件,如有问题,欢迎指出!

        

        

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值