ZOA Localization Authoring
The optional Workbench surfaces for localization production: table wizard, key browser, coverage report, and source extraction review.
This package exists to be left out. A game that needs localized text installs com.zoa.localization and nothing else: runtime service, table assets, UI Toolkit bindings, exchange codecs, validation, and the extraction and inventory APIs all live there. This package adds only the producer-facing Workbench module on top, and it is the only half that depends on Workbench.
Tests enforce the split. The base package's asmdef and manifest are asserted to contain no reference to Workbench, and the base package is asserted to have no Editor/Workbench directory at all. A matching test on this side asserts that both authoring surfaces sit in the ZOA.Localization.Authoring.Editor.Workbench namespace.
Installing it adds no capability. It adds a place to do the work: the module wraps APIs that already exist in the base package, giving them a browsable, searchable home inside the Workbench alongside every other Foundry authoring surface.
Depends on (2)
Depended on by (0)
How it works
Concepts
An optional layer over an owned API
Everything this package renders is computed by the base package. The string browser and coverage report are views over LocalizationKeyInventoryBuilder.BuildFromProject. The source extraction panel is a button over LocalizationHardcodedStringExtractor. The table wizard emits through LocalizationTableAssetEmitter.
The reusable logic stays where a game can call it from a build step or a CI job, and only the presentation lives here. Uninstalling this package removes UI, never functionality.
The inventory is the shared data source
LocalizationKeyInventoryBuilder walks every LocalizationTableAsset in the project and produces one row per key and locale, carrying the value, the table name, the asset path, the translator context and notes, the screenshot path, the character limit, and the review status, plus placeholder and duplicate flags.
Both the string browser and the coverage report read that same inventory. The browser lists the rows with duplicates and placeholders flagged inline and filters them by key, locale, value, or table name. The report asks the inventory for Coverage and MissingRows against English as the reference locale and prints a line per locale with coverage, missing count, placeholder count, and duplicate count.
Because the source is the asset files rather than a running service, both work in the editor without entering play mode and without any scene being installed.
The table wizard is a guided draft
LocalizationTableWizard runs on FoundryWizardShell with a step rail and an issue tray, in three steps: Locale Selection, Import Keys, and Review. Locale Selection takes a BCP 47 code with buttons for five common locales, and rejects an unparseable code by reverting the field rather than throwing.
Import Keys either loads a named template or accepts keys typed one at a time into a growing list. Review shows the locale, the key count, the template in use, and the key list before anything is written.
Completing the wizard captures a LocalizationTableDraft and hands it to LocalizationTableAssetEmitter, which writes a LocalizationTableAsset under Assets/ZOA/Generated/Localization. Each step has a validator, so a missing locale or an empty key set surfaces in the issue tray rather than producing a broken asset.
Extraction review is a scan you can act on
The source extraction capability walks every C# and UXML file under Assets and runs LocalizationHardcodedStringExtractor over them, reporting the candidate count and listing the first twenty with their proposed key, the literal text, and the file and line where it was found.
Suppression is a comment token. Writing ZOA_LOCALIZATION_IGNORE next to an intentional literal takes it out of the scan, which keeps the report on strings that need a key instead of on debug logs and asset paths.
In the editor
Screens
Screenshot pending
/screenshots/localization-authoring-coverage-report.png
The Workbench with the Localization module selected and Coverage Report active, showing the reference locale and key and locale totals at the top, then several per-locale lines with coverage percentages, missing counts, placeholder counts, and duplicate counts.
Screenshot pending
/screenshots/localization-authoring-table-wizard.png
The wizard embedded in the Workbench with the three-step rail visible, the Use Template toggle on with a template name field, the growing key list below it, and the New Key field with its Add button at the bottom.
Screenshot pending
/screenshots/localization-authoring-string-browser.png
The String Browser capability with a search term entered, the filtered list showing rows in the locale, key, table, value format, and at least one row carrying a DUPLICATE flag and one carrying PLACEHOLDER.
Setup
Workflow
- 01
Decide whether you need it at all
A game that only ships localized text does not. Install com.zoa.localization alone and drive extraction, inventory, validation, and emission from your own build steps. Install this package when people are doing localization production inside the editor and want a home for it.
- 02
Open the module
The Localization module appears in the Workbench under the workflow.environment lane at order 51, with five capabilities across the strip: Localization Tables, String Browser, Coverage Report, Source Extraction, and Overview.
- 03
Create a table with the wizard
Pick or type a BCP 47 locale code, choose a template or add keys one by one, review the summary, and finish. The asset lands under Assets/ZOA/Generated/Localization; from there it is an ordinary LocalizationTableAsset that the base package's scene installer can register.
- 04
Read coverage before committing to a locale
The Coverage Report scans every table asset in the project and prints coverage, missing count, placeholder count, and duplicate count per locale against English. Duplicates are as important as gaps: a duplicated key is a composition conflict waiting to happen at runtime.
- 05
Sweep for hardcoded strings
Run Source Extraction over Assets and work down the candidate list. Give a real string a key and a table row; mark an intentional literal with ZOA_LOCALIZATION_IGNORE so it stops appearing.
- 06
Gate the build from the base package
Build gating stays in com.zoa.localization. Call LocalizationValidator.ValidateBuildReadiness from a build step so the gate runs on a machine that never installed the authoring package.
Surface
Key types
LocalizationWorkbenchModule
class
The Workbench module itself, registered as "localization" in the workflow.environment lane. Builds a capability strip over table definitions, the string browser, the coverage report, source extraction, and an architecture overview.
- string Id => "localization"
- string Title => "Localization"
- string Subtitle => "Manage translations, preview localized content, and validate coverage"
- int Order => 51
- VisualElement Build(ZOAWorkbenchContext context)
LocalizationTableWizard
component
The three-step guided table creator. Each step carries a validator feeding the issue tray, and completion emits a LocalizationTableAsset from the captured draft.
- static void ShowWindow()
- LocalizationTableDraft CaptureDraft()
LocalizationKeyInventoryBuilder
class
Owned by the base package and consumed here. Builds the project-wide inventory that both the string browser and the coverage report render.
- static LocalizationKeyInventory BuildFromProject()
- static LocalizationKeyInventory BuildFromAssets(IEnumerable<LocalizationTableAsset> assets)
LocalizationKeyInventory
class
The inventory the Workbench views read: every row, the distinct locales and keys, and the two questions the coverage report asks per locale.
- IReadOnlyList<LocalizationKeyInventoryRow> Rows { get; }
- IReadOnlyList<LocaleId> Locales { get; } IReadOnlyList<LocalizationKey> Keys { get; }
- IReadOnlyList<LocalizationKeyInventoryRow> MissingRows(LocaleId referenceLocale, LocaleId targetLocale)
- float Coverage(LocaleId referenceLocale, LocaleId targetLocale)
LocalizationKeyInventoryRow
class
One key in one locale, with everything the browser and report need: the value, its table and asset path, the translator metadata, the review status, and the placeholder and duplicate flags.
- LocalizationKey Key { get; } LocaleId Locale { get; } string Value { get; }
- string TableName { get; } string AssetPath { get; }
- LocalizationReviewStatus ReviewStatus { get; }
- bool IsPlaceholder { get; } bool IsDuplicate { get; }
LocalizationHardcodedStringExtractor
class
The scanner behind the source extraction capability, owned by the base package. Returns candidates with a proposed key, the literal, and the file and line; a ZOA_LOCALIZATION_IGNORE token suppresses a literal.
- LocalizationExtractionReport ExtractFromFiles(IEnumerable<string> paths)
LocalizationExtractionCandidate
class
One extraction finding: the proposed key, the source text, the file path and line, a context string, and the name of the detector that matched.
- LocalizationKey Key { get; } string SourceText { get; }
- string SourcePath { get; } int Line { get; }
- string Context { get; } string Detector { get; }
LocalizationTableAssetEmitter
class
Writes the wizard's draft to disk, defaulting to Assets/ZOA/Generated/Localization. Also the path an imported translator document takes back into the project.
- const string DefaultSaveFolder = "Assets/ZOA/Generated/Localization"
- static LocalizationTableAsset EmitFromDraft(LocalizationTableDraft draft, string savePath)
- static LocalizationTableAsset EmitFromDocument(...)
LocalizationTableDraft
class
The in-flight shape the wizard captures before emission: a locale code, a table name, and the draft entries with their values and metadata.
- string LocaleCode; string TableName;
- List<LocalizationTableDraftEntry> Entries;
- static LocalizationTableDraft FromPairs(...)
Usage
Examples
using ZOA.Localization.Core;
using ZOA.Localization.Unity.Editor.Inventory;
// This is exactly what the Coverage Report capability renders.
var inventory = LocalizationKeyInventoryBuilder.BuildFromProject();
var reference = DefaultLocales.English;
foreach (var locale in inventory.Locales)
{
float coverage = inventory.Coverage(reference, locale);
int missing = inventory.MissingRows(reference, locale).Count;
UnityEngine.Debug.Log(locale.Value + ": " + coverage + "% coverage, " + missing + " missing");
}using System.IO;
using System.Linq;
using ZOA.Localization.Unity.Editor.Extraction;
var paths = Directory.GetFiles("Assets", "*.cs", SearchOption.AllDirectories)
.Concat(Directory.GetFiles("Assets", "*.uxml", SearchOption.AllDirectories));
var report = new LocalizationHardcodedStringExtractor().ExtractFromFiles(paths);
foreach (var candidate in report.Candidates)
{
UnityEngine.Debug.Log(
candidate.Key.Value + " -> " + candidate.SourceText +
" (" + candidate.SourcePath + ":" + candidate.Line + ")");
}
// A row that should stay hardcoded gets a ZOA_LOCALIZATION_IGNORE
// comment token next to it and drops out of the next scan.using ZOA.Localization.Unity.Editor.Assets;
// The wizard's completion step does exactly this.
var draft = new LocalizationTableDraft
{
LocaleCode = "fr-FR",
TableName = "UI Strings",
};
draft.Entries.Add(new LocalizationTableDraftEntry
{
Key = "ui.menu.start",
Value = "Commencer",
Context = "Main menu, primary button",
CharacterLimit = 16,
ReviewStatus = ZOA.Localization.Core.LocalizationReviewStatus.InReview,
});
// savePath null falls back to Assets/ZOA/Generated/Localization.
var asset = LocalizationTableAssetEmitter.EmitFromDraft(draft, savePath: null);Tooling
Editor tools
Localization Table Wizard
Tools > ZOA > Advanced > Define > Localization > Table Wizard
Three steps on FoundryWizardShell: Locale Selection with a BCP 47 field and preset buttons for English, French, German, Spanish, and Japanese; Import Keys with an optional template and an add-key field; and Review showing the locale, key count, template, and key list. Each step has a validator feeding the issue tray. The menu item routes into the Workbench rather than opening a floating window.
Localization Workbench module
ZOA Workbench > Localization
Registered as "localization" in the workflow.environment lane at order 51. Five capabilities: Localization Tables (a definition editor over LocalizationTableAsset that browses, edits, validates, duplicates, and deletes, grouped by locale code), String Browser, Coverage Report, Source Extraction, and an Overview of the fallback chain and core types.
String Browser
ZOA Workbench > Localization > String Browser
A searchable list over the project inventory, one row per key and locale showing the locale, key, table name, value, and a DUPLICATE or PLACEHOLDER flag. The search filters on key, locale, value, and table name at once.
Coverage Report
ZOA Workbench > Localization > Coverage Report
Reports the reference locale, total key and locale counts, then one line per locale with coverage percentage, missing count, placeholder count, and duplicate-row count, computed from the table assets rather than from a running service.
Source Extraction
ZOA Workbench > Localization > Source Extraction
Scans every C# and UXML file under Assets on demand and reports the candidate count with the first twenty listed as proposed key, literal text, and file and line. Literals marked with ZOA_LOCALIZATION_IGNORE are suppressed.
Read this
Notes and caveats
See also