From c8d924982804b5a318499e68a6b94bf9cde243cc Mon Sep 17 00:00:00 2001 From: ste Date: Fri, 12 Apr 2019 10:57:11 +0100 Subject: [PATCH 1/7] GPII-3852: Getting the language list from a more accurate location. --- gpii/node_modules/gpii-localisation/src/language.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gpii/node_modules/gpii-localisation/src/language.js b/gpii/node_modules/gpii-localisation/src/language.js index 754060fc6..febc40817 100644 --- a/gpii/node_modules/gpii-localisation/src/language.js +++ b/gpii/node_modules/gpii-localisation/src/language.js @@ -109,10 +109,11 @@ gpii.windows.language.fixCodeCase = function (langCode) { return parts.join("-"); }; + /** * 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 in HKEY_CURRENT_USER\Control Panel\International\User Profile * * @param {Component} that The gpii.windows.language instance. * @return {Promise>} A promise, resolving with either the language names if the list has @@ -120,7 +121,7 @@ gpii.windows.language.fixCodeCase = function (langCode) { */ gpii.windows.language.getInstalled = function (that) { var langCodes = gpii.windows.enumRegistryKeys( - "HKEY_LOCAL_MACHINE", "SYSTEM\\CurrentControlSet\\Control\\MUI\\UILanguages"); + "HKEY_CURRENT_USER\\", "Control Panel\\International\\User Profile"); langCodes = langCodes.map(gpii.windows.language.fixCodeCase); From 2494e2d2b7aa09978771419497989658625ed32b Mon Sep 17 00:00:00 2001 From: ste Date: Fri, 12 Apr 2019 17:47:38 +0100 Subject: [PATCH 2/7] GPII-3852: Fixing installed language codes which do not have a region. --- gpii/node_modules/gpii-localisation/src/language.js | 7 ++++++- gpii/node_modules/gpii-localisation/test/testLanguage.js | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/gpii/node_modules/gpii-localisation/src/language.js b/gpii/node_modules/gpii-localisation/src/language.js index febc40817..0c7f615cc 100644 --- a/gpii/node_modules/gpii-localisation/src/language.js +++ b/gpii/node_modules/gpii-localisation/src/language.js @@ -88,6 +88,11 @@ fluid.defaults("gpii.windows.language", { gpii.windows.language.fixCodeCase = function (langCode) { var parts = (langCode && langCode.split) ? langCode.split("-") : []; + // Handle the case where just a language is presented, and also use that as the region. eg, "pl" becomes "pl-PL". + if (parts.length === 1 && parts[0].length === 2) { + parts.push(parts[0]); + } + // [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(); @@ -121,7 +126,7 @@ gpii.windows.language.fixCodeCase = function (langCode) { */ gpii.windows.language.getInstalled = function (that) { var langCodes = gpii.windows.enumRegistryKeys( - "HKEY_CURRENT_USER\\", "Control Panel\\International\\User Profile"); + "HKEY_CURRENT_USER", "Control Panel\\International\\User Profile"); langCodes = langCodes.map(gpii.windows.language.fixCodeCase); diff --git a/gpii/node_modules/gpii-localisation/test/testLanguage.js b/gpii/node_modules/gpii-localisation/test/testLanguage.js index 58af3e461..8ec195fd7 100644 --- a/gpii/node_modules/gpii-localisation/test/testLanguage.js +++ b/gpii/node_modules/gpii-localisation/test/testLanguage.js @@ -33,8 +33,8 @@ fluid.registerNamespace("gpii.tests.windows.language"); // 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"], + // Simple language subtag, without region: + "bg-BG": ["bg", "bg"], // Language subtag plus Script subtag: "zh-Hant": ["zh-Hant", "ZH-Hant"], // Extended language subtags and their primary language subtag counterparts From ca1a05d9d04540fb36d9fd7333edac12025d224e Mon Sep 17 00:00:00 2001 From: ste Date: Fri, 12 Apr 2019 17:51:44 +0100 Subject: [PATCH 3/7] GPII-3852: Made a language test more useful. --- gpii/node_modules/gpii-localisation/test/testLanguage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpii/node_modules/gpii-localisation/test/testLanguage.js b/gpii/node_modules/gpii-localisation/test/testLanguage.js index 8ec195fd7..231e521a5 100644 --- a/gpii/node_modules/gpii-localisation/test/testLanguage.js +++ b/gpii/node_modules/gpii-localisation/test/testLanguage.js @@ -34,7 +34,7 @@ fluid.registerNamespace("gpii.tests.windows.language"); // expected => [inputs] gpii.tests.windows.language.codeCaseTests = fluid.freezeRecursive({ // Simple language subtag, without region: - "bg-BG": ["bg", "bg"], + "bg-BG": ["bg", "BG"], // Language subtag plus Script subtag: "zh-Hant": ["zh-Hant", "ZH-Hant"], // Extended language subtags and their primary language subtag counterparts From 517357e71102150d5aa8992bb5a92b351bccec50 Mon Sep 17 00:00:00 2001 From: ste Date: Mon, 15 Apr 2019 00:21:04 +0100 Subject: [PATCH 4/7] GPII-3852: Improved fixing language codes without a region. --- .../gpii-localisation/src/language.js | 35 +++- .../gpii-localisation/test/testLanguage.js | 179 ++++++++++-------- 2 files changed, 125 insertions(+), 89 deletions(-) diff --git a/gpii/node_modules/gpii-localisation/src/language.js b/gpii/node_modules/gpii-localisation/src/language.js index 0c7f615cc..83070dd35 100644 --- a/gpii/node_modules/gpii-localisation/src/language.js +++ b/gpii/node_modules/gpii-localisation/src/language.js @@ -80,17 +80,26 @@ 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. + * @param {Array} fullCodes A list of language codes all of which contain a region, to be used as a lookup to + * complete a langCode which does not specify the region. * @return {String} The language code, in the correct casing. */ -gpii.windows.language.fixCodeCase = function (langCode) { +gpii.windows.language.fixLangCode = function (langCode, fullCodes) { var parts = (langCode && langCode.split) ? langCode.split("-") : []; - // Handle the case where just a language is presented, and also use that as the region. eg, "pl" becomes "pl-PL". + // Handle the case where just a language is presented. if (parts.length === 1 && parts[0].length === 2) { - parts.push(parts[0]); + var codePrefix = langCode.toLowerCase() + "-"; + var fullCode = fullCodes.find(function (fullCode) { + return fullCode.toLowerCase().startsWith(codePrefix); + }); + if (fullCode) { + parts = fullCode.split("-"); + } } // [RFC-5646] Language is always the first section - "2*3ALPHA ; shortest ISO 639 code" @@ -125,10 +134,22 @@ gpii.windows.language.fixCodeCase = function (langCode) { * changed, or null if there was no change. */ gpii.windows.language.getInstalled = function (that) { - var langCodes = gpii.windows.enumRegistryKeys( + + // The language codes in the list of installed languages doesn't always contain the region part, for languages + // which are used in a single region, such as Polish (pl) or Korean (ko). Fortunately, the list of recently used + // languages does. The recently used language list can therefore be used as the lookup for region-less languages. + + // Get the languages that are installed (may contain region-less language codes). + var installed = gpii.windows.enumRegistryKeys( "HKEY_CURRENT_USER", "Control Panel\\International\\User Profile"); - langCodes = langCodes.map(gpii.windows.language.fixCodeCase); + // Get the list of recently used languages (containing full language codes, but also removed languages). + var fullCodes = gpii.windows.enumRegistryKeys( + "HKEY_LOCAL_MACHINE", "SYSTEM\\CurrentControlSet\\Control\\MUI\\UILanguages"); + + var langCodes = installed.map(function (lang) { + return gpii.windows.language.fixLangCode(lang, fullCodes); + }); var current = gpii.windows.language.getDisplayLanguage(); @@ -298,7 +319,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 231e521a5..5decebb75 100644 --- a/gpii/node_modules/gpii-localisation/test/testLanguage.js +++ b/gpii/node_modules/gpii-localisation/test/testLanguage.js @@ -33,86 +33,100 @@ fluid.registerNamespace("gpii.tests.windows.language"); // 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, without region: - "bg-BG": ["bg", "BG"], - // 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 language, no region + "xx": ["xx", "XX"], + + // 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"] + } }); // Tests for getLanguageNames(); @@ -240,14 +254,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); }); }); From fcb19f036816983bf5d0f82eb92d7919af3bd87f Mon Sep 17 00:00:00 2001 From: ste Date: Mon, 15 Apr 2019 23:57:49 +0100 Subject: [PATCH 5/7] GPII-3852: Correctly identifying installed languages. --- .../WindowsUtilities/WindowsUtilities.js | 4 + .../gpii-localisation/src/language.js | 92 ++++++++++--------- .../gpii-localisation/test/testLanguage.js | 67 +------------- 3 files changed, 58 insertions(+), 105 deletions(-) diff --git a/gpii/node_modules/WindowsUtilities/WindowsUtilities.js b/gpii/node_modules/WindowsUtilities/WindowsUtilities.js index 0b9ab1eb2..ecc2314f3 100644 --- a/gpii/node_modules/WindowsUtilities/WindowsUtilities.js +++ b/gpii/node_modules/WindowsUtilities/WindowsUtilities.js @@ -180,6 +180,10 @@ windows.kernel32 = ffi.Library("kernel32", { // https://msdn.microsoft.com/library/aa363858 "CreateFileW": [ t.HANDLE, [ "char*", t.DWORD, t.DWORD, t.HANDLE, t.DWORD, t.DWORD, 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 83070dd35..b7e11b9ef 100644 --- a/gpii/node_modules/gpii-localisation/src/language.js +++ b/gpii/node_modules/gpii-localisation/src/language.js @@ -84,44 +84,24 @@ fluid.defaults("gpii.windows.language", { * part if it's not provided. * * @param {String} langCode The language code. - * @param {Array} fullCodes A list of language codes all of which contain a region, to be used as a lookup to - * complete a langCode which does not specify the region. * @return {String} The language code, in the correct casing. */ -gpii.windows.language.fixLangCode = function (langCode, fullCodes) { - var parts = (langCode && langCode.split) ? langCode.split("-") : []; - - // Handle the case where just a language is presented. - if (parts.length === 1 && parts[0].length === 2) { - var codePrefix = langCode.toLowerCase() + "-"; - var fullCode = fullCodes.find(function (fullCode) { - return fullCode.toLowerCase().startsWith(codePrefix); - }); - if (fullCode) { - parts = fullCode.split("-"); - } - } +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); - // [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; - } - } + var result = gpii.windows.kernel32.ResolveLocaleName(langCodeBuffer, fixedBuffer, LOCALE_NAME_MAX_LENGTH); - if (region) { - parts[region] = parts[region].toUpperCase(); - } + var togo; + if (result) { + togo = gpii.windows.stringFromWideChar(fixedBuffer); + } else { + fluid.log(gpii.windows.win32errorText("ResolveLocaleName", result)); + togo = langCode; } - return parts.join("-"); + return togo; }; /** @@ -135,20 +115,46 @@ gpii.windows.language.fixLangCode = function (langCode, fullCodes) { */ gpii.windows.language.getInstalled = function (that) { - // The language codes in the list of installed languages doesn't always contain the region part, for languages - // which are used in a single region, such as Polish (pl) or Korean (ko). Fortunately, the list of recently used - // languages does. The recently used language list can therefore be used as the lookup for region-less languages. - - // Get the languages that are installed (may contain region-less language codes). - var installed = gpii.windows.enumRegistryKeys( + // Get the languages that are activated. This may contain region-less language codes, and the language pack may + // not be installed. + var active = gpii.windows.enumRegistryKeys( "HKEY_CURRENT_USER", "Control Panel\\International\\User Profile"); - // Get the list of recently used languages (containing full language codes, but also removed languages). - var fullCodes = gpii.windows.enumRegistryKeys( - "HKEY_LOCAL_MACHINE", "SYSTEM\\CurrentControlSet\\Control\\MUI\\UILanguages"); + // Get the installed language packs + var languagePacks = gpii.windows.wmi.getQuery([null, "SELECT MUILanguages FROM Win32_OperatingSystem"]); + // Make a case-insensitive map + var installedLanguages = {}; + fluid.each(languagePacks, function (language) { + installedLanguages[language.toLowerCase()] = language; + }); + - var langCodes = installed.map(function (lang) { - return gpii.windows.language.fixLangCode(lang, fullCodes); + // Fix the case of each language code, and get the region if it's not specified. + active = active.map(gpii.windows.language.fixLangCode); + + var langCodes = []; + // For each active language, get the matching language pack (if it's installed). + fluid.each(active, function (langCode) { + var result = installedLanguages[langCode.toLowerCase()]; + if (!result) { + // For some languages like "nl-BE", the language pack "nl-NL" is really used. Chop off the region and get the + // "official" region. + var lang = langCode.split("-")[0]; + var fixedLang = gpii.windows.language.fixLangCode(lang); + result = installedLanguages[fixedLang.toLowerCase()]; + if (!result) { + // That didn't work - instead, find a language pack of the same language, ignoring the region. + lang = lang.toLowerCase(); + result = Object.keys(installedLanguages).find(function (lp) { + return lp.startsWith(lang); + }); + } + } + + if (result) { + result = gpii.windows.language.fixLangCode(result); + langCodes.push(result); + } }); var current = gpii.windows.language.getDisplayLanguage(); diff --git a/gpii/node_modules/gpii-localisation/test/testLanguage.js b/gpii/node_modules/gpii-localisation/test/testLanguage.js index 5decebb75..512519284 100644 --- a/gpii/node_modules/gpii-localisation/test/testLanguage.js +++ b/gpii/node_modules/gpii-localisation/test/testLanguage.js @@ -45,76 +45,19 @@ gpii.tests.windows.language.codeCaseTests = fluid.freezeRecursive({ "ko-KR": ["ko", "KO"], "te-IN": ["te", "TE"], "pl-PL": ["pl", "pl"], - // Unknown language, no region - "xx": ["xx", "XX"], - - // 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], + // 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"], + "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"], + "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"], From 3c037b737ba0e0c1eabddbcc7f371fc6649fc919 Mon Sep 17 00:00:00 2001 From: ste Date: Tue, 16 Apr 2019 11:45:43 +0100 Subject: [PATCH 6/7] GPII-3852: Reduced the frequency of language detection. --- gpii/node_modules/gpii-localisation/src/language.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gpii/node_modules/gpii-localisation/src/language.js b/gpii/node_modules/gpii-localisation/src/language.js index b7e11b9ef..d045eb04a 100644 --- a/gpii/node_modules/gpii-localisation/src/language.js +++ b/gpii/node_modules/gpii-localisation/src/language.js @@ -290,12 +290,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(); } From 730f994773c869570c8146d709b494b30dd14e5f Mon Sep 17 00:00:00 2001 From: ste Date: Fri, 19 Apr 2019 10:31:43 +0100 Subject: [PATCH 7/7] GPII-3852: Using the installed language packs as the source of languages. --- .../gpii-localisation/src/language.js | 42 +------------------ 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/gpii/node_modules/gpii-localisation/src/language.js b/gpii/node_modules/gpii-localisation/src/language.js index d045eb04a..fadb23cf9 100644 --- a/gpii/node_modules/gpii-localisation/src/language.js +++ b/gpii/node_modules/gpii-localisation/src/language.js @@ -107,7 +107,7 @@ gpii.windows.language.fixLangCode = function (langCode) { /** * Gets the display languages that are installed on the system, and updates the model. * - * These are listed in HKEY_CURRENT_USER\Control Panel\International\User Profile + * 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 @@ -115,47 +115,9 @@ gpii.windows.language.fixLangCode = function (langCode) { */ gpii.windows.language.getInstalled = function (that) { - // Get the languages that are activated. This may contain region-less language codes, and the language pack may - // not be installed. - var active = gpii.windows.enumRegistryKeys( - "HKEY_CURRENT_USER", "Control Panel\\International\\User Profile"); - // Get the installed language packs var languagePacks = gpii.windows.wmi.getQuery([null, "SELECT MUILanguages FROM Win32_OperatingSystem"]); - // Make a case-insensitive map - var installedLanguages = {}; - fluid.each(languagePacks, function (language) { - installedLanguages[language.toLowerCase()] = language; - }); - - - // Fix the case of each language code, and get the region if it's not specified. - active = active.map(gpii.windows.language.fixLangCode); - - var langCodes = []; - // For each active language, get the matching language pack (if it's installed). - fluid.each(active, function (langCode) { - var result = installedLanguages[langCode.toLowerCase()]; - if (!result) { - // For some languages like "nl-BE", the language pack "nl-NL" is really used. Chop off the region and get the - // "official" region. - var lang = langCode.split("-")[0]; - var fixedLang = gpii.windows.language.fixLangCode(lang); - result = installedLanguages[fixedLang.toLowerCase()]; - if (!result) { - // That didn't work - instead, find a language pack of the same language, ignoring the region. - lang = lang.toLowerCase(); - result = Object.keys(installedLanguages).find(function (lp) { - return lp.startsWith(lang); - }); - } - } - - if (result) { - result = gpii.windows.language.fixLangCode(result); - langCodes.push(result); - } - }); + var langCodes = languagePacks.map(gpii.windows.language.fixLangCode); var current = gpii.windows.language.getDisplayLanguage();