Search Unity

Resolved (Arguments.Add) "Dynamic String" example in Documentation doesn't work

Discussion in 'Localization Tools' started by RiQuY, May 22, 2020.

  1. RiQuY

    RiQuY

    Joined:
    Jun 29, 2018
    Posts:
    5
    I'm trying to update a Smart String following the example "Dynamic Strings" in the docs:
    https://docs.unity3d.com/Packages/com.unity.localization@0.6/manual/Scripting.html

    (I'm working with Unity 2019.3.14f1)
    Visual Studio gives me an error in the line 18: HitsText.Arguments.Add(this); (Check screenshot)
    LocalizationError.png

    I attached another screenshot too, showing the Unity GameObject Inspector to add more info.
    LocalizationErrorInspector.png

    I'm doing exactly the same like in the docs example.
    Are the docs obsolete?

    Thanks.
     
    Last edited: May 22, 2020
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Ah yes looks like we missed something. We changed Arguments to be an array.
    do this:
    Code (csharp):
    1. HitsText.Arguments = new[] { this };
    Ill get the docs updated.

    Thanks
     
  3. RiQuY

    RiQuY

    Joined:
    Jun 29, 2018
    Posts:
    5
    Thanks!
    The code error got fixed with the code you shared, but my Smart String is still not updating.
    Can you help me, please?
     
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Can you share the code? I dont see anywhere in the code above where you use the string. Do you need to add another GUILayout.Label in your OnGUI for the string?
     
  5. RiQuY

    RiQuY

    Joined:
    Jun 29, 2018
    Posts:
    5
    In the second screenshot you can see the Inpector of the TextMeshProText element where I'm setting the text.
    The Localize String component sets the text "Hits{Hits}x" in the TextMeshProText element.

    The following code does this: In the file HitsLocalization.cs I retrieve the value of PlayerPrefs.GetInt("Hits") and set it to the public variable int Hits:
    Code (CSharp):
    1.  
    2. // File HitsLocalization.cs
    3.  
    4. public string Hits;
    5. void Start()
    6. {
    7.     // PlayerPrefs contains the value 100
    8.     Hits = PlayerPrefs.GetInt("Hits");
    9. }
    10.  
    Then I drag the file HitsLocalization.cs into the Element 0 in the Format Arguments of the component Localize String (screenshot 2).

    The problem is the following:

    The text shown when loading the game is: "Hits 0x", but if I change the language of the game through the dropdown in the top right of the Game view, the text updates correctly to: "Hits 100x".
    That means the Start() executed after the component Localize String and the value of Hits didn't get updated.
    I tried Awake() and it didn't worked too.
     
    Last edited: May 22, 2020
  6. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    So I think I can see what you are doing.
    You have a Localize String attached to the text which is doing the update.
    The Localize String is using the Arguments provided by your script.

    So if I'm correct then you don't need the LocalizedString component in your class, there is already one in the Localize String component. Changes made to LocalizedString will not be made to other LocalizedStrings in the world, this is by design. They operate independent of each other, so you could provide different arguments to the same localized string.
    You just need to call RefreshString on the LocalizedString that is part of the component attached to the text.

    Code (csharp):
    1.  
    2. public class Hits : MonoBehaviour
    3. {
    4.     public int Hits;
    5.     public LocalizeStringEvent localizeString; // Hold a reference to the LocalizeStringBehaviour component. Assign this in the inspector.
    6.  
    7.     void OnEnable()
    8.     {
    9.         Hits = PlayerPrefs.GetInt("Hits");
    10.         localizeString.StringReference.RefreshString();
    11.     }
    12.  
    13.     void Update()
    14.     {
    15.         // If the value changes then just call RefreshString again
    16.         //localizeString.StringReference.RefreshString();
    17.     }
    18. }
     
    drewjosh likes this.
  7. RiQuY

    RiQuY

    Joined:
    Jun 29, 2018
    Posts:
    5
    Hi, I tried the code you suggested and Visual Studio throws an error about the class LocalizeStringEvent doesn't exists, I checked and using UnityEngine.Localization; is in the file.
    LocalizedStringError.png
     
  8. RiQuY

    RiQuY

    Joined:
    Jun 29, 2018
    Posts:
    5
    Nevermind, I found the error, it was LocalizeStringBehaviour instead LocalizeStringEvent class.

    Thank you so much for the help, the localization tool is amazing, you are doing a great work ;)
    Having one example about using LocalizeStringBehaviour in the docs should be really useful too.

    Thanks again.
     
  9. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Oh yes sorry. We renamed it in the next version.
     
  10. SKArctop

    SKArctop

    Joined:
    Feb 12, 2018
    Posts:
    38
    I'm trying to use this method, and it does work in editor.
    However, for some reason, smart values are always blank and do not correctly get updated when I am building for android.

    I'm not seeing any exceptions. putting in break points, i can see my getters getting called, but the final string is stripped of any smart format objects.

    Any ideas?
     
  11. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    By default, Smart Format errors are ignored. Try changing it in the settings so they throw
    upload_2020-5-26_10-12-13.png
     
  12. SKArctop

    SKArctop

    Joined:
    Feb 12, 2018
    Posts:
    38
    Seems that for some reason, the formatter is null. Here is the exception coming up on adb logcat:
    Code (csharp):
    1.  
    2. 2020-05-26 15:30:15.345 19268-19302/co.arctop.gaming E/Unity: FormattingException: Error parsing format string: Value cannot be null.
    3.     Parameter name: source at 266
    4.     You will be presented with a series of exercises (like: 100 +21 = )
    5.    
    6.     Write the correct answer and press the ‘Done’ button as quickly as possible.
    7.    
    8.     The calculations should be done only in your head, and without using a paper or calculator.
    9.    
    10.     You will have {SessionTime} {SessionMeasureUnits} to complete this task.
    11.    
    12.     Have fun!
    13.     --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------^
    14.       at 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) [0x00054] in E:\Dev\Gaming_Library_Native
    15.  
    Added an image of the stack trace as well.

    Why would the formatter be null? the values of my properties are set, and it does work in editor. I have made sure to build the addressables. It is just the smart object that fails.
     

    Attached Files:

  13. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Do you know which formatter is going null? There was some bugs with the SerializeReference feature that caused this to happen. What version of Unity are you using? Can you try the latest?
    Some people have mentioned that the List formatter is going null for them, ty removing if if you are not using it.
     
  14. SKArctop

    SKArctop

    Joined:
    Feb 12, 2018
    Posts:
    38
    Using latest. Not sure which one it was. I will try and play around with the settings, but it is a holiday now here so not available to check until next Sunday. Will circle back with more info when I can.
     
    karl_jones likes this.
  15. SKArctop

    SKArctop

    Joined:
    Feb 12, 2018
    Posts:
    38
    So i've done some digging.

    in SmartFormatter.cs , InvokeFormatterExtensions()

    the formatter name property accessed in line 387: (see screenshot_1)

    var formatterName = formattingInfo.Placeholder.FormatterName;
    Is an empty string. The placeholder is valid, but no format or name is present here.
    The stacktrace here is GetLocalizedString->FormatWithCache->Format->Format->EvaluateFormatters->InvokeFormatterExtension (see stacktrace.png).

    Upon further playing around, it appears that removing the ListFormatter from the settings does result in correct working solution in build as well.
     

    Attached Files:

    karl_jones likes this.
  16. vengefullfoot

    vengefullfoot

    Joined:
    Sep 24, 2018
    Posts:
    31
    Hello,
    I'm jumping into this thread as I have a related issue. How do you manage to put a variable of a script as an argument for a smart string like in your screenshot where it's written "Hits(HitsLocalization)" in Format arguments ?
    I've carefully followed the manual and this thread, but I always end up with the error : "Could not evaluate the selector".
     
  17. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Can you share what you have so far?
     
  18. vengefullfoot

    vengefullfoot

    Joined:
    Sep 24, 2018
    Posts:
    31
    I'm also a bit lost with smart format... Here is what I have

    I've added that script as a componenent in my label gameobject. It has finally worked with integers for another field, but here with the boolean, impossible to make it work.

    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3. using UnityEngine.Localization;
    4.  
    5.     public class LocalizedFocus : MonoBehaviour
    6.     {
    7.         public LocalizedString myString;
    8.  
    9.         string localizedText;
    10.  
    11.         public bool focusMode = true;
    12.  
    13.         public TMP_Text focusLabel;
    14.  
    15.         /// <summary>
    16.         /// Register a ChangeHandler. This will be called whenever we need to update our string.
    17.         /// </summary>
    18.         void OnEnable()
    19.         {
    20.             myString.Arguments = new[] { this };
    21.             myString.RegisterChangeHandler(UpdateString);
    22.         }
    23.  
    24.         private void OnDisable()
    25.         {
    26.             myString.ClearChangeHandler();
    27.         }
    28.  
    29.         void UpdateString(string s)
    30.         {
    31.             localizedText = s;
    32.         }
    33.  
    34.         void OnGUI()
    35.         {
    36.             // This will either call UpdateString immediately (if the table is loaded) or when the table is available.
    37.             myString.RefreshString();
    38.             focusLabel.text = localizedText;
    39.            // GUILayout.Label(localizedText);
    40.         }
    41.     }
    42.  
    43.  
     

    Attached Files:

  19. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Hi,
    I can see how Smart Format can be a bit confusing. We do want to make it easier in the future.
    I recreated your example and the problem you have is that you are using the {0} syntax but passing in an instance of LocalizedFocus which is not a bool. If you want to use the focusMode variable then you need to either pass in the variable as an argument or change the Smart Format string to use it.

    So to pass the argument you would do:
    Code (csharp):
    1. myString.Arguments = new[] { (object)focusMode };
    When you change the value of focusMode you will need to change the arguments as this value will be held by value, not reference.

    Alternatively use this as your Smart String:
    "{focusMode:focus Mode|Cool Mode}"
     
  20. vengefullfoot

    vengefullfoot

    Joined:
    Sep 24, 2018
    Posts:
    31
    Thank you. I've tried both alternatives, they work well in the editor, but on ios devices, I get the same issue "Could not evaluate the selector". Do you have an idea from where it may come ?
     
  21. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Hi,
    We have a bug that could be the same issue https://issuetracker.unity3d.com/is...-shown-in-the-build-when-they-are-localized-1
    It only just came in so I can't really say what the issue is yet but ill try and take a look at it later in the week.

    Edit:
    I just took a look at the bug. I know what's wrong, its actually an issue I reported myself ;)
    https://issuetracker.unity3d.com/is...lds-lose-their-values-when-entering-play-mode

    A fix is on the way to 2019.3. When the fix lands things should just work.
    For now try this workaround:
    Make sure it gets called before you use any LocalizedStrings.
    https://gist.github.com/karljj1/4b10868766a33435c2fe34606874a274

    @sk_unity246 Looks like this is the same issue you were having

    Edit2:
    Our QA tested against that fix and it does not fix our issue. We have created our own fix instead which will be in 0.7
     
    Last edited: Jun 5, 2020
  22. SKArctop

    SKArctop

    Joined:
    Feb 12, 2018
    Posts:
    38
    @karl_jones Yeah, the list formatter is the issue, but since removing it works fine I'm happy to just do that until new version of the package are released.

    I am having a different issue atm, that I'm trying to debug.
    We use unity as a library within an android app. When I load in the player the first time, all smart objects are evaluated and presented correctly. After we quit the player, go back into our app's MainActivity, and re-launch unity, the values (of smart objects) are now empty again.
    I'm in the process of trying to debug this, so I might have more info on the subject later. Wanted to point this out now, in case you might have other insight.
    I'm guessing that this has to do with the localization system not getting destroyed/un-initialized, as the Unity player keeps some things around after it is unloaded in that context. So there is never another call to update the strings. Is there a way to force objects / system to re-load all the strings? or trigger all the "on changed" events?
     
  23. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Thats strange. You could try calling the SendLocaleChangedEvents function in LocalizationSettings. Its private but you can just change that in the package source.
     
  24. Flx24

    Flx24

    Joined:
    Sep 19, 2015
    Posts:
    6

    Attached Files:

  25. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Hi.
    Try changing the Smart Format error action so you can see whats going wrong
    upload_2020-6-16_13-55-44.png
     
  26. Flx24

    Flx24

    Joined:
    Sep 19, 2015
    Posts:
    6
    Here comes the error code:

    FormattingException: Error parsing format string: Could not evaluate the selector "TimeNow" at 17
    The time now is {TimeNow}.
     
  27. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    It looks like you have 2 components both doing the same string?
    LocalizeStringEvent and LocalizedStringExample.
    The LocalizedStringExample seems to be working(its the white text) but the other text it not. From what I can see you are not passing in any arguments to the one that does not work.
    Untitled.jpg

    {TimeNow} is the name of a field in a script that it will find by reflection, you need to pass an instance of that script into the Format Arguments so that it can find it.
    So if you change Format Arguments to 1 and drag in the Localized String Example script into the field then it should work.
    You would want to create a new script which just has the TimeNow field for the next step so its not dependent on the rest of the script.
     
  28. Flx24

    Flx24

    Joined:
    Sep 19, 2015
    Posts:
    6
    That's it. Thanks a lot.
     
    karl_jones likes this.
  29. drewjosh

    drewjosh

    Joined:
    Sep 24, 2019
    Posts:
    29
    I implemented it exactly as described here and it works, but after disabling once a parent of the localized string and when activating and calling RefreshString() the next time I receive following exception:

    RefreshString should be used with StringChanged however no change handler has been registered.

    UPDATE: I see know there is another thread for this: https://forum.unity.com/threads/eve...-lost-when-deactivating-a-game-object.959856/
    I will try to figure out if the gameobject is really active when trying to refresh. Thanks for the great work into this package!
     
    Last edited: Dec 1, 2020
  30. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    The string should refresh automatically when being enabled so you should not need to call RefreshString
     
    drewjosh likes this.
  31. drewjosh

    drewjosh

    Joined:
    Sep 24, 2019
    Posts:
    29
  32. pezezzle

    pezezzle

    Joined:
    Nov 17, 2019
    Posts:
    11
    i do it this way, it works for me:

    Code (CSharp):
    1. LocalizeStringEvent lse = GetComponent<LocalizeStringEvent>();
    change the string variable

    Code (CSharp):
    1. LocalizeStringlse.RefreshString();
    and finally i have the refreshed string variable on my GUI.
     
    Masea1 likes this.
  33. hubertstruminski

    hubertstruminski

    Joined:
    Jul 7, 2021
    Posts:
    2
    Hi,
    Everything worked for me until I clicked "Upgrade" over the Format Arguments of LocalizeStringEvent. Now instead of Format Arguments I see Local Variables.

    In the attached screenshot in ShieldStringSmart I have "public int shieldCount = 0;", but when I run game I see "ShieldButtonText (ShieldStringSmart)) instead of 0.

    Could you explain me what I'm doing wrong ?

    Link to the screenshot:

    https://imgur.com/a/510POBK
     
  34. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,281
    Doing shieldCount will return the instance from the local variables, you then need to select the property from the instance so it should be {shieldCount.shieldCount} ;) previously we would use the first value as the default so you didn't need to specify the first part However we don't have defaults for local variables. We decided against it as it would make things more complicated if you also had script arguments.
    Maybe rename the variable.
     
    drewjosh likes this.