.NET Core resource localization at runtime - c#

I am trying to localize a hosted service in response to a runtime condition which is fed in a variable lang, which represents a 2-letter ISO code (such as 'en', 'es', ...).
I set the localization service in my Startup.cs like this:
services.AddLocalization(options => { options.ResourcesPath = "xresx"; });
In my controller I have the following code:
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(lang);
I know this works, because when I pass in lang='es' the following:
var check = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName;
returns the correct value check = 'es'.
But then the next statement:
var msg = Resources.TestMsg
picks up my the value from my English resource file Resource.resx instead of Resource.es.resx.
What am I doing wrong, and how can I make it work?
Thanks!

OK, so what ultimately worked for me was following exactly the steps in this guide: https://joonasw.net/view/aspnet-core-localization-deep-dive
This link is the only source I've found that worked for me, and it was better than Microsoft's own documentation (which omits potential pitfalls like not naming your Resource files a very certain way).
Let me summarize some points:
One needs to inject IStringLocalizer into their controller, eg:
IStringLocalizer<MyController> _localizer;
Then inside the controller you can localize your strings, eg:
_localizer["STRINGKEY"]
where STRINGKEY is a key from your resource file.
Make sure to name your resource files properly. This is very important and is not documented by Microsoft as far as I know! Cost me a lot of time until I stumbled over the web link I've referenced above.
I was naming my files like this:
Resource.resx, Resource.es.resx etc
and the localized wasn't finding the values, instead just returning the key itself.
Eg, _localizer["STRINGKEY]" would return "STRINGKEY" rather than the corresponding value in the resource file.
So you must name your files instead using your Controller's name, like this:
Controllers.MyController.resx, Controllers.MyController.es.resx
These are the main points to remember. Sadly, Microsoft documentation glosses over a lot of this stuff.

Related

What's the easiest way to create a managed visualiser in C#?

I have a background in C++ and recently I started working in C#.
I have written following pieces of code (in Visual Studio):
var list_Loads = database.GetData<Load>().ToList();
var test_list = list_Loads.Where(o => (o.Name.Substring(0, 3) == "123")).ToList();
When I run the program and I move my mouse over both lists, first I get the count, which is very useful, but when I ask for the entries, this is what I get:
0 : namespace.Load
1 : namespace.Load
2 : namespace.Load
...
Not very useful, as you can imagine :-)
So my question: how can I show the Name attributes of those objects?
I thought: no problem. I have a background in native visualisers, so it should be rather easy to turn this into useful information, but then it comes:
In order to alter the way that those objects are represented, there is the first proposal to add a [DebuggerDisplay] "tag" to the definition of that class in source code.
However, as those classes are part of a framework I'm just referring to, I don't have access to the source code and hence I can't modify this.
Then I found another solution, which comes down to: "Write an entire C# project, debug, test and install it and it might work" (see documentation on "Custom visualisers of data" on the Microsoft website).
I almost choked in my coffee: writing an entire project, just for altering the view of an object??? (While, in C++, you just create a simple .natvis file, mention the classname and some configuration, launch .nvload and that's it.
Does anybody know a simple way to alter the appearance of C# object, without needing to pass through the whole burden of creating an entire C# project?
By the way, when I try to load a natvis file in Visual Studio immediate window, this is what I get:
.nvload "C:\Temp_Folder\test.natvis"
error CS1525: Invalid expression term '.'
What am I doing wrong?
Thanks in advance
OP (my emphasis):
In order to alter the way that those objects are represented, there is the first proposal to add a [DebuggerDisplay] "tag" to the definition of that class in source code.
However, as those classes are part of a framework I'm just referring to, I don't have access to the source code and hence I can't modify this.
Does anybody know a simple way to alter the appearance of C# object, without needing to pass through the whole burden of creating an entire C# project?
If you just want to specify [DebuggerDisplay] on a type, you don't have to have access to the source code. You can make use of [assembly:DebuggerDisplay()] and control how a type appears in the debugger. The only downside is that [assembly:DebuggerDisplay()] naturally only affects the current assembly whose code your mouse is hovering over. If you wish to use the customised display in other assemblies that you own, then you must repeat the [assembly:DebuggerDisplay()] definition.
Here's an easy before-and-after example with DateTime. I picked DateTime because we generally don't have access to the source code and it has some interesting properties:
var items = new List<DateTime>
{
DateTime.Now.AddDays(-2),
DateTime.Now.AddDays(-1),
DateTime.Now
};
...which on my machine defaults to:
Maybe I'm fussy and I just want to see:
Day of the week and
Day of the year
...I can do that via:
using System.Diagnostics;
[assembly: DebuggerDisplay("{DayOfWeek} {DayOfYear}", Target = typeof(DateTime))]
...which results in:
Example:
namespace DebuggerDisplayTests
{
public class DebuggerDisplayTests
{
public DebuggerDisplayTests()
{
var items = new List<DateTime>
{
DateTime.Now.AddDays(-2),
DateTime.Now.AddDays(-1),
DateTime.Now
};
}
}
.
.
.
}
Overrides
[assembly:DebuggerDisplay()] can also be used as a means to override pre-existing [DebuggerDisplay] on a 3-rd party type. Don't like what style they have chosen? Is the type showing far too much information? Change it with [assembly:DebuggerDisplay()].

Language component in URL of default Identity UI for asp.net 6

I have an application which I must port to asp.net 6. I try to implement the authentication logic with scaffolded default identity UI pages.
The application uses URLs which start with a path component which holds the user’s language, then followed by the concrete path components. Something like:
/{language}/product/{product}
Now I try to establish this url schema also with the asp.net identity default UI pages. For example, the login page url should be look something like this:
/en/login
/fr/login
/it/login
However, up to now I had only little success in doing so. In changing the #page directive in the scaffolded pages I was able to introduce the {language} path component. However, how do I now tell the cookie middleware to integrate the current {language} placeholder into the redirect? Something like this:
builder.Services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/{language}/Login";
});
Is this feasible in some way or another or is there even a more solid way to accomplish the goal?
Update
Up to now I came up with a solution and posted it as an answer, since it works. However if anybody knows a more sophisticated approach, please post it, I feel that the way I did this is really ugly and I cannot believe that there is no cleaner way to accomplish this, since also Microsoft uses the Url schema I try to implement in their websites.
One possibility I've found is to use the CookieAuthenticationOptions.Events-instance. This seems a feasible way, however it seems to me extremely brutish and one has to register to every event which is concerned and every scaffolded page has to be changed (redirects etcetera).
However, as long as no other solution is provided, this may help someone:
builder.Services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/language/Login";
options.Events.OnRedirectLogin=>(context){
// here would stand some Ajax-checks ...
var myLanguage= ... // detection of the language from the query string
context.Response.Redirect(context.RedirectUri.Replace("/language/", myLanguage));
}
});
The above code is best refactored in a new class which derives from CookieAutenticationEvents, where then proper handling can be done for each event. So the only assigment in the Program.cs file is the assignment of the custom authentication events class. The original class seems a bit quirky and depending on the purpose of the derived class, it is either better to initially assign custom events while constructing the instance or to override the methods which raise the assigned event handlers.
Within the pages, the language can be declared via the page directive:
#page "/{language}/login"
#page "/{language}/loginWith2fa"
etcetera
The code behind then will be changed, for example the signature of the get and set method will be extended with the language-parameter.
public async Task<IActionResult> OnGetAsync(string language,bool rememberMe, string returnUrl = null) {
and additionally, any url references in the code behinds must be changed. Since this way is so ugly, I tried to completely abstain from the default identity ui. In this post I ask for a way to do so.

How can I get the comments for symbols referenced by a CompletionItem?

Coming from this question, I've managed to get all CompletionItem instances available for a specific offset using completionService.GetCompletionsAsync(document, offset);.
So, after querying for completions of "MyString".Len, I get a CompletionItem for the Length method and can then, using the CompletionService, call service.GetDescriptionAsync(document, completionItem) to retrieve "int string.Length { get; }".
But, how can I get the comments for Length, e.g. "Gets the number of characters in the current String object."? And, if easily possible, other information regarding potential overloads?
Assuming that you're adding references to the assemblies using
MetadataReference.CreateFromFile method, you should pass an DocumentationProvider instance as an additional parameter, like this:
MetadataReference.CreateFromFile(path, MetadataReferenceProperties.Assembly, new MyDocumentationProvider(path));
DocumentationProvider is an abstract class, we ended up implementing our own by overriding GetDocumentationForSymbol method and locating appropriate XML node inside XML document.
Looking at Roslyn source code, there is XmlDocumentationProvider class which has an abstract method GetSourceStream (where you're supposed to pass a content of .xml file that stores documentation for .NET assemblies).
Please note that for this feature to work there should be an .xml file with descriptions file next to the assembly (which is normally produced from the source code when you have compile an assembly with Documentation File option set).
For .NET assemblies these files are included as part of SDK, and normally can be found at:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\vxxx
We use this approach in our Roslyn-based parsers in our AlterNET Studio product. You may find a bit more information about these parsers here.
GetDescriptionAsync can only return a number of overloads, to get list of overloads available at the same position you might need to use Recommender API like this:
var model = document.GetSemanticModelAsync().Result
var symbols = Microsoft.CodeAnalysis.Recommendations.Recommender.GetRecommendedSymbolsAtPositionAsync(model, pos, workspace).Result;
This API will return a separate symbol for every overload.
We asked a while ago whether it's possible to retrieve additional information (such as underlying symbol) from CompletionItem and the short answer is no. You may refer to the discussion here:
https://github.com/dotnet/roslyn/issues/57677

How to turn a resx resource file into a dictionary

I'm currently working on a dll which would be used across future projects, and trying to fill it with every useful method developped in the last one. One of these methods that i'd like to re-use allows to generate an excel file with any object collection, and uses a resx resource file to get the column headers based on the object's property names.
Now, my problem is that I can't access or even check the existence of such a file from the library. A possible workaround would be to turn this resource file into a dictionary and pass it as a parameter, but I didn't find a way to do so or any documentation on the subject. Any hint or suggestion about this ?
Thank you for helping me here...
P.S. : I'm working with Visual Studio 2013, in case that could be relevant.
Here's a workaround I found and which allows to get a "RuntimeResourceSet" object, which is structurally close to a dictionnary (and which could be easily turned into one, if you specifically need a dictionnary) :
var myResourceSet = MyResource.ResourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
Then, you can get the set's items this way :
var myString = myResourceSet.GetString("MyKey");
Hope that will help !
I am using 2 separators, and I re-create the dictionary by using split:
Dictionary<string, string> MyDictionary = Resource.MyResourceNme.Split(',').ToDictionary(x => x.Split('|')[0], x => x.Split('|')[1]);
MyResource would look like:
AUD|SYD,GBP|LON,JPY|TOK
Maybe there is a more efficient way of doing it (I don't like using twice the second split).

ASP.NET MVC 4 switch resource file via url parameter

I am new at working with resource files and I haven't quite got how it works yet. Now I need to have my application's text available in English and in Chinese. I will receive a get parameter (e.g. lang) and from there I will need to decide whether to use Language.zh.resx or my default Language.resx - That's what I understood from articles that I have been reading. Now I have my View Title for example:
#{
ViewBag.Title = MyApplication.App_GlobalResources.Language.MyPage_Title;
}
I can't figure out where to check the parameter lang and apply it. I saw articles where people say I should create an action filter and they add stuff to cookies and they were confusing. In my case it might not be necessary as it just has one request, there is no requirement for preserving the state as once the page is loaded that's it.
If someone could also give some brief explanation of how resources work that would be nice, thanks!
There are a couple of articles that discuss Globalization and MVC using both session and URL variables. I am linking both because the session one covers Views a little more in deoth, but the logic for views should stay the same regardless if you are routing (/en-us/Controller) or using a session.
Session based Globalization
Routing Based Globalization
lets say you have a string lang in your controller, in the controller you should change the current culture, so the only thing you need to do is is something like this
var culture = new CultureInfo(lang);
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
i suggests you to do this steps via some attribute, and then apply it on controllers.

Categories