Exporting all tables to .csv?

Hi,

As far as I can tell, you can export every single Localization Table at once through the “Localization window”:

How exactly do you do this, and where is this window? I’ve only been able to export from the String Table window, which only exports one single .csv file at a time.

In general, I think a screenshot or two on the documentation site could really help here! :slight_smile:

Ah yeah you need to click the dots at the top.
7956543--1019439--upload_2022-3-11_11-7-54.png

We are working on UX improvements so ill make a note to look at improving this

1 Like

Thanks, but where exactly is that window? Only managed to find the export option for individual .csv files inside the Localization Tables window:
7956789--1019523--upload_2022-3-11_14-9-41.png
7956789--1019529--upload_2022-3-11_14-9-59.png

Would like to export every table, if that’s possible? Looks like it is from the documentation.

Hmm there is no option to do that. We do provide a code sample that does this though:

This expects the string table collections to have CSV Extensions on them.

[MenuItem("Localization/CSV/Export All CSV Files(With Extensions)")]
public static void PullAllExtensions()
{
   // Get every String Table Collection
   var stringTableCollections = LocalizationEditorSettings.GetStringTableCollections();

   foreach (var collection in stringTableCollections)
   {
       // Its possible a String Table Collection may have more than one extension.
       foreach (var extension in collection.Extensions)
       {
           if (extension is CsvExtension csvExtension)
           {
               if (!string.IsNullOrEmpty(csvExtension.File))
               {
                   using (var stream = new StreamWriter(csvExtension.File, false, Encoding.UTF8))
                   {
                       Csv.Export(stream, collection, csvExtension.Columns);
                   }
               }
           }
       }
   }
}

If you want to push them all with defaults you can do the following:

    [MenuItem("Localization/CSV/Export All CSV Files")]
    public static void ExportAllCsv()
    {
        // Get every String Table Collection
        var stringTableCollections = LocalizationEditorSettings.GetStringTableCollections();

        var path = EditorUtility.SaveFolderPanel("Export String Table Collections - CSV", "", "");
        if (string.IsNullOrEmpty(path))
            return;

        foreach (var collection in stringTableCollections)
        {
            var file = Path.Combine(path, collection.TableCollectionName + ".csv");
            using (var stream = new StreamWriter(file, false, Encoding.UTF8))
            {
                Csv.Export(stream, collection);
            }
        }
    }
1 Like

The second code snippet did exactly what I wanted. Thanks!
Would suggest adding this as a core feature, though, especially seeing as it’s possible to export all tables through the editor when choosing “Export XLIFF”:
7957053--1019604--upload_2022-3-11_15-46-42.png

Something similar for CSV would be perfect! Regardless, appreciate the help.

2 Likes

One final question - is there any way to export without the columns for comments? I have no comments in my tables so they end up empty:
7957125--1019619--upload_2022-3-11_16-19-56.png

I know I could remove them manually or with code, but is there any way to not include them when exporting? Thanks.

Did some poking around, stumbled upon ColumnMapping.CreateDefaultMapping(includeComments: false).
This achieves the desired result, however, I would like to be able to include only the column for Shared Comments without including comment columns for each locale. Is this possible somehow?

Yes the first column returned by ColumnMapping.CreateDefaultMapping will be a key column that has a property called IncludeSharedComments. You can enable this. Class KeyIdColumns | Localization | 1.1.1

1 Like

What does package developers expect game developers/designers use to edit string table? I find that excel cant handle a new line very well using csv. Also, excel export csv cant be recognized by importer.8446475--1120064--upload_2022-9-17_22-38-0.png

Excel export should work. What settings do you use when you export?
You may find Google sheets is better or xliff which is designed specifically for transferring localization data.

Thanks for reply.
Xliff seems more specific, so I tried Poedit, which is a good xliff editor. It can import, export well with Unity.
The problem of developing is, xliff export different locales to different files. And you can only edit one file at a time.
Lets say I only have 2 locales. I need to constantly sync Unity editor and two locales table xliff file. I’m not sure this is a expected way?
Karl, could u give me some guidance for this specific situation? I have so many dynamic generated text and tooltips( making a card game). I used to just put those text in class field like:
class CardA:Card{ public override string description=>"get two coin on use" public override string name=>"activate" }
Now since I’m looking for a way to localize, I replace those hard-coded string with the table entry key(also hard-coded string). And retrieve them when I use these field. So every card provides 3-4 entries in the table.
In the future, cards and items like these are the major extensive content that we will iterate on. I can see that we are gonna have a huge stringtable like this. And they are all binded together through that entry key string. I’m not sure this is a reliable thing.
Is there a sort of best practice for this?

This is by design. Translators will typically only work with 2 languages at a time, a source language and a target language.

Why do you need to keep syncing? Can you not use the Table Editor in Unity to edit the tables?
You would export to xliff once you are ready to start translating the game, send the xliff files to the translators and then import them back in at a later date.

Dont hard code the string keys.
Use a LocalizedString for each field. This can then be set in the inspector and will link to a table and entry using the Guid and Key Id. This is much safer than hard-coded strings.
This also means you can have multiple tables to better organize the data, so can avoid having to keep it all in a single table.

So for example:

[CreateAsset]
public class Card : ScriptableObject
{
    public LocalizedString cardDescription;
    public LocalizedString cardName;

    public string description=> cardDescription.GetLocalizedString();
    public string name=> cardName.GetLocalizedString();
}

Here we can now create multiple card assets in the project and set the description and name through the inspector.
If you need to add additional behavior to the card then you can inherit as before but you dont need to override the description or name, you set that through the inspector into the asset.
This is a common way to do this, you want to avoid hard-coding things and keep them flexible. This lets you create a template to represent a card which you then configure by turning it into an asset in the project.

There are also many other advantages to using a LocalizedString, such as being able to add variables into your localized string.

Wow, this immediately solves most of my confusion! I am just about to implement a custom Serializable Field that does this for me, right before I come back to this post!
8474618--1126364--upload_2022-9-29_12-33-22.png
I think I can work a way out now. Karl, great thank to you!

Edit:
One question though:
Localized string in the editor is referencing a 'None" string table by default. Is it possible to let it reference the only string table that exists in my project ( I need to choose it from the dropdown,not very convinient to create a new entry).
Also, in card games, there are a lot of entry values with RIchText like, CardADesc: BattleCry:do something. It would save some time if we can write some util helper for ourselves.(say markdown style parser?)
It would be good if I can write a custom editor for LocalizedString. Is there any pre-knowledge I need to check? Since it is not a MonoBehaviour.

PS:
To explain a little bit. This is a workflow that I imaged: The developer who created a prefab asset(most of time in my scenario, is a programmer), will create the entry + a language(his nature language). And this guy is willingly to work on assets inspector, which would reference a table entry( Localized String does exactly this). And after the projects are nealy done( entries are there, with just one language (developers)), xliff are exported to the file system and delivered to/from the translators, who would rather work on tables, maybe in another software. Then the project would work in another locale.
This is the expected use case, right?

You could add the default in your script:

public LocalizedString myLocalizedString = new LocalizedString("My Table", "My Entry");

https://docs.unity3d.com/Packages/com.unity.localization@1.3/api/UnityEngine.Localization.LocalizedString.html#constructors

What do you mean? You want to always add the rich text to wrap your text?
The template formatter could help with this. See the highlight examples.

What would this editor look like? We use a property drawer at the moment.

Yes thats expected. Export the xliffs and reimport them at a later date with the translations.

Your answer solve most of the problems I got. While I’m automating this procedure, I find a little problem that I failed to solve.
In the CustomEditor of a card, I use something like this :(pseudo code)

OnInspectorgui(){
target.description = new LocalizedString( tbName, entryName)
}

The value is assigned successfully, but the property drawer of that localizedString doesn’t change (which is still ‘None’). I need to make other changes in the Inspector for it to refresh.
I know there is something like
‘serializedObject.ApplyModifiedProperties();’,
but I didnt find a way to use something like
‘serializedObject.FindProperty(‘description’).objectReference=new Localized String’,
since LocalizedString is a custom Serializable field instead of a UnityEngine.Object.

Sorry to bother u again though.

Try setting the component dirty after you make the change Unity - Scripting API: EditorUtility.SetDirty