Localizating strings on script

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:

                    LocalizedString localized = new LocalizedString();
                    localized.TableReference = "Texts";
                    localized.TableEntryReference = "USERNAME-ALREADY-EXISTS";
                    Debug.Log(localized.ToString());
                    Debug.Log(localized.GetLocalizedString() );

but do not works I get that:

[TableReference(Texts)]TableEntryReference(USERNAME-ALREADY-EXISTS)
UnityEngine.Debug:Log(Object)

UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
UnityEngine.Debug:Log(Object)

Thanks

Hi,

localized.GetLocalizedString() is correct however this returns a AsyncOperationHandle.
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,

var op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync("Texts", "USERNAME-ALREADY-EXISTS");
if (op.IsDone)
   Debug.Log(op.Result);
else
   op.Completed += (op) => Debug.Log(op.Result);
4 Likes

Thanks! That worked!

1 Like

Hi,
I tried to use the code above in a method:

    public void BtnPressed()
    {
        var op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync("MyStrings", "Text1");
        if (op.IsDone)
            Debug.Log(op.Result);
        else
            op.Completed += (op) => Debug.Log(op.Result);
    }

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

It’s because you declare another variable called op.

Use a different name in (op) =>

E.g

op.Completed += (o) => Debug.Log(o.Result);
2 Likes

Works fine . Thanks!

1 Like

I’m using the above method to get a localized string, which is working for standard strings, but failing for Smart Strings.

FormattingException: Error parsing format string: Could not evaluate the selector "nCorrect" at 1
{nCorrect} problems solved correctly (out of {nSolved})
-^

My code:

   public static void setLocalText(string key, Text textComponent, IList<object> args) {
     AsyncOperationHandle<string> op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync(key, args);
     if (op.IsDone) {
      textComponent.text = op.Result;
     } else {
      op.Completed += (o) => textComponent.text = o.Result;
     }
   }

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!

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?

Sorry, I should have provided more complete code.

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

As list of ints:

int n_correct = 0;
int n_solved = 0;
setLocalText("solved_status", this.progressText.GetComponent<Text>(), new List<object> {n_correct, n_solved});

As dictionary (passed as single member of object list):

Dictionary<string, string> dict = new Dictionary<string, string>();
int n_correct = 0;
int n_solved = 0;
dict["nCorrect"] = n_correct.ToString();
dict["nSolved"] = n_solved.ToString();
setLocalText("solved_status", this.progressText.GetComponent<Text>(), new List<object> {dict});

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

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?

FormattingException: Error parsing format string: Could not evaluate the selector "nCorrect" at 1
{nCorrect} problems solved correctly (out of {nSolved})
-^
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)
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)
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)
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)
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)
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)
UnityEngine.Localization.GetLocalizedStringOperation.Execute () (at Library/PackageCache/com.unity.localization@0.11.1-preview/Runtime/Operations/GetLocalizedStringOperation.cs:49)
UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].InvokeExecute () (at Library/PackageCache/com.unity.addressables@1.18.2/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:549)
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)
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)
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)
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)
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)
Util.setLocalText (System.String key, UnityEngine.UI.Text textComponent, System.Collections.Generic.IList`1[T] args) (at Assets/Scripts/Util.cs:11)
ReplayRunner.ListProblems () (at Assets/Scripts/ReplayRunner.cs:106)
ReplayRunner.HasUserFinishedExperiment () (at Assets/Scripts/ReplayRunner.cs:58)
ReplayRunner.Start () (at Assets/Scripts/ReplayRunner.cs:37)

Are you able to share your project or an example project? I think ill need to take a look and see whats going wrong.

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.

7107562–847774–localization-test.zip (386 KB)

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:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.Localization.Settings;



public class TestLocalization : MonoBehaviour
{
    public GameObject textBox;

    // Start is called before the first frame update
    void Start()
    {
        Dictionary<string, object> dict = new Dictionary<string, object>();
        int n_correct = 2;
        int n_solved = 4;
        dict["nCorrect"] = n_correct;
        dict["nSolved"] = n_solved;
        setLocalText("test", textBox.GetComponent<Text>(), new List<object>{ dict });
    }

    // Update is called once per frame
    void Update()
    {
       
    }

    void setLocalText(string key, Text textComponent, IList<object> args) {
        AsyncOperationHandle<string> op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync("Main", key, args);
        if (op.IsDone) {
            textComponent.text = op.Result;
        } else {
            op.Completed += (o) => textComponent.text = o.Result;
        }
    }
}

Excellent. Thanks so much for your quick support!

1 Like

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 :
8772442--1190347--upload_2023-2-1_10-49-11.png

Here is my manifest,json :
8772442--1190350--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 :frowning:

I’ve tryed to remove, re install the package, i’ve tryed to import the examples samples, but no change

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

1 Like

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.

1 Like

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.

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.