Localization for UI Toolkit?

Hi. I found that Unity created localization system for classic UGUI, About Localization | Localization | 0.6.1-preview

So is there any chance for Unity-made localization system for UI Toolkit?

5 Likes

I haven’t used Unity’s localisation; But I am making my own localisation tool so I’m interested in this topic.

What I think you mean to ask is if the localisation package will work with UI elements, with labels and buttons and what not. I’m sure it would be possible to work with it using C# to grab the required text, and change the text on a element to that acquired text.

It looks like Localization’s are stored as Assets in the project (much like my own). It would be fabulous if Unity’s UI Toolkit could like assets into UXML (other than resource loading, I guess).

I am facing the dilemma now of if I should extend all of Unity’s Visual Elements for my own plugin; it feels like I should, but no plan how as of yet.

1 Like

Localization in my experience depends just as much on how you receive and want to process the files from the person/people/agency you hire to do your localization for you, how you want to set up the localization table used in memory, and when/where/how you want the “current language” to be tracked and changed by the user, as it does on what you’re applying that data to. In this case (UI Elements), you can query through all labels and get whatever properties you like from them, so you can identify them by name, or make a new tag for “localization-name” or something in order to work as an identifier. You then just replace whatever text is in that label with your localized version.

Add-ons that extend VisualElement/Label to automate this would simply be wiring into your own localization table (probably a dictionary of strings, dictionary of audio files, dictionary of images), which is really based on how you use assets in your project. I can’t imagine a “one size fits all” solution to this- though ScriptableObjects could probably create some kind of a standard, like localization for UGUI does, I probably wouldn’t even bother using it since ScriptableObjects wouldn’t be how I’d be getting the translated assets from the company I hire for localizing the game.

shrugs

If they develop something it should be extensible enough so that if I want to provide the data I can implement it my way. For example: get the data from one or several *.json, *.csv, …

I saw that at 1:13:26 in this webinar from December 2020 the Localization team says they don’t have an eta on UI Builder Localization support but that they are working on it with the localization team. Does anyone have an update on this? Thank you!

1 Like

Short answer is its on the roadmap of things we need to do for localization but we’ve not finalized a timeline for its support.

Longer answer is our current objectives are to get the Localization package stable and a 1.0 version released. Once that has been concluded we will pivot to determine what the next requirements for localization are and then meet those needs (UIToolkit being one of the higher priorities).

7 Likes

Thank you so much, Phil! If you need testers to try and get the packages working together, I’d be happy to try and be of assistance!

1 Like

Our team uses the UI toolkit and we would like to use the Localization package, it looks very good. Any update on when we can expect the UI toolkit integration? Thanks.

6 Likes

My team is also super keen on using UI Toolkit, but having to create a custom solution for Localization is not that ideal.
Looking forward to hearing more about Unity’s Localization support for this.

1 Like

2 years after the first post there is still no localization solution inside UIToolkit or even a road map with an approximative date?

Hello there, we’re also looking for a clean way to localize strings and assets in the UI builder. Strings are first thing, but localizing assets like background images could also help us a lot. Has the Unity team made progress on the UI Builder integration? If not, do you have any suggestions on ways to achieve this with the UI builder? I’m worried using a MonoBehaviour might cause flickering between non-localized and localized strings when the UI is being loaded.

Hey all, thought I’d share my interim solution (fingers crossed for proper integration from Unity soon).

Notes:

  • My code uses a different code style to that which Unity does. Season to taste
  • Set Windows > Asset Management > Localization Scene Controls > Active Locale so you can see resolved text in-editor
  • You must change Localization Smart Format error actions to NOT throw exceptions, otherwise UI Builder may fail to load your UXML files or… EXPLOSIONS. e.g. here’s what I’ve set them to:
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.UIElements;

namespace FrankenStorm.UI
{
    public class LocalisedLabel : TextElement
    {
        public new class UxmlFactory : UxmlFactory<LocalisedLabel, UxmlTraits> { }
        public new class UxmlTraits : VisualElement.UxmlTraits
        {
            readonly UxmlStringAttributeDescription _table = new() { name = "table" };
            readonly UxmlStringAttributeDescription _tableEntry = new() { name = "table-entry" };

            public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
            {
                base.Init(ve, bag, cc);
                var label = (LocalisedLabel)ve;
                label.Table = _table.GetValueFromBag(bag, cc);
                label.TableEntry = _tableEntry.GetValueFromBag(bag, cc);
                label.Refresh();
            }
        }


        const string USSClassName = "fs-localised-label";


        public string Table { get; set; }
        public string TableEntry { get; set; }
        public LocalizedString LocalisedString { get; private set; }

        public LocalisedLabel() : this(string.Empty, string.Empty) { }

        public LocalisedLabel(string table, string tableEntry)
        {
            AddToClassList(USSClassName);

            LocalisedString = new LocalizedString();
            LocalisedString.StringChanged += HandleStringChanged;

            Table = table;
            TableEntry = tableEntry;

            Refresh();
        }

        public void Refresh()
        {
            LocalisedString.TableReference = string.IsNullOrEmpty(Table)
                ? LocalizationSettings.StringDatabase.DefaultTable
                : Table;
            LocalisedString.TableEntryReference = TableEntry;
        }

        void HandleStringChanged(string val) =>
            text = val;
    }
}

Note that I’ve got this class in a namespace. You don’t need to do that, but if you do, then you do need to add an assembly attribute to make UI Builder aware of your namespace. e.g. I’ve got this in my UI assembly’s AssemblyInfo.cs:

#if UNITY_EDITOR
using UnityEditor.UIElements;

[assembly: UxmlNamespacePrefix("FrankenStorm.UI", "fs")]
#endif

This works fine with smart strings, just access the LocalisedString directly and set variables as you desire, e.g.:

using UnityEngine;
using UnityEngine.Localization.SmartFormat.PersistentVariables;
using UnityEngine.UIElements;

namespace FrankenStorm.UI
{
    [RequireComponent(typeof(UIDocument))]
    public class UIController : MonoBehaviour
    {
        UIDocument _doc;
        LocalisedLabel _test;

        public void Awake()
        {
            _doc = GetComponent<UIDocument>();
            _test = _doc.rootVisualElement.Q<LocalisedLabel>("test");
        }

        public void Update()
        {
            _test.LocalisedString.Remove("SecondsIn");
            _test.LocalisedString.Add("SecondsIn", new IntVariable { Value = (int)Time.time });
            _test.LocalisedString.RefreshString();
        }
    }
}

I’ve not tested this much. If anyone sees any red flags, please share with me!

EDIT: AssemblyInfo.cs UnityEditor.UIElements needed preprocessor conditions.

5 Likes

Try this instead of recreating the variable each time:

[RequireComponent(typeof(UIDocument))]
public class UIController : MonoBehaviour
{
    UIDocument _doc;
    LocalisedLabel _test;
    IntVariable m_Time;

    public void Awake()
    {
        _doc = GetComponent<UIDocument>();
        _test = _doc.rootVisualElement.Q<LocalisedLabel>("test");
    }

    public void Update()
    {
        if (m_Time == null)
        {
            m_Time = new IntVariable();
            _test.LocalisedString.Add("SecondsIn", m_Time);
        }

        m_Time.Value = (int)Time.time; // Will trigger the string to update
    }
}
2 Likes

Ah great tip, thanks Karl! Didn’t cross my mind to re-use to reference

EDIT from the future: Even with this approach, it still seems required to call RefreshString at least once, once the variable is added to the string the first time. Otherwise the string seems to remain in an erroneous state.

1 Like

I had already posted my code here but using StringChanged and making the LocalizedString public are two nice improvements:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.UIElements;

namespace UnityEngine.UIElements
{
    internal class LocalizedLabel : TextElement
    {
        public new class UxmlFactory : UxmlFactory<LocalizedLabel, UxmlTraits> { }

        public new class UxmlTraits : TextElement.UxmlTraits
        {
            UxmlStringAttributeDescription m_LocaleKey = new UxmlStringAttributeDescription { name = "locale-key" };

            public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
            {
                get { yield break; }
            }

            public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
            {

                base.Init(ve, bag, cc);
                var ll = ve as LocalizedLabel;
                ll.LocaleKey = m_LocaleKey.GetValueFromBag(bag, cc);
                ll.initLabel();
            }
        }
        public string LocaleKey { get; set; }
        public LocalizedString LocalizedString { get; private set; }

        public new static readonly string ussClassName = "localized-label";

        public LocalizedLabel()
        {
            AddToClassList(ussClassName);
            LocalizedString = new LocalizedString();
        }

        public LocalizedLabel(string localeKey) : this()
        {
            this.LocaleKey = localeKey;
            initLabel();
        }


        private void initLabel()
        {
            var p = LocaleKey.IndexOf('/');
            if (p > 0 && p != (LocaleKey.Length - 1) && LocaleKey.Length > 2)
            {
                LocalizedString.TableReference = LocaleKey.Substring(0, p);
                LocalizedString.TableEntryReference = LocaleKey.Substring(p + 1);
                RegisterCallback<GeometryChangedEvent>(initChange) ;
            }
        }

        private void initChange(GeometryChangedEvent evt)
        {
            LocalizedString.StringChanged += stringChanged;
            UnregisterCallback<GeometryChangedEvent>(initChange);  
        }

        private void stringChanged(string value)
        {
            text = value;
        }
    }
}
2 Likes

I’ve fixed the warning: “SendMessage cannot be called during Awake, CheckConsistency, or OnValidate” by using a GeometryChangedEvent callback.

1 Like

Another option would be to use Asset Localization instead of String Localization.

You can use Localized Property Variants (Unity 2020.3 or later) or Component Localizers to change the Tree View Asset of the UIDocument component at runtime, and have one Tree View Asset for each locale. This is useful for situations in which the layout is altered with the language change or you want a more fine control of the visual elements of the document like animations. I would suggest the latter unless you have a lot of different UI Documents to deal with in a lot of languages.

For Component Localizers, it only requires 2 lines of code and adding the LocalizeUIDocumentEvent component to the UIDocument you want to localize:

public class LocalizeUIDocumentEvent : LocalizedAssetEvent<VisualTreeAsset, LocalizedVisualTreeAsset, UnityEvent<VisualTreeAsset>>{}

(It might be necessary to replace the derived class to LocalizedAssetEvent depending on the version of your Localization package and editor, the documentation is a bit outdated)

[Serializable]
public class LocalizedVisualTreeAsset: LocalizedAsset<VisualTreeAsset> {}

Note that you mustn’t set the Visual Tree Asset directly. Instead, make a setter function that does the cleanup for the previous Visual Tree, and the initialization for the new one.

Plus, you can combine this with the LocalizedLabel from above.

2 Likes

We do have a prototype based on a new bindings feature for UI Toolkit. Unfortunately, it will require Unity 2023 and above as that’s the version the feature should land in.

5 Likes

Any update on this? No support in 1.4. Am I looking in the right place? https://docs.unity3d.com/Packages/com.unity.localization@1.4/changelog/CHANGELOG.html

Still in the roadmap as “Planned” : https://portal.productboard.com/rcczqdfvurr8zuws3eth2ift/c/301-ui-toolkit-compatibility-with-the-localization-package?utm_medium=social&utm_source=portal_share