Localization is crucial for making your ASP.NET Core applications accessible to users from different cultures and languages. By integrating effective software internationalization practices, you ensure that your applications are not only linguistically accurate but also culturally relevant, enhancing user experience across diverse markets. In this tutorial, we’ll explore how to implement localization and translation in ASP.NET Core.
We’ll cover:
- ASP.NET Core i18n/l10n (internationalization and localization)
- Adding language resources and following localization conventions
- Localizing text with
IStringLocalizer
- Translating views with
IViewLocalizer
- Detecting user culture using
IRequestCultureProvider
- Localizing date and time formats
- Handling pluralization in localization
Check out the source code for this tutorial on GitHub.
Prerequisites
Before you begin, make sure you have:
- ASP.NET Core 8+ installed
- Visual Studio 2022
- Basic understanding of C# and ASP.NET
ASP.NET Core localization: Preparing project
First, let’s set up a basic ASP.NET Core project that we’ll later turn into an internationalized web application.
Open Visual Studio and create a new project with the following configuration:
- Template: ASP.NET Core Empty
- Name: ASPLokaliseDemo
- Target Framework: .NET 8.0 (LTS)
- Note: Tick the “Place solution and project in the same directory” option.
Setting up services and building the application
Next, open the Program.cs
file and add the following code::
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); var app = builder.Build();
Key points:
- Initializes the
WebApplicationBuilder
to configure services and middleware. - Registers MVC and Razor Pages services for dependency injection.
- Builds the app with the configured services.
Configuring the HTTP request pipeline
Now, let’s configure the HTTP request pipeline by adding this code into the same file:
// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseStaticFiles();
Key points:
- Enables the Developer Exception Page in development mode.
- Redirects HTTP requests to HTTPS for security.
- Serves static files like HTML, CSS, and JavaScript.
Adding routing and endpoint mapping
Finally, set up routing and endpoint mapping in the same file:
app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.MapRazorPages(); app.Run();
Key points:
UseRouting
matches incoming requests to their corresponding endpoints.UseAuthorization
checks for user permissions.- Maps the default route and Razor Pages.
- Runs the application and handles incoming HTTP requests.
Creating the Home Controller
To set up a Home Controller for your project:
- Create a
Controllers
directory in the project’s root. - Add the
HomeController
:- In Visual Studio, right-click the
Controllers
directory. - Select Add > New Item…
- Choose MVC Controller – Empty and name it
HomeController.cs
.
- In Visual Studio, right-click the
- Update
HomeController.cs
:- Visual Studio will generate a basic
HomeController
class with anIndex
method. This method will be the default action when users navigate to the root URL.
- Visual Studio will generate a basic
Here’s what the HomeController.cs
should look like:
using Microsoft.AspNetCore.Mvc; namespace ASPLokaliseDemo.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(); } } }
The HomeController
is created to match the “default” route set up earlier in Program.cs
. When the application runs, and the user navigates to the root URL (e.g., https://localhost:5001/
), the request is routed to the Index
action of the HomeController
.
Creating the Razor view for the Index action
With your HomeController
set up, it’s time to create the view for the Index
action:
- Create a
Views
directory in the project’s root. - Add a
Home
folder inside theViews
directory. - Add the
Index.cshtml
file:- Right-click the
Home
folder. - Select Add > New Item…
- Choose Razor View and name it
Index.cshtml
.
- Right-click the
- Fill in
Index.cshtml
with the following code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>ASP.NET localization demo | Lokalise</title> </head> <body> <p>Ready to get localized!</p> </body> </html>
Razor views are used to generate HTML dynamically on the server. The Index.cshtml
file corresponds to the Index
action in HomeController
. When this action is invoked, it looks for a view named Index.cshtml
within the Views/Home/
directory by convention.
Resource (RESX) files for ASP.NET localization
Before diving into localization logic, we need to set up language resources in our ASP.NET Core project. In ASP.NET Core, localized strings for classes or views are stored in .resx
files.
ASP.NET Core localized resource file organizing
In ASP.NET Core, localization involves storing strings in .resx
resource files. These files contain the localized text for different languages and are referenced by the corresponding views or classes within the application.
Organizing and naming resource files
Resource files follow specific naming conventions:
- Matching namespace: If the namespace matches the project’s assembly name, the resource file should be named after the class.
- Example: For
ASPNETCoreL10n.HomeController
, name the resource fileHomeController.resx
.
- Example: For
- Non-matching namespace: If the namespace doesn’t match, use the fully qualified class name.
- Example: For
ASPNETCoreUtils.StringFormatter
, name the resource fileASPNETCoreUtils.StringFormatter.resx
.
- Example: For
Resource files are typically placed in the same directory as the class or view they localize, simplifying the association. Alternatively, you can organize all resources in a centralized Resources
folder.
Supporting multiple locales
To support multiple languages, create separate .resx
files for each locale:
- Neutral culture:
<resource-file-name>.<language>.resx
- Example:
HomeController.en.resx
(for English)
- Example:
- Specific culture:
<resource-file-name>.<language>-<region>.resx
- Example:
HomeController.fr-FR.resx
(for French – France)
- Example:
The language and region codes follow ISO standards, such as en-US
for English (United States) and fr-FR
for French (France).
Creating resource files for localization
To support English (US) and French (France) localization in your HomeController
, follow these steps:
- Create the
Resources
directory:- In the root of your project, create a new folder named
Resources
.
- In the root of your project, create a new folder named
- Add resource files:
- Inside the
Resources
folder, add two resource files namedControllers.HomeController.en-US.resx
andControllers.HomeController.fr-FR.resx
.
- Inside the
- Adding keys to resource files:
- Open one of the resource files, like
Controllers.HomeController.en-US.resx
, in Visual Studio. - Click on the “plus” icon (or “Add Resource” if you see that option).
- A dialog will appear, prompting you to enter the details of the new resource.
- Name: Enter
welcome
. This is the key that you’ll use in your code to reference this resource. - Value: Enter
Welcome!
. This is the English text that will be displayed to users when thewelcome
key is accessed.
- Name: Enter
- Click “Add” or “OK” to save the resource.
- Open one of the resource files, like
- Handling multiple locales:
- After adding the key to one resource file, repeat the process for the other file (
Controllers.HomeController.fr-FR.resx
). - In the French resource file, set the
Value
for thewelcome
key toBienvenue !
.
- After adding the key to one resource file, repeat the process for the other file (
- Fallback resource file:
- Once you add resources, you might notice that a default
Controllers.HomeController.resx
file is created in the same directory. This file acts as a fallback for when a specific localization isn’t available. Open it, and you should see thewelcome
key listed. However, this file typically remains empty or contains neutral/default values.
- Once you add resources, you might notice that a default
- Review the resource table:
- In each resource file, you’ll see a table where your
welcome
key is listed along with its corresponding values. For theen-US
file, you’ll haveWelcome!
, and for thefr-FR
file, you’ll haveBienvenue !
. These values will be used based on the user’s locale.
- In each resource file, you’ll see a table where your
By following these steps, you’ve successfully set up localized resources for your HomeController
in English and French!
Implementing ASP.NET core localization with IStringLocalizer
In ASP.NET Core, using the IStringLocalizer
interface is the recommended way to manage localization. It integrates smoothly with the framework, making your code cleaner and easier to maintain.
Setting up localization in the application
First, we need to configure our application to support localization. Open your Program.cs
file and update it with the following code:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); // Add localization services builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); var app = builder.Build(); // Enable request localization middleware app.UseRequestLocalization(); // Additional configurations like routing, authorization, etc. // ... app.Run();
In this setup, we’re adding localization services and pointing to the Resources
folder for our .resx
files. The UseRequestLocalization
middleware is crucial—it ensures that your application can switch between different cultures and languages based on the user’s request.
Localizing strings in the HomeController
Now, let’s modify the HomeController
to use IStringLocalizer
for fetching localized strings. Here’s how to do it:
- Open the
HomeController.cs
file. - Update the class to inject
IStringLocalizer
and use it to retrieve the localized strings:
using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; namespace ASPLokaliseDemo.Controllers { public class HomeController(IStringLocalizer<HomeController> localizer) : Controller { public IActionResult Index() { ViewData["greeting"] = localizer["welcome"]; return View(); } } }
Here’s what’s happening:
IStringLocalizer<HomeController>
is injected into the controller, giving you access to localized strings specific to this controller.- The line
_localizer["welcome"]
retrieves the localized string associated with thewelcome
key. This string will change based on the current culture set in the user’s request.
Displaying the localized message in the view
Finally, let’s update the view to display the localized message. Open the Views/Home/Index.cshtml
file and modify it as follows:
<body> <h1>@ViewData["greeting"]</h1> </body>
Now, when a user visits the homepage, they’ll see a greeting message that’s automatically translated based on their language preference.
Switching locales in ASP.NET Core
By default, your ASP.NET Core application might always display the same locale (like en-US
), even if you change your browser’s language settings or access the site from different regions. This happens because we haven’t yet configured the application to detect and switch between supported locales automatically.
Setting up supported cultures
To support different cultures, you need to specify which ones your application can handle. In ASP.NET Core, this is done using the SupportedCultures
and SupportedUICultures
properties:
- SupportedCultures: These cultures affect culture-specific operations like date and time formatting.
- SupportedUICultures: These cultures determine how the UI elements (such as text in Razor Views) are localized.
Without defining these properties, ASP.NET Core won’t know which languages or cultures your application is ready to handle.
Using UseRequestLocalization
middleware
To enable your application to switch locales based on user requests (like their browser’s language setting), you’ll configure the UseRequestLocalization
middleware. This middleware adjusts the application’s culture for each request based on the user’s preferences.
First, update Program.cs
with the following code:
using System.Globalization; var builder = WebApplication.CreateBuilder(args); // Add localization services and specify the Resources folder builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); var app = builder.Build(); // Define the supported cultures var supportedCultures = new[] { new CultureInfo("en-US"), new CultureInfo("fr-FR") }; // Configure the Request Localization options var requestLocalizationOptions = new RequestLocalizationOptions { DefaultRequestCulture = new RequestCulture("en-US"), // Default culture SupportedCultures = supportedCultures, SupportedUICultures = supportedCultures }; // Add the Request Localization middleware app.UseRequestLocalization(requestLocalizationOptions); // Configure the HTTP request pipeline if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.MapRazorPages(); app.Run();
In this configuration:
- SupportedCultures: We specify the cultures (
en-US
for English andfr-FR
for French) that our application supports. - RequestLocalizationOptions: This object sets the supported cultures and defines a default culture (
en-US
in this example). - UseRequestLocalization: This middleware is added early in the pipeline to ensure that the culture is set correctly for each request.
Enhancing locale switching with RequestCultureProviders
To further refine how your application detects and switches locales, you can add various RequestCultureProviders
. These providers determine the culture based on different sources, like query strings, cookies, or browser headers.
Here’s how to include additional providers:
// Define the supported cultures var supportedCultures = new[] { new CultureInfo("en-US"), new CultureInfo("fr-FR") }; // Configure the Request Localization options var requestLocalizationOptions = new RequestLocalizationOptions { DefaultRequestCulture = new RequestCulture("en-US"), SupportedCultures = supportedCultures, SupportedUICultures = supportedCultures, // Explicitly specifying the type for RequestCultureProviders RequestCultureProviders = [ new QueryStringRequestCultureProvider(), new CookieRequestCultureProvider(), new AcceptLanguageHeaderRequestCultureProvider() ] }; app.UseRequestLocalization(requestLocalizationOptions);
You can access your site with a URL like https://localhost:5001/?culture=fr-FR
to see the French version.
Understanding RequestCultureProviders
RequestCultureProviders
in ASP.NET Core check different sources to determine the appropriate culture for each request:
- QueryStringRequestCultureProvider: This provider checks the URL query string for culture information. For example,
https://localhost:5001/?culture=fr-FR&ui-culture=fr-FR
would set the culture to French. - CookieRequestCultureProvider: This provider reads the user’s culture preference from a cookie. It’s useful for remembering the user’s language across sessions.
- AcceptLanguageHeaderRequestCultureProvider: This provider uses the
Accept-Language
header sent by the browser to determine the culture, typically reflecting the user’s language preferences in their browser settings.
The order in which these providers are listed matters. ASP.NET Core will go through them sequentially and apply the first one that successfully determines a culture. A common order might start with the QueryStringRequestCultureProvider
for explicit user choices, followed by the CookieRequestCultureProvider
to remember past choices, and finally, the AcceptLanguageHeaderRequestCultureProvider
as a fallback based on the browser’s settings.
Date and time localization
After setting up text localization and enabling language switching, it’s important to ensure that dates and times are also formatted according to the user’s culture. Date and time formats vary widely between cultures, and ASP.NET Core makes it easy to display these in the appropriate format.
Displaying a localized date on the home page
To demonstrate, let’s add the current date to the home page and ensure it’s localized based on the selected culture.
In your HomeController
, pass the current date and time to the view. ASP.NET Core will automatically format this date according to the current culture.
Here’s how to modify the Index
action:
public IActionResult Index() { ViewData["greeting"] = localizer["welcome"]; ViewData["currentDate"] = DateTime.Now.ToString("D", CultureInfo.CurrentCulture); // "D" stands for long date format return View(); }
What’s happening:
DateTime.Now.ToString("D", CultureInfo.CurrentCulture)
fetches the current date and formats it as a long date string (like “Monday, August 16, 2024”) according to the user’s selected culture. The"D"
specifier tells ASP.NET Core to use the long date pattern, which includes the day of the week, the full month name, the day, and the year.CultureInfo.CurrentCulture
automatically reflects the culture set by the localization providers (query string, cookie, or browser settings), adapting the date format to match the user’s locale.
Now, update the Index.cshtml
view to display the localized date alongside the greeting message:
<body> <h1>@ViewData["greeting"]</h1> <p>@ViewData["currentDate"]</p> </body>
What’s happening:
- The
@ViewData["currentDate"]
line will now display the date formatted according to the user’s selected culture.
Custom date format example
If you prefer a different format, such as "Monday, August 16, 2024"
, you can specify it directly:
ViewData["currentDate"] = DateTime.Now.ToString("dddd, MMMM dd yyyy", CultureInfo.CurrentCulture);
Handling time zones
If your application serves users in different time zones, you may want to convert the date and time to the user’s local time zone before displaying it:
var userTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"); // Example for CET ViewData["currentDate"] = TimeZoneInfo.ConvertTime(DateTime.Now, userTimeZone).ToString("F", CultureInfo.CurrentCulture);
In this case, the “F” format specifier produces a detailed date and time string that includes the day of the week, the full month name, the day, the year, and the time down to the seconds.
Pluralization
When localizing content, it’s important to account for pluralization—how words change depending on quantity. Different languages handle plurals in unique ways, so ensuring that your application correctly displays singular and plural forms is crucial for providing a natural user experience.
To handle pluralization, we’ll add entries for both singular and plural forms in our resource files. First, open the HomeController.en-US.resx
file using the XML editor, and add the following entries:
<data name="welcome" xml:space="preserve"> <value>Welcome!</value> </data> <data name="AppleCount" xml:space="preserve"> <value>{0} apple</value> </data> <data name="AppleCountPlural" xml:space="preserve"> <value>{0} apples</value> </data>
These entries define the messages for singular (AppleCount
) and plural (AppleCountPlural
) forms. The {0}
placeholder will be replaced by the actual number at runtime.
Do the same for the French resource file (HomeController.fr-FR.resx
):
<data name="welcome" xml:space="preserve"> <value>Bienvenue !</value> </data> <data name="AppleCount" xml:space="preserve"> <value>{0} pomme</value> </data> <data name="AppleCountPlural" xml:space="preserve"> <value>{0} pommes</value> </data>
Next, we’ll modify the HomeController
to decide whether to use the singular or plural form based on the number of items (in this case, apples).
Here’s the updated Index
action:
public IActionResult Index() { ViewData["greeting"] = localizer["welcome"]; // ViewData["currentDate"] = DateTime.Now.ToString("D", CultureInfo.CurrentCulture); // "D" stands for long date format var userTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"); // Example for CET ViewData["currentDate"] = TimeZoneInfo.ConvertTime(DateTime.Now, userTimeZone).ToString("F", CultureInfo.CurrentCulture); int appleCount = 3; // Example count string appleMessage = appleCount == 1 ? localizer["AppleCount", appleCount] : localizer["AppleCountPlural", appleCount]; ViewData["appleMessage"] = appleMessage; return View(); }
Finally, let’s display the localized message in the view. Open the Index.cshtml
file and update it as follows:
<body> <h1>@ViewData["greeting"]</h1> <p>@ViewData["currentDate"]</p> <p>@ViewData["appleMessage"]</p> </body>
Now your text is pluralized!
Using IViewLocalizer
for Views in ASP.NET Core
While IStringLocalizer
is excellent for general localization within controllers and other backend code, ASP.NET Core provides IViewLocalizer
specifically for localizing content directly within Razor views. This can make your view code cleaner and more tightly integrated with localization, especially when you need to localize strings that are specific to a particular view.
IViewLocalizer
is an interface in ASP.NET Core designed for use in Razor views. It automatically associates the localized strings with the specific view they are used in. This means that it looks for resource files that match the view’s path and name, making it very convenient for localizing view-specific content.
Setting Up IViewLocalizer
in a View
IViewLocalizer
is a specialized service in ASP.NET Core used for localizing content directly within Razor views. It’s particularly useful when the text you want to localize is closely tied to a specific view, keeping your localization logic organized and easy to manage.
To use IViewLocalizer
in a Razor view, you first need to inject it using the @inject
directive. This makes the IViewLocalizer
service available within the view.
For example, in your Index.cshtml
view, add the following:
<p>From IView: @Localizer["welcome_iview"]</p>
Next, you need to ensure that your ASP.NET Core application is configured to support view localization. Open the Program.cs
file and add the necessary services:
using Microsoft.AspNetCore.Localization; using System.Globalization; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); // Add localization services builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); // Register View Localization builder.Services.AddMvc() .AddViewLocalization(); // This adds support for IViewLocalizer var app = builder.Build();
To localize content in a specific view, you’ll need to create resource files corresponding to that view:
Resources/Views/Home/Index.en-US.resx
Resources/Views/Home/Index.fr-FR.resx
Then create translation keys inside as before. Make sure that your key are assigned to the proper resources file (to the Index
and not to the resources for controllers). You can easily move your translation keys:
When to Use IViewLocalizer
vs. IStringLocalizer
It’s important to choose the right localizer based on your needs:
IViewLocalizer
: Use this when the content you’re localizing is specific to a single view. It keeps localization tied to the view, which can simplify maintenance since everything related to that view is kept together. For example, a localized string that only appears on theIndex
page is a good candidate forIViewLocalizer
.IStringLocalizer
: This is more flexible and can be used across controllers, services, and views. It’s ideal for localization that spans multiple areas of your application, such as common phrases, error messages, or UI elements that are reused in different parts of the site.IStringLocalizer
is best for centralizing localization, making it easier to update common strings across the application.
Example: If you have a string like “Submit” that appears on various buttons across multiple views, IStringLocalizer
would be the better choice. But if you have a unique message or header that only shows up on a single page, IViewLocalizer
might be more appropriate.
Let Lokalise do the localizing
In this section, I’d like to introduce you to Lokalise, a translation management system that handles all your ASP.NET application’s internationalization needs.
Lokalise offers a range of features that simplify the localization process, such as easy integration with third-party services like GitHub, Jira, Figma, Trello, S3, and more. It also supports collaborative translations, provides quality assurance tools, and allows easy translation management through a central dashboard.
With these features, Lokalise makes it easier to expand your ASP.NET Core app to all the locales you plan to reach.
Getting started with Lokalise is simple:
- Sign up for a free trial (no credit card information required).
- Log in to your account.
- Follow the wizard instructions to create a new Web and mobile project.
- Make sure to select the correct base and target languages. For instance, if your sample app’s default locale is
en-US
with support forfr-FR
, ensure you choose similar locales in Lokalise. Don’t worry if you make a mistake; you can easily adjust the languages later through the UI.
- Make sure to select the correct base and target languages. For instance, if your sample app’s default locale is
Once your project is set up, open it and go to the Upload page. From there, choose the RESX files from your ASP.NET project:
Alternatively, you can drag and drop the entire Resources folder into the designated area.
After uploading, return to the Editor to modify your translations, add more languages, and create new keys as needed.
When you’re ready, navigate to the Download page, select RESX as the file format, and download your translated files back into your project.
That’s it! For more detailed guidance, check out the Getting started documentation on Lokalise, where you’ll find a collection of articles to help you kick-start your localization journey.
Conclusion
Localizing your ASP.NET Core application is essential for reaching a global audience. By leveraging built-in tools like IStringLocalizer
and IViewLocalizer
, you can easily manage text and date formats across multiple cultures.
And with Lokalise, the process becomes even smoother, allowing you to efficiently handle translations and expand your app’s reach. Whether you’re managing simple strings or complex pluralization, the combination of ASP.NET Core and Lokalise provides a powerful and flexible solution for all your internationalization needs.