Globalization means a web application can adapt to different cultures and regions without code changes. Localization means a web application can display different content based on the user’s culture and region. Globalization and localization are two different concepts, but they are often used together.
There are many ways to implement multiple languages: resource files, databases, configuration files, and so on. This article focuses on the resource file approach, which is also what Microsoft officially recommends. Other options such as PO files and JSON files can also achieve multilingual support, but they are less convenient than resource files.
Creating resource files
Welcome.en-US.resx
Welcome.zh-CN.resx
Adding localization services and resource location
public static IServiceCollection AddCustomLocalization(this IServiceCollection services)
{
services.AddLocalization(options => options.ResourcesPath = "Resources");
return services;
}
Using the localization service
public class HelloWorldController(IStringLocalizerFactory stringLocalizerFactory) : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
var location = Assembly.GetExecutingAssembly().FullName;
ArgumentException.ThrowIfNullOrWhiteSpace(location);
var localizer = stringLocalizerFactory.Create("Welcome", location);
return Ok(localizer["HelloWorld"].Value);
}
}
Setting the language from HTTP requests
The UseRequestLocalization middleware reads the language setting from the request and then sets the current thread’s language so it can be used by subsequent requests. This achieves app-wide localization without having to configure localization in every controller.
public static IApplicationBuilder UseCustomLocalization(this IApplicationBuilder app)
{
var supportedCultures = new[] { "zh-CN", "en-US" };
var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures.First())
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);
return app;
}
Use the following approaches to supply the language setting from a Header, QueryString, or Cookie:
Accept-Language: en-US
http://localhost:5000/?culture=en-Us
.AspNetCore.Culture=en-US
Setting Accept-Language in OpenApi for multilingual support
services.Configure<SwaggerGenOptions>(options => options.OperationFilter<AcceptLanguageHeaderOperationFilter>());
public class AcceptLanguageHeaderOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var parameter = new OpenApiParameter
{
Name = HeaderNames.AcceptLanguage,
In = ParameterLocation.Header,
Required = false,
Schema = new OpenApiSchema
{
Default = new OpenApiString("zh-CN"),
Type = "string",
Enum = [new OpenApiString("zh-CN"), new OpenApiString("en-US")]
}
};
operation.Parameters.Add(parameter);
}
}
Globalizing data display
public class HelloWorldController(IStringLocalizerFactory stringLocalizerFactory) : ControllerBase
{
[HttpGet("time")]
public IActionResult GetTime(
{
//string amount=88.88.ToString("C");
return Ok(TimeProvider.System.GetUtcNow().ToString());
}
}
Localizing models and properties
IStringLocalizerFactory localizerFactory = app.ApplicationServices.GetRequiredService<IStringLocalizerFactory>();
ValidatorOptions.Global.DisplayNameResolver = (type, memberInfo, lambdaExpression) =>
{
string displayName = memberInfo.Name;
DisplayAttribute? displayAttribute = memberInfo.GetCustomAttribute<DisplayAttribute>(true);
displayName = displayAttribute?.Name ?? displayName;
DisplayNameAttribute? displayNameAttribute = memberInfo.GetCustomAttribute<DisplayNameAttribute>(true);
displayName = displayNameAttribute?.DisplayName ?? displayName;
var localizer = localizerFactory.Create(type);
return localizer[displayName];
};
Localizing validation error messages
public UserCreateRequestValidator(IdentityServiceDbContext dbContext, IStringLocalizer<UserCreateRequest> localizer)
{
RuleFor(m => m.PhoneNumber).NotNull().NotEmpty().Length(11).Matches(@"^1\d{10}$").Must((model, phoneNumber) =>
{
return !dbContext.Users.Any(e => e.PhoneNumber == phoneNumber);
}).WithMessage(localizer["PhoneNumberExists"]);
}
Microsoft Resx editing tools
Searching Bing for the keyword Resx Editor turns up many tools that can edit Resx files.
https://learn.microsoft.com/zh-cn/dotnet/framework/tools
Translating resource files with Bing
Searching Bing for the keyword Resx Translation turns up many translation tools that can translate English resource files into other languages.

https://github.com/salarcode/AutoResxTranslator
https://github.com/stevencohn/ResxTranslator