问题
In the following program, in order to get resource strings i am using _localizer["About Title"] where "About Title" is a magic string. How to avoid using strings like this? Is there any strongly typed way?
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
namespace Localization.StarterWeb.Controllers
{
[Route("api/[controller]")]
public class AboutController : Controller
{
private readonly IStringLocalizer<AboutController> _localizer;
public AboutController(IStringLocalizer<AboutController> localizer)
{
_localizer = localizer;
}
[HttpGet]
public string Get()
{
return _localizer["About Title"];
}
}
}
回答1:
If you're trying to avoid using a hardcoded string (key) to lookup the localization conversion you could create a LocalizationKeys
class that contains the lookup keys for you. You could then leverage the C# 6 nameof
operator. This would help alleviate the concern of "magic strings".
public static class LocalizationKeys
{
public const string AboutTitle = nameof(AboutTitle); // Note: this is "AboutTitle"
// ... other keys here
}
Then you could consume it wherever. One of the benefits are that since this is a member of the class, if the key changes you can use common refactoring tools to safely replace all the references to it rather than trying to do a global string replace on the "magic string". Another benefit is you'd get to use intellisense when accessing the class. I suppose one could consider this "strongly typed".
You'd consume it as such:
[Route("api/[controller]")]
public class AboutController : Controller
{
private readonly IStringLocalizer<AboutController> _localizer;
public AboutController(IStringLocalizer<AboutController> localizer)
{
_localizer = localizer;
}
[HttpGet]
public string Get()
{
return _localizer[LocalizationKeys.AboutTitle];
}
}
If you really want to get fancy with C# 6 you can also utilize a static using. This will allow you to reference the members of the type you specify. Finally, for simple "single line" returns, we can make them expression bodies. For example:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using static Localization.StarterWeb.LocalizationKeys; // Note: static keyword
namespace Localization.StarterWeb.Controllers
{
[Route("api/[controller]")]
public class AboutController : Controller
{
private readonly IStringLocalizer<AboutController> _localizer;
public AboutController(IStringLocalizer<AboutController> localizer)
{
_localizer = localizer;
}
[HttpGet]
public string Get() => _localizer[AboutTitle]; // Note: omission of qualifier
}
}
回答2:
Create a Resource file (.resx) with your translations. For example if you're localizing
AboutController
that would be something likeAboutController.String.resx
.Enable code generation for your resource file by changing Access Modifier from "No code generation" to "Public" or "Internal" while on edit resource screen. On save this will create a
.Designer.cs
class for your resource file. It will contain a static property for each key in the resource file. Don't modify the generated class manually. It'll be regenerated automatically after each modification of the.resx
.
Use the generated designer class in your controller to get translated text (no need for string localizer in that case):
[HttpGet] public string Get() { return AboutController_Strings.AboutTitle; }
This will work with
using static
trick as well:using static Localization.StarterWeb.AboutController_Strings; //(...) [HttpGet] public string Get() { return AboutTitle; }
Alternatively you can use it with ASP's localizers. This adds no value at in that particular case but can be useful with the
IHtmlLocalizer
as it'll html-escape the values for you.[HttpGet] public string Get() { return _localizer[nameof(AboutTitle)]; }
Why is this better than the accepted answer? This way you do not need to manually create and maintain all of LocalizationKeys
-like classes with number of const strings inside. (In bigger projects there will be hundreds or thousands of them!) The generated .Designer.cs
class will contain all keys from your resource file. Nothing more. Nothing less. If you delete something from the resource fill it will be deleted from the generated code and produce compilation errors in your code wherever the deleted property was used. If you add something to the resource file it will automatically generate a new property on save of the resource file which will appear in code-completion.
This is a more traditional approach, the way it was originally done with with for example WinForms. In the Microsoft's documentation for localizing ASP.NET Core apps there a brief explanation why they added a new way using IStringLocalizer
: hardcoding translation string makes your workflow faster. There's no need to maintain additional things until needed.
For many developers the new workflow of not having a default language .resx file and simply wrapping the string literals can reduce the overhead of localizing an app. Other developers will prefer the traditional work flow as it can make it easier to work with longer string literals and make it easier to update localized strings.
来源:https://stackoverflow.com/questions/41976455/how-to-get-resource-strings-in-strongly-typed-way-in-asp-net-core