我最终解决这个问题的方法如下。请记住,我需要从数据库/远程 api 中提取本地化文本。
有了这个解决方案,我可以像这样进行数据绑定,当我更改语言时,所有绑定的文本都会自动更改:
<Label Content="{Binding Path=Strings.ErrorLevelColumnHeaderLabel}"/>
当然,Strings 对象必须可以从所有数据上下文中访问。
字符串存储在如下所示的数据库表中,每个支持的语言都有一个 ID 列和一个列:
ID en da de
24 'Level' 'Niveau' 'Stufe'
我创建了一个实现INotifyPropertyChanged 接口的UIStringsVM 类。在我的示例中,我在一个名为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 个月左右,使用它真的很棒。