Overview#
Beta feature#
Coverage is not 100%. Help output and common use cases have been prioritized. Configuration errors for developers are not included at this time as it's assumed the app will be tested before being given to users who wouldn't be able to act on the errors anyway. We will accept PR's if this is important to you.
Submit feedback via GitHub Issues, GitHub Discussions or
We will also need help with translations and will accept PRs for those translations.
TLDR, How to enable#
Enable the feature by setting appSettings.Localize
to a Func<string,string?>
such as text => stringLocalizer[text]
.
Every package that supports localization will detect this during registration.
static int Main(string[] args)
{
IStringLocalizer localizer = ConfigureLocalizer();
var settings = new AppSettings{ Localization = t => localizer[t] };
return new AppRunner<ValidationApp>(settings).Run(args);
}
Advanced cases#
To use a different Func<string,string?>
per package, supply the appropriate ResourcesProxy
in the AppRunner
constructor and in the registration for each package.
static int Main(string[] args)
{
IDictionary<string,IStringLocalizer> localizers = ConfigureLocalizers();
var proxy = new ResourcesProxy{ Localization = t => localizers["core"][t] };
return new AppRunner<ValidationApp>(settings,
new ResourcesProxy{ Localization = t => localizers["core"][t] })
.UseDataAnnotationValidations(args,
new DataAnnotations.ResourcesProxy{ Localization = t => localizers["validation"][t] })
.Run(args);
}
To use a more custom approach, override the appropriate Resources
class in each package and supply them during registration.
class MyResources : Resources
{
public override string Error_File_not_found(string fullPath) => $"missing file: {fullPath}";
...
}
static int Main(string[] args)
{
return new AppRunner<ValidationApp>(settings, new MyResources()).Run(args);
}
Implementation#
Goals#
- provide localization
- easy to use
- extensible
- avoid the cost of translation lookups when not needed
- common entries available for reuse in other middleware
- easy to identify new entries
- fallback to default when localized values are not found
Resources and ResourcesProxy#
Every package that contains localizable content will contain, in the root namespace, a Resources class and a ResourcesProxy that takes a Func<string,string>
. Each package will have it's own implementation of Resources
and ResourcesProxy
. For example, DataAnnotations and FluentValidation have their own versions.
As seen in the examples above, the IStringLocalizer indexer can be used for Func<string,string?>
, making this approach extensible without taking a dependency on localization extensions.
We chose to use a class instead of interface for Resources
to avoid every addition being a breaking change. Localization should not cause your app to fail and should not force you to recompile code for every update. We've provided tooling to help you identify when new resources are added via unit testing.
Testing#
See Localization testing to see how we ensure proxies include every member and how you can generate and test your own.
See the culture directive for a way to set the culture for a command. Enable for unit tests and manual testing.
Generating Resource Files#
There are examples in ResourceGenerators
Basic resx and json translations files can be found in the localization_files folder.
Warning
Some keys differ only in case, eg. Arguments
& arguments
, which is not supported by the VS resx editor. The ResourceManager should honor case by default.
If you'd like to see another format supported, submit a PR generating the file in ResourceGenerators
and it will be updated when new keys are added.