Hello,
In a .NET Core 3.0 project, localized resources from culture specific RESX files are ignored although the CultureInfo.DefaultThreadCurrentCulture and CultureInfo.DefaultThreadCurrentUICulture properties are set to a specific culture. To resolve the problem, one can set Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture instead and handle the AssemblyLoadContext.Default.Resolving event as follows:
private static System.Reflection.Assembly Default_Resolving(AssemblyLoadContext arg1, System.Reflection.AssemblyName arg2) {
return null;
}
I've attached a simple WPF project reproducing the issue (WpfApp1.zip). In a similar .NET Framework 4.5.2 project, everything works fine.
Current behavior: TextBlock has its text set to 'Invariant String'
Expected behavior: TextBlock displays 'Chinese String'
Thank you in advance.
DefaultThreadCurrentUICulture is useful to set the default culture for the threads that are going to be created. DefaultThreadCurrentUICulture is not going to affect the current thread if the current thread already initialized the UI culture there (CultureInfo.CurrentUICulture). the thread UI culture can get initialized for different reason and not necessary from your code. If you want to force specific culture in current thread, would be better explicit calling CurrentUICulture. so in your code, you can do:
```C#
private void Application_Startup(object sender, StartupEventArgs e) {
var culture = CultureInfo.CreateSpecificCulture("zh-CN");
// CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
CultureInfo.CurrentUICulture = culture;
//Thread.CurrentThread.CurrentCulture = culture;
}
The other idea is you can set the culture in the generated resource handling class ```Resource1``` something like:
```C#
Resource1.Culture = culture;
doing that, will ensure resource lookup using this culture and not CurrentUICulture/DefaultThreadCurrentUICulture.
Feel free to send more question if there is anything unclear here.
Hello,
Thank you for your reply.
In addition, would you please explain why the following behavior takes place? If I follow these steps:
then 'Invariant String' is displayed in the TextBlock.
However, if I handle the AssemblyLoadContext.Default.Resolving event, then 'Chinese String' is shown.
I demonstrated the steps above in the attached video.
@Andre644 when you compile your app, the file netcoreapp3.0\WpfApp1.deps.json will get generated. This file will contains all modules the app can load. If the file zh-CN/WpfApp1.resources.dll not exist during the compilation, then this will not be included in netcoreapp3.0\WpfApp1.deps.json and the app will not load it even if you copy it manually. either you'll need to modify the json file or just include the file during the compilation or you use AssemblyLoadContext.Default.Resolving as you did.
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v3.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v3.0": {
"WpfApp1/1.0.0": {
"runtime": {
"WpfApp1.dll": {}
},
"resources": {
"zh-CN/WpfApp1.resources.dll": {
"locale": "zh-CN"
}
}
}
}
},
"libraries": {
"WpfApp1/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
Thanks! But why is it working without returning an actual assembly from the Resolving event? It seems strange to me that null is sufficient in this case.
@jeffschwMSFT @sdmaclea could you help answering @Andre644 question?
It seems strange to me that null is sufficient in this case.
null should be sufficient in .NET Core 3.0. In .NET Core 3.0 the default resource loading algorithm was changed to find resources w/o json support.
For <path>/WpfApp1.dll, the runtime should be looking by default in <path>/<culture-name>/WpfApp1.resources.dll.
The need to add this handler is a bit surprising. Perhaps Wpf is adding its own incorrect AssemblyLoadContext.Default.Resolving event handler. This could be breaking the default runtime loader behavior.
/cc @vitek-karas @elinor-fung
It is not actually the event handler that is making this work, but simply the usage of AssemblyLoadContext.Defualt . Doing _ = AssemblyLoadContext.Default; instead of adding an event handler for Resolving gets the resource resolved from the subdirectory as well.
Without that usage, AssemblyLoadContext.Default wasn't initialized yet, so the native runtime side did not run its resolution fallback for when the resource is not listed in the deps.json - i.e. looking in the subdirectory - as that logic requires a pointer to the managed AssemblyLoadContext. We should probably make it so that the default context is always initialized before / as part of GetSatelliteAssembly.
I am inclining agreeing with @elinor-fung. regardless of the details of how we fix it, I want to emphasize that the satellite assemblies should be loaded if exist regardless if the deps file include them or not.
by the way, should we reactivate this issue to track fixing that?
Thanks @elinor-fung
IMO We should fix in 5.0, 3.1, and 3.0.