Getting Started With ASP.NET MVC i18n

Phrase
Software Localization Tutorials
10 min readMay 11, 2020
Let’s walk you through the foundational steps of getting ASP.NET MVC apps ready for different cultures.

ASP.NET MVC is a modern Model-View-Controller framework for building web applications built on Microsoft’s large and reliable .NET environment. You probably already knew that. In fact, you’re probably building an ASP.NET MVC app and you’re looking to globalize it so that you can serve it in different languages. Well, have no fear. In this article, we’ll build a small demo app and globalize its UI and routes, giving you a foundation to build on as you develop and deliver your app to users from all over the world. ASP.NET MVC i18n, here we come!

Globalization, i18n, l10n … Oh My!

Outside of .NET, we often refer to the process of getting an application ready for delivery to people in different parts of the world as internationalization, abbreviated as i18n. This usually means not hard-coding our UI strings so that their translations can be used dynamically. It also often means we’re aware of regional differences when it comes to dates and calendars, currency, and more. It’s worth noting that Microsoft calls this process globalization in their documentation. So we’ll use the terms globalization and i18n interchangeably here.

Localization, or l10n, is the process of building on i18n and providing the actual translations and regional formatting that is required for a given locale. That’s mostly fancy talk for l10n == translation (although there’s a bit more to it).

Oh, and while we’re at it: A locale is a combination of a language and a region, like “Canadian English”, and is often denoted with a code like “en-CA”. In .NET, this is called a culture. Again, we’ll use the terms locale and culture interchangeably here.

Alright enough with the semantics. Let’s get to building.

The Demo App

Our little demo will be a simple web app called Heveanture, a foray into the world of constellations.

Our home page will list constellations
Each constellation will have a details page

Nothing too crazy, and it will allow us to cover basic i18n pretty well.

Resource » You can get all the code for the app we will build here from the app’s GitHub repo.

Framework and Package Versions

We’re using the following IDE, frameworks, and packages to build this demo app, with versions at the time of writing.

  • Visual Studio 2019
  • .NET 4.7
  • ASP.NET MVC 5.2

We’ll also be working in C#, although a lot of what we cover should apply to any language that works on top of .NET.

Heads Up » We’re using the traditional .NET framework, which generally requires Windows, not to be confused with .NET Core, the newer cross-platform variant of .NET.

Creating the Project

We’ll get started by opening Visual Studio and creating a new project. In the Create a new project dialog, let’s select the ASP.NET Web Application (.NET Framework) template with the C# label (again not ASP.NET Core) and click Next.

Using the search box can help filter to the template we want

In the Configure your new project dialog, we can enter the name of our app, and click Create.

We’re using .NET 4.7 here

And, in the Create a new ASP.NET Web Application dialog, we can select the MVC template and click Create.

Would you create the project already?!

Alright, that should be it for creating the project. If we now run the project using the green play button in Visual Studio, we should be greeted with a placeholder home page.

Featuring out-of-the-box Bootstrap CSS for styling

Building our App

Let’s start cleaning up some of the scaffolding that Visual Studio has given us and get our own app logic and styles in place.

We’ll start with a mock view model that represents our constellations. We won’t be touching the database layer in this article. If you would like us to cover database globalization with ASP.NET MVC, please let us know in the comments below :). For now, some hard-coded data will get us globalizing the front end.

Constellation has a few simple properties, a hard-coded All() method that retrieves a List<Constellation>, and a FindById() method that finds and returns a constellation by its Id. Let’s wire this model up to our Home controller.

We just query the data and pass it on to our views. Speaking of which, let’s build those.

Our index view displays our constellations as columns, with images and names. The image and name of each constellation link to its respective details page.

In our details view, we simply display the constellation’s image and properties in an orderly fashion.

We also update our CSS, adding a Boostrap theme from Bootswatch called Cyborg, removing extraneous views and actions, and adding our constellation images.

Resource » If you would like to see all the changes we made up to this point, checkout the commit tagged “start” in the demo app’s GitHub repo. You can also checkout the start commit if you want to code along with us, and you want to get to globalization right away, without building everything we have up to this point yourself.

When we run our app now, we see this beauty:

Humans have always connected the dots peppering the void of space
The Great Bear growls

Using Resource Files for Localized Messages

.NET supports resource files (.resx) for translation messages. It can be a bit tricky to set up resource files if you’ve never done it before, however. Let’s walk through it step-by-step.

Creating Resources Files in Visual Studio

First, let’s create a folder/namespace that houses our resource files. In the Visual Studio Solution Explorer, we can right-click on our project (Heaventure), and select Add > New Folder. We can name this folder anything we want. I’ll go with Resources.

Next, let’s create our default resources file. We can right-click on the folder we just created and select Add > New Item. There doesn’t seem to be a template for resources files in Visual Studio 2019, but there’s an easy workaround for this. We can select the Visual C# > General tab in the sidebar and select Text File. Then, we can name the file Resources.resx, and click Add.

Make sure to change the file extension to .resx

Once we’ve added the file, we can open it in Visual Studio.

A collection of name-value pairs

Heads Up » At this point, we need to make sure to click the Access Modifier dropdown and select Public. Otherwise, our resource file won’t work.

We just created our app’s default, English resource file. We can now repeat the above process for each additional culture our app supports. We need to follow the naming convention Resources.{culture-code}.resx, otherwise .NET won’t load the correct file when we switch cultures later. I’ll add an Arabic resource file named Resources.ar.resx, and make sure to set its Access Modifier to Public.

Adding Translations to Resource Files

At this point, we can add a string to Resources.resx. Let’s open the file and add our application’s name in English.

Don’t forget to save the file

If we want to add an Arabic translation for our app’s name, we can open our Resources.ar.resx file and add a string with the same Name we used in our English Resources.resx. We then can add the Arabic translation as the Value for the string.

Same name, different language

Using Resource Strings in Our Views

Let’s pull our newly adding string into our _Views > Shared > Layout.cshtml file.

https://gist.github.com/ashour/8237fc04fe7847808dd77c8aeb1e95fc#file-views-shared-_layout-cshtml

Instead of the hard-coded string, we’re now using Heaventure.Resources.AppName.

To see the benefit of what we’ve just done, we can go into our HomeController‘s Index action and set the culture to Arabic before we return our view.

We’ll go through setting culture in more detail in a little bit. We’re just trying to see if our resource files are working for now. After adding the code above, we can run our app and visit the root route (/) to load the index view.

We now have translated messages!

We can now remove the hard-coded culture setting we added to HomeController; we won’t be needing it.

Note » .NET will automatically fall back onto the string with the same name in the default Resources.resx if it can’t find it in Resources.{current-culture}.resx.

Adding the Resources Namespace to Web.Config

Typing Heaventure.Resources.{Name} everywhere we want a translated message seems a bit too verbose. We don’t have to use the fully qualified namespace, however. We can make our lives easier by adding the Heaventure.Resources namespace to our Web.config.

Inside the <namespaces> element, we can <add namespace="Heaventure.Resources"/>. Once we do, we can type Resources.AppName instead of Heaventure.Resources.AppName in our views.

Alright, that’s basic translation strings taken care of. Now let’s see how we can set our app’s current culture via routes.

.NET Culture

Let’s take a look at how .NET deals with locales, or cultures. .NET sets a culture per thread, so when we want to set or get the current culture, we need to do something like the following.

CultureInfo is the class that defines culture in .NET. It contains a wealth of information about a given culture, including its name, currency format, calendar, and much more.

Resource » Check out the official .NET documentation for more information about CultureInfo.

The Difference Between Culture and UICulture

You may have wondered why we’re setting both CurrentCulture and CurrentUICulture in the code above. Well, the two properties are responsible for different things.

CurrentUICulture deals with resource files (.resx), like the ones we created above. If we set CurrentUICulture to Arabic, for example, .NET will load the Resources.ar.resx file automatically.

CurrentCulture deals with almost everything else when it comes to localization: formatting and parsing of values and sorting, among other things.

Setting our App’s Culture

Let’s get back to coding, and use the information we know about .NET cultures to set the culture in our app depending on a route parameter. This means that hitting a route like /en/Details/1 will load our app in the default, English culture. Hitting a route like /ar/Details/1 will load the same view in Arabic.

Localized Routes

We can configure routes like the ones we outlined above by updating our App_Start > RouteConfig.cs file.

Note that we’re redirecting our root route (/) to a localized route with our default culture (English in this case). To accomplish this, we’re setting a default culture value in our localized route. We’re also routing our root to BaseController.RedirectoToLocalized() action.

A Base Controller

Adding a base controller for all our other controllers gives us a place to put common behavior that may look awkward in other controllers, like the RedirectToLocalized() action. BaseController, of course, has to derive from .NET’s MVC Controller.

The BaseController is also a good place to set the app’s culture based on the current culture route parameter. To do this, we can override Controller‘s OnActionExecuting(), which is run before every action on the controller.

We yank the culture value out of the RouteData.Values dictionary, and use it to set our CurrentCulture and CurrentUICulture in our app.

Now we can update our HomeController (and any other controller in our app) to derive from BaseController.

With that in place, when we attempt to hit the root route (/), we’re redirected to /en. If we go to /ar, we can see our app name appearing in Arabic.

Our localized routes are now setting our app’s culture

Resource » We go into setting an app’s culture in more detail in our dedicated article How Do I Set Culture in an ASP.NET MVC App?

A Simple Language/Culture Switcher

Let’s provide our app’s users a simple dropdown to allow them to switch cultures using our new localized route system.

We’re using a Bootstrap .dropdown here, and we’re wrapping it in a .navbar-right so we can embed it in our main _Layout.cshtml.

Now, when we run the app, we have a working language switcher.

Clicking a language takes you to its localized route

Resource » Grab all the code for the demo app we built here from the app’s GitHub repo.

And with that in place, we have working globalization in our app!

Conclusion

We hope you’ve enjoyed this little adventure into ASP.NET MVC i18n. Globalization can be a lot of work, but it doesn’t have to be a pain in the neck. Imagine that you can run a CLI command, and your resource files are automatically sent to translators. When the translators are done working with the resource files in a beautiful web UI, they can save them, and you can sync them back to your project. This and more is possible with Phrase. Built by developers for developers, Phrase is a battle-tested localization platform with a developer CLI and API. Featuring GitHub, GitLab, and Bitbucket sync, Phrase takes care of the i18n plumbing to allow you to focus on the creative code you love. Check out all of Phrase’s features, and sign up for a free 14-day trial.

Originally published on The Phrase Blog.

--

--