From fc211d9a2e58f4be90ebc0a81bd72589217bad16 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sun, 28 Jan 2024 16:21:10 +0400 Subject: [PATCH 1/5] Cleaned up traces of userlang control via cookie --- test/server.cpp | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/test/server.cpp b/test/server.cpp index 80f8e3ae0..f47e06149 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -1237,7 +1237,6 @@ TEST_F(ServerTest, UserLanguageControl) const std::string description; const std::string url; const std::string acceptLanguageHeader; - const char* const requestCookie; // Cookie: header of the request const std::string expectedH1; operator TestContext() const @@ -1248,64 +1247,45 @@ TEST_F(ServerTest, UserLanguageControl) {"acceptLanguageHeader", acceptLanguageHeader}, }; - if ( requestCookie ) { - ctx.push_back({"requestCookie", requestCookie}); - } - return ctx; } }; - const char* const NO_COOKIE = nullptr; - const TestData testData[] = { { "Default user language is English", /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article", /*Accept-Language:*/ "", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "Not Found" }, { "userlang URL query parameter is respected", /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article?userlang=en", /*Accept-Language:*/ "", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "Not Found" }, { "userlang URL query parameter is respected", /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article?userlang=test", /*Accept-Language:*/ "", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" }, { "'Accept-Language: *' is handled", /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article", /*Accept-Language:*/ "*", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "Not Found" }, { "Accept-Language: header is respected", /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article", /*Accept-Language:*/ "test", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" }, - { - "userlang cookie is ignored", - /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article", - /*Accept-Language:*/ "", - /*Request Cookie:*/ "userlang=test", - /* expected

*/ "Not Found" - }, { "userlang query parameter takes precedence over Accept-Language", /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article?userlang=en", /*Accept-Language:*/ "test", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "Not Found" }, { @@ -1314,7 +1294,6 @@ TEST_F(ServerTest, UserLanguageControl) // with quality values) the most suitable language is selected. /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article", /*Accept-Language:*/ "test;q=0.9, en;q=0.2", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" }, { @@ -1323,7 +1302,6 @@ TEST_F(ServerTest, UserLanguageControl) // with quality values) the most suitable language is selected. /*url*/ "/ROOT%23%3F/content/zimfile/invalid-article", /*Accept-Language:*/ "test;q=0.2, en;q=0.9", - /*Request Cookie:*/ NO_COOKIE, /* expected

*/ "Not Found" }, }; @@ -1335,9 +1313,6 @@ TEST_F(ServerTest, UserLanguageControl) if ( !t.acceptLanguageHeader.empty() ) { headers.insert({"Accept-Language", t.acceptLanguageHeader}); } - if ( t.requestCookie ) { - headers.insert({"Cookie", t.requestCookie}); - } const auto r = zfs1_->GET(t.url.c_str(), headers); EXPECT_FALSE(r->has_header("Set-Cookie")); std::regex_search(r->body, h1Match, h1Regex); From 258a6d029fbc7b1ac3271a98986fa793434dbe5d Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sun, 28 Jan 2024 16:43:07 +0400 Subject: [PATCH 2/5] Changed the format of skin/languages.js ... so that extra info about the count of translated strings can be added. Note that due to increased size skin/languages.js lost its too-small-to-be-worth-compressing status. --- static/generate_i18n_resources_list.py | 6 +- static/skin/i18n.js | 6 +- static/skin/languages.js | 102 ++++++++++++++-------- test/server.cpp | 116 ++++++++++++++++--------- 4 files changed, 150 insertions(+), 80 deletions(-) diff --git a/static/generate_i18n_resources_list.py b/static/generate_i18n_resources_list.py index 037ab77a1..8145cf119 100755 --- a/static/generate_i18n_resources_list.py +++ b/static/generate_i18n_resources_list.py @@ -47,7 +47,11 @@ with open(resource_file, 'w', encoding="utf-8") as f: print(f"Warning: missing 'name' in {i18n_file.name}") f.write(str(i18n_file.relative_to(script_path.parent)) + '\n') -language_list = [{name: code} for code, name in sorted(language_list)] +def make_language_entry(lang_info_tuple): + iso_code, self_name = lang_info_tuple + return dict(iso_code=iso_code, self_name=self_name) + +language_list = [make_language_entry(x) for x in sorted(language_list)] language_list_jsobj_str = json.dumps(language_list, indent=2, ensure_ascii=False) diff --git a/static/skin/i18n.js b/static/skin/i18n.js index 2a44cc500..e937eda3e 100644 --- a/static/skin/i18n.js +++ b/static/skin/i18n.js @@ -164,10 +164,8 @@ function initUILanguageSelector(activeLanguage, languageChangeCallback) { } const languageSelector = document.getElementById("ui_language"); for (const lang of uiLanguages ) { - const lang_name = Object.getOwnPropertyNames(lang)[0]; - const lang_code = lang[lang_name]; - const is_selected = lang_code == activeLanguage; - languageSelector.appendChild(new Option(lang_name, lang_code, is_selected, is_selected)); + const is_selected = lang.iso_code == activeLanguage; + languageSelector.appendChild(new Option(lang.self_name, lang.iso_code, is_selected, is_selected)); } languageSelector.onchange = languageChangeCallback; } diff --git a/static/skin/languages.js b/static/skin/languages.js index df90303a8..ead06ef11 100644 --- a/static/skin/languages.js +++ b/static/skin/languages.js @@ -1,104 +1,138 @@ const uiLanguages = [ { - "الإنجليزية": "ar" + "iso_code": "ar", + "self_name": "الإنجليزية" }, { - "বাংলা": "bn" + "iso_code": "bn", + "self_name": "বাংলা" }, { - "Čeština": "cs" + "iso_code": "cs", + "self_name": "Čeština" }, { - "Deutsch": "de" + "iso_code": "de", + "self_name": "Deutsch" }, { - "English": "en" + "iso_code": "en", + "self_name": "English" }, { - "español": "es" + "iso_code": "es", + "self_name": "español" }, { - "suomi": "fi" + "iso_code": "fi", + "self_name": "suomi" }, { - "Français": "fr" + "iso_code": "fr", + "self_name": "Français" }, { - "עברית": "he" + "iso_code": "he", + "self_name": "עברית" }, { - "हिन्दी": "hi" + "iso_code": "hi", + "self_name": "हिन्दी" }, { - "Հայերեն": "hy" + "iso_code": "hy", + "self_name": "Հայերեն" }, { - "interlingua": "ia" + "iso_code": "ia", + "self_name": "interlingua" }, { - "italiano": "it" + "iso_code": "it", + "self_name": "italiano" }, { - "日本語": "ja" + "iso_code": "ja", + "self_name": "日本語" }, { - "한국어": "ko" + "iso_code": "ko", + "self_name": "한국어" }, { - "kurdî": "ku-latn" + "iso_code": "ku-latn", + "self_name": "kurdî" }, { - "Lëtzebuergesch": "lb" + "iso_code": "lb", + "self_name": "Lëtzebuergesch" }, { - "македонски": "mk" + "iso_code": "mk", + "self_name": "македонски" }, { - "Bahasa Melayu": "ms" + "iso_code": "ms", + "self_name": "Bahasa Melayu" }, { - "Nederlands": "nl" + "iso_code": "nl", + "self_name": "Nederlands" }, { - "ߒߞߏ": "nqo" + "iso_code": "nqo", + "self_name": "ߒߞߏ" }, { - "ଓଡ଼ିଆ": "or" + "iso_code": "or", + "self_name": "ଓଡ଼ିଆ" }, { - "Polski": "pl" + "iso_code": "pl", + "self_name": "Polski" }, { - "русский": "ru" + "iso_code": "ru", + "self_name": "русский" }, { - "Sardu": "sc" + "iso_code": "sc", + "self_name": "Sardu" }, { - "slovenčina": "sk" + "iso_code": "sk", + "self_name": "slovenčina" }, { - "سرائیکی": "skr-arab" + "iso_code": "skr-arab", + "self_name": "سرائیکی" }, { - "slovenščina": "sl" + "iso_code": "sl", + "self_name": "slovenščina" }, { - "Shqip": "sq" + "iso_code": "sq", + "self_name": "Shqip" }, { - "Svenska": "sv" + "iso_code": "sv", + "self_name": "Svenska" }, { - "ఇంగ్లీషు": "te" + "iso_code": "te", + "self_name": "ఇంగ్లీషు" }, { - "Türkçe": "tr" + "iso_code": "tr", + "self_name": "Türkçe" }, { - "英语": "zh-hans" + "iso_code": "zh-hans", + "self_name": "英语" }, { - "繁體中文": "zh-hant" + "iso_code": "zh-hant", + "self_name": "繁體中文" } ] \ No newline at end of file diff --git a/test/server.cpp b/test/server.cpp index f47e06149..1cd17ad89 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -59,7 +59,7 @@ const ResourceCollection resources200Compressible{ { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/autoComplete/css/autoComplete.css" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/autoComplete/css/autoComplete.css?cacheid=ef30cd42" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/i18n.js" }, - { STATIC_CONTENT, "/ROOT%23%3F/skin/i18n.js?cacheid=4ab55b42" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/i18n.js?cacheid=0b99e7a9" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.css" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/index.css?cacheid=1e78e7cf" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.js" }, @@ -83,6 +83,8 @@ const ResourceCollection resources200Compressible{ { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/i18n/test.json" }, // TODO: implement cache management of i18n resources //{ STATIC_CONTENT, "/ROOT%23%3F/skin/i18n/test.json?cacheid=unknown" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/languages.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/languages.js?cacheid=792fee65" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/search" }, @@ -148,8 +150,6 @@ const ResourceCollection resources200Uncompressible{ { STATIC_CONTENT, "/ROOT%23%3F/skin/search-icon.svg?cacheid=b10ae7ed" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/search_results.css" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/search_results.css?cacheid=76d39c84" }, - { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/languages.js" }, - { STATIC_CONTENT, "/ROOT%23%3F/skin/languages.js?cacheid=96f2cf73" }, { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Title" }, { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Description" }, @@ -285,8 +285,8 @@ R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/kiwix.css?cacheid=2158fad9" - - + + @@ -318,8 +318,8 @@ R"EXPECTEDRESULT( - - + + const blankPageUrl = root + "/skin/blank.html?cacheid=6b1fa032"; @@ -1126,106 +1126,140 @@ TEST_F(ServerTest, UserLanguageList) EXPECT_EQ(r->body, R"EXPECTEDRESPONSE(const uiLanguages = [ { - "الإنجليزية": "ar" + "iso_code": "ar", + "self_name": "الإنجليزية" }, { - "বাংলা": "bn" + "iso_code": "bn", + "self_name": "বাংলা" }, { - "Čeština": "cs" + "iso_code": "cs", + "self_name": "Čeština" }, { - "Deutsch": "de" + "iso_code": "de", + "self_name": "Deutsch" }, { - "English": "en" + "iso_code": "en", + "self_name": "English" }, { - "español": "es" + "iso_code": "es", + "self_name": "español" }, { - "suomi": "fi" + "iso_code": "fi", + "self_name": "suomi" }, { - "Français": "fr" + "iso_code": "fr", + "self_name": "Français" }, { - "עברית": "he" + "iso_code": "he", + "self_name": "עברית" }, { - "हिन्दी": "hi" + "iso_code": "hi", + "self_name": "हिन्दी" }, { - "Հայերեն": "hy" + "iso_code": "hy", + "self_name": "Հայերեն" }, { - "interlingua": "ia" + "iso_code": "ia", + "self_name": "interlingua" }, { - "italiano": "it" + "iso_code": "it", + "self_name": "italiano" }, { - "日本語": "ja" + "iso_code": "ja", + "self_name": "日本語" }, { - "한국어": "ko" + "iso_code": "ko", + "self_name": "한국어" }, { - "kurdî": "ku-latn" + "iso_code": "ku-latn", + "self_name": "kurdî" }, { - "Lëtzebuergesch": "lb" + "iso_code": "lb", + "self_name": "Lëtzebuergesch" }, { - "македонски": "mk" + "iso_code": "mk", + "self_name": "македонски" }, { - "Bahasa Melayu": "ms" + "iso_code": "ms", + "self_name": "Bahasa Melayu" }, { - "Nederlands": "nl" + "iso_code": "nl", + "self_name": "Nederlands" }, { - "ߒߞߏ": "nqo" + "iso_code": "nqo", + "self_name": "ߒߞߏ" }, { - "ଓଡ଼ିଆ": "or" + "iso_code": "or", + "self_name": "ଓଡ଼ିଆ" }, { - "Polski": "pl" + "iso_code": "pl", + "self_name": "Polski" }, { - "русский": "ru" + "iso_code": "ru", + "self_name": "русский" }, { - "Sardu": "sc" + "iso_code": "sc", + "self_name": "Sardu" }, { - "slovenčina": "sk" + "iso_code": "sk", + "self_name": "slovenčina" }, { - "سرائیکی": "skr-arab" + "iso_code": "skr-arab", + "self_name": "سرائیکی" }, { - "slovenščina": "sl" + "iso_code": "sl", + "self_name": "slovenščina" }, { - "Shqip": "sq" + "iso_code": "sq", + "self_name": "Shqip" }, { - "Svenska": "sv" + "iso_code": "sv", + "self_name": "Svenska" }, { - "ఇంగ్లీషు": "te" + "iso_code": "te", + "self_name": "ఇంగ్లీషు" }, { - "Türkçe": "tr" + "iso_code": "tr", + "self_name": "Türkçe" }, { - "英语": "zh-hans" + "iso_code": "zh-hans", + "self_name": "英语" }, { - "繁體中文": "zh-hant" + "iso_code": "zh-hant", + "self_name": "繁體中文" } ])EXPECTEDRESPONSE"); } From 1f44465d09a5c017815d5ffdd1edc9b217230085 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sun, 28 Jan 2024 17:03:33 +0400 Subject: [PATCH 3/5] Added translation counts to skin/languages.js Note that static/skin/languages.js must be generated/updated manually by running the static/generate_i18n_resources_list.py script. Previously it had to be done only when new languages were added. Now the translation counts will also need to be updated when new entries are added to static/skin/i18n/en.json or upon merging a few translatewiki PRs. --- static/generate_i18n_resources_list.py | 15 ++-- static/skin/languages.js | 102 +++++++++++++++-------- test/server.cpp | 108 ++++++++++++++++--------- 3 files changed, 146 insertions(+), 79 deletions(-) diff --git a/static/generate_i18n_resources_list.py b/static/generate_i18n_resources_list.py index 8145cf119..0fc1d0682 100755 --- a/static/generate_i18n_resources_list.py +++ b/static/generate_i18n_resources_list.py @@ -30,7 +30,10 @@ def get_translation_info(filepath): with open(filepath, 'r', encoding="utf-8") as f: content = json.load(f) lang_name = content.get("name") - return lang_code, lang_name + translation_count = len(content) + return dict(iso_code=lang_code, + self_name=lang_name, + translation_count=translation_count) language_list = [] json_files = translation_dir.glob("*.json") @@ -40,18 +43,14 @@ with open(resource_file, 'w', encoding="utf-8") as f: continue print("Processing", i18n_file.name) if i18n_file.name != "test.json": - lang_code, lang_name = get_translation_info(i18n_file) + translation_info = get_translation_info(i18n_file) + lang_name = translation_info["self_name"] if lang_name: - language_list.append((lang_code, lang_name)) + language_list.append(translation_info) else: print(f"Warning: missing 'name' in {i18n_file.name}") f.write(str(i18n_file.relative_to(script_path.parent)) + '\n') -def make_language_entry(lang_info_tuple): - iso_code, self_name = lang_info_tuple - return dict(iso_code=iso_code, self_name=self_name) - -language_list = [make_language_entry(x) for x in sorted(language_list)] language_list_jsobj_str = json.dumps(language_list, indent=2, ensure_ascii=False) diff --git a/static/skin/languages.js b/static/skin/languages.js index ead06ef11..bcddf8114 100644 --- a/static/skin/languages.js +++ b/static/skin/languages.js @@ -1,138 +1,172 @@ const uiLanguages = [ { "iso_code": "ar", - "self_name": "الإنجليزية" + "self_name": "الإنجليزية", + "translation_count": 25 }, { "iso_code": "bn", - "self_name": "বাংলা" + "self_name": "বাংলা", + "translation_count": 12 }, { "iso_code": "cs", - "self_name": "Čeština" + "self_name": "Čeština", + "translation_count": 25 }, { "iso_code": "de", - "self_name": "Deutsch" + "self_name": "Deutsch", + "translation_count": 49 }, { "iso_code": "en", - "self_name": "English" + "self_name": "English", + "translation_count": 53 }, { "iso_code": "es", - "self_name": "español" + "self_name": "español", + "translation_count": 48 }, { "iso_code": "fi", - "self_name": "suomi" + "self_name": "suomi", + "translation_count": 22 }, { "iso_code": "fr", - "self_name": "Français" + "self_name": "Français", + "translation_count": 52 }, { "iso_code": "he", - "self_name": "עברית" + "self_name": "עברית", + "translation_count": 52 }, { "iso_code": "hi", - "self_name": "हिन्दी" + "self_name": "हिन्दी", + "translation_count": 49 }, { "iso_code": "hy", - "self_name": "Հայերեն" + "self_name": "Հայերեն", + "translation_count": 15 }, { "iso_code": "ia", - "self_name": "interlingua" + "self_name": "interlingua", + "translation_count": 49 }, { "iso_code": "it", - "self_name": "italiano" + "self_name": "italiano", + "translation_count": 29 }, { "iso_code": "ja", - "self_name": "日本語" + "self_name": "日本語", + "translation_count": 26 }, { "iso_code": "ko", - "self_name": "한국어" + "self_name": "한국어", + "translation_count": 13 }, { "iso_code": "ku-latn", - "self_name": "kurdî" + "self_name": "kurdî", + "translation_count": 26 }, { "iso_code": "lb", - "self_name": "Lëtzebuergesch" + "self_name": "Lëtzebuergesch", + "translation_count": 22 }, { "iso_code": "mk", - "self_name": "македонски" + "self_name": "македонски", + "translation_count": 52 }, { "iso_code": "ms", - "self_name": "Bahasa Melayu" + "self_name": "Bahasa Melayu", + "translation_count": 14 }, { "iso_code": "nl", - "self_name": "Nederlands" + "self_name": "Nederlands", + "translation_count": 49 }, { "iso_code": "nqo", - "self_name": "ߒߞߏ" + "self_name": "ߒߞߏ", + "translation_count": 43 }, { "iso_code": "or", - "self_name": "ଓଡ଼ିଆ" + "self_name": "ଓଡ଼ିଆ", + "translation_count": 49 }, { "iso_code": "pl", - "self_name": "Polski" + "self_name": "Polski", + "translation_count": 24 }, { "iso_code": "ru", - "self_name": "русский" + "self_name": "русский", + "translation_count": 45 }, { "iso_code": "sc", - "self_name": "Sardu" + "self_name": "Sardu", + "translation_count": 49 }, { "iso_code": "sk", - "self_name": "slovenčina" + "self_name": "slovenčina", + "translation_count": 25 }, { "iso_code": "skr-arab", - "self_name": "سرائیکی" + "self_name": "سرائیکی", + "translation_count": 20 }, { "iso_code": "sl", - "self_name": "slovenščina" + "self_name": "slovenščina", + "translation_count": 52 }, { "iso_code": "sq", - "self_name": "Shqip" + "self_name": "Shqip", + "translation_count": 49 }, { "iso_code": "sv", - "self_name": "Svenska" + "self_name": "Svenska", + "translation_count": 52 }, { "iso_code": "te", - "self_name": "ఇంగ్లీషు" + "self_name": "ఇంగ్లీషు", + "translation_count": 49 }, { "iso_code": "tr", - "self_name": "Türkçe" + "self_name": "Türkçe", + "translation_count": 25 }, { "iso_code": "zh-hans", - "self_name": "英语" + "self_name": "英语", + "translation_count": 16 }, { "iso_code": "zh-hant", - "self_name": "繁體中文" + "self_name": "繁體中文", + "translation_count": 52 } ] \ No newline at end of file diff --git a/test/server.cpp b/test/server.cpp index 1cd17ad89..c22bffe88 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -84,7 +84,7 @@ const ResourceCollection resources200Compressible{ // TODO: implement cache management of i18n resources //{ STATIC_CONTENT, "/ROOT%23%3F/skin/i18n/test.json?cacheid=unknown" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/languages.js" }, - { STATIC_CONTENT, "/ROOT%23%3F/skin/languages.js?cacheid=792fee65" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/languages.js?cacheid=c41aae47" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/search" }, @@ -286,7 +286,7 @@ R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/kiwix.css?cacheid=2158fad9" - + @@ -319,7 +319,7 @@ R"EXPECTEDRESULT( - + const blankPageUrl = root + "/skin/blank.html?cacheid=6b1fa032"; @@ -1127,139 +1127,173 @@ TEST_F(ServerTest, UserLanguageList) R"EXPECTEDRESPONSE(const uiLanguages = [ { "iso_code": "ar", - "self_name": "الإنجليزية" + "self_name": "الإنجليزية", + "translation_count": 25 }, { "iso_code": "bn", - "self_name": "বাংলা" + "self_name": "বাংলা", + "translation_count": 12 }, { "iso_code": "cs", - "self_name": "Čeština" + "self_name": "Čeština", + "translation_count": 25 }, { "iso_code": "de", - "self_name": "Deutsch" + "self_name": "Deutsch", + "translation_count": 49 }, { "iso_code": "en", - "self_name": "English" + "self_name": "English", + "translation_count": 53 }, { "iso_code": "es", - "self_name": "español" + "self_name": "español", + "translation_count": 48 }, { "iso_code": "fi", - "self_name": "suomi" + "self_name": "suomi", + "translation_count": 22 }, { "iso_code": "fr", - "self_name": "Français" + "self_name": "Français", + "translation_count": 52 }, { "iso_code": "he", - "self_name": "עברית" + "self_name": "עברית", + "translation_count": 52 }, { "iso_code": "hi", - "self_name": "हिन्दी" + "self_name": "हिन्दी", + "translation_count": 49 }, { "iso_code": "hy", - "self_name": "Հայերեն" + "self_name": "Հայերեն", + "translation_count": 15 }, { "iso_code": "ia", - "self_name": "interlingua" + "self_name": "interlingua", + "translation_count": 49 }, { "iso_code": "it", - "self_name": "italiano" + "self_name": "italiano", + "translation_count": 29 }, { "iso_code": "ja", - "self_name": "日本語" + "self_name": "日本語", + "translation_count": 26 }, { "iso_code": "ko", - "self_name": "한국어" + "self_name": "한국어", + "translation_count": 13 }, { "iso_code": "ku-latn", - "self_name": "kurdî" + "self_name": "kurdî", + "translation_count": 26 }, { "iso_code": "lb", - "self_name": "Lëtzebuergesch" + "self_name": "Lëtzebuergesch", + "translation_count": 22 }, { "iso_code": "mk", - "self_name": "македонски" + "self_name": "македонски", + "translation_count": 52 }, { "iso_code": "ms", - "self_name": "Bahasa Melayu" + "self_name": "Bahasa Melayu", + "translation_count": 14 }, { "iso_code": "nl", - "self_name": "Nederlands" + "self_name": "Nederlands", + "translation_count": 49 }, { "iso_code": "nqo", - "self_name": "ߒߞߏ" + "self_name": "ߒߞߏ", + "translation_count": 43 }, { "iso_code": "or", - "self_name": "ଓଡ଼ିଆ" + "self_name": "ଓଡ଼ିଆ", + "translation_count": 49 }, { "iso_code": "pl", - "self_name": "Polski" + "self_name": "Polski", + "translation_count": 24 }, { "iso_code": "ru", - "self_name": "русский" + "self_name": "русский", + "translation_count": 45 }, { "iso_code": "sc", - "self_name": "Sardu" + "self_name": "Sardu", + "translation_count": 49 }, { "iso_code": "sk", - "self_name": "slovenčina" + "self_name": "slovenčina", + "translation_count": 25 }, { "iso_code": "skr-arab", - "self_name": "سرائیکی" + "self_name": "سرائیکی", + "translation_count": 20 }, { "iso_code": "sl", - "self_name": "slovenščina" + "self_name": "slovenščina", + "translation_count": 52 }, { "iso_code": "sq", - "self_name": "Shqip" + "self_name": "Shqip", + "translation_count": 49 }, { "iso_code": "sv", - "self_name": "Svenska" + "self_name": "Svenska", + "translation_count": 52 }, { "iso_code": "te", - "self_name": "ఇంగ్లీషు" + "self_name": "ఇంగ్లీషు", + "translation_count": 49 }, { "iso_code": "tr", - "self_name": "Türkçe" + "self_name": "Türkçe", + "translation_count": 25 }, { "iso_code": "zh-hans", - "self_name": "英语" + "self_name": "英语", + "translation_count": 16 }, { "iso_code": "zh-hant", - "self_name": "繁體中文" + "self_name": "繁體中文", + "translation_count": 52 } ])EXPECTEDRESPONSE"); } From e5dab19844cef1051a0eac187a9f6765f063ccd0 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sun, 28 Jan 2024 17:51:50 +0400 Subject: [PATCH 4/5] Default UI language is resolved in the frontend This change eliminates any need for defaultUserLanguage in viewer_settings.js. --- static/skin/i18n.js | 24 ++++++++++++++++++++++-- test/server.cpp | 6 +++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/static/skin/i18n.js b/static/skin/i18n.js index e937eda3e..ed92d6f34 100644 --- a/static/skin/i18n.js +++ b/static/skin/i18n.js @@ -104,11 +104,31 @@ const DEFAULT_UI_LANGUAGE = 'en'; Translations.load(DEFAULT_UI_LANGUAGE, /*asDefault=*/true); +// Below function selects the most suitable UI language from the list +// of preferred languages in browser preferences and available translations. +// Since, unlike Accept-Language header, navigator.languages doesn't contain +// qvalues, they are computed using the same algorithm as in Firefox 121 +function getDefaultUserLanguage() { + const mostSuitableLang = { code: DEFAULT_UI_LANGUAGE, score: 0 } + const n = navigator.languages.length; + for (const lang of uiLanguages ) { + const rank = navigator.languages.indexOf(lang.iso_code); + if ( rank >= 0 ) { + const qvalue = Math.round(10*(1 - rank/n))/10; + const score = qvalue * lang.translation_count; + if ( score > mostSuitableLang.score ) { + mostSuitableLang.code = lang.iso_code; + mostSuitableLang.score = score; + } + } + } + return mostSuitableLang.code; +} + function getUserLanguage() { return new URLSearchParams(window.location.search).get('userlang') || window.localStorage.getItem('userlang') - || viewerSettings.defaultUserLanguage - || DEFAULT_UI_LANGUAGE; + || getDefaultUserLanguage(); } function setUserLanguage(lang, callback) { diff --git a/test/server.cpp b/test/server.cpp index c22bffe88..74e082c16 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -59,7 +59,7 @@ const ResourceCollection resources200Compressible{ { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/autoComplete/css/autoComplete.css" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/autoComplete/css/autoComplete.css?cacheid=ef30cd42" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/i18n.js" }, - { STATIC_CONTENT, "/ROOT%23%3F/skin/i18n.js?cacheid=0b99e7a9" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/i18n.js?cacheid=071abc9a" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.css" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/index.css?cacheid=1e78e7cf" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.js" }, @@ -285,7 +285,7 @@ R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/kiwix.css?cacheid=2158fad9" - + @@ -318,7 +318,7 @@ R"EXPECTEDRESULT( - + From c6976110643fdcfd846c8ce245ce6fd840554fe9 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sun, 28 Jan 2024 17:58:16 +0400 Subject: [PATCH 5/5] Dropped defaultUserLanguage from viewer_settings.js --- src/server/internalServer.cpp | 3 +- static/templates/viewer_settings.js | 3 +- test/server.cpp | 51 +++-------------------------- 3 files changed, 6 insertions(+), 51 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 2c1ea518c..5c9d733d9 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -767,8 +767,7 @@ std::unique_ptr InternalServer::handle_viewer_settings(const RequestCo const kainjow::mustache::object data{ {"enable_toolbar", m_withTaskbar ? "true" : "false" }, {"enable_link_blocking", m_blockExternalLinks ? "true" : "false" }, - {"enable_library_button", m_withLibraryButton ? "true" : "false" }, - {"default_user_language", request.get_user_language() } + {"enable_library_button", m_withLibraryButton ? "true" : "false" } }; return ContentResponse::build(RESOURCE::templates::viewer_settings_js, data, "application/javascript; charset=utf-8"); } diff --git a/static/templates/viewer_settings.js b/static/templates/viewer_settings.js index 37669bb17..0ccf6579f 100644 --- a/static/templates/viewer_settings.js +++ b/static/templates/viewer_settings.js @@ -1,6 +1,5 @@ const viewerSettings = { toolbarEnabled: {{enable_toolbar}}, linkBlockingEnabled: {{enable_link_blocking}}, - libraryButtonEnabled: {{enable_library_button}}, - defaultUserLanguage: "{{default_user_language}}" + libraryButtonEnabled: {{enable_library_button}} } diff --git a/test/server.cpp b/test/server.cpp index 74e082c16..5a0bd072e 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -2077,8 +2077,7 @@ TEST_F(ServerTest, viewerSettings) R"(const viewerSettings = { toolbarEnabled: false, linkBlockingEnabled: false, - libraryButtonEnabled: false, - defaultUserLanguage: "en" + libraryButtonEnabled: false } )"); } @@ -2089,8 +2088,7 @@ R"(const viewerSettings = { R"(const viewerSettings = { toolbarEnabled: false, linkBlockingEnabled: true, - libraryButtonEnabled: false, - defaultUserLanguage: "en" + libraryButtonEnabled: false } )"); } @@ -2101,8 +2099,7 @@ R"(const viewerSettings = { R"(const viewerSettings = { toolbarEnabled: true, linkBlockingEnabled: false, - libraryButtonEnabled: false, - defaultUserLanguage: "en" + libraryButtonEnabled: false } )"); } @@ -2113,47 +2110,7 @@ R"(const viewerSettings = { R"(const viewerSettings = { toolbarEnabled: true, linkBlockingEnabled: false, - libraryButtonEnabled: true, - defaultUserLanguage: "en" -} -)"); - } - - { - resetServer(ZimFileServer::WITH_TASKBAR_AND_LIBRARY_BUTTON); - const Headers headers{ {"Accept-Language", "fr"} }; - ASSERT_EQ(zfs1_->GET("/ROOT%23%3F/viewer_settings.js", headers)->body, -R"(const viewerSettings = { - toolbarEnabled: true, - linkBlockingEnabled: false, - libraryButtonEnabled: true, - defaultUserLanguage: "fr" -} -)"); - } - - { - resetServer(ZimFileServer::WITH_TASKBAR_AND_LIBRARY_BUTTON); - const Headers headers{ {"Accept-Language", "test;q=0.2, en;q=0.9"} }; - ASSERT_EQ(zfs1_->GET("/ROOT%23%3F/viewer_settings.js", headers)->body, -R"(const viewerSettings = { - toolbarEnabled: true, - linkBlockingEnabled: false, - libraryButtonEnabled: true, - defaultUserLanguage: "en" -} -)"); - } - - { - resetServer(ZimFileServer::WITH_TASKBAR_AND_LIBRARY_BUTTON); - const Headers headers{ {"Accept-Language", "test;q=0.9, en;q=0.2"} }; - ASSERT_EQ(zfs1_->GET("/ROOT%23%3F/viewer_settings.js", headers)->body, -R"(const viewerSettings = { - toolbarEnabled: true, - linkBlockingEnabled: false, - libraryButtonEnabled: true, - defaultUserLanguage: "test" + libraryButtonEnabled: true } )"); }