수색…
JSON 언어 리소스를 사용하여 지역화
ASP.NET 코어에는 앱을 현지화 / 세계화 할 수있는 여러 가지 방법이 있습니다. 귀하의 필요에 맞는 방법을 고르는 것이 중요합니다. 이 예제에서는 .json
파일에서 언어 별 문자열을 읽고이를 메모리에 저장하여 응용 프로그램의 모든 섹션에서 현지화를 제공하고 고성능을 유지하는 다국어 ASP.NET 핵심 응용 프로그램을 만드는 방법을 보여줍니다.
우리가하는 일은 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
파일을 다음과 같이 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));
}
}
}
위의 코드에서 세 개의 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
}
}
앞으로 프로젝트 루트에 세 개의 폴더를 만듭니다.
Models
, Services
및 Languages
. Models
폴더에서 Localization
라는 다른 폴더를 만듭니다.
Services
폴더에서 EFLocalization
이라는 새 .cs 파일을 EFLocalization
. 내용은 다음과 같습니다 (다시 문장을 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
인터페이스를 구현합니다. 중요한 부분은 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();
}
}
}
위의 파일은 언어 자원, 문화로 채워질 모델 DBContext
아니라 EF 코어에서 사용되는 일반적인 DBContext
도 있습니다.
이 모든 작업을 수행하는 데 필요한 마지막 사항은 언어 리소스 파일을 만드는 것입니다. 앱에서 사용할 수있는 다양한 언어의 키 - 값 쌍을 저장하는 데 사용되는 JSON 파일입니다.
이 예제에서 우리의 앱은 두 가지 언어 만 사용할 수 있습니다. 영어와 페르시아어. 각 언어에 대해 두 개의 파일이 필요합니다. 키 - 값 쌍을 포함하는 JSON 파일과 JSON 파일과 이름이 같은 클래스가 포함 된 .cs
파일. 이 클래스에는 JSON 파일을 deserialize하고 반환하는 GetList
라는 하나의 메서드가 있습니다. 이 메소드는 이전에 작성한 EFStringLocalizerFactory
의 생성자에서 호출됩니다.
따라서 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
파일에서 (컨트롤러가 되든 상관 .cs
상관 없습니다) :
// Returns "Welcome" for en-US and "خوش آمدید" for fa-IR
var welcome = Startup._e["Welcome"];
면도기 뷰 파일 ( .cshtml
)에서
<h1>@Startup._e["Welcome"]</h1>
명심할 것 :
- 당신이 액세스하려고하면
Key
JSON 파일에 존재 또는로드되지 않습니다, 당신은 단지에 액세스하려고, 위의 예에서 키 문자를 (얻을 것이다Startup._e["How are you"]
반환합니다How are you
존재하지 않기 때문에 언어 설정에 관계없이 - 당신이 언어의 문자열 값을 변경하는 경우
.json
파일, 앱을 다시 시작해야합니다. 그렇지 않으면 기본값 (키 이름) 만 표시됩니다. 이것은 디버깅하지 않고 앱을 실행할 때 특히 중요합니다. -
appsettings.json
은 앱에서 필요할 수있는 모든 종류의 설정을 저장하는 데 사용할 수 있습니다. -
appsettings.json
파일에서 언어 / 문화 설정 을 변경하려는 경우 앱을 다시 시작 하지 않아도 됩니다. 즉, 앱 인터페이스에 사용자가 런타임에 언어 / 문화를 변경할 수있는 옵션을 가질 수 있습니다.
다음은 최종 프로젝트 구조입니다.
URL 경로를 통해 문화권 요청하기
기본적으로 기본 제공 Request Localization 미들웨어는 query, cookie 또는 Accept-Language
헤더를 통한 culture 설정 만 지원합니다. 이 예제는 경로를 /api/en-US/products
와 같이 경로의 일부로 설정할 수있는 미들웨어를 만드는 방법을 보여줍니다.
이 예제 미들웨어는 로켈이 경로의 두 번째 세그먼트에 있다고 가정합니다.
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?}");
});