我是WPF的新手,但想要使用XAML中定义的UI构建一个Windows应用程序,该应用程序从一开始就支持本地化。不仅需要本地化的UI元素,而且还有许多驻留在数据库中的内容。
我已经设置了一个数据库结构,以便能够保存本地化信息,但我不知道如何在选择的语言环境中显示它。 " EN-US"或者" de-DE"。
如何以对WPF / XAML自然的方式向XAML UI元素提供本地化文本?我不想要依赖任何第三方代码,例如WPFLocalizationExtension。
我已经看到了如何使用资源文件,但仅支持已知UI元素的本地化,而不是动态生成的内容。
我应该实施一个特定的提供者,还是我错过了一些完全不同的东西?
答案 0 :(得分:0)
我最终解决此问题的方法如下。记住,我需要从数据库/远程api中提取本地化的文本。
使用此解决方案,我可以像这样进行数据绑定,并且当我更改语言时,所有绑定的文本都会自动更改:
<Label Content="{Binding Path=Strings.ErrorLevelColumnHeaderLabel}"/>
当然,Strings
对象必须可从所有数据上下文访问。
字符串存储在一个如下所示的数据库表中,一个ID列和每种支持的语言的一列:
ID en da de
24 'Level' 'Niveau' 'Stufe'
我创建了一个UIStringsVM
类,该类实现了INotifyPropertyChanged
接口。在我的示例中,我已经在名为Observable
的基类中实现了这一点,我敢肯定,还有许多其他基类。 See this answer for details。
public class UIStringsVM : Observable
{
public static UIStringsVM CurrentStringsInstance;
private bool stringsAreLoading = false;
private Dictionary<int, string> stringDictionary;
}
UIStringsVM
类为我需要本地化的每个字符串都有一个属性。由于该类通过基类支持INotifyPropertyChanged
接口,因此我可以依赖于语言更改时将更改反映在UI中。
在UIStringsVM
类中,当前语言的字符串存储在Dictionary<int, string>
中。原因是我可以使用数据库中的字符串ID来访问正确的字符串。
现在,我可以使用属性Get方法中的ID返回该值存储的任何字符串。因此,属性将如下所示:
public string ErrorLevelColumnHeaderLabel
{
get =>
this.stringDictionary[24].Replace("\\n", Environment.NewLine);
private set =>
this.stringDictionary[24] = value;
}
这些属性永远不会单独设置,因此可以忽略设置器。
构造函数:
public UIStringsVM()
{
this.stringDictionary = new Dictionary<int, string>();
// Initialize with default values. The ! at the end makes it easier to identify missing values in the database.
this.LoginButtonText = "Login!";
this.LogoutButtonText = "Logout!";
this.UserLabelFormatString = "{0} logged in!";
this.ErrorLevelColumnHeaderLabel = "Level!";
UIStringsVM.CurrentStringsInstance = this;
}
为了加载字符串,我使用以下方法:
public async Task LoadStringsAsync(string languageCode, CancellationToken ct)
{
if (languageCode.Length != 2)
throw new ArgumentException("languageCode must be exactly 2 characters.", nameof(languageCode));
this.StringsAreLoading = true;
var client = new UIStringsClient(AppVM.BaseURL);
try
{
var apiStrings = await client.GetByLanguageAsync(languageCode, ct);
foreach (var r in apiStrings)
{
/* Note: this will make it impossible to overwrite a string with an empty string from the database,
* thus always keeping the string defined in this class' constructor. However strings will always
* have a value as defined in the constructor even if one does not exist in the database.
* */
if (string.IsNullOrWhiteSpace(r.Value))
continue;
this.stringDictionary[r.Key] = r.Value;
}
this.OnPropertyChanged((string)null); // Raise a change event for the entire object, not just a named property
}
finally
{
this.StringsAreLoading = false;
}
}
我希望这对可能碰到这个最新答案的人有所帮助。我已经运行了15个月左右的解决方案,并且与它一起工作真的很棒。