Xamarin.Forms在运行时(XAML)更改UI语言 [英] Xamarin.Forms change UI language at runtime (XAML)

查看:70
本文介绍了Xamarin.Forms在运行时(XAML)更改UI语言的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using Strings.resx, Strings.de.resx, etc. to localize Xamarin.Forms app. I need to be able to change interface language at run time, and it (allmost) works.

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

Xamarin generates static class Strings in namespace MyProject.Resources from resource files, and I use those values to display strings on UI. When doing it from code, it works flawlessly:

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

问题是-当我在运行时更改UI区域性时,并非从XAML以此方式定义的所有属性都会更新. 按钮,标签,条目属性(占位符等)会按需更改,但PageTitle,Toolbaritems和其他一些属性仍保持先前的语言.

Problem is - not all attributes defined this way from XAML are updated when I change UI culture during runtime. Buttons, Labels, Entry properties (Placeholder etc.) change as they should, but PageTitle, Toolbaritems, and some other properties remain in previous language.

我认为其中一些是在首次创建Page时填充的,并且不会随着文化(和UI文化)的变化而更新. 因此,基本上,我需要一种将{DynamicResource ...}与资源值结合起来的方法. 我知道DynamicResource是与Resource词典一起使用的,但是这不是存储用于本地化的语言翻译的好方法.

I presume that some of these are populated when Page is first created, and are not updated on culture (and UI culture) change. So, basically, I need a way to combine {DynamicResource ...} with values from resources. I know that DynamicResource is ment to be used with Resource dictionary, but that is not a good way to store language translations for localization.

我尝试了

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

也不起作用.

有没有一种动态刷新页面的方法?

Is there a way of refreshing page dynamicaly?

我也尝试打电话给

global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(MainListPage));

从该页面的出现事件"开始,但这也不起作用.

from Appearing event for that page, but that also does not work.

有什么想法吗?

<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来解决了该问题.

When i encountered that challenge in a project I resolved it by using a simple class ResourceLoader and making use of INotifyPropertyChanged.

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

You can access the Instanceproperty from anywhere and change the culture. All String that are bound to the index would update.

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

The ResourceManager instance injected into the constructor must be set up appropriately.

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


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


To display the localized strings in your application you need to bind through the indexer like so:

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

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

Since it is now bound it should update when you call ResourceLoader.SetCultureInfo because the Item[] 'PropertyName' is causing bound controls to re-fetch the values to their bound keys.

如果我说的是伪造品,我只是对其进行了测试,并且由于某种原因,财产变更无法正常进行.我在下面添加了另一种方法,该方法与我在生产环境中使用的方法很接近.我敦促您添加某种弱引用缓存",而不是简单的包含所有字符串资源的列表 (否则它们将被永久保存)

I just tested it if i was talking bogus and for some reason the property changed didn't work. I've added a different approach below, which is close to what i'm using in production i urge you to add some kind of weak reference 'caching' instead of the simple list holding all the string resources (otherwise they will be kept forever)

我一直在上面供参考.

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:

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

此链接,他们在其中实现了与我的第一种方法类似的方法.也许您可以尝试一下.


Update 2

Came across this link where they implemented it similarly to my first approach. Maybe you can give it a try.

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

Fixed the first approach. Both are working now. What was needed was this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null)); instead of this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Item[]));

这篇关于Xamarin.Forms在运行时(XAML)更改UI语言的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆