지역화는 애플리케이션의 리소스를 애플리케이션이 지원할 각 문화권에 대해 지역화된 버전으로 변환하는 프로세스입니다. 지역화 가능성 검토 단계를 완료한 후에만 지역화 단계를 진행하여 세계화된 애플리케이션이 지역화할 준비가 되었는지 확인해야 합니다.
지역화할 준비가 된 애플리케이션은 모든 사용자 인터페이스 요소를 포함하는 블록과 실행 코드가 포함된 블록이라는 두 가지 개념 블록으로 구분됩니다. 사용자 인터페이스 블록에는 중립 문화권에 대한 문자열, 오류 메시지, 대화 상자, 메뉴, 포함된 개체 리소스 등과 같은 지역화 가능한 사용자 인터페이스 요소만 포함됩니다. 코드 블록에는 지원되는 모든 문화권에서 사용할 애플리케이션 코드만 포함됩니다. 공용 언어 런타임은 애플리케이션의 실행 코드를 해당 리소스와 분리하는 위성 어셈블리 리소스 모델을 지원합니다. 이 모델을 구현하는 방법에 대한 자세한 내용은 .NET의 리소스를 참조하세요.
애플리케이션의 지역화된 각 버전에 대해 대상 문화권에 적합한 언어로 번역된 지역화된 사용자 인터페이스 블록을 포함하는 새 위성 어셈블리를 추가합니다. 모든 문화권의 코드 블록은 동일하게 유지되어야 합니다. 사용자 인터페이스 블록의 지역화된 버전을 코드 블록과 조합하면 지역화된 버전의 애플리케이션이 생성됩니다.
이 기사에서는 IStringLocalizer<T> 및 IStringLocalizerFactory 구현을 사용하는 방법을 배웁니다. 이 문서의 모든 예제 소스 코드는 Microsoft.Extensions.Localization NuGet 패키지와 Microsoft.Extensions.Hosting NuGet 패키지를 사용합니다. 호스팅에 대한 자세한 내용은 .NET 제네릭 호스트를 참조하세요.
리소스 파일
지역화 가능한 문자열을 격리하기 위한 기본 메커니즘은 리소스 파일을 사용하는 것입니다. 리소스 파일은 .resx 파일 확장자를 가진 XML 파일입니다. 리소스 파일은 소비하는 애플리케이션을 실행하기 전에 번역됩니다. 즉, 미사용 시 번역된 콘텐츠를 나타냅니다. 리소스 파일 이름은 가장 일반적으로 로캘 식별자를 포함하며 다음 형식을 사용합니다.
<FullTypeName><.Locale>.resx
위치:
- 특정
<FullTypeName>형식에 대한 지역화 가능한 리소스를 나타냅니다. - 선택적
<.Locale>리소스 파일 내용의 로캘을 나타냅니다.
로캘 지정
로캘은 최소한 언어를 정의해야 하지만 문화권(지역 언어) 및 국가 또는 지역을 정의할 수도 있습니다. 이러한 세그먼트는 일반적으로 문자로 - 구분됩니다. 문화권의 구체성이 추가되면 최적의 일치를 우선적으로 고려하여 "문화 대체" 규칙이 적용됩니다. 로캘은 잘 알려진 언어 태그에 매핑되어야 합니다. 자세한 내용은 CultureInfo.Name를 참조하세요.
문화권 대체 시나리오
지역화된 앱이 다양한 세르비아어 로캘을 지원하고 다음과 같은 리소스 파일이 있다고 MessageService상상해 보세요.
| 파일 | 지역 언어 | 국가 코드 |
|---|---|---|
| MessageService.sr-Cyrl-RS.resx | (키릴 자모, 세르비아) | 알에스 |
| MessageService.sr-Cyrl.resx | 키릴 문자체 | |
| MessageService.sr-Latn-BA.resx | (라틴 문자, 보스니아 및 헤르체고비나) | 학사 |
| MessageService.sr-Latn-ME.resx | (라틴어, 몬테네그로) | 저 |
| MessageService.sr-Latn-RS.resx | (라틴어, 세르비아) | 알에스 |
| MessageService.sr-Latn.resx | 라틴어 | |
| MessageService.sr.resx | † 라틴어 | |
| MessageService.resx |
† 언어의 기본 지역 언어입니다.
앱이 CultureInfo.CurrentCulture으로 실행되며 문화 "sr-Cyrl-RS"으로 지역화를 시도할 때, 파일을 다음 순서로 확인하려고 시도합니다.
- MessageService.sr-Cyrl-RS.resx
- MessageService.sr-Cyrl.resx
- MessageService.sr.resx
- MessageService.resx
앱이 CultureInfo.CurrentCulture로 실행 중이지만 "sr-Latn-BA" 문화 설정이 있는 경우, 다음 순서로 파일을 확인하려고 시도합니다.
- MessageService.sr-Latn-BA.resx
- MessageService.sr-Latn.resx
- MessageService.sr.resx
- MessageService.resx
"문화권 대체" 규칙은 일치하는 항목이 없을 때 로캘을 무시합니다. 즉, 일치 항목을 찾을 수 없는 경우 리소스 파일 번호 4가 선택됩니다. 문화권이 설정된 "fr-FR"경우 지역화는 문제가 될 수 있는 MessageService.resx 파일로 떨어지게 됩니다. 자세한 내용은 리소스 대체 프로세스를 참조하세요.
리소스 조회
리소스 파일은 조회 루틴의 일부로 자동으로 확인됩니다. 프로젝트 파일 이름이 프로젝트의 루트 네임스페이스와 다른 경우 어셈블리 이름이 다를 수 있습니다. 이렇게 하면 리소스 조회가 성공하지 못할 수 있습니다. 이 불일치를 해결하려면 지역화 서비스에 힌트를 제공하는 데 사용합니다 RootNamespaceAttribute . 제공된 경우 리소스 조회 중에 사용됩니다.
예제 프로젝트는 example.csproj로 이름이 지정되어 있으며 example.dll와 example.exe를 생성하지만, Localization.Example 네임스페이스가 사용됩니다.
assembly 수준 특성을 적용하여 이 불일치를 수정합니다.
[assembly: RootNamespace("Localization.Example")]
지역화 서비스 등록
지역화 서비스를 등록하려면 서비스를 구성하는 동안 확장 메서드 중 AddLocalization 하나를 호출합니다. 이렇게 하면 다음 형식의 DI(종속성 주입)가 활성화됩니다.
- Microsoft.Extensions.Localization.IStringLocalizer<T>
- Microsoft.Extensions.Localization.IStringLocalizerFactory
지역화 옵션 구성
오버로드는 AddLocalization(IServiceCollection, Action<LocalizationOptions>) 형식의 setupAction 매개 변수를 Action<LocalizationOptions> 허용합니다. 이를 통해 지역화 옵션을 구성할 수 있습니다.
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
// Omitted for brevity.
자원 파일은 프로젝트의 어느 곳에나 살 수 있지만 성공한 것으로 입증된 일반적인 사례가 있습니다. 더 자주는 아니지만, 최소 저항의 경로가 따릅니다. 앞의 C# 코드는 다음과 같습니다.
- 기본 호스트 앱 작성기를 만듭니다.
- 서비스 컬렉션 호출
AddLocalization에서 LocalizationOptions.ResourcesPath을"Resources"로 지정합니다.
이로 인해 지역화 서비스가 Resources 디렉터리에서 리소스 파일을 찾게 됩니다.
IStringLocalizer<T> 및 IStringLocalizerFactory 사용
지역화 서비스를 등록 (및 필요에 따라 구성)한 후 DI에서 다음 형식을 사용할 수 있습니다.
지역화된 문자열을 반환할 수 있는 메시지 서비스를 만들려면 다음 MessageService을 고려합니다.
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Localization;
namespace Localization.Example;
public sealed class MessageService(IStringLocalizer<MessageService> localizer)
{
[return: NotNullIfNotNull(nameof(localizer))]
public string? GetGreetingMessage()
{
LocalizedString localizedString = localizer["GreetingMessage"];
return localizedString;
}
}
위의 C# 코드에서:
-
IStringLocalizer<MessageService> localizer필드가 선언됩니다. - 기본 생성자는 매개 변수를
IStringLocalizer<MessageService>정의하고 인수로localizer캡처합니다. - 메서드는
GetGreetingMessage을 호출하여 IStringLocalizer.Item[String]을 인수로 전달합니다.
IStringLocalizer 매개 변수가 있는 문자열 리소스도 지원합니다. 다음ParameterizedMessageService을 고려합니다.
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Localization;
namespace Localization.Example;
public class ParameterizedMessageService(IStringLocalizerFactory factory)
{
private readonly IStringLocalizer _localizer =
factory.Create(typeof(ParameterizedMessageService));
[return: NotNullIfNotNull(nameof(_localizer))]
public string? GetFormattedMessage(DateTime dateTime, double dinnerPrice)
{
LocalizedString localizedString = _localizer["DinnerPriceFormat", dateTime, dinnerPrice];
return localizedString;
}
}
위의 C# 코드에서:
-
IStringLocalizer _localizer필드가 선언됩니다. - 기본 생성자는
IStringLocalizerFactory매개 변수를 받아IStringLocalizer유형에서ParameterizedMessageService을 생성하고_localizer필드에 할당합니다. -
GetFormattedMessage메서드는 IStringLocalizer.Item[String, Object[]]를 호출하여"DinnerPriceFormat",dateTime객체, 그리고dinnerPrice를 인수로 전달합니다.
중요합니다
필수 IStringLocalizerFactory 는 아닙니다. 대신, 서비스를 사용할 때 IStringLocalizer<T>을 요구하는 것이 좋습니다.
두 인덱서 모두 암시적 변환이 가능한 IStringLocalizer.Item[]을 반환하며, 이는 LocalizedString에서 로 변환될 수 있습니다.
전부 합치세요
지역화 및 리소스 파일과 함께 메시지 서비스를 모두 사용하여 앱을 예로 들려면 다음 Program.cs 파일을 고려합니다.
using System.Globalization;
using Localization.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using static System.Console;
using static System.Text.Encoding;
[assembly: RootNamespace("Localization.Example")]
OutputEncoding = Unicode;
if (args is [var cultureName])
{
CultureInfo.CurrentCulture =
CultureInfo.CurrentUICulture =
CultureInfo.GetCultureInfo(cultureName);
}
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddLocalization();
builder.Services.AddTransient<MessageService>();
builder.Services.AddTransient<ParameterizedMessageService>();
builder.Logging.SetMinimumLevel(LogLevel.Warning);
using IHost host = builder.Build();
IServiceProvider services = host.Services;
ILogger logger =
services.GetRequiredService<ILoggerFactory>()
.CreateLogger("Localization.Example");
MessageService messageService =
services.GetRequiredService<MessageService>();
logger.LogWarning(
"{Msg}",
messageService.GetGreetingMessage());
ParameterizedMessageService parameterizedMessageService =
services.GetRequiredService<ParameterizedMessageService>();
logger.LogWarning(
"{Msg}",
parameterizedMessageService.GetFormattedMessage(
DateTime.Today.AddDays(-3), 37.63));
await host.RunAsync();
위의 C# 코드에서:
-
RootNamespaceAttribute은
"Localization.Example"을 루트 네임스페이스로 설정합니다. - Console.OutputEncoding는 Encoding.Unicode에 할당됩니다.
- 단일 인수가
args에 전달되면, CultureInfo.CurrentCulture이(가) 주어진 상황에서 CultureInfo.CurrentUICulture과 CultureInfo.GetCultureInfo(String)에arg[0]의 결과가 할당됩니다. - Host은 기본 값으로 만듭니다.
- DI를 위해
MessageService에 등록되는 지역화 서비스,ParameterizedMessageService및IServiceCollection입니다. - 노이즈를 제거하기 위해 로깅은 경고보다 낮은 로그 수준을 무시하도록 구성됩니다.
-
MessageService는IServiceProvider인스턴스로부터 해결되고 생성된 메시지가 기록됩니다. -
ParameterizedMessageService는IServiceProvider인스턴스로부터 해결되어 그 결과로 형식이 지정된 메시지가 기록됩니다.
각 *MessageService 클래스는 각각 단일 항목을 가진 .resx 파일의 집합을 정의합니다. 다음은 MessageService부터 시작하는 리소스 파일에 대한 예제 콘텐츠입니다.
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="GreetingMessage" xml:space="preserve">
<value>Hi friends, the ".NET" developer community is excited to see you here!</value>
</data>
</root>
MessageService.sr-Cyrl-RS.resx:
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="GreetingMessage" xml:space="preserve">
<value>Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!</value>
</data>
</root>
MessageService.sr-Latn.resx:
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="GreetingMessage" xml:space="preserve">
<value>Zdravo prijatelji, ".NET" developer zajednica je uzbuđena što vas vidi ovde!</value>
</data>
</root>
다음은 ParameterizedMessageService부터 시작하는 리소스 파일에 대한 예제 콘텐츠입니다.
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="DinnerPriceFormat" xml:space="preserve">
<value>On {0:D} my dinner cost {1:C}.</value>
</data>
</root>
ParameterizedMessageService.sr-Cyrl-RS.resx:
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="DinnerPriceFormat" xml:space="preserve">
<value>У {0:D} моја вечера је коштала {1:C}.</value>
</data>
</root>
ParameterizedMessageService.sr-Latn.resx:
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="DinnerPriceFormat" xml:space="preserve">
<value>U {0:D} moja večera je koštala {1:C}.</value>
</data>
</root>
팁 (조언)
모든 리소스 파일 XML 주석, 스키마 및 <resheader> 요소는 간결하게 하기 위해 의도적으로 생략됩니다.
예제 실행
다음 예제 실행에서는 지정된 대상 로캘에 따라 다양한 지역화된 출력을 보여 줍니다.
다음을 고려합니다."sr-Latn"
dotnet run --project .\example\example.csproj sr-Latn
warn: Localization.Example[0]
Zdravo prijatelji, ".NET" developer zajednica je uzbuđena što vas vidi ovde!
warn: Localization.Example[0]
U utorak, 03. avgust 2021. moja večera je koštala 37,63 ¤.
.NET CLI를 사용하여 프로젝트를 실행할 때, 인수가 생략되면 기본 시스템 문화권이 사용됩니다. 이 경우 "en-US":
dotnet run --project .\example\example.csproj
warn: Localization.Example[0]
Hi friends, the ".NET" developer community is excited to see you here!
warn: Localization.Example[0]
On Tuesday, August 3, 2021 my dinner cost $37.63.
전달 "sr-Cryl-RS"하면 올바른 해당 리소스 파일이 발견되고 지역화가 적용됩니다.
dotnet run --project .\example\example.csproj sr-Cryl-RS
warn: Localization.Example[0]
Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!
warn: Localization.Example[0]
У уторак, 03. август 2021. моја вечера је коштала 38 RSD.
샘플 애플리케이션은 리소스 파일을 "fr-CA"제공하지 않지만 해당 문화권으로 호출될 때 지역화되지 않은 리소스 파일이 사용됩니다.
경고
문화권을 찾았지만 올바른 리소스 파일은 없으므로 서식이 적용되면 부분 지역화로 끝납니다.
dotnet run --project .\example\example.csproj fr-CA
warn: Localization.Example[0]
Hi friends, the ".NET" developer community is excited to see you here!
warn: Localization.Example[0]
On mardi 3 août 2021 my dinner cost 37,63 $.
참고하십시오
.NET