Search Unity

Localizating strings on script

Discussion in 'Localization Tools' started by Thor-Apps, Mar 15, 2020.

  1. Thor-Apps

    Thor-Apps

    Joined:
    Dec 13, 2013
    Posts:
    32
    Hi,
    I'm developing a game with multi-language support.
    I'm able to correctly do the localization of buttons and text label in the UI, but now, I'll new to localizate some text via script as they are error messages shown on a unique text label.

    How to do it? How do I get the localizated value (text) of a known localizated key in c#?

    I tried with that code:

    Code (CSharp):
    1.                     LocalizedString localized = new LocalizedString();
    2.                     localized.TableReference = "Texts";
    3.                     localized.TableEntryReference = "USERNAME-ALREADY-EXISTS";
    4.                     Debug.Log(localized.ToString());
    5.                     Debug.Log(localized.GetLocalizedString() );
    but do not works I get that:

    Code (Boo):
    1. [TableReference(Texts)]TableEntryReference(USERNAME-ALREADY-EXISTS)
    2. UnityEngine.Debug:Log(Object)
    3.  
    4. UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
    5. UnityEngine.Debug:Log(Object)
    Thanks
     
  2. karl_jones

    karl_jones

    Unity Technologies

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

    localized.GetLocalizedString() is correct however this returns a AsyncOperationHandle<string>.
    You would then either:

    • Yield on the operation in a coroutine
    • Check if `IsDone` is true and use `Result` if it is. If the StringTable is already loaded then isDone should be true.
    • Use the `Completed` event to wait for the string to be ready.
    Instead of using a LoclizedString you can also just call directly into the Localization system to get the same AsyncOperation,

    Code (csharp):
    1. var op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync("Texts", "USERNAME-ALREADY-EXISTS");
    2. if (op.IsDone)
    3.    Debug.Log(op.Result);
    4. else
    5.    op.Completed += (op) => Debug.Log(op.Result);
     
    Last edited: Mar 15, 2020
  3. Thor-Apps

    Thor-Apps

    Joined:
    Dec 13, 2013
    Posts:
    32
    Thanks! That worked!
     
    karl_jones likes this.
  4. Flx24

    Flx24

    Joined:
    Sep 19, 2015
    Posts:
    6
    Hi,
    I tried to use the code above in a method:
    Code (CSharp):
    1.     public void BtnPressed()
    2.     {
    3.         var op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync("MyStrings", "Text1");
    4.         if (op.IsDone)
    5.             Debug.Log(op.Result);
    6.         else
    7.             op.Completed += (op) => Debug.Log(op.Result);
    8.     }
    and get an error:
    Assets\ButtonPressed.cs(28,30): error CS0136: A local or parameter named 'op' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter
    How can I solve this?
    Thanks
     
  5. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    It's because you declare another variable called op.

    Use a different name in (op) =>

    E.g

    Code (csharp):
    1. op.Completed += (o) => Debug.Log(o.Result);
     
    D12294 and schlenger like this.
  6. Flx24

    Flx24

    Joined:
    Sep 19, 2015
    Posts:
    6
    Works fine . Thanks!
     
    karl_jones likes this.
  7. onejgordon

    onejgordon

    Joined:
    Jul 17, 2018
    Posts:
    6
    I'm using the above method to get a localized string, which is working for standard strings, but failing for Smart Strings.

    Code (JavaScript):
    1. FormattingException: Error parsing format string: Could not evaluate the selector "nCorrect" at 1
    2. {nCorrect} problems solved correctly (out of {nSolved})
    3. -^
    My code:

    Code (CSharp):
    1.    public static void setLocalText(string key, Text textComponent, IList<object> args) {
    2.      AsyncOperationHandle<string> op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync(key, args);
    3.      if (op.IsDone) {
    4.       textComponent.text = op.Result;
    5.      } else {
    6.       op.Completed += (o) => textComponent.text = o.Result;
    7.      }
    8.    }
    I've tried passing in args as a list of integers, strings, and as a single lookup Dictionary<string, string>, as I saw in another post, but all throw this formatting error. The table editor (attached) seems to correctly interpret the format string, so I'm not sure what I'm doing wrong.

    Appreciate any help!
     

    Attached Files:

  8. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    What is nCorrect? Is it a key in a dictionary or a field in a class instance?
    Can you show how you pass the arguments in and what they are?
     
  9. onejgordon

    onejgordon

    Joined:
    Jul 17, 2018
    Posts:
    6
    Sorry, I should have provided more complete code.

    Here are two ways I've tried calling the utility method above.

    As list of ints:

    Code (CSharp):
    1.  
    2. int n_correct = 0;
    3. int n_solved = 0;
    4. setLocalText("solved_status", this.progressText.GetComponent<Text>(), new List<object> {n_correct, n_solved});
    5.  
    As dictionary (passed as single member of object list):

    Code (CSharp):
    1.  
    2. Dictionary<string, string> dict = new Dictionary<string, string>();
    3. int n_correct = 0;
    4. int n_solved = 0;
    5. dict["nCorrect"] = n_correct.ToString();
    6. dict["nSolved"] = n_solved.ToString();
    7. setLocalText("solved_status", this.progressText.GetComponent<Text>(), new List<object> {dict});
    8.  
     
  10. karl_jones

    karl_jones

    Unity Technologies

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

    For your first example, it won't work because we can't resolve the names of the variables when passed in like that.
    You would need to use a normal index for this usage.
    E.G {0}, {1} etc.
    Or you would need to pass in an instance to a class that had a property or field with the names.

    The 2nd example should work and does for me. Check that you have a Dictionary Source added in the Localization Settings under String Database. Also your dictionary can be <string,object>, that way we can format the values instead of having them as strings. E.G if you want currency formatting {nCorrect:C} etc
    Screen Shot 2021-05-04 at 17.09.05.png
     
  11. onejgordon

    onejgordon

    Joined:
    Jul 17, 2018
    Posts:
    6
    Hm, I switched to Dictionary<string, object>, and pass ints, but still get an error "evaluating the selector".

    Confirmed that my localization settings contain Dictionary Source, as the second item, like your screenshot.

    Full stack trace below. So, based on your answer, it seems like the formatter isn't able to satisfy the selectors in the localized string with the objects being passed to GetLocalizedStringAsync, is that correct? And just to confirm, it's correct to pass my args dictionary within a 1-item List<object>?

    Code (csharp):
    1.  
    2. FormattingException: Error parsing format string: Could not evaluate the selector "nCorrect" at 1
    3. {nCorrect} problems solved correctly (out of {nSolved})
    4. -^
    5. UnityEngine.Localization.SmartFormat.SmartFormatter.FormatError (UnityEngine.Localization.SmartFormat.Core.Parsing.FormatItem errorItem, System.Exception innerException, System.Int32 startIndex, UnityEngine.Localization.SmartFormat.Core.Formatting.FormattingInfo formattingInfo) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Smart Format/SmartFormatter.cs:310)
    6. UnityEngine.Localization.SmartFormat.SmartFormatter.Format (UnityEngine.Localization.SmartFormat.Core.Formatting.FormattingInfo formattingInfo) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Smart Format/SmartFormatter.cs:282)
    7. UnityEngine.Localization.SmartFormat.SmartFormatter.Format (UnityEngine.Localization.SmartFormat.Core.Formatting.FormatDetails formatDetails, UnityEngine.Localization.SmartFormat.Core.Parsing.Format format, System.Object current) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Smart Format/SmartFormatter.cs:251)
    8. UnityEngine.Localization.SmartFormat.SmartFormatter.FormatWithCache (UnityEngine.Localization.SmartFormat.Core.Formatting.FormatCache& cache, System.String format, System.IFormatProvider formatProvider, System.Collections.Generic.IList`1[T] args) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Smart Format/SmartFormatter.cs:222)
    9. UnityEngine.Localization.Tables.StringTableEntry.GetLocalizedString (System.IFormatProvider formatProvider, System.Collections.Generic.IList`1[T] args, UnityEngine.Localization.Pseudo.PseudoLocale pseudoLocale) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Tables/StringTable.cs:143)
    10. UnityEngine.Localization.Settings.LocalizedStringDatabase.GenerateLocalizedString (UnityEngine.Localization.Tables.StringTable table, UnityEngine.Localization.Tables.StringTableEntry entry, UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Tables.TableEntryReference tableEntryReference, UnityEngine.Localization.Locale locale, System.Collections.Generic.IList`1[T] arguments) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Settings/Database/LocalizedStringDatabase.cs:136)
    11. UnityEngine.Localization.GetLocalizedStringOperation.Execute () (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Operations/GetLocalizedStringOperation.cs:49)
    12. UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].InvokeExecute () (at Library/PackageCache/com.unity.addressables@1.18.2/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:549)
    13. UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].Start (UnityEngine.ResourceManagement.ResourceManager rm, UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle dependency, DelegateList`1[T] updateCallbacks) (at Library/PackageCache/com.unity.addressables@1.18.2/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:544)
    14. UnityEngine.ResourceManagement.ResourceManager.StartOperation[TObject] (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject] operation, UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle dependency) (at Library/PackageCache/com.unity.addressables@1.18.2/Runtime/ResourceManager/ResourceManager.cs:440)
    15. UnityEngine.Localization.Settings.LocalizedStringDatabase.GetLocalizedStringAsync (UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Tables.TableEntryReference tableEntryReference, System.Collections.Generic.IList`1[T] arguments, UnityEngine.Localization.Locale locale, UnityEngine.Localization.Settings.FallbackBehavior fallbackBehavior) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Settings/Database/LocalizedStringDatabase.cs:125)
    16. UnityEngine.Localization.Settings.LocalizedStringDatabase.GetLocalizedStringAsync (UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Tables.TableEntryReference tableEntryReference, UnityEngine.Localization.Locale locale, UnityEngine.Localization.Settings.FallbackBehavior fallbackBehavior, System.Object[] arguments) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Settings/Database/LocalizedStringDatabase.cs:104)
    17. UnityEngine.Localization.Settings.LocalizedStringDatabase.GetLocalizedStringAsync (UnityEngine.Localization.Tables.TableEntryReference tableEntryReference, System.Collections.Generic.IList`1[T] arguments, UnityEngine.Localization.Locale locale, UnityEngine.Localization.Settings.FallbackBehavior fallbackBehavior) (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Settings/Database/LocalizedStringDatabase.cs:87)
    18. Util.setLocalText (System.String key, UnityEngine.UI.Text textComponent, System.Collections.Generic.IList`1[T] args) (at Assets/Scripts/Util.cs:11)
    19. ReplayRunner.ListProblems () (at Assets/Scripts/ReplayRunner.cs:106)
    20. ReplayRunner.HasUserFinishedExperiment () (at Assets/Scripts/ReplayRunner.cs:58)
    21. ReplayRunner.Start () (at Assets/Scripts/ReplayRunner.cs:37)
    22.  
     
  12. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Are you able to share your project or an example project? I think ill need to take a look and see whats going wrong.
     
  13. onejgordon

    onejgordon

    Joined:
    Jul 17, 2018
    Posts:
    6
    Here's a basic project with nothing but a text box and localization setup via script, that on my machine, reproduces the error I'm getting.
     

    Attached Files:

  14. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    I see. Its a bug. the List gets sent into a params object[] args method later on and so you end up with an array with a list with a dict inside.
    Ill make a bug. For now you can fix this by using the version of GetLocalizedString that also takes a table name.
    Like so:

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6. using UnityEngine.ResourceManagement.AsyncOperations;
    7. using UnityEngine.Localization.Settings;
    8.  
    9.  
    10.  
    11. public class TestLocalization : MonoBehaviour
    12. {
    13.     public GameObject textBox;
    14.  
    15.     // Start is called before the first frame update
    16.     void Start()
    17.     {
    18.         Dictionary<string, object> dict = new Dictionary<string, object>();
    19.         int n_correct = 2;
    20.         int n_solved = 4;
    21.         dict["nCorrect"] = n_correct;
    22.         dict["nSolved"] = n_solved;
    23.         setLocalText("test", textBox.GetComponent<Text>(), new List<object>{ dict });
    24.     }
    25.  
    26.     // Update is called once per frame
    27.     void Update()
    28.     {
    29.        
    30.     }
    31.  
    32.     void setLocalText(string key, Text textComponent, IList<object> args) {
    33.         AsyncOperationHandle<string> op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync("Main", key, args);
    34.         if (op.IsDone) {
    35.             textComponent.text = op.Result;
    36.         } else {
    37.             op.Completed += (o) => textComponent.text = o.Result;
    38.         }
    39.     }
    40. }
    41.  
     
  15. onejgordon

    onejgordon

    Joined:
    Jul 17, 2018
    Posts:
    6
    Excellent. Thanks so much for your quick support!
     
    karl_jones likes this.
  16. Lagio0

    Lagio0

    Joined:
    May 8, 2021
    Posts:
    2
    Hello all ,

    i'm facing a strange issue that I don't understand.
    I'm working on an app using unity 2021.3.4f1,
    I've installed the localization package 1.3.2 using package manager.
    It work perfectly for static string in the ui like buttons and stuff. But now i'm trying to use this system for dynamic string in some of my c# scripts.

    My problem is that the namespace is not recognized. and so I can't use this system in my scripts.

    I have followed the quick start for Unity 2020.3+ here :
    https://docs.unity3d.com/Packages/com.unity.localization@1.3/manual/QuickStartGuideWithVariants.html

    but when i try to follow the scripting manual here :
    https://docs.unity3d.com/Packages/com.unity.localization@1.3/manual/Scripting.html

    The namespace is not recogniezed :
    upload_2023-2-1_10-49-11.png

    Here is my manifest,json :
    upload_2023-2-1_10-49-52.png

    So i just don't know where could be the problem...
    Please does someone have a solution for that :(

    I've tryed to remove, re install the package, i've tryed to import the examples samples, but no change
     
  17. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Are your scripts inside of an assembly definition (asmdef)? You will need to add a reference to the package asmdef Unity.Localization
    Also worth updating to the latest version 1.4.3
     
    Last edited: Feb 1, 2023
    Lagio0 likes this.
  18. Lagio0

    Lagio0

    Joined:
    May 8, 2021
    Posts:
    2
    Thank you so much it was the assembly refenrence ! It was added a recently by a coworker, i didn't pay attention to this, my mistake.
     
    karl_jones likes this.
  19. nmg_spade

    nmg_spade

    Joined:
    Aug 11, 2022
    Posts:
    4
    What's the best practice for using this on a string that appears multiple times throughout the game? Example: I'm building a card game. If you have many of one type of card... say "attack card"... the string will appear multiple times, but it feels overkill to be "retrieving" the translation every time a card appears on the scene.
     
  20. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    You could use a single LocalizedString to fetch the string and then assign it to the cards that use it although I would say it's not worth doing this unless you are having performance issues. The lookup is only done once and is pretty fast once the table has loaded.
     
  21. nmg_spade

    nmg_spade

    Joined:
    Aug 11, 2022
    Posts:
    4
    Got it... yeah, I'm not having performance issues. The fact that it requires an async process made me hesitate to add it to an object that I'm pooling (I use the same game objects for the cards, I just tell each object what card they're supposed to represent). So.... the same card will be "loaded" multiple times in a play session. But if the async process is mostly required for loading the table, and then all other calls are fast then doing the WaitForCompletion thing is good enough for me.
     
  22. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Making multiple requests to the async is fine. They will all use the same table operation and just have a separate operation to extract the value. So there won't be any problems with making multiple requests.