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 text files that translators can edit in any text editor. Build-time file generation, runtime loading, and language drop-in still work.
key=valueA 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:
- A project with Localization settings and a table collection. Refer to Get started with localization.
Create the reader
The reader script parses a file's stream into a snapshot, which consists of the collection header plus one per row. The reader implements :
ResourceTableDataEntryDataITableFileReaderpublic 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 and returns the reader. The base class handles file lookup, locale discovery, and rebuilding tables.
FileTableProvider[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 snapshot into file content. The writer script runs in the Editor, so put it in an folder.
ResourceTableDataEditorpublic 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 folder.
Editor// 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:
- Open Edit > > Localization and add Text Resource Provider to the content sources.Project Settings
?
- Select a table collection asset and assign it to the new source in the window.Inspector
?
- To test without building, set the source's Play Mode Source to Generated files and enter Play mode: values now come from generated files.
.txt - Build the Player and check the output: contains one
StreamingAssets/LocalizationTablesfile per locale..txt
Add a language by file
Because discovers locales from files, the format supports post-release languages without extra work. To add a new language, copy an existing locale's file, rename it to the new locale code, translate it, and relaunch. Refer to Add a language to a built Player.
FileTableProvider.txt