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

时间:2017-06-07 10:42:05

标签: xaml localization xamarin.forms

我正在使用Strings.resxStrings.de.resx等来本地化Xamarin.Forms应用。 我需要能够在运行时更改界面语言,并且它(最重要的)都能正常工作。

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

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

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

我认为其中一些是在首次创建页面时填充的,并且未在文化(和UI文化)更改时更新。 所以,基本上,我需要一种方法将{DynamicResource ...}与资源中的值结合起来。 我知道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"
>

3 个答案:

答案 0 :(得分:4)

当我在项目中遇到这个挑战时,我通过使用简单的类ResourceLoader并使用INotifyPropertyChanged来解决它。

您可以从任何地方访问Instance属性并更改文化。绑定到索引的所有String都将更新。

必须正确设置注入构造函数的ResourceManager实例。

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;
}

要在应用程序中显示本地化字符串,您需要通过索引器进行绑定,如下所示:

<Label Text="{Binding [Test], Source={x:Static ResourceLoader.Instance}}" />

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

更新

我刚刚测试了它,如果我说的是假的,并且由于某种原因,属性改变不起作用。我在下面添加了一个不同的方法,这与我在生产中使用的方法很接近我建议您添加某种弱引用'缓存'而不是包含所有字符串资源的简单列表 (否则他们将永远保留)

我一直在上面作为参考。

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);
        }
    }

}

StringResource:

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绑定

<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[]));

答案 1 :(得分:0)

我使用ur代码进行第一种方法并成功更改标签文本。我正在使用Xamarin表单主页详细信息页面。在我的母版页中,我有一个列表视图,当我设置文化信息时,列表视图内容没有根据语言改变。

更新:列表视图项目源必须进行通知更改,因此列表视图将更新。

答案 2 :(得分:0)

我解决的非常类似于@woelliJ。我只是想从静态类中获得作为强类型的键,并且绑定应该在后面的代码中。

ITranslationService 是来自静态变量的单例。非常接近@woelliJ。

[ContentProperty("Text")]
public sealed class TranslateExtension : IMarkupExtension<BindingBase>
{
    private readonly ITranslationService? _translationService;

    public TranslateExtension()
    {
        _translationService = Mobile.App.TranslationService;
    }

    public string? Text { get; set; }

    public BindingBase ProvideValue(IServiceProvider serviceProvider)
    {
        var translationItem = _translationService[Text];

        var binding = new Binding
        {
            Mode = BindingMode.OneWay,
            Path = $"Value",
            Source = translationItem,
        };

        return binding;
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return (this as IMarkupExtension<BindingBase>).ProvideValue(serviceProvider);
    }
}

[AddINotifyPropertyChangedInterface]
public class TranslationItem
{
    public string? Key { get; set; }
    public string? Value { get; set; }
}

然后标签会是这样的

<Label FontSize="Title" Text="{services:Translate Text={x:Static models:M.AboutTestInfoTitle}}" />