#Localization Settings asset does not

1 messages ยท Page 1 of 1 (latest)

pliant reef
#

Go to Localization Settings, String Database, Smart Strings, Source. Add a Persistent Variables source if one does not exist. Add the reference to the global variables asset to it.

calm summit
#

yeah, no worries

#

I found how to add global one

#

I'm currently in a middle of figuring out smth else
I'm doing data binding with UI toolkit (can't wait for Unity to finish it's own at the end of a year) and I'm trying to figure whether it would be possible to manually assign variables to smart strings BEFORE Unity does it.

#

in other words, I want to obtain {Workers} key in code before Unity throws that it can't resolve such variable

pliant reef
#

Yeah there are a few ways to do this. How do you fetch the localized string? Are you doing it in code?

calm summit
#

yes

pliant reef
#

Can you share a snippet?

calm summit
#

I have reference to Table and string key

#

this is pretty much all I have regarding LocalizedString, I did it previously through my own code only

#

but I want to make it even more streamlined for development

pliant reef
#

Yes you can add a persistent variable for Workers

calm summit
#

that's not what I need

#

I am using MVVM pattern in my UI implementation

#

and binding object is always known

#

so what I want

#

is to bind to Properties of that object (it has INotifyPropertyChanged interface) through keys defined in locale

#

basically

#

hmm

#

so, developer writes such localization string

#

and then in his binding path he writes key for that string

#

now, if I simply don't do anything

#

Smart will automatically try to resolve Workers variable

#

and fail

#

without giving me a chance to add that variable

#

but I guess that won't be possible, according to source of LocalizedString

#

it simply has no such callbacks

pliant reef
#

ok I see. Well you could try adding a custom Source. The Sources are executed in the order they are defined in the settings so if they all fail you could be at the bottom of the list to handle it

calm summit
#

yeah, but I'm not sure how I can pass anything into Source from outside

#

since Properties to which variables should be bound to are defined through code

pliant reef
#

You can access it through the LocalizationSettings. E.G: LocalizationSettings.StringDatabase.SmartFormatter.GetSourceExtension<MySource>()

#

The Source will be visible in the settings and can have serialized fields or a custom property drawer

calm summit
#

๐Ÿค”

#

that is smth to consider, but it feels very hacky ngl

pliant reef
#

How would you prefer it to work?

calm summit
#

the perfect scenario

#

just the way I showed on screenshot

#

so developper simply writes Property name in braces

#

and in Binding of specific UI element - just key of that string

#

I assme

#

that is possible with custom source

#

he will just need to add additional selector key

#

{b:Workers}
I assume ISource will be able to figure b and obtain Workers as argument somehow?

pliant reef
#

Hmm. So when a selector is missing you want to add it to the LocalizedString?

#

In that example Workers is a formatter

#

:

calm summit
#

oh, so it needs to be b.Workers?

pliant reef
#

You can chain selectors so b.Workers

#

yes

#

b will be evaluated, then if its found Workers will be evaluated and the value from b will be available to check against

calm summit
#

So that would give me true and I will have access to Workers string?

    public string selector = "b";

    public bool TryEvaluateSelector(ISelectorInfo selectorInfo)
    {
        if (selectorInfo.SelectorText != selector)
pliant reef
#

Yes you do have access to Workers however it is better to think of each selector as an independent task. So b is one and Workers is the other.

#

If you want to handle both then you just return an object that contains some info you need for the next selector, Then it will come back to you

calm summit
#

Don't think it's a good idea to create selector for literally each possible variable

pliant reef
#

You want a single selector for b.Workers?

calm summit
#

yeah, user defines which ViewModel (with properties) gets which StringTable in UI Authoring

#

so I don't want to do the same in tables

#

or in binding path

#

kind of like this

#

binding is done through text e.g. <ui:Button text="#Settings" display-tooltip-when-elided="true" name="Settings" />
So assuming Settings key in MainMenu string table has some variables. I don't want user to define anything else in UXML

#

and instead define Property names in localized string

pliant reef
#

ok I see. So "Settings" is the key to an entry in the String Table?

calm summit
#

so if for example MainMenuViewModel has property CurrentTime
And localized string for Settings key would be Settings {CurrentTime}
I want it to be all user has to do

pliant reef
#

And at the moment the LocalizedString is unable to query the MainMenuViewModel properties?

calm summit
#

but so far the best I managed:
#Settings:CurrentTime
With localized Settings {0}

calm summit
#
FormattingException: Error parsing format string: Could not evaluate the selector "Workers" at 10
Workers: {Workers}
----------^
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@1.4.2/Runtime/Smart Format/SmartFormatter.cs:347)
#

hmmm

#

I got some idea

#

to test

pliant reef
#

ok. It does sound like a custom source that accesses MainMenuViewModel would be a good approach. If you want access to the raw string then you can use LocalizationSettings.StringDatabase.GetEntry

calm summit
#

Nah, this does not help either

            table.TableChanged += OntableChanged;
            _string = new LocalizedString(table.TableReference, key);
            _string.StringChanged += OnChange;
        }

        private void OntableChanged(StringTable value)
        {
            Debug.Log("TableChange");
        }

        private void OnChange(string value)
        {
            Debug.Log("StringChange");
        }
#

StringChange happens before TableChange, thus exception will be thrown first

pliant reef
#

ohh. Ok we have a new feature in 1.4.2 called ITablePostprocessor. That could work.

#

It is called after loading the table but before anything is done with the table

calm summit
#

๐Ÿค”

#

it says after game is built, but using Editor namespace

pliant reef
#

Thats the part that assigns it. The top example is the runtime part

#

You can also apply it at runtime. Its just this example shows how to assign it so you never need to assign it again. It will be part of the settings

calm summit
#

Yeah, that worked

    [Serializable]
    public class Kek : ITablePostprocessor
    {
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
        private static void Register()
        {
            // Create an instance of the table provider.
            var provider = new Kek();

            LocalizationSettings.StringDatabase.TablePostprocessor = provider;
        }

        public void PostprocessTable(LocalizationTable table)
        {
            Debug.Log("PostProcess");
        }
    }
#

it is indeed called before exceptions

pliant reef
#

๐Ÿ˜Œ

calm summit
#

so

#

that called

#

when language is already chosen

#

and table is loaded

#

so I can access localized strings?

pliant reef
#

Yes.

#

From that table

calm summit
#

that could work

pliant reef
#

Not all tables may have loaded

calm summit
#

Is there meant to be only single TablePostProcessor?

pliant reef
#

Yes but you can always create a version that is a list of them.

#

Its just a callback. In hindsight it could have been an event ๐Ÿค”

calm summit
#

it's more of a thing is that I'm providing a framework here, so

pliant reef
#

Although it would not be serialzable if it was

calm summit
#

premaking something is not really I'm looking for, especially if it's includes tweaking project settings in a ny way

pliant reef
#

ah yes. Well if you are assigning it at runtime you could always keep track of the old value if one is assigned and call it after your callback has executed

calm summit
#

๐Ÿค”

#

any idea when that callback is made then?

#

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]

#

So I assign as late as possible

#

so in a very case nothing overrides my instance

pliant reef
#

The callback could be executed during Start from the LocalizedBehavior which uses ExecuteInEditMode

#

I think that is after your callback

calm summit
#

I see

#

all right, let's see what I can do

pliant reef
#

Ill make a note to support multiple providers in the future so you dont have to worry about overwriting.

calm summit
#

yeah, modding would love that

calm summit
#

new LocalizedStringTable("Story");
I'm guessing that how I can create any table during any moment after PostProcessor callback happened?

#

since in Authoring I have LocalizedStringTable
while Postprocessor has LocalizationTable
no direct reference

#

๐Ÿค”

#

yeah, I'm not sure how to get Entries without tableChanged callback on LocalizedStringTable

#

nvm, turns out it's already updated by that moment

pliant reef
#

You want to make permanent changes to the tables during this callback?

pliant reef
#

ah great ๐Ÿ‘

calm summit
#

what I am trying to do now

#

is analyzing every entry that is .IsSmart

#

and getting all braces from it {value}

#

so I can attach variables

#

with that value

#

before Unity throws exceptions at me for unresolved variable

pliant reef
#

You can parse it using the Smart string system to extract the variables instead of checking for braces

calm summit
#

What system?

pliant reef
#

LocalizationSettings.StringDatabase.SmartFormatter.Parser.ParseFormat

#

LocalizationSettings.StringDatabase.SmartFormatter.Parser.ParseFormat("TEXT", LocalizationSettings.StringDatabase.SmartFormatter.GetNotEmptyFormatterExtensionNames());

calm summit
#

that method looks big

#

I implented a way simpler one ๐Ÿ˜…

#
        public static string[] GetFormatKeys(string str)
        {
            var count = 0;
            // -4 is because after opening `{@` there needs to be at least 1 char for key, and 1 char to close braces.
            for (var i = 0; i < str.Length - 4; i++)
            {
                var chr = str[i];
                if (chr is '{' && str[i + 1] is '#') count++;
            }

            if (count > 0)
            {
                var resultInd = 0;
                var result = new string[count];
                for (var i = 0; i < str.Length - 4; i++)
                {
                    var chr = str[i];
                    if (chr is '{' && str[i + 1] is '#')
                    {
                        i++;
                        var substr = str[i..];
                        var ind = substr.IndexOf('}');

                        if (ind <= 0)
                        {
                            throw new StringParsingException(
                                $"Invalid format provided: {str}. No enclosing brace was found.");
                        }

                        result[resultInd] = substr[..ind];
                        resultInd++;
                        i += ind;
                    }
                }

                return result;
            }

            return null;
        }
pliant reef
#

Ah yes you have your own format ๐Ÿ™‚

calm summit
#

๐Ÿฅด

pliant reef
#

oh no. You are loading a table inside of the callback?

calm summit
#

yeah

pliant reef
#

Its going to keep calling into your processor causing the recursion

calm summit
#

๐Ÿค”

#

Sometimes it doesn't

pliant reef
#

If its already loaded it wont

calm summit
#

how else can I obtain Table then?

pliant reef
#

If it needs to be loaded it will

calm summit
#

ah

#

wait

#

if (_tableCollectionName == tableCollectionName)

#

I do this check

#
        public void PostprocessTable(LocalizationTable table)
        {
            onTableLoaded?.Invoke(table.TableCollectionName);

            _previousValue?.PostprocessTable(table);
        }
#

which is obtained from here

#

so I obtain table only if it is being postprocessed

pliant reef
#

Is that causing the recursion?

calm summit
#

not sure

#

I don't think there's recursion

pliant reef
#

its something with a string comparison I think

calm summit
#
StackOverflowException: The requested operation caused a stack overflow.
System.String.Equals (System.String a, System.String b, System.StringComparison comparisonType) (at <8f06425e63004caf99a79845675f751e>:0)
UnityEngine.Localization.LocaleIdentifier.Equals (UnityEngine.Localization.LocaleIdentifier other) (at ./Library/PackageCache/com.unity.localization@1.4.2/Runtime/Locale.cs:162)
System.Collections.Generic.GenericEqualityComparer`1[T].Equals (T x, T y) (at <8f06425e63004caf99a79845675f751e>:0)
System.ValueTuple`2[T1,T2].Equals (System.ValueTuple`2[T1,T2] other) (at <8f06425e63004caf99a79845675f751e>:0)
System.Collections.Generic.GenericEqualityComparer`1[T].Equals (T x, T y) (at <8f06425e63004caf99a79845675f751e>:0)
System.Collections.Generic.Dictionary`2[TKey,TValue].FindEntry (TKey key) (at <8f06425e63004caf99a79845675f751e>:0)
System.Collections.Generic.Dictionary`2[TKey,TValue].TryGetValue (TKey key, TValue& value) (at <8f06425e63004caf99a79845675f751e>:0)
UnityEngine.Localization.Settings.LocalizedDatabase`2[TTable,TEntry].GetTableAsync (UnityEngine.Localization.Tables.TableReference tableReference, UnityEngine.Localization.Locale locale) (at ./Library/PackageCache/com.unity.localization@1.4.2/Runtime/Settings/Database/LocalizedDatabase.cs:371)
#

yes

pliant reef
#

ohhhh

calm summit
#

so... Any alternative ways of obtaining entries?

pliant reef
#

Hmm im confused about that error. I cant see why its causing an overflow.

#

Is that the full callstack?

calm summit
#

hold on, I'm rebuilding to test

#

damn, nvm me

#

It is recursion

#

๐Ÿค”

pliant reef
#

Yeah StackOverflow is usually recursion

calm summit
#

_previousValue?.PostprocessTable(table);
ah yes

#

no domain reload

#

no static reload

#

_previousValue = LocalizationSettings.StringDatabase.TablePostprocessor;
But seems like this is at Unity's fault as well. Since that's the only way I obtain value

pliant reef
#

oh

#

Do a check on _previousValue to make sure its not the same

calm summit
#

Yeah, this should do I hope

    public class TablePostprocessor : ITablePostprocessor
    {
        private static ITablePostprocessor _previousValue;

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
        private static void Register()
        {
            _previousValue = LocalizationSettings.StringDatabase.TablePostprocessor;
            LocalizationSettings.StringDatabase.TablePostprocessor = new TablePostprocessor();
        }

        public static event TableLoadedEvent onTableLoaded;

        public void PostprocessTable(LocalizationTable table)
        {
            onTableLoaded?.Invoke(table.TableCollectionName);

            if (_previousValue is not null && _previousValue != this)
            {
                _previousValue?.PostprocessTable(table);
            }
        }
    }
pliant reef
#

๐Ÿ‘

#

You may want to do a check on the type instead of this. if _previousValue is not TablePostprocessor

#

In case its already been assigned somewhere else

calm summit
#

Damn. What can I use so it's not parsed as selector or formatter?

ParsingErrors: The format string has 1 issue:
'0x23': There are trailing operators in the selector
In: "Restrooms: {#Restrooms}"
#

some prefix

pliant reef
#

For the #?

#

๐Ÿค”

#

You could add # as an allowed selector

#

This will now expect your selectors to start with #

calm summit
#

but won't it trigger looking for one?

#

since I have none

pliant reef
#

Isnt that what you want? Or you want to do a find and replace to add the correct selector in?

calm summit
#

I guess I'll just do it without selectors at all

#

I kind of wanted to have some prefix to explicitly show that binding is done to ViewModel

#

not some variable or anything

pliant reef
#

You could use the custom source. If the selector starts with # then you handle it

calm summit
pliant reef
#

Ah I see.

#

You could always add your source the same way you add the post processor

calm summit
#

yeah, that could be solution, but for now I just want to make it work

pliant reef
#

It does sound like what you need is a custom source. The source is responsible for converting the selector into an object. Yours would do that through the bindings

calm summit
#

yay, it works
Now all authoring is required - just key of entry

        <ui:Label text="#Workers" name="Workers"/>
        <ui:Label text="#Military" name="Military"/>
        <ui:Label text="#Scientists" name="Scientists"/>
        <ui:Label text="#Budget" name="Budget"/>
        <ui:Label text="#Barracks" name="Barracks"/>
        <ui:Label text="#Restrooms" name="Restrooms"/>
        <ui:Label text="#Labs" name="Labs"/>

Aaaand it binds to ViewModel

#

thank you so much, sir

pliant reef
#

Great news ๐Ÿ™‚

#

How did you solve it in the end?

calm summit
#

through horrible reflection for now ๐Ÿ˜…

#

I'll optimize it for primitives, using explicit generics though, so there will be way less PropertyInfo.GetValue calls

pliant reef
#

What is GetFormatKeys?

calm summit
pliant reef
#

oh right

#

I thought it was reflection

#

interesting. Nice work ๐Ÿ‘

calm summit
#

I want to implement nested variables support

#

and also support of other selectors

pliant reef
#

You want to parse the string to extract that info?

calm summit
#

Yeah, so it looks like this

        private void OnTableLoaded(string tableCollectionName)
        {
            if (_tableCollectionName != tableCollectionName) return;

            var table = _table.GetTableAsync().Result;
            var entry = table[_ls.TableEntryReference.Key];
            if (entry is null || !entry.IsSmart) return;

            var format = entry.Value;
#

I have raw entry

pliant reef
#

Try LocalizationSettings.StringDatabase.SmartFormatter.Parser.ParseFormat

#

It will return a Format that contains everything. You then iterate through the Items1

#

Nested will contain another Format in the Items

calm summit
#

๐Ÿค”

pliant reef
#

com.unity.localization\Runtime\Smart Format\SmartFormatter.cs shows the full process of parsing and formatting a string

#

Start at line 189

#

var formatParsed = Parser.ParseFormat(format, GetNotEmptyFormatterExtensionNames());

#

Then line 300

#

Step through each item

calm summit
#

hmmm, I got another trouble. For some reason some tables are not loaded and my hook into postprocessor does not trigger

#
        public LocalizedStringBinding(LocalizedString ls, INotifyPropertyChanged binding)
        {
            _ls = ls;
            _table = new LocalizedStringTable(_ls.TableReference);
            _tableCollectionName = ls.TableReference.TableCollectionName;
            TablePostprocessor.OnTableLoaded += OnTableLoaded;
        }

        private void OnTableLoaded(string tableCollectionName)
        {
            if (_tableCollectionName != tableCollectionName) return;
#

so, I have stored LocalizedString in other table

#

and either it's somehow preloaded, that's why there's no postprocessing

#

or there's something else

pliant reef
#

The postprocessing is always called, it should never be skipped. Maybe you are not registered at that time?

calm summit
#

I do it during approximately Start

pliant reef
#

Is this in playmode?

calm summit
#

yes

#

tables are definetely not loaded instantly, as I see wrong texts

#

but there's different thing about those

pliant reef
#

We also do init in Start so it could be getting missed. It could be a script execution order issue. You could also try in Awake

calm summit
#

they are stored as LocalizedString

#

in ScriptableObject

calm summit
pliant reef
#

oh ๐Ÿ™ƒ

calm summit
#

but they should not be loaded anyway

#

because it's async

#

while my init and binding is instant

pliant reef
#

The async stuff can be instant at times, especially in the editor when using the fast addressables play mode

calm summit
#

๐Ÿค”

#

is that one to choose for proper emulation of build?

pliant reef
#

fastest will be instant, use existing will be slowest. Im not sure about simulate ๐Ÿ˜‰

#

I would use the bottom and use the actual data

#

if its built

calm summit
#

nah

#

does not work either

#

basically I attached breakpoint here

pliant reef
#

Can you attach a breakpoint to com.unity.localization\Runtime\Settings\Database\LocalizedDatabase.cs line 722 in PatchTableContents?

calm summit
#

didn't trigger

#

allthough

#

I think

#

there's smth wrong with breakpoint here

pliant reef
#

hmm. May be because its a package. One thing you can do is move the package into the project. Copy it from the package cache folder into the project Packages folder. You can then make changes and debug. Once you are done just delete the package and it will switch back to the cache

calm summit
#

just some backup ๐Ÿ˜…

  "dependencies": {
    "com.unity.localization": "1.4.2"
  },
pliant reef
#

We also have 1.4.3 now with some small fixes ๐Ÿ™‚

#

You shouldn't need to change the manifest file

calm summit
#

it's my package

pliant reef
#

ah

#

I think it should still allow the above approach. If it detects the same version in the project then it should switch to that

#

I do it all the time when debugging addressables ๐Ÿ™‚

calm summit
#

ahem even with local package it won't be recognized, odd

pliant reef
#

strange

#

Check that you are generating a cs proj file for the package

#

in Preferences/External Tools

calm summit
#

ah, right

pliant reef
#

Maybe also regenerate the project file to be safe

calm summit
#

ok, it got hooked

#

aaand, my tables aren't here

#

Once again, I don't reference those tables anywhere

#

in my code

#

I use LocalizedString directly

#

in ScriptableObject even

pliant reef
#

The table is null in the callback?

calm summit
#

the correct table is never inside callback

#

callback goes through all my tables that are referenced

pliant reef
#

Im not sure I understand. The table is loaded but the callback never fires?

calm summit
#

wait a second

#

it fires on demand

#

hmm

pliant reef
#

oh so you are not using the table yet?

#

If its not marked as preload it wont be loaded until its needed

calm summit
#

bruh, now it works

#

I am confused

#

but at least now I can continue with actual work

pliant reef
#

๐Ÿ‘

calm summit
#

oh wow, looks like everything is pretty much ready to grab. Just gotta figure out how it works now

#

one thing that I am intesrested is whether there's a way to figure, whether placeholder is already resolved (global variable for example)

#

or maybe some local variable was created before (but that's not critical)

pliant reef
#

No thats not possible without formatting and passing in the data. It gets especially complicated when dealing with implicit formatters/source where a different formatter/source may be used based on the data passed in

calm summit
#

I see...

pliant reef
#

E.G

calm summit
#

I guess one way is to just ignore everything that uses all global variables keys

#

at the start of a string

#

at least this way I won't mess with global variables

pliant reef
#

Potentially however there could be times when it wont work.

#

E.G "My {global.value}". if You happen to pass in a class instance with a field called "global" and the reflection source is before the persistent variables source then it would use the class instead

calm summit
#

well yeah, but I don't think that would be the case

#

or at least

#

there will be a warning about such niche cases

#

on the other hand

#

I can just parse everything

pliant reef
#

Yeah it really depends on the project. If you know its safe then you are fine

calm summit
#

and if there is variable called g then why not parse it for target property?

#

but annoying part - that makes parsing heavy

#

and loading longer

pliant reef
#

Yes its tricky to do in a generic way without the source data.

#

Its even possible to change global variables at runtime, such as different ones for each scene. So you may not have a true picture of what is a global variable

calm summit
#

well, I will have to get them from runtime so

#

but anyway, I'll get to work now

calm summit
#

so...
if I have a key like this {Settings.LabCost}
Is there a way I can make a nested dictionary of string,IVariable or smth similiar to bind it to LocalizedString?

pliant reef
#

You can add a LocalizedString as a persistent variable

calm summit
#

but I don't have LocalizedString

#

I am binding abstract types

pliant reef
#

Can you control what data is passed into the format?

calm summit
#

Here's generally what I do

                    var getter = DelegateUtility.Get(property, target);

                    var stringVariable = new StringVariable { Value = getter().ToString() };

                    var name = property.Name;
                    var action = new PropertyChangedEventHandler((_, args) =>
                    {
                        if (args.PropertyName != name) return;
                        stringVariable.Value = getter().ToString();
                    });

                    var targetObject = (INotifyPropertyChanged)target;
                    targetObject.PropertyChanged += action;

                    _bindings.Add((targetObject, action));
                    _ls.Add(key, stringVariable);
pliant reef
#

You can use a dictionary although im not sure it will work with LocalizedString

calm summit
#

that works on non-nested keys

pliant reef
#

Maybe try creating a Nested Variables Group

calm summit
#

oh

#

I totally forgot it exists

calm summit
#

So... simple nesting through . works fine, but it appears that parser does not split more complex nested variables

#

{NestedFloat} Nested bool = {NestedBool}

#

original Nested float = {Group:{NestedFloat} Nested bool = {NestedBool}}

#

how does built in parser work with that?

calm summit
#

๐Ÿค”

#

Maybe I shouldn't create variables

#

and instead I could attach custom source

#

to bound LocalizedStrings

#

that certainly seems like a cleaner approach

#

I didn't really liked the approach at first, as I didn't use LocalizedString atm

calm summit
#

nah, that won't be good for perfomance during runtime, I should parse string myself then.
But I am yet trying to figure out how to properly parse whole string, as so far Parse method doesn't do recursive

pliant reef
#

Sorry I missed your messages. Are you still having issues with nesting?

calm summit
#

Kind of. I figured how it works generally and found an interesting way:
I'll add argument to LocalizedString, which will be detected by ISource
And I'll subscribe to both: table postprocess event and string changed event. This way I'll be able to know whether bindings are dirty or not.
Now I don't have to parse everything myself and my own parser will simply be fed with already parsed hierarchy.
So basically, for now I'm just figuring how to properly generate variables the way nesting will be supported.
public class BindingGroup : IVariableGroup, IVariable current attempt - my own group with binding logic.

pliant reef
#

Sounds interesting. If you have any feedback on how things can be improved I'm happy to hear it ๐Ÿ™‚

calm summit
#

as far as I understand, there are only 2 splitters: : and .

pliant reef
calm summit
#

here example. current selector is a group. But I'm not sure how to figure whether it's one

pliant reef
#

Where do you need to figure this out? In a custom source?

calm summit
#

yeah

#

๐Ÿค”

#

maybe I don't need to figure it

#

and just parse it as a variable of type group

#

nvm

#

won't be possible without knowing it

pliant reef
#

I think you can find out if you are currently nested by checking if the parent is null

calm summit
#

hmm

pliant reef
#

Yes the FormattingInfo Parent field should tell you

calm summit
#

but here's the thing

#

how can I know that I don't need to parse it as variable?

#

This is kind of non-trivial as I initially thought

pliant reef
#

I thought you were using # to indicate thats it needs handling? Does that not work?

calm summit
#

nah, I didn't do that

#

I just try to add variables, so that next group can parse them

#

Persistent one

#

allthough

#

maybe I will need some indicator for binding

pliant reef
#

I think an indicator would make it easier to parse

calm summit
#

hmm, I see. It's parsed as operator

#

interesting

pliant reef
#

That depends on the configuration. It can be part of a selector

#

You can add it to the AllowedSelectorChars

#

In the settings

calm summit
#

yeah, I think that's the way. At least that would make development much easier and straightforward

calm summit
#

Nested float = {#Group:{>NestedFloat} Deep bool = {#Deep:{>NestedBool} Deep float = {>NestedFloat}}}
For some reason this cannot be parsed.

ParsingErrors: The format string has 1 issue:
Format string is missing a closing brace
#

and I believe this could be bug

#

huh

#

if I add a space before last brace it works

#

aside from that everything seems to work

pliant reef
#

Yes this is by design. By default it uses the String.Format rules where }} and {{ is used to represent a literal } and {. You can enable the Alternative Escaping option in the Smart Format settings so that it always requires a \ before a literal, so \{ and \}. You will then be able to do {{ and }}.

calm summit
#

oh, huh

#

wait, so if I use }}}}} that would work then?
I should check

#

no, it doesn't. Is there any white space symbols in Localization?

pliant reef
#

What do you mean?

#

}}}}} will become 2 literal } and a placeholder end

#

E.G Hello {{{{{variable}}}}} would become Hello {{123}} if variable = 123

calm summit
#

allthough not really an issue

pliant reef
#

oh nice! Hmm im not sure how to avoid the whitespace issue. Maybe theres some unicode character that can be inserted that wont be visible?

#

When you bind in the Uxml how do you know what table the Key belongs to?

calm summit
#

here my own game as example

pliant reef
#

ah very nice ๐Ÿ™‚

calm summit
#

Hmm. I have encountered very odd behavior.
Is locale switch supposed to Refresh all strings?
This causes errors, due to the fact my arguments are assigned to specific LocalizedStrings only, while language switch refreshes all of them without any reference to my argument (binding)

calm summit
#

huh, seems to be fixed in 2022.2.5

#

that was fast ๐Ÿ˜…

calm summit
#

@pliant reef hello sir once again.
I encountered another challenge: localized asset binding.
The problem - I can't find a way to determine table's asset type with only having LocalizedAssetTable reference.
Do you happen to know whether it's even possible to determine table type?

calm summit
#

all right, seems like Addressables.LoadAssetAsync<Object> works just fine and provides me type. Allthough it's not clear how can I get this type during binding yet, but I have some ideas