I have an ASP .NET 5 RC1 website to which I am trying to add localization.
Based on the information found I did the following
In the ConfigureService in Startup.cs:
Enable Localization and setting the ResourcePath to "Resources"
Enable View Localization and Enable Data Annotations Localization
//check http://damienbod.com/2015/10/21/asp-net-5-mvc-6-localization/
services.AddLocalization(options => options.ResourcesPath = "Resources");
// Add MVC services to the services container.
//check http://blogs.msdn.com/b/webdev/archive/2015/10/15/announcing-availability-of-asp-net-5-beta8.aspx
services.AddMvc().AddViewLocalization().AddDataAnnotationsLocalization();
In the Configure method fin Startup.cs
Setup the list of supported cultures
Enable Request Localization
//check http://www.jerriepelser.com/blog/setting-thread-culture-aspnet5
//check http://damienbod.com/2015/10/21/asp-net-5-mvc-6-localization/
List<CultureInfo> supportedCultures = new List<CultureInfo>()
{
new CultureInfo("en"),
new CultureInfo("es")
};
var requestLocalizationOptions = new RequestLocalizationOptions()
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
app.UseRequestLocalization(requestLocalizationOptions, new RequestCulture(new CultureInfo("es")));
Create a Resources folder under the project
Create the Resources for the Controller. With the convention {Project}.{Controllers}.{ControllerClassName}.{culture}.resx
Create the Resources for the Views. With the convention Views.{ViewFolder}.{ViewName}.cshtml.{culture}.resx
Use the IHtmlLocalizer in the controller, and access the item. In this case localizer["Title"], which is found and works just fine. However when the culture is set to "es" it is not found and just falls back to the default resource.
private IHtmlLocalizer<HomeController> _htmlLocalizer;
public HomeController(IOptions<PTIWebPortal.Configuration.PTIWebPortalConfiguration> pConfiguration,
ILoggerFactory factory, IHtmlLocalizer<HomeController> localizer) : base(pConfiguration, factory)
{
this._htmlLocalizer = localizer;
}
The same is happens for the views, it only works with the default resource, but not with the others.
Any ideas on how to fix it?
There are a lot of known issues of things not working for localization as of rc1.
Some of the known issues are tooling related, so some of the localization things work if you run the app from the command line instead of launching from visual studio.
But even from the command line a lot of things that were supposed to work do not work.
There has been quite a bit of work after rc1 and a lot of localization issues have been fixed and closed recently, so things should be much better after rc2 is released sometime in February.
Related
I'm having trouble configuring localization in my asp.net 7.0 MVC project.
Configuration:
.AddLocalization(opts => opts.ResourcesPath = "Resources")
then
CultureInfo[] supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("bg-BG")
};
mvcBuilder
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
mvcBuilder
.Services
.Configure<RequestLocalizationOptions>(options =>
{
options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
This is called before
.AddRazorPages();
And at the end
app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
I have installed Microsoft.Extensions.Localization nuget.
I have two resource files in folder Resources
Controllers.HomeController.bg-BG.resx
Controllers.HomeController.en-US.resx
In both resources there is entry "title" with some values
Injected IStringLocalizer into HomeController but everytime it returns only "title", used it like this:
this.stringLocalizer["title"].Value
After hours of trial and error I really can't seem to find what's the problem.
You appear to be mixing up two different localization techniques. The .resx resource files is the classic .NET technique for localization while the IStringLocalizer based approach has been added in .NET Core. Fortunately, it is still perfectly fine to utilize .resx resources .NET Core Views/Pages.
Add a using statement at the top of your view. Make sure that the namespace matches the actual C# namespace of your resource. The example below assumes that the default namespace of the project is WebApp and the .resx files live inside a Resources folder:
#using WebApp.Resources
Referencing a resource string inside your view is straight forward:
<h2>#Controllers_HomeController.title</h2>
NOTE: The underscore in Controllers_HomeController is caused your use of a . in the resource filename, which would cause problems with the strongly typed generated class inside the corresponding Controllers_HomeController.Designer.cs
The problem was something to do with the namespaces, changed the default namespace and it works now.
I'm developing a cross-platform (win/mac/linux) application and I'm looking to store my application state. What is the .NET Core recommended way of doing this?
I've dug through the documentation and the closest thing I found was this, which is aimed at Visual Studio/.NET Framework (and I'm on VS Code).
There are 3 ways,
ONLY For Localhost
Simply stash them in your appsettings.json or as environment
variables.
For Staging / Production,
Azure Key Vault
By utilising Azure Key Vault and the Microsoft.Extensions.Configuration.AzureKeyVault NuGet Package, you will be able to stash configurations for your projects in the best way possible in the actual environment.
You then simply inject it in,
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var root = config.Build();
config.AddAzureKeyVault(
$”https://{root["KeyVault:Vault"]}.vault.azure.net/",
root[“KeyVault:ClientId”],
root[“KeyVault:ClientSecret”]);
})
.UseStartup<Startup>();
Although you still have to stash those 3 variables in, Azure has Azure AD to enforce access only to specified Applications. Thus, you need to register an application under the Azure AD in order for this to work. There are also restrictive features that will help you sandbox Azure Key Vault further.
Existing Vault Storages
Last but not least, the last way is to utilise existing vault storage options like HashiCorp. Libraries like https://github.com/rajanadar/VaultSharp will help you to implement it quickly and effectively. This is suitable for you if you primarily use a non-Azure provider for your servers.
As described here, you can use appsettings.json, which is generated and referenced within the new project template in dotnet new command
You can use the ConfigurationBuilder from the Microsoft.Extensions.Configuration nuget package
Although the docs are for ASP Core, you should be able to use them in your regular .Net Core app.
Create settings.json:
{
"mysetting": "value",
}
And use it:
var configuration = new ConfigurationBuilder()
.AddJsonFile("settings.json")
.Build();
// get the values from the settings.json
var mySetting = configuration["mysetting"];
Console.WriteLine(mySetting);
I have a Class Library that I'm converting to a .Net Standard 2 class library in order to also use in ASP.Net Core 2.0 projects.
The library has always read from a config file items such as SMTP settings, connection strings etc.
In Web Projects it finds these values in web.config.
In Console/WinForms it finds these values in app.config.
Is there an equivalent config file for .Net Core 2.0 projects that "just works" like the previous examples?
I assume the answer is no, but looking for best way to handle this given the library is used across the organization, so maintaining backwards compatibility is important.
Turns out System.Configuration.ConfigurationManager was added back in .NETStandard 2.0.
Just pull it in from nuget and compile the .NETStandard 2.0 class library project.
Then, the library will work across projects using standard config files:
Net Core 2.0 projects use app.config
Web projects work from web.config
Console and Windows apps work with app.config
.Net Core revised configuration approach greatly.
You don't call ConfigurationManager.AppSettings["someSetting"] anymore whenever you need value for some setting. Instead you load configuration on application startup with ConfigurationBuilder. There could be multiple configuration sources (json or/and xml configuration file, environment variables, command line, Azure Key Vault, ...).
Then you build your configuration and pass strongly typed setting objects wrapped into IOption<T> to consuming classes.
Here is a basic idea of how it works:
// Application boostrapping
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("AppSettings.json");
var configuration = configurationBuilder.Build();
// IServiceCollection services
services.AddOptions();
services.Configure<SomeSettings>(configuration.GetSection("SomeSection"));
// Strongly typed settings
public class SomeSettings
{
public string SomeHost { get; set; }
public int SomePort { get; set; }
}
// Settings consumer
public class SomeClient : ISomeClient
{
public SomeClient(IOptions<SomeSettings> someSettings)
{
var host = someSettings.Value.SomeHost;
var port = someSettings.Value.SomePort;
}
}
// AppSettings.json
{
"SomeSection": {
"SomeHost": "localhost",
"SomePort": 25
}
}
For more details check article Configure an ASP.NET Core App.
I'm afraid that it will be difficult (trying to avoid word 'impossible') to maintain backward compatibility.
Locally my project runs fine but when I deploy on Azure using a web app, I get the following error when it starts:
MissingMethodException: Method not found: 'Newtonsoft.Json.JsonSerializerSettings Microsoft.AspNet.Mvc.Formatters.JsonOutputFormatter.get_SerializerSettings()'.
SmartAdmin.Startup.<>c.b__13_7(MvcOptions options)
I've tried this:
services.AddMvc(options =>
{
options.Filters.Add(new UserPreferencesLoaderAtrribute());
var jsonFormatter = (JsonOutputFormatter)options.OutputFormatters.FirstOrDefault(f => f is JsonOutputFormatter);
if (jsonFormatter != null)
{
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
});
And this:
services.AddMvc(options =>
{
options.Filters.Add(new UserPreferencesLoaderAtrribute());
}).AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
Yes, I just worked all night and did eventually figured it out. Here is what you need to do:
Make sure you install:
-Microsoft.AspNet.Mvc.Formatters.Json version "6.0.0-rc1-final"
and
-Revert Netonsoft.Json to "6.0.6".
Then you can keep this:
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
project.json:
"Microsoft.AspNet.Mvc.Formatters.Json": "6.0.0-rc1-final",
"Newtonsoft.Json": "6.0.6"
I had a bunch of trouble redeploying too but eventually this worked.
Good luck!
Just got off a call with Microsoft support as of yesterday (02 Aug 2016) Azure App Services now only support ASP.NET core, due to a breaking change:
A breaking change was released and anything other than ASP.NET core is not supported, so the only option is an upgrade. The breaking change is being rolled out to all (regions) eventually all your instances will fail.
Is ASP.NET 5, Core RC1, RC2 supported on Azure App Service? NO
https://blogs.msdn.microsoft.com/waws/2016/06/14/supporting-asp-net-5-core-rc1-on-azure-app-service/
Verify your app is running the latest version of ASP.NET Core and not RC1 or RC2.
We were affected (North Europe) and upgraded our app from RC2 and it worked first time.
We also saw this in production, contacted the team and got this out: https://social.msdn.microsoft.com/Forums/en-US/f0a6bbaf-498a-4c1f-b869-6779ee18e04e/app-service-applications-may-experience-methodnotfound-exceptions-due-to-incorrect-newtonsoft-json?forum=windowsazurewebsitespreview
It appears that a fix for App Service is on its way as well. Meanwhile, the linked post contains pretty much the same instructions as the other answers here.
I have the requirement that the end-user can change localized resources and the changes should be visible in the application without the need to restart the application.
Update to clarify the scenario:
I am talking about changing the localized resources at runtime. Lets say I have a typo in the german translation of a page. Then some admin-user should have the possibility to change that typo at runtime. There should be no need for a redeployment or restart in order for this change to be reflected in the UI.
I am using ASP.NET MVC3.
What options do I have?
I have been looking into writing a custom ResourceProvider that loads resources from the database.
This seems not too much effort, however so far I pointed out two drawbacks:
It is not working with the DataAnnotations that are used for convenient validation in MVC3 (DataAnnotations work with a ErrorMessageResourceType parameter, which only works with compiled resources)
We basically have to provide our own tooling around managing resources (like translating etc.) which is a pity, since there are a lot of tools for this that work with resx-files.
What are the other options? Would manipulation of the deployed resx-files at runtime be an option?
But I suspect that the application is automatically "restarted" when it detects those changes: I suspect ASP.NET realizes that the resx-files have changed, it then recycles the application-pool and compiles the new resx-files on the fly.
Is this correct? Is there any way around this?
I have not yet looked into compiling the resources into satellite assemblies before deployment. Is this even a recommended scenario for web applications?
But even with compiled satellite assemblies I suspect that ASP.NET restarts the application, when those assemblies are changed on the fly. Is this correct?
I would be interested in any experience in how the original requirement can be satisfied?
And I would be interested in any comments about the options I have mentioned above.
DataAnnotations accept a ErrorMessageResourceType which tells the ValidationAttrributes where to access resources. You can pass this as follows:
[Required(
ErrorMessageResourceType = typeof(DynamicResources),
ErrorMessageResourceName = "ResourceKey")]
public string Username { get; set; }
By creating a type for this parameter with static properties for each key you can create an implementation that loads resources from a database or other implementation. You could then combine this with a dynamic object for DRY and move the implementation into TryGetMember. Potentially then use T4 templates to generate the statics from your database at compile time, ending up with this:
public class DynamicResources : DynamicObject
{
// move these into partial and generate using T4
public static string MyResource
{
get { return Singleton.MyResource; }
}
public static string MyOtherResource
{
get { return Singleton.MyOtherResource; }
}
// base implementation to retrieve resources
private static dynamic singleton;
private static dynamic Singleton
{
get { return singleton ?? (singleton = new DynamicResources()); }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
// some logic here to look up resources
result = GetResourceKeyFromDatabase(binder.Name);
return true;
}
}
Of course it would be perfect if resources weren't static properties.