最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Localization with binding - Stack Overflow

programmeradmin1浏览0评论

I have TranslateExtension:

[ContentProperty(nameof(Name))]
public class TranslateExtension : IMarkupExtension<BindingBase>
{
    public required string Name { get; set; }
    public BindingBase ProvideValue(IServiceProvider serviceProvider)
    {
        return new Binding
        {
            Mode = BindingMode.OneWay,
            Path = $"[{Name}]",
            Source = LocalizationResourceManager.Instance
        };
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return ProvideValue(serviceProvider);
    }
}

and LocalizationResourceManager

public class LocalizationResourceManager : INotifyPropertyChanged
{
    private LocalizationResourceManager()
    {
        Strings.Culture = CultureInfo.GetCultureInfo("en");
    }

    public static LocalizationResourceManager Instance { get; } = new();

    public object this[string resourceKey]
        => Strings.ResourceManager.GetObject(resourceKey, Strings.Culture) ??
        Array.Empty<byte>();

    public event PropertyChangedEventHandler? PropertyChanged;

    public string GetString(string resourceKey, CultureInfo? culture)
    {
        if (culture == null)
        {
            return Strings.ResourceManager.GetString(resourceKey, Strings.Culture)!;
        }
        return Strings.ResourceManager.GetString(resourceKey, culture)!;
    }

    public void SetCulture(CultureInfo culture)
    {
        Strings.Culture = culture;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
    }
}

and it works fine with this <Label Text={extension:Translate MY_WORD_TO_TRANSLATE}> But MY_WORD_TO_TRANSLATE should based on variable in viewmodel. I mean there are many possible implementation with MY_WORD_TO_TRANSLATE it could be a MY_WORD_TO_TRANSLATE_1, MY_WORD_TO_TRANSLATE_2 and so on, and I wonder how to handle it when MY_WORD_TO_TRANSLATE is not hardcoded, it can change. I am looking for a solution like <Label Text={extension:Translate Name={Binding Word}} where Word is in viewmodel.

I have TranslateExtension:

[ContentProperty(nameof(Name))]
public class TranslateExtension : IMarkupExtension<BindingBase>
{
    public required string Name { get; set; }
    public BindingBase ProvideValue(IServiceProvider serviceProvider)
    {
        return new Binding
        {
            Mode = BindingMode.OneWay,
            Path = $"[{Name}]",
            Source = LocalizationResourceManager.Instance
        };
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return ProvideValue(serviceProvider);
    }
}

and LocalizationResourceManager

public class LocalizationResourceManager : INotifyPropertyChanged
{
    private LocalizationResourceManager()
    {
        Strings.Culture = CultureInfo.GetCultureInfo("en");
    }

    public static LocalizationResourceManager Instance { get; } = new();

    public object this[string resourceKey]
        => Strings.ResourceManager.GetObject(resourceKey, Strings.Culture) ??
        Array.Empty<byte>();

    public event PropertyChangedEventHandler? PropertyChanged;

    public string GetString(string resourceKey, CultureInfo? culture)
    {
        if (culture == null)
        {
            return Strings.ResourceManager.GetString(resourceKey, Strings.Culture)!;
        }
        return Strings.ResourceManager.GetString(resourceKey, culture)!;
    }

    public void SetCulture(CultureInfo culture)
    {
        Strings.Culture = culture;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
    }
}

and it works fine with this <Label Text={extension:Translate MY_WORD_TO_TRANSLATE}> But MY_WORD_TO_TRANSLATE should based on variable in viewmodel. I mean there are many possible implementation with MY_WORD_TO_TRANSLATE it could be a MY_WORD_TO_TRANSLATE_1, MY_WORD_TO_TRANSLATE_2 and so on, and I wonder how to handle it when MY_WORD_TO_TRANSLATE is not hardcoded, it can change. I am looking for a solution like <Label Text={extension:Translate Name={Binding Word}} where Word is in viewmodel.

Share Improve this question asked Mar 6 at 13:23 infinitesimalinfinitesimal 4527 silver badges21 bronze badges 1
  • 1 Property in ViewModel could simply provide translated text. You will need to monitor for language changes and rise notification for all such properties. – Sinatr Commented Mar 6 at 13:28
Add a comment  | 

2 Answers 2

Reset to default 1

If you look at recent contributions I made to Gerard Versluis TranslateExtension (https://github/jfversluis/MauiLocalizationSample/blob/main/MauiLocalizationSample/TranslateExtension.cs) we see Name is now a BindableProperty so that it can come from a static string (like before) or from your view model. In addition, it supports X0, X1.

The only caveat is that when you use BindableObject in a markup extension, it will not have access to the visual tree so it's necessary to reset your BindingContext again, e.g.

<Label Text="{i18n:TranslateExtension
                 BindingContext={Reference your_view_model}
                 x:DataType=your_view_model_type
                 Name={Binding NameFromYourViewModel}}" />
[ContentProperty(nameof(Name))]
public class TranslateExtension : BindableObject, IMarkupExtension<BindingBase> {
    public static BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(TranslateExtension), null,
            propertyChanged: (b, o, n) => ((TranslateExtension)b).OnTranslatedNameChanged());
    public string Name
    {
        get => (string)GetValue(NameProperty);
        set => SetValue(NameProperty, value);
    }

    public static BindableProperty X0Property = BindableProperty.Create(nameof(X0), typeof(object), typeof(TranslateExtension), null,
            propertyChanged: (b, o, n) => ((TranslateExtension)b).OnTranslatedNameChanged());
    public object X0
    {
        get => GetValue(X0Property);
        set => SetValue(X0Property, value);
    }

    public static BindableProperty X1Property = BindableProperty.Create(nameof(X1), typeof(object), typeof(TranslateExtension), null,
            propertyChanged: (b, o, n) => ((TranslateExtension)b).OnTranslatedNameChanged());
    public object X1
    {
        get => GetValue(X1Property);
        set => SetValue(X1Property, value);
    }

    public string? TranslatedName
        => (Name is string name && LocalizationResourceManager.Instance[name] is string translatedName)
            ? String.Format(translatedName, new object?[] { X0, X1 })
            : null;

    public void OnTranslatedNameChanged() => OnPropertyChanged(nameof(TranslatedName));

    public TranslateExtension()
    {
        LocalizationResourceManager.Instance.PropertyChanged += (s, e) => OnTranslatedNameChanged();
    }

    public BindingBase ProvideValue(IServiceProvider serviceProvider)
        => BindingBase.Create<TranslateExtension, string?>(
            static source => source.TranslatedName,
            mode: BindingMode.OneWay,
            source: this
        );

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
        => ProvideValue(serviceProvider);
}

Thanks to Stephen Quan answer I managed this, but my label should looks like:

<Label Text="{extension:Translate BindingContext={Binding Path=BindingContext, Source={x:Reference MyContainerName}, x:DataType=myViewModel:MyViewModel}, 
        Name={Binding MyName}}"/>
发布评论

评论列表(0)

  1. 暂无评论