zoukankan      html  css  js  c++  java
  • (转)Asp.net MVC 多语言问题的解决方案

    原文地址:http://www.cnblogs.com/huyq2002/archive/2012/01/19/2326177.html

    上篇文章我们就Asp.net MVC权限问题做了一个较为全面的解决方案,这篇我们就多语言问题进行探讨。

    全球化
    在IT行业,具有全球化和本地化特性的计算机软件,可以适应不同的语言,地区差异和目标市场的业务要求。

    如果一个网站的目标来自 世界不同地区的的用户,这些用户可能会希望看到自己的语言的网站内容。
    达到全球化,我们的应用程序,将满足以下要求:

    (1)它可以用不同的语言显示信息内容。
    (2)自动检测用户的浏览器的语言。


    (3)允许用户方便的更改系统的默认语言,并且系统记住客户的选择。

    Culture和UICulture

    在传统的.net程序中,全球化主要依靠Culture和UICulture两个概念。

    Culture 决定了日期,数字和货币格式,UICulture决定了ResourceManager加载那种语言的资源文件。

    .NET中的每一个线程都有CurrentCulture和的CurrentUICulture对象,他们控制了当前线程的语言问题。

    Asp.net MVC遵循相同的概念,以应付全球化的要求。

    Culture标识

    文化名称Culture 标识语言、区域
    zh-CN0x0804Chinese - China
    en-GB0x0809English - United Kingdom
    en-US0x0409English - United States
    fr-FR0x040CFrench - France
    ja-JP0x0411Japanese - Japan







    ...



    BaseController

    在我们的示例应用程序中,我们构造一个MVC Controller 的基类BaseController,它将设置执行线程的CurrentCulture和的CurrentUICulture。

    该BaseController将检查客户端的cookie首选Culture标识,如果不存在,将读取的浏览器中首选语言设置。(满足需求2,3)

    [HandleError]
    public class BaseController:Controller
    {
    protected override void ExecuteCore()
    {
    string cultureName = null;
    // Attempt to read the culture cookie from Request
    HttpCookie cultureCookie = Request.Cookies["_culture"];
    if (cultureCookie != null)
    cultureName = cultureCookie.Value;
    else
    // obtain it from HTTP header AcceptLanguages
    cultureName = Request.UserLanguages[0];

    // Validate culture name
    //cultureName = CultureHelper.GetImplementedCulture(cultureName);
    CultureHelper.SetCulture(cultureName);

    base.ExecuteCore();
    }

    protected override void OnException(ExceptionContext filterContext)
    {
    base.OnException(filterContext);
    }

    }

    CultureHelper
    CultureHelper类封装了关于文化设置的基本功能,例如设置当前线程的Culture和UICulture对象等等

    public static class CultureHelper
    {
    // Valid cultures
    private static readonly IList<string> _validCultures = new List<string> { "af", "af-ZA", "sq", "sq-AL", "gsw-FR", "am-ET", "ar", "ar-DZ", "ar-BH", "ar-EG", "ar-IQ", "ar-JO", "ar-KW", "ar-LB", "ar-LY", "ar-MA", "ar-OM", "ar-QA", "ar-SA", "ar-SY", "ar-TN", "ar-AE", "ar-YE", "hy", "hy-AM", "as-IN", "az", "az-Cyrl-AZ", "az-Latn-AZ", "ba-RU", "eu", "eu-ES", "be", "be-BY", "bn-BD", "bn-IN", "bs-Cyrl-BA", "bs-Latn-BA", "br-FR", "bg", "bg-BG", "ca", "ca-ES", "zh-HK", "zh-MO", "zh-CN", "zh-Hans", "zh-SG", "zh-TW", "zh-Hant", "co-FR", "hr", "hr-HR", "hr-BA", "cs", "cs-CZ", "da", "da-DK", "prs-AF", "div", "div-MV", "nl", "nl-BE", "nl-NL", "en", "en-AU", "en-BZ", "en-CA", "en-029", "en-IN", "en-IE", "en-JM", "en-MY", "en-NZ", "en-PH", "en-SG", "en-ZA", "en-TT", "en-GB", "en-US", "en-ZW", "et", "et-EE", "fo", "fo-FO", "fil-PH", "fi", "fi-FI", "fr", "fr-BE", "fr-CA", "fr-FR", "fr-LU", "fr-MC", "fr-CH", "fy-NL", "gl", "gl-ES", "ka", "ka-GE", "de", "de-AT", "de-DE", "de-LI", "de-LU", "de-CH", "el", "el-GR", "kl-GL", "gu", "gu-IN", "ha-Latn-NG", "he", "he-IL", "hi", "hi-IN", "hu", "hu-HU", "is", "is-IS", "ig-NG", "id", "id-ID", "iu-Latn-CA", "iu-Cans-CA", "ga-IE", "xh-ZA", "zu-ZA", "it", "it-IT", "it-CH", "ja", "ja-JP", "kn", "kn-IN", "kk", "kk-KZ", "km-KH", "qut-GT", "rw-RW", "sw", "sw-KE", "kok", "kok-IN", "ko", "ko-KR", "ky", "ky-KG", "lo-LA", "lv", "lv-LV", "lt", "lt-LT", "wee-DE", "lb-LU", "mk", "mk-MK", "ms", "ms-BN", "ms-MY", "ml-IN", "mt-MT", "mi-NZ", "arn-CL", "mr", "mr-IN", "moh-CA", "mn", "mn-MN", "mn-Mong-CN", "ne-NP", "no", "nb-NO", "nn-NO", "oc-FR", "or-IN", "ps-AF", "fa", "fa-IR", "pl", "pl-PL", "pt", "pt-BR", "pt-PT", "pa", "pa-IN", "quz-BO", "quz-EC", "quz-PE", "ro", "ro-RO", "rm-CH", "ru", "ru-RU", "smn-FI", "smj-NO", "smj-SE", "se-FI", "se-NO", "se-SE", "sms-FI", "sma-NO", "sma-SE", "sa", "sa-IN", "sr", "sr-Cyrl-BA", "sr-Cyrl-SP", "sr-Latn-BA", "sr-Latn-SP", "nso-ZA", "tn-ZA", "si-LK", "sk", "sk-SK", "sl", "sl-SI", "es", "es-AR", "es-BO", "es-CL", "es-CO", "es-CR", "es-DO", "es-EC", "es-SV", "es-GT", "es-HN", "es-MX", "es-NI", "es-PA", "es-PY", "es-PE", "es-PR", "es-ES", "es-US", "es-UY", "es-VE", "sv", "sv-FI", "sv-SE", "syr", "syr-SY", "tg-Cyrl-TJ", "tzm-Latn-DZ", "ta", "ta-IN", "tt", "tt-RU", "te", "te-IN", "th", "th-TH", "bo-CN", "tr", "tr-TR", "tk-TM", "ug-CN", "uk", "uk-UA", "wen-DE", "ur", "ur-PK", "uz", "uz-Cyrl-UZ", "uz-Latn-UZ", "vi", "vi-VN", "cy-GB", "wo-SN", "sah-RU", "ii-CN", "yo-NG" };

    // Include ONLY cultures you are implementing
    private static readonly IList<string> _cultures = new List<string> {
    "en-US", // first culture is the DEFAULT
    "zh-CN"
    };

    /// <summary>
    /// Returns a valid culture name based on "name" parameter. If "name" is not valid, it returns the default culture "en-US"
    /// </summary>
    /// <param name="name">Culture's name (e.g. en-US)</param>
    public static string GetImplementedCulture(string name)
    {
    // make sure it's not null
    if (string.IsNullOrEmpty(name))
    return GetDefaultCulture(); // return Default culture

    // make sure it is a valid culture first
    if (_validCultures.Where(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Count() == 0)
    return GetDefaultCulture(); // return Default culture if it is invalid

    // if it is implemented, accept it
    if (_cultures.Where(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Count() > 0)
    return name; // accept it

    // Find a close match. For example, if you have "en-US" defined and the user requests "en-GB",
    // the function will return closes match that is "en-US" because at least the language is the same (ie English)
    var n = GetNeutralCulture(name);
    foreach (var c in _cultures)
    if (c.StartsWith(n))
    return c;

    // else
    // It is not implemented
    return GetDefaultCulture(); // return Default culture as no match found
    }

    /// <summary>
    /// set current culture based on input name
    /// </summary>
    /// <param name="name">Culture's name (e.g. en-US)</param>
    public static void SetCulture(string name)
    {
    if (string.IsNullOrEmpty(name)) return;
    // Modify current thread's cultures
    Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(name);
    Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
    }

    /// <summary>
    /// Returns default culture name which is the first name decalared (e.g. en-US)
    /// </summary>
    /// <returns></returns>
    public static string GetDefaultCulture()
    {
    return _cultures[0]; // return Default culture

    }

    public static string GetCurrentCulture()
    {
    return Thread.CurrentThread.CurrentCulture.Name;
    }

    public static string GetCurrentNeutralCulture()
    {
    return GetNeutralCulture(Thread.CurrentThread.CurrentCulture.Name);
    }

    public static string GetNeutralCulture(string name)
    {
    if (name.Length < 2)
    return name;

    return name.Substring(0, 2); // Read first two chars only. E.g. "en", "es"
    }

    }

    使用资源文件

    构造多语言的系统的核心就是所有页面和类里面的literal不能硬编码,而是要把他们放到资源文件中,并且针对不同的文化名称( en-GB)构造不同的资源文件,各个资源文件具有相同名称的条目,但是条目的具体信息随语言不同而不同,例如

    UI.Resources.resx (无文化设置)中有一条设置为

    Name        Value

    About_AboutLabel    About

    对应的适应于中文的UI.Resources.zh-CN.resx (命名规范为  XXX.文化名称.resx)

    Name        Value

    About_AboutLabel    关于

    在页面中应该使用资源文件

     <span class="supportingFunctions">
    @Html.ActionLink(UI_Resources.About_HelpLabel, "Help", "Home")
    @Html.ActionLink(UI_Resources.Tab_About, "About", "Home")
    </span>

    让浏览器记住客户的选择

    如何让浏览器记住客户的选择?

    答案很简单,在主页面(其实是模板页,对应Razor的_layout.cshmtl)上面增加需要的文化的Link,当客户点击这些link的时候,将客户的选择的文化符合保存到Cookie,然后重新导入该页面,那样我们的BaseController就可以正确的加载对应的文化和资源了。

    <span class="language">
    <a href="javascript:void(0);" class="en-GB">@UI_Resources.Lan_English</a>
    <a href="javascript:void(0);" class="zh-CN">@UI_Resources.Lan_Chinese</a>
    <a href="javascript:void(0);" class="ja-JP">@UI_Resources.Lan_Japanese</a>
    </span>
    <script type="text/javascript">
    function setCookie(c_name, value, exdays) {
    var exdate = new Date();
    exdate.setDate(exdate.getDate() + exdays);
    var c_value = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString());
    document.cookie = c_name + "=" + c_value;
    }

    $(".language a").click(function () {
    setCookie("_culture", $(this).attr("class"), 365);
    window.location.reload();
    });
    </script>


    这样设计的语言解决方案有以下特点

    1)程序有很好的性能,无需在数据库里面存储所有的页面的控件翻译信息

    2)资源文件集中了所有的多语言的信息,不同的语言使用不同的文件,便于维护

    3)该设计只针对于系统界面的语言问题,业务数据的语言还是得需要存储于数据库正确的语言信息 (NVARCHAR, NTEXT..)

    ----------------------------------------------------------------------------------------------------------

    示例代码

    https://files.cnblogs.com/huyq2002/Sample.zip

  • 相关阅读:
    理解Linux系统负荷 和 ubuntu静态、动态设置ip地址
    Kafka笔记--监控系统KafkaOffsetMonitor
    Kafka笔记--使用ubuntu为bocker(服务器)windows做producer和comsumer(客户端)
    Android学习笔记--Broadcast, BroadcastReceiver(广播)
    Java学习笔记--xml构造与解析之Sax的使用
    Android学习笔记--存储方案(SharedPreference、文件IO)
    Android学习笔记--Menu菜单的使用
    Android学习笔记--AlertDialog应用
    Kafka笔记--参数说明及Demo
    Kafka笔记--分布式环境搭建
  • 原文地址:https://www.cnblogs.com/fcsh820/p/2328387.html
Copyright © 2011-2022 走看看