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

.NET 资源 (.resx 文件) 翻译器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (46投票s)

2010 年 2 月 4 日

CPOL

4分钟阅读

viewsIcon

223592

downloadIcon

6724

一个 .NET 资源 (.resx 文件) 翻译器。英语翻译成任何其他语言。

Main Window

引言

除了应用程序的默认语言(通常是英语)之外,您的软件还应该支持不同的语言,因为人们更喜欢使用带有母语界面的软件。为了在全球范围内分发应用程序,您需要将用户界面翻译成尽可能多的语言。当您这样做时,可以说该应用程序是全球化的。

使应用程序全球化的第一步是将 Windows 窗体的 Localizable 属性设置为 true。当您创建基于窗体的 Windows 应用程序时,每个窗体都有一个关联的资源(*.resx*)文件。此资源文件特定于一种语言,其中包含该窗体的所有区域性特定详细信息。

在本文中,我们将讨论如何从默认的英语资源文件生成另一种语言的资源文件。

背景

我还发布了一篇关于全球化/国际化文章。在阅读本文之前,请先阅读:使用 C# 和 .NET 2.0 进行全球化、国际化 (I18N) 和本地化

Using the Code

文本翻译基于 Google 翻译网站(http://translate.google.com)提供的翻译。

尽管 Google 翻译 .NET API 类可用,但现在使用这些 API 是收费的。因此,我已更新源代码以直接从网站读取翻译后的数据。

在这里,我们有 Translator 类,它基本上使用 Google 翻译网站来翻译文本。它将 text 与适当的参数发送到网站,并从响应中提取翻译后的 text

/// <summary>
/// Translates the given text in the given language.
/// </summary>
/// <param name="targetLanguage">Target language.</param>
/// <param name="value">Text to be translated.</param>
/// <returns>Translated value of the given text.</returns>
public static string Translate(Language targetLanguage, string text)
{
    string translatedValue = string.Empty;
    try
    {
        string languagePair = string.Format("en|{0}", targetLanguage.Value);
        // Get the translated value.
        translatedValue = TranslateText(text, languagePair);
        Trace.WriteLine(string.Format("Given Text is {0} and Target Language is {1}. Result - {2}.",
            text, targetLanguage.Name, translatedValue));
    }
    catch (Exception ex)
    {
        string errorString = "Exception while translating, please check the connectivity." + ex.Message;
        Trace.WriteLine(errorString);
        throw new WebException(errorString);
    }
    return translatedValue;
}

一旦给定的文本被翻译成目标语言,剩下的工作就是创建一个 *.resx* 文件并将文本添加到该文件中。下面提供的代码执行此任务。

/// <summary>
/// Translates the given resx in the specified language. new language resx
/// file will be created in the same folder and with the same name suffixed with
/// the locale name.
/// </summary>
/// <param name="targetLanguage">Language in which text to be translated.</param>
/// <param name="resxFilePath">Source resx file path</param>
public static void Write(Language targetLanguage, 
       string resxFilePath, bool onlyTextStrings)
{
    if (string.IsNullOrEmpty(resxFilePath))
    {
        throw new ArgumentNullException(resxFilePath, 
          "Resx file path cannot be null or empty");
    }
    if (targetLanguage == null)
    {
        throw new ArgumentNullException(targetLanguage, 
                  "Target Language cannot be null");
    }
    using (ResXResourceReader resourceReader = new ResXResourceReader(resxFilePath))
    {
        //string locale = targetLanguage.ToString().Substring(0, 2).ToLower();
        string locale = targetLanguage.Value.ToLower();
        #region Create locale specific directory.
        //string outputFilePath = Path.Combine(Path.GetDirectoryName(ResxFilePath),
            locale);
        //if (!Directory.Exists(outputFilePath))
        //{
        // Directory.CreateDirectory(outputFilePath);
        //} 
        #endregion
        // Create the required file name with locale.
        string outputFilePath = Path.GetDirectoryName(resxFilePath);
        string outputFileName = Path.GetFileNameWithoutExtension(resxFilePath);
        outputFileName += "." + locale + ".resx";
        outputFilePath = Path.Combine(outputFilePath, outputFileName);
        // Create a resx writer.
        using (ResXResourceWriter resourceWriter = new ResXResourceWriter(outputFilePath))
        {
            foreach (DictionaryEntry entry in resourceReader)
            {
                string key = entry.Key as string;
                // Check if the Key is UI Text element.
                if (!String.IsNullOrEmpty(key))
                {
                    if (onlyTextStrings)
                    {
                        if (!key.EndsWith(".Text"))
                        {
                            continue;
                        }
                    }
                    string value = entry.Value as string;
                    // check for null or empty
                    if (!String.IsNullOrEmpty(value))
                    {
                        // Get the translated value.
                        string translatedValue = Translator.Translate(targetLanguage,
                            value);
                        // add the key value pair.
                        resourceWriter.AddResource(key, translatedValue);
                    }
                }
            }
            // Generate resx file.
            resourceWriter.Generate();
        }
    }
}

由于文本翻译发生在 Google 网站上,因此需要时间来获取文本并获得翻译后的文本。我使用了 BackgroundWorker 类来完成这项工作。BackgroundWorker 通过绑定通道开始该过程,然后发送和接收具有指定语言的文本。

BackgroundWorker 类允许您在独立的专用线程上运行操作。下载和数据库事务等耗时操作在运行时可能会导致您的用户界面(UI)看起来像是停止响应了。当您想要一个响应迅速的 UI 并且面临与此类操作相关的长时间延迟时,BackgroundWorker 类提供了一种便捷的解决方案。

private void myStartButton_Click(object sender, EventArgs e)
{
    *                 
    *            
    myBackgroundWorker.RunWorkerAsync(languages);        
    *
    *
}
void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    Language[] languages = e.Argument as Language[];
    PerformTranslation(languages, worker, e, myOnlyTextCheckBox.Checked);
}

一旦您在 UI 上点击“开始翻译”按钮,BackgroundWorker 就会开始文本翻译。如果您想中止后台进程,请调用 CancelAsync() 方法,然后检查 BackgoundWorker 实例上的 CancellationPending 标志,并将 DoWorkEventArgs - e.Cancel 设置为 true

/// <summary>
/// Perform translation for each selected language for all selected resx files.
/// </summary>
/// <param name="languages">selected languages.</param>
/// <param name="worker">BackgroundWorker instance.</param>
/// <param name="e">DoWorkEventArgs.</param>
/// <param name="onlyTextStrings">true; to convert only '.Text' keys valus.</param>
private void PerformTranslation(Language[] languages, 
        BackgroundWorker worker, DoWorkEventArgs e, bool onlyTextStrings)
{
    int totalProgress = 0;
    foreach (string file in mySelectedFiles)
    {
        foreach (Language targetLanguage in languages)
        {
            if (worker.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                ResxWriter.Write(targetLanguage, file, onlyTextStrings);
                totalProgress++;
                worker.ReportProgress(totalProgress);
            }
        }
    }
}

*.resx* 文件可能有两种类型。当您将 Localizable 属性设置为 true 时,窗体的所有信息(特定于区域性)都会移动到 *.resx* 文件,包括位置、大小等。这是一个自动生成的资源文件,其中包含 UI 文本字符串以外的条目。另一种选择可以是用户定义的资源文件,它只包含字符串。

在 *Resx Translator* 应用程序 UI 中,您可以勾选“仅转换 .Text 键”复选框,以便仅转换自动生成的 *.resx* 文件中的字符串。翻译字符串以外的条目可能会导致一些严重错误。对于用户定义的资源文件,您可以不勾选此选项。

此应用程序包含两个组件 - *Translator* 和 *Config* UI。在 *Translator* UI 中,您可以浏览并选择 *.resx* 文件。在 *Config* UI 中,您可以选择要将英语 *.resx* 文件翻译成的语言。

翻译开始后,进度条和状态栏将显示整个过程。希望这个工具能帮助您生成不同语言的资源文件。眨眼 | ;)

兴趣点 

它直接从网站读取翻译后的数据。您应该有有效的互联网连接才能获取翻译后的数据。

请查看代码以更好地理解翻译/提取过程。

有时在开发阶段,您可能需要验证应用程序是否真正本地化(所有 UI 文本是否都已翻译成目标语言/文化)。您可以通过调整 Translator 类代码来实现这一点。对于特定语言,请勿实际翻译 text,而应返回 反转 text(或添加一个字符作为前缀/后缀)。现在,在以该特定语言加载应用程序后,您可以轻松地验证 UI 中所有翻译后的 texts

历史 

  • 2012年8月23日 - 修改代码以从 Google 翻译网站读取翻译后的数据。
  • 2010年2月8日 - 添加了 API 的详细信息。
  • 2010年2月4日 - 上传了源代码。
  • 2010年2月4日 - 初次发布。
© . All rights reserved.