Version: Unity 6.7 Beta (6000.7)
Language : English
Create a custom startup locale selector
Create a custom asset provider

Create a file table provider for text files

Ship localization tables as plain text files with your own format.

The built-in data-file source ships tables as JSON. This example replaces the format with plain key=value text files that translators can edit in any text editor. Build-time file generation, runtime loading, and language drop-in still work.

A file format has two halves: a runtime reader that turns a file into table data, and a writer in the Unity Editor that generates the files. For how the generated files ship and load, refer to Data file tables. The example’s file looks like this:

@collection=Menus
@guid=1f4a09e21b6c44dd8f2eab27d3a54c10
@locale=fr
# key=value, one entry per line
greeting=Bonjour
quit=Quitter

Prerequisites

Before you start, make sure you have the following:

Create the reader

The reader script parses a file’s stream into a ResourceTableData snapshot, which consists of the collection header plus one EntryData per row. The reader implements ITableFileReader:

public class TextTableReader : ITableFileReader
{
    public const string Extension = "txt";
    public static readonly TextTableReader Instance = new();

    public string FileExtension => Extension;

    public ResourceTableData Read(Stream stream)
    {
        var data = new ResourceTableData();
        using var reader = new StreamReader(stream, Encoding.UTF8, true, 1024, leaveOpen: true);
        while (reader.ReadLine() is { } line)
        {
            // Lines starting with '#' are comments; every other line is "key=value".
            if (line.StartsWith("#", StringComparison.Ordinal))
                continue;
            var split = line.IndexOf('=');
            if (split <= 0)
                continue;
            var key = line[..split].Trim();
            var value = line[(split + 1)..].Trim();
            switch (key)
            {
                case "@collection":
                    data.CollectionName = value;
                    break;
                case "@guid":
                    data.CollectionGuid = value;
                    break;
                case "@locale":
                    data.LocaleCode = value;
                    break;
                default:
                    // The file carries no entry ids, so the shared table data matches rows
                    // by key and assigns an id to any key it has not seen before.
                    data.Entries.Add(new EntryData { Key = key, Value = value });
                    break;
            }
        }
        return data;
    }
}

Create the provider

The provider script is the content source itself. The class derives from FileTableProvider and returns the reader. The base class handles file lookup, locale discovery, and rebuilding tables.

[Serializable]
public class TextResourceProvider : FileTableProvider
{
    protected override ITableFileReader Reader => TextTableReader.Instance;
}

Create the writer

The writer script performs the opposite operation to the reader script. It turns a ResourceTableData snapshot into file content. The writer script runs in the Editor, so put it in an Editor folder.

public class TextTableWriter : ITableFileWriter
{
    public static readonly TextTableWriter Instance = new();

    public void Write(ResourceTableData data, Stream stream)
    {
        using var writer = new StreamWriter(stream, new UTF8Encoding(false), 1024, leaveOpen: true);
        writer.WriteLine($"@collection={data.CollectionName}");
        writer.WriteLine($"@guid={data.CollectionGuid}");
        writer.WriteLine($"@locale={data.LocaleCode}");
        foreach (var entry in data.Entries)
        {
            if (entry != null && !string.IsNullOrEmpty(entry.Key))
                writer.WriteLine($"{entry.Key}={entry.Value}");
        }
    }
}

Register the Editor half

Connect the writer to the provider type with an Editor class. Without a registered Editor, Unity never generates the provider’s files. This script runs in the Editor, so put it in an Editor folder.

// Registering an editor for the provider type is what makes Unity generate its files at build time.
[AssetProviderEditor(typeof(TextResourceProvider))]
public class TextResourceProviderEditor : FileTableProviderEditor
{
    public override ITableFileWriter Writer => TextTableWriter.Instance;
}

Use the new source

To use your provider in a project:

  1. Open Edit > Project Settings > Localization and add Text Resource Provider to the content sources.
  2. Select a table collection asset and assign it to the new source in the Inspector window.
  3. To test without building, set the source’s Play Mode Source to Generated files and enter Play mode: values now come from generated .txt files.
  4. Build the Player and check the output: StreamingAssets/LocalizationTables contains one .txt file per locale.

Add a language by file

Because FileTableProvider discovers locales from files, the format supports post-release languages without extra work. To add a new language, copy an existing locale’s .txt file, rename it to the new locale code, translate it, and relaunch. Refer to Add a language to a built Player.

Additional resources

Create a custom startup locale selector
Create a custom asset provider