Search Unity

Question How to load string by key in script?

Discussion in 'Localization Tools' started by syjgin, May 16, 2020.

  1. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    Can not found such guide in dicumentation.
    Currently trying this in such way:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine.Localization;
    6. using UnityEngine.ResourceManagement;
    7.  
    8. namespace UI {
    9.  public class FlyingLogUI: MonoBehaviour {
    10.   [SerializeField]
    11.   private UnityEngine.Localization.Tables.TableReference _tableRef;
    12.   [SerializeField]
    13.   private UnityEngine.Localization.Settings.LocalizedStringDatabase _stringDatabase;
    14.  
    15.   void Awake() {
    16.    StartCoroutine(LoadLocalization());
    17.   }
    18.  
    19.   IEnumerator LoadLocalization() {
    20.    UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle < string > localizedString =
    21.     _stringDatabase.GetLocalizedStringAsync(_tableRef, "test_levels"); //will be obtained dynamically
    22.    yield
    23.    return localizedString;
    24.    if (localizedString.IsDone) {
    25.     Debug.Log(localizedString.Result);
    26.    }
    27.   }
    28.  }
    29. }
    30.  
    But I see several errors like this:

    Failed to extract SmartFormatter instance.
    UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr) (at /home/builduser/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:187)


    and one, whuch related to script itself:

    NullReferenceException: Object reference not set to an instance of an object
    UnityEngine.Localization.Settings.LocalizedDatabase`2[TTable,TEntry].GetTableLoadTable (UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Locale locale) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedDatabase.cs:191)
    UnityEngine.Localization.Settings.LocalizedDatabase`2[TTable,TEntry].GetTableAsync (UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Locale locale) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedDatabase.cs:114)
    UnityEngine.Localization.Settings.LocalizedDatabase`2[TTable,TEntry].GetTableEntryAsync (UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Tables.TableEntryReference tableEntryReference, UnityEngine.Localization.Locale locale) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedDatabase.cs:147)
    UnityEngine.Localization.Settings.LocalizedStringDatabase.GetLocalizedStringAsyncInternal (UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Tables.TableEntryReference tableEntryReference, UnityEngine.Localization.Locale locale, System.Object[] arguments) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedStringDatabase.cs:160)
    UnityEngine.Localization.Settings.LocalizedStringDatabase+<>c__DisplayClass15_0.<GetLocalizedStringAsync>b__0 (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1[TObject] op) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedStringDatabase.cs:153)
    UnityEngine.ResourceManagement.ChainOperation`2[TObject,TObjectDependency].Execute () (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/AsyncOperations/ChainOperation.cs:34)
    UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].InvokeExecute () (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:401)
    UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].<.ctor>b__27_0 (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle o) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:102)
    DelegateList`1[T].Invoke (T res) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/DelegateList.cs:69)
    UnityEngine.Debug:LogException(Exception)
    DelegateList`1:Invoke(AsyncOperationHandle) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/DelegateList.cs:73)
    UnityEngine.Localization.InitializationOperation:<LoadLocales>b__13_0(AsyncOperationHandle)
    DelegateList`1:Invoke(AsyncOperationHandle) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/DelegateList.cs:69)
    UnityEngine.ResourceManagement.ChainOperationTypelessDepedency`1:OnWrappedCompleted(AsyncOperationHandle`1)
    DelegateList`1:Invoke(AsyncOperationHandle`1) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/DelegateList.cs:69)
    UnityEngine.ResourceManagement.ChainOperation`2:OnWrappedCompleted(AsyncOperationHandle`1)
    DelegateList`1:Invoke(AsyncOperationHandle`1) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/DelegateList.cs:69)
    UnityEngine.ResourceManagement.ResourceManager:Update(Single)
    MonoBehaviourCallbackHooks:Update() (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/MonoBehaviourCallbackHooks.cs:19)


    Is it possible for now to load localized string by key in script?
     
  2. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    Table name in reference and default table name in database reference are filled out with existing table name, not empty
     
  3. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Hi.
    Try yielding on
    Code (csharp):
    1. LocalizationSettings.InitializationOperation
    first.
    GetLocalizedStringAsync should do this for you but there's a bug in the current one where it does not.

    Also for your use case I would use a LocalizedString. it wraps up all the work for you and provides a nicer UI
     
  4. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    It's possible to change LocalizedString key in runtime?
    LocalizedReference.TableEntryReference.Key seems to be read only. I want to get string key from eventBus and display localized string (maybe with args) to user
     
  5. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Yes you can change it. Change the value of TableEntryReference directly, not the Key property.
    e.g LocalizedReference.TableEntryReference = "New Key" or LocalizedReference.TableEntryReference = 123; // Key id
     
    FarmerJon likes this.
  6. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine.Localization;
    5. using UnityEngine.ResourceManagement;
    6.  
    7. namespace UI
    8. {
    9.     public class FlyingLogUI : MonoBehaviour
    10.     {
    11.         //TODO: load localized string
    12.         [SerializeField]
    13.         LocalizedString _localizedString;
    14.         void Awake()
    15.         {
    16.             _localizedString.TableEntryReference = "test_levels";
    17.             StartCoroutine(LoadLocalization());
    18.         }
    19.  
    20.         IEnumerator LoadLocalization()
    21.         {
    22.             UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<string> localizedString =
    23.             _localizedString.GetLocalizedString();
    24.             yield
    25.             return localizedString;
    26.             if (localizedString.IsDone)
    27.             {
    28.                 Debug.Log(localizedString.Result);
    29.             }
    30.         }
    31.     }
    32. }
    Updated loading code, but table reference is not recognized:
     

    Attached Files:

  7. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    It's empty in the editor. You need to select a table entry, not just table for a Localized string. Click where it says String(Empty)
     
    FarmerJon likes this.
  8. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    I'm trying to load key dynamically from script, not to set it by hand. I changed key for other existing, but one of errors is still here (was trying to load both "height_is_low" and "UI/height_is_low" keys in script):

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine.Localization;
    5. using UnityEngine.ResourceManagement;
    6.  
    7. namespace UI
    8. {
    9.     public class FlyingLogUI : MonoBehaviour
    10.     {
    11.         //TODO: load localized string
    12.         [SerializeField]
    13.         LocalizedString _localizedString;
    14.         void Awake()
    15.         {
    16.             _localizedString.TableEntryReference = "height_is_low";
    17.             StartCoroutine(LoadLocalization());
    18.         }
    19.  
    20.         IEnumerator LoadLocalization()
    21.         {
    22.             UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<string> localizedString =
    23.             _localizedString.GetLocalizedString();
    24.             yield
    25.             return localizedString;
    26.             if (localizedString.IsDone)
    27.             {
    28.                 Debug.Log(localizedString.Result);
    29.             }
    30.         }
    31.     }
    32. }
     

    Attached Files:

  9. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    Error fired in LocalizedDatabase, line 191
     
  10. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Can you try yielding in LocalisationSettings.InitializationOperation before calling the string? It's a known bug in 0.6.1
     
  11. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    Assets/Scripts/UI/FlyingLogUI.cs(22,30): error CS0246: The type or namespace name 'LocalizationSettings' could not be found (are you missing a using directive or an assembly reference?)

    I have to include this class by some other using directive? https://docs.unity3d.com/Packages/c...Engine.Localization.LocalizationSettings.html It seems to be already included in UnityEngine.Localization
     
  12. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    That's the wrong docs. https://docs.unity3d.com/Packages/c...calization.Settings.LocalizationSettings.html
     
    syjgin likes this.
  13. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    karl_jones likes this.
  14. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    Can't find in documentation how I can get localized string value of table by given key?
    I need to update UI Text by condition.

    something like..
    if(inputWrong)
    {
    messageUI.text = localizationTable.GetValueByKey("WRONG_INPUT");
    }
    else
    {
    messageUI.text = localizationTable.GetValueByKey("RIGHT_INPUT");
    }
     
  15. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Take a look at the samples that come with the package. They can be found in the package manager window, you want the "Loading Strings" ones.
     
  16. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    thanks
     
    karl_jones likes this.
  17. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    I have issue with getting localized value in editor.
    Code (CSharp):
    1. if (stringTable != null)
    2.             {
    3.                 var entry = stringTable.GetEntry(entryName);
    4.                 return entry.GetLocalizedString();
    5.             }
    the thing is on build the value is getting in the right language

    Code (CSharp):
    1.  int langIndex = PlayerPrefs.GetInt(K.PlayerPrefKey.language);
    2.             Debug.Log($"[GameManager] - Application language: {(Language)langIndex}");
    3.             LocalizationSettings.SelectedLocale
    4.                  = LocalizationSettings.AvailableLocales.Locales[langIndex];
    but in editor, it shows in English (which is the first language in locate) and only after switching language in dropdown list on game window,
    Screen Shot 2021-02-24 at 14.48.11.png
    it appears the way it should be.
     
    Last edited: Feb 24, 2021
  18. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Is this in playmode or just Editor without play mode? We only support playmode at the moment, Editor support will be in the next version.

    Also if you are using the latest preview version of Addressables there is a bug with their completion events which is causing the Locales to not be fully loaded. I have reported it to the team.
    I would use 1.16.15 or 1.16.16 if possible
     
  19. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    Yes, play mode. Also, I think it was working fine couple days ago, or maybe haven't noticed then.
    Could you please help, where to look for version of Addressables and how update it?
     
  20. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Windows/Package Manager. Look for Addressable Assets and see what version you are on.
    Another thing to try is to print out the list of Locales in LocalizationSettings.AvailableLocales.Locales to see if they are all there the first time. Its likely some are missing which is the above bug I mentioned. They then get added after the callback you are part of.
     
  21. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    I updated the Addressable version to 1.16.16 and printed out all locales, all locales are available.

    Code (CSharp):
    1.  if (stringTable != null)
    2.             {
    3.                 var entry = stringTable.GetEntry(entryName);
    4.                 foreach (var l in LocalizationSettings.AvailableLocales.Locales)
    5.                 {
    6.                     Debug.Log($"Locale: {l.name}");
    7.                 }
    8.                 return entry.GetLocalizedString();
    9.             }
    Still the same issue.
     
  22. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Is it working now? Can you share the full script?
     
  23. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    On enable of object

    Code (CSharp):
    1. protected LocalizationService localizationService;
    2.  
    3.         private void OnEnable()
    4.         {
    5.             localizationService = LocalizationService.SharedInstance;
    6.  
    7.             StartCoroutine(localizationService.LoadStrings(OnLocalizationLoaded));
    8.             LocalizationSettings.SelectedLocaleChanged += OnSelectedLocaleChanged;
    9.         }
    LocalizationService is singleton

    Code (CSharp):
    1. private StringTable stringTable;
    2. public IEnumerator LoadStrings(Action callback)
    3.         {
    4.             var loadingOperation = LocalizationSettings.StringDatabase.GetTableAsync(localizationTableName);
    5.             yield return loadingOperation;
    6.  
    7.             if (loadingOperation.Status == AsyncOperationStatus.Succeeded)
    8.             {
    9.                 stringTable = loadingOperation.Result;
    10.  
    11.                 callback();
    12.             }
    13.             else
    14.             {
    15.                 Debug.LogError("Could not load String Table\n" + loadingOperation.OperationException.ToString());
    16.             }
    17.         }
    18.  
    19.         public string GetStringFor(string entryName)
    20.         {
    21.             if (stringTable != null)
    22.             {
    23.                 var entry = stringTable.GetEntry(entryName);
    24.                 foreach (var l in LocalizationSettings.AvailableLocales.Locales)
    25.                 {
    26.                     Debug.Log($"Locale: {l.name}");
    27.                 }
    28.                 return entry.GetLocalizedString();
    29.             }
    30.  
    31.             return "<Empty>";
    32.         }
    after everything loaded, I click on button and get localized string

    Code (CSharp):
    1. messageTMP.text = localizationService.GetStringFor(K.LocalizationKey.msgChangeNumber);
     
  24. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    no, it doesn't work.
     
  25. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Im not sure I understand what is going wrong? It finds all the locales, so none are missing. Is the problem that the game starts in English and you want it to start in a different language?

    Is it something to do with this code you posted?
    Code (csharp):
    1.  
    2. [LIST=1]
    3. [*] int langIndex = PlayerPrefs.GetInt(K.PlayerPrefKey.language);
    4. [*]            Debug.Log($"[GameManager] - Application language: {(Language)langIndex}");
    5. [*]            LocalizationSettings.SelectedLocale
    6. [*]                 = LocalizationSettings.AvailableLocales.Locales[langIndex];
    7. [*]
    [/LIST]
    When do you select the language?
     
  26. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    I save language only in one scene (Settings) into PlayerPrefs.

    And in all other scenes I set the value of locale in Start method with next code:
    Code (CSharp):
    1. private IEnumerator Start()
    2.         {
    3.             // Wait for the localization system to initialize
    4.             yield return LocalizationSettings.InitializationOperation;
    5.  
    6.             int langIndex = PlayerPrefs.GetInt(K.PlayerPrefKey.language);
    7.             Debug.Log($"[GameManager] - Application language: {(Language)langIndex}");
    8.             LocalizationSettings.SelectedLocale
    9.                  = LocalizationSettings.AvailableLocales.Locales[langIndex];
    10.         }
    On each scene load I see in log that language are different then English.
    Every text which is set via Inspector works fine but when I set some text via script I get texts on English.
    Again, on build application everything works fine. The issue only on Play mode.
     
  27. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Sorry I'm not sure I fully understand. Are you able to share an example project that shows the problem?
     
  28. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    Something wrong with GetLocalizedString() method.
    Because from below script in log I see the language Russian but the returned string in English.
    Code (CSharp):
    1. public string GetStringFor(string entryName)
    2.         {
    3.             if (stringTable != null)
    4.             {
    5.                 StringTableEntry entry = stringTable.GetEntry(entryName);
    6.                 Debug.Log($"SelectedLocale: {LocalizationSettings.SelectedLocale.name}");
    7.                 return entry.GetLocalizedString();
    8.             }
    9.  
    10.             return "<Empty>";
    11.         }
    I have already described full implementation.
     
  29. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    GetLocalizedString will return the string for the current table, so if the table is English it will always be English. It sounds to me like you may be caching the Table at the start and not updating when the locale changes.
    Try adding a callback to LocalizationSettings.SelectedLocaleChanged. When it is called you need to get the table again as the locale has changed and you now have the wrong table.
     
  30. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    Yes, that was issue. Is there a method to cache table sets of all languages, and fetch text regarding to selected locale? Or I have to cache each language table separately and implement own method of fetching value?
     
  31. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    You could just call directly into the system with LocalozationSettings.StringDatabase.GetLocalizedStrinf. if you mark the tables as preload(in the metadata editor/side panel) then they will be immediately available.

    If you want to cache all table sets you could get table and pass in the locale, that will give you a table for a particular language. So just do a foreach on the locales and call get table for each and put them in a dict.
     
    noio likes this.
  32. EKO_LX

    EKO_LX

    Joined:
    Dec 25, 2020
    Posts:
    45
    Thank you.
     
    karl_jones likes this.
  33. Redmarian

    Redmarian

    Joined:
    Jul 6, 2017
    Posts:
    8
    Hello,
    I'm not sure it is related, but I'm trying to get the String value of the TableEntryReference.Key to add "_txt" after, so I can reuse it in another text reference. I can't seem to be able to access Name, and Key is actually null when I Debug.Log it. If I use ToString() too, and if I use ToString() to TableEntryReference, it just gives me numbers all over the place.

    Right now I have this
    Code (CSharp):
    1. public GameObject detectedObject;
    2. public TextMeshProUGUI EmoDisplayText;
    3.     public TextMeshProUGUI EmoExplainText;
    4.  
    5.     public LocalizeStringEvent emoExplanationStringEvent;
    6.     LocalizedString emoExplainLocalizedString;
    7.     public string keyName = "e_anger"; // I want keyName to go grom "e_anger" to "e_anger_txt", and this vallue is change quite regularly according to
    8.  
    9.     void FixedUpdate()
    10.     {
    11.         //Debug.Log(detectedObject.GetComponent<LocalizeStringEvent>().StringReference.TableEntryReference + " is your table entry reference. I'll try to copy it into " + EmoDisplayText.GetComponent<LocalizeStringEvent>().StringReference.TableEntryReference);
    12.         if (detectedObject.GetComponent<LocalizeStringEvent>() != null) {
    13.             EmoDisplayText.GetComponent<LocalizeStringEvent>().StringReference.TableEntryReference = detectedObject.GetComponent<LocalizeStringEvent>().StringReference.TableEntryReference;
    14. //This part works great, no problem, I'm transfering values from different objects to apply to the EmoDisplayText
    15.  
    16.             emoExplanationStringEvent = EmoExplainText.GetComponent<LocalizeStringEvent>();
    17.  
    18.             Debug.Log(EmoDisplayText.GetComponent<LocalizeStringEvent>().StringReference.TableEntryReference.Key); //could add ToString(), doesn't change anything, returns null
    19.             keyName = EmoDisplayText.GetComponent<LocalizeStringEvent>().StringReference.TableEntryReference.Key + "_txt";
    20.             emoExplanationStringEvent.StringReference.SetReference("EmotionsName", keyName);
    21.         }
    22.     }
    Is there something I'm missing?

    Thak you to you all, great work answering all of us all ofver the place.
     
  34. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Hi,

    The TableEntryReference is basically a helper struct that either contains a Key or Key Id, its not able to convert between them. So if it contains a Key then it wont be able to tell you the Key Id and vice versa, the numbers you get mean that its using the Key Id. You can find out the type by checking its ReferenceType property.

    If you want to get the Key name then that is stored in the SharedTableData.
    So you would need to Load the table and then get it from that.
    For your example you just need to pass in the TableEntryReference into SetReference instead of the Key. This will then handle if it is a Key or Key Id for you.

    If you want to print out the key for debug purposes etc then we do have some support for this. We have a version of ToString that can get the Key name
    https://docs.unity3d.com/Packages/c...ityEngine_Localization_Tables_TableReference_

    Edit: I see you want to modify the key entry. So you would need to get the key. Something like this:
    Code (csharp):
    1. var table = LocalizationSettings.StringDatabase.GetTableAsync(EmoDisplayText.GetComponent<LocalizeStringEvent>().StringReference.TableReference);
    2. var entry = table.Result.SharedData.GetEntryFromReference(EmoDisplayText.GetComponent<LocalizeStringEvent>().StringReference.TableEntryReference);
    3. keyName = entry.Key + "_txt";
     
    Last edited: May 12, 2021
  35. Redmarian

    Redmarian

    Joined:
    Jul 6, 2017
    Posts:
    8
    Thank you for the detailed explanation. I will study the links you gave.
    The code this way works like a charm!

    Thak you very much for your kind help!
     
    karl_jones likes this.