I have a microservice in .Net-Core that have to handle some resources in a resx file and return them based on the culture I provide with a call to an API so I will not use the culture of the current thread, but when I call the method GetString(key, culture) it always return the default language.
I have 2 resx file at the moment: resource.resx and resource.it-IT.resx if i call the api with the it-IT culture string I always get the translation in the resource.resx file and not in the resource.it-IT.resx file
The resx files are in another project called Localization
I have a generic method where I pass the Enum I have to localize and the type of the file where the localization is stored, then I compose the key of the resource and call the GetString method.
I have also tried changing the culture of the current thread with
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName);
public static string GetDisplayName(this Enum e, Type resourceType, string cultureName)
{
var rm = new ResourceManager(resourceType);
var resourceDisplayName = rm.GetString(e.GetType().Name + "_" + e, CultureInfo.CreateSpecificCulture(cultureName));
return string.IsNullOrWhiteSpace(resourceDisplayName) ? string.Format("[[{0}]]", e) : resourceDisplayName;
}
I have investigated a bit more and in the resource manager when I inspect it I have 3 resourceSet
resource
resource.it
resource.it-IT
If I inspect inside these 3 resource set I have all my resource always in English it seems that the resource manager doesn't load the resx italian file
About your resource.resx and resource.it-IT.resx files, please check if your key is the same in both the files. If the key you are trying to access is not available, the code automatically redirects to default file (resource.resx).
This link will help complete implementation of Localization and Globalization
After reading this: NetCore Bug I managed to solve my problem, first of all I have refactored my method to this:
public static string GetDisplayName(this Enum e, Type resourceType, string cultureName)
{
var rm = new ResourceManager(resourceType.FullName, resourceType.Assembly);
var key = $"{e.GetType().Name}_{e}";
var culture = CultureInfo.CreateSpecificCulture(cultureName);
var resourceDisplayName = rm.GetString(key, culture);
return string.IsNullOrWhiteSpace(resourceDisplayName) ? string.Format("[[{0}]]", e) : resourceDisplayName;
}
Then I have removed the reference to the Localization project from the API project and only left that reference in another project that is then referenced from the API project
Related
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
I have added a resource file which needs to support globalization.
In Global.asax I received the culture info.
protected void Application_BeginRequest()
{
var cul = Context.Request.Headers["culture"];
if (cul != null && !string.IsNullOrEmpty(cul))
{
var culture = new CultureInfo(cul);
//Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
}
}
Now I'm trying to access it as:
ResourceManager rm = new ResourceManager("Resource", System.Reflection.Assembly.GetExecutingAssembly());
string err = rm.GetString("ERROR_1");
The error I get is:
{"Could not find any resources appropriate for the specified culture or the neutral culture. Make sure \"Resource.resources\" was correctly embedded or linked into assembly \"...\" at compile time, or that all the satellite assemblies required are loadable and fully signed."} System.SystemException {System.Resources.MissingManifestResourceException}
The code and resource has same namespace.
I don't have much experience with the ResourceManager, but from the official documentation I don't think that using reflection to get the assembly is intended. It specifically mentions that the second parameter, the assembly, should be 'the assembly in which the default .resources file resides'. It later talks about setting Thread.CurrentThread.CurrentUICulture. You can find all that information on msdn: https://msdn.microsoft.com/en-us/library/system.resources.resourcemanager(v=vs.110).aspx
However, you might want to take a look at the msdn documentation on localizing asp.net web pages: https://msdn.microsoft.com/en-us/library/ms227427.aspx. It seems to have some specialized mechanisms that might be very useful to you.
When we want to make our application for all users(speak different languages),We need a global technology.
In C# we use ResourceManager as follow:
using System;
using System.Reflection;
using System.Resources;
public class Example
{
public static void Main()
{
// Retrieve the resource.
ResourceManager rm = new ResourceManager("ExampleResources" ,
typeof(Example).Assembly);
string greeting = rm.GetString("Greeting");
Console.Write("Enter your name: ");
string name = Console.ReadLine();
Console.WriteLine("{0} {1}!", greeting, name);
}
}
// The example produces output similar to the following:
// Enter your name: John
// Hello John!
The assembly have two or more language resources:
Assembly
|--Assembly.en-us.resx
|--Assembly.zh-cn.resx
Then we archive to change the resource by changing thread cultureinfo to use different resource.
If the application have a lot of dll(assembly) files.
I want to have a single point(one resource file for one language) for the application,
Is there a good solution for my idea?
Before I just change the View(eg.Winform or UserControl)'s Language to implement different UI for corresponding language.
Just build the internationalization in C# using the way you described. But as last step of the build process, you can run Fody.Costura.
This will take all the different dlls and pack them into your application so that you only have a single .exe file with everything included.
The benefit is that you can use the C# internationalization frameworks as intended without any hacks, but you still get a single exe you can deliver to your customers.
I find the C# internationalization frameworks very lacking, so I normally
make one assembly for resources and reference from the other projects. The resource files I generate from some tool (DB, excel, textfile) and keep both the source data and the resource files under version control.
MyApp.sln
ResourceProject.csproj
Resources.resx
Resources.ru.resx
Resources.de.resx
Resource.cs
Core.csproj
UI.csproj
The resource class can load all the different assemblies
namespace MyApp.Resources
{
public static class Resource
{
private static ResourceManager manager;
static Resource()
{
manager = new ResourceManager("MyApp.Resources", Assembly.GetAssembly(typeof(Resource)));
}
public static string GetString(string key, string culture)
{
return GetString(key, new CultureInfo(culture));
}
public static string GetString(string key, CultureInfo culture)
{
return manager.GetString(key, culture);
}
}
}
This simple class can be extended in various ways. In the calling assemblies you can have utility classes that will call based on the current UI culture or thread culture depending on the situation.
Note that this completely sidesteps any built-in WinForms or WPF i18N methods.
For GUI:s you can make a utility that recursively translates whole forms. The lookup itself can/should be extended with warnings for missing keys, fallback arguments, prefixes/namespaces if you have thousands of keys and so on.
I know it is possible to store localized versions of resources to be retrieved by the application.
I would like to use a similar principle and to store different versions of data into a resource file/assembly, then use the version as the key to retrieve this data. Is this possible? Is this a common technique?
The data I will be storing is UI Control data identifications.
So for example I would have multiple resources available per version:
Version 1.0 btnID = "thisButton1"
Version 2.0 btnID = "thisButton2"
The application would determine automatically which resource to pick up based on the version currently being used.
You can create a custom class that will wrap the access to the resources and load the correct resource file internally. Assuming you have chosen to name your resource file Resources.1.0.0.0.resources, you can do the following:
public class ResourceReader
{
private static ResourceManager _resourceManager = new ResourceManager(
"Resources." + System.Reflection.Assembly
.GetExecutingAssembly()
.GetName()
.Version.ToString(),
Assembly.GetExecutingAssembly());
// If you have resource property btnID, you can expose it like this:
public static string BtnID
{
get
{
return _resourceManager.GetString("btnID");
}
}
// you can add other properties for every value in the resource file.
}
All you need to do is to know the exact version of your application and to ensure such resource file exists. This can be cumbersome if you have enabled automatic versioning in the assembly info (by using 1.0.* for instance). Of course, you may choose not to use the entire version, but only the major or major and minor version numbers.
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.