xaml - Xamarin.Forms 在运行时更改 UI 语言 (XAML)

标签 xaml localization xamarin.forms

我正在使用 Strings.resx , Strings.de.resx等本地化 Xamarin.Forms应用程序。
我需要能够在运行时更改界面语言,并且它(几乎)可以工作。

Xamarin 生成 static class Stringsnamespace MyProject.Resources来自资源文件,我使用这些值在 UI 上显示字符串。
从代码中执行此操作时,它可以完美运行:

await DisplayAlert(Strings.lblConfirmDelete, Strings.lblDeleteMessage, Strings.lblOK, Strings.lblCancel));

问题是 - 当我在运行时更改 UI 文化时,并非所有从 XAML 以这种方式定义的属性都会更新。
按钮、标签、条目属性(占位符等)应按原样更改,但 PageTitle、Toolbaritems 和其他一些属性仍保留在以前的语言中。

我认为其中一些是在第一次创建 Page 时填充的,并且不会随着文化(和 UI 文化)的变化而更新。
所以,基本上,我需要一种方法来组合 {DynamicResource ...}来自资源的值(value)。
我知道DynamicResource与资源字典一起使用,但这不是存储语言翻译以进行本地化的好方法。

我试过
Text="{DynamicResource {x:Static lr:Strings.lblAddNew}}"

也不工作。

有没有办法动态刷新页面?

我也试过打电话
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(MainListPage));

来自该页面的出现事件,但这也不起作用。

有任何想法吗?

XAML 文件的一部分
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MyProject.View"
    xmlns:rs="clr-namespace:MMPI"
    x:Class="MyProject.MainListPage"
    xmlns:lr="clr-namespace:MyProject.Resources"

    Title="{x:Static lr:Strings.appName}"
    >

<ContentPage.ToolbarItems>
    <ToolbarItem 
     Name="New" 
     Order="Primary" 
     Priority="0"
     Text="{x:Static lr:Strings.lblAddNew}" 
     Clicked="New_Clicked"
>

最佳答案

当我在一个项目中遇到这个挑战时,我使用一个简单的类 ResourceLoader 解决了它。并利用 INotifyPropertyChanged .

您可以访问 Instance来自任何地方的属性(property)并改变文化。所有绑定(bind)到索引的字符串都会更新。
ResourceManager注入(inject)构造函数的实例必须正确设置。

public class ResourceLoader : INotifyPropertyChanged
{
    private readonly ResourceManager manager;
    private CultureInfo cultureInfo;

    public ResourceLoader(ResourceManager resourceManager)
    {
        this.manager = resourceManager;
        Instance = this;
        this.cultureInfo = CultureInfo.CurrentUICulture;
    }

    public static ResourceLoader Instance { get; private set; }

    public string GetString(string resourceName)
    {
        string stringRes = this.manager.GetString(resourceName, this.cultureInfo);
        return stringRes;
    }

    public string this[string key] => this.GetString(key);

    public void SetCultureInfo(CultureInfo cultureInfo)
    {
        this.cultureInfo = cultureInfo;
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

要在应用程序中显示本地化字符串,您需要通过索引器绑定(bind),如下所示:
<Label Text="{Binding [Test], Source={x:Static ResourceLoader.Instance}}" />

由于它现在已绑定(bind),因此应在您调用 ResourceLoader.SetCultureInfo 时更新因为 Item[] “PropertyName”导致绑定(bind)控件将值重新获取到其绑定(bind)键。

更新

如果我在说假话,我只是对其进行了测试,并且由于某种原因,更改的属性不起作用。我在下面添加了一种不同的方法,它与我在生产中使用的接近 我敦促您添加某种弱引用“缓存”,而不是包含所有字符串资源的简单列表 (否则它们将被永久保存)

我保留在上面以供引用。

public class ResourceLoader
{

    public ResourceLoader(ResourceManager resourceManager)
    {
        this.manager = resourceManager;
        Instance = this;
        this.cultureInfo = CultureInfo.CurrentUICulture;
    }

    private readonly ResourceManager manager;
    private CultureInfo cultureInfo;

    private readonly List<StringResource> resources = new List<StringResource>();

    public static ResourceLoader Instance { get; private set; }

    public StringResource this[string key] {
        get { return this.GetString(key); }
    }

    public StringResource GetString(string resourceName)
    {
        string stringRes = this.manager.GetString(resourceName, this.cultureInfo);
        var stringResource = new StringResource(resourceName, stringRes);
        this.resources.Add(stringResource);
        return stringResource;
    }

    public void SetCultureInfo(CultureInfo cultureInfo)
    {
        this.cultureInfo = cultureInfo;
        foreach (StringResource stringResource in this.resources) {
            stringResource.Value = this.manager.GetString(stringResource.Key, cultureInfo);
        }
    }

}

字符串资源:

public class StringResource : INotifyPropertyChanged
{

    public StringResource(string key, string value)
    {
        this.Key = key;
        this.Value = value;
    }

    private string value;

    public string Key { get; }

    public string Value {
        get { return this.value; }
        set {
            this.value = value;
            this.OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

XAML 绑定(bind)
<Label  Text="{Binding [ResourceKey].Value, Mode=OneWay, Source={x:Static local:ResourceLoader.Instance}}" 
           />

更新 2

碰到了this link他们实现它的方式与我的第一种方法类似。也许你可以试一试。

更新 3

修正了第一种方法。两人现在都在工作。需要的是this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));而不是 this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Item[]));

关于xaml - Xamarin.Forms 在运行时更改 UI 语言 (XAML),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44410407/

相关文章:

Xamarin Forms-强制控件刷新绑定(bind)值

c# - 使用 MVVM 相对于 WPF 中图像的鼠标位置

android - 允许用户选择应用程序语言/语言环境?

ios - 导出到 xliff 时,Xcode6 不导出 header 中定义的 NSLocalizedStrings()

asp.net - 将静态数据存储在数据库或文件系统中

c# - Datepicker 不会在 xamarin.forms 中的默认日期选择时触发 - Android

c# - 可以在样式内的 XAML 中使用绑定(bind)吗?

c# - 此 View 列表不允许编辑项目

C#:System.Windows.Controls.UserControl 作为列表框

xamarin - 在RelativeLayout Xamarin Forms中将 View 与底部和顶部对齐