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

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

问题描述

我正在使用 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 以这种方式定义的属性都会更新.按钮、标签、条目属性(占位符等)会按原样更改,但页面标题、工具栏项和其他一些属性仍保留以前的语言.

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.

我认为其中一些是在首次创建页面时填充的,并且不会随着文化(和 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 属性并更改文化.所有绑定到索引的字符串都会更新.

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

}

字符串资源:

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 他们在那里实现了与我的第一种方法类似的方法.也许你可以试一试.


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 在运行时更改 UI 语言 (XAML)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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