我正在使用Strings.resx
,Strings.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));
来自该页面的“显示”事件,但这也不起作用。
有什么想法吗?
<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"
>
答案 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}}"
/>
遇到了this link他们在那里实施的方式与我的第一种方法类似。也许你可以尝试一下。
修正了第一种方法。两者现在都在运作。需要的是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}}" />