软件国际化是在软件设计和文档开发过程中,使得功能和代码设计能处理多种语言和文化习俗,在创建不同语言版本时,不需要重新设计源程序代码的软件工程方法。这在很多成熟的软件开发平台中非常常见。对于.net开发者来说,我们一般可以通过以下两种方式来实现软件的国际化。

  • 语言配置文件
  • 资源文件

在.net平台中,软件的国际化主要依靠工作线程的国际化来完成。在.net框架的的处理线程中,我们通过设置Thread.CurrentCulture属性来实现对日期、时间、数字、货币值、文本的排序顺序,负载约定和字符串比较的默认值的格式确定,默认情况下,这个属性来自于“控制面板”的“区域和语言选项”中的用户区域性。当然,在软件运行过程中也可以通过手动的方式强制改变Thread.CurrentCulture属性值。CurrentUICulture属性则用来确定需要向用户呈现的资源格式,它对软件的操作界面来说最有用,因为它标识了在显示UI元素时应使用的语言。在.net中通常给软件设置不同的UI资源文件,使得软件运行时通过CurrentUICulture属性值来选择不同语言的资源文件渲染软件界面。线程的CurrentUICulture 和 CurrentCulture 属性一般设置为同一个CultureInfo对象,也就是就他它使用相同的语言、国家信息,然而也可将它们设为不同的对象。例如一个美国人在北京借用了一台操作系统是简体中文版的计算机进行工作时,就可以通过这种方式让软件满足美国用户的使用需求。

下面的例子解释了对于不同的Thread.CurrentCulture属性值,同一个日期字符串转换为日期类型时会生成不同结果

         static void Main(string[] args)
{
string birthdate = "02/06/2013";
DateTime dateTime;
dateTime = DateTime.Parse(birthdate);
Console.WriteLine(dateTime.ToString("yyyy年MM月dd日"));
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-GB");
dateTime = DateTime.Parse(birthdate);
Console.WriteLine(dateTime.ToString("yyyy年MM月dd日"));
Console.Read();
}

下面的例子解释了对于不同的Thread.CurrentUICulture属性值,框架会选择不同的资源文件读取其中的值,以便向使用不同语言的用户呈现不同的信息。运行前,需要在项目中建立如下的资源文件。

每个资源文件的内容如下:

其中,文件名中的语言代码代表不同的国家及使用的语言,如en-US=美国英语、en-GB=英国英语等等。文件名不带语言代码的为默认资源文件,框架会根据当前计算机所在的区域来调用这个资源文件。由于我在中国,并且使用简体中文的操作系统,所以,会将默认资源文件中的中文字符读出。

         static void Main(string[] args)
{
string hello;
hello = International.Hello;
Console.WriteLine(hello);
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("ja-JP");
hello = International.Hello;
Console.WriteLine(hello);
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
hello = International.Hello;
Console.WriteLine(hello);
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-GB");
hello = International.Hello;
Console.WriteLine(hello);
Console.Read();
}

那么在Web API中,如何确定用户来自哪个国家以及使用哪种语言、文化呢?由于Web API无法主动读取用户所有区域的信息,那么就只能被动地从用户那里获取此类信息。而这部分信息则会被包含在用户提交的HTTP请求头信息中。比如:

Accept-Language: en-us, en-gb;q=0.8, en;q=0.7

在这段HTTP请求头信息中,所传达的信息就是用户可接受的语言和地区文化。每种国家及语言后面的参数q称为相对质量的因素(relative quality factor),代表用户对于该种语言可接受的优先度(取值0.0~1.0,默认值为1.0)。总之通俗一来就讲,上面这段头信息的意思就是用户首选是美式英语,如果不支持的话,没关系,那就英式英语吧,再不行的话,其它类型的英语也可以。

上面的这段请求头信息提交到服务器时,会被封装在HttpRequestMessage类(System.Net.Http命名空间)的Headers属性中,我们可以很轻易地在Web API的控制器中读取到。参考以下例子

         public void Post(Object obj)
{
HttpHeaderValueCollection<StringWithQualityHeaderValue> acceptedLanguages = Request.Headers.AcceptLanguage;
foreach (StringWithQualityHeaderValue language in acceptedLanguages)
{
Debug.WriteLine(language.Value);
Debug.WriteLine(language.Quality);
}
}

当然,在实际的开发中,上面的方法不是推荐的方法。更加科学的方法是我们新建一个自定义的DelegatingHandler,简单一点来讲,DelegatingHandler是HTTP请求通道中的过滤器。我们可以在这个DelegatingHandler中读取Accept-Language信息,并且设置处理线程的Thread.CurrentCulture属性和CurrentUICulture属性。源码如下:

 namespace HelloWebAPI.Infrastructure
{
public class CultureHandler : DelegatingHandler
{ private List<string> supportedCulture = new List<string>()
{
"zh-cn", "en-us", "ja-jp"
}; protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
HttpHeaderValueCollection<StringWithQualityHeaderValue> acceptedLanguage = request.Headers.AcceptLanguage;
if (acceptedLanguage != null && acceptedLanguage.Count > )
{
StringWithQualityHeaderValue preferredLanguage =
acceptedLanguage.OrderByDescending(e => e.Quality ?? 1.0D)
.Where(e => !e.Quality.HasValue || e.Quality.Value > 0.0D)
.FirstOrDefault(
e => supportedCulture.Contains(e.Value, StringComparer.OrdinalIgnoreCase));
if (preferredLanguage != null)
{
// 如需要,此处也可同时设置CurrentCulture属性
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(preferredLanguage.Value);
} if (acceptedLanguage.Any(e => e.Value == "*" &&(!e.Quality.HasValue || e.Quality.Value > 0.0D)))
{
string selectedCulture =
supportedCulture.FirstOrDefault(e => !acceptedLanguage.Any(
ee =>
ee.Value.Equals(e, StringComparison.OrdinalIgnoreCase) && ee.Quality.HasValue &&
ee.Quality.Value == 0.0D));
if (!string.IsNullOrWhiteSpace(selectedCulture))
{
// 如需要,此处也可同时设置CurrentCulture属性
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(selectedCulture);
}
}
}
return base.SendAsync(request, cancellationToken);
}
}
}

上面的这段代码中,假设系统支持的语言有美式英语、简体中文、日文。并且如果用户支持多种语言,该DelegatingHandler也会根据头信息中的相对质量的因素q进行排序以确定首选语言。

打开WebApiConfig.cs文件,注册自定义的DelegatingHandler,在Register静态方法中添加如下的语句。

         public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.MessageHandlers.Add(new CultureHandler());
}

应用场景一:根据用户所在的国家和使用的语言,向用户提供不同语言的响应

在项目中,新建一个App_GlobalResources文件夹,该文件为ASP.net的专属文件夹,专门用于存放资源文件。分别建立几个对应语言的资源文件。文件中均有一个同键不同值的字符串“NotFound”,该字符串用于提示用户找不到指定的文件或资源。

在控制器中,建立一个示例动作方法如下:

     public class EmployeesController : ApiController
{
public HttpResponseMessage Get(int id)
{
// 业务逻辑,此处省略
return Request.CreateErrorResponse(HttpStatusCode.NotFound, Resources.Message.NotFound);
}
}

为了调试,我们使用Fiddler来进行,运行项目之后,我们通过设置HTTP请求头信息中的Accept-Lanuage,可以得到不同语言的响应。

应用场景二:根据用户所在的国家和使用的语言,对用户提交的数据进行处理和提交。

回想文章开头的例子,对于一个相同的时间日期字符串,不同国家的用户可能会有不同的理解。比如"05/06/2013",类似于这种格式的日期在计算机中很常见。但恰恰是这样一种表达方式在美国人看来是2013年5月6日,因为美国习惯于MM/dd/yyyy这种日期格式;但在英国人看来则会是2013年6月5日,因为他们所理解的日期格式是 dd/MM/yyyy。因此,对于Web API来说,如果我们面对的文化背景如此复杂的用户,那么在处理用户的数据时不得不倍加细心,否则将会造成难以挽回的损失。如何应对这个问题呢?

首先要知道,在Web API框架中,关于JSON的序列化和反序列化,微软已经将这个任务委托给第三方类库JSON.net。经过查阅文档,发现JSON.net对于日期格式都使用了日期转换器进行转换,并且内置了两个常用的日期转换器,JavaScriptDateTimeConverter和IsoDateTimeConverter,这两个日期转换器运行时,通过读取SerializerSettings类的Culture属性来设置线程的Culture。

所以,我们通过设置JSON.net的SerializerSettings类中的Culture属性也可以实现国际化。因此,在WebApiConfig.cs文件中,我们可以直接对其设置。

         public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SerializerSettings.Culture = new System.Globalization.CultureInfo("en-US");
}

但是,这根本不是最好的解决方案。因为它没办法根据HTTP请求头信息来设置Culture属性,WebApiConfig类只是一个配置类,不是一个过滤器,它没办法访问HTTP请求。

所以,问题的最终解决方案是我们应该避开SerializerSettings类,自己编写DataTimeConverter类对日期进行转换,设置线程的Cultrue工作交由之前自定义的CultureHandler去完成。

DataTimeConverter源代码如下:

 namespace HelloWebAPI.Infrastructure
{
public class DateTimeConverter : DateTimeConverterBase
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((DateTime)value).ToString());
} public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return DateTime.Parse(reader.Value.ToString());
}
}
}

同时,我们还需要将CultureHandler源码中的Thread.CurrentThread.CurrentUICulture改为Thread.CurrentThread.CurrentCulture。

最后,我们需要在WebApiConfig.cs中对日期转换器进行注册,为了避免与框架中内置的DateTimeConverter冲突,此处使用了类的完全限定名。

         public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new HelloWebAPI.Infrastructure.DateTimeConverter());
config.MessageHandlers.Add(new CultureHandler());
}

启动项目,同时使用Fiddler提交示例的JSON数据如下:

{id:123,firstName:"Gates", lastName:"Bill", age:58, birthdate:"05/06/1955"}

分别设置不同的Accept-Language头信息进行调试

Hello Web API系列教程——Web API与国际化的更多相关文章

  1. ASP.NET Web API系列教程目录

    ASP.NET Web API系列教程目录 Introduction:What's This New Web API?引子:新的Web API是什么? Chapter 1: Getting Start ...

  2. ASP.NET Web API系列教程(目录)(转)

    注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP.NET Web API.这是一个用来在.NET平台上建立HTTP服务的Web API框架,是微软的又一项令人振奋的技术.目前,国内 ...

  3. [转]ASP.NET Web API系列教程(目录)

    本文转自:http://www.cnblogs.com/r01cn/archive/2012/11/11/2765432.html 注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP ...

  4. Web攻防系列教程之文件上传攻防解析(转载)

    Web攻防系列教程之文件上传攻防解析: 文件上传是WEB应用很常见的一种功能,本身是一项正常的业务需求,不存在什么问题.但如果在上传时没有对文件进行正确处理,则很可能会发生安全问题.本文将对文件上传的 ...

  5. SpringBoot系列教程web篇之过滤器Filter使用指南扩展篇

    前面一篇博文介绍了在 SpringBoot 中使用 Filter 的两种使用方式,这里介绍另外一种直接将 Filter 当做 Spring 的 Bean 来使用的方式,并且在这种使用方式下,Filte ...

  6. SpringBoot系列教程Web篇之开启GZIP数据压缩

    本篇可以归纳在性能调优篇,虽然内容非常简单,但效果可能出乎预料的好: 分享一个真实案例,我们的服务部署在海外,国内访问时访问服务时,响应有点夸张:某些返回数据比较大的接口,耗时在 600ms+上,然而 ...

  7. SpringBoot系列教程web篇Listener四种注册姿势

    java web三要素Filter, Servlet前面分别进行了介绍,接下来我们看一下Listener的相关知识点,本篇博文主要内容为SpringBoot环境下,如何自定义Listener并注册到s ...

  8. SpringBoot系列教程web篇Servlet 注册的四种姿势

    原文: 191122-SpringBoot系列教程web篇Servlet 注册的四种姿势 前面介绍了 java web 三要素中 filter 的使用指南与常见的易错事项,接下来我们来看一下 Serv ...

  9. SpringBoot系列教程web篇之过滤器Filter使用指南

    web三大组件之一Filter,可以说是很多小伙伴学习java web时最早接触的知识点了,然而学得早不代表就用得多.基本上,如果不是让你从0到1写一个web应用(或者说即便从0到1写一个web应用) ...

随机推荐

  1. 当忘记mysql数据库密码时如何进行修改

    因为长时间没有使用数据库了,或者把密码改完之后就忘了数据库密码,不能正常进入数据库,也无法修改密码,有一个简单的常用修改密码方式: 1.首先找到和打开mysql.exe和mysqld.exe所在的文件 ...

  2. AutoFac在项目中的应用

    技能大全:http://www.cnblogs.com/dunitian/p/4822808.html#skill 完整Demo:https://github.com/dunitian/LoTCode ...

  3. Spring框架概述

    Spring是最流行的Java企业级应用开发框架,全球数以百万的开发者在使用Spring框架创建高性能.易测试.可重用的代码. Spring框架的核心特性可以应用于任何Java应用,但扩展的JavaE ...

  4. [APUE]文件和目录(上)

    一.文件权限 1. 各种ID 我在读这一章时遇到了各种ID,根据名字完全不清楚什么意思,幸好看到了这篇文章,http://blog.csdn.net/ccjjnn19890720/article/de ...

  5. css中的浮动与三种清除浮动的方法

    说到浮动之前,先说一下CSS中margin属性的两种特殊现象 1, 外边距的合并现象: 如果两个div上下排序,给上面一个div设置margin-bottom,给下面一个div设置margin-top ...

  6. ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起

    我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点.由于前面两章已经涵盖了依赖注入在管道构建过程中以及管道在处理请求过程的应用,但是内 ...

  7. 企业做数据缓存是使用Memcached还是选Redis?

    企业是使用Memcached还是选Redis? 在构建一款现代且由数据库驱动的Web应用程序并希望使其拥有更为出色的性能表现时,这个问题总会时不时出现.并给每一位开发人员带来困扰.在考虑对应用程序的性 ...

  8. CSS3自定义滚动条样式 -webkit-scrollbar(转)

    有没有觉得浏览器自带的原始滚动条很不美观,同时也有看到很多网站的自定义滚动条显得高端,就连chrome32.0开发板都抛弃了原始的滚动条,美观多了.那webkit浏览器是如何自定义滚动条的呢? 前言 ...

  9. 报错:You need to use a Theme.AppCompat theme (or descendant) with this activity.

    学习 Activity 生命周期时希望通过 Dialog 主题测试 onPause() 和 onStop() 的区别,点击按钮跳转 Activity 时报错: E/AndroidRuntime: FA ...

  10. Xamarin.Android-用ZXing实现二维码扫描以及连续扫描

    一.前言 本文的内容有两个基础:ZXing.Net和ZXing.Net.Mobile ZXing.Net:ZXing的C#实现,主要封装了各种二维码的编码.解码等跨平台的算法 ZXing.Net.Mo ...