ASP.NET Core 앱의 콘텐츠를 지역화할 수 있도록 만들기

히스함 빈 아테야, 데미안 보덴, 바트 칼릭스토, 나뎀 아파나

앱을 지역화하는 한 가지 작업은 지역화 가능한 콘텐츠를 다른 문화권에 맞게 쉽게 바꿀 수 있는 코드로 래핑하는 것입니다.

IStringLocalizer

IStringLocalizerIStringLocalizer<T>는 지역화된 앱을 개발할 때 생산성을 향상하도록 설계되었습니다. IStringLocalizerResourceManagerResourceReader를 사용하여 런타임에 문화권 관련 리소스를 제공합니다. 인터페이스에는 지역화된 문자열을 반환하기 위한 인덱서 및 IEnumerable이 있습니다. IStringLocalizer는 리소스 파일에 기본 언어 문자열을 저장하도록 요구하지 않습니다. 지역화를 대상으로 하는 앱을 개발할 수 있으며 초기 개발에서 리소스 파일을 만들 필요가 없습니다.

다음 코드 예제에서는 지역화를 위해 문자열 "About Title"을 래핑하는 방법을 보여줍니다.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

namespace Localization.Controllers;

[Route("api/[controller]")]
public class AboutController : Controller
{
    private readonly IStringLocalizer<AboutController> _localizer;

    public AboutController(IStringLocalizer<AboutController> localizer)
    {
        _localizer = localizer;
    }

    [HttpGet]
    public string Get()
    {
        return _localizer["About Title"];
    }
}

위의 코드에서 IStringLocalizer<T> 구현은 종속성 주입에서 옵니다. "About Title"의 지역화된 값을 찾을 수 없는 경우 인덱서 키가 반환됩니다. 즉, "About Title" 문자열입니다.

앱에서 기본 언어 리터럴 문자열을 그대로 두고 앱 개발에 집중할 수 있도록 로컬라이저에서 래핑할 수 있습니다. 기본 언어로 앱을 개발하고 먼저 기본 리소스 파일을 만들지 않고 지역화 단계를 준비합니다.

또는 기존의 접근 방식을 사용하고 기본 언어 문자열을 검색하도록 키를 제공할 수 있습니다. 많은 개발자가 기본 언어 .resx 파일을 사용하지 않고 문자열 리터럴을 래핑하는 새로운 워크플로는 앱 지역화의 오버헤드를 줄일 수 있습니다. 다른 개발자는 긴 문자열 리터럴로 작업하기 쉽고 지역화된 문자열을 더 쉽게 업데이트할 수 있으므로 기존 작업 흐름을 선호합니다.

IHtmlLocalizer

HTML을 포함하는 리소스에 대해 IHtmlLocalizer<TResource> 구현을 사용합니다. IHtmlLocalizer HTML은 리소스 문자열에 서식이 지정되어 있지만 리소스 문자열 자체를 HTML로 인코딩하지 않는 인수를 인코딩합니다. 다음 강조 표시된 코드에서 매개 변수 값 name 만 HTML로 인코딩됩니다.

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;

namespace Localization.Controllers;

public class BookController : Controller
{
    private readonly IHtmlLocalizer<BookController> _localizer;

    public BookController(IHtmlLocalizer<BookController> localizer)
    {
        _localizer = localizer;
    }

    public IActionResult Hello(string name)
    {
        ViewData["Message"] = _localizer["<b>Hello</b><i> {0}</i>", name];

        return View();
    }

참고: 일반적으로 HTML이 아닌 텍스트만 지역화합니다.

IStringLocalizerFactory

가장 낮은 수준에서 IStringLocalizerFactory 종속성 주입에서 검색할 수 있습니다.

public class TestController : Controller
{
    private readonly IStringLocalizer _localizer;
    private readonly IStringLocalizer _localizer2;

    public TestController(IStringLocalizerFactory factory)
    {
        var type = typeof(SharedResource);
        var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
        _localizer = factory.Create(type);
        _localizer2 = factory.Create("SharedResource", assemblyName.Name);
    }       

    public IActionResult About()
    {
        ViewData["Message"] = _localizer["Your application description page."] 
            + " loc 2: " + _localizer2["Your application description page."];

        return View();
    }

위의 코드는 두 팩터리 만들기 메서드를 각각 보여 줍니다.

공유 리소스

컨트롤러 또는 영역별로 지역화된 문자열을 분할하거나 컨테이너를 하나만 가질 수 있습니다. 샘플 앱에서 명명 SharedResource 된 표식 클래스는 공유 리소스에 사용됩니다. 표식 클래스는 호출되지 않습니다.

// Dummy class to group shared resources

namespace Localization;

public class SharedResource
{
}

다음 샘플 InfoController 에서는 지역화자와 SharedResource 로캘라이저가 사용됩니다.

public class InfoController : Controller
{
    private readonly IStringLocalizer<InfoController> _localizer;
    private readonly IStringLocalizer<SharedResource> _sharedLocalizer;

    public InfoController(IStringLocalizer<InfoController> localizer,
                   IStringLocalizer<SharedResource> sharedLocalizer)
    {
        _localizer = localizer;
        _sharedLocalizer = sharedLocalizer;
    }

    public string TestLoc()
    {
        string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
                     " Info resx " + _localizer["Hello!"];
        return msg;
    }

지역화 보기

IViewLocalizer 서비스는 보기에 대한 지역화된 문자열을 제공합니다. ViewLocalizer 클래스는 이 인터페이스를 구현하고 보기 파일 경로에서 리소스 위치를 찾습니다. 다음 코드는 IViewLocalizer의 기본 구현을 사용하는 방법을 보여 줍니다.

@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>@Localizer["Use this area to provide additional information."]</p>

IViewLocalizer의 기본 구현은 보기의 파일 이름에 따라 리소스 파일을 찾습니다. 전역 공유 리소스 파일을 사용할 수 있는 옵션이 없습니다. ViewLocalizer 는 로캘라이저를 사용하여 IHtmlLocalizer구현하므로 Razor 지역화된 문자열을 HTML로 인코딩하지 않습니다. 리소스 문자열을 매개 변수화할 수 있으며 IViewLocalizer HTML은 리소스 문자열이 아닌 매개 변수를 인코딩합니다. 다음 Razor 태그를 살펴보세요.

@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]

프랑스어 리소스 파일에는 다음 값이 포함될 수 있습니다.

<i>Hello</i> <b>{0}!</b> <i>Bonjour</i> <b>{0} !</b>

렌더링된 보기는 리소스 파일에서 HTML 표시를 포함합니다.

일반적으로 HTML이 아닌 텍스트만 지역화합니다.

보기에서 공유 리소스 파일을 사용하려면 IHtmlLocalizer<T>를 삽입합니다.

@using Microsoft.AspNetCore.Mvc.Localization
@using Localization.Services

@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedLocalizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>

<h1>@SharedLocalizer["Hello!"]</h1>

DataAnnotations 지역화

DataAnnotations 오류 메시지는 IStringLocalizer<T>로 지역화됩니다. ResourcesPath = "Resources" 옵션을 사용하여 RegisterViewModel의 오류 메시지는 다음 경로 중 하나에 저장될 수 있습니다.

  • Resources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resx
using System.ComponentModel.DataAnnotations;

namespace Localization.ViewModels.Account;

public class RegisterViewModel
{
    [Required(ErrorMessage = "The Email field is required.")]
    [EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required(ErrorMessage = "The Password field is required.")]
    [StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.",
                                                                 MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage =
                            "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

유효성 검사 특성이 아닌 특성은 지역화됩니다.

여러 클래스에 하나의 리소스 문자열을 사용하는 방법

다음 코드는 다중 클래스를 사용하여 유효성 검사 특성에 대해 하나의 리소스 문자열을 사용하는 방법을 보여 줍니다.

    services.AddMvc()
        .AddDataAnnotationsLocalization(options => {
            options.DataAnnotationLocalizerProvider = (type, factory) =>
                factory.Create(typeof(SharedResource));
        });

위의 코드 SharedResource 에서는 유효성 검사 메시지가 저장되는 .resx 파일에 해당하는 클래스입니다. 이 방법을 사용하면 DataAnnotations는 각 클래스에 대한 리소스 대신만 사용합니다 SharedResource.

지역화 서비스 구성

지역화 서비스는 다음에서 구성됩니다.Program.cs

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization에 대한 구현을 IStringLocalizerFactory포함하여 지역화 서비스를 서비스 컨테이너에 IStringLocalizer<T> 추가합니다. 앞의 코드는 리소스 경로도 "리소스"로 설정합니다.

  • AddViewLocalization은 지역화된 보기 파일에 대한 지원을 추가합니다. 이 샘플에서 뷰 지역화는 보기 파일 접미사를 기반으로 합니다. 예를 들어 파일의 "fr"입니다 Index.fr.cshtml .

  • AddDataAnnotationsLocalizationIStringLocalizer 추상화를 통해 지역화된 DataAnnotations 유효성 검사 메시지에 대한 지원을 추가합니다.

참고 항목

십진수 필드에는 십진수 쉼표를 입력하지 못할 수도 있습니다. 소수점으로 쉼표(“,”)를 사용하는 영어가 아닌 로캘 및 미국 영어가 아닌 날짜 형식에 대해 jQuery 유효성 검사를 지원하려면 앱을 전역화하는 단계를 수행해야 합니다. 소수점 추가에 대한 지침은 이 GitHub 설명 4076을 참조하세요.

다음 단계

앱 지역화에는 다음 작업도 포함됩니다.

추가 리소스

작성자: Rick Anderson, Damien Bowden, Bart Calixto, Nadeem AfanaHisham Bin Ateya

앱을 지역화하는 한 가지 작업은 지역화 가능한 콘텐츠를 다른 문화권에 맞게 쉽게 바꿀 수 있는 코드로 래핑하는 것입니다.

IStringLocalizer

IStringLocalizerIStringLocalizer<T>는 지역화된 앱을 개발할 때 생산성을 향상하도록 설계되었습니다. IStringLocalizerResourceManagerResourceReader를 사용하여 런타임에 문화권 관련 리소스를 제공합니다. 인터페이스에는 지역화된 문자열을 반환하기 위한 인덱서 및 IEnumerable이 있습니다. IStringLocalizer는 리소스 파일에 기본 언어 문자열을 저장하도록 요구하지 않습니다. 지역화를 대상으로 하는 앱을 개발할 수 있으며 초기 개발에서 리소스 파일을 만들 필요가 없습니다.

다음 코드 예제에서는 지역화를 위해 문자열 "About Title"을 래핑하는 방법을 보여줍니다.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

namespace Localization.Controllers
{
    [Route("api/[controller]")]
    public class AboutController : Controller
    {
        private readonly IStringLocalizer<AboutController> _localizer;

        public AboutController(IStringLocalizer<AboutController> localizer)
        {
            _localizer = localizer;
        }

        [HttpGet]
        public string Get()
        {
            return _localizer["About Title"];
        }
    }
}

위의 코드에서 IStringLocalizer<T> 구현은 종속성 주입에서 옵니다. "About Title"의 지역화된 값을 찾을 수 없는 경우 인덱서 키가 반환됩니다. 즉, "About Title" 문자열입니다.

앱에서 기본 언어 리터럴 문자열을 그대로 두고 앱 개발에 집중할 수 있도록 로컬라이저에서 래핑할 수 있습니다. 기본 언어로 앱을 개발하고 먼저 기본 리소스 파일을 만들지 않고 지역화 단계를 준비합니다.

또는 기존의 접근 방식을 사용하고 기본 언어 문자열을 검색하도록 키를 제공할 수 있습니다. 많은 개발자가 기본 언어 .resx 파일을 사용하지 않고 문자열 리터럴을 래핑하는 새로운 워크플로는 앱 지역화의 오버헤드를 줄일 수 있습니다. 다른 개발자는 긴 문자열 리터럴로 작업하기 쉽고 지역화된 문자열을 더 쉽게 업데이트할 수 있으므로 기존 작업 흐름을 선호합니다.

IHtmlLocalizer

HTML을 포함하는 리소스에 대해 IHtmlLocalizer<T> 구현을 사용합니다. IHtmlLocalizer HTML은 리소스 문자열에 서식이 지정되어 있지만 리소스 문자열 자체를 HTML로 인코딩하지 않는 인수를 인코딩합니다. 다음 강조 표시된 코드에서 매개 변수 값 name 만 HTML로 인코딩됩니다.

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;

namespace Localization.Controllers
{
    public class BookController : Controller
    {
        private readonly IHtmlLocalizer<BookController> _localizer;

        public BookController(IHtmlLocalizer<BookController> localizer)
        {
            _localizer = localizer;
        }

        public IActionResult Hello(string name)
        {
            ViewData["Message"] = _localizer["<b>Hello</b><i> {0}</i>", name];

            return View();
        }

참고 항목

일반적으로는 HTML이 아닌 텍스트만 지역화합니다.

IStringLocalizerFactory

가장 낮은 수준에서 종속성 주입IStringLocalizerFactory를 얻을 수 있습니다.

{
    public class TestController : Controller
    {
        private readonly IStringLocalizer _localizer;
        private readonly IStringLocalizer _localizer2;

        public TestController(IStringLocalizerFactory factory)
        {
            var type = typeof(SharedResource);
            var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
            _localizer = factory.Create(type);
            _localizer2 = factory.Create("SharedResource", assemblyName.Name);
        }       

        public IActionResult About()
        {
            ViewData["Message"] = _localizer["Your application description page."] 
                + " loc 2: " + _localizer2["Your application description page."];

위의 코드는 두 팩터리 만들기 메서드를 각각 보여 줍니다.

공유 리소스

컨트롤러 또는 영역별로 지역화된 문자열을 분할하거나 컨테이너를 하나만 가질 수 있습니다. 샘플 앱에서 SharedResource라는 더미 클래스는 공유 리소스에 사용됩니다.

// Dummy class to group shared resources

namespace Localization
{
    public class SharedResource
    {
    }
}

일부 개발자는 Startup 클래스를 사용하여 전역 또는 공유 문자열을 포함합니다. 다음 샘플 InfoController 에서는 지역화자와 SharedResource 로캘라이저가 사용됩니다.

public class InfoController : Controller
{
    private readonly IStringLocalizer<InfoController> _localizer;
    private readonly IStringLocalizer<SharedResource> _sharedLocalizer;

    public InfoController(IStringLocalizer<InfoController> localizer,
                   IStringLocalizer<SharedResource> sharedLocalizer)
    {
        _localizer = localizer;
        _sharedLocalizer = sharedLocalizer;
    }

    public string TestLoc()
    {
        string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
                     " Info resx " + _localizer["Hello!"];
        return msg;
    }

지역화 보기

IViewLocalizer 서비스는 보기에 대한 지역화된 문자열을 제공합니다. ViewLocalizer 클래스는 이 인터페이스를 구현하고 보기 파일 경로에서 리소스 위치를 찾습니다. 다음 코드는 IViewLocalizer의 기본 구현을 사용하는 방법을 보여 줍니다.

@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>@Localizer["Use this area to provide additional information."]</p>

IViewLocalizer의 기본 구현은 보기의 파일 이름에 따라 리소스 파일을 찾습니다. 전역 공유 리소스 파일을 사용할 수 있는 옵션이 없습니다. ViewLocalizer 는 로캘라이저를 사용하여 IHtmlLocalizer구현하므로 Razor 지역화된 문자열을 HTML로 인코딩하지 않습니다. 리소스 문자열을 매개 변수화할 수 있으며 IViewLocalizer HTML은 리소스 문자열이 아닌 매개 변수를 인코딩합니다. 다음 Razor 태그를 살펴보세요.

@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]

프랑스어 리소스 파일에는 다음 값이 포함될 수 있습니다.

<i>Hello</i> <b>{0}!</b> <i>Bonjour</i> <b>{0} !</b>

렌더링된 보기는 리소스 파일에서 HTML 표시를 포함합니다.

참고 항목

일반적으로는 HTML이 아닌 텍스트만 지역화합니다.

보기에서 공유 리소스 파일을 사용하려면 IHtmlLocalizer<T>를 삽입합니다.

@using Microsoft.AspNetCore.Mvc.Localization
@using Localization.Services

@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedLocalizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>

<h1>@SharedLocalizer["Hello!"]</h1>

DataAnnotations 지역화

DataAnnotations 오류 메시지는 IStringLocalizer<T>로 지역화됩니다. ResourcesPath = "Resources" 옵션을 사용하여 RegisterViewModel의 오류 메시지는 다음 경로 중 하나에 저장될 수 있습니다.

  • Resources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resx
public class RegisterViewModel
{
    [Required(ErrorMessage = "The Email field is required.")]
    [EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required(ErrorMessage = "The Password field is required.")]
    [StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

ASP.NET Core MVC 1.1.0 이상에서는 유효성 검사되지 않은 특성이 지역화됩니다.

여러 클래스에 하나의 리소스 문자열을 사용하는 방법

다음 코드는 다중 클래스를 사용하여 유효성 검사 특성에 대해 하나의 리소스 문자열을 사용하는 방법을 보여 줍니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddDataAnnotationsLocalization(options => {
            options.DataAnnotationLocalizerProvider = (type, factory) =>
                factory.Create(typeof(SharedResource));
        });
}

위의 코드 SharedResource 에서는 유효성 검사 메시지가 저장되는 .resx 파일에 해당하는 클래스입니다. 이 방법을 사용하면 DataAnnotations는 각 클래스에 대한 리소스 대신만 사용합니다 SharedResource.

지역화 서비스 구성

지역화 서비스는 메서드에서 Startup.ConfigureServices 구성됩니다.

services.AddLocalization(options => options.ResourcesPath = "Resources");

services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization에 대한 구현을 IStringLocalizerFactory포함하여 지역화 서비스를 서비스 컨테이너에 IStringLocalizer<T> 추가합니다. 앞의 코드는 리소스 경로도 "리소스"로 설정합니다.

  • AddViewLocalization은 지역화된 보기 파일에 대한 지원을 추가합니다. 이 샘플에서 뷰 지역화는 보기 파일 접미사를 기반으로 합니다. 예를 들어 파일의 "fr"입니다 Index.fr.cshtml .

  • AddDataAnnotationsLocalizationIStringLocalizer 추상화를 통해 지역화된 DataAnnotations 유효성 검사 메시지에 대한 지원을 추가합니다.

참고 항목

십진수 필드에는 십진수 쉼표를 입력하지 못할 수도 있습니다. 소수점으로 쉼표(“,”)를 사용하는 영어가 아닌 로캘 및 미국 영어가 아닌 날짜 형식에 대해 jQuery 유효성 검사를 지원하려면 앱을 전역화하는 단계를 수행해야 합니다. 소수점 추가에 대한 지침은 이 GitHub 설명 4076을 참조하세요.

다음 단계

앱 지역화에는 다음 작업도 포함됩니다.

추가 리소스