Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Enter the 2020.2 Beta Sweepstakes for a chance to win an Oculus Quest 2.
    Dismiss Notice

Scriptable Object Localization

Discussion in 'Localisation Tools Previews' started by Goty-Metal, Aug 10, 2020.

  1. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    So i'm new with the Localization Package (which is super useful by the way), added a dropdown to pick different languages, textmeshpro works like a charm with that.. perfect.
    The problem is that i have a ton of NPCs with different Scriptable Objects which contains the strings of dialog, how can i "Localize" them?

    Thanks in advance!
     
  2. Alexis-Dev

    Alexis-Dev

    Joined:
    Apr 16, 2019
    Posts:
    80
    Hi,

    You could use LocalizeString in Scriptable Objects.
    Localize the TextMeshPro Text (by adding LocalizeStringEvent component).

    And use:
    Instead of:
    Best,
    Alexis
     
    maxwald and karl_jones like this.
  3. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    Thanks Alexis, TMP are working fine, but i can't figure how to use the LocalizedString, i have a list like this:
    Code (CSharp):
    1. List<LocalizedString> phrases
    And BEFORE i just did:
    Code (CSharp):
    1. String phrase = phrases[i];
    But as LocalizedString is not a string anymore i'm not sure how to work with them, can't find any example in the localization package documents.
     
  4. Alexis-Dev

    Alexis-Dev

    Joined:
    Apr 16, 2019
    Posts:
    80
    Hello,

    It's depend of your goal.

    - If you want to localize a string with the current language and don't localize it when language change, you can do something like this:


    I work with task to control the asynchronous function but you can work with coroutine.

    - If you want to localize a string with the current language and localize it when language change, it's more complex...

    I advise you to use TMP_Text and don't use string...

    I hope this answer your question!

    Best,
    Alexis
     
  5. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    This is for a dialog system, so each npc has a scriptable object with a list of phrases (strings), i changed that to a list of LocalizedString, the problem comes when i'm reading the phrases to print them in the UI via TMP, i just want to get the current localization string from the LocalizedString key, but to change the TMP text depending on the dialog index:
    TMP.text = phrases[1];
    (presses next)
    TMP.text = phrases[2];
    (presses next)
    TMP.text = phrases[3];
    ...

    I can no longer do that cause "phrases" now contains LocalizedString instead of string.
     
  6. Alexis-Dev

    Alexis-Dev

    Joined:
    Apr 16, 2019
    Posts:
    80
    Ok!

    you need to change:
    by:
    LocalizeStringEvent is the added component when you localize TMP_Text component in editor.

    Best,
    Alexis
     
    karl_jones likes this.
  7. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    Thanks Alexis, finally i came up with the solution:

    Code (CSharp):
    1. UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<string> localizedText = _conversacion[_indexConversacion].GetLocalizedString();
    2.  
    3. if (localizedText.IsDone)
    4.     this.dialogTextTMP.text = localizedText.Result;
    The only problem is that "isDone" takes a bit of time so i have to re-call the method, i think i should use an IEnumerator to wait for the text to be ready but i'm not 100% sure how, i'm doing some testings... any ideas are welcome!
     
  8. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    Ok so i'm currently calling this method from my MonoBehaviour "Awake()":
    Code (CSharp):
    1. private async Task preloadConversation() {
    2.         for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++) {
    3.             UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<string> localizedText = npcScriptableObject.localizedStringsConversation[i].GetLocalizedString();
    4.  
    5.             while (!localizedText.IsDone)
    6.                 await Task.Delay(10);
    7.  
    8.             conversation.Add(localizedText.Result);
    9.         }
    10. }
    Not sure if this is the best method... i'm bassically loading the strings into a list while the scene loads.

    I've tried this instead but didn't work, don't know why, this one with "StartCorutine()":

    Code (CSharp):
    1. private IEnumerator preloadConversation() {
    2.         for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++) {
    3.             UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<string> localizedText = npcScriptableObject.localizedStringsConversation[i].GetLocalizedString();
    4.             yield return new WaitUntil(() => localizedText.IsDone);
    5.             conversation.Add(localizedText.Result);
    6.         }
    7.  
    8. }
     
  9. Alexis-Dev

    Alexis-Dev

    Joined:
    Apr 16, 2019
    Posts:
    80
    Hi,

    hum try this:
    Code (CSharp):
    1.        
    2. private async Task preloadConversation() {
    3.     for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++)
    4.     {
    5.         string localizedText =
    6.             await npcScriptableObject.localizedStringsConversation[i].GetLocalizedString().Task;
    7.  
    8.         conversation.Add(localizedText);
    9.     }
    10. }
    11.  
    I'm not a coroutine expert, so I will investigate and edit this post if I find something.

    Best,
    Alexis
     
  10. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    4,243
    What do you mean it doesnt work?
    I have had mixed results using Tasks. I find Coroutines to be more reliable
    Code (csharp):
    1.  
    2.         private IEnumerator PreloadConversation()
    3.         {
    4.             for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++)
    5.             {
    6.                 var localizedText = npcScriptableObject.localizedStringsConversation[i].GetLocalizedString();
    7.                 yield return localizedText;
    8.                 conversation.Add(localizedText.Result);
    9.             }
    10.         }
    To make the above faster you could call GetLocalizedString on all strings and then yield on them. if you check the IsDone flag then this should also help as you wont need to yield an extra frame when the string is already loaded
     
  11. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    Thanks or your reply, unfortunately i'm getting this:

    ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: index
    System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource) (at <fb001e01371b4adca20013e0ac763896>:0)
    System.ThrowHelper.ThrowArgumentOutOfRangeException () (at <fb001e01371b4adca20013e0ac763896>:0)
    System.Collections.Generic.List`1[T].get_Item (System.Int32 index) (at <fb001e01371b4adca20013e0ac763896>:0)
    DialogManager_SC.MostrarFraseConversacion () (at Assets/Scripts...

    When trying to retrieve the string from the list, in other words, is not preloading the strings.

    So THIS works:
    Code (CSharp):
    1.  
    2. void Awake() {
    3.         if (npcScriptableObject != null)
    4.             PreloadConversation();
    5. }
    6.  
    7. private async Task PreloadConversation() {
    8.         for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++) {
    9.             UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<string> localizedText = npcScriptableObject.localizedStringsConversation[i].GetLocalizedString();
    10.  
    11.             while (!localizedText.IsDone)
    12.                 await Task.Delay(10);
    13.  
    14.             conversation.Add(localizedText.Result);
    15.         }
    16. }

    But this DOESN'T:
    Code (CSharp):
    1.  
    2. void Awake() {
    3.         if (npcScriptableObject != null)
    4.            StartCoroutine(PreloadConversation());
    5. }
    6. private IEnumerator PreloadConversation() {
    7.     for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++) {
    8.         var localizedText = npcScriptableObject.localizedStringsConversation[i].GetLocalizedString();
    9.         yield return localizedText;
    10.         conversation.Add(localizedText.Result);
    11.     }
    12. }
     
  12. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    4,243
    What does the full script look like?
     
  13. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    It's a bit complex, it's a dialog system, so the basics are:
    Awake -> Preload
    On collision enter 2d -> launch dialog, which starts on index 0
    Hit interact button -> show next index

    With the "IEnumerator" method the list is empty, is not preloading anything, so when you try to access any of the indexes it throws that error.

    I did a bit of debug:

    Code (CSharp):
    1.  
    2. private async Task PreloadConversation() {
    3.         for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++) {
    4.             UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<string> localizedText = npcScriptableObject.localizedStringsConversation[i].GetLocalizedString();
    5.             Debug.Log("Preloading = " + i);
    6.             while (!localizedText.IsDone)
    7.                 await Task.Delay(10);
    8.  
    9.             Debug.Log("Preloaded correctly = " + localizedText.Result);
    10.             conversation.Add(localizedText.Result);
    11.         }
    12. }
    And got in console:
    - Preloading = 0
    - Preloaded correctly = Hi my name is Mike...
    - Preloading = 1
    - Preloaded correctly = ...

    So it's loading the strings as i already know because the NPC dialogs are working and beight shown.

    But with this:

    Code (CSharp):
    1. private IEnumerator PreloadConversation() {
    2.     for (int i = 0; i < npcScriptableObject.localizedStringsConversation.Count; i++) {
    3.         var localizedText = npcScriptableObject.localizedStringsConversation[i].GetLocalizedString();
    4.         Debug.Log("Preloading = " + i);
    5.         yield return localizedText;
    6.         conversation.Add(localizedText.Result);
    7.         Debug.Log("Preloaded correctly = " + localizedText.Result);
    8.     }
    9. }
    I only got:
    - Preloading = 0 (one per scriptable object).

    I think it could be because it's inside a loop? don't know, but it never reaches the part in which i get the result.
     
  14. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    It's just a way to preload ALL strings from ALL tables in the game start so i can access them instantly while in runtime? thanks!
     
  15. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    4,243
    Did you start the function with StartCouroutine?
    Try StartCoroutine(PreloadConversation());
     
  16. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    I did it on awake()

    Code (CSharp):
    1. void Awake() {
    2.     StartCoroutine(PreloadConversation());
    3. }
     
  17. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    4,243
    Does that work?
     
  18. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    Nope, that's how i did it the first time (comment #11) it only shows "Preloading = 0 (one per scriptable object)." and it never loads the string.

    Isn't just a method to preload all tables/strings so i can access them instantly in runtime?
     
  19. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    4,243
    What's the code that sets up localizedStringsConversation look like?
    There is no immediate preloading because we need to wait for the addressable asset system to load the asset bundles and this updates in LateUpdate so will be at least 1 frame.
     
  20. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    It's assigned in the inspector, so i pick every scriptable object localizedStrings from inspector 1 by 1.
     
  21. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    4,243
    Are you waiting for the preloading coroutine to finish before you try and use the localized strings? I think you may be trying to access them in the same frame. You need to wait for them all, then maybe set a flag to indicate the loading is finished.
     
  22. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    I can wait an entire minute if you want, they never load like with the task method, really i don't know why, maybe because it's inside a loop, don't worry, if the task one works is OK =D
     
  23. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    4,243
    Hmm one thing it could be is that they are already done. Try checking the IsDone flag and only yield if it is false.
     
  24. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    Again index error, it's not loading with corutines.
    By the way just found that you can preload tables by ticking the "Preload Table" or "Preload All Tables" checkbox, but i'm doing some testings and some entries are loaded and others are NOT, this is really weird, i'm starting to think that 0.7.1 is bugged someway...
     
  25. Goty-Metal

    Goty-Metal

    Joined:
    Apr 4, 2020
    Posts:
    99
    So i've updated to 0.8.0 preview and preloading tables works (with the checkbox), so it was a bug on 0.7.1, i'm goign to use that method so i don't have to preload the strings manually, thanks for the help!
     
    karl_jones likes this.
unityunity