Skip to content

Translating Localizer

Taiizor edited this page Jul 3, 2026 · 4 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 SHA256 (TextToSHA256) 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.

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