asp.net-core
ローカリゼーション
サーチ…
JSON言語リソースを使用したローカリゼーション
ASP.NET Coreには、アプリをローカライズ/グローバル化できるいくつかの方法があります。あなたのニーズに合った方法を選ぶことが重要です。この例では、 .json
ファイルから言語固有の文字列を読み込んでメモリに保存して、アプリケーションのすべてのセクションでローカリゼーションを行い、高性能を維持する多言語ASP.NET Coreアプリケーションを作成する方法を.json
ます。
私たちがやる方法は、 Microsoft.EntityFrameworkCore.InMemory
パッケージを使用することです。
ノート:
- このプロジェクトのネームスペースは
DigitalShop
であり、これをプロジェクトの名前空間に変更することができます - 奇妙なエラーに遭遇しないように新しいプロジェクトを作成することを検討してください
- 決してベストプラクティスを示しているわけではありませんので、改善できると思うのであれば親切に編集してください
まず、 project.json
ファイルの既存の dependencies
セクションに次のパッケージを追加します。
"Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"
さて、 Startup.cs
ファイルを次のように置き換えてみましょう:( using
ステートメントは後で簡単に追加できるので削除されています)
Startup.cs
namespace DigitalShop
{
public class Startup
{
public static string UiCulture;
public static string CultureDirection;
public static IStringLocalizer _e; // This is how we access language strings
public static IConfiguration LocalConfig;
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) // this is where we store apps configuration including language
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
LocalConfig = Configuration;
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddViewLocalization().AddDataAnnotationsLocalization();
// IoC Container
// Add application services.
services.AddTransient<EFStringLocalizerFactory>();
services.AddSingleton<IConfiguration>(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, EFStringLocalizerFactory localizerFactory)
{
_e = localizerFactory.Create(null);
// a list of all available languages
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-US"),
new CultureInfo("fa-IR")
};
var requestLocalizationOptions = new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
};
requestLocalizationOptions.RequestCultureProviders.Insert(0, new JsonRequestCultureProvider());
app.UseRequestLocalization(requestLocalizationOptions);
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
public class JsonRequestCultureProvider : RequestCultureProvider
{
public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
var config = Startup.LocalConfig;
string culture = config["AppOptions:Culture"];
string uiCulture = config["AppOptions:UICulture"];
string culturedirection = config["AppOptions:CultureDirection"];
culture = culture ?? "fa-IR"; // Use the value defined in config files or the default value
uiCulture = uiCulture ?? culture;
Startup.UiCulture = uiCulture;
culturedirection = culturedirection ?? "rlt"; // rtl is set to be the default value in case culturedirection is null
Startup.CultureDirection = culturedirection;
return Task.FromResult(new ProviderCultureResult(culture, uiCulture));
}
}
}
上記のコードでは、最初に3つのpublic static
フィールド変数を追加します。これは後で設定ファイルから読み込まれた値を使用して初期化されます。
Startup
クラスのコンストラクタでは、json設定ファイルをbuilder
変数に追加します。最初のファイルは、アプリケーションが動作するために必要なので、プロジェクトルートにappsettings.json
がまだ存在しない場合は作成してください。 Visual Studio 2015を使用すると、このファイルが自動的に作成されるので、その内容を次のように変更します(使用しない場合は、「 Logging
セクションを省略することができます)
appsettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"AppOptions": {
"Culture": "en-US", // fa-IR for Persian
"UICulture": "en-US", // same as above
"CultureDirection": "ltr" // rtl for Persian/Arabic/Hebrew
}
}
次に、プロジェクトルートに3つのフォルダを作成します。
Models
、 Services
、 Languages
。 Models
フォルダに、 Localization
という別のフォルダを作成します。
Services
フォルダに、 EFLocalization
という名前の新しい.csファイルを作成します。内容は次のようになります(ここでもusing
ステートメントが含まれていません)
EFLocalization.cs
namespace DigitalShop.Services
{
public class EFStringLocalizerFactory : IStringLocalizerFactory
{
private readonly LocalizationDbContext _db;
public EFStringLocalizerFactory()
{
_db = new LocalizationDbContext();
// Here we define all available languages to the app
// available languages are those that have a json and cs file in
// the Languages folder
_db.AddRange(
new Culture
{
Name = "en-US",
Resources = en_US.GetList()
},
new Culture
{
Name = "fa-IR",
Resources = fa_IR.GetList()
}
);
_db.SaveChanges();
}
public IStringLocalizer Create(Type resourceSource)
{
return new EFStringLocalizer(_db);
}
public IStringLocalizer Create(string baseName, string location)
{
return new EFStringLocalizer(_db);
}
}
public class EFStringLocalizer : IStringLocalizer
{
private readonly LocalizationDbContext _db;
public EFStringLocalizer(LocalizationDbContext db)
{
_db = db;
}
public LocalizedString this[string name]
{
get
{
var value = GetString(name);
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
public LocalizedString this[string name, params object[] arguments]
{
get
{
var format = GetString(name);
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
}
public IStringLocalizer WithCulture(CultureInfo culture)
{
CultureInfo.DefaultThreadCurrentCulture = culture;
return new EFStringLocalizer(_db);
}
public IEnumerable<LocalizedString> GetAllStrings(bool includeAncestorCultures)
{
return _db.Resources
.Include(r => r.Culture)
.Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
.Select(r => new LocalizedString(r.Key, r.Value, true));
}
private string GetString(string name)
{
return _db.Resources
.Include(r => r.Culture)
.Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
.FirstOrDefault(r => r.Key == name)?.Value;
}
}
public class EFStringLocalizer<T> : IStringLocalizer<T>
{
private readonly LocalizationDbContext _db;
public EFStringLocalizer(LocalizationDbContext db)
{
_db = db;
}
public LocalizedString this[string name]
{
get
{
var value = GetString(name);
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
public LocalizedString this[string name, params object[] arguments]
{
get
{
var format = GetString(name);
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
}
public IStringLocalizer WithCulture(CultureInfo culture)
{
CultureInfo.DefaultThreadCurrentCulture = culture;
return new EFStringLocalizer(_db);
}
public IEnumerable<LocalizedString> GetAllStrings(bool includeAncestorCultures)
{
return _db.Resources
.Include(r => r.Culture)
.Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
.Select(r => new LocalizedString(r.Key, r.Value, true));
}
private string GetString(string name)
{
return _db.Resources
.Include(r => r.Culture)
.Where(r => r.Culture.Name == CultureInfo.CurrentCulture.Name)
.FirstOrDefault(r => r.Key == name)?.Value;
}
}
}
上記のファイルでは、カスタムローカライザサービスを作成するために、Entity Framework CoreからIStringLocalizerFactory
インターフェイスを実装してIStringLocalizerFactory
ます。重要な部分は、 EFStringLocalizerFactory
のコンストラクタですEFStringLocalizerFactory
そこでは、使用可能なすべての言語のリストを作成し、データベースコンテキストに追加します。これらの言語ファイルのそれぞれは、別々のデータベースとして機能します。
次に、次の各ファイルをModels/Localization
フォルダに追加します。
Culture.cs
namespace DigitalShop.Models.Localization
{
public class Culture
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Resource> Resources { get; set; }
}
}
Resource.cs
namespace DigitalShop.Models.Localization
{
public class Resource
{
public int Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public virtual Culture Culture { get; set; }
}
}
LocalizationDbContext.cs
namespace DigitalShop.Models.Localization
{
public class LocalizationDbContext : DbContext
{
public DbSet<Culture> Cultures { get; set; }
public DbSet<Resource> Resources { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase();
}
}
}
上記のファイルは、言語リソース、文化、およびEFコアで使用される典型的なDBContext
される単なるモデルです。
この作業をすべて行うために必要な最後のことは、言語リソースファイルを作成することです。 JSONファイルは、アプリで使用できるさまざまな言語のキーと値のペアを格納するために使用されます。
この例では、2つの言語しか利用できません。英語とペルシア語。言語ごとに2つのファイルが必要です。キーと値のペアを含むJSONファイルと、JSONファイルと同じ名前のクラスを含む.cs
ファイル。そのクラスには、JSONファイルを逆シリアル化して返すGetList
というメソッドがあります。このメソッドは、 EFStringLocalizerFactory
作成したEFStringLocalizerFactory
のコンストラクタでEFStringLocalizerFactory
れます。
したがって、これら4つのファイルをLanguages
フォルダに作成します。
en-US.cs
namespace DigitalShop.Languages
{
public static class en_US
{
public static List<Resource> GetList()
{
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
return JsonConvert.DeserializeObject<List<Resource>>(File.ReadAllText("Languages/en-US.json"), jsonSerializerSettings);
}
}
}
en-US.json
[
{
"Key": "Welcome",
"Value": "Welcome"
},
{
"Key": "Hello",
"Value": "Hello"
},
]
fa-IR.cs
public static class fa_IR
{
public static List<Resource> GetList()
{
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
return JsonConvert.DeserializeObject<List<Resource>>(File.ReadAllText("Languages/fa-IR.json", Encoding.UTF8), jsonSerializerSettings);
}
}
fa-IR.json
[
{
"Key": "Welcome",
"Value": "خوش آمدید"
},
{
"Key": "Hello",
"Value": "سلام"
},
]
私たちはすべて終わった。コード( .cs
または.cshtml
)の任意の場所にある言語文字列(キーと値のペア)にアクセスするには、次の操作を行います。
.cs
ファイルで(コントローラかどうかは関係ありません):
// Returns "Welcome" for en-US and "خوش آمدید" for fa-IR
var welcome = Startup._e["Welcome"];
Razorビューファイル( .cshtml
)で
<h1>@Startup._e["Welcome"]</h1>
心に留めておくべきことはほとんどありません。
- あなたがアクセスしようとすると
Key
JSONファイルに存在またはロードされていない、あなただけにアクセスしようとすると、上記の例では、キーリテラルを(取得しますStartup._e["How are you"]
返しますHow are you
存在しないため言語設定に関係なく - あなたは、言語の文字列値を変更した場合
.json
ファイルは、アプリを再起動する必要があります。それ以外の場合は、デフォルト値(キー名)が表示されます。 これは、デバッグなしでアプリを実行しているときに特に重要です。 -
appsettings.json
を使用すると、アプリで必要となるすべての種類の設定を保存できます -
appsettings.json
ファイルから言語/カルチャの設定を変更したい場合は、アプリケーションを再起動する必要はありません 。これは、ユーザーが実行時に言語/文化を変更できるようにするためのオプションをアプリケーションインターフェイスに持たせることを意味します。
最終的なプロジェクトの構造は次のとおりです。
urlパス経由でリクエスト文化を設定する
デフォルトでは、組み込みのRequest Localizationミドルウェアは、クエリー、Cookie、またはAccept-Language
ヘッダーによるカルチャの設定のみをサポートしています。この例では、 /api/en-US/products
ようなパスの一部としてカルチャを設定するためのミドルウェアを作成する方法を示し/api/en-US/products
。
このミドルウェアの例では、ロケールがパスの2番目のセグメントにあると想定しています。
public class UrlRequestCultureProvider : RequestCultureProvider
{
private static readonly Regex LocalePattern = new Regex(@"^[a-z]{2}(-[a-z]{2,4})?$",
RegexOptions.IgnoreCase);
public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
var url = httpContext.Request.Path;
// Right now it's not possible to use httpContext.GetRouteData()
// since it uses IRoutingFeature placed in httpContext.Features when
// Routing Middleware registers. It's not set when the Localization Middleware
// is called, so this example simply assumes the locale will always
// be located in the second segment of a path, like in /api/en-US/products
var parts = httpContext.Request.Path.Value.Split('/');
if (parts.Length < 3)
{
return Task.FromResult<ProviderCultureResult>(null);
}
if (!LocalePattern.IsMatch(parts[2]))
{
return Task.FromResult<ProviderCultureResult>(null);
}
var culture = parts[2];
return Task.FromResult(new ProviderCultureResult(culture));
}
}
ミドルウェアの登録
var localizationOptions = new RequestLocalizationOptions
{
SupportedCultures = new List<CultureInfo>
{
new CultureInfo("de-DE"),
new CultureInfo("en-US"),
new CultureInfo("en-GB")
},
SupportedUICultures = new List<CultureInfo>
{
new CultureInfo("de-DE"),
new CultureInfo("en-US"),
new CultureInfo("en-GB")
},
DefaultRequestCulture = new RequestCulture("en-US")
};
// Adding our UrlRequestCultureProvider as first object in the list
localizationOptions.RequestCultureProviders.Insert(0, new UrlRequestCultureProvider
{
Options = localizationOptions
});
app.UseRequestLocalization(localizationOptions);
カスタムルートの制約
カスタムルート制約の追加と作成は、[ ルート制約]の例に示されています 。制約を使用すると、カスタムルート制約の使用が簡単になります。
ルートの登録
カスタム制約を使用せずにルートを登録する例
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "api/{culture::regex(^[a-z]{{2}}-[A-Za-z]{{4}}$)}}/{controller}/{id?}");
routes.MapRoute(
name: "default",
template: "api/{controller}/{id?}");
});