使用动态内容本地化WPF

时间:2017-10-31 10:14:07

标签: c# wpf xaml localization

我是WPF的新手,但想要使用XAML中定义的UI构建一个Windows应用程序,该应用程序从一开始就支持本地化。不仅需要本地化的UI元素,而且还有许多驻留在数据库中的内容

我已经设置了一个数据库结构,以便能够保存本地化信息,但我不知道如何在选择的语言环境中显示它。 " EN-US"或者" de-DE"。

如何以对WPF / XAML自然的方式向XAML UI元素提供本地化文本?想要依赖任何第三方代码,例如WPFLocalizationExtension

我已经看到了如何使用资源文件,但支持已知UI元素的本地化,而不是动态生成的内容。

我应该实施一个特定的提供者,还是我错过了一些完全不同的东西?

1 个答案:

答案 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个月左右的解决方案,并且与它一起工作真的很棒。