Translation and Localization
Grex uses Windows .resw resource catalogs under Strings/<culture>/Resources.resw.
The repository currently contains:
- one source catalog:
Strings/en-US/Resources.resw; - 107 non-English catalogs;
- 476 English resource entries at the time of this documentation audit.
Non-English catalogs are not complete. Do not describe every listed locale as fully translated. Run the status script for current counts.
Runtime behavior
LocalizationService:
- loads strings for the selected culture;
- falls back to
en-USwhen possible; - returns the resource key when lookup fails;
- formats parameterized strings;
- updates thread culture;
- raises
PropertyChangedwhen culture changes; - caps cached resource contexts at 20.
The Settings language list is built from culture directories that contain Resources.resw. If packaged resource files cannot be enumerated, the UI uses a fallback culture list.
Views refresh dynamic text after a culture change. LocalizedToolTipRegistry refreshes registered tooltips. Some WinUI resource behavior may still require an application restart.
Resource lookup patterns
XAML resources
Use x:Uid when WinUI can populate a static property:
<Button x:Name="SearchButton"
x:Uid="SearchButton" />
The resource key includes the property:
<data name="SearchButton.Content" xml:space="preserve">
<value>Search</value>
<comment>status:complete</comment>
</data>
C# resources
Use the localization service for runtime strings:
var text = LocalizationService.Instance.GetLocalizedString("SearchErrorTitle");
var formatted = LocalizationService.Instance.GetLocalizedString(
"FoundMatchesStatus",
matchCount,
fileCount,
elapsed);
Do not hard-code user-facing English in C# or XAML fallback paths unless an existing compatibility pattern requires it.
Regex Builder strings follow this same system. See Regex Builder Localization for its resource families and focused validation steps.
Status comments
Every <data> entry should have one status comment.
| Comment | Meaning |
|---|---|
status:complete |
Reviewed translation |
status:incomplete |
English placeholder or unreviewed translation |
status:error:<details> |
Automatic translation failed |
status:error:permanent |
Do not retry automatically |
English entries use status:complete.
Example:
<data name="NewFeatureButton.Content" xml:space="preserve">
<value>New feature</value>
<comment>status:incomplete</comment>
</data>
Add a resource key
English is the source of truth.
- Add the key and English text to
Strings/en-US/Resources.reswwithstatus:complete. -
From the repository root, propagate the same key:
python Scripts/add_localization_entry.py "NewFeatureButton.Content" "New feature" - The script skips the existing English key and adds an English placeholder with
status:incompleteto other catalogs. - Translate and review the affected non-English entries.
- Run script tests and the Windows .NET tests.
The add script:
- discovers
Strings/*/Resources.resw; - does not overwrite an existing key;
- escapes XML through
xml.etree.ElementTree; - reports added and skipped catalogs.
Remove a resource key
Use the removal script rather than hand-editing 108 files:
python Scripts/remove_localization_entry.py "OldFeatureButton.Content"
Then search for remaining code/XAML references:
rg -n "OldFeatureButton" Controls ViewModels Services MainWindow.xaml MainWindow.xaml.cs
Review the diff before committing.
Update existing text
When changing an English message:
- update
en-US; - identify every catalog containing the key;
- preserve placeholders and XML whitespace;
- mark stale non-English values
status:incomplete; - translate and review;
- run the status report.
Do not silently leave a previously translated value attached to changed English semantics.
Formatting rules for translators
Preserve:
- format placeholders such as
{0},{1}, and{2}; - literal command names, flags, paths, and file extensions;
- keyboard names;
- XML escaping;
- leading/trailing whitespace when meaningful;
- punctuation required by the UI;
- accelerator or glyph text used by controls.
Do not translate:
Grex;- API model ids;
- command names such as
grep,docker,wsl, andgrex-cli; - CLI options such as
--gitignore; - technical units such as KB, MB, and GB;
- file or resource keys.
Keep translations concise enough for the existing control. Test long strings and right-to-left scripts in the running app.
Automated translation
Scripts/translate_remaining_entries.py can process incomplete entries with the optional googletrans package:
python -m pip install googletrans==4.0.0rc1
python Scripts/translate_remaining_entries.py
Without googletrans, the script identifies work but cannot translate it.
The script:
- compares non-English values with English;
- skips complete and permanent-error entries;
- maps Windows culture codes to provider language codes;
- marks successful values complete;
- records transient or permanent errors;
- avoids overwriting values already different from English.
Machine translation is a draft, not approval. Review terminology, placeholders, directionality, truncation, and feature context before changing a status to complete.
The translation provider is external. Do not place secrets or private product text in resource values submitted to it.
Report translation status
Run:
python Scripts/generate_translation_status.py
The report:
- parses the English key set;
- counts complete, incomplete, and error entries per locale;
- summarizes locale status;
- lists locales nearest completion;
- lists translation errors.
This output is authoritative. Counts in prose can become stale.
Test localization scripts
Standard library:
python Scripts/test_add_localization_entry.py
python Scripts/test_remove_localization_entry.py
With pytest:
python -m pytest Scripts/test_add_localization_entry.py Scripts/test_remove_localization_entry.py
On Windows, run localization-focused .NET tests:
dotnet test Tests/Grex.Tests.csproj -p:Platform=x64 -p:WindowsAppSdkBootstrapInitialize=false --filter "FullyQualifiedName~Localization"
dotnet test Tests/Grex.Tests.csproj -p:Platform=x64 -p:WindowsAppSdkBootstrapInitialize=false --filter "FullyQualifiedName~RegexBuilderLanguage"
dotnet test IntegrationTests/Grex.IntegrationTests.csproj -p:Platform=x64 -p:WindowsAppSdkBootstrapInitialize=false --filter "FullyQualifiedName~AiSearchLocalization"
Then manually verify:
- Settings language list;
- Search, Regex Builder, Settings, About, and Credits;
- tooltips;
- dialogs and notifications;
- result/status pluralization;
- restart persistence;
- fallback for an intentionally missing key.
Line endings
All .resw files are CRLF. Python’s XML writer emits platform-native newlines, so running mutation scripts on Linux or macOS can turn every line into LF.
Before editing on any platform:
git config core.autocrlf false
git ls-files --eol Strings/en-US/Resources.resw
Expected index form:
i/crlf
After running a script:
git diff --stat -- Strings
git ls-files --eol Strings/en-US/Resources.resw
On Linux or macOS, convert changed .resw files back to CRLF before committing. Avoid a repository-wide line-ending diff.
Add a language
- Choose a valid Windows culture code, such as
xx-YY. - Copy the English catalog to
Strings/xx-YY/Resources.resw. - Keep the same key set and XML structure.
- Set non-English entries to
status:incomplete. - Translate and review.
- Run the status report.
- Build on Windows so PRI resource generation validates the culture.
- Verify the language appears in Settings and survives restart.
Grex.csproj includes Strings\**\Resources.resw as PRI resources, so no per-language project entry is normally needed.
Review checklist
- Every English key exists once.
- Every changed key exists in every catalog.
- Format placeholders match English.
- No user-facing text was added outside localization.
- Status comments match review state.
.reswfiles remain CRLF.- Script and .NET localization tests pass.
- Dynamic language switching was checked.
- Machine-translated text received human review.