2
votes

We are developing several mobile apps, with Common .NET Standard library between them, which holds the common functionallity. (MVVM) The Common project has a TranslationManager Class, and a Resource file, which holds the common translations. TranslationManager uses constructor injection, to inject the app specific translation resources.

    public TranslationManager(ResourceManager appSpecificLanguageResources)
    {
        _commonResources = CommonTranslationResources.ResourceManager;
        _appSpecificLanguageResources = appSpecificLanguageResources;
    }

With this code, we earn the possibilty to use common translations, and application specific translations with using only one Translation provider.

            if (string.IsNullOrWhiteSpace(translationKey))
                return null;
            string commonTranslation = _commonResources.GetString(translationKey, new CultureInfo(_preferenceCache.CultureName));
            string appSpecificTranslation = _appSpecificLanguageResources.GetString(translationKey, new CultureInfo(_preferenceCache.CultureName));
            if (commonTranslation == null && appSpecificTranslation == null)
            {
                MobileLogger.Instance.LogWarning($"Translate could not found by translationKey: {translationKey}");
                return $"TRANSLATION_{translationKey}";
            }
            if (!string.IsNullOrWhiteSpace(commonTranslation) && !string.IsNullOrWhiteSpace(appSpecificTranslation))
            {
                MobileLogger.Instance.LogDebug(TAG, $"Warning! Duplicate translate found for '{translationKey}' translationkey. Common translate is : '{commonTranslation}' , AppSpecific Translation is: {appSpecificTranslation}. Returning with appspecific translation.");
                return appSpecificTranslation;
            }
            if (commonTranslation == null)
                return appSpecificTranslation;
            else
                return commonTranslation;

In XAML, we have one MarkupExtension which provides the translation for the current language.

public class TranslateMarkupExtension : IMarkupExtension
{
    public TranslateMarkupExtension()
    {

    }

    public string TranslationKey { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        if (string.IsNullOrWhiteSpace(TranslationKey)) return "nullref";
        return Resolver.Resolve<TranslationManager>().GetTranslationByKeyForCurrentCulture(TranslationKey);
    }
}

XAML Usage seem to be like:

  Entry Placeholder="{extensions:TranslateMarkup TranslationKey=PlaceholderPhoneNumber}"

The problem is, when i set the language at runtime, the translation extension markup does not evaluate the new translation.

Raising propertychanged with null parameter refreshes the bindings on the view, but does not affect MarkupExtensions.

I do not want to push the same page to the navigation stack, it seems patchwork for me.

2
You can try to call InitializeComponent() whenever you change the language at runtime. this will call the your extension again...and translation text will be reflected. - Hamid Shaikh
Sadly, you can not call initalizecomponent from outside. - Bandi Tóth
well you can take advantage of MessagingService provided by xamarin forms. - Hamid Shaikh

2 Answers

0
votes

The problem is, when i set the language at runtime, the translation extension markup does not evaluate the new translation.

you may need to use INotifyPropertychanged interface for TranslationManager,when you change UI culture, all string that are bound to the index would update.

More detailed info, please refer to:

Xamarin.Forms change UI language at runtime (XAML)

0
votes
public class TranslateExtension : IMarkupExtension<BindingBase>
{       
    public TranslateExtension(string text)
    {
        Text = text;            
    }

    public string Text { get; set; }

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

    public BindingBase ProvideValue(IServiceProvider serviceProvider)
    {
        var binding = new Binding
        {
            Mode = BindingMode.OneWay,
            Path = $"[{Text}]",
                Source = Translator.Instance,
        };
    return binding;
    }        
}

and this the Translator class as initially proposed, but reproduced here for clarity with the GetString call:

public class Translator : INotifyPropertyChanged
{
    public string this[string text]
    {
    get
    {
        return Strings.ResourceManager.GetString(text, Strings.Culture);
    }
    }        

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

    public event PropertyChangedEventHandler PropertyChanged;

    public void Invalidate()
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
    }
}

Binding text with:

{i18n:Translate Label_Text}

To trigger the update of languages you just then need to call:

Translator.Instance.Invalidate()

Solution from: https://forums.xamarin.com/discussion/82458/binding-indexername-and-binding-providevalue-in-xamarin-forms