Hello,
Currently I am trying to create some tool on top of the localization package.
For that, I am writing my own localization manager, but I am struggling to have a selected locale within the LocalizationSettings instance in editor mode, wich I need for editing and creating localized tables.
None of the following callbacks seems to be triggered
Is there a way to force the LocalProvider to preload in editor mode ?
Hi,
The concept of a selected locale does not really exist in edit mode at the moment. We do have an editor API under UnityEditor.Localization.LocalizationEditorSettings. If you are doing any editor work then this is likely whatr you need.
For example UnityEditor.Localization.LocalizationEditorSettings.GetLocales() will return all the Locales in the project.
I assume the package workflow is supposed to be like:
create the collections and tables using the editor interface
add the localize components and bind them to the collections
at runtime, the locale will load at start, wich will update all the localize components, wich will update theirs respective targets (UI.Text, TextMeshPro, Image, âŚ)
I actually feel a bit wrong, localization is not something we do while setting up the scenes, but afterwards. Our artists will setup the UI and fill the text and images for their own language and are not supposed to care about localization at that point.
My objectives about my tool is :
a localization element I can add later, that element may not be a component since I would not only need it in GameObject, but also within ScriptableObject or EditorWindow (Actually I am embedding in a propertyDrawer).
I want the database to be generated from what the artists made
be able to visualize any screen for any locale in editor mode and not only at runtime. It means a shortcut to change the application locale and updating the UI elements when a new locale is setted (but as I said, the API is mainly about runtime)
the localization element have to works both in editor and runtime
possibility to export/import all strings as/from csv
a not-too-ugly editor layout (I am using Odin)
By the way, I now have regularly GameObjects âEventCollectorâ and âResourceManagerCallbacksâ spawning in my scene hierachy (attached file). It seems to be related to the adressables, and since the Localization package is the only one using it, can I assume it come from the Localization package (or some issue between the package and Odin) ?
Localization does not require components. We have LocalizedString, LocalizedTexture etc which are classes that can be used anywhere. Are you just localizing something for the player or also editor tools? Editor tools localization is supported but its not part of the localization package, its an editor feature.
At the moment we have the Localize option for UI, TMP etc. This currently adds a component and maps it to a string that matches the original text. In the future, we will improve this so that if the string does not exist it will add it to the database. We will also be adding a specialized version of UI and TMP instead of an additional component. The workflow would then be to swap the UI/TMP component with a localized version, find or add the original string in the db and map it across. Then this could be automated so that a scene could be automatically localized and the assets in the scene extracted, in the way you describe.
This is planned as part of the localized components(UI, TMP & UIElements) work.
At the moment we are working on import and export for google sheets and a few other formats. csv will be one although we may not be able to support metadata for all formats, some may have to just be key and values.
Yes this is addressables. Addressables does support loading in editor however this looks like a bug. Could you please file a bug report?
Localization is still very much in development, there are gaps in the API and workflows. We are still in a position where we can make big changes so if you have feedback or suggestions we are open to them. It always helps to have a use case or solid example of what you need and why.
Thanks for the feedback so far.
Actually the project I am working for is a template to create projects faster. Projects are touristic mobile applications with some gamification elements (like geolocated points on a map, points are mini games with an inventory shared to those points).
I am currently working on the localization and inventory tools (which rely on the localization).
I do have a very basic InventoryItem ScriptableObject class wich holds 2 strings (name and description) and an image. I did achieve to localize it but by referencing 2 LocalizedString and one LocalizedSprite. But those implementations do not allow a designer to edit them directly, he would have to open the Localization Table Editor Windows and I want to avoid that (as said in my previous post, he should not care about localization at that point).
That said, I will probably wreate an EditorWindow to handle my Inventory, but I still think designer will always be tempted to edit the ScriptableObject asset directly.
As reference, here is where I am (attached file)
âApplication Current Localeâ is the shorcut to change the application Locale, with the idea changing that will update the whole application as reaction.
âUpdate when locale changeâ is a way to disable the locale update on for the element listed below.
The âUpdate Databaseâ button will override the strings for the binds listed below for their corrsponding TextMeshPro element.
The âRead from Databaseâ button will override the textmesh pro texts with the values from the database.
âTextMeshPro Fieldsâ is a list of TextMeshProUGUI references associated with a Bind.
The Bind is actually similar to the Localization package LocalizeString, it is populated with the current tables and key in the Localization database. You can edit the path (format is tablename/key), if it does not exists in the database, the â+â button will allows to add it directly.
I plan to add more type list (direct string, images, etcâŚ) and an event on the class invoked when all elements references are updated(*)
(*) In the current package implementation, the update is per-element based, but imo its actually rarely the case in real world scenario, you want to update the UI or objects only when every backing fields are ready and have a single method to update everythings. This is the idea I followed here.
I realized I am heavily relying on the AssetTableCollection definition without noticing it, which is actually part of the UnityEditor.Localization namespace , meaning I canât do that way at runtime.
For the runtime, are the GetLocalizedStringAsync from the LocalizedStringDatabase and LocalizedStringGetLocalizedString() method the only ways to fetch localized string ?
Since it can only be used with TableReference and TableEntryReference or LocalizedString definitions so it is very limitating/locked for custom code.
Could you simply provide overloads using simple strings for the table and the key for runtime context ?
Have you tried? This should already work as we have implicit conversions.
TableReference can be a string or Guid and TableEntryReference string or uint.
Actually⌠no.
I saw theirs interface definition but assumed it had to be fully initialized to be used. The idea of creating an instance and just fill them like that didnât even crossed my mind to be honest (and if it did, I probably wouldnât even try assuming it would not be sufficient). I will try that. Thanks!
By the way, did you finished reading my yesterday post ?
Yes I read through it
The window to show the bindings is an interesting concept. I have some ideas I want to try out similar to this. An overview window to show all bindings in a scene. I think I understand what you are doing, would you be able to share an example when you have something? I find it much easier to understand when I play about with an example.
We are currently working on improving the editor side, converting AssetTableCollections into Editor only assets which should make things a little simpler. There are plans for editor support to change selected locale etc but that will come after the next release.
actually holds âGUID:bed3f1bfcb3f9fe459a58df7c1ff935eâ and an empty string as values. The keyid and Gui holds the expected values.
Is it the expected behavior ?
Yes. You can reference a table by either its string name or its name guid and a table entry with its key name or key id. This can be determined by the ReferenceType property.
We favour Guid and id by default as this means changes to the table name or key name will still allow us to keep the mapping(Guid and Id should never change).
So by far the safest option is to use Guid and Id however if you dont expect to make changes to table names or key names then using strings is fine.
For example, if you had a table named âMy_Game_Assetsâ and you then later decide to rename this table to âLevel_1_Assetsâ then you would need to go back into all the scenes that referenced the âMy_Game_Assetsâ table via a string and update them to reference the âLevel_1_Assetsâ table. However, if they referenced the table with a Guid then you would not need to change anything after the table name.
If you are familiar with our assembly definition assets (asmdefs) then its a similiar concept. They do give you an option to use guid or string, maybe we can do the same.
Edit:
Showing TableName as GUID is a bug, it should be empty (ââ). Ill make a bug for it.
The point was more I was expecting TableName to be âTestStringTableâ and Key to be âCountryâ as the LocalizedString appears as âTestStringTable/Countryâ in the inspector. The field do access these info since they are displayed in the editor when expanding the LocalizedString property (âTable Collectionâ and âEntry Nameâ). Are TableEntryReference.Key; and the entry name supposed to be 2 differents things ?
I understand the GUID and the entry keyid are more reliable, but the natural language string values could be accessible for convenience (and debug!) and filled once the bind is done.
Yes being able to see the string values would be good for debugging. Iâll look into how we can support this. Showing a table name from guid should be simple although showing a entry name is a little trickier as we donât have the table it belongs to in the struct.
ow I am supposed to read/write the database in editor mode without tableName and entryKey as strings ?
I didnât find relevant methods in LocalizationEditorSettings and I tried
The issue disapeared at some point and came back now. They were popping after Unity recompile and I opened my localization property in the inspector. Now this is when I click on a button.
I modified theirs scripts to make them run in editor with [ExecuteInEditMode] to get a call stack log:
InvalidOperationException: The following game object is invoking the DontDestroyOnLoad method: ResourceManagerCallbacks. Notice that DontDestroyOnLoad can only be used in play mode and, as such, cannot be part of an editor script.
MonoBehaviourCallbackHooks.Awake () (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/MonoBehaviourCallbackHooks.cs:13)
UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1:<.ctor>b__27_0(AsyncOperationHandle)
DelegateList`1:Invoke(AsyncOperationHandle) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/DelegateList.cs:69)
UnityEngine.AddressableAssets.ResourceProviders.InternalOp:OnCatalogLoaded(AsyncOperationHandle`1)
DelegateList`1:Invoke(AsyncOperationHandle`1) (at Library/PackageCache/com.unity.addressables@1.5.1/Runtime/ResourceManager/Util/DelegateList.cs:69)
UnityEngine.AsyncOperation:InvokeCompletionEvent()
I have not tested the localization loading side in editor mode, I suspect we may have some guards that prevent it.
Could you please file a bug report for the ResourceManagerCallbacks error, that will need to go to a different team.
So if this is an editor script I would suggest avoiding the Async methods and use the Editor code. It should be much simpler and wonât need to be async.
Here is a simple example
// This will get us all table collections that are StringTables.
var tableCollections = LocalizationEditorSettings.GetAssetTablesCollection<StringTable>();
// Now lets find the one we care about
var foundTableCollection = tableCollections.First(col => col.TableName == "My Table");
// Now lets get the table for English. Calling Tables will force all the table assets to be loaded so there may be a small pause here.
var englishTable = foundTableCollection.Tables.First(tbl => tbl.LocaleIdentifier == "en") as StringTable;
// We need to mark the table dirty so changes are saved. If a new key is created then the shared datas will also need to be dirty.
Undo.RecordObjects(new Object[] { englishTable.SharedData, englishTable }, "Added entry");
// Now we can get or set the value
var entry = englishTable.GetEntry("My Table Entry");
entry.Value = "Some New Value";
// Or do it like this which will also create the entry if it does not exist
englishTable.AddEntry("My Table Entry", "Some New Value");
The editor code is going through some refactoring at the moment, it needs some improvements and may change a little but this should get you what you need. Feedback is welcome
Sounds like you skipped the first line of my previous post
This was more or less what I was doing (but without linq nor Undo handling) but the point was I donât have the TableName and the KeyName as strings, only the guid and the entryid. Wich is frustrating since they appears in the inspector, so they are resolved somehow at somepoint.
I canât find a guid value in LocalizedTable or a way to get a LocalizedTable from an AssetTableCollection using a guid.
I am not familiar with the addressable API, but I guess I should not retrieve the LocalizedTable from it directly anyway.
Not sure there is any guard to prevent loading that way (or warnings/errors should be raised), AsyncOperation probably relies on Coroutines, which relies themselves on MonoBehavior update loops, since they are not called in Editor, they canât reach their completion point.