Translate .NET .resx resource files
A .resx file mixes text with icons and other typed resources in one XML. KAERIS translates only the strings, leaves the rest byte for byte, and checks the {0} format items.
How a .resx file is built
An XML <root> with <data name="…"><value>…</value></data> entries. An entry with a type or mimetype attribute (an icon, an image) is not text. Names like SaveButton.Text are control properties from WinForms.
<?xml version="1.0" encoding="utf-8"?>
<root>
<resheader name="resmimetype"><value>text/microsoft-resx</value></resheader>
<data name="WelcomeMessage" xml:space="preserve">
<value>Welcome back, {0}!</value>
<comment>{0} is the user's first name</comment>
</data>
<data name="ItemsInCart" xml:space="preserve">
<value>You have {0} items in your cart.</value>
</data>
<data name="SaveButton.Text" xml:space="preserve">
<value>Save and close</value>
</data>
<data name="AppIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\app.ico;System.Drawing.Icon, System.Drawing</value>
</data>
</root>
What to watch for in .resx translations
Typed entries are not text
The
AppIconentry in the real run below is a file reference. It is copied to the output untouched and never sent for translation.Format items
{0}instring.Formatmust come back; in the real run below both did, and a missing one would be reported.Keys are not text
Names such as
SaveButton.Textare control properties from WinForms. They are keys: kept as written, never translated.Comments stay in the source
<comment>elements are not carried into the translated file. Keep notes for translators in the source resource.
A real run: translated to German and checked
A real run, 27 October 2026, nothing edited by hand: kaeris translate Strings.resx --langs de with kaeris 0.2.35, 2.6 seconds, model openai/gpt-4.1-mini (the service's fallback model answered this job). Below, the German file it wrote and what kaeris check says about it.
<?xml version="1.0" encoding="utf-8"?>
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="WelcomeMessage" xml:space="preserve">
<value>Willkommen zurück, {0}!</value>
</data>
<data name="ItemsInCart" xml:space="preserve">
<value>Sie haben {0} Artikel in Ihrem Warenkorb.</value>
</data>
<data name="SaveButton.Text" xml:space="preserve">
<value>Speichern und schließen</value>
</data>
<data name="AppIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\app.ico;System.Drawing.Icon, System.Drawing</value>
</data>
</root>
$ kaeris check --source Strings.resx --langs de ✓ de: complete & placeholder-safe ✓ all locales complete & placeholder-safe
What KAERIS does with it
Strings only
Typed and binary entries are preserved verbatim, as in the real run above: the
AppIconentry came back unchanged.DTD and entities refused
Files with a DOCTYPE or entity declaration are rejected before parsing, a guard shared with XLIFF and Android XML.
Free check for this format
The free check on /check reads this format and runs without a key or an account. The terminal command
kaeris checkreads it too: it takes all 13 formats since kaeris 0.2.35, with the same rules and nothing sent anywhere.
Run it in three steps
Install the CLI
Run
pip install kaeris.Translate the resource file
Run
kaeris translate Resources/Strings.resx --langs de,ja.Check the result
Upload the source and the translated .resx on the free check page.
pip install kaeris kaeris translate Resources/Strings.resx --langs de,ja --verify
Questions
Are icons and other typed resources touched?
No. Entries with a type or mimetype attribute are kept verbatim.
Are elements kept?
No: the translated file is written without them. Your source file keeps them.
Is it free?
Yes, without an account: 10,000 characters per file and up to 8 languages per run. A free API key raises the file limit to 50,000 characters. Lifetime is $79 once, on your own OpenRouter key; monthly plans start at $19. Pricing
Can I run it from the terminal or in CI?
Yes. pip install kaeris, then kaeris translate Resources/Strings.resx --langs de,ja. The GitHub Action runs the same translation in a pull request. CLI docs · GitHub Action
Keep going
CLI reference · Check reference · Translate in the browser · Free check · Convert formats · Pseudo-localization
Other formats: JSON · YAML · .po · ARB · XLIFF · .strings · .xcstrings · .properties · CSV · Markdown · Fluent · Android XML