-
-
Notifications
You must be signed in to change notification settings - Fork 60
Translating 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.
- What the Localizer is
- The three formats
- Running the tool
- The nine-option menu
- Conversion mechanics
- The Hash key
- Working copies & file locations
- Adding a new language
- Gotchas
- See also
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-windowsbut withUseWPF=false;OutputType=Exe; defines theLOCALIZERpreprocessor 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.
| 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 isTerm,Translate,Context,Reference,Comment.
The entry point is App.cs → Main, 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=x64Helper/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.
- Recurses
*.xaml; the language code is the last dot-segment of the filename (e.g.Portal.Enum.TR.xaml→TR). - Merged-dictionary blocks (
<ResourceDictionary.MergedDictionaries>…</…>) are captured whole, Base64-encoded (CryptologyExtension.TextToBase), and stored under the keyBase64. - Each
system:Stringbecomes a CSV rowHash,File,Key,Value;&is escaped to&; blank separator rows preserve the XAML spacing.
- Re-emits a
ResourceDictionarywithxmlns:system="clr-namespace:System;assembly=mscorlib". -
Base64keys are decoded back to raw XAML (BaseToText); empty key+value rows become blank lines. - If a file has no
system:Stringentries, the header is simplified (drops thesystemnamespace, prepends a BOM).
- Emits
Term,Translate,Context,Reference,Comment. - Empty keys become
PASS⦙n;Base64keys becomeBase64⦙n. - Such rows get Context
Don't Touchand the comment "Please don't touch the translation in this line." TheReferencecolumn carries theFile.
- Reverses the above:
PASS⦙terms become empty,⦙-marked translations are cleared, and theHashis 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 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.
| 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.
Use menu option 9 (Creating.Create(csvDir, srcLang, outLang, outLangName)):
-
It copies
<src>.csv→<out>.csv. -
It rewrites the
Filecolumn's language tokens (.SRC.→.OUT.) — including inside the Base64-encoded merged-dictionary blocks. -
It appends a row to
Locale.csvof 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.
- The Localizer is interactive-only (stdin prompts, no CLI flags). Automating it would require piping the answers.
- The
Hashcolumn 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.
Getting Started
- Installation
- System Requirements
- Quick Start
- Portal Interface Tour
- Updating Sucrose
- Uninstalling Sucrose
Wallpaper Types
Using Sucrose
- Managing Library
- Using Store
- Customizing Wallpaper
- Multi-Monitor
- Wallpaper Cycling
- Choosing Engines
- Performance Rules
- Theme, Tray & Startup
- Discord Rich Presence
Settings Reference
- Settings Overview
- Settings: General
- Settings: Personal
- Settings: Performance
- Settings: Wallpaper
- Settings: System
- Settings: Other
- Settings: All Keys
Creating Wallpapers
- Create Overview
- Create: Step By Step
- Create: Package Format
- Create: Customization Controls
- Create: JS Bridge
- Create: Audio API
- Create: System API
- Create: Property Listener & Filters
- Create: Web Architecture
- Create: Compatibility
- Create: Example Wallpapers
- Create: Sharing & Publishing
Engine Reference
- Engines Overview
- Engine: MpvPlayer
- Engine: VlcPlayer
- Engine: WebView
- Engine: CefSharp
- Engine: Nebula
- Engine: Vexana
- Engine: Xavier
- Engine: Aurora
- Engine Comparison
Automation & Command Line
Architecture & Internals
- Architecture Overview
- Lifecycle
- Commandog Dispatcher
- Single-Instance Mutexes
- IPC
- Backgroundog Service
- Crash Reporting
- Update Internals
- Property Service
- Undo Internals
Data, Files & Diagnostics
Building & Contributing
- Building From Source
- Repository Layout
- Shared Item Projects
- Code Conventions
- Preprocessor Symbols
- Publish Pipeline
- Bundle Installer Internals
- Extending Sucrose
- Contributing
- Translating with Localizer
- Localization Coverage
- Security Policy
- Privacy & Telemetry
Help & Support