Change Locale in runtime

Hi Karl!

First of all, congrats for the development of this plugin.

This is a very useful tool for translation of a game in an efficient way.

I have a doubt. I have an application that has only one string table. When the app starts, I initialize the Localization Settings and then, I initialize the table in an Async Way.

Then, I store the table in a class property.

StringTable table;

IEnumerator Start()
{
        yield return LocalizationSettings.InitializationOperation;
        LocalizationSettings.SelectedLocaleChanged += changedLocale;

        var tableOp = LocalizationSettings.StringDatabase.GetTableAsync("StringCollection");
        yield return tableOp;

        print("Initialized StringCollectionTable");

        table = tableOp.Result;
}

Then, when I get a string, I use the wrapper method

public string getString(string code){           
        return table.GetEntry(code)?.GetLocalizedString()??$"[[{code}]]";
}

And I created a method that change the locale

public void setSelectedLocale(Locale locale){
        LocalizationSettings.SelectedLocale = locale;
}

Now, I’m testing the method in a button, but always returns the text in the default language.

Manager.localization.setSelectedLocale(locales.First(x=>x.name == "English (en)"));
print(Manager.localization.getSelectedLocale());
print(Manager.localization.getString("Build"));

Am i to refresh the table after change the locale? ¿Are there other more efficient way to get the translated string?

Thanks!!

Yes, if you change Locale then you also need to load the new table. Each Locale has its own table.
Have a look at the Samples which can be imported through the package manager window. We have some examples for loading strings.
LocalizedString is a class that provides a simple api to get a string. You can hook into an event that will be called when a new string is loaded.

If you want the table then LocalizedStringTable is a similiar class but for StringTables.

Great!

Now I’m using

return new LocalizedString() { TableReference = "StringCollection", TableEntryReference = code }?.GetLocalizedString().Result??$"[[{code}]]";

and it works!

Great job!

Thanks

1 Like

Hmm. How often does this code get called?
Generally, you want to hang onto the LocalizedString, that is safe to keep even when the language changes.

Hi!

I’m testing the complete use cycle, and I have question.

The workflow is the following

I have a LocalizationManager, that encapsulate all the functionality.

First, in Start(), I write

IEnumerator Start()
        {
            yield return LocalizationSettings.InitializationOperation;

            LocalizationSettings.SelectedLocaleChanged += changedLocale;

            stringCollectionTable.TableReference = "StringCollection";
}

I have a class property

LocalizedStringTable stringCollectionTable = new LocalizedStringTable();

Then, I have methods for getting localized string and change locale

        public string getString(string code){    

            return new LocalizedString(){TableReference = stringCollectionTable.TableReference, TableEntryReference = code}
                ?.GetLocalizedString().Result;
}
        public void setSelectedLocale(Locale locale){
            LocalizationSettings.SelectedLocale = locale;
        }

In the game, I make four test buttons.

1 - Change Locale to Spanish
2 - Change locale to English
3 - Get string without params
4 - Get string with params

When I start the application, and press button to get a String, I receive a null response. But when I press the same button another time, I get the string translated to the default Language

Then I press the button to change the Locale to English. When I press the button to get a String, I receive a null response. But when I press the same button again, I get the string translated to English.

Then, If I change the languaje and get this string or another string, all string is received in a correct way.

What I’m doing wrong?

And another question abot the performance. Is correct to call getLocalizedString() every time I need a string? Or is better to store in a Key-Value structure the result of getLocalizedString result for each Locale?

Thank you very much!!!

Hi,

Is the String Table marked as preload? If it’s not that may explain why you have the null response.
Mark the table as preload and it will be loaded during InitializationOperation. You will also need to yield on
InitializationOperation again whenever the language is changed.

Here are some different ways you can try

using System.Collections;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;

public class Example1 : MonoBehaviour
{
    StringTable m_Table;

    public string GetString(string code)
    {
        var entry = m_Table.GetEntry(code);
        return entry?.GetLocalizedString();
    }

    IEnumerator Start()
    {
        LocalizationSettings.SelectedLocaleChanged += locale => StartCoroutine(LoadTable());
        yield return LoadTable();
    }

    IEnumerator LoadTable()
    {
        var tableOp = LocalizationSettings.StringDatabase.GetTableAsync("StringCollection");
        yield return tableOp;

        m_Table = tableOp.Result;
    }
}

public class Example2 : MonoBehaviour
{
    IEnumerator Start()
    {
        yield return LocalizationSettings.InitializationOperation;
    }

    public string GetString(string code)
    {
        return LocalizationSettings.StringDatabase.GetLocalizedStringAsync("StringCollection", code).Result;
    }
}

public class Example3 : MonoBehaviour
{
    LocalizedStringTable m_LocalizedStringDatabase;
    StringTable m_Table;

    void Start()
    {
        m_LocalizedStringDatabase.RegisterChangeHandler(table => m_Table = table);
    }

    public string GetString(string code)
    {
        return m_Table.GetEntry(code)?.GetLocalizedString();
    }
}

Why do you need a LocalizationManager? The Localization package is already a LocalizationManager so having an extra manager on top seems strange :wink:

It should be much simpler to deal directly with the system. For example if you wanted some localized text you could just do this:

using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.UI;

public class TextExample : MonoBehaviour
{
    public Text text;
    public LocalizedString localizedString = new LocalizedString();

    void Start()
    {
        // This event will be called whenever the locale is changed
        localizedString.RegisterChangeHandler(localizedText => text.text = localizedText);
    }
}

If you want to extract string table values for multiple items from a single table then do something like this.

using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.UI;

public class TextExample : MonoBehaviour
{
    public Text text1;
    public Text text2;
    public Text text3;
    public LocalizedStringTable localizedStringTable = new LocalizedStringTable();

    void Start()
    {
        // This event will be called whenever the locale is changed
        localizedStringTable.RegisterChangeHandler(table =>
        {
            text1.text = table.GetEntry("Entry 1").GetLocalizedString();
            text2.text = table.GetEntry("Entry 2").GetLocalizedString();
            text3.text = table.GetEntry("Entry 3").GetLocalizedString();
        });
    }
}

These are the ideal way to handle static strings as you dont need to deal with the loading/yielding etc and just assign the string when its ready. If you have dynamic strings then you can make more direct calls to get an updated version.

1 Like

Hi Karl

Thanks for the response!

You are right, another manager over the existing manager doesn’t make sense

In my case, I’m developing a RTS game that has a GUI whith fixed texts and variable texts (The available actions depends of the selcted type of character)

With the fixed texts (Food, Wood, Stone, Menu items, etc) I will use the localization component.

About the variable text, I will use the RegisterChangeHandler.

It seems a very comfortable way to keep the game translated with little effort and in real time

When I will develop this part, I will give you feedback

Thanks for the help!

1 Like

Hi Karl,

It works!

I like this way to translate strings because only is calculated when game start and when change Location

void Start(){
    changedLocaleEvent(null);
    LocalizationSettings.SelectedLocaleChanged += changedLocaleEvent;
}

private void changedLocaleEvent(Locale locale){
    print("Changed locale to " + locale.name);
    StartCoroutine(onChangedLocale(locale));
}

private IEnumerator onChangedLocale(Locale locale){
    var loadingOperation = LocalizationSettings.StringDatabase.GetTableAsync("StringTableCollection");
    yield return loadingOperation;
         
    if (loadingOperation.Status == AsyncOperationStatus.Succeeded){
        StringTable table = loadingOperation.Result;
        string locString = table.GetEntry("EntryKey")?.GetLocalizedString();
    }else{
        print("Could not load String Table\n" + loadingOperation.OperationException.ToString());
    }
}

Thank you very much for the help! :smile::smile::smile:

3 Likes

Thank you very much too. This thread and final script helped me a lot to arrive at functional version to change strings after locale change.

1 Like