サーチ…


JSON言語リソースを使用したローカリゼーション

ASP.NET Coreには、アプリをローカライズ/グローバル化できるいくつかの方法があります。あなたのニーズに合った方法を選ぶことが重要です。この例では、 .jsonファイルから言語固有の文字列を読み込んでメモリに保存して、アプリケーションのすべてのセクションでローカリゼーションを行い、高性能を維持する多言語ASP.NET Coreアプリケーションを作成する方法を.jsonます。

私たちがやる方法は、 Microsoft.EntityFrameworkCore.InMemoryパッケージを使用することです。

ノート:

  1. このプロジェクトのネームスペースはDigitalShopであり、これをプロジェクトの名前空間に変更することができます
  2. 奇妙なエラーに遭遇しないように新しいプロジェクトを作成することを検討してください
  3. 決してベストプラクティスを示しているわけではありませんので、改善できると思うのであれば親切に編集してください

まず、 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つのフォルダを作成します。

ModelsServicesLanguagesModelsフォルダに、 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?}"); 
});


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow