Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How to add timestamp columns into the Localization String Tables

Discussion in 'Localization Tools' started by cameronjohnson-mz, Jul 28, 2023.

  1. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Is there a way to add a string creation timestamp and modified timestamp column into the localization string tables?
     
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    cameronjohnson-mz likes this.
  3. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    @karl_jones I'm trying to add a `lastUpdated` timestamp column into a table so I can filter the data after it's exported.

    The doc above has a new class, but I'm not sure where that file should go or how to attach to the metadata to an `en` locale in a string a table and set the metadata automatically every time the value is modified.
     
    Last edited: Aug 19, 2023
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    You would do something like this:

    Code (csharp):
    1. // Get the collection
    2. var collection = LocalizationEditorSettings.GetStringTableCollection("My String Table");
    3.  
    4. var englishTable = collection.GetTable("en") as StringTable;
    5. var entry = englishTable["My Entry"];
    6. var metadata = entry.GetMetaData<MyTimeStampClass>();
    7. if (metadata == null)
    8. {
    9.     metadata = new MyTimeStampClass();
    10.     entry.AddMetadata(metadata);
    11. }
    12.  
    13. metadata.TimeTamp = // Some new timestamp
    14.  
    15. // Mark table dirty so changes are saved
    16. EditorUtility.SetDirty(englishTable);
    As for setting the value every time its modified it depends on how you are modifying it. If its done through a script it should be fine but if you want to detect when the entry is modified then it may get a bit more complicated. You could try using some of the editor events. https://docs.unity3d.com/Packages/c...or.Localization.LocalizationEditorEvents.html
     
    cameronjohnson-mz likes this.
  5. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Okay, I'll give that a shot.

    Is there way get the Project Locale Identifier e.g the source locale from the LocalizationEditorSettings?
     
  6. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Proj
    Thats in the LocalizationSettings - LocalizationSettings.ProjectLocale
     
    cameronjohnson-mz likes this.
  7. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    I had to sync the LocalizationSettings before `ProjectLocale` was available.

    Thank you for help.

    Code (CSharp):
    1. public static async void syncLocalizationSettings()
    2.     {
    3.         await LocalizationSettings.InitializationOperation.Task;
    4.     }
    5. public static void Export()
    6.     {
    7.         syncLocalizationSettings();
    8.  
    9.         Locale sourceLocale = LocalizationSettings.ProjectLocale;
    10.         if (sourceLocale == null)
    11.         {
    12.             Debug.LogError("The source language is empty.");
    13.             return;
    14.         }
    15.         var sourceColumnMapping = getSourceColumnMapping(sourceLocale.Identifier.Code);
    16. ...
    17.  
     
    karl_jones likes this.
  8. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    That's strange. You should not need to sync it just for the project locale, that's already set without initialization.
     
  9. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    hmm, I can give it an extra look. Is it set in the com.unity.localization 1.3.2 version.
     
  10. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Sorry, my mistake you do need to sync. It needs the available locales.
     
  11. tina_gifts5

    tina_gifts5

    Joined:
    Jul 5, 2022
    Posts:
    2
    Hi @cameronjohnson-mz! If you want to track changes in your source texts and translations, I would suggest managing translations in an external tool, where you can collaborate on translations and see more metadata.

    Localization tools also allow you to run QA checks - that will check for typos, wrong terms used, extra spaces, etc. Of course, the tool should integrate with Unity so everything is automated. Just so I don't ramble here, here's a reference on using a Unity loc package+Crowdin localization tool. It might give you more context on how you can manage your translations.
     
  12. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Hi Tina, yes we intend to use an external tool to manage our text. Our general workflow is game designers will enter source text and metadata (e.g Speaker, Listener, ItemType) into a String Asset Table, then export the data into a CSV which we send to translators, then import the translations.

    Since the `lastModified` column doesn't exist, but we might be able to use a `translationStatus` metadata column to assist filtering the text keys.

    How do I attach an `IMetadata` interface to a String table?

    Do I need to create a class (e.g a `myMetadataCols.cs` script) and then how do I attach it to all TextMeshPro objects so the metadata columns appear?
     
  13. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    @karl_jones just to confirm, if I create this class, these metadata columns will or won't show up in the export CSV file?
    Code (CSharp):
    1. [Metadata(AllowedTypes = MetadataType.StringTableEntry)]
    2.  
    3. public class LocMetadataColumns : IMetadata
    4. {
    5.         public string description;
    6.         public string speaker;
    Export headers

    Key,Id,English (United States)(en-US),German (Germany)(de-DE), description, speaker
     
    Last edited: Sep 28, 2023
  14. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
  15. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Okay that makes sense, I'll do that after getting the metadata into the table.

    But I don't know where I am supposed to enter the data from the UI after creating the metadata class.
     

    Attached Files:

  16. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    You can edit the metadata for a table or entry by clicking the Metadata buttons {o} in the tables window.
     
    cameronjohnson-mz likes this.
  17. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Ok I found the area to add the metadata to the source locale, but I don't see an option to add custom CSV columns in the Table Editor and custom columns are mentioned in the column mapping docs.
    https://docs.unity3d.com/Packages/c...zation.Plugins.CSV.Columns.ColumnMapping.html

    I wan't to do something like this, but I know this doesn't work
    Code (CSharp):
    1. columnMappings.Add(new CsvColumns
    2.         {
    3.             FieldName = "English (United States) (en-US) Description",
    4.         });
     

    Attached Files:

    Last edited: Sep 29, 2023
  18. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    The custom columns can either be added via code when exporting or to the String TableCollection asset under the extensions list in the inspector, add a CSV extension and you can configure the columns.
     
    Last edited: Sep 29, 2023
    cameronjohnson-mz likes this.
  19. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Yeah I added the CSV extension, but I don't see the option for custom columns, my only options are Key Id and Locale Columns.

    Also, I copied the CSVCustomExample class and it throws these errors. I see some docs mention a CSVHelper, but haven't installed that.

    error CS0246: The type or namespace name 'CsvReader' could not be found (are you missing a using directive or an assembly reference?)

    error CS0534: 'CsvCustomColumnsExample' does not implement inherited abstract member 'CsvColumns.ReadBegin(StringTableCollection, CsvReader)'
     

    Attached Files:

    Last edited: Sep 29, 2023
  20. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    CsvReader comes from a third party library we use, you need to add a reference to our third-party dll. Take a look at Unity.Editor.Localization.asmdef and copy the third party reference it uses. This should fix this compilation errors.
    Once the errors are fixed you should see your new column in the list of available columns to add.
     
  21. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    So it's using `Unity.Localization.ThirdParty.Editor.dll` but where am I suppose to paste the reference? This is the metadata file.

    Do I need a CsvColumns class object along side the IMetadata class to access the metadata field names in the UI.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Text;
    4. using UnityEngine;
    5. using UnityEngine.Localization;
    6. using UnityEngine.Localization.Metadata;
    7. using UnityEngine.Localization.Tables;
    8. using UnityEditor.Localization;
    9. using UnityEditor.Localization.Plugins;
    10. using UnityEditor.Localization.Plugins.CSV;
    11. using UnityEditor.Localization.Plugins.CSV.Columns;
    12. using UnityEngine.Localization.Settings;
    13.  
    14. [Serializable]
    15. [Metadata(AllowedTypes = MetadataType.StringTableEntry)]
    16.  
    17. public class LocMetadataColumns : IMetadata
    18. {
    19.         public string description;
    20.         public string speaker;
    21. }
    22. public class CsvCustomColumnsExample : CsvColumns
    23. {
    24.         [SerializeField] LocaleIdentifier m_LocaleIdentifier;
    25.  
    26.         int m_SomeValueIndex, m_SomeOtherValueIndex, m_CollectionTableIndex;
    27.         StringTable m_ImportTable;
    28.  
    29.         string SomeValueFieldName => m_LocaleIdentifier + " Some Value";
    30.         string SomeOtherValueFieldName => m_LocaleIdentifier + " Some Other Value";
    31.  
    32.     public override void ReadBegin(StringTableCollection collection, CsvReader reader)
    33.     {
    34.         m_ImportTable = collection.GetTable(m_LocaleIdentifier) as StringTable;
    35.         if (m_ImportTable != null)
    36.         {
    37.             m_SomeValueIndex = reader.GetFieldIndex(SomeValueFieldName, isTryGet: true);
    38.             m_SomeOtherValueIndex = reader.GetFieldIndex(SomeOtherValueFieldName, isTryGet: true);
    39.         }
    40.     }
    41. }
     
    Last edited: Sep 29, 2023
  22. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Your script needs to be inside of an asmdef. Add one at the root directory if it's not already inside of an asmdef and then you can setup the references.
     
  23. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Ok, and do I need a CsvColumns class object along side the IMetadata class to access the metadata field names in the UI so it can be added to the CSV extension?
     
  24. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Yes or you need to add a reference to the assembly (asmdef) that the metadata belongs to. The CSV code is editor only so you would be better putting the metadata class into a separate asmdef.
     
  25. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Where in the UI do I add the reference to the third party Unity.Localization.ThirdParty.Editor.dll library because it gets deleted when I apply changes from the UI?

    Code (CSharp):
    1.  "precompiledReferences": [
    2.         "Unity.Localization.ThirdParty.Editor.dll"
    3.     ]
     

    Attached Files:

    Last edited: Sep 30, 2023
  26. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Looks like you have to set `Override References` to true and then add it to the `Assembly References`.

    However, adding it doesn't fix the error `error CS0246: The type or namespace name 'CsvReader'`
     
    Last edited: Sep 30, 2023
    karl_jones likes this.
  27. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Is the script Inside the folder or a sub folder with the asmdef? Also check that you are not missing a using definition in your code.
     
  28. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Yes the script and asmdef are next to each other.
     

    Attached Files:

  29. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Does your script have
    Code (csharp):
    1. using CsvHelper;
     
    cameronjohnson-mz likes this.
  30. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    That fixed it, thank you.

    1. When I open the String Table in the Inspector tab and click Save, the metadata columns are exported in the file, but when I Export from the Localization Tables tab, the columns don't show it up.

    Is possible to export them from the Table tab?

    2. In code in my Export function, how do I create a columnMapping to export metadata column?

    Code (CSharp):
    1.  
    2. var columnMappings = new List<CsvColumns>();
    3. columnMappings.Add(new LocaleColumns {
    4.    LocaleIdentifier = sourceLocale.Identifier.Code,
    5.    FieldName = sourceLocale.ToString(),
    6.    IncludeComments = false,
    7. });
    8. columnMappings.Add(new LocaleColumns {
    9.    LocaleIdentifier = "English (United States) (en-US) Description" //locale.Identifier + " Description"
    10. });
    11. Csv.Export(stream, stringTableCollection, columnMappings);
    12.  
    13.  
     
  31. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    No, you cant export from the table view, it doesn't know how to configure the custom columns, You could add some MenuItems to provide shortcuts.

    To create the column mapping you would add it to the columnMappings list:

    Code (csharp):
    1. columnMappings.Add(new CsvCustomColumnsExample
    2. {
    3.     LocaleIdentifier  = "en"
    4. });
     
    cameronjohnson-mz likes this.
  32. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    I'm getting a strange error even though I'm referencing the class in the script thats calling it.

    error CS0117: 'CsvCustomColumnsExample' does not contain a definition for 'LocaleIdentifier'

    I added `LocMetadataColumns` to the assembly definition file and reference it.
    Code (CSharp):
    1. using static LocMetadataColumns;
    LocMetadataColumns.cs
    Code (CSharp):
    1. public class LocMetadataColumns : IMetadata
    2. {
    3.         public string description;
    4.         public string speaker;
    5. }
    6. public class CsvCustomColumnsExample : CsvColumns
    7. {
    8.         [SerializeField] LocaleIdentifier m_LocaleIdentifier;
     
  33. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    If the column is representing a per locale value then you need to inherit from LocaleColumn, not CsvColumns.
     
  34. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    So by doing that I get a bunch of errors. For what it's worth, the metadata will only be attached to the "en-US" locale.

    The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(CsvCustomColumnsExample) m_LocaleIdentifier

    The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(CsvCustomColumnsExample) m_CollectionTableIndex

    The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(CsvCustomColumnsExample) m_ImportTable


    Code (CSharp):
    1. public class CsvCustomColumnsExample : LocaleColumns
    2. {
    3.         [SerializeField] LocaleIdentifier m_LocaleIdentifier;
    4.  
    5.         int m_SomeValueIndex, m_SomeOtherValueIndex, m_CollectionTableIndex;
    6.         StringTable m_ImportTable;
     
  35. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    I'm not really sure where to go from here since I can't get the columnMapping to work with
    CsvCustomColumnsExample code.
     
  36. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Can you share an example project with your setup? It's probably easier :)
     
  37. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    The project is a really basic, I have 2 scripts called UnityLocalizeSupport.cs and LocMetadataColumns.cs. I populated the String Table and the goal is to Export the data using code for automation.

    Code (CSharp):
    1. [Serializable]
    2. [Metadata(AllowedTypes = MetadataType.StringTableEntry)]
    3.  
    4. public class LocMetadataColumns : IMetadata
    5. {
    6.         public string description;
    7.         public string speaker;
    8. }
    9. public class CsvCustomColumnsExample : LocaleColumns
    10. {
    11.     [SerializeField] LocaleIdentifier m_LocaleIdentifier;
    12.     int m_SomeValueIndex, m_SomeOtherValueIndex, m_CollectionTableIndex;
    13.     StringTable m_ImportTable;
    14.  
    15.     string SomeValueFieldName => m_LocaleIdentifier + " Description";
    16.     string SomeOtherValueFieldName => m_LocaleIdentifier + " Speaker";
    17.  
    18. // the remaining functions match the example.
    19.  
    20.  
    Code (CSharp):
    1. public class UnityLocalizeSupport
    2. {
    3.     public static List<CsvColumns> getSourceColumnMapping(string locale)
    4.     {
    5.         var columnMappings = new List<CsvColumns>();
    6.         Locale sourceLocale = LocalizationEditorSettings.GetLocale(locale);
    7.         if (sourceLocale == null)
    8.             return columnMappings;
    9. `
    10.         columnMappings.Add(new KeyIdColumns
    11.         {
    12.             IncludeId = true, // Include the Id column field.
    13.             IncludeSharedComments = false, // Exclude Shared comments.
    14.         });
    15.  
    16.         columnMappings.Add(new LocaleColumns
    17.         {
    18.             LocaleIdentifier = sourceLocale.Identifier.Code,
    19.             FieldName = sourceLocale.ToString(),
    20.             IncludeComments = false,
    21.         });
    22.  
    23.         columnMappings.Add(new CsvCustomColumnsExample
    24.         {
    25.             LocaleIdentifier = "en-US"
    26.         });
    27.  
    28.         return columnMappings;
    29.     }
    30. public static void Export(){
    31.  
    32.    foreach (var stringTableCollection in LocalizationEditorSettings.GetStringTableCollections()) {
    33.        using (var stream = new StreamWriter($"{stringTableCollection.TableCollectionName}.csv", false, Encoding.UTF8))
    34. {
    35.     Csv.Export(stream, stringTableCollection,      getSourceColumnMapping(sourceLocale.Identifier.Code);
    36.   }
    37. }
     
    Last edited: Oct 4, 2023
  38. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    Is this enough of an example to debug what's going on?
     
  39. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    @karl_jones I think I may have figured it by making the protected member public.

    Is it possible to add a Field Name so the header in the CSV will just be Description and not include the language and language code?

    Code (CSharp):
    1. public class CsvCustomColumnsExample : CsvColumns {
    2. [SerializeField] public LocaleIdentifier m_LocaleIdentifier;
    Code (CSharp):
    1.   columnMappings.Add(new CsvCustomColumns
    2. {
    3.      m_LocaleIdentifier = new LocaleIdentifier("en-US")
    4. });
     
    Last edited: Oct 5, 2023
  40. cameronjohnson-mz

    cameronjohnson-mz

    Joined:
    Jan 17, 2023
    Posts:
    25
    I figured out the WriteBegin function is where the headers are set so simply putting the string there works.
    Code (CSharp):
    1. writer.WriteField("Description");
    I think we're good here now. Thank you for the help @karl_jones.
     
    karl_jones likes this.
  41. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,845
    Ah great news. I was going to take a look at the project this morning, glad you managed to solve it yourself.