다음을 통해 공유


애플리케이션에 다국어 사용자 인터페이스 지원 추가

이 자습서에서는 단일 언어 애플리케이션을 사용하여 세계화할 수 있도록 하는 방법을 보여 줍니다. 이 애플리케이션은 Microsoft Visual Studio 내에서 빌드된 완전한 솔루션의 형태입니다.

개요

Windows Vista부터 Windows 운영 체제 자체는 처음부터 다국어 플랫폼으로 빌드되었으며, Windows MUI 리소스 모델을 사용하는 다국어 애플리케이션을 만들 수 있는 추가 지원이 제공됩니다.

이 자습서에서는 다음 측면을 다루며 다국어 애플리케이션에 대한 새로운 지원을 보여 줍니다.

  • 향상된 MUI 플랫폼 지원을 사용하여 다국어 애플리케이션을 쉽게 사용하도록 설정합니다.
  • Windows Vista 이전 버전의 Windows에서 실행되도록 다국어 애플리케이션을 확장합니다.
  • 콘솔 애플리케이션과 같은 특수 다국어 애플리케이션 개발과 관련된 추가 고려 사항을 다룹니다.

이러한 링크를 통해 국제화 및 MUI에 대한 개념을 빠르게 새로 고칠 수 있습니다.

Hello MUI의 이면에 있는 아이디어

기본 프로그래밍 개념을 보여주는 클래식 헬로 월드 애플리케이션에 익숙할 것입니다. 이 자습서에서는 MUI 리소스 모델을 사용하여 단일 언어 애플리케이션을 업데이트하여 Hello MUI라는 다국어 버전을 만드는 방법을 보여 줍니다.

참고 항목

이 자습서의 태스크는 이러한 활동을 수행해야 하는 정밀도와 이러한 작업에 대한 경험이 거의 없는 개발자에게 세부 정보를 설명해야 하기 때문에 자세한 단계에 설명되어 있습니다.

 

Hello MUI 솔루션 설정

다음 단계에서는 Hello MUI 솔루션 만들기를 준비하는 방법을 간략하게 설명합니다.

플랫폼 요구 사항

Windows 7 및 Visual Studio 2008용 Windows SDK(소프트웨어 개발 키트)를 사용하여 이 자습서의 코드 샘플을 컴파일해야 합니다. Windows 7 SDK는 Windows XP, Windows Vista 및 Windows 7에 설치되며 샘플 솔루션은 이러한 운영 체제 버전에서 빌드할 수 있습니다.

이 자습서의 모든 코드 샘플은 x86 및 x64 버전의 Windows XP, Windows Vista 및 Windows 7에서 실행되도록 설계되었습니다. Windows XP에서 작동하지 않는 특정 부분은 적절한 경우 호출됩니다.

필수 조건

  1. Visual Studio 2008을 설치합니다.

    자세한 내용은 Visual Studio 개발자 센터를 참조 하세요.

  2. Windows 7용 Windows SDK를 설치합니다.

    Windows 개발자 센터의 Windows SDK 페이지에서 설치할 수 있습니다. SDK에는 Windows XP부터 최신 버전까지 OS 버전용 애플리케이션을 개발하는 유틸리티가 포함되어 있습니다.

    참고 항목

    패키지를 기본 위치에 설치하지 않거나 시스템 드라이브(일반적으로 C 드라이브)에 설치하지 않는 경우 설치 경로를 기록해 둡다.

     

  3. Visual Studio 명령줄 매개 변수를 구성합니다.

    1. Visual Studio 명령 창을 엽니다.
    2. 형식 집합 경로입니다.
    3. 경로 변수에 Windows 7 SDK의 bin 폴더 경로가 포함되어 있는지 확인합니다. ... Microsoft SDKs\Windows\v7.0\bin
  4. Microsoft NLS 하위 수준 API 추가 기능 패키지를 설치합니다.

    참고 항목

    이 자습서의 컨텍스트에서 이 패키지는 Windows Vista 이전의 Windows 버전에서 실행되도록 애플리케이션을 사용자 지정하는 경우에만 필요합니다. 5단계: Hello MUI 사용자 지정을 참조하세요.

    1. Microsoft 다운로드 센터에서 더 이상 사용할 수 없는 패키지를 다운로드하고 설치합니다. Windows 10 2019년 5월 업데이트 이상 버전에서 ICU 세계화 API를 사용합니다.

    2. Windows SDK와 마찬가지로 패키지를 기본 위치에 설치하지 않거나 시스템 드라이브(일반적으로 C 드라이브)에 설치하지 않는 경우 설치 경로를 기록해 둡다.

    3. 개발 플랫폼이 Windows XP 또는 Windows Server 2003인 경우 Nlsdl.dll이 올바르게 설치되고 등록되었는지 확인합니다.

      1. 설치 경로 위치 아래의 "redist" 폴더로 찾습니다.
      2. nlsdl.x86.exe와 같은 적절한 재배포 가능 Nlsdl.*.exe를 실행합니다. 이 단계에서는 Nlsdl.dll을 설치하고 등록합니다.

    참고 항목

    MUI를 사용하고 Windows Vista 이전의 Windows 버전에서 실행해야 하는 애플리케이션을 개발하는 경우 Nlsdl.dll은 대상 Windows 플랫폼에 있어야 합니다. 대부분의 경우 이는 애플리케이션이 재배포 가능 Nlsdl 설치 관리자를 수행하고 설치해야 하며 단순히 Nlsdl.dll 자체를 복사하는 것이 아니라는 것을 의미합니다.

     

0단계: 하드 코딩된 Hello MUI 만들기

이 자습서는 Hello MUI 애플리케이션의 단일 언어 버전으로 시작합니다. 애플리케이션은 출력에 C++ 프로그래밍 언어, 와이드 문자열 및 MessageBoxW 함수를 사용하는 것으로 가정합니다.

먼저 이 자습서의 모든 애플리케이션을 포함하는 HelloMUI 솔루션뿐만 아니라 초기 GuiStep_0 애플리케이션을 만듭니다.

  1. Visual Studio 2008에서 새 프로젝트를 만듭니다. 다음 설정 및 값을 사용합니다.

    1. 프로젝트 유형: Visual C++에서 Win32를 선택한 다음 Visual Studio 설치 템플릿에서 Win32 프로젝트를 선택합니다.
    2. 이름: GuiStep_0.
    3. 위치: ProjectRootDirectory (이후 단계에서 이 디렉터리를 참조).
    4. 솔루션 이름: HelloMUI.
    5. 솔루션에 대한 디렉터리 만들기를 선택합니다.
    6. Win32 애플리케이션 마법사에서 기본 애플리케이션 유형인 Windows 애플리케이션을 선택합니다.
  2. 프로젝트 스레딩 모델을 구성합니다.

    1. 솔루션 탐색기 프로젝트 GuiStep_0 마우스 오른쪽 단추로 클릭한 다음 속성을 선택합니다.

    2. 프로젝트 속성 페이지 대화 상자에서 다음을 수행합니다.

      1. 왼쪽 위 드롭다운에서 구성을 모든 구성으로 설정합니다.
      2. 구성 속성에서 C/C++를 확장하고, 코드 생성을 선택하고, 런타임 라이브러리: 다중 스레드 디버그(/MTd)를 설정합니다.
  3. GuiStep_0.cpp의 내용을 다음 코드로 바꿉니다.

    // GuiStep_0.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_0.h"
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        MessageBoxW(NULL,L"Hello MUI",L"HelloMUI!",MB_OK | MB_ICONINFORMATION);
        return 0;
    }
    
  4. 애플리케이션을 빌드 및 실행합니다.

위의 간단한 소스 코드는 사용자가 볼 수 있는 모든 출력(이 경우 텍스트 "Hello MUI")을 간단하게 디자인하여 하드 코딩 또는 포함을 선택합니다. 이 선택은 영어 단어 "Hello"를 인식하지 못하는 사용자의 애플리케이션 유틸리티를 제한합니다. MUI는 기술 기반 영어 약어이므로 이 자습서에서는 문자열이 모든 언어에 대해 "MUI"를 다시 기본 가정합니다.

1단계: 기본 리소스 모듈 구현

Microsoft Win32는 애플리케이션 개발자가 UI 리소스 데이터를 애플리케이션 소스 코드와 분리할 수 있는 기능을 오랫동안 노출해 왔습니다. 이러한 분리는 일반적으로 사용자에게 표시되는 문자열, 비트맵, 아이콘, 메시지 및 기타 항목이 실행 파일의 고유 섹션으로 패키지되고 실행 코드와 분리되는 Win32 리소스 모델의 형태로 제공됩니다.

실행 코드와 리소스 데이터 패키징 간의 이러한 분리를 설명하기 위해 이 단계에서 자습서에서는 이전에 하드 코딩된 "Hello" 문자열("en-US" 리소스)을 HelloModule_en_us 프로젝트의 DLL 모듈의 리소스 섹션에 배치합니다.

이 Win32 DLL에는 라이브러리 형식 실행 파일 기능도 포함될 수 있습니다(다른 DLL과 마찬가지로). 그러나 Win32 리소스 관련 측면에 집중하기 위해 런타임 DLL 코드는 dll기본.cpp로 스텁아웃합니다. 이 자습서의 후속 섹션에서는 여기에 빌드되는 HelloModule 리소스 데이터를 활용하고 적절한 런타임 코드도 제공합니다.

Win32 리소스 모듈을 생성하려면 먼저 스텁 아웃된 dll을 사용하여 DLL을 만듭니다기본.

  1. HelloMUI 솔루션에 새 프로젝트를 추가합니다.

    1. [파일] 메뉴에서 [추가], [새 프로젝트]를 선택합니다.
    2. 프로젝트 유형: Win32 프로젝트입니다.
    3. 이름: HelloModule_en_us.
    4. 위치: ProjectRootDirectory\HelloMUI.
    5. Win32 애플리케이션 마법사에서 애플리케이션 유형 DLL을 선택합니다.
  2. 이 프로젝트의 스레딩 모델을 구성합니다.

    1. 솔루션 탐색기 프로젝트 HelloModule_en_us 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

    2. 프로젝트의 속성 페이지 대화 상자에서 다음을 수행합니다.

      1. 왼쪽 위 드롭다운에서 구성을 모든 구성으로 설정합니다.
      2. 구성 속성에서 C/C++를 확장하고, 코드 생성을 선택하고, 런타임 라이브러리: 다중 스레드 디버그(/MTd)를 설정합니다.
  3. dll기본.cpp를 검사합니다. (여기서와 같이 생성된 코드에 UNREFERENCED_PARAMETER 매크로를 추가하여 경고 수준 4에서 컴파일할 수 있도록 할 수 있습니다.)

    // dllmain.cpp : Defines the entry point for the DLL application.
    #include "stdafx.h"
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        UNREFERENCED_PARAMETER(hModule);
        UNREFERENCED_PARAMETER(lpReserved);
    
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    
  4. 프로젝트에 리소스 정의 파일 HelloModule.rc를 추가합니다.

    1. 프로젝트 HelloModule_en_us 아래에서 Resource Files 폴더를 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음 새 항목을 선택합니다.

    2. 새 항목 추가 대화 상자에서 다음을 선택합니다.

      1. 범주: Visual C++에서 리소스를 선택한 다음, "Visual Studio 설치된 템플릿" 아래에서 리소스 파일(.rc)을 선택합니다.
      2. 이름: HelloModule.
      3. 위치: 기본값을 적용합니다.
      4. 추가를 클릭합니다.
    3. 새 HelloModule.rc 파일을 유니코드로 저장하도록 지정합니다.

      1. 솔루션 탐색기 HelloModule.rc를 마우스 오른쪽 단추로 클릭하고 코드 보기를 선택합니다.
      2. 파일이 이미 열려 있음을 알리는 메시지가 표시되고 파일을 닫을지 묻는 메시지가 표시되면 [예]를 클릭합니다.
      3. 파일이 텍스트로 표시되면 메뉴 선택 파일 및 고급 저장 옵션을 만듭니다.
      4. 인코딩에서 유니코드 - 코드 페이지 1200을 지정합니다.
      5. 확인을 클릭합니다.
      6. HelloModule.rc를 저장하고 닫습니다.
    4. "Hello" 문자열을 포함하는 문자열 테이블을 추가합니다.

      1. 리소스 보기에서 HelloModule.rc를 마우스 오른쪽 단추로 클릭하고 리소스 추가를 선택합니다.

      2. 문자열 테이블을 선택합니다.

      3. 새로 만들기를 클릭합니다.

      4. 문자열 테이블에 문자열을 추가합니다.

        1. ID: HELLO_MUI_STR_0.
        2. 값: 0.
        3. 캡션: 안녕하세요.

      이제 HelloModule.rc를 텍스트로 보는 경우 리소스별 소스 코드의 다양한 부분이 표시됩니다. 가장 큰 관심사는 "Hello" 문자열을 설명하는 섹션입니다.

      /////////////////////////////////////////////////////////////////////////////
      //
      // String Table
      //
      
      STRINGTABLE 
      BEGIN
          HELLO_MUI_STR_0         "Hello"
      END
      

      이 "Hello" 문자열은 애플리케이션에서 지원하려는 모든 언어로 지역화(즉, 번역)해야 하는 리소스입니다. 예를 들어 다음 단계에서 만들 HelloModule_ta_in 프로젝트에는 "ta-IN"에 대한 자체 지역화된 버전의 HelloModule.rc가 포함됩니다.

      /////////////////////////////////////////////////////////////////////////////
      //
      // String Table
      //
      
      STRINGTABLE 
      BEGIN
          HELLO_MUI_STR_0         "வணக்கம்"
      END
      
    5. 오류가 없는지 확인하기 위해 HelloModule_en_us 프로젝트를 빌드합니다.

  5. HelloMUI 솔루션에서 6개의 프로젝트를 더 만들어(또는 원하는 만큼) 추가 언어에 대한 6개의 리소스 DLL을 추가로 만듭니다. 다음 표의 값을 사용합니다.

    프로젝트 이름 .rc 파일의 이름 문자열 ID 문자열 값 문자열 캡션
    HelloModule_de_de HelloModule HELLO_MUI_STR_0 0 안녕
    HelloModule_es_es HelloModule HELLO_MUI_STR_0 0 안녕
    HelloModule_fr_fr HelloModule HELLO_MUI_STR_0 0 Bonjour
    HelloModule_hi_in HelloModule HELLO_MUI_STR_0 0 नमस्
    HelloModule_ru_ru HelloModule HELLO_MUI_STR_0 0 Здравствуйте
    HelloModule_ta_in HelloModule HELLO_MUI_STR_0 0 வணக்கம்

     

.rc 파일 구조 및 구문에 대한 자세한 내용은 리소스 파일 정보(About Resource Files)를 참조하세요.

2단계: 기본 리소스 모듈 빌드

이전 리소스 모델을 사용하여 7개의 HelloModule 프로젝트 중 하나를 빌드하면 7개의 개별 DLL이 생성됩니다. 각 DLL에는 적절한 언어로 지역화된 단일 문자열이 있는 리소스 섹션이 포함됩니다. 기록 Win32 리소스 모델에 적합하지만 이 디자인은 MUI를 활용하지 않습니다.

Windows Vista SDK 이상에서 MUI는 실행 파일을 소스 코드 및 지역화 가능한 콘텐츠 모듈로 분할하는 기능을 제공합니다. 5단계의 뒷부분에서 설명하는 추가 사용자 지정을 사용하면 Windows Vista 이전 버전에서 다국어 지원을 실행할 수 있도록 애플리케이션을 사용할 수 있습니다.

Windows Vista부터 실행 코드에서 리소스를 분할하는 데 사용할 수 있는 기본 메커니즘은 다음과 같습니다.

  • 특정 스위치와 함께 rc.exe(RC 컴파일러) 사용 또는
  • 빌드 후 스타일 분할 도구(muirct.exe)를 사용합니다.

자세한 내용은 리소스 유틸리티를 참조하세요.

편의상 이 자습서에서는 muirct.exe를 사용하여 "Hello MUI" 실행 파일을 분할합니다.

다양한 언어 리소스 모듈 분할: 개요

여러 부분으로 구성된 프로세스는 DLL을 하나의 실행 가능한 HelloModule.dll로 분할하고 이 자습서 내에서 지원되는 7개 언어 각각에 대해 HelloModule.dll.mui를 분할하는 데 사용됩니다. 이 개요에서는 필요한 단계를 설명합니다. 다음 섹션에서는 이러한 단계를 수행하는 명령 파일을 제공합니다.

먼저 다음 명령을 사용하여 영어 전용 HelloModule.dll 모듈을 분할합니다.

mkdir .\en-US
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0409 -g 0x0407 .\HelloModule_en_us.dll .\HelloModule.dll .\en-US\HelloModule.dll.mui

위의 명령줄은 DoReverseMuiLoc.rcconfig 구성 파일을 사용합니다. 이 유형의 구성 파일은 일반적으로 muirct.exe에서 LN(언어 중립) DLL과 언어 종속 .mui 파일 간에 리소스를 분할하는 데 사용됩니다. 이 경우 DoReverseMuiLoc.rcconfig xml 파일(다음 섹션에 나열됨)은 많은 리소스 유형을 나타내지만 모두 "localizedResources" 또는 .mui 파일 범주에 속하며 언어 중립적 범주에는 리소스가 없습니다. 리소스 구성 파일을 준비하는 방법에 대한 자세한 내용은 리소스 구성 파일 준비를 참조 하세요.

영어 문자열 "Hello"가 포함된 HelloModule.dll.mui 파일을 만드는 것 외에도, muirct.exe는 분할하는 동안 MUI 리소스를 HelloModule.dll 모듈에 포함합니다. 런타임에 언어별 HelloModule.dll.mui 모듈에서 적절한 리소스를 제대로 로드하려면 각 .mui 파일에는 기준 언어 중립적 LN 모듈의 검사와 일치하도록 검사sums가 고정되어 있어야 합니다. 이 작업은 다음과 같은 명령을 통해 수행됩니다.

muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui

마찬가지로, muirct.exe는 다른 각 언어에 대한 HelloModule.dll.mui 파일을 만들기 위해 호출됩니다. 그러나 이러한 경우 처음 만든 DLL만 필요하므로 언어 중립적인 DLL은 카드 해제됩니다. 스페인어 및 프랑스어 리소스를 처리하는 명령은 다음과 같습니다.

mkdir .\es-ES
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0C0A -g 0x0407 .\HelloModule_es_es.dll .\HelloModule_discard.dll .\es-ES\HelloModule.dll.mui
muirct.exe -c HelloModule.dll -e es-ES\HelloModule.dll.mui

mkdir .\fr-FR
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x040C -g 0x0407 .\HelloModule_fr_fr.dll .\HelloModule_discard.dll .\fr-FR\HelloModule.dll.mui
muirct.exe -c HelloModule.dll -e fr-FR\HelloModule.dll.mui

위의 muirct.exe 명령줄에서 가장 중요한 것 중 하나는 -x 플래그를 사용하여 대상 언어 ID를 지정한다는 것입니다. muirct.exe에 제공되는 값은 언어별 HelloModule.dll 모듈마다 다릅니다. 이 언어 값은 중앙에 있으며, 분할 중에 .mui 파일을 적절하게 표시하기 위해 muirct.exe에서 사용됩니다. 잘못된 값은 런타임에 특정 .mui 파일에 대한 리소스 로드 실패를 생성합니다. 언어 이름을 LCID에 매핑하는 자세한 내용은 언어 식별자 상수 및 문자열을 참조하세요.

각 분할 .mui 파일은 언어 중립적인 HelloModule.dll이 상주하는 디렉터리 바로 아래에 해당 언어 이름에 해당하는 디렉터리에 배치됩니다. .mui 파일 배치에 대한 자세한 내용은 애플리케이션 배포를 참조하세요.

다양한 언어 리소스 모듈 분할: 파일 만들기

이 자습서에서는 다양한 DLL을 분할하는 명령이 포함된 명령 파일을 만들고 수동으로 호출합니다. 실제 개발 작업에서는 이러한 명령을 HelloMUI 솔루션에 빌드 전 또는 빌드 후 이벤트로 포함시켜 빌드 오류 가능성을 줄일 수 있지만 이 자습서의 범위를 벗어납니다.

디버그 빌드에 대한 파일을 만듭니다.

  1. 다음 명령을 포함하는 명령 파일 DoReverseMuiLoc.cmd를 만듭니다.

    mkdir .\en-US
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0409 -g 0x0407 .\HelloModule_en_us.dll .\HelloModule.dll .\en-US\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui
    
    mkdir .\de-DE
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0407 -g 0x0407 .\HelloModule_de_de.dll .\HelloModule_discard.dll .\de-DE\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e de-DE\HelloModule.dll.mui
    
    mkdir .\es-ES
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0C0A -g 0x0407 .\HelloModule_es_es.dll .\HelloModule_discard.dll .\es-ES\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e es-ES\HelloModule.dll.mui
    
    mkdir .\fr-FR
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x040C -g 0x0407 .\HelloModule_fr_fr.dll .\HelloModule_discard.dll .\fr-FR\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e fr-FR\HelloModule.dll.mui
    
    mkdir .\hi-IN
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0439 -g 0x0407 .\HelloModule_hi_in.dll .\HelloModule_discard.dll .\hi-IN\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e hi-IN\HelloModule.dll.mui
    
    mkdir .\ru-RU
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0419 -g 0x0407 .\HelloModule_ru_ru.dll .\HelloModule_discard.dll .\ru-RU\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e ru-RU\HelloModule.dll.mui
    
    mkdir .\ta-IN
    muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0449 -g 0x0407 .\HelloModule_ta_in.dll .\HelloModule_discard.dll .\ta-IN\HelloModule.dll.mui
    muirct.exe -c HelloModule.dll -e ta-IN\HelloModule.dll.mui
    pause
    
  2. 다음 줄을 포함하는 xml 파일 DoReverseMuiLoc.rcconfig를 만듭니다.

    <?xml version="1.0" encoding="utf-8"?>
        <localization>
            <resources>
                <win32Resources fileType="Application">
                    <neutralResources>
                    </neutralResources>
                    <localizedResources>
                        <resourceType typeNameId="#1"/>
                        <resourceType typeNameId="#10"/>
                        <resourceType typeNameId="#1024"/>
                        <resourceType typeNameId="#11"/>
                        <resourceType typeNameId="#12"/>
                        <resourceType typeNameId="#13"/>
                        <resourceType typeNameId="#14"/>
                        <resourceType typeNameId="#15"/>
                        <resourceType typeNameId="#16"/>
                        <resourceType typeNameId="#17"/>
                        <resourceType typeNameId="#18"/>
                        <resourceType typeNameId="#19"/>
                        <resourceType typeNameId="#2"/>
                        <resourceType typeNameId="#20"/>
                        <resourceType typeNameId="#2110"/>
                        <resourceType typeNameId="#23"/>
                        <resourceType typeNameId="#240"/>
                        <resourceType typeNameId="#3"/>
                        <resourceType typeNameId="#4"/>
                        <resourceType typeNameId="#5"/>
                        <resourceType typeNameId="#6"/>
                        <resourceType typeNameId="#7"/>
                        <resourceType typeNameId="#8"/>
                        <resourceType typeNameId="#9"/>
                        <resourceType typeNameId="HTML"/>
                        <resourceType typeNameId="MOFDATA"/>
                    </localizedResources>
                </win32Resources>
            </resources>
        </localization>
    
  3. DoReverseMuiLoc.cmd 및 DoReverseMuiLoc.rcconfig를 ProjectRootDirectory\HelloMUI\Debug에 복사합니다.

  4. Visual Studio 2008 명령 프롬프트를 열고 디버그 디렉터리로 이동합니다.

  5. DoReverseMuiLoc.cmd를 실행합니다.

릴리스 빌드를 만들 때 동일한 DoReverseMuiLoc.cmd 및 DoReverseMuiLoc.rcconfig 파일을 릴리스 디렉터리에 복사하고 이 디렉터리에서 명령 파일을 실행합니다.

3단계: 리소스에 정통한 "Hello MUI" 만들기

위의 초기 하드 코딩된 GuiStep_0.exe 예제를 기반으로 Win32 리소스 모델을 통합하도록 선택하여 애플리케이션의 범위를 여러 언어 사용자로 확장할 수 있습니다. 이 단계에서 제공되는 새 런타임 코드에는 모듈 로드(LoadLibraryEx) 및 문자열 검색(LoadString) 논리가 포함됩니다.

  1. 다음 설정과 값을 사용하여 HelloMUI 솔루션에 새 프로젝트를 추가합니다(메뉴 선택 파일, 추가 및 새 프로젝트 사용).

    1. 프로젝트 유형: Win32 프로젝트입니다.
    2. 이름: GuiStep_1.
    3. 위치: 기본값을 적용합니다.
    4. Win32 애플리케이션 마법사에서 기본 애플리케이션 유형인 Windows 애플리케이션을 선택합니다.
  2. Visual Studio 내에서 실행되도록 이 프로젝트를 설정하고 스레딩 모델을 구성합니다.

    1. 솔루션 탐색기 프로젝트 GuiStep_1 마우스 오른쪽 단추로 클릭하고 시작 프로젝트로 설정을 선택합니다.

    2. 다시 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

    3. 프로젝트의 속성 페이지 대화 상자에서 다음을 수행합니다.

      1. 왼쪽 위 드롭다운에서 구성을 모든 구성으로 설정합니다.
      2. 구성 속성에서 C/C++를 확장하고, 코드 생성을 선택하고, 런타임 라이브러리: 다중 스레드 디버그(/MTd)를 설정합니다.
  3. GuiStep_1.cpp의 내용을 다음 코드로 바꿉니다.

    // GuiStep_1.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_1.h"
    #include "..\HelloModule_en_us\resource.h"
    
    #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2)
    #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2)
    #define HELLO_MODULE_CONTRIVED_FILE_PATH  (L"HelloModule.dll")
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        // The following code presents a hypothetical, yet common use pattern of MUI technology
        WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER];
    
        // 1. Basic application obtains access to the proper resource container 
        // for standard Win32 resource loading this is normally a PE module - use LoadLibraryEx
        // LoadLibraryEx is the preferred alternative for resource modules as used below because it
        // provides increased security and performance over that of LoadLibrary
        HMODULE resContainer = LoadLibraryExW(HELLO_MODULE_CONTRIVED_FILE_PATH,NULL,LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
        if(!resContainer)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        // 2. Application parses the resource container to find the appropriate item
        WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER];
        if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            FreeLibrary(resContainer);
            return 1; // exit
        }
    
        // 3. Application presents the discovered resource to the user via UI
        swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello);
        MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION);
    
        // 4. Application cleans up memory associated with the resource container after this item is no longer needed.
        if(!FreeLibrary(resContainer))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        return 0;
    }
    
  4. 애플리케이션을 빌드 및 실행합니다. 출력은 현재 컴퓨터의 표시 언어로 설정된 언어로 표시됩니다(빌드한 7개 언어 중 하나인 경우).

4단계: "Hello MUI" 전역화

이전 예제에서는 출력을 다른 언어로 표시할 수 있지만 여러 영역에서는 부족합니다. 아마도 가장 눈에 띄는 것은 응용 프로그램은 Windows 운영 체제 자체와 비교할 때 언어의 작은 하위 집합에서만 사용할 수 있다는 것입니다. 예를 들어 이전 단계의 GuiStep_1 애플리케이션이 일본어 Windows 빌드에 설치된 경우 리소스 위치 오류가 발생할 수 있습니다.

이 상황을 해결하기 위해 두 가지 기본 옵션이 있습니다.

  • 미리 결정된 최종 대체 언어의 리소스가 포함되어 있는지 확인합니다.
  • 최종 사용자가 애플리케이션에서 특별히 지원되는 언어 하위 집합 중에서 해당 언어 기본 설정을 구성할 수 있는 방법을 제공합니다.

이러한 옵션 중에서 궁극적인 대체 언어를 제공하는 것은 매우 바람직하며 위에서 본 것처럼 -g 플래그를 muirct.exe에 전달하여 이 자습서에서 구현됩니다. 이 플래그는 특정 언어(de-DE/0x0407)를 언어 중립 dll 모듈(HelloModule.dll)과 연결된 궁극적인 대체 언어로 만들도록 muirct.exe에 지시합니다. 런타임에 이 매개 변수는 사용자에게 표시할 텍스트를 생성하는 최후의 수단으로 사용됩니다. 최종 대체 언어를 찾을 수 없고 언어 중립 이진 파일에서 적절한 리소스를 사용할 수 없는 경우 리소스 로드가 실패합니다. 따라서 애플리케이션에서 발생할 수 있는 시나리오를 신중하게 결정하고 그에 따라 궁극적인 대체 언어를 계획해야 합니다.

구성 가능한 언어 기본 설정을 허용하고 이 사용자 정의 계층 구조를 기반으로 리소스를 로드하는 다른 옵션은 고객 만족도를 크게 높일 수 있습니다. 아쉽게도 애플리케이션 내에서 필요한 기능도 복잡합니다.

자습서의 이 단계에서는 간소화된 텍스트 파일 메커니즘을 사용하여 사용자 지정 사용자 언어 구성을 사용하도록 설정합니다. 텍스트 파일은 런타임에 애플리케이션에 의해 구문 분석되고, 구문 분석되고 유효성이 검사된 언어 목록은 사용자 지정 대체 목록을 설정하는 데 사용됩니다. 사용자 지정 대체 목록이 설정되면 Windows API는 이 목록에 명시된 언어 우선 순위에 따라 리소스를 로드합니다. 코드의 re기본der는 이전 단계에서 찾은 것과 비슷합니다.

  1. 다음 설정과 값을 사용하여 HelloMUI 솔루션에 새 프로젝트를 추가합니다(메뉴 선택 파일, 추가 및 새 프로젝트 사용).

    1. 프로젝트 유형: Win32 프로젝트입니다.
    2. 이름: GuiStep_2.
    3. 위치: 기본값을 적용합니다.
    4. Win32 애플리케이션 마법사에서 기본 애플리케이션 유형인 Windows 애플리케이션을 선택합니다.
  2. Visual Studio 내에서 실행되도록 이 프로젝트를 설정하고 스레딩 모델을 구성합니다.

    1. 솔루션 탐색기 프로젝트 GuiStep_2 마우스 오른쪽 단추로 클릭하고 시작 프로젝트로 설정을 선택합니다.

    2. 다시 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

    3. 프로젝트의 속성 페이지 대화 상자에서 다음을 수행합니다.

      1. 왼쪽 위 드롭다운에서 구성을 모든 구성으로 설정합니다.
      2. 구성 속성에서 C/C++를 확장하고, 코드 생성을 선택하고, 런타임 라이브러리: 다중 스레드 디버그(/MTd)를 설정합니다.
  3. GuiStep_2.cpp의 내용을 다음 코드로 바꿉니다.

    // GuiStep_2.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_2.h"
    #include <strsafe.h>
    #include "..\HelloModule_en_us\resource.h"
    
    #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2)
    #define USER_CONFIGURATION_STRING_BUFFER (((LOCALE_NAME_MAX_LENGTH+1)*5)+1)
    #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2)
    #define HELLO_MODULE_CONTRIVED_FILE_PATH  (L"HelloModule.dll")
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize);
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        // The following code presents a hypothetical, yet common use pattern of MUI technology
        WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER];
    
        // 1. Application starts by applying any user defined language preferences
        // (language setting is potentially optional for an application that wishes to strictly use OS system language fallback)
        // 1a. Application looks in pre-defined location for user preferences (registry, file, web, etc.)
        WCHAR userLanguagesString[USER_CONFIGURATION_STRING_BUFFER*2];
        if(!GetMyUserDefinedLanguages(userLanguagesString,USER_CONFIGURATION_STRING_BUFFER*2))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to find the user defined language configuration, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1b. Application converts the user defined 'readable' languages to the proper multi-string 'less readable' language name format
        WCHAR userLanguagesMultiString[USER_CONFIGURATION_STRING_BUFFER];
        if(!ConvertMyLangStrToMultiLangStr(userLanguagesString,userLanguagesMultiString,USER_CONFIGURATION_STRING_BUFFER))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to convert the user defined language configuration to multi-string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1c. Application now sets the appropriate fallback list
        DWORD langCount = 0;
        // next commented out line of code could be used on Windows 7 and later
        // using SetProcessPreferredUILanguages is recomended for new applications (esp. multi-threaded applications)
    //    if(!SetProcessPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&langCount) || langCount == 0)
        // the following line of code is supported on Windows Vista and later
        if(!SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&langCount) || langCount == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to set the user defined languages, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // NOTES on step #1:
        // an application developer that makes the assumption the fallback list provided by the
        // system / OS is entirely sufficient may or may not be making a good assumption based 
        // mostly on:
        // A. your choice of languages installed with your application
        // B. the languages on the OS at application install time
        // C. the OS users propensity to install/uninstall language packs
        // D. the OS users propensity to change laguage settings
    
        // 2. Application obtains access to the proper resource container 
        // for standard Win32 resource loading this is normally a PE module - use LoadLibraryEx
        // LoadLibraryEx is the preferred alternative for resource modules as used below because it
        // provides increased security and performance over that of LoadLibrary
        HMODULE resContainer = LoadLibraryExW(HELLO_MODULE_CONTRIVED_FILE_PATH,NULL,LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
        if(!resContainer)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        // 3. Application parses the resource container to find the appropriate item
        WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER];
        if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            FreeLibrary(resContainer);
            return 1; // exit
        }
    
        // 4. Application presents the discovered resource to the user via UI
        swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello);
        MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION);
    
        // 5. Application cleans up memory associated with the resource container after this item is no longer needed.
        if(!FreeLibrary(resContainer))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        return 0;
    }
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize)
    {
        BOOL rtnVal = FALSE;
        // very simple implementation - assumes that first 'langStrSize' characters of the 
        // L".\\langs.txt" file comprises a string of one or more languages
        HANDLE langConfigFileHandle = CreateFileW(L".\\langs.txt", GENERIC_READ, 0, 
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if(langConfigFileHandle != INVALID_HANDLE_VALUE)
        {
            // clear out the input variables
            DWORD bytesActuallyRead = 0;
            if(ReadFile(langConfigFileHandle,langStr,langStrSize*sizeof(WCHAR),&bytesActuallyRead,NULL) && bytesActuallyRead > 0)
            {
                rtnVal = TRUE;
                DWORD nullIndex = (bytesActuallyRead/sizeof(WCHAR) < langStrSize) ? bytesActuallyRead/sizeof(WCHAR) : langStrSize;
                langStr[nullIndex] = L'\0';
            }
            CloseHandle(langConfigFileHandle);
        }
        return rtnVal;
    }
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize)
    {
        BOOL rtnVal = FALSE;
        size_t strLen = 0;
        rtnVal = SUCCEEDED(StringCchLengthW(langStr,USER_CONFIGURATION_STRING_BUFFER*2,&strLen));
        if(rtnVal && strLen > 0 && langMultiStr && langMultiStrSize > 0)
        {
            WCHAR * langMultiStrPtr = langMultiStr;
            WCHAR * last = langStr + (langStr[0] == 0xFEFF ? 1 : 0);
            WCHAR * context = last;
            WCHAR * next = wcstok_s(last,L",; :",&context);
            while(next && rtnVal)
            {
                // make sure you validate the user input
                if(SUCCEEDED(StringCchLengthW(last,LOCALE_NAME_MAX_LENGTH,&strLen)) && 
                    IsValidLocaleName(next))
                {
                    langMultiStrPtr[0] = L'\0';
                    rtnVal &= SUCCEEDED(StringCchCatW(langMultiStrPtr,(langMultiStrSize - (langMultiStrPtr - langMultiStr)),next));
                    langMultiStrPtr += strLen + 1;
                }
                next = wcstok_s(NULL,L",; :",&context);
                if(next)
                    last = next;
            }
            if(rtnVal && (langMultiStrSize - (langMultiStrPtr - langMultiStr))) // make sure there is a double null term for the multi-string
            {
                langMultiStrPtr[0] = L'\0';
            }
            else // fail and guard anyone whom might use the multi-string
            {
                langMultiStr[0] = L'\0';
                langMultiStr[1] = L'\0';
            }
        }
        return rtnVal;
    }
    
  4. 다음 줄을 포함하는 유니코드 텍스트 파일 langs.txt를 만듭니다.

    hi-IN ta-IN ru-RU fr-FR es-ES en-US
    

    참고 항목

    파일을 유니코드로 저장해야 합니다.

     

    langs.txt를 프로그램이 실행되는 디렉터리에 복사합니다.

    • Visual Studio 내에서 실행되는 경우 ProjectRootDirectory\HelloMUI\GuiStep_2 복사합니다.
    • Windows 탐색기에서 실행하는 경우 GuiStep_2.exe와 동일한 디렉터리에 복사합니다.
  5. 프로젝트를 빌드하고 실행합니다. 목록의 맨 앞에 다른 언어가 표시되도록 langs.txt를 편집해 보세요.

5단계: "Hello MUI" 사용자 지정

이 자습서 내에서 지금까지 멘션 많은 런타임 기능은 Windows Vista 이상에서만 사용할 수 있습니다. 애플리케이션이 Windows XP와 같은 하위 Windows 운영 체제 버전에서 작동하도록 하여 리소스를 지역화하고 분할하는 데 투자한 노력을 다시 사용할 수 있습니다. 이 프로세스에는 다음 두 가지 주요 영역에서 이전 예제를 조정하는 작업이 포함됩니다.

  • Windows Vista 이전 리소스 로드 함수(예: LoadString, LoadIcon, LoadBitmap, FormatMessage 등)는 MUI를 인식하지 않습니다. 분할 리소스(LN 및 .mui 파일)와 함께 제공되는 애플리케이션은 다음 두 함수 중 하나를 사용하여 리소스 모듈을 로드해야 합니다.

    • 애플리케이션을 Windows Vista 이상에서만 실행하려면 LoadLibraryEx사용하여 리소스 모듈을 로드해야 합니다.
    • Windows Vista 이전 버전과 Windows Vista 이상 버전에서 애플리케이션을 실행하려면 Windows 7 SDK에서 제공되는 특정 하위 수준 함수인 LoadMUILibrary를 사용해야 합니다.
  • Windows Vista 이전 버전의 Windows 운영 체제에서 제공되는 언어 관리 및 언어 대체 순서 지원은 Windows Vista 이상 버전과 크게 다릅니다. 이러한 이유로 사용자가 구성한 언어 대체를 허용하는 애플리케이션은 해당 언어 관리 방법을 조정해야 합니다.

    • 애플리케이션을 Windows Vista 이상에서만 실행하려면 SetThreadPreferredUILanguages를 사용하여 언어 목록을 설정하는 것으로 충분합니다 .
    • 애플리케이션을 모든 Windows 버전에서 실행하려면 사용자가 구성한 언어 목록을 반복하고 원하는 리소스 모듈을 검색하기 위해 다운레벨 플랫폼에서 실행되는 코드를 생성해야 합니다. 이 단계는 이 단계의 뒷부분에서 제공된 코드의 섹션 1c 및 2에서 확인할 수 있습니다.

모든 버전의 Windows에서 지역화된 리소스 모듈을 사용할 수 있는 프로젝트를 만듭니다.

  1. 다음 설정과 값을 사용하여 HelloMUI 솔루션에 새 프로젝트를 추가합니다(메뉴 선택 파일, 추가 및 새 프로젝트 사용).

    1. 프로젝트 유형: Win32 프로젝트입니다.
    2. 이름: GuiStep_3.
    3. 위치: 기본값을 적용합니다.
    4. Win32 애플리케이션 마법사에서 기본 애플리케이션 유형인 Windows 애플리케이션을 선택합니다.
  2. Visual Studio 내에서 실행되도록 이 프로젝트를 설정하고 스레딩 모델을 구성합니다. 또한 필요한 헤더 및 라이브러리를 추가하도록 구성합니다.

    참고 항목

    이 자습서에 사용된 경로는 Windows 7 SDK 및 Microsoft NLS 다운레벨 API 패키지가 기본 디렉터리에 설치되었다고 가정합니다. 그렇지 않은 경우 경로를 적절하게 수정합니다.

     

    1. 솔루션 탐색기 프로젝트 GuiStep_3 마우스 오른쪽 단추로 클릭하고 시작 프로젝트로 설정을 선택합니다.

    2. 다시 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

    3. 프로젝트의 속성 페이지 대화 상자에서 다음을 수행합니다.

      1. 왼쪽 위 드롭다운에서 구성을 모든 구성으로 설정합니다.

      2. 구성 속성에서 C/C++를 확장하고, 코드 생성을 선택하고, 런타임 라이브러리: 다중 스레드 디버그(/MTd)를 설정합니다.

      3. 일반을 선택하고 추가 포함 디렉터리에 추가합니다.

        • "C:\Microsoft NLS Downlevel API\Include".
      4. 언어를 선택하고 wchar_t 기본 제공 형식으로 지정합니다( 아니요(/Zc:wchar_t-).

      5. 고급을 선택하고 호출 규칙을 설정합니다. _stdcall(/Gz).

      6. 구성 속성에서 링커를 확장하고 입력을 선택하고 추가 종속성에 추가합니다.

        • "C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib".
        • "C:\Microsoft NLS Downlevel API\Lib\x86\Nlsdl.lib".
  3. GuiStep_3.cpp의 내용을 다음 코드로 바꿉니다.

    // GuiStep_3.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "GuiStep_3.h"
    #include <strsafe.h>
    #include <Nlsdl.h>
    #include <MUILoad.h>
    #include "..\HelloModule_en_us\resource.h"
    
    #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2)
    #define USER_CONFIGURATION_STRING_BUFFER (((LOCALE_NAME_MAX_LENGTH+1)*5)+1)
    #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2)
    #define HELLO_MODULE_CONTRIVED_FILE_PATH  (L"HelloModule.dll")
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize);
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        UNREFERENCED_PARAMETER(hInstance);
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        UNREFERENCED_PARAMETER(nCmdShow);
    
        // The following code presents a hypothetical, yet common use pattern of MUI technology
        WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER];
    
        // 1. Application starts by applying any user defined language preferences
        // (language setting is potentially optional for an application that wishes to strictly use OS system language fallback)
        // 1a. Application looks in pre-defined location for user preferences (registry, file, web, etc.)
        WCHAR userLanguagesString[USER_CONFIGURATION_STRING_BUFFER*2];
        if(!GetMyUserDefinedLanguages(userLanguagesString,USER_CONFIGURATION_STRING_BUFFER*2))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to find the user defined language configuration, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1b. Application converts the user defined 'readable' languages to the proper multi-string 'less readable' language name format
        WCHAR userLanguagesMultiString[USER_CONFIGURATION_STRING_BUFFER];
        if(!ConvertMyLangStrToMultiLangStr(userLanguagesString,userLanguagesMultiString,USER_CONFIGURATION_STRING_BUFFER))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to convert the user defined language configuration to multi-string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
        // 1c. Application now attempts to set the fallback list - this is much different for a down-level 
        // shipping application when compared to a Windows Vista or Windows 7 only shipping application    
        BOOL setSuccess = FALSE;
        DWORD setLangCount = 0;
        HMODULE hDLL = GetModuleHandleW(L"kernel32.dll");
        if( hDLL )
        {
            typedef BOOL (* SET_PREFERRED_UI_LANGUAGES_PROTOTYPE ) ( DWORD, PCWSTR, PULONG );
            SET_PREFERRED_UI_LANGUAGES_PROTOTYPE fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE)NULL;
            fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE) GetProcAddress(hDLL,"SetProcessPreferredUILanguages");
            if( fp_SetPreferredUILanguages )
            {
                // call SetProcessPreferredUILanguages if it is available in Kernel32.dll's export table - Windows 7 and later
                setSuccess = fp_SetPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&setLangCount);
            }
            else
            {
                fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE) GetProcAddress(hDLL,"SetThreadPreferredUILanguages");
                // call SetThreadPreferredUILanguages if it is available in Kernel32.dll's export table - Windows Vista and later
                if(fp_SetPreferredUILanguages)
                    setSuccess = fp_SetPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&setLangCount);
            }
        }
    
        // 2. Application obtains access to the proper resource container 
        // for standard Win32 resource loading this is normally a PE module
        // LoadMUILibrary is the preferred alternative for loading of resource modules
        // when the application is potentially run on OS versions prior to Windows Vista
        // LoadMUILibrary is available via Windows SDK releases in Windows Vista and later
        // When available, it is advised to get the most up-to-date Windows SDK (e.g., Windows 7)
        HMODULE resContainer = NULL;
        if(setSuccess) // Windows Vista and later OS scenario
        {
            resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,0);
        }
        else // this block should only be hit on Windows XP and earlier OS platforms as setSuccess will be TRUE on Windows Vista and later
        {
            // need to provide your own fallback mechanism such as the implementation below
            // in essence the application will iterate through the user configured language list
            WCHAR * next = userLanguagesMultiString;
            while(!resContainer && *next != L'\0')
            {
                // convert the language name to an appropriate LCID
                // DownlevelLocaleNameToLCID is available via standalone download package 
                // and is contained in Nlsdl.h / Nlsdl.lib
                LCID nextLcid = DownlevelLocaleNameToLCID(next,DOWNLEVEL_LOCALE_NAME);
                // then have LoadMUILibrary attempt to probe for the right .mui module
                resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,(LANGID)nextLcid);
                // increment to the next language name in our list
                size_t nextStrLen = 0;
                if(SUCCEEDED(StringCchLengthW(next,LOCALE_NAME_MAX_LENGTH,&nextStrLen)))
                    next += (nextStrLen + 1);
                else
                    break; // string is invalid - need to exit
            }
            // if the user configured list did not locate a module then try the languages associated with the system
            if(!resContainer)
                resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,0);
        }
        if(!resContainer)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        // 3. Application parses the resource container to find the appropriate item
        WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER];
        if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0)
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            FreeLibrary(resContainer);
            return 1; // exit
        }
    
        // 4. Application presents the discovered resource to the user via UI
        swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello);
        MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION);
    
        // 5. Application cleans up memory associated with the resource container after this item is no longer needed.
        if(!FreeMUILibrary(resContainer))
        {
            swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError());
            MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR);
            return 1; // exit
        }
    
        return 0;
    }
    
    BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize)
    {
        BOOL rtnVal = FALSE;
        // very simple implementation - assumes that first 'langStrSize' characters of the 
        // L".\\langs.txt" file comprises a string of one or more languages
        HANDLE langConfigFileHandle = CreateFileW(L".\\langs.txt", GENERIC_READ, 0, 
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if(langConfigFileHandle != INVALID_HANDLE_VALUE)
        {
            // clear out the input variables
            DWORD bytesActuallyRead = 0;
            if(ReadFile(langConfigFileHandle,langStr,langStrSize*sizeof(WCHAR),&bytesActuallyRead,NULL) && bytesActuallyRead > 0)
            {
                rtnVal = TRUE;
                DWORD nullIndex = (bytesActuallyRead/sizeof(WCHAR) < langStrSize) ? bytesActuallyRead/sizeof(WCHAR) : langStrSize;
                langStr[nullIndex] = L'\0';
            }
            CloseHandle(langConfigFileHandle);
        }
        return rtnVal;
    }
    BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize)
    {
        BOOL rtnVal = FALSE;
        size_t strLen = 0;
        rtnVal = SUCCEEDED(StringCchLengthW(langStr,USER_CONFIGURATION_STRING_BUFFER*2,&strLen));
        if(rtnVal && strLen > 0 && langMultiStr && langMultiStrSize > 0)
        {
            WCHAR * langMultiStrPtr = langMultiStr;
            WCHAR * last = langStr + (langStr[0] == 0xFEFF ? 1 : 0);
            WCHAR * context = last;
            WCHAR * next = wcstok_s(last,L",; :",&context);
            while(next && rtnVal)
            {
                // make sure you validate the user input
                if(SUCCEEDED(StringCchLengthW(last,LOCALE_NAME_MAX_LENGTH,&strLen)) 
                    && DownlevelLocaleNameToLCID(next,0) != 0)
                {
                    langMultiStrPtr[0] = L'\0';
                    rtnVal &= SUCCEEDED(StringCchCatW(langMultiStrPtr,(langMultiStrSize - (langMultiStrPtr - langMultiStr)),next));
                    langMultiStrPtr += strLen + 1;
                }
                next = wcstok_s(NULL,L",; :",&context);
                if(next)
                    last = next;
            }
            if(rtnVal && (langMultiStrSize - (langMultiStrPtr - langMultiStr))) // make sure there is a double null term for the multi-string 
            {
                langMultiStrPtr[0] = L'\0';
            }
            else // fail and guard anyone whom might use the multi-string
            {
                langMultiStr[0] = L'\0';
                langMultiStr[1] = L'\0';
            }
        }
        return rtnVal;
    }
    
  4. 4단계: "Hello MUI" 전역화에서 설명한 대로 langs.txt를 만들거나 적절한 디렉터리에 복사합니다.

  5. 프로젝트를 빌드하고 실행합니다.

참고 항목

Windows Vista 이전의 Windows 버전에서 애플리케이션을 실행해야 하는 경우 Nlsdl.dll을 재배포하는 방법에 대한 Microsoft NLS 다운레벨 API 패키지와 함께 제공된 문서를 읽어야 합니다. (Microsoft 다운로드 센터에서 더 이상 사용할 수 없습니다. Windows 10 2019년 5월 업데이트 이상 버전에서 ICU 세계화 API를 사용합니다.)

 

MUI에 대한 추가 고려 사항

콘솔 애플리케이션 지원

이 자습서에서 다루는 기술은 콘솔 애플리케이션에서도 사용할 수 있습니다. 그러나 대부분의 표준 GUI 컨트롤과 달리 Windows 명령 창에는 모든 언어의 문자가 표시될 수 없습니다. 이러한 이유로 다국어 콘솔 애플리케이션에는 특별한 주의가 필요합니다.

특정 필터링 플래그를 사용하여 API SetThreadUILanguage 또는 SetThreadPreferredUILanguages 를 호출하면 리소스 로드 함수가 명령 창 내에 일반적으로 표시되지 않는 특정 언어에 대한 언어 리소스 프로브를 제거합니다. 이러한 플래그가 설정되면 언어 설정 알고리즘은 명령 창에 제대로 표시되는 언어만 대체 목록에 표시되도록 허용합니다.

이러한 API를 사용하여 다국어 콘솔 애플리케이션을 빌드하는 방법에 대한 자세한 내용은 SetThreadUILanguageSetThreadPreferredUILanguages의 설명 섹션을 참조하세요.

런타임에 지원할 언어 결정

다음 디자인 제안 중 하나를 채택하여 런타임에 애플리케이션에서 지원해야 하는 언어를 결정할 수 있습니다.

  • 설치하는 동안 최종 사용자가 지원되는 언어 목록에서 기본 설정 언어를 선택할 수 있도록 설정합니다.

  • 구성 파일에서 언어 목록 읽기

    이 자습서의 일부 프로젝트에는 언어 목록을 포함하는 langs.txt 구성 파일을 구문 분석하는 데 사용되는 함수가 포함되어 있습니다.

    이 함수는 외부 입력을 사용하므로 입력으로 제공되는 언어의 유효성을 검사합니다. 유효성 검사 수행에 대한 자세한 내용은 IsValidLocaleName 또는 DownLevelLocaleNameToLCID 함수를 참조하세요.

  • 운영 체제를 쿼리하여 설치된 언어 확인

    이 방법은 애플리케이션이 운영 체제와 동일한 언어를 사용하는 데 도움이 됩니다. 사용자에게 프롬프트가 필요하지는 않지만 이 옵션을 선택하는 경우 언제든지 운영 체제 언어를 추가하거나 제거할 수 있으며 사용자가 애플리케이션을 설치한 후에 변경될 수 있습니다. 또한 경우에 따라 운영 체제가 제한된 언어 지원으로 설치되고 운영 체제에서 지원하지 않는 언어를 지원하는 경우 애플리케이션이 더 많은 가치를 제공한다는 점에 유의하세요.

    운영 체제에서 현재 설치된 언어를 확인하는 방법에 대한 자세한 내용은 EnumUILanguages 함수를 참조하세요.

Windows Vista 이전 버전의 복잡한 스크립트 지원

특정 복잡한 스크립트를 지원하는 애플리케이션이 Windows Vista 이전 버전의 Windows에서 실행되는 경우 해당 스크립트의 텍스트가 GUI 구성 요소에 제대로 표시되지 않을 수 있습니다. 예를 들어 이 자습서 내의 하위 수준 프로젝트에서는 복잡한 스크립트 처리 문제 및 관련 글꼴 부족으로 인해 hi-IN 및 ta-IN 스크립트가 메시지 상자에 표시되지 않을 수 있습니다. 일반적으로 이러한 특성의 문제는 GUI 구성 요소에서 정사각형 상자로 나타납니다.

복잡한 스크립트 처리를 사용하도록 설정하는 방법에 대한 자세한 내용은 Windows스크립트 및 글꼴 지원에서 확인할 수 있습니다.

요약

이 자습서에서는 단일 언어 애플리케이션을 세계화하고 다음 모범 사례를 보여 줍니다.

  • Win32 리소스 모델을 활용하도록 애플리케이션을 디자인합니다.
  • 리소스의 MUI 분할을 위성 이진 파일(.mui 파일)로 활용합니다.
  • 지역화 프로세스가 대상 언어에 적합하도록 .mui 파일의 리소스를 업데이트하는지 확인합니다.
  • 리소스 로드 API가 지역화된 콘텐츠를 찾을 수 있도록 애플리케이션, 연결된 .mui 파일 및 구성 콘텐츠의 패키징 및 배포가 올바르게 수행되었는지 확인합니다.
  • 최종 사용자에게 애플리케이션의 언어 구성을 조정하는 메커니즘을 제공합니다.
  • 언어 구성을 활용하도록 런타임 코드를 조정하여 애플리케이션이 최종 사용자의 요구에 보다 신속하게 응답할 수 있도록 합니다.