Documentation

Unity Engine


User Manual

Script Reference

Unity Engine


Create a file table provider for text files

Ship localization tables as plain text files with your own format.
Read time 3 minutesLast updated 6 days ago

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 linegreeting=Bonjourquit=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