65.9K
CodeProject 正在变化。 阅读更多。
Home

RESX 翻译工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (8投票s)

2009年3月18日

CPOL

2分钟阅读

viewsIcon

48212

downloadIcon

1482

一个用于本地化 .NET 项目的 C# 工具。

引言

虽然英语是一种国际语言,但许多人更喜欢使用具有本地语言界面的软件。因此,如果您希望您的软件在不同国家/地区的人们中非常受欢迎,您需要将用户界面翻译成尽可能多的语言。但在这里应该提到,软件本地化是一项艰巨的工作。这个 RESX 翻译工具有助于避免陷入困境并自动化一些操作。

该工具基于 C# 中的翻译 Web 服务,作者 Matthew Brealey,以及 用于多语言支持的 RESX 到 XLS 转换,作者 Marco Roello。

背景

有很多多语言翻译 Web 服务,例如 GoogleBabelfishYandex 等。当然,计算机翻译不如人工翻译好,但它可以帮助加快本地化速度。

代码

翻译器执行以下操作:

  1. 生成发送到翻译 Web 服务 (Google) 的 POST 请求
  2. 接收来自 Web 服务的可读响应
  3. 使用正则表达式解析响应
  4. 返回翻译

函数 Translate 实现这些操作

public string Translate(string text, TranslateDir dir)
{
    HttpWebRequest request = 
      (HttpWebRequest)WebRequest.Create(TranslateUrl);

    // Encode the text to be translated
    string postSourceData = GetPostSourceData(text, dir);

    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = postSourceData.Length;
    request.UserAgent = "Mozilla/4.0 (compatible; " + 
                        "MSIE 6.0; Windows NT 5.1)";

    HttpWebResponse response;
    
    try
    {
        using (Stream writeStream = request.GetRequestStream())
        {
            byte[] bytes = Encoding.UTF8.GetBytes(postSourceData);
            writeStream.Write(bytes, 0, bytes.Length);
            writeStream.Close();
        }
        response = (HttpWebResponse)request.GetResponse();
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
    StreamReader readStream = 
      new StreamReader(response.GetResponseStream(), Encoding.UTF8);
    string page = readStream.ReadToEnd();
    response.Close();

    Regex reg = new Regex(RegexpResult, RegexOptions.IgnoreCase);
    Match m = reg.Match(page);
    string s;
    if (m.Success)
    {
        s = m.Result("${text}");
    }
    else s = "regexp error";

    return s;
}

protected override string RegexpResult
{
    get { return @"<div[^>]+id=result_box[^>]+" + 
                 @">(?<text>[^>]+)</div>"; }
}

函数 GetPostSourceData 生成用于发布请求的主体,该主体将文本和语言选项传递给 Google 翻译引擎。Google 的响应是可读的,并且翻译被放入 id 等于 result_box 的 div 中。

DataSet 包含两个表:ResxResxLocalized。这些表用从加载的 *.resx 文件中读取的数据填充。函数 TranslateDataSet 用翻译后的值填充 ResxLocalized 表。为了避免由 Web 服务请求引起的额外延迟,此函数仅填充未填充的单元格。

首先,使用 LINQ 将所有未填充的记录选择到枚举 query 中。然后,查找是否有任何值等于当前值并且已经被翻译。否则,将翻译请求发送到 Google 翻译 Web 服务。

private void TranslateDataSet(ResxData rd)
{
    GoogleTranslator gt = new GoogleTranslator();
    DataTable resxs = rd.Resx;
    DataTable resxlocs = rd.ResxLocalized;
    int count = 0;
    int amount;

    var query =
        from resx in resxs.AsEnumerable()
        join resxloc in resxlocs.AsEnumerable()
        on resx.Field<int32 >("ID") equals
        resxloc.Field<int32 >("ParentID")
        where resxloc.Field<string >("Value") == null || 
              resxloc.Field<string >("Value") == ""
        select new
        {
            Value = resx.Field<string >("Value"),
            Culture = resxloc.Field<string >("Culture"),
            Translation = resxloc.Field<string >("Value"),
            LocID = resxloc.Field<int32 >("ID")
        };
    string translation;
    this.BeginInvoke((MethodInvoker)delegate()
    { progressBar1.Maximum = query.Count() + 1; progressBar1.Value = 1; });

    if (!query.Any())
    {
        this.BeginInvoke((MethodInvoker)delegate()
            { words.Text = "0 of " + query.Count().ToString(); });
    }
    amount = query.Count();
    foreach (var record in query)
    {
        var enumer = query.Where(r => r.Value == record.Value && 
                                 r.Translation != "" && 
                                 r.Translation != null && 
                                 r.Culture == record.Culture);
        count++;
        
        this.BeginInvoke((MethodInvoker)delegate()
        { words.Text = count.ToString() + " of " + amount.ToString(); });
        if (enumer.Any())
        {
            translation = enumer.First().Translation;
        }
        else
        {
            translation = gt.Translate(record.Value, 
               new TranslateDir("auto", record.Culture.Substring(0, 2)));
        }

        this.BeginInvoke((MethodInvoker) delegate() { progressBar1.Value++; });
        resxlocs.Rows.Find(record.LocID).SetField<string >("Value", translation);
    }
}

关注点

在 C# 中处理 Excel 文件有几种方法:

  • 早期绑定 COM
  • 后期绑定 COM
  • 使用 OLE DB 提供程序

只有最后一种方法不需要安装 Excel。以下代码显示了如何在 Excel 工作表中获取和设置值:

private void SetCell(int row, int col, OleDbConnection con, string value)
{
    string c = Convert.ToChar((Convert.ToInt16('A') + col - 1)).ToString();
    string r = row.ToString();

    OleDbCommand cmd = new OleDbCommand("UPDATE [Localize$" + c + r + ":" + 
                       c + r + "] SET F1 = \"" + value + "\"", con);
    cmd.ExecuteNonQuery();
}

private string GetCell(int row, int col, OleDbConnection con)
{
    string c = Convert.ToChar((Convert.ToInt16('A') + col - 1)).ToString();
    string r = row.ToString();

    OleDbCommand cmd = new OleDbCommand("SELECT * FROM [Localize$" + 
                       c + r + ":" + c + r + "]", con);
    object ret = cmd.ExecuteScalar();
    if (ret == DBNull.Value) return null; else return (string)ret;
}

建议

如果有人可以贡献并实现其他翻译器的接口并发布源代码,我将更新文章并包含它们。

© . All rights reserved.