十年磨一劍--從程序員到架構師

一个.net程序员,一个企业应用的开发者,喜欢系统架构,数据库,领域驱动,面向对象,表现层技术。关注重用的理论和实践。设计原则:简单,快速,适应变化能力强,表现层灵活多变...

博客园 首页 新随笔 联系 订阅 管理

对于有跨国业务的web系统来说,一般都需要提供多语言功能。然而在众多多语言方案里,如.net自带的Resource方式,都会在程序开发时增加程序员的额外负担,不易开发:

 

1.各种不同的地方实现多语言,如aspxcontrol绑定,js脚本,cs代码,procedure里的提示信息等,这些多语言实现方式各不相同,加重了开发难度。

 

2.在要实现多语言的代码中都要与获取语系文字的代码进行强耦合,不方便扩展和维护。如.netResource方式某个label实现多语言:

lblName.Text = Resources.Strings.name;

 

3.开发和维护程序时,程序员都必须同时打开source code和多语言资源文件,特别是在多人开发时,共享一个多语言文件,且对于共享的文字实难处理和同步。

 

以上这些只是简单地罗列了一下传统多语言开发时的困扰,那有什么方式解决这些问题,轻松实现多语言功能呢?

 

记得有人说过web编程,实际上就是字符串处理。

 

为什么呢?因为web,实际上就是RequestResponse,而RequestResponse就是字符串。在我们各种程序里,最终输出到Browser的都是html格式的字符串,因此,只要我们统一在程序最后一步输出html格式时,能够识别其中需要进行多语言转换的文字,将将其替换为当前设定的语言版本的文字就可以了。

 

asp.net中,因为有一个Response.Filter的属性,让这一切变得很简单。

什么是Response.Filter

简单地说,就是在经过层层转换后,最终asp.net要输出html,在输出的过程中,是将html放到一个管道(pipeline)里,然后在管道那头取出要发往客户端的html进行Responseasp.net提供Response.Filter属性,让你可以对经过的html进行相应的转换。

 

因此只要将多语言转换功能放在这里,并识别“中文”,将其转换为user设定的相应语系就可以完成了。

 

1.      首先要实现一个多语言Stream:

using System;

using System.IO;

using System.Text;

using System.Text.RegularExpressions;

 

public class HttpMulLangStream : Stream

{

    private Stream strSink;

    private long lngPosition;

 

    public HttpMulLangStream(Stream sink)

    {

        strSink = sink;

    }

 

    // The following members of Stream must be overriden.

    public override bool CanRead

    {

        get { return true; }

    }

 

    public override bool CanSeek

    {

        get { return true; }

    }

 

    public override bool CanWrite

    {

        get { return true; }

    }

 

    public override long Length

    {

        get { return 0; }

    }

 

    public override long Position

    {

        get { return lngPosition; }

        set { lngPosition = value; }

    }

 

    public override long Seek(long offset, System.IO.SeekOrigin direction)

    {

        return strSink.Seek(offset, direction);

    }

 

    public override void SetLength(long length)

    {

        strSink.SetLength(length);

    }

 

    public override void Close()

    {

        strSink.Close();

    }

 

    public override void Flush()

    {

        strSink.Flush();

    }

 

    public override int Read(byte[] buffer, int offset, int count)

    {

        return strSink.Read(buffer, offset, count);

    }

 

    // 关键代码.

    public override void Write(byte[] buffer, int offset, int count)

    {

        //读出写的文字

        byte[] data = new byte[count];

        Buffer.BlockCopy(buffer, offset, data, 0, count);

        string inputstring = Encoding.UTF8.GetString(data);

 

        //找出汉字(对于复杂一些的,可以设定固定识别方式,如使用这些标记识别[@多语言文字],并按设定的语言作转换

        inputstring = Regex.Replace(inputstring, "[\u3000-\u9fff]+", new MatchEvaluator(capText));

 

        //将翻译后的语言写入response

        byte[] newdata = Encoding.UTF8.GetBytes(inputstring);

        strSink.Write(newdata, 0, newdata.Length);

    }

 

    private string capText(Match m)

    {

        //简单的替换,实际开发过程可以从xml文件,Resouce资源,进行缓存等等替换策略

        string x = m.Value;

        if (x.Equals("账号"))

            return "Account";

        else if (x.Equals("登录"))

            return "Login";

        else if (x.Equals("你的名字"))

            return "Your Name";

        //如果没找到,原样返回

        return x;

    }

}

 

2.      其次要在Global.asax中设定Response.Filter

<%@ Application Language="C#" %>

<%@ Import Namespace="System.IO" %>

 

<script runat="server">

     //在每次Request之前设定好Response.Filter属性

    void Application_PreRequestHandlerExecute(object sender, EventArgs e)

    {

        Response.Filter = new HttpMulLangStream(Response.Filter);

    }

</script>

 

3.      测试代码 test.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>未命名页面</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Label ID="Label1" runat="server" Text="账号"></asp:Label>

        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

        <asp:Button ID="Button1" runat="server" Text="登录" /></div>

    </form>

</body>

</html>

 

使用这种方式对于程序员开发系统基本上是零侵入,程序员基本感受不到多语言功能增加需要的额外工作。

 

上面只是一个简单的Demo,实际运用中,还有几点需要说明:

1.识别:

不能靠单纯地识别汉字,来进行转换。因为在有的地方,确实不需要进行转换,如数据内容。比如某个user的姓名叫张三,不管在英文还是中文,都显示张三。这时候就需要对程序开发加入一点侵入,如程序员在开发时,需将多语言转换的字符串作特别标记,如[@姓名:],这样在识别时,就以[@.*]来作为识别方式。

 

2.转换:

转换是这种方式最成功的地方,一是效率,可以利用staticcache等方式缓存资源,提高性能。二就是,对于没有做翻译的地方,可以原样输出。这样有几个好处,可以先开发程序,最后增加资源文件来进行多语言转换。二就是有的确实不需要多语言转换时,可以不提供资源文件,它就不会转,而不需要额外写程序去指定。

 

3.通用性:

对于一个多语言方案来说,通用性比较重要,有的地方需要对单独的js文件进行多语言转换,这时就要将js文件在IIS中加入到asp.net引擎处理。而更简单的方式就是直接把js文件的后缀名改为aspx

至于从DB中的程序过来的字符串,以及嵌入在aspx中的script,因为都最终会通过aspx输出到页面上,所以不用额外处理。

 

4.重用:

透过asp.netHttpModule特性,将多语言实现方案进行单独封装,然后在web.config中配置就可动态装载多语言功能了。

 

posted on 2008-11-26 10:24  Kevin Zou  阅读(3270)  评论(26编辑  收藏  举报