diff --git a/gpii/node_modules/WindowsUtilities/WindowsUtilities.js b/gpii/node_modules/WindowsUtilities/WindowsUtilities.js index 2fc9fe8a8..cb477225f 100644 --- a/gpii/node_modules/WindowsUtilities/WindowsUtilities.js +++ b/gpii/node_modules/WindowsUtilities/WindowsUtilities.js @@ -201,6 +201,10 @@ windows.kernel32 = ffi.Library("kernel32", { // https://msdn.microsoft.com/library/ms686211 "SetEvent": [ t.BOOL, [ t.HANDLE ] + ], + // https://docs.microsoft.com/windows/desktop/api/winnls/nf-winnls-resolvelocalename + "ResolveLocaleName": [ + t.BOOL, ["char*", "char*", "int"] ] }); diff --git a/gpii/node_modules/gpii-localisation/src/language.js b/gpii/node_modules/gpii-localisation/src/language.js index 03ad0b698..74128458c 100644 --- a/gpii/node_modules/gpii-localisation/src/language.js +++ b/gpii/node_modules/gpii-localisation/src/language.js @@ -80,49 +80,44 @@ fluid.defaults("gpii.windows.language", { */ /** - * Fixes the casing of a language code, so the language is lowercase and the region is uppercase. + * Fixes the casing of a language code, so the language is lowercase and the region is uppercase, and add the region + * part if it's not provided. * * @param {String} langCode The language code. * @return {String} The language code, in the correct casing. */ -gpii.windows.language.fixCodeCase = function (langCode) { - var parts = (langCode && langCode.split) ? langCode.split("-") : []; - - // [RFC-5646] Language is always the first section - "2*3ALPHA ; shortest ISO 639 code" - if (parts.length > 0 && (parts[0].length === 2 || parts[0].length === 3)) { - parts[0] = parts[0].toLowerCase(); - - // [RFC-5646] describes a language tag as having a varying number of sections before and after the region. The - // region is the only section after the language that is 2 letters (may also be 3 digits, but for casing that's - // irrelevant) - var region; - for (var n = 1; n < parts.length; n++) { - if (parts[n].length === 2) { - region = n; - } - } +gpii.windows.language.fixLangCode = function (langCode) { + var LOCALE_NAME_MAX_LENGTH = 85; + var langCodeBuffer = gpii.windows.stringToWideChar(langCode); + var fixedBuffer = Buffer.alloc(LOCALE_NAME_MAX_LENGTH * 2); - if (region) { - parts[region] = parts[region].toUpperCase(); - } + var result = gpii.windows.kernel32.ResolveLocaleName(langCodeBuffer, fixedBuffer, LOCALE_NAME_MAX_LENGTH); + + var togo; + if (result) { + togo = gpii.windows.stringFromWideChar(fixedBuffer); + } else { + fluid.log(gpii.windows.win32errorText("ResolveLocaleName", result)); + togo = langCode; } - return parts.join("-"); + return togo; }; + /** * Gets the display languages that are installed on the system, and updates the model. * - * These are listed in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\MUI\UILanguages + * These are listed retrieved from the MUILanguages field of the Win32_OperatingSystem WMI class. * * @param {Component} that The gpii.windows.language instance. * @return {Promise>} A promise, resolving with either the language names if the list has * changed, or null if there was no change. */ gpii.windows.language.getInstalled = function (that) { - var langCodes = gpii.windows.enumRegistryKeys( - "HKEY_LOCAL_MACHINE", "SYSTEM\\CurrentControlSet\\Control\\MUI\\UILanguages"); - langCodes = langCodes.map(gpii.windows.language.fixCodeCase); + // Get the installed language packs + var languagePacks = gpii.windows.wmi.getQuery([null, "SELECT MUILanguages FROM Win32_OperatingSystem"]); + var langCodes = languagePacks.map(gpii.windows.language.fixLangCode); var current = gpii.windows.language.getDisplayLanguage(); @@ -259,12 +254,12 @@ gpii.windows.language.getLanguageNames = function (that, langCodes) { * * @param {Component} that The gpii.windows.language component. * @param {Number} hwnd The window handle of the message window. - * @param {Number} msg The message identifier. - * #param {Number} wParam Message specific data. (unused) + * @param {Number|String} msg The message identifier. + * @param {Number} wParam Message specific data. * #param {Buffer} lParam Additional message specific data. (unused) */ -gpii.windows.language.windowMessage = function (that, hwnd, msg) { - if (msg === gpii.windows.API_constants.WM_SETTINGCHANGE +gpii.windows.language.windowMessage = function (that, hwnd, msg, wParam) { + if (msg === "GPII-TrayButton-Message" && wParam === 1 || msg === gpii.windows.API_constants.WM_INPUTLANGCHANGE) { that.getInstalledLanguages(); } @@ -294,7 +289,7 @@ gpii.windows.language.getDisplayLanguage = function () { fluid.log(gpii.windows.win32errorText("LCIDToLocaleName failed", result)); } } - return gpii.windows.language.fixCodeCase(langCode); + return gpii.windows.language.fixLangCode(langCode); }; /** diff --git a/gpii/node_modules/gpii-localisation/test/testLanguage.js b/gpii/node_modules/gpii-localisation/test/testLanguage.js index 1b060b4f3..dc5b05cdc 100644 --- a/gpii/node_modules/gpii-localisation/test/testLanguage.js +++ b/gpii/node_modules/gpii-localisation/test/testLanguage.js @@ -32,86 +32,43 @@ jqUnit.module("Language tests"); // Language code casing tests (from https://tools.ietf.org/html/rfc5646#appendix-A) // expected => [inputs] gpii.tests.windows.language.codeCaseTests = fluid.freezeRecursive({ - // Simple language subtag: - "de": ["de", "DE"], - // Language subtag plus Script subtag: - "zh-Hant": ["zh-Hant", "ZH-Hant"], - // Extended language subtags and their primary language subtag counterparts - "zh-cmn-Hans-CN": ["zh-cmn-Hans-cn", "ZH-cmn-Hans-CN"], - "cmn-Hans-CN": ["cmn-Hans-cn", "CMN-Hans-CN"], - "yue-HK": ["yue-hk", "YUE-HK"], - "yue": ["yue", "YUE"], - - // Language-Script-Region: - "sr-Latn-RS": ["sr-Latn-rs", "SR-Latn-RS"], - - // Language-Variant: - "sl-rozaj": ["sl-rozaj", "SL-rozaj"], - "sl-rozaj-biske": ["sl-rozaj-biske", "SL-rozaj-biske"], - - // Language-Region-Variant: - "de-CH-1901": ["de-ch-1901", "DE-CH-1901"], - "sl-IT-nedis": ["sl-it-nedis", "SL-IT-nedis"], - - // Language-Script-Region-Variant: - "hy-Latn-IT-arevela": ["hy-Latn-it-arevela", "HY-Latn-IT-arevela"], - - // Language-Region: - "de-DE": ["de-de", "DE-DE"], - "en-US": ["en-us", "EN-US"], - "es-419": ["es-419", "ES-419"], - - // Private use subtags: - "de-CH-x-phonebk": ["de-ch-x-phonebk", "de-ch-x-phonebk"], - "az-Arab-x-AZE-derbend": ["az-Arab-x-AZE-derbend", "AZ-Arab-x-AZE-derbend"], - - // Private use registry values: - "x-whatever": ["x-whatever", "x-whatever"], - "qaa-Qaaa-QM-x-southern": ["qaa-Qaaa-qm-x-southern", "QAA-Qaaa-QM-x-southern"], - "de-Qaaa": ["de-Qaaa", "DE-Qaaa"], - "sr-Qaaa-RS": ["sr-Qaaa-rs", "SR-Qaaa-RS"], - - // Tags that use extensions (examples ONLY -- extensions MUST be defined by revision or update to this document, or by RFC): - "en-US-u-islamcal": ["en-us-u-islamcal", "EN-US-u-islamcal"], - "zh-CN-a-myext-x-private": ["zh-cn-a-myext-x-private", "ZH-CN-a-myext-x-private"], - "en-a-myext-b-another": ["en-a-myext-b-another", "EN-a-myext-b-another"], - - // Some Invalid Tags - "de-419-DE": ["de-419-de", "DE-419-DE"], // incorrect result - "a-DE": "a-DE", - "a-de": "a-de", - "ar-a-aaa-b-bbb-a-ccc": ["ar-a-aaa-b-bbb-a-ccc", "AR-a-aaa-b-bbb-a-ccc"], // incorrect result - - "-": "-", - "-AB": "-AB", - "-ab": "-ab", - "-XYZ": "-XYZ", - "-xyz": "-xyz", - - "": ["", null, 123, 0, {}, [], NaN, fluid.identity], - - // Actual language packs for Windows - "az-Latn-AZ": [ "az-Latn-az", "AZ-Latn-AZ" ], - "bs-Latn-BA": [ "bs-Latn-ba", "BS-Latn-BA" ], - "ku-ARAB-IQ": [ "ku-ARAB-iq", "KU-ARAB-IQ" ], - "chr-CHER-US": [ "chr-CHER-us", "CHR-CHER-US" ], - "prs-AF": [ "prs-af", "PRS-AF" ], - "fil-PH": [ "fil-ph", "FIL-PH" ], - "ha-Latn-NG": [ "ha-Latn-ng", "HA-Latn-NG" ], - "iu-Latn-CA": [ "iu-Latn-ca", "IU-Latn-CA" ], - "quc-Latn-GT": [ "quc-Latn-gt", "QUC-Latn-GT" ], - "qut-GT": [ "qut-gt", "QUT-GT" ], - "kok-IN": [ "kok-in", "KOK-IN" ], - "pa-Arab-PK": [ "pa-Arab-pk", "PA-Arab-PK" ], - "quz-PE": [ "quz-pe", "QUZ-PE" ], - "sr-Cyrl-BA": [ "sr-Cyrl-ba", "SR-Cyrl-BA" ], - "sr-Cyrl-CS": [ "sr-Cyrl-cs", "SR-Cyrl-CS" ], - "sr-Cyrl-RS": [ "sr-Cyrl-rs", "SR-Cyrl-RS" ], - "nso-ZA": [ "nso-za", "NSO-ZA" ], - "sd-Arab-PK": [ "sd-Arab-pk", "SD-Arab-PK" ], - "tg-Cyrl-TJ": [ "tg-Cyrl-tj", "TG-Cyrl-TJ" ], - "uz-Latn-UZ": [ "uz-Latn-uz", "UZ-Latn-UZ" ], - "ca-ES-valencia": [ "ca-ES-valencia", "CA-ES-valencia" ] + fullCodes: [ + "bg-BG", + "ko-KR", + "TE-in", + "PL-PL" + ], + tests: { + // Simple language subtag, without region (expanded using fullCode): + "bg-BG": ["bg", "BG"], + "ko-KR": ["ko", "KO"], + "te-IN": ["te", "TE"], + "pl-PL": ["pl", "pl"], + // Unknown languages + "": ["xx", "XX", "xx-YY", "XX-yy"], + + // Actual language packs for Windows + "az-Latn-AZ": ["az-Latn-az", "AZ-Latn-AZ"], + "bs-Latn-BA": ["bs-Latn-ba", "BS-Latn-BA"], + "ku-Arab-IQ": ["ku-ARAB-iq", "KU-ARAB-IQ"], + "chr-Cher-US": ["chr-CHER-us", "CHR-CHER-US"], + "prs-AF": ["prs-af", "PRS-AF"], + "fil-PH": ["fil-ph", "FIL-PH"], + "ha-Latn-NG": ["ha-Latn-ng", "HA-Latn-NG"], + "iu-Latn-CA": ["iu-Latn-ca", "IU-Latn-CA"], + "quc-Latn-GT": ["quc-Latn-gt", "QUC-Latn-GT", "qut-gt", "QUT-GT"], + "kok-IN": ["kok-in", "KOK-IN"], + "pa-Arab-PK": ["pa-Arab-pk", "PA-Arab-PK"], + "quz-PE": ["quz-pe", "QUZ-PE"], + "sr-Cyrl-BA": ["sr-Cyrl-ba", "SR-Cyrl-BA"], + "sr-Cyrl-CS": ["sr-Cyrl-cs", "SR-Cyrl-CS"], + "sr-Cyrl-RS": ["sr-Cyrl-rs", "SR-Cyrl-RS"], + "nso-ZA": ["nso-za", "NSO-ZA"], + "sd-Arab-PK": ["sd-Arab-pk", "SD-Arab-PK"], + "tg-Cyrl-TJ": ["tg-Cyrl-tj", "TG-Cyrl-TJ"], + "uz-Latn-UZ": ["uz-Latn-uz", "UZ-Latn-UZ"], + "ca-ES-valencia": ["ca-ES-valencia", "CA-ES-valencia"] + } }); // Tests for getLanguageNames(); @@ -239,14 +196,15 @@ jqUnit.test("Checking current language", function () { }); jqUnit.test("Test language code casing", function () { - var tests = gpii.tests.windows.language.codeCaseTests; + var tests = gpii.tests.windows.language.codeCaseTests.tests; + var fullCodes = gpii.tests.windows.language.codeCaseTests.fullCodes; fluid.each(tests, function (inputs, langCode) { inputs = fluid.makeArray(inputs); inputs.push(langCode); fluid.each(inputs, function (input) { console.log(langCode, input); - var result = gpii.windows.language.fixCodeCase(input); + var result = gpii.windows.language.fixLangCode(input, fullCodes); jqUnit.assertEquals("Language case should be correct", langCode, result); }); });