Skip to content

Translating Localizer

Taiizor edited this page Jun 5, 2026 · 2 revisions

Translating with the Localizer

Audience: translators / contributors. Sucrose.Localizer is a standalone console application that contributors use to translate the Sucrose app. It is not part of the main application — it is a tooling project that converts translation data between three formats: the runtime XAML resources, an intermediate CSV working format, and POEditor CSV (for the POEditor translation service). This page documents the tool's nine-option menu, the conversion pipeline, the file formats and their separator characters, and how to add a new language. For the current set of translated languages, see Localization Coverage.

Contents


What the Localizer is

Sucrose.Localizer lives in src/Localizer/Sucrose.Localizer/ and has its own solution, src/Sucrose.Localizer.slnx (one of the project's three solutions — see Repository Layout). Project facts:

  • Targets net10.0-windows but with UseWPF=false; OutputType=Exe; defines the LOCALIZER preprocessor symbol.
  • Package references: Skylark, Skylark.Standard, CsvHelper.
  • Output goes to …/Localizer/{x86|x64|ARM64}.

It is purely a contributor tool: it reads and writes files on disk and is driven by an interactive numbered menu.


The three formats

Format Where it lives Shape
XAML (runtime) src/Library/Sucrose.Resources/Locales/Locale.<CODE>.xaml and grouped files like Portal.<area>.<CODE>.xaml ResourceDictionary of <system:String x:Key="…">value</system:String> entries; the app loads one per language at runtime.
CSV (working) .localize/ (repo root) Columns Hash,File,Key,Value — used for diffing/checking and as the working format.
POEditor CSV .localize/POEditor/ Columns Term,Translate,Context,Reference,Comment — the import/export format for the POEditor service.

Do not conflate the two CSV schemas: the working CSV is Hash,File,Key,Value; the POEditor CSV is Term,Translate,Context,Reference,Comment.


Running the tool

The entry point is App.csMain, which sets the console encoding to UTF-8, sets the culture, and calls Helper.Program.Start(). Helper/Program.cs presents the interactive menu. Each menu option prompts for directory paths on stdin via Console.ReadLine.

Because the tool is interactive-only (there are no command-line flags), running it means launching the built Sucrose.Localizer.exe and answering its prompts. Build it from its own solution:

dotnet build src/Sucrose.Localizer.slnx -c Release -p:PlatformTarget=x64

The nine-option menu

Helper/Program.cs exposes nine operations:

# Operation Helper call Notes
1 Convert XAML → CSV XamlToCsv.Convert(xamlDir, csvDir) Extracts system:String entries plus merged-dictionary blocks.
2 Convert CSV → XAML CsvToXaml.Convert(csvDir, xamlDir) Rebuilds the ResourceDictionary XAML files.
3 Convert CSV → POEditor CsvToPoe.Convert(csvDir, poeDir)
4 Convert POEditor → CSV PoeToCsv.Convert(poeDir, csvDir)
5 Check CSV files against each other Against.CheckCsv(csvDir) Row-by-row consistency across all two-letter CSVs.
6 Check POEditor files against each other Against.CheckPoe(poeDir)
7 CSV reindexer from a reference CSV Against.ReindexCsv(csvDir, lang) Re-orders all CSVs to match a reference language's line order by Hash.
8 Alphabetic key indexer for a CSV Against.AlphabeticIndexer(csvDir, lang) Sorts keys (shortest-first, then natural alpha-numeric) per file group; excludes Base64/empty keys.
9 Create a new language file from CSVs Creating.Create(csvDir, srcLang, outLang, outLangName) Clones a source CSV to a new language and registers it in Locale.csv.

The typical contributor loop is: export the current strings to POEditor (1 → 3), translate in POEditor, import back (4 → 2), then check consistency (5, 6) and optionally reindex (7) before committing.


Conversion mechanics

XAML → CSV (XamlToCsv.cs)

  • Recurses *.xaml; the language code is the last dot-segment of the filename (e.g. Portal.Enum.TR.xamlTR).
  • Merged-dictionary blocks (<ResourceDictionary.MergedDictionaries>…</…>) are captured whole, Base64-encoded (CryptologyExtension.TextToBase), and stored under the key Base64.
  • Each system:String becomes a CSV row Hash,File,Key,Value; & is escaped to &amp;; blank separator rows preserve the XAML spacing.

CSV → XAML (CsvToXaml.cs)

  • Re-emits a ResourceDictionary with xmlns:system="clr-namespace:System;assembly=mscorlib".
  • Base64 keys are decoded back to raw XAML (BaseToText); empty key+value rows become blank lines.
  • If a file has no system:String entries, the header is simplified (drops the system namespace, prepends a BOM).

CSV → POEditor (CsvToPoe.cs)

  • Emits Term,Translate,Context,Reference,Comment.
  • Empty keys become PASS⦙n; Base64 keys become Base64⦙n.
  • Such rows get Context Don't Touch and the comment "Please don't touch the translation in this line." The Reference column carries the File.

POEditor → CSV (PoeToCsv.cs)

  • Reverses the above: PASS⦙ terms become empty, -marked translations are cleared, and the Hash is recomputed.

Internal separator characters used by the helpers: (entry split) and (special-marker split). These are literal Unicode characters baked into the file format — preserve them exactly.


The Hash key

The Hash column is the cross-language join key that ties the same string together across all languages. It is computed as the MD5 (TextToMD5) of "{file-without-langcode}.{key-or-counter}". Because reindexing and checking (menu options 5–7) depend on the Hash being stable, do not hand-edit it — let the tool compute it.


Working copies & file locations

Path Contents
src/Library/Sucrose.Resources/Locales/Locale.<CODE>.xaml Runtime XAML resource per language.
src/Library/Sucrose.Resources/Locales/Locale.xaml The runtime locale registry (code → display name + version), e.g. Locale.EN = "English (v1.4)".
.localize/ (repo root) Working/translation CSVs (Portal.csv, Launcher.csv, Discord.csv, and per-language <CODE>.csv).
.localize/POEditor/ POEditor-format CSVs.

Localized XAML resources under Sucrose.Resources/Locales/ are grouped by app area — e.g. Locales/Portal/… (Portal.<area>.<CODE>.xaml, including Portal.Enum.<CODE>.xaml, which localizes enum display names such as StoreServerType and ReportThemeType (the Category names live in a separate Portal.Category.<CODE>.xaml group)), plus Launcher and Discord groups. The runtime looks strings up with segment keys via Sucrose.Resources.Extension.Resources.GetValue("Portal","StoreCard","MenuInstall")-style calls.


Adding a new language

Use menu option 9 (Creating.Create(csvDir, srcLang, outLang, outLangName)):

  1. It copies <src>.csv<out>.csv.

  2. It rewrites the File column's language tokens (.SRC..OUT.) — including inside the Base64-encoded merged-dictionary blocks.

  3. It appends a row to Locale.csv of the form:

    Locale.xaml, Locale.<OUT>, <Language Name> (v0.1)
    

    and re-sorts the registry by Key.

After creating the language CSV, translate it (export to POEditor with option 3, translate, import back with option 4), then convert CSV → XAML (option 2) to produce the runtime Locale.<CODE>.xaml that the app loads.

The locale registry maps each code to a human display name and version, e.g. Locale.EN = "English (v1.4)". The version is a maturity indicator for the translation.


Gotchas

  • The Localizer is interactive-only (stdin prompts, no CLI flags). Automating it would require piping the answers.
  • The Hash column must stay stable — it is the cross-language join key; reindexing/checking depends on it.
  • There are two distinct CSV schemas (working vs POEditor); do not mix them.
  • The website's translations are a separate surface: the .pages/ site uses its own i18n JSON files and is not produced by the Localizer. See Localization Coverage.

See also

Home

Getting Started

Wallpaper Types

Using Sucrose

Settings Reference

Creating Wallpapers

Engine Reference

Automation & Command Line

Architecture & Internals

Data, Files & Diagnostics

Building & Contributing

Help & Support

Clone this wiki locally