.NET WPF / BAML · LocBaml alternative

WPF and BAML localization from the compiled build.

Lingobit reads the BAML inside your compiled WPF assemblies, shows a visual preview while you translate, and writes satellite assemblies for each language, on .NET Framework 4.8 and .NET 5–10.

Already translated? See how migration works

Your build EXE MyApp.exe Lingobit Localizer Harbor Supply 12 New Order Neue Bestellung Customer: Kunde: Quantity: Menge: Create Cancel Erstellen Abbrechen The window as it is in the build, translated in the preview You ship EXE MyApp.exe DEde\ FRfr\ JAja\ The same MyApp.exe; MyApp.resources.dll in each language folder Your build EXE MyApp.exe Translate in LingobitPreview Harbor Supply 12 New Order Neue Bestellung Customer: Kunde: Quantity: Menge: Create Cancel Erstellen Abbrechen You ship EXE MyApp.exe unchanged DEde\ FRfr\ JAja\
Input
Your compiled .exe and .dll files
Output
.resources.dll satellite assemblies in culture folders
.NET
Framework 4.8 and .NET 5–10, detected automatically
Preview
Compiled BAML from the built application
On this page

LocBaml alternative

Everything LocBaml does, Lingobit does too. On .NET 5–10 as well.

LocBaml is a Microsoft sample for .NET Framework; developers report that it cannot load WPF assemblies built for .NET Core 3.1 and .NET 6. Lingobit does each of its steps on .NET Framework 4.8 and .NET 5–10, and the work LocBaml left to you.

  1. locbaml /parseReads the BAMLin the .exe and .dll you ship, keyed by x:Uid.
  2. CSV in ExcelTranslate with the window in viewor send translators CSV, XLIFF 1.2, or a translator project.
  3. locbaml /generateSatellites for all languageswith the version of your assembly, signed with your key if needed.
  4. locbaml.exe in a scriptCommand line toofor your build script. Professional and higher.

What LocBaml left to you: Validation Pseudo-translation Scan for Changes on every build Import of the translations you shipped

Tutorial

Localize a WPF application with Lingobit, step by step.

The steps follow the WPF Localization sample that comes with Localizer: two windows in English, a German translation, and a second version of the application. Use your own build in the same way. If you used LocBaml before, the steps say which LocBaml step they replace.

Before XAML <Window Title="New Order"> <TextBlock Text="Customer:"/> msbuild /t:updateuid After XAML <Window x:Uid="Window_1" Title="New Order"> <TextBlock x:Uid="TextBlock_1" Text="Customer:"/> Keep the keys: translations are matched to them in every next version. Before <Window Title="New Order"> <TextBlock Text="Customer:"/> msbuild /t:updateuid After <Window x:Uid="Window_1" Title="New Order"> <TextBlock x:Uid="TextBlock_1" Text="Customer:"/> Keep the keys: translations are matched to them in every next version.
Width="90" Neue Bestellung Kunde: Erstellen Noch heute versenden The text is cut Width="Auto" Neue Bestellung Kunde: Noch heute versenden Erstellen The text fits Width="90" The text is cut Neue Bestellung Kunde: Erstellen Noch heute versenden Width="Auto" The text fits Neue Bestellung Kunde: Noch heute versenden Erstellen
Text in code C# Show("File saved."); Stays in English in every language Extractor Text in resources C# Show(Strings.FileSaved); RESX Strings.resx FileSavedFile saved. DE Datei gespeichert. Translated with the windows Text in code stays in English C# Show("File saved."); Extractor Text in resources C# Show(Strings.FileSaved); RESX Strings.resx FileSavedFile saved. DE Datei gespeichert. Translated with the windows
  1. Before XAML <Window Title="New Order"> <TextBlock Text="Customer:"/> msbuild /t:updateuid After XAML <Window x:Uid="Window_1" Title="New Order"> <TextBlock x:Uid="TextBlock_1" Text="Customer:"/> Keep the keys: translations are matched to them in every next version. Before <Window Title="New Order"> <TextBlock Text="Customer:"/> msbuild /t:updateuid After <Window x:Uid="Window_1" Title="New Order"> <TextBlock x:Uid="TextBlock_1" Text="Customer:"/> Keep the keys: translations are matched to them in every next version.
  2. Width="90" Neue Bestellung Kunde: Erstellen Noch heute versenden The text is cut Width="Auto" Neue Bestellung Kunde: Noch heute versenden Erstellen The text fits Width="90" The text is cut Neue Bestellung Kunde: Erstellen Noch heute versenden Width="Auto" The text fits Neue Bestellung Kunde: Noch heute versenden Erstellen
  3. Text in code C# Show("File saved."); Stays in English in every language Extractor Text in resources C# Show(Strings.FileSaved); RESX Strings.resx FileSavedFile saved. DE Datei gespeichert. Translated with the windows Text in code stays in English C# Show("File saved."); Extractor Text in resources C# Show(Strings.FileSaved); RESX Strings.resx FileSavedFile saved. DE Datei gespeichert. Translated with the windows

Your build DLL MyApp.dll Project DE FR JA DLL MyApp.dll WindowsBAML MainWindow ExportOptions Strings.NET resources ExportSummaryFormat FileSaved Detected .NET 8 · 64-bit DLL Your build DLL MyApp.dll Project DE FR JA DLL MyApp.dll WindowsBAML MainWindow ExportOptions Stringsresources ExportSummaryFormat FileSaved Detected .NET 8 · 64-bit DLL
Your .NET 5–10 build EXE MyApp.exe only starts the app DLL MyApp.dll windows, BAML, resources Add DLL Orders.dll more windows Add … On .NET Framework 4.8 the windows are in MyApp.exe: add it. Your .NET 5–10 build EXE MyApp.exe only starts the app DLL MyApp.dll Add windows, BAML, resources DLL Orders.dll Add more windows On .NET Framework 4.8 the windows are in MyApp.exe: add it.
  1. Your build DLL MyApp.dll Project DE FR JA DLL MyApp.dll WindowsBAML MainWindow ExportOptions Strings.NET resources ExportSummaryFormat FileSaved Detected .NET 8 · 64-bit DLL Your build DLL MyApp.dll Project DE FR JA DLL MyApp.dll WindowsBAML MainWindow ExportOptions Stringsresources ExportSummaryFormat FileSaved Detected .NET 8 · 64-bit DLL
  2. Your .NET 5–10 build EXE MyApp.exe only starts the app DLL MyApp.dll windows, BAML, resources Add DLL Orders.dll more windows Add … On .NET Framework 4.8 the windows are in MyApp.exe: add it. Your .NET 5–10 build EXE MyApp.exe only starts the app DLL MyApp.dll Add windows, BAML, resources DLL Orders.dll Add more windows On .NET Framework 4.8 the windows are in MyApp.exe: add it.

Project MyApp.dll MainWindow ExportOptions Resources Harbor Supply Quantity: 12 Create Cancel New Order Neue Bestellung Customer: Kunde: SourceGerman New Order Neue Bestellung Customer: Kunde: Project MyApp.dll MainWindow ExportOptions Harbor Supply Quantity: 12 Create Cancel New Order Neue Bestellung Customer: Kunde: SourceGerman New Order Neue Bestellung Customer: Kunde:
ProjectDEFRJAMyApp.dllWindowsMainWindowStrings PrepareTranslationTaskImportTranslationResults TranslatorDENew OrderNeue BestellungCustomer:Kunde:CreateErstellenSourceGermanNew OrderNeue BestellungCustomer:Kunde:Quantity:Menge:Validation on save Or their CAT toolXLIFF 1.2for the tool the translatoralready uses ProjectDEFRJAMyApp.dllWindowsMainWindowStrings PrepareTranslation Task TranslatorDENew OrderNeue BestellungCustomer:Kunde:CreateErstellenSourceGermanNew OrderNeue BestellungCustomer:Kunde:Quantity:Menge:Validation on save Or their CAT toolXLIFF 1.2for the tool the translatoralready uses ImportResults
Project Auto Translate Using Harbor Supply 12 New Order Neue Bestellung Customer: Kunde: Quantity: Menge: Ship today Noch heute versenden Create Cancel Erstellen Abbrechen DeepL… Google Translate… Azure Translator… Translation Memory… Terminology… Pseudo Translate… Project Auto Translate Using Harbor Supply 12 New Order Neue Bestellung Customer: Kunde: Quantity: Menge: Ship today Noch heute versenden Create Cancel Erstellen Abbrechen DeepL… Google Translate… Azure Translator… Translation Memory… Terminology… Pseudo Translate…
  1. Project MyApp.dll MainWindow ExportOptions Resources Harbor Supply Quantity: 12 Create Cancel New Order Neue Bestellung Customer: Kunde: SourceGerman New Order Neue Bestellung Customer: Kunde: Project MyApp.dll MainWindow ExportOptions Harbor Supply Quantity: 12 Create Cancel New Order Neue Bestellung Customer: Kunde: SourceGerman New Order Neue Bestellung Customer: Kunde:
  2. ProjectDEFRJAMyApp.dllWindowsMainWindowStrings PrepareTranslationTaskImportTranslationResults TranslatorDENew OrderNeue BestellungCustomer:Kunde:CreateErstellenSourceGermanNew OrderNeue BestellungCustomer:Kunde:Quantity:Menge:Validation on save Or their CAT toolXLIFF 1.2for the tool the translatoralready uses ProjectDEFRJAMyApp.dllWindowsMainWindowStrings PrepareTranslation Task TranslatorDENew OrderNeue BestellungCustomer:Kunde:CreateErstellenSourceGermanNew OrderNeue BestellungCustomer:Kunde:Quantity:Menge:Validation on save Or their CAT toolXLIFF 1.2for the tool the translatoralready uses ImportResults
  3. Project Auto Translate Using Harbor Supply 12 New Order Neue Bestellung Customer: Kunde: Quantity: Menge: Ship today Noch heute versenden Create Cancel Erstellen Abbrechen DeepL… Google Translate… Azure Translator… Translation Memory… Terminology… Pseudo Translate… Project Auto Translate Using Harbor Supply 12 New Order Neue Bestellung Customer: Kunde: Quantity: Menge: Ship today Noch heute versenden Create Cancel Erstellen Abbrechen DeepL… Google Translate… Azure Translator… Translation Memory… Terminology… Pseudo Translate…

Pseudo Translate German Simulation · +30% Chinese Simulation Harbor Supply 12 Saved at 10:42 Orders ÖrderßÖ ?????? Customer: Cüßtömer:Cü 阿昂俩拉基豪拉恶: Quantity: Qüäntity:Qü 阿昂阿拉拉哈拉哟: Ship today Ship tödäyShi 俩色哈勒 拉基俄阿哟 Create Cancel CreäteC CäncelC 阿恶拉阿拉拉 阿阿拉阿拉瑟 Too long Still in English Not displayed Pseudo Translate German Simulation · +30% Chinese Simulation Harbor Supply 12 Saved at 10:42 Orders ÖrderßÖ ?????? Customer: Cüßtömer:Cü 阿昂俩拉基豪拉恶: Quantity: Qüäntity:Qü 阿昂阿拉拉哈拉哟: Ship today Ship tödäyShi 俩色哈勒 拉基俄阿哟 Create Cancel CreäteC CäncelC 阿恶拉阿拉拉 阿阿拉阿拉瑟 Too long Still in English Not displayed
Output · Validation C# Format String: inconsistent arguments ExportSummaryFormat Fixed Resource ExportSummaryFormat Source Export {0} pages to {1}. Export %d pages to %s. Export <b>all</b> pages. Export %1 pages to %2. German {0} Seiten nach {2} exportieren. %d Seiten nach %d exportieren. <b>Alle</i> Seiten exportieren. %1 Seiten nach %3 exportieren. {0} Seiten nach {1} exportieren. %d Seiten nach %s exportieren. <b>Alle</b> Seiten exportieren. %1 Seiten nach %2 exportieren. Output · Validation C# Format String: inconsistent arguments ExportSummaryFormat Fixed ExportSummaryFormat Source Export {0} pages to {1}. Export %d pages to %s. Export <b>all</b> pages. Export %1 pages to %2. German {0} Seiten nach {2} exportieren. %d Seiten nach %d exportieren. <b>Alle</i> Seiten exportieren. %1 Seiten nach %3 exportieren. {0} Seiten nach {1} exportieren. %d Seiten nach %s exportieren. <b>Alle</b> Seiten exportieren. %1 Seiten nach %2 exportieren.
Preview Druckeinstellungen Papier: A4 Kopien: 2 Exportieren Seiteneinrichtung Einrichten Does not fit the column Fits SourceGerman Paper:Papier: Copies:Kopien: Page Setup Seiteneinrichtung Einrichten ExportExportieren Preview Druckeinstellungen Papier: A4 Kopien: 2 Exportieren Seiteneinrichtung Einrichten Does not fit the column Fits SourceGerman Copies:Kopien: Page Setup Seiteneinrichtung Einrichten ExportExportieren
  1. Pseudo Translate German Simulation · +30% Chinese Simulation Harbor Supply 12 Saved at 10:42 Orders ÖrderßÖ ?????? Customer: Cüßtömer:Cü 阿昂俩拉基豪拉恶: Quantity: Qüäntity:Qü 阿昂阿拉拉哈拉哟: Ship today Ship tödäyShi 俩色哈勒 拉基俄阿哟 Create Cancel CreäteC CäncelC 阿恶拉阿拉拉 阿阿拉阿拉瑟 Too long Still in English Not displayed Pseudo Translate German Simulation · +30% Chinese Simulation Harbor Supply 12 Saved at 10:42 Orders ÖrderßÖ ?????? Customer: Cüßtömer:Cü 阿昂俩拉基豪拉恶: Quantity: Qüäntity:Qü 阿昂阿拉拉哈拉哟: Ship today Ship tödäyShi 俩色哈勒 拉基俄阿哟 Create Cancel CreäteC CäncelC 阿恶拉阿拉拉 阿阿拉阿拉瑟 Too long Still in English Not displayed
  2. Output · Validation C# Format String: inconsistent arguments ExportSummaryFormat Fixed Resource ExportSummaryFormat Source Export {0} pages to {1}. Export %d pages to %s. Export <b>all</b> pages. Export %1 pages to %2. German {0} Seiten nach {2} exportieren. %d Seiten nach %d exportieren. <b>Alle</i> Seiten exportieren. %1 Seiten nach %3 exportieren. {0} Seiten nach {1} exportieren. %d Seiten nach %s exportieren. <b>Alle</b> Seiten exportieren. %1 Seiten nach %2 exportieren. Output · Validation C# Format String: inconsistent arguments ExportSummaryFormat Fixed ExportSummaryFormat Source Export {0} pages to {1}. Export %d pages to %s. Export <b>all</b> pages. Export %1 pages to %2. German {0} Seiten nach {2} exportieren. %d Seiten nach %d exportieren. <b>Alle</i> Seiten exportieren. %1 Seiten nach %3 exportieren. {0} Seiten nach {1} exportieren. %d Seiten nach %s exportieren. <b>Alle</b> Seiten exportieren. %1 Seiten nach %2 exportieren.
  3. Preview Druckeinstellungen Papier: A4 Kopien: 2 Exportieren Seiteneinrichtung Einrichten Does not fit the column Fits SourceGerman Paper:Papier: Copies:Kopien: Page Setup Seiteneinrichtung Einrichten ExportExportieren Preview Druckeinstellungen Papier: A4 Kopien: 2 Exportieren Seiteneinrichtung Einrichten Does not fit the column Fits SourceGerman Copies:Kopien: Page Setup Seiteneinrichtung Einrichten ExportExportieren

Project MyApp.dll MainWindow ExportOptions Resources DE FR JA Create Localized Files Run Localized Application Release folder EXE MyApp.exe DLL MyApp.dll unchanged DEde\MyApp.resources.dll FRfr\MyApp.resources.dll JAja\MyApp.resources.dll Neue Bestellung Kunde: Harbor Supply Erstellen Abbrechen Project MyApp.dll MainWindow ExportOptions Create Localized Files EXE MyApp.exe · .dll unchanged DEde\ FRfr\ JAja\ Run Localized Application Neue Bestellung Kunde: Harbor Supply Erstellen Abbrechen
Release folder EXEMyApp.exe DEde\ FRfr\ JAja\ WPF loads the folder of the chosen language Harbor Supply 12 Neue Bestellung DateiSprache Kunde:Menge: Erstellen Abbrechen Deutsch Français 日本語 Nouvelle commande FichierLangue Client :Quantité : Créer Annuler Release folder EXEMyApp.exe DEde\ FRfr\ JAja\ Harbor Supply 12 Neue Bestellung DateiSprache Kunde:Menge: Erstellen Abbrechen Deutsch Français 日本語 Nouvelle commande FichierLangue Client :Quantité : Créer Annuler
  1. Project MyApp.dll MainWindow ExportOptions Resources DE FR JA Create Localized Files Run Localized Application Release folder EXE MyApp.exe DLL MyApp.dll unchanged DEde\MyApp.resources.dll FRfr\MyApp.resources.dll JAja\MyApp.resources.dll Neue Bestellung Kunde: Harbor Supply Erstellen Abbrechen Project MyApp.dll MainWindow ExportOptions Create Localized Files EXE MyApp.exe · .dll unchanged DEde\ FRfr\ JAja\ Run Localized Application Neue Bestellung Kunde: Harbor Supply Erstellen Abbrechen
  2. Release folder EXEMyApp.exe DEde\ FRfr\ JAja\ WPF loads the folder of the chosen language Harbor Supply 12 Neue Bestellung DateiSprache Kunde:Menge: Erstellen Abbrechen Deutsch Français 日本語 Nouvelle commande FichierLangue Client :Quantité : Créer Annuler Release folder EXEMyApp.exe DEde\ FRfr\ JAja\ Harbor Supply 12 Neue Bestellung DateiSprache Kunde:Menge: Erstellen Abbrechen Deutsch Français 日本語 Nouvelle commande FichierLangue Client :Quantité : Créer Annuler

MyApp.dll 1.0 1.1 New Order Customer: Client: Harbor Supply Quantity: 12 Ship today Gift wrap Create Cancel Project Scan for Changes Neue Bestellung Kunde: Updated Harbor Supply Menge: 12 Noch heute versenden Removed Gift wrap New Erstellen Abbrechen Rows are matched by x:Uid: Client keeps the translation of Customer. MyApp.dll 1.0 1.1 New Order Customer: Client: Harbor Supply Quantity: 12 Ship today Gift wrap Create Cancel Project Scan for Changes Neue Bestellung Kunde: Updated Harbor Supply Menge: 12 Noch heute versenden Removed Gift wrap New Erstellen Abbrechen Rows are matched by x:Uid.
Translated earlier MyApp.exe de\ DE MyApp.resources.dll fr\ FR MyApp.resources.dll Project Not Translated Imported DE FR MyApp.exe Dialogs Menus String Table Import from Localized Files Translated with another tool or long ago? Add your build: Lingobit finds the localized files and offers to import them. Import Translations from Localized Files Germande\MyApp.resources.dll Frenchfr\MyApp.resources.dll OK Cancel OK Translated earlier MyApp.exe de\ DE MyApp.resources.dll fr\ FR MyApp.resources.dll Project Not Translated Imported DE FR MyApp.exe Dialogs Menus String Table Import from Localized Files Translated with another tool or long ago? Add your build: Lingobit offers the import. Import Translations from Localized Files Germande\MyApp.resources.dll Frenchfr\MyApp.resources.dll OK Cancel OK
  1. MyApp.dll 1.0 1.1 New Order Customer: Client: Harbor Supply Quantity: 12 Ship today Gift wrap Create Cancel Project Scan for Changes Neue Bestellung Kunde: Updated Harbor Supply Menge: 12 Noch heute versenden Removed Gift wrap New Erstellen Abbrechen Rows are matched by x:Uid: Client keeps the translation of Customer. MyApp.dll 1.0 1.1 New Order Customer: Client: Harbor Supply Quantity: 12 Ship today Gift wrap Create Cancel Project Scan for Changes Neue Bestellung Kunde: Updated Harbor Supply Menge: 12 Noch heute versenden Removed Gift wrap New Erstellen Abbrechen Rows are matched by x:Uid.
  2. Translated earlier MyApp.exe de\ DE MyApp.resources.dll fr\ FR MyApp.resources.dll Project Not Translated Imported DE FR MyApp.exe Dialogs Menus String Table Import from Localized Files Translated with another tool or long ago? Add your build: Lingobit finds the localized files and offers to import them. Import Translations from Localized Files Germande\MyApp.resources.dll Frenchfr\MyApp.resources.dll OK Cancel OK Translated earlier MyApp.exe de\ DE MyApp.resources.dll fr\ FR MyApp.resources.dll Project Not Translated Imported DE FR MyApp.exe Dialogs Menus String Table Import from Localized Files Translated with another tool or long ago? Add your build: Lingobit offers the import. Import Translations from Localized Files Germande\MyApp.resources.dll Frenchfr\MyApp.resources.dll OK Cancel OK

WPF compiles XAML into BAML; languages ship as satellite assemblies.

When you build a WPF application, every window and page written in XAML is compiled into BAML and stored inside the assembly. Lingobit works on that compiled BAML: it writes the same windows in each language into a satellite assembly, and your code stays where it is.

Your build Lingobit Localizer XAML <Window x:Uid="Win_1" Title="New Order"> <TextBlock x:Uid="Txt_1" Text="Customer:"/> build DLL MyApp.dll Code BAML Win_1New Order Txt_1Customer: DE de\ MyApp.resources.dll BAML Win_1Neue Bestellung Txt_1Kunde: The keys stay the same in every build, so translations carry over to the next version. XAML Your build <Window x:Uid="Win_1" Title="New Order"> <TextBlock x:Uid="Txt_1" Text="Customer:"/> build DLL MyApp.dll code + BAML Win_1New Order Txt_1Customer: Lingobit DE de\MyApp.resources.dll Win_1Neue Bestellung Txt_1Kunde: The keys stay the same in every build, so translations carry over.

At startup, WPF picks the language folder.

.NET uses the current UI culture, from Windows or set by your application, and loads the windows from the matching folder next to the application. You copy the folders with the release; nothing is rebuilt.

At startup UI culture de-DE from Windows or set by the app Release folder EXE MyApp.exe DE de\ loaded FR fr\ JA ja\ The window Neue Bestellung Kunde: Menge: Erstellen in German At startup UI culture de-DE Windows or app EXE MyApp.exe DEde\ FRfr\ JAja\ Neue Bestellung Kunde: Erstellen Menge:

BAML or RESX: choose by what your build ships.

Most WPF text is in XAML and is compiled into BAML. Text that your XAML takes from .resx files, through {x:Static} bindings, is compiled into the same assembly, and Lingobit translates it there together with the windows. Translate the .resx files themselves only when the translated files must go back into source control.

The compiled-build workflow and the RESX workflow for WPF
Compiled build RecommendedRESX files Alternative
Input.exe, .dll.resx files from your source
What gets translatedWindows (BAML) and the .NET resources in the same assembliesThe strings in the .resx files only
PreviewThe WPF windows, as they are in the buildNone: a list of strings
Output.resources.dll satellite assemblies, ready to shipTranslated .resx files; your build makes the satellites
Use whenYou ship the compiled application; this is most WPF projectsTranslated .resx files must stay in source control

.NET Resources guide for the RESX workflow

Moving from LocBaml.

Keep what worked in the LocBaml approach, x:Uid keys and satellite assemblies, and stop parsing and merging CSV files.

With LocBaml, for each assembly and language

msbuild -t:updateuid MyApp.csproj
msbuild MyApp.csproj
LocBaml.exe /parse MyApp.resources.dll /out:MyApp.csv
rem translate MyApp.csv in Excel
LocBaml.exe /generate MyApp.resources.dll
    /trans:MyApp.de-DE.csv /out:de-DE /cul:de-DE
rem copy, version, and sign the satellite
rem next build: parse again, merge the CSV by hand

With Lingobit, once

  • Keep your x:Uid values and keep checking them with updateuid and checkuid.
  • Create a project from the current build with the same target languages; on .NET 5–10, add MyApp.dll. No <UICulture> needed.
  • Import the translations you shipped from your existing satellite assemblies with Import Translations from Localized Files. LocBaml's CSV files are not imported.
  • Scan for Changes on every next build instead of parsing and merging CSV files again.

Reference

Culture decides which resources load and how text behaves.

If the application does not set a language itself, .NET uses the current UI culture and loads the best match it has: de-AT, then de, then the neutral resources in the main assembly.

WPF looks in order de-AT de-AT\ not shipped de de\ found neutral MyApp.exe English Neue Bestellung Kunde: Menge: Erstellen Abbrechen The window, from de\ WPF looks in order de-AT de-AT\ not shipped de de\ found neutral MyApp.exe English Neue Bestellung from de\ Kunde: Menge: Erstellen
  • Each window is looked up on its own. A window comes whole from one folder; a window that is missing in de\ opens in the neutral language.
  • Ship the parent culture. A de\ folder serves de-DE, de-AT, and de-CH; add de-CH\ only when Swiss German text differs.

Choose the language in your app instead of relying on fallback.

Many applications let users pick the language. Set the culture yourself, before WPF loads the first window:

  1. List the languages you ship from the culture folders next to the application.
  2. Read the saved choice or a command-line switch; fall back to the source language.
  3. Set CurrentUICulture before InitializeComponent of the application.
  4. Restart to switch after the user picks another language.
[STAThread]
public static void Main(string[] args)
{
    var culture = CultureInfo.GetCultureInfo(LoadSavedLanguage() ?? "en-US");
    CultureInfo.DefaultThreadCurrentCulture = culture;
    CultureInfo.DefaultThreadCurrentUICulture = culture;
    Thread.CurrentThread.CurrentCulture = culture;
    Thread.CurrentThread.CurrentUICulture = culture;

    var app = new App();
    app.InitializeComponent();
    app.Run();
}

The WPF sample does exactly this: its Language menu lists only the cultures whose satellites contain the windows, and --culture de-DE starts it in German.

xml:lang and neutral resources.

xml:lang

Set it in the WPF source when the application needs language-specific text behavior: font fallback, number substitution, spell checking, hyphenation, and text shaping. It is part of the application setup, not of the translation.

Neutral resources

Set UICulture only when the neutral-language BAML should go into a neutral-language satellite assembly; keep NeutralResourcesLanguage and UltimateResourceFallbackLocation aligned with that choice.

Show the settings

Neutral resources in the main assembly

[assembly: NeutralResourcesLanguage(
    "en-US",
    UltimateResourceFallbackLocation.MainAssembly)]

Neutral resources in a satellite assembly

<PropertyGroup>
  <UICulture>en-US</UICulture>
</PropertyGroup>

[assembly: NeutralResourcesLanguage(
    "en-US",
    UltimateResourceFallbackLocation.Satellite)]
<Window x:Class="MyApp.MainWindow" xml:lang="de-DE">

Do and don't.

Do

  • Localize the build you release, every time from the current files.
  • Keep x:Uid values stable between versions.
  • Pseudo-translate and run the build before translators start.
  • Look at long translations in the preview.
  • Ship the parent culture folder, such as de\.

Don't

  • Regenerate UIDs after localization has started.
  • Give windows fixed widths that only fit English.
  • Build UI text in code from pieces of strings.
  • Mix the two neutral-resource layouts in one project.
  • Set the culture after the first window is created.

Typical localizability problems in WPF applications.

Problems in the code that translation cannot fix, how each shows up in the localized build, and how to find it before translators start. The general checklist is in Software internationalization; the test build in Pseudo-localization.

Localizability problems, how they show, and how to find them
ProblemHow it showsFind and fix
Elements have no x:UidTranslations cannot be matched to the next versionRun msbuild /t:updateuid once, check with /t:checkuid, and keep the values stable
Text is set in code-behind or a view modelIt stays in English: it is not in the BAMLMove it into .resx; Lingobit Extractor finds hard-coded C# strings
Sentences are built from piecesWord order is wrong in other languagesUse one resource with placeholders and string.Format; Validation reports placeholders that do not match the source
Widths are fixedGerman text is clipped in buttons and grid columnsUse Auto and star sizes; pseudo-translate with longer text and check the windows in the preview
Dates in bindings use US formatsStringFormat ignores the user's cultureSet FrameworkElement.Language; see Change the language at runtime

Troubleshooting.

Symptoms, causes, and what to do
SymptomLikely causeWhat to do
The application stays in EnglishThe satellite is not in a culture folder next to the assembly, or the UI culture has no matching folderCopy the folders with the release; ship the parent culture; set the culture before the first window
One window stays in EnglishThe window comes from another assembly of the applicationAdd that DLL to the project and create its satellite too
Some strings stay in EnglishThey are written in C# code, not in XAML or resourcesMove them into .resx, by hand or with Extractor
Translations are New again after an updatex:Uid values were renamed or regeneratedKeep UIDs stable; import translations from your previous satellites
The preview does not open a windowAn assembly the window needs is missingKeep the dependencies next to the assembly you add; translate in the table meanwhile
A .NET Framework 4.8 application ignores the satellitesThe main assembly is strong-named, and the satellites are not signedSign the satellites with your key in the project
Creating .NET 5–10 satellites failsThe .NET SDK of that version is not installedInstall it, or allow a newer SDK in the project
The localized build crashesA translation breaks a format string or resourceRun Validation; use Crash Finder (Enterprise)
The application is on .NET 1.x–3.5Localizer 10 supports .NET Framework 4.8 and .NET 5–10Use Localizer 9

The WPF Localization sample.

Localizer installs a WPF sample with its project, so you can try every step above without your own build.

  • Two windows: the main window with Print Settings, Profiles, and Resources tabs, and Export Options.
  • English source, a German translation, and a Language menu that restarts the application.
  • Two deliberate defects: a German caption too long for its column, to spot in the preview, and a broken format string.
  • Version 2 of the application to try Scan for Changes.

More about WPF localization.

RESX-based WPF localization

Some WPF applications bind UI text to RESX resources rather than relying on BAML. Use this path when translated .resx files must stay in source control and be compiled by the normal Visual Studio, MSBuild, or CI process.

Input:  Resources.resx  Strings.resx  ValidationMessages.resx
Output: Resources.de.resx  Strings.de.resx  ValidationMessages.de.resx
  • Translated resource files stay in the source and build workflow, and developers review the changes in source control.
  • Localization stays tied to compilation: Visual Studio, MSBuild, or CI builds the satellite assemblies.
  • Visual context is weaker than in the BAML / binary workflow.
Reuse existing WPF translations

If previous WPF translations exist, start from the current original build, choose the target languages, and import the translations that still match from:

  • existing .resources.dll satellite assemblies;
  • previous localized WPF binaries;
  • localized .resx files.
What to check in the pseudo-localized WPF build
  • BAML resources are extracted, and x:Uid values are present and stable.
  • Longer text still fits; wrapping and automatic layout behave correctly.
  • FlowDirection and font fallback are acceptable for the target languages.
  • No hardcoded strings were missed, and the application does not crash. If it does, Crash Finder (Enterprise) isolates the resource or translation that causes it.

WPF localization FAQ

Does LocBaml work with .NET 8?

LocBaml is a Microsoft sample that targets .NET Framework. Developers report that it cannot load WPF assemblies built for .NET Core 3.1 and .NET 6. Lingobit reads the BAML in WPF builds on .NET Framework 4.8 and .NET 5–10, including .NET 8.

Do I still need x:Uid?

Yes. Lingobit matches translations to the x:Uid keys in every next version. Add them with msbuild /t:updateuid, check them with msbuild /t:checkuid, and do not regenerate them after localization starts.

Can I keep the translations I made with LocBaml?

Yes, through the satellite assemblies you built: Import Translations from Localized Files takes the translations from existing satellites where they still match the current build. Lingobit does not import LocBaml's CSV files.

Do I need a neutral-language satellite and UICulture, as with LocBaml?

No. Lingobit reads the BAML from the assemblies you ship, including the main assembly. Set UICulture only when you want the neutral-language resources in a satellite; see xml:lang and neutral resources.

Can I change a translation without rebuilding the WPF application?

Yes, when you localize the compiled build. Lingobit writes de\MyApp.resources.dll from the assemblies you ship: correct the translation in the project, run Create Localized Files again, and replace the satellite. The main assembly stays as it is. Create the satellite from the same build you ship, so its version matches the main assembly. Translated .resx files, by contrast, need your build to compile them again.

How do I run msbuild /t:updateuid for all projects in a solution?

Run it for each project that has XAML: msbuild MyApp.csproj /t:updateuid. In an SDK project with several target frameworks (<TargetFrameworks>), the target is only found for one framework at a time, so name it: /p:TargetFramework=net8.0-windows. The UIDs go into the XAML files and serve every framework. Check them with /t:checkuid before localization starts.

Why does ResourceManager.GetString return only English?

.NET found no satellite for the current UI culture: the culture folder is not next to the assembly, its name does not match the culture, or the culture was set after the first resources were read. Go through Satellite assembly not loading.