Update language translation on the fly using TranslateExtension

JonAlzaJonAlza ESMember ✭✭

Following this guide I'm trying to localize my app. I don't want to get system language. I have 3 resx files for the 3 languages that I want to support. I'm able to set the current culture at start and the strings are displayed correctly.

My goal is to change the language while you are using the app. TranslateExtension class is in MyApp.Resources PCL library and this is my page to select the language.

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:vm="clr-namespace:MyApp"
                 xmlns:t="clr-namespace:MyApp.Resources;assembly=MyApp.Resources"
                 x:Class="MyApp.Pages.LanguagePage"
                 BindingContext="{Binding Source={x:Static vm:App.Locator}, Path=Language}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <ListView x:Name="contactsListView" Grid.Row="0" ItemsSource="{Binding Languages}" SelectedItem="{Binding SelectedLanguage}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell Text="{Binding Value}" Detail="{Binding Key}"  />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Button Grid.Row="1" Text="{t:Translate Accept}" Clicked="OkButtonClicked" Command="{Binding SaveLanguageCommand}"/>
        </Grid>
    </ContentPage>

When a item in the ListView is selected the accept button should change the text (this is what I want). How can I do that?

Tagged:

Answers

  • RaymondKellyRaymondKelly USMember ✭✭✭

    If I understand you correctly, you can force a language from the main XF project. In this example I am asking the platform specific code to return the currently language/culture. But you can just hard code one of the 3 you are trying to use. You do this in the App.cs constructor.

    public App() { var l = DependencyService.Get<ILocalize>(); if (l != null) { var netLanguage = l.GetCurrentCultureInfo(); AppResources.Culture = netLanguage; } }

  • JonAlzaJonAlza ESMember ✭✭

    Thank you for your reply @RaymondKelly , I think that I don't explain very well my goal. I will try to explain it again. I have English, Spanish and French resx files and the TranslationExtension class. I can use this code to initialize the language (it works):

    public App ()
    {
        AppResources.Culture = new CultureInfo("es"); // I can change among "en", "es" and "fr"
        this.MainPage = new NavigationPage(new TestPage());
    }
    

    But this only works to initialize the language. I want to change the language after the page is displayed:

    public App ()
    {
        AppResources.Culture = new CultureInfo("es");
        this.MainPage = new NavigationPage(new TestPage());
        AppResources.Culture = new CultureInfo("en");
        // At this point the texts are diplayed in Spanish, not in English
    }
    

    How can I refresh de page to display English texts without doing new TestPage() again?

  • RaymondKellyRaymondKelly USMember ✭✭✭

    You can try SetLocale. I only have an Android example at this time. It sets the current UI thread to the given language.

    public void SetLocale () { var androidLocale = Java.Util.Locale.Default; // user's preferred locale var netLocale = androidLocale.ToString().Replace ("_", "-"); if (netLocale == "iw-IL" || netLocale == "iw") { netLocale = "he"; } var ci = new System.Globalization.CultureInfo (netLocale); Thread.CurrentThread.CurrentCulture = ci; Thread.CurrentThread.CurrentUICulture = ci; }

  • JonAlzaJonAlza ESMember ✭✭

    @RaymondKelly said:
    You can try SetLocale. I only have an Android example at this time. It sets the current UI thread to the given language.

    public void SetLocale () { var androidLocale = Java.Util.Locale.Default; // user's preferred locale var netLocale = androidLocale.ToString().Replace ("_", "-"); if (netLocale == "iw-IL" || netLocale == "iw") { netLocale = "he"; } var ci = new System.Globalization.CultureInfo (netLocale); Thread.CurrentThread.CurrentCulture = ci; Thread.CurrentThread.CurrentUICulture = ci; }

    This doesn't work.

  • bdebde USMember

    Hello,

    I try to do the same thing.
    Have you found a solution?

    Thanks

  • JonAlzaJonAlza ESMember ✭✭

    No, I didn't...

  • batmacibatmaci DEMember ✭✭✭✭✭

    I also cant find a way to refresh main page without exiting and re-entering the app. Have you found something ?

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭

    If you set the Text hardcoded with

    Text="{t:Translate Accept}"
    

    then the expression will execute just once and when you change the locale, nothing will happen.

    You could bind the Text to your ViewModel and send a message when the locale is changed. All translated strings would need to listen to that message, re-run the TranslateExtension and trigger PropertyChanged for the bound text.

  • batmacibatmaci DEMember ✭✭✭✭✭

    @MichaelRumpler is it not expensive to implement like that if text is static ? and if you have many of such texts

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭

    @batmaci

    The text isn't static. It changes when the user selects a different language. Yes, it is a lot more effort for the developer if you want all texts to be dynamic. The user won't change the language very often, so I don't think, that he will recognize any lag.

    You could also ask the user for the language right at the start of the app with no choice to change it afterwards. Then you could leave the code for all following pages as it is.
    Another option would be, to save the language to some settings and use the saved language. Then the user would have to restart the app.

  • JuanPabloSillerJuanPabloSiller USMember ✭✭
    edited July 2017

    Hi,

    I was able to implement changing language on the fly by sending a message to the App class whenever the language changes. Then in the App replace the MainPage with a new page resulting on all the labels translating again. It works great, the only problem is that depending on your logic you might not want to reload the pages again.

    Anyway, I had to do this because previously I had a message stating "Requires application reboot" but apple rejected my binary because of this so keep this in mind.

    Basically I did something like this:
    Messenger.Default.Register<PropertyChangedMessage>(
    this,
    (args) =>
    {
    if (args.OldValue != null && args.OldValue.Name != args.NewValue.Name)
    {
    SaveSettings();
    Messenger.Default.Send(new NotificationMessage(""), "CleanUpView");
    Messenger.Default.Send(new NotificationMessage(""), "ReloadApp");
    }
    }
    );

    And then in my App.cs class this:
    Messenger.Default.Register<NotificationMessage>(this, "ReloadApp", ReloadApp);

    And finally the method does this:

    private void ReloadApp(NotificationMessage message)
            {
                MainPage = new MainPage();
            }
    

    Keep in mind I'm ussing Mvvm framework with messaging functionality. The "CleanUpView" message calls a method on each view to remove any registered messages. This is just to remove any messages that could cause a memory leak or duplicate in the message service before creating the whole page again. My MainPage is a tabbed page so it ends up recreating every view again and registering the messages again.

    ViewModels are static so no need to do anything there since they are not recreated.

    Hope this helps, using the Translate extension is a good way to localize and for me having to do it using the view model approach just for translations seems too much.

    Regards

  • SamiAlAwadhiSamiAlAwadhi KWMember

    Here is something that worked for me while using TrasnslateExtension and wanting to change the language on the fly. Basically I had a button on the main menu page to change the language. Once clicked all the labels on this page would switch to another language.

    In my App.xaml.cs I am using the App properties to store the default language. I also created a method for Reloading the app, in which I sent the new language in the properties and then reloading the page.

    public App()
            {
                InitializeComponent();
    
                Application.Current.Properties["currentLanguage"] = "ar";
                MainPage = new MainPage();
            }
    
            public void ReloadApp(string language)
            {
                Application.Current.Properties["currentLanguage"] = language;
                MainPage = new MainPage();
            }
    

    Then in my button event, I simply call the ReloadApp method with the language parameter. I only switch between two languages, so the logic is pretty simple.

    void OnLanguageButtonClicked(Object sender, EventArgs EventArgs)
            {
                var currentLanguage = App.Current.Properties["currentLanguage"] as string;
    
                if (currentLanguage == "ar")
                {
                    ((App)Application.Current).ReloadApp("en");
                }
                else
                {
                    ((App)Application.Current).ReloadApp("ar");
                }
            }
    

    The XAML code would look like this:

    <Button x:Name="Button1"  Text="{i18n:Translate Label1}" />
    <Button x:Name="Button2"  Text="{i18n:Translate Label2}" />
    <Button x:Name="Button3"  Text="{i18n:Translate Label3}" />
    <Button x:Name="Button4"  Clicked="OnLanguageButtonClicked" Text="{i18n:Translate Label4}" />
    

    Button4 would be the one that changes the language.

    Finally, I had to change the TranslateExtension slightly. Instead of getting the CultureInformation from the system, I get it from the stored App Properties.

    In the constructor

    public TranslateExtension()
            {
                ci = new CultureInfo(Application.Current.Properties["currentLanguage"] as string);
            }
    

    and in the ProvideValue method

    public object ProvideValue(IServiceProvider serviceProvider)
            {
                if (Text == null)
                {
                    return "";
                }
    
                if (ci.TwoLetterISOLanguageName != Application.Current.Properties["currentLanguage"] as string)
                {
                    ci = new CultureInfo(Application.Current.Properties["currentLanguage"] as string);
                }
    
                var translation = ResMgr.Value.GetString(Text, ci);
    
                if (translation == null)
                {
                    translation = Text; // returns the key, which GETS DISPLAYED TO THE USER
                }
                return translation;
            }
    
  • DmitryBlackDmitryBlack RUMember ✭✭
    edited September 2017

    @SamiAlAwadhi said:
    Here is something that worked for me while using TrasnslateExtension and wanting to change the language on the fly. ...

    Unfortunately, the proposed method does not work "on the fly". MainPage is recreated, but still in the same language with which the application was launched. To update the application language, you have to restart it.

  • batmacibatmaci DEMember ✭✭✭✭✭

    @SamiAlAwadhi your proposal is theoretically nicely implemented but I am afraid to reset mainpage because xamarin forms can be unecpted. what you think is not usually not what you get. it may create 2 mainpage instances in the memory. talking from my experience and spent weeks, months of memory profiling, these things are happening believe me. please profile your memory to ensure that unexpected not happening.

  • Your ideas are very good! But I know what you do not have to find a solution-rest. Try to order yourself a https: //escortcracow.info, relax with her and the decision will come with itself, I guarantee!

  • surafeldsurafeld Member

    You can insert the following code in App.Xaml.cs file which create new instance of your app
    static App _instance;
    public static App Instance
    {
    get
    {
    return _instance ?? (_instance = new App());
    }
    }
    then,
    DependencyService.Get().SetLocale(new CultureInfo("en"));
    App.Current.MainPage = new MainPage();
    This will change the app language then refreshes the page with closing the application.
    :)

Sign In or Register to comment.