понедельник, 2 ноября 2015 г.

Ещё немного по теме локализованных ресурсов

В дополнение к предыдущей заметке...


Нейтральная локаль, как и все остальные, определяется в ресурсах EXE или DLL и имеет код 0x000. Я думаю, что зачастую имеет смысл определять более обобщённые варианты ресурсов, дабы не дублировать их для каждой конкретной локали, например:
  • ru вместо ru-RU
  • en вместо en-US, en-GB и т.п.
Если специфичная локаль (en-US или en-GB) не будет найдена в ресурсах модуля, то будет выполнена дополнительная попытка найти более обобщённый вариант локали: en. Однако обратное не верно: если задан поиск локали en, а в ресурсах вместо неё присутствуют более конкретные локали (en-US или en-GB), то они будут проигнорированы.

В качестве примера в файле resources.txt определим текстовые ресурсы с таким набором локализаций:
  • нейтральная
  • ru
  • en-US

; // ***** resources.txt *****
; // This is the header section.

MessageIdTypedef=DWORD

SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
Warning=0x2:STATUS_SEVERITY_WARNING
Error=0x3:STATUS_SEVERITY_ERROR
)

FacilityNames=(System=0x0:FACILITY_SYSTEM
Runtime=0x2:FACILITY_RUNTIME
Stubs=0x3:FACILITY_STUBS
Io=0x4:FACILITY_IO_ERROR_CODE
)

; // The neutral locale.
LanguageNames=(Neutral=0x000:MSG00000)

; // This locale will be used for any RU-based locale.
LanguageNames=(ru=0x019:MSG00019)

; // This locale will be used only for en-US locale, instead
; // of any EN-based locale.
LanguageNames=(en_US=0x409:MSG00409)

; // The following are message definitions.

MessageId=0x1
Severity=Success
Facility=System
SymbolicName=MSG_HELLO
Language=en_US
USA locale only (en-US).
.

Language=ru
Любая RU-основанная локаль (RU).
.

Language=Neutral
Neutral locale.
.



Далее разными способами программно пытаемся получить локализованный вариант нашего текста:


#include <Windows.h>
#include <tchar.h>
#include <iostream>
#include <exception>
#include "resources.h"
using namespace std;
/*
MSDN resources:
FormatMessage function:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351%28v=vs.85%29.aspx
Message Text Files:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd996906%28v=vs.85%29.aspx
Sample Message Text File:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd996907%28v=vs.85%29.aspx
Message Compiler (MC.exe):
https://msdn.microsoft.com/en-us/library/windows/desktop/aa385638%28v=vs.85%29.aspx
*/
// Our function uses the WinAPI mechanism for notifying of error
void PrintMessage(DWORD);

int wmain(int argc, LPTSTR argv[])
try {
    // For right displaying of Cyrillic chars in the console window
    setlocale(LC_ALL, "Russian");

    wcout << L"For neutral locale:" << endl;
    DWORD nl = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    PrintMessage(nl);

    TCHAR x[LOCALE_NAME_MAX_LENGTH];
   
    DWORD du = GetUserDefaultLCID();   
    int n = GetUserDefaultLocaleName(x, LOCALE_NAME_MAX_LENGTH);
    if (0 != n) {
        wcout << L"For user default locale <" << x << L">:" << endl;
        PrintMessage(du);
    }   
       
    DWORD ds = GetSystemDefaultLCID();
    n = GetSystemDefaultLocaleName(x, LOCALE_NAME_MAX_LENGTH);
    if (0 != n) {
        wcout << L"For system default locale <" << x << L">:" << endl;
        PrintMessage(ds);
    }

    // Any RU-based locale (defined in the resources)
    wcout << L"For RU locale:" << endl;
    DWORD ru = MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL);
    PrintMessage(ru);

    // Specific locale (NOT defined in the resources)
    // At this case will be used more common locale: RU
    wcout << L"For ru-RU locale:" << endl;
    DWORD ru_ru = MAKELANGID(LANG_RUSSIAN, SUBLANG_RUSSIAN_RUSSIA);
    PrintMessage(ru_ru);

    // Any EN-based locale (defined in the resources)
    wcout << L"For EN locale:" << endl;
    DWORD en = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL);
    PrintMessage(en);

    // Specific locale (defined in the resources)
    wcout << L"For en-US locale:" << endl;
    DWORD en_us = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
    PrintMessage(en_us);

    wcout << L"Press any char for exit..." << endl;
    wchar_t c;
    wcin >> c;
}
catch (...) {
    wcerr << L"Unknown exception." << endl;
    return 1;
}

void PrintMessage(DWORD localeId) {
    DWORD msgId = MSG_HELLO;
    LPTSTR buffer = NULL;
    HMODULE hModule = GetModuleHandle(NULL);

    DWORD result = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
        hModule, msgId, localeId, (LPTSTR)&buffer, 0, NULL);

    if (0 == result) {
        msgId = GetLastError();
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, msgId, 0, (LPTSTR)&buffer, 0, NULL);
    }
    wcout << buffer << endl;
    HeapFree(GetProcessHeap(), 0, buffer);
}


Результат работы кода выглядит следующим образом:



Комментариев нет: