.NET Troubleshooting

Satellite assembly not loading? Check these in order.

You ship de\MyApp.resources.dll, the user runs German Windows, and the application still shows English. ResourceManager does not throw when a satellite is missing or rejected: it quietly falls back to the neutral resources. Nine causes cover nearly every case.

UI culture de-DE: each string is looked up on its own de-DE\not shippedde\neutralMyApp.exeCustomer:—Kunde:Customer:Quantity:—Menge:Quantity:Cancel—missingCancel Neue BestellungKunde:Menge:Cancel Cancel is missing in de\,so it comes from the neutralresources; the rest is German Each string on its own de-DE\not shippedde\neutralMyApp.exeCustomer:—Kunde:Customer:Quantity:—Menge:Quantity:Cancel—missingCancel Neue BestellungKunde:Menge:Cancel
On this page

Two-minute diagnosis.

Put this in the application, run it on the machine where the language is wrong, and compare the output with the checklist below.

var asm = typeof(Program).Assembly;
var ui = CultureInfo.CurrentUICulture;
Console.WriteLine($"UI culture: {ui.Name}  (parent: {ui.Parent.Name})");
Console.WriteLine($"Assembly:   {asm.Location}");
foreach (var name in asm.GetManifestResourceNames())
    Console.WriteLine($"  neutral:  {name}");

for (var c = ui; c.Name != ""; c = c.Parent)
{
    try
    {
        var sat = asm.GetSatelliteAssembly(c);
        Console.WriteLine($"{c.Name}: {sat.Location}");
        foreach (var name in sat.GetManifestResourceNames())
            Console.WriteLine($"  {name}");
    }
    catch (Exception e)
    {
        Console.WriteLine($"{c.Name}: {e.GetType().Name}: {e.Message}");
    }
}
  • UI culture is not the one you expect: see 1.
  • No satellite found for any culture in the chain: see 2, 3, and 7.
  • Satellite found, wrong resource names inside: see 4.
  • A load exception on .NET Framework: see 5 and 6.

The checklist.

Causes of a satellite assembly not loading, how to check each, and the fix
CauseHow to checkFix
1. The UI culture is not what you thinkCurrentUICulture decides the language, not CurrentCulture (number and date formats). Windows display language and regional format are different settings.Set CultureInfo.DefaultThreadCurrentUICulture and CurrentUICulture at startup, before the first resource is read and before any form or window is created.
2. The folder name is not a culture in the chainFor de-AT the runtime tries de-AT\, then de\. A folder named German, de_DE, or de-DE for an Austrian user is never found. On Linux and macOS the name is case-sensitive.Name folders as CultureInfo.Name writes them, and ship the parent culture, such as de\.
3. The satellite is not next to its assemblyResources of MyLib.dll load from de\MyLib.resources.dll next to MyLib.dll, not next to the EXE if the library lives elsewhere. Installers and publish profiles often drop the culture folders.Add the culture folders to the installer or publish output, and check the installed application, not bin\Debug.
4. Resource names do not matchnew ResourceManager("MyApp.Strings", …) expects MyApp.Strings.resources in the main assembly and MyApp.Strings.de.resources in the satellite. A changed namespace or RootNamespace breaks the pair.Compare the names the diagnosis prints. Rebuild the satellites after renaming a namespace or a .resx file.
5. Version mismatch (.NET Framework)After the main assembly's version changed, satellites built for the old version are ignored. Compare the versions in the file properties.Rebuild the satellites with the new version, or keep old ones valid with [assembly: SatelliteContractVersion("1.0.0.0")].
6. Strong name (.NET Framework)A strong-named main assembly accepts only satellites signed with the same key. sn -T on both files shows the public key tokens.Sign the satellites with the application's key. .NET 5–10 loads unsigned satellites.
7. Neutral resources live in a satelliteNeutralResourcesLanguage(…, UltimateResourceFallbackLocation.Satellite), or <UICulture> in a WPF project, moves the neutral resources to a folder such as en-US\. Without it, WPF reports IOException: Cannot locate resource 'mainwindow.xaml'.Ship that folder as well, or keep the neutral resources in the main assembly. See also the WPF guide.
8. Invariant globalization mode (.NET 5–10)<InvariantGlobalization>true</InvariantGlobalization> in the project or runtimeconfig.json leaves only the invariant culture; by default creating de-DE then throws CultureNotFoundException.Turn invariant mode off for applications that show more than one language.
9. The string is missing from the satelliteThe satellite loads, but one string is in English: ResourceManager took it from the parent culture or the neutral resources.Translate the missing resource and rebuild the satellite. A localization tool's validation lists untranslated items before release.

Trace the loader.

When the checklist does not explain it, let the runtime tell you which files it tried.

.NET Framework 4.8

Run Fusion Log Viewer (fuslogvw.exe) as administrator, turn on logging of failed binds, start the application, and open the entry for MyApp.resources. It lists every path probed and the reason a file was rejected, such as a version or public key mismatch.

.NET 5–10

The default context raises Resolving for every satellite it did not find. Log it during startup:

AssemblyLoadContext.Default.Resolving += (ctx, name) =>
{
    Console.WriteLine($"Not found: {name.Name}, {name.CultureName}");
    return null;
};

Satellites from Lingobit Localizer.

Lingobit builds satellite assemblies from your compiled .NET application: Create Localized Files writes de\MyApp.resources.dll and the other languages next to your files, and the EXE and DLLs stay unchanged. Several causes above are handled in the project settings.

  • Version: satellites get the version of the original assembly, so 5 does not happen after you rebuild them for a release.
  • Strong name: for strong-named .NET Framework assemblies, set your key in the project and the satellites are signed.
  • .NET 5–10: building satellites needs the .NET SDK of that version, or a newer one if the project allows it. The Output panel reports when a newer SDK was used.
  • Missing strings: validation lists untranslated items, and Run Localized Application starts the result in the chosen language.