A multilingual site should translate the following:
- Date and time formatting
- Currency
- Text resources: lables, buttons, validation messages, tooltips
It must be easy to switch languages.
It should be relatively easy to add more languages.
1. Routing
Add lang parameter to routes in RegisterRoutes method of RouteConfig class. Set constraints to enabled languages. Set default language in default route.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Language",
url: "{lang}/{controller}/{action}/{id}",
defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional },
constraints: new { lang = @"ru|en" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional, lang = "en" }
);
}
}
|
2. Activate culture
Culture can be activated by two different ways: in controller’s Initialize method or by using filter attribute.
2.1 Initialize method
Every controller that needs language data must Initialize culture in it’s Initialize method. Let’s create a base controller class and override Initialize method in it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public abstract class BaseController : Controller
{
private string CurrentLanguageCode { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (requestContext.RouteData.Values["lang"] != null && requestContext.RouteData.Values["lang"] as string != "null")
{
CurrentLanguageCode = (string)requestContext.RouteData.Values["lang"];
if (CurrentLanguageCode != null)
{
try
{
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo(CurrentLanguageCode);
}
catch (Exception)
{
throw new NotSupportedException($"Invalid language code '{CurrentLanguageCode}'.");
}
}
}
base.Initialize(requestContext);
}
}
|
All the other language-dependent controllers must be inherited from this base controller class.
2.2 Language localization filter attribute
Create a LocalizationAttribute class inherited from ActionFilterAttribute:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class LocalizationAttribute : ActionFilterAttribute
{
private readonly string _defaultLanguage;
public LocalizationAttribute(string defaultLanguage)
{
_defaultLanguage = defaultLanguage;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string lang = (string)filterContext.RouteData.Values["lang"] ?? _defaultLanguage;
if (lang != null)
{
try
{
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
}
catch (Exception)
{
throw new NotSupportedException($"Invalid language code '{lang}'.");
}
}
}
}
|
Add FilterConfig class to App_Start folder and add created LocalizationAttribute in it.
|
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LocalizationAttribute("en"), 0);
}
}
|
Use [Localization] attribute on every controller that needs language information.
|
[Localization("en")]
public class DefaultController : Controller
{
public ActionResult Index()
{
return View();
}
}
|
3. Create translation resources
3.1 Add App_LocalResources folder
Add Asp_Net folder App_LocalResources to project. Resource files will be placed in it.
3.2 Add resource files
Add language resource files
GlobalRes.resx for the default language (English in my case) and files for other languages like GlobalRes.
ru.resx to App_LocalResources folder. Two letters in the file name must be region info ISO (country code) as defined in
RegionInfo.TwoLetterISORegionName Property. The complete list of region codes can be found at the Wikipedia page
ISO 3166-1 alpha-2.
3.3 Set resource files properties
- Resource type: String
- Build Action: Embedded Resource
- Custom Tool: PublicResXFileCodeGenerator
- Access Modifier: Public
3.4 Add translations for all string resources
3.5 Add reference to resource namespace to Razor in web.config
Make sure you have added this namespace to the ~/Views/web.config file and not to the standard ~/Web.config file:
|
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
...
<add namespace="AspMvc5Multilingual.App_LocalResources" />
</namespaces>
</pages>
</system.web.webPages.razor>
|
3.6 Use translated resources in code
DisplayAttribute to define translation for a model’s class name:
[Display(Name = "Name", ResourceType = typeof(GlobalRes))]
|
public class Widget
{
[Required(ErrorMessageResourceType = typeof(GlobalRes), ErrorMessageResourceName = "This_field_is_required")]
[StringLength(50, MinimumLength = 5, ErrorMessageResourceType = typeof(GlobalRes), ErrorMessageResourceName = "Must_be_at_least_5_charachters")]
[RegularExpression(@"^[A-Za-z0-9_]+$", ErrorMessageResourceType = typeof(GlobalRes), ErrorMessageResourceName = "Must_contain_only_letters")]
[Display(Name = "Name", ResourceType = typeof(GlobalRes))]
public string Name { get; set; }
}
|
Use GlobalRes.resname references in Razor files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@model AspMvc5Multilingual.Models.Widget
@Html.ActionLink(GlobalRes.MainMenu, "Index", "Default", null, new { title = GlobalRes.Tooltip_help })
<br/>
@GlobalRes.Money: @($"{Model.Money:c0}") <br/>
@GlobalRes.DateAndTime: @Model.DateTime.ToString("F") <br/>
<br/>
@using (Html.BeginForm())
{
@Html.ValidationSummary(false, "")
<div>
@Html.LabelFor(model => model.Name, GlobalRes.Label_text)
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name, "")
</div>
<input type="submit" value="@GlobalRes.Submit_and_test_error_messages" />
}
|
4. Switch between languages
Use the following helper class that produces language links based on routing. It creates UrlHelper extension method.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public static class LanguageHelper
{
public static MvcHtmlString LangSwitcher(this UrlHelper url, string Name, RouteData routeData, string lang)
{
var liTagBuilder = new TagBuilder("li");
var aTagBuilder = new TagBuilder("a");
var routeValueDictionary = new RouteValueDictionary(routeData.Values);
if (routeValueDictionary.ContainsKey("lang"))
{
if (routeData.Values["lang"] as string == lang)
{
liTagBuilder.AddCssClass("active");
}
else
{
routeValueDictionary["lang"] = lang;
}
}
aTagBuilder.MergeAttribute("href", url.RouteUrl(routeValueDictionary));
aTagBuilder.SetInnerText(Name);
liTagBuilder.InnerHtml = aTagBuilder.ToString();
return new MvcHtmlString(liTagBuilder.ToString());
}
}
|
Use language links in for instance _Layout.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@using AspMvc5Multilingual.Helper
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<ul>
@Url.LangSwitcher("English", ViewContext.RouteData, "en")
@Url.LangSwitcher("Russian", ViewContext.RouteData, "ru")
</ul>
<div>
@RenderBody()
</div>
</body>
</html>
|
5. Format some numbers as another currency then current culture
|
NumberFormatInfo nfi = new NumberFormatInfo {CurrencySymbol = "€"};
string.Format(nfi,"{0:c0}",12345);
|
6. Format all numbers as another currency then current culture
|
NumberFormatInfo nfi = new NumberFormatInfo { CurrencySymbol = "£" };
Thread.CurrentThread.CurrentUICulture.NumberFormat = Thread.CurrentThread.CurrentCulture.NumberFormat = nfi;
|
7. Add new language
- Add new language code in routing map
- Add new resource file and translate all strings in it
- Add new language switch-link to views
- Sưu tầm tại nguồn: http://blog.chudinov.net/setup-a-multilingual-site-using-asp-net-mvc5/
Nhận xét
Đăng nhận xét