目录
介绍
背景
实现
Windows
Linux
扩展
感谢...
参考和出处
- 下载Gtk示例应用程序-70.9 KB
- 下载Windows示例应用程序(VS2008)-78.6 KB
- 下载Windows示例应用程序(VS2012)-78.8 KB
GTK3示例
WPF示例
介绍我的大多数Windows程序都是使用WPF/MVVM组合编写的。在本地化方面,我并不是卫星组件的忠实拥护者,因此从一开始,我就选择使用动态资源。当我转向Linux时,我的目标显然是尽可能地(重新)使用我现有的代码。由于我的Windows应用程序使用动态资源,并且以双语(英语和希腊语)编写,因此我已经拥有一堆将资源文件翻译成希腊语的功能,因此我有强烈的动机来利用它们。在本文中,我提出了一种在Gtk3 C#应用程序中重用Windows/WPF资源文件的方法。
背景在本文中,我包括一个小型MVVM Windows WPF应用程序,该应用程序显示了WPF如何使用动态资源以及如何通过代码对其进行访问。简而言之,它是这样的:
资源文件包含键/值对的集合。每种语言都有一个资源文件。用于命名资源文件的约定是“Dictionary.”+特定文化的语言代码+“.xaml”。因此,我的应用程序中的默认资源文件是“Dictionary.en-US.xaml ”,希腊文的文件名是“Dictionary.el-GR.xaml ”。
在XAML文件中,WPF绑定发挥了神奇的作用,您要做的就是将资源键绑定到控件的属性,而不是拥有不可翻译的string文字:
您声明如下内容:
在资源文件中的哪个位置,您拥有:
Hello World
WPF替您代劳。
要启用此功能,请在App.xml中将Resource字典声明为应用程序Resource:
在App.config中,您需要声明应用程序当前将使用的Language。为简单起见,我们使用Language的特定于语言环境的代码:
在App.xml.cs中,事情变得更加有趣,因为在那里,我们还可以通过代码访问动态资源,这是通过以下功能完成的:
///
/// This loads the Dictionary into the Application's Properties
/// IMPORTANT: Call this first, before everything else.
///
private void InitializeLocalizedResources()
{
String szLanguage = ConfigurationManager.AppSettings [ "Language" ];
if ( String.IsNullOrEmpty( szLanguage ) )
szLanguage = "en_US";
ResourceDictionary dict = new ResourceDictionary();
dict.Source =
new Uri( "\\Resources\\Dictionary." + szLanguage + ".xaml", UriKind.Relative );
this.Resources.MergedDictionaries.Add( dict );
// allows us to access it via code
Properties [ szLanguage ] = dict;
}
唯一的限制是在启动时,您必须在其他所有操作之前调用它,因此:
private void OnAppStartup( object sender, StartupEventArgs e )
{
try
{
// Initialize these here *before* everything else!!
InitializeLocalizedResources();
// rest of the code ..
最后,在.cs代码文件中,要从资源中检索string,您需要类似以下内容:
String GetStringFromDynamicResources( String szLang, String szKey )
{
if( Application.Current == null )
return String.Empty;
ResourceDictionary rd = ( ResourceDictionary ) Application.Current.Properties [ szLang ];
return ( rd != null ) ? ( rd [ szKey ] as String ) : String.Empty ;
}
就是这样。
遵循GOF格言“对接口进行编程,而不是对实现进行编程”之后,我们从一个最能描述我们要完成的任务的接口开始:
public interface ITranslator
{
String GetResourceString( String szKey, String szDefault );
}
第二个参数非常有用,因为如果在字典中找不到条目,则默认将其返回给调用方。具有讽刺意味的是,事实证明,创建Translator static类更方便,因此该接口似乎是多余的。但是,它可以帮助我们专注于大局。
在Windows上,我们有一个WPFTranslator类,其中的实现实质上封装了我们上面描述的代码(请参阅随附的示例应用程序),因此,由于本文的重点是Linux版本,所以我将省略其详细信息。
LinuxLinux版本之所以被称为Translator仅仅是因为它根本没有任何特定于Gtk的代码。尽管WPFTranslator可以利用Application的属性来访问字典,但Linux上却没有这种奢侈。这里的“繁重工作”是由Lexicon类完成的,该类包含一个Dictionary键/值对。创建类后,它将从磁盘读取Resource文件,并将在该文件中找到的资源加载到其自己的内存字典中。
protected bool LoadDictionary()
{
bool bRetVal = false;
XmlDocument xDoc = new XmlDocument();
int n = 1;
try
{
xDoc.Load( m_szFileName );
XmlNodeList lst = xDoc.GetElementsByTagName( "sys:String" );
if( lst != null && lst.Count > 0 )
{
foreach( XmlNode node in lst )
{
m_lstDict.Add( node.Attributes[0].Value, node.InnerText );
n++;
}
}
bRetVal = true;
}
catch( Exception )
{
bRetVal = false ;
}
return bRetVal;
}
然后,如果需要的话,它将根据需要向翻译器提供所需的条目。
public String GetResourceString( String szKey, String szDefault )
{
if( String.IsNullOrEmpty( szKey ) )
return szDefault;
try
{
String sz = m_lstDict[ szKey ];
return ( !String.IsNullOrEmpty( sz ) ) ? sz : szDefault;
}
catch
{
return szDefault;
}
}
Windows和Linux版本之间的主要区别在于,尽管Windows上的Dictionary.zzzzz.xaml文件被视为Pages并与所有其他XAML文件一起编译为可执行文件,但在Linux上不会发生。此处的解决方案是将文件复制到输出目录,Lexicon类可在其中找到它们。这样做的一个好处是,您可以对Resource文件进行更改,例如,以纠正输入错误,而不必重新编译可执行文件。
因此,在Linux上:
- 就像在Windows中一样,您可以在App.config文件中定义Language。
- 在项目设置中,标记要复制到输出目录的资源文件。
- 在您的代码中,您调用Translator.GetResourceString( “key”, “Value” )。
在那里,你有它。
请注意:在Linux上,文件名区分大小写,因此任何“找不到文件”错误都可能是由于该原因。
扩展随附的示例程序目前仅适用于英语和希腊语,但也可以与其他多种语言一起使用。
通过更改App.config文件中的“Language”条目,您的应用程序将加载适当的资源。
感谢...首先,对于MVVM“福音主义者”,Josh Smith, Sacha Barber, Jason_Dolinger等人。谁让我们看到了这种模式的巨大优势。
如本文所述,此处提供的示例程序可以将其起源追溯到Josh Smith的示例应用程序。实际上,在Linux示例程序中,您会发现我“借用”了他的SimpleCommand类。
还要特别感谢Mono和MonoDevelop团队使在Linux上运行C#程序以及Gtk#团队的框架成为可能。
多亏了他们,我才能够重用大约80%的代码。:-)
有关示例程序的注释
本文的重点是Linux程序,但是我也包括Windows WPF程序,以便您可以比较不同的实现。您当然会注意到资源文件是相同的,这就是我们想要的。
拥有两个Windows程序的原因基本上是一个函数调用!Visual Studio 2008版本调用,ConfigurationSettings.AppSettings而2012版本调用ConfigurationManager。在准备本文时,我决定不包括两个相同的WPF程序,而是决定修改VS 2012版本以使用ITranslator接口,以说明差异。在2012版本中,WPFTranslator类实现了ITranslator接口,所以没有实现static。因此,此类的用户必须实例化它。请参阅VS 2012项目的App.xaml.cs,MainViewModel.cs和FirstViewModel.cs。您可以从那里看到静态版本使用起来更加方便。
我知道Visual Studio 2008可能已经有12年的历史了,但并不是每个人都有拥有最新版本的特权,我的目标是让这段代码(无论如何它都没有使用c#的最新特性)能够被尽可能多的人使用。
在Linux上,提供的示例应用程序是MonoDevelop解决方案。它需要GtkSharp3软件包。使用MonoDevelop将其打开,然后选择“恢复软件包”,然后再进行编译。
参考和出处可以在此处找到特定于语言环境的语言代码的完整列表。
如前所述,Gtk3应用程序使用的SimpleCommand类来自Sacha Barber's Cinch。
Gtk3应用程序使用的MultiTab控件已从ApprenticeHacker的代码(此处找到)改编而成。
示例应用程序使用的图标来自iconpack研究文件夹(Designer:FixIcon(Greg)),位于此处。
https://www.codeproject.com/Articles/5294456/Localization-in-Gtk3-Csharp-using-WPF-Dynamic-Reso