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






4.75/5 (46投票s)
一个 .NET 资源 (.resx 文件) 翻译器。英语翻译成任何其他语言。
引言
除了应用程序的默认语言(通常是英语)之外,您的软件还应该支持不同的语言,因为人们更喜欢使用带有母语界面的软件。为了在全球范围内分发应用程序,您需要将用户界面翻译成尽可能多的语言。当您这样做时,可以说该应用程序是全球化的。
使应用程序全球化的第一步是将 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日 - 初次发布。