.resx is Microsoft's XML resource format, the canonical way to store user-facing strings in .NET desktop, Xamarin, and many legacy ASP.NET projects. Modern web frontends, by contrast, want a flat i18next-style JSON object that can be served as a static asset and consumed directly by react-i18next, next-intl, or any other JavaScript i18n library. Teams that have a long .NET history and a new web client repeatedly write the same conversion — typically as a one-off PowerShell or Python script that gets the schema mostly right and the escaping mostly wrong.
i18n-convert parses the RESX XML, skips the boilerplate <resheader> blocks that every RESX file is required to start with, and emits a JSON object whose keys are the name attributes of the <data> elements and whose values are the inner text of the <value> children. The xml:space="preserve" attribute is respected — meaningful whitespace inside <value> is kept, while unrelated XML formatting whitespace between elements is discarded. Empty <value> elements become empty strings in the JSON (""), not null, so downstream code can rely on values being strings without runtime checks. The output is sorted alphabetically by key for deterministic diffs across translation rounds. <comment> siblings are dropped because i18next JSON has no slot for them.
Command
i18n-convert simple.resx --to i18next -o messages.json
Input
<?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>
<data name="greeting" xml:space="preserve">
<value>Hello, World!</value>
</data>
<data name="farewell" xml:space="preserve">
<value>Goodbye!</value>
</data>
<data name="empty_value" xml:space="preserve">
<value></value>
</data>
</root>
Output
{
"empty_value": "",
"farewell": "Goodbye!",
"greeting": "Hello, World!"
}