Localization Health Scanner¶
A scanner over the surfaces it can see — a clean report is not proof of complete coverage.
The Localization Health Scanner is an editor-only diagnostic tool that inspects localization tables (CSVs), configuration ScriptableObjects, and localized asset references for syntax errors, missing translations, placeholder mismatches, plural-category gaps, and cyclic fallbacks.
Features¶
- Structural table validation: Detects duplicate keys, malformed rows, empty tables, and missing translation cells (absorbing and expanding upon
LocValidator). - Placeholder mismatch analysis: Validates named tokens (
{score}) and indexed tokens ({0}/{1}) across all translation columns against reference templates to ensure runtime substitutions will not fail. - Plural category gap analysis: Probes
PluralRulesdirectly across integer counts to detect missing CLDR plural categories (e.g. Slavic[few]or[many]) and identifies unused plural definitions. - Configuration & fallback verification: Detects cyclic fallback chains (e.g.
French -> CanadianFrench -> French), unsupported fallback targets, and mismatches betweensupportedLanguagesand CSV table columns. - Deterministic reports: Generates byte-identical JSON and Markdown reports across runs for auditability and CI workflows.
- Interactive Editor Window: Grouped findings by table, severity filters, count summaries, and click-to-navigate asset pings.
Finding Codes¶
| ID | Severity | Category | Description & Runtime Consequence |
|---|---|---|---|
LOC001_DUPLICATE_KEY |
Error | Table | Duplicate key in table. At runtime the table is loaded into a dictionary, so the last occurrence silently overwrites the earlier ones. |
LOC002_MALFORMED_ROW |
Error | Table | Row has mismatched column count or an empty key with translation values. |
LOC003_MISSING_CELL |
Warning | Table | A language column is empty or whitespace-only for a given key. |
LOC004_NO_LANGUAGE_COLUMNS |
Error | Table | CSV header contains no language columns. |
LOC005_EMPTY_TABLE |
Warning | Table | Table contains no data rows. |
LOC010_PLACEHOLDER_MISSING |
Warning | Placeholder | Named placeholder in reference template is missing in translation (token will never be substituted). |
LOC011_PLACEHOLDER_EXTRA |
Warning | Placeholder | Extra named placeholder in translation that is not present in reference template (renders as literal text). |
LOC012_PLACEHOLDER_ARITY |
Warning | Placeholder | Indexed placeholder set differs from reference language (e.g. {0}/{1} vs {0}). |
LOC020_PLURAL_CATEGORY_MISSING |
Warning | Plural | Language requires a CLDR category variant (KEY[few], KEY[many], etc.) that is missing in the table. |
LOC021_PLURAL_CATEGORY_UNUSED |
Info | Plural | Table defines a plural variant whose category that language never produces at runtime. |
LOC030_FALLBACK_CYCLE |
Error | Config | Cyclic fallback loop detected in LocalizationConfig.languageFallbacks. |
LOC031_FALLBACK_TO_UNSUPPORTED |
Warning | Config | Fallback target language is not listed in supportedLanguages. |
LOC032_SUPPORTED_LANGUAGE_NO_COLUMN |
Warning | Config | Supported language has no column in a scanned table. |
LOC033_TABLE_COLUMN_UNSUPPORTED |
Info | Config | Table column language is not declared in supportedLanguages. |
LOC034_INVALID_LANGUAGE_HEADER |
Warning | Config | A table column header is not a recognized SystemLanguage name (usually a typo). Plural analysis skips that column entirely. |
LOC035_DEFAULT_LANGUAGE_NOT_SUPPORTED |
Warning | Config | defaultLanguage is not listed in supportedLanguages, so the configured fallback language is one the service is not set up to load. |
LOC036_EMPTY_SUPPORTED_LANGUAGES |
Warning | Config | supportedLanguages is empty — the scanner cannot cross-check table columns against the config, so LOC032/LOC033 are suppressed for it. |
LOC040_LOCALIZED_ASSET_MISSING |
Warning | Asset Ref | Localized asset not found at Resources path convention (note: edit-time probing is not authoritative for Addressables). |
Scope and Limitations¶
The banner at the top of this page is not boilerplate — the scanner reports on the surfaces it can reach, and a clean report is not proof that your localization is complete. Specifically:
- Table discovery is by convention. A project scan collects
.csvassets underAssets/whose path contains/Localization/or whose filename containsStringTable. A table stored outside that convention is never scanned; an unrelated CSV that happens to match it is scanned, and will produce structural findings. - Config cross-checks are scoped by ownership. A table belongs to a
LocalizationConfigwhen it sits under.../Resources/<localizationTablesPath>/. Tables outside every config's path still get structural, placeholder, and plural analysis, but no config cross-checks (LOC030-LOC036), and their placeholder reference column is the table's first language column rather than a configureddefaultLanguage. LOC040is evaluated against every config. Localized asset references found onLocalizedImage/LocalizedAudiocomponents are checked against eachLocalizationConfig's language set andlocalizationTablesPath. Findings are deduplicated across configs by expected path, key, and type: two configs sharing a tables path report a missing asset once, while two configs with different tables paths each report it - those are genuinely different expected locations. With noLocalizationConfigasset in the project,LOC040is not evaluated at all.- Resources probing is edit-time only. An asset served through Addressables at runtime will look
missing here. Treat
LOC040as a prompt to check, not a verdict. - Runtime-composed keys are invisible. Keys built at runtime (
"item_" + id) cannot be seen by a static scan; only literal keys on components and rows in tables are covered.
Using the Scanner Window¶
Open Tools ▸ OxHeart ▸ Localization ▸ Localization Health Scanner:
- Click Run Health Scan to scan all CSV tables and
LocalizationConfigassets in the project. - Filter findings using the search bar or severity dropdown (All, Errors & Warnings, Errors Only, Warnings Only, Info Only).
- Expand table sections to inspect findings with line numbers and column locations.
- Click Ping to highlight the source CSV or Config asset in the Project window.
- Export reports via Export JSON... or Export Markdown....
CI & Build Script Integration¶
LocValidator delegates directly to the health scanner core:
- Run via menu: Tools ▸ OxHeart ▸ Localization ▸ Validate Localization Tables
- Call via code:
LocValidator.ValidateAll()orLocValidator.ValidateFile(assetPath) - Programmatic headless scan:
var report = OxHeart.Editor.Localization.Health.LocHealthScanner.ScanProject(); if (!report.IsClean) { string json = OxHeart.Editor.Localization.Health.LocHealthReportWriter.ToJson(report); // Process or write report }