Exceptions depending on initialization order?

Hello,

I’m having some trouble while using this package. I think that depending on initialization order I’m getting some exceptions:

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].GetTableLoadTable (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1[TObject] sharedTableDataOperation, UnityEngine.Localization.Locale locale) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedDatabase.cs:212)
UnityEngine.Localization.Settings.LocalizedDatabase`2+<>c__DisplayClass23_0[TTable,TEntry].<GetTableLoadTable>b__0 (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1[TObject] op) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedDatabase.cs:188)
UnityEngine.ResourceManagement.ChainOperation`2[TObject,TObjectDependency].Execute () (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/AsyncOperations/ChainOperation.cs:34)
UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].InvokeExecute () (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:409)
UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].<.ctor>b__33_0 (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle o) (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:110)
DelegateList`1[T].Invoke (T res) (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/Util/DelegateList.cs:69)
UnityEngine.Debug:LogException(Exception)
DelegateList`1:Invoke(AsyncOperationHandle) (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/Util/DelegateList.cs:73)
UnityEngine.ResourceManagement.Util.DelayedActionManager:LateUpdate() (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/Util/DelayedActionManager.cs:169)

The offending like is:

if (TableOperations.TryGetValue((locale.Identifier, tableReference), out var asyncOp))

at LocalizedDatabase.cs, and the null reference is that locale object.

This happens in my current scene when I just add some UI button (using TMP_Text) but it doesn’t use localized strings itself, or (and this is the most strange one) even when I’m just editing a prefab and run the game, without changing anything else.

May I know if I’m doing anything wrong, or if I need to change initialization order of some script for this to work properly? I’ve tried rebuilding addressables and things like that, but this obviously doesn’t make much sense, as it works fine until I do on of those two things.

Thanks in advance.

Hi,
I’ll try and take a deeper look into this. Would you be able to file a bug report?

Sure (you mean inside Unity itself, or does this package have a separate bugtracker?), although I cannot provide any more info than that, at least for now :slight_smile: (or unless you tell me what to look for)

Thanks!

Just a normal bug report through Unity. It will help make sure the bug gets tracked and not forgotten about :wink:

Done, case 1225937. Thanks again, and I hope you get to the bottom of it.I had to disable the localization, as I cannot continue with the crash, so let me know if you need any debugging/testing or similar.

This Bug still exists when I try to use Localize Texture Component in latest release 0.6.1.
here is log :

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].GetTableLoadTable (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1[TObject] sharedTableDataOperation, UnityEngine.Localization.Locale locale) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedDatabase.cs:212)
UnityEngine.Localization.Settings.LocalizedDatabase`2+<>c__DisplayClass23_0[TTable,TEntry].<GetTableLoadTable>b__0 (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1[TObject] op) (at Library/PackageCache/com.unity.localization@0.6.1-preview/Runtime/Settings/Database/LocalizedDatabase.cs:188)
UnityEngine.ResourceManagement.ChainOperation`2[TObject,TObjectDependency].Execute () (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/AsyncOperations/ChainOperation.cs:34)
UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].InvokeExecute () (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:409)
UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1[TObject].<.ctor>b__33_0 (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle o) (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs:110)
DelegateList`1[T].Invoke (T res) (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/Util/DelegateList.cs:69)
UnityEngine.Debug:LogException(Exception)
DelegateList`1:Invoke(AsyncOperationHandle) (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/Util/DelegateList.cs:73)
UnityEngine.ResourceManagement.Util.DelayedActionManager:LateUpdate() (at Library/PackageCache/com.unity.addressables@1.6.2/Runtime/ResourceManager/Util/DelayedActionManager.cs:169)

This happens only once at the beginning after running.

@moomaru yeah, I forgot to mention my version, but I tested against 0.6.1 (and it still exists because I reported it 3 days ago :smile:).

I’ve investigated a bit more, and I’m getting the error when using localized strings in Awake() and it seems that the list of locales is not populated at that point, although I’m using the asynchronous methods for them.

Can you share the code?

Right now the “code” was my game, so I sadly cannot do that.

I also tried to set the LocalizationSettings script order before anything on my own game, but that did not fix the problem, so for now I’ve just removed this package.

here is my code:

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

public enum localeType
{
    Chinese,
    English,
    Japanese
};

[ExecuteInEditMode]
public class LocalizationManager : MonoBehaviour
{
    private localeType lang = localeType.Japanese;

    public Button leftButton;
    public Button rightButton;

    IEnumerator Start()
    {
        // Wait for the localization system to initialize, loading Locales, preloading etc.
        yield return LocalizationSettings.InitializationOperation;

        // Set default locale
        var locale = LocalizationSettings.AvailableLocales.Locales[(int)lang];
        LocalizationSettings.SelectedLocale = locale;
    }

    public void OnClickLefftButton()
    {
        Text text = leftButton.GetComponentInChildren<Text>();
        SetTextLocale(text);
    }

    public void OnClickRightButton()
    {
        Text text = rightButton.GetComponentInChildren<Text>();
        SetTextLocale(text);
    }

    public void SetTextLocale(Text text)
    {
        var nextLocaleIndex = (int)Enum.Parse(typeof(localeType), text.text);
        var nextLocale = LocalizationSettings.AvailableLocales.Locales[nextLocaleIndex];

        var currentLocale = LocalizationSettings.SelectedLocale;
        var currentLocaleIndex = LocalizationSettings.AvailableLocales.Locales.IndexOf(currentLocale);

        text.text = Enum.ToObject(typeof(localeType), currentLocaleIndex).ToString();

        LocalizationSettings.SelectedLocale = nextLocale;
    }
}

capture.png image is when I just select play in editor.

Are the issues happening only in edit mode?
We don’t currently support edit mode(we will in the future), its a limitation of Addressables so this would explain why its not working.

Not sure about their problems, but mine happened when playing, and I’m assuming theirs as well.

Their example script is using [ExecuteInEditMode] which I suspect is their problem.

Ah, you’re completely right, I missed that, sorry.

There is same error even I remove [ExecuteInEditMode].
In my case, I inserted this code in LocalizedDatabase.cs.

        AsyncOperationHandle<TTable> GetTableLoadTable(TableReference tableReference, Locale locale)
        {
            if (tableReference.ReferenceType == TableReference.Type.Guid)
            {
                // We need to load the SharedTableData so we can resolve the name of the table
                var sharedTableDataOperation = GetSharedTableData(tableReference);
                if (sharedTableDataOperation.IsDone)
                    return GetTableLoadTable(sharedTableDataOperation, locale);
                return ResourceManager.CreateChainOperation(sharedTableDataOperation, op => GetTableLoadTable(op, locale));
            }

            // ------------from here I inserted code----------------
            if (locale==null)
                locale = LocalizationSettings.SelectedLocale;
            //--------------------------------------------------------------
            
            if (TableOperations.TryGetValue((locale.Identifier, tableReference), out var asyncOp))
                return asyncOp;

            var tableAddress = AddressHelper.GetTableAddress(tableReference, locale.Identifier);
            asyncOp = Addressables.LoadAssetAsync<TTable>(tableAddress);
            RegisterTableOperation(asyncOp, locale.Identifier, tableReference.TableName);
            return asyncOp;
        }

Thats strange. If you dont provide a Locale then it should already be using LocalizationSettings.SelectedLocale earlier in the code. Ill try and reproduce the issue locally.

I noticed a potential issue in your script

var locale = LocalizationSettings.AvailableLocales.Locales[(int)lang];

We can not assume the order of the locales as they will be in the order that Addressables provides them, so (int)lang may not always be the same.

Do something like this:

LocalizationSettings.AvailableLocales.GetLocale(SystemLanguage.Japanese);

Ill make a note to sort them so that in the future you can do this.

We can not assume the order of the locales as they will be in the order that Addressables provides them, so (int)lang may not always be the same.

Yep, I noticed it when build as a standalone app. now I changed to other way. thanks!

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Localization.Settings;

public class LocalizationManager : MonoBehaviour
{
    private Dictionary<string, string> localeText = new Dictionary<string, string>() { { "ja", "Japanese" }, { "en", "English" }, { "zh", "Chinese" } };

    public Button leftButton;
    public Button rightButton;

    public void OnClickLefftButton()
    {
        Text text = leftButton.GetComponentInChildren<Text>();
        SetTextLocale(text);
    }

    public void OnClickRightButton()
    {
        Text text = rightButton.GetComponentInChildren<Text>();
        SetTextLocale(text);
    }

    public void SetTextLocale(Text text)
    {
        var nextLocaleText = localeText.FirstOrDefault(dict => dict.Value == text.text);
        var nextLocale = LocalizationSettings.AvailableLocales.Locales.Find(item => item.name.Contains(nextLocaleText.Key));

        var currentLocaleText = localeText.FirstOrDefault(dict => LocalizationSettings.SelectedLocale.name.Contains(dict.Key));
        text.text = currentLocaleText.Value;

        LocalizationSettings.SelectedLocale = nextLocale;
    }
}

As I understand it the problem is as follows:

In the method OnEnable() of LocalizeStringBehaviour LocalizedDatabase<StringTable, StringTableEntry>.GetTableEntryAsync() is executed (not directly):

LocalizedDatabase<StringTable, StringTableEntry>.GetTableEntryAsync() at Settings\Database\LocalizedDatabase.cs:line 162
LocalizedString.HandleLocaleChange() at \Localized Reference\LocalizedString.cs:line 146
LocalizedString.ForceUpdate() at Localized Reference\LocalizedString.cs:line 137
LocalizedString.RegisterChangeHandler() at Localized Reference\LocalizedString.cs:line 59
LocalizeStringBehaviour.OnEnable() at Component Localizers\LocalizeStringBehaviour.cs:line 58
public AsyncOperationHandle<TableEntryResult> GetTableEntryAsync(TableReference tableReference, TableEntryReference tableEntryReference)
        {
            // We need to initialize before we can use .LocalizationSettings.SelectedLocale.
            var initOp = LocalizationSettings.InitializationOperation;
            if (!initOp.IsDone)
                return ResourceManager.CreateChainOperation(initOp, (op) => GetTableEntryAsync(tableReference, tableEntryReference, LocalizationSettings.SelectedLocale));
            return GetTableEntryAsync(tableReference, tableEntryReference, LocalizationSettings.SelectedLocale);
        }

Inside this method LocalizationSettings.SelectedLocale is used after checking for the initialization. Also there is a comment for that. So it is correct and there is no problem here.

At the same time in the method OnEnable() of LocalizedAssetBehaviour LocalizedAssetDatabase.GetLocalizedAssetAsync is executed (not directly):

LocalizedAssetDatabase.GetLocalizedAssetAsync<TMP_FontAsset>() at Settings\Database\LocalizedAssetDatabase.cs:line 55
LocalizedAsset<TMP_FontAsset>.LoadAssetAsync() at Localized Reference\LocalizedAssetReference.cs:line 71
LocalizedAsset<TMP_FontAsset>.HandleLocaleChange() at Localized Reference\LocalizedAssetReference.cs:line 87
LocalizedAsset<TMP_FontAsset>.ForceUpdate() at Localized Reference\LocalizedAssetReference.cs:line 78
LocalizedAsset<TMP_FontAsset>.RegisterChangeHandler() at Localized Reference\LocalizedAssetReference.cs:line 50
LocalizedAssetBehaviour<TMP_FontAsset, LocalizedFont>.OnEnable() at Component Localizers\LocalizedAssetBehaviour.cs:line 30
public AsyncOperationHandle<TObject> GetLocalizedAssetAsync<TObject>(TableReference tableReference, TableEntryReference tableEntryReference) where TObject : Object
        {
            return GetLocalizedAssetAsync<TObject>(tableReference, tableEntryReference, LocalizationSettings.SelectedLocale);
        }

Inside this method LocalizationSettings.SelectedLocale is used without any checking. So it is null here.

If you look inside the method you can see call of GetTableEntryAsync with passed locale (wich is null).

protected virtual AsyncOperationHandle<TObject> GetLocalizedAssetAsyncInternal<TObject>(TableReference tableReference, TableEntryReference tableEntryReference, Locale locale) where TObject : Object
        {
            var tableEntryOp = GetTableEntryAsync(tableReference, tableEntryReference, locale);
            if (!tableEntryOp.IsDone)
                return ResourceManager.CreateChainOperation<TObject>(tableEntryOp, (op) => GetLocalizedAssetLoadAsset<TObject>(tableEntryOp, tableEntryReference));
            return GetLocalizedAssetLoadAsset<TObject>(tableEntryOp, tableEntryReference);
        }

I assume that locale should not be passed here, and logic should be same as in LocalizeStringBehaviour. And first usage of LocalizationSettings.SelectedLocale should be inside GetTableEntryAsync.

I hope it will be helpful

Ah yes, I see the problem. We are calling SelectedLocale on Assets before we have called LocalizationSettings.InitializationOperation. Thanks for the info, that has helped narrow down the issue. Ill get a fix out in the next release.