Search Unity

[SOLVED] UnityEngine.Localization not available in code?

Discussion in 'Localization Tools' started by NKCSS, May 3, 2020.

  1. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    Hi, I installed the 0.6.1 package today, set up a string db, added 2 locales and translations and set up some translations in the UI, works fine!

    Now, I want a more complex scenario where I want to have sub-references to other translations, so I want to have string properties that just get the other translations in code to expose in a property to another translation via string format, however, I dont even have a UnityEngine.Localization namespace I can reference, all I have is UnityEngine.LocalizationAsset.

    How can I retrieve a translated text via script?

    Thanks in advance,

    Nick.
     
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    Is your code inside of an assembly definition file? you need to add a reference to Unity.Localization.
    The stuff you see under Localisation at the moment is actually just stuff for editor Localisation, not the package.

    Can you send explain a little more what you are trying to do? I'm much better with examples :)
     
  3. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    After making the code crash with a smart formatstring that was not smart, it all of a sudden picked up the namespace, so no idea why it didn't recognise it before, but it does so now.

    Here is what I am trying to do. This is my format string for English:

    "Careless detectivework lead to the ruining of {AccusedCard_DisplayName} Social life. After being wrongly sent to prison, none of {AccusedCard_Pronoun3} mates would hang with {AccusedCard_Pronoun2} anymore."

    There will be 4 types of card (man, woman, zombie, robot), what I want to do, is set up display names for the 4 types and their corresponding pronouns, then, from code, supply them to the format string.

    If there is a better way to do so, please let me know,
     
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    Interesting. So we do plan to allow for nesting translations in this way so that you can use a table name and key name as a SmartFormat argument to retrieve another translation however this is not available at the moment.

    For your example here I think a nested dictionary could work, as shown here https://docs.unity3d.com/Packages/com.unity.localization@0.6/manual/Dot-Notation.html
    You could store all the potential values into the dictionary and then pass that as an argument to the main translation.
     
  5. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    Thank you for the feedback; I just need to go find the best way to load a translation from code, as the key for the string will be based on code (e.g. I can't predict that in advance. The scripting page could do with some additional examples. I guess I will wire up a reference in the UI then set a breakpoint to inspect how it was constructed.
     
  6. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    If there's any particular scripting examples you think would help then do please let us know.
     
  7. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    karl_jones likes this.
  8. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    Ok, I think I still might need your help @karl_jones

    I have added the following code to get task references to the strings I would need:


    Code (CSharp):
    1.  
    2.         public async Task<string> GetTranslation(string key)
    3.         {
    4.             var translated = LocalizationSettings.StringDatabase.GetLocalizedStringAsync(StringTableName, key);
    5.             return translated.IsDone ? translated.Result : await translated.Task;
    6.         }
    7.         public async Task<string> DisplayName() => await GetTranslation($"{CharacterType}_Name");
    8.         public async Task<string> Pronoun1() => await GetTranslation($"{CharacterType}_Pronoun1");
    9.         public async Task<string> Pronoun2() => await GetTranslation($"{CharacterType}_Pronoun2");
    10.         public async Task<string> Pronoun3() => await GetTranslation($"{CharacterType}_Pronoun3");
    How am I able to use the output of a Task in my smart format string?

    I tried

    Careless detectivework lead to the ruining of {AccusedCard.DisplayName} Social life. After being wrongly sent to prison, none of {AccusedCard.Pronoun3} mates would hang with {AccusedCard.Pronoun2} anymore.


    and

    Careless detectivework lead to the ruining of {AccusedCard.DisplayName()} Social life. After being wrongly sent to prison, none of {AccusedCard.Pronoun3()} mates would hang with {AccusedCard.Pronoun2()} anymore.


    But both would just omit the formats completely. Any suggestions?
     
  9. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    Hmm, I tested to see if the code works at all, but, if I do a simple

    _ = Guilty.DisplayName();


    I get a nice stacktrace of code that crashes...



    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)

     
  10. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    Make sure to yield on the Localisation settings initialization operation fits. I'll try and put an example together for how you can tackle this
     
    NKCSS likes this.
  11. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    That is much appreciated; thanks.

    I made my Start async and added this; no more error now, so that's nice.


    await UnityEngine.Localization.Settings.LocalizationSettings.InitializationOperation.Task;


    Looking forward to the example. Thanks in advance. I figured I could do a workaround and do all text updates entirely from code, but that feels like admitting defeat in this case, so, yeah, really looking forward to your reply!
     
  12. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    Ok here's an example:

    For what you are doing I assume that the values are all in 1 table although you can easily rework this example if they are not

    Instead of getting each string we just fetch the table, then populate a dictionary with the sub strings (AccusedCard_DisplayName, AccusedCard_Pronoun3 etc). We then get the main string we care about and pass that dictionary in as a smart format argument.

    Code (csharp):
    1. IEnumerator Start()
    2.     {
    3.         yield return LocalizationSettings.InitializationOperation;
    4.  
    5.  
    6.         // If the translations are all in 1 table then its quicker to just get the table, we wont need to do anymore waiting
    7.         var tableOp = LocalizationSettings.StringDatabase.GetTableAsync("My Table");
    8.         yield return tableOp;
    9.  
    10.         // Now put the values we care about into a dictionary
    11.         var dict = new Dictionary<string, string>();
    12.  
    13.         // E.G dict["AccusedCard_DisplayName"] = "Karl"
    14.         dict["AccusedCard_DisplayName"] = tableOp.Result.GetEntry("AccusedCard_DisplayName")?.GetLocalizedString();
    15.      
    16.  
    17.         // We can add them all if we need to
    18.         foreach(var entry in tableOp.Result)
    19.         {
    20.             // Get the key name
    21.             var keyName = tableOp.Result.SharedData.GetEntry(entry.Key)?.Key;
    22.  
    23.             // If its a smart string then we may need to pass some arguments here
    24.             dict[keyName] = entry.Value.GetLocalizedString();
    25.         }
    26.  
    27.         // Now get the main entry
    28.         var smartFormatEntry = tableOp.Result.GetEntry("My String");
    29.  
    30.         // Translate it using the dictionary
    31.         // E.G "My name is {AccusedCard_DisplayName} => My name is Karl
    32.         var translatedString = smartFormatEntry.GetLocalizedString(dict);
    33.  
    34.         Debug.Log(translatedString);
    35.     }
    This is the type of thing that should be available in the future but for now it requires a little extra work :)
     
    NKCSS likes this.
  13. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    I took some of what you did and did it a bit differently. I decided to, on start, build the dictionary with all non-smart translations and have three properties that will refer to the proper translations, so in my smart format string, I'll have these properties that I can refer to that will have the proper referred translations. Will only have to make sure that when the active language switches, that I update the dictionary, but other than that, this is an OK solution.

    I plan to make a small video about it to showcase what it can do and how I solved this specific problem. It goes against the idea of the package to not need to have all the translations in memory, but since there is no other proper way to hook into this as far as I can tell, it will have to do for now.

    Code (CSharp):
    1.  
    2.     public string AccusedCard_DisplayName { get => Translations[$"{AccusedCard?.CharacterType}_Name"]; }
    3.     public string AccusedCard_Pronoun1 { get => Translations[$"{AccusedCard?.CharacterType}_Pronoun1"]; }
    4.     public string AccusedCard_Pronoun2 { get => Translations[$"{AccusedCard?.CharacterType}_Pronoun2"]; }
    5.     public string AccusedCard_Pronoun3 { get => Translations[$"{AccusedCard?.CharacterType}_Pronoun3"]; }
     
    karl_jones likes this.
  14. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    Sounds good. If you have any feedback on improvements im happy to hear them any time.

    Your solution sounds fine, the package is not granular enough to only storing single strings in memory, it's done on a per-table basis. So if all your translations are in a single table then they are already stored in memory somewhere.

    I'd be interested in your video when its ready :)
     
    NKCSS likes this.
  15. NKCSS

    NKCSS

    Joined:
    Aug 29, 2015
    Posts:
    19
    Thanks I'll reply to this thread when I get around to it and I hope I do it justice :)
     
    karl_jones likes this.
  16. jamesor

    jamesor

    Joined:
    Jan 1, 2020
    Posts:
    11
    Hi Karl, I'm struggling with the same issue. I'm running 2019.3.10f1 and the preview package was not available for me through Package Manager so I pasted "com.unity.localization": "0.6.1-preview" into my project's manifest.json file as a dependency. That worked for getting all of the Localization tools into the editor. I can create Localization Settings and String Tables just fine in the editor.

    My trouble is the same as NKCSS, in that I try to create a script with "using UnityEngine.Localization;" and get the error that the type or namespace is not found in UnityEngine.

    I checked my assembly references and it appears to be there.
    localization.png

    I'm not sure what step I'm missing about getting the assembly reference connected.
     
  17. jamesor

    jamesor

    Joined:
    Jan 1, 2020
    Posts:
    11
    I figured it out. I had an .asmdef file that needed updating.

    asmdef.png

    Adding Unity.Localization and Unity.ResourceManager here fixed my issues.
     
    karl_jones likes this.
  18. juliochassan

    juliochassan

    Joined:
    Nov 11, 2017
    Posts:
    2
    the easiest way is Edit -> preferences -> external Tools , and press the buton "regenerate project files"
     
  19. prajwalg8736

    prajwalg8736

    Joined:
    Jan 22, 2020
    Posts:
    3
    To install the localization package in a version before 2021.2, type in com.unity.localization under “Add package from git URL” in the Package Manager