Localising a WPF application doesn't work? - c#

I must be missing something here.
I create a brand new WPF application in VS2015. I create a resource 'String1' and set the value to 'fksdlfdskfs'.
I update the default MainWindow.xaml.cs so that the constructor has:
public MainWindow()
{
InitializeComponent();
this.Title = Properties.Resources.String1;
}
And run the application, and it works fine, my window title is fksdlfdskfs.
In the AssemblyInfo.cs file I see the below comments:
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
So I add the following into my WpfApplication5.csproj file and reload the project in VS:
<UICulture>en-US</UICulture>
And then uncommented the following line in AssemblyInfo.cs:
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
If I now go to run the application, the application no longer runs and I get the following exception on the line where I read the resource:
System.Resources.MissingManifestResourceException: Could not find any
resources appropriate for the specified culture or the neutral
culture. Make sure
"WpfApplication5.Properties.Resources.en-US.resources" was correctly
embedded or linked into assembly "WpfApplication5" at compile time, or
that all the satellite assemblies required are loadable and fully
signed.
If I change UltimateResourceFallbackLocation.Satellite to UltimateResourceFallbackLocation.MainAssembly in the AssemblyInfo.cs file, I get the following exception instead:
System.IO.IOException: Cannot locate resource 'mainwindow.xaml'
What am I doing wrong or what am I missing?

You're not forced to use code behind for localization, you can simply use x:Static markup extension to bind to static fields:
<Window
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:SandBox.Properties"
Title="{x:Static properties:Resources.TitleSandbox}">
</Window>
Just make sure your Resource file access modifier is set to Public
The error message you get typically means you do not have a Resource.en-US.resx file because [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] is here to tell your app to use en en-US resource file as default source.
Add a file named Resources.en-US.resx if you want to get rid of the error the quick way
What I personally do to localize a WPF app is :
I leave AssemblyInfo.cs as it is, which means that Resource.resx (without language id) file will be the default (which is generally en-US)
I create additional Resource.{id}.resx file next to default like this :
,
It is usually the same as Resource.resx but translated in the matching language
I force the culture at startup (typically in the App.xaml.cs) with a user settable language id so user can change application language :
// language is typically "en", "fr" and so on
var culture = new CultureInfo(language);
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
// You'll need to restart the app if you do this after application init

Related

Unable to read culture specific values

I working on a localization project and unable to read the value by local from the resource file.
I have a sample solution as below:
1. Web API project
2. Resources Project.
- In resources project, I have 3 resource files de-DE, fr-FR, and en-US.
I am reading the resource key "Name" from the resource file, I am able to get the value from de-DE and en-US by setting culture value as below.
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");
var name = Employees.Name;
return new string[] { name };
}
However, When I try to read the key for fr-FR local, it is giving English value.
The only difference is, the French resource file is present inside a folder and others are present at project level.
The .NET resource system is based largely on convention; it expects resource files for specific cultures to be found at very specific paths.
Employees.resx contains the neutral resources for the Employees container.
Employees.[culture].resx contains the resources for the Employees container for a specific culture.
If you have a resource file in another folder, the resource-lookup system treats it as a separate resource. fr-FR/Employees.fr-FR.resx is the French resources for the fr_FR.Employees container.
To make your resources load correctly, you just need to put them in the same folder:
Employees.resx
Employees.de-DE.resx
Employees.fr-FR.resx

Access Resources from Externally-Created .resx File

My main Windows Forms (managed C++) project has a class that presents an image with tiles that can be shown or hidden to create a responsive diagram.
I created a separate utility app that helps me get all the images positioned correctly, etc. This app is written in C# and writes a .resx file containing the image data and positioning, using the following code snippet:
using(ResXResourceWriter resx = new ResXResourceWriter(sfd.FileName)) {
resx.AddResource("Size", canvas.Size);
List<int> IDs = canvas.IDs;
resx.AddResource("IDList", IDs);
resx.AddResource("BackgroundIndex", canvas.BackgroundIndex);
foreach(int id in IDs) {
String positionKey = String.Format("Position.id{0}", id);
String visibilityKey = String.Format("Visibility.id{0}", id);
String imageKey = String.Format("Image.id{0}", id);
resx.AddResource(imageKey, canvas.TileImage(id));
resx.AddResource(positionKey, canvas.TilePosition(id));
resx.AddResource(visibilityKey, canvas.TileVisible(id));
}
}
I can open the .resx file in a text editor and see that it is well-formed and contains the expected data.
Then I take that .resx file and add it to my main application's project. Now I can't figure out how to get at the resources inside it. The code I've tried is:
ResourceManager ^ image_rm = gcnew ResourceManager(
"resx_file_name_without_extension", GetType()->Assembly);
ResourceSet ^ image_rs = image_rm->GetResourceSet(
System::Globalization::CultureInfo::CurrentCulture, true, true);
At runtime, the second line (the GetResourceSet call) throws a System.Resources.MissingManifestResourceException with the following message text:
Resource load failure:
Could not find any resources appropriate for the specified culture or the
neutral culture. Make sure "resx_file_name_without_extension.resources" was
correctly embedded or linked into assembly "my_assembly" at compile time, or
that all the satellite assemblies required are loadable and fully signed.
I suspect my problem is...well I don't really know. Maybe I'm not using the right identifier in the ResourceManager constructor. I tried explicitly setting "Excluded From Build: No" and "Content: Yes" in the file's properties, but that had no effect.
Is it even possible to drop an externally-created .resx file into a project and get at the resources within? I definitely need it compiled in; I can't ship a product with dangling .resx files. I can always create a set of static data objects in .cpp files, but the .resx approach seemed more elegant...
Turns out a comment on this unanswered question was the secret sauce. Prepending the root namespace to the identifier made the ResourceManager happy:
ResourceManager ^ image_rm = gcnew ResourceManager(
"my_root_namespace.resx_file_name_without_extension", GetType()->Assembly);
ResourceSet ^ image_rs = image_rm->GetResourceSet(
System::Globalization::CultureInfo::CurrentCulture, true, true);
Just how or why the compiler decided to place the resources within that namespace is still a bit of a mystery to me, but that's trivia for another day.
The question to which I link involves a .resx created with VS within the project, and mine involves adding one created externally, so I think it's a different-enough situation to warrant a separate Q/A.

ResourceLoader in shared assembly

Class Library A
Strings
en-US
Resources.resw
zh-CN
Resources.resw
Application
Views
ViewA.xaml
So I have a View, ViewA that I need to manually load localized strings from. However, whenever I do this, I receive an empty string.
I have tried:
var loader = ResourceLoader.GetForCurrentView();
var myLocalizedText = loader.GetString("Foo");
and
var loader = new ResourceLoader();
var myLocalizedText = loader.GetString("Foo");
The variable myLocalizedText is always an empty string.
I tried to reproduce your issue, however I just got ResourceMap Not Found. exception when using ResourceLoader.GetForCurrentView() or new ResourceLoader().
If we want to construct a resource loader for resources from a library package, we need use ResourceLoader.GetForCurrentView(String) method or ResourceLoader.ResourceLoader(String) constructor. Here the String is the resource identifier of the ResourceMap that the new resource loader uses for unqualified resource references.
Components or library files are typically added into a subfolder of the package they are included in during the build process, similar to their string resources. Their resource identifier usually takes the following form:
ClassLibraryOrAssemblyName/ResourceFileName/StringName
So for your case, we can use following code to load localized strings:
var loader = ResourceLoader.GetForCurrentView("ClassLibraryA/Resources");
var myLocalizedText = loader.GetString("Foo");
For more information, please see: Loading strings from libraries or controls and scenario 6 in Application resources and localization sample.
Besides, please note that for a UWP app, it's better to use GetForCurrentView(String) method:
[ResourceLoader(String) may be altered or unavailable for releases after Windows 8.1. Instead, use GetForCurrentView(String).]
Also if you want to add Chinese language, you'd better use zh-Hans instead of zh-CN:
Include script when there is no suppress script value defined for the language. See the IANA subtag registry for language tag details. For example, use zh-Hant, zh-Hant-TW, or zh-Hans, and not zh-CN or zh-TW.
For more information see: Qualify resources with their language.

ResourceManager not picking up correct resx file

I am having an issue getting the correct resource file entries and need some help ... here is my scenario
I have a series of projects that are a part of a large reporting solution that follow this format
Plugin.****Report
Reference (folder)
DataAccessLayer (folder)
DataSets (folder)
DataWrappers (folder)
Localization (folder)
*.cs
Where * is the name of the report I am going to generate
The *.cs has an export statement so that I can pick it up using MEF (not sure if this is relevant, but thought it worth mentioning). The namespace in *.cs is CompanyName.DataShaper.Plugin.*Report. Due to the project name, and location of the files, I changed the default namespace for each project to be CompanyName.DataShaper.Plugin.*Report (it was just Plugin.*Report before my change).
Now for the problem .. inside of *.cs I am instantiating a ResourceManager. That looks like this ...
_resourceManager =
new ResourceManager("CompanyName.DataShaper.Plugin.*Report.Localization.*Report",
Assembly.GetExecutingAssembly());
inside my resx designer file I have the following ...
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CompanyName.DataShaper.Plugin.*Report.Localization.*Report", typeof(*Report).Assembly);
resourceMan = temp;
}
return resourceMan;
}
My call to the resource manager looks like this ...
string resourceString = _resourceManager.GetString(resourceKey, _currrentCultureInfo);
where _currentCultureInfo is a a correct CultureInfo object.
I have 4 different resx files in my Localization folder for each project, (****Report.resx, ****Report.fr-ca.resx, ****Report.en-gb.resx, ****Report.de-de.resx).
When I make the call to the resource manager, I always get the entry from the .resx ... never any of the other language files, regardless of the CultureInfo object I pass into the call. What have I messed up?
--> Quick update to my original question, but this does appear to be MEF related. I instantiated my class the old fashioned way (new *Report()) and made the localization call and it works fine
OK, I figured this out .. I am getting the .resx file because it is truly embedded into the main assembly. The other files are getting built into separate dlls for each language, I then need to copy them into the same folder that I build my aggregate container from, my resource manager then sees all languages.

How to use ResourceManager in a "website" mode?

I am trying here to do a manual translation for the application I am working with. (There is already a working LocalizationModule but it's working dodgy, so I can't use <asp:Localize /> tags.
Normally with ResourceManager you are supposed to be using it as Namespace.Folder.Resourcename (in an application). Currently I am translating an existing asp.net "website" (not web application so no namespace here....).
The resources are located into a folder name "Locales/resources" which contains "fr-ca.resx" and "en-us.resx".
So I used a code with something like this :
public static string T(string search)
{
System.Resources.ResourceManager resMan = new System.Resources.ResourceManager( "Locales", System.Reflection.Assembly.GetExecutingAssembly(), null );
var text = resMan.GetString(search, System.Threading.Thread.CurrentThread.CurrentCulture);
if (text == null)
return "null";
else if (text == string.Empty)
return "empty";
else
return text;
}
and inside the page I have something like this <%= Locale.T("T_HOME") %>
When I refresh I have this :
Could not find any resources
appropriate for the specified culture
or the neutral culture. Make sure
"Locales.resources" was correctly
embedded or linked into assembly
"App_Code.9yopn1f7" at compile time,
or that all the satellite assemblies
required are loadable and fully
signed. Description: An unhandled
exception occurred during the
execution of the current web request.
Please review the stack trace for more
information about the error and where
it originated in the code.
Exception Details:
System.Resources.MissingManifestResourceException:
Could not find any resources
appropriate for the specified culture
or the neutral culture. Make sure
"Locales.resources" was correctly
embedded or linked into assembly
"App_Code.9yopn1f7" at compile time,
or that all the satellite assemblies
required are loadable and fully
signed.
Source Error:
Line 14:
System.Resources.ResourceManager
resMan = new
System.Resources.ResourceManager(
"Locales",
System.Reflection.Assembly.GetExecutingAssembly(),
null ); Line 15: Line 16: var
text = resMan.GetString(search,
System.Threading.Thread.CurrentThread.CurrentCulture);
Line 17: Line 18: if (text == null)
Source File:
c:\inetpub\vhosts\galerieocarre.com\subdomains\dev\httpdocs\App_Code\Locale.cs
Line: 16
I even tried to load the resource with Locales.fr-ca or only fr-ca nothing quite work here.
Marvin Smit's solution is great if you do not have access to the HTTPContext
const string ASSEMBLY_NAME = "App_GlobalResources";
const string RESOURCE_NAME = "Resources.MetaTagResource";
const string RESOURCE_MANAGER = "ResourceManager";
Assembly assembly = Assembly.Load(ASSEMBLY_NAME);
Type type = assembly.GetType(RESOURCE_NAME);
PropertyInfo propertyInfo = type.GetProperty(RESOURCE_MANAGER);
ResourceManager resourceManager = propertyInfo.GetValue(null, new object[] { }) as ResourceManager;
resourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, true);
But if you have access to the HTTPContext just use HttpContext.GetGlobalResourceObject
string title = HttpContext.GetGlobalResourceObject("MetaTagResource", "Title").ToString();
string keywords = HttpContext.GetGlobalResourceObject("MetaTagResource", "keywords").ToString();
string description = HttpContext.GetGlobalResourceObject("MetaTagResource", "Description").ToString();
Maybe you are just looking for The System.Web.Page's base GetLocalResourceObject(),?
see http://msdn.microsoft.com/en-us/library/ms153597.aspx
When using resources in ASP.Net, you will see a specialized compiler being used (custom Tool property) on the .resx files (ResXCodeGen)
Bottom line of this is that your resources are compiled into a class. The name of this class is the name you should use when you want to address the resources in there.
The name of the class is generated as follows:
{project namespace}{folder structure}{filename}.resx (.resx is NOT part of the generated name)
Since you mentioned your resx file is located in a directory name "Locales/resources", the name you would have to use is (assuming the resx file is called 'locales.resx')
"locales.resources.locales"
The addition of the language and locale, like the "uk-en" is added by the resource manager, loading the appropriate assembly based on the Culture that is specified when creating an instance of the ResourceManager. If none is given, the Thread.CurrentCulture is used. You should not use the language/locale extensions yourself, leave it to the resource manager to deal with that.
If you are not sure how the resources class will end up, you can always look at the MSIL or use reflector to determine the name of the resource class in the actual deployed assembly.
;-- Added after comments where placed -------
I also looked at some other code i had arround;
Have you tried the Reflection on App_GlobalResources approach?
1: You load the "App_GlobalResources" library while in the context of the website (in a HttpHandler, Aspx, Ascx, etc). =>
Assembly.Load("App_GlobalResources");
2: Walk through the available types in there and grab the "ResourceManager" property from each type.=>
PropertyInfo pi = type.GetProperty("ResourceManager");
resManager = pi.GetValue(null, new object[] { }) as ResourceManager;
3: If it had one, get the ResourceSet you want => resManager.GetResourceSet(englishCulture, true, true);
Hope this helps.
this is not c# but you can easily convert it into it
I have in the app_localResources directory with 2 files
myPage.aspx.resx
myPage.aspx.fr-ca.resx
and I use it like this in my .aspx page
<asp:Label ID="lAddress1" runat="server" Text="<%$ Resources: lAddress1 %>"></asp:Label>
this is how I manage it on mine
'using LCID from
Public Enum elang
En = &H1009 'en-CA http://www.microsoft.com/resources/msdn/goglobal/default.mspx?submitted=1009&OS=Windows%202003%20%20Service%20Pack%201
Fr = &HC0C 'fr-CA http://www.microsoft.com/resources/msdn/goglobal/default.mspx?submitted=0C0C&OS=Windows%202003%20%20Service%20Pack%201
End Enum
'strongly typed value in session
Private _lang As elang = elang.En
Public Property lang() As elang
Get
Return _lang
End Get
Set(ByVal value As elang)
_lang = value
End Set
End Property
and in every page I got
Protected Overrides Sub InitializeCulture()
If Not Me.IsPostBack Then
Threading.Thread.CurrentThread.CurrentUICulture = New Globalization.CultureInfo(Sess.lang)
Threading.Thread.CurrentThread.CurrentCulture = Globalization.CultureInfo.CreateSpecificCulture(Threading.Thread.CurrentThread.CurrentUICulture.Name)
End If
MyBase.InitializeCulture()
End Sub
when I want to use the resourcemanager I create a global resources file at the root level the I use it like this
Dim rm = New System.Resources.ResourceManager("Resources.MyPage", Reflection.Assembly.Load("App_GlobalResources"))
Edit
In case of a Web Site project, read this walkthrough of Microsoft.
I'm afraid you can't use the ResourceMagager in a Website project, because it requires a namespace / assembly where the resources are located.

Categories

Resources