在全球化的今天,软件产品需要支持多种语言以满足不同地区用户的需求。本地化是软件开发中的一个重要环节,它涉及到将应用程序的界面、文档和帮助等内容翻译成目标语言。本文将介绍如何使用Blazor模板应用程序对ASP.NET应用程序进行本地化处理,包括本地化的益处、从全局到局部的本地化策略、构建本地化类、设置文化、提供本地化器、使用本地化器以及运行时语言切换。
本地化不仅仅是简单的翻译工作,它还包括对目标语言和文化的理解。通过本地化,可以为不同地区的用户提供更加友好和亲切的用户体验。以下是本地化带来的一些主要益处:
1. 本地化文本按主题分组,便于管理和维护。
2. 本地化参数具有类型安全性,减少了错误和混淆。
3. 枚举类型的本地化,使得枚举值也能被正确翻译。
4. 清晰的翻译模板,方便翻译人员理解和翻译。
要实现本地化,应用程序必须设计得尽可能文化中立,即作为一个全球应用程序。然后,通过本地化相应的文化来扩展全局应用程序。这种分离是通过将本地化文本放置在资源文件中实现的。
资源文件的类型通常是.resx。以下是一个示例应用程序的本地化概览:
在Visual Studio中,本地化表示如下:
所有本地化文本都按主题划分到本地化类中,这些类继承自基类LocalizerBase。通过根类Localizer访问本地化文本:
public class Localizer : LocalizerBase
{
public Localizer(IStringLocalizerFactory factory) : base(factory, nameof(Localizer))
{
App = new AppLocalizer(factory);
Home = new HomeLocalizer(factory);
Survey = new SurveyLocalizer(factory);
Counter = new CounterLocalizer(factory);
Forecast = new ForecastLocalizer(factory);
}
public AppLocalizer App { get; }
public HomeLocalizer Home { get; }
public SurveyLocalizer Survey { get; }
public CounterLocalizer Counter { get; }
public ForecastLocalizer Forecast { get; }
}
对于每个主题,实现一个本地化器以提供对翻译资源的访问,例如CounterLocalizer:
public class CounterLocalizer : LocalizerBase
{
public CounterLocalizer(IStringLocalizerFactory factory) : base(factory)
{
}
public string Title => Localization();
public string Click => Localization();
public string CurrentCount(int currentCount) =>
ApplyParameter(Localization(), nameof(currentCount), currentCount);
}
固定的翻译数据是通过Localization()方法确定的。翻译键Counter.Title、Counter.Click和Counter.CurrentCount是从类名和属性生成的。带有变量参数的翻译会生成带有参数名占位符的格式化文本,例如“当前计数:{currentCount}”。
Culture类管理翻译文化:
对于ASP.NET应用程序,结构不是在应用程序级别(CultureInfo.CurrentCulture和CultureInfo.CurrentUICulture)设置的,而是在web请求线程级别(CultureInfo.DefaultThreadCurrentCulture和CultureInfo.DefaultThreadCurrentUICulture)设置的。
Localizer通过依赖注入访问,在应用程序启动时设置。
public class Program
{
private static void SetupLocalization(IServiceCollection services)
{
services.AddLocalization(o => { o.ResourcesPath = Cultures.ResourcesPath; });
services.AddTransient<Localizer>();
Cultures.ApplyCulture();
}
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
// localizations
SetupLocalization(builder.Services);
var app = builder.Build();
// app setup
...
app.Run();
}
}
SetupLocalization方法执行以下步骤:
Localizer通过Inject插入到页面中,并用可读的属性或方法访问替换硬编码文本。
<ml-basic>
@page "/counter"
@using Localization
<PageTitle>Counter</PageTitle>
<h1>@Localizer?.Counter.Title</h1>
<p role="status">@Localizer?.Counter.CurrentCount(currentCount)</p>
<button class="btn btn-primary" @onclick="IncrementCount">
@Localizer?.Counter.Click
</button>
@code {
private int currentCount;
[Inject]
private Localizer? Localizer { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
</ml-basic>
值@Localizer?.Counter.Title返回本地化的标题,@Localizer?.Counter.CurrentCount(currentCount)提供类型安全性(int)。
另一种场景是翻译枚举,其本地化存储按照Enum.<EnumTypeName>.<EnumValue>约定。可以使用Localizer.Enum<T>(T value)查询本地化的枚举。
<ml-basic>
@page "/"
@using Localization
<PageTitle>@Localizer?.Home.Title</PageTitle>
<h1>@Localizer?.Home.Header</h1>
@Localizer?.Home.Welcome
<SurveyPrompt Title="@Localizer?.Home.SurveyTitle"/>
@Localizer?.Home.EnumLocalization
<InputSelect @bind-Value="CurrentColor">
@foreach (var value in Enum.GetValues(typeof(Color)))
{
<option>@Localizer?.Enum((Color)value)</option>
}
</InputSelect>
@code {
[Inject]
private Localizer? Localizer { get; set; }
private string? CurrentColor { get; set; }
protected override Task OnInitializedAsync()
{
CurrentColor = Localizer?.Enum(Color.Red);
return base.OnInitializedAsync();
}
}
</ml-basic>