From ab5202c103a54448de1917b4b99fe333be89701d Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 17 Feb 2024 16:46:46 +0000 Subject: [PATCH 1/2] Add ability to change language without a restart --- builtin/mainmenu/content/dlg_contentstore.lua | 13 ++ builtin/mainmenu/settings/components.lua | 11 ++ builtin/mainmenu/settings/dlg_settings.lua | 147 +++++++++--------- src/gettext.cpp | 129 +++++++++------ src/gettext.h | 7 + src/main.cpp | 4 + util/updatepo.sh | 1 + 7 files changed, 195 insertions(+), 117 deletions(-) diff --git a/builtin/mainmenu/content/dlg_contentstore.lua b/builtin/mainmenu/content/dlg_contentstore.lua index 1d6b30cf6..e77d95254 100644 --- a/builtin/mainmenu/content/dlg_contentstore.lua +++ b/builtin/mainmenu/content/dlg_contentstore.lua @@ -74,6 +74,19 @@ local REASON_UPDATE = "update" local REASON_DEPENDENCY = "dependency" +function reset_contentdb() + store.load_ok = false + store.loading = false + store.load_error = false + store.packages = {} + store.packages_full = {} + store.packages_full_unordered = {} + store.aliases = {} + search_string = "" + cur_page = 1 +end + + local function get_download_url(package, reason) local base_url = core.settings:get("contentdb_url") local ret = base_url .. ("/packages/%s/releases/%d/download/"):format( diff --git a/builtin/mainmenu/settings/components.lua b/builtin/mainmenu/settings/components.lua index 51cc0c95b..d6168c3f3 100644 --- a/builtin/mainmenu/settings/components.lua +++ b/builtin/mainmenu/settings/components.lua @@ -163,6 +163,9 @@ function make.enum(setting) self.resettable = core.settings:has(setting.name) local labels = setting.option_labels or {} + if type(labels) == "function" then + labels = labels(self, setting, value) + end local items = {} for i, option in ipairs(setting.values) do @@ -187,6 +190,14 @@ function make.enum(setting) end core.settings:set(setting.name, value) + + if setting.name == "language" then + -- Refresh content to update translations + pkgmgr.refresh_globals() + pkgmgr.update_gamelist() + reset_contentdb() + end + return true end, } diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index d6bbf2570..82c5b4d99 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -15,6 +15,11 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +-- Capture translations without translating +local function gettext_lazy(x) + return x +end + local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM .. "settings" .. DIR_DELIM .. "components.lua") @@ -65,23 +70,23 @@ local change_keys = { add_page({ id = "accessibility", - title = fgettext_ne("Accessibility"), + title = gettext_lazy("Accessibility"), content = { "language", - { heading = fgettext_ne("General") }, + { heading = gettext_lazy("General") }, "font_size", "chat_font_size", "gui_scaling", "hud_scaling", "show_nametag_backgrounds", - { heading = fgettext_ne("Chat") }, + { heading = gettext_lazy("Chat") }, "console_height", "console_alpha", "console_color", - { heading = fgettext_ne("Controls") }, + { heading = gettext_lazy("Controls") }, "autojump", "safe_dig_and_place", - { heading = fgettext_ne("Movement") }, + { heading = gettext_lazy("Movement") }, "arm_inertia", "view_bobbing_amount", "fall_bobbing_amount", @@ -96,7 +101,7 @@ local function load_settingtypes() if not page then page = add_page({ id = (section or "general"):lower():gsub(" ", "_"), - title = section or fgettext_ne("General"), + title = section or gettext_lazy("General"), section = section, content = {}, }) @@ -120,7 +125,7 @@ local function load_settingtypes() elseif entry.level == 2 then ensure_page_started() page.content[#page.content + 1] = { - heading = fgettext_ne(entry.readable_name or entry.name), + heading = entry.readable_name or entry.name, } end else @@ -153,68 +158,70 @@ end -- These must not be translated, as they need to show in the local -- language no matter the user's current language. -- This list must be kept in sync with src/unsupported_language_list.txt. -get_setting_info("language").option_labels = { - [""] = fgettext_ne("(Use system language)"), - --ar = " [ar]", blacklisted - be = "Беларуская [be]", - bg = "Български [bg]", - ca = "Català [ca]", - cs = "Česky [cs]", - cy = "Cymraeg [cy]", - da = "Dansk [da]", - de = "Deutsch [de]", - --dv = " [dv]", blacklisted - el = "Ελληνικά [el]", - en = "English [en]", - eo = "Esperanto [eo]", - es = "Español [es]", - et = "Eesti [et]", - eu = "Euskara [eu]", - fi = "Suomi [fi]", - fil = "Wikang Filipino [fil]", - fr = "Français [fr]", - gd = "Gàidhlig [gd]", - gl = "Galego [gl]", - --he = " [he]", blacklisted - --hi = " [hi]", blacklisted - hu = "Magyar [hu]", - id = "Bahasa Indonesia [id]", - it = "Italiano [it]", - ja = "日本語 [ja]", - jbo = "Lojban [jbo]", - kk = "Қазақша [kk]", - --kn = " [kn]", blacklisted - ko = "한국어 [ko]", - ky = "Kırgızca / Кыргызча [ky]", - lt = "Lietuvių [lt]", - lv = "Latviešu [lv]", - mn = "Монгол [mn]", - mr = "मराठी [mr]", - ms = "Bahasa Melayu [ms]", - --ms_Arab = " [ms_Arab]", blacklisted - nb = "Norsk Bokmål [nb]", - nl = "Nederlands [nl]", - nn = "Norsk Nynorsk [nn]", - oc = "Occitan [oc]", - pl = "Polski [pl]", - pt = "Português [pt]", - pt_BR = "Português do Brasil [pt_BR]", - ro = "Română [ro]", - ru = "Русский [ru]", - sk = "Slovenčina [sk]", - sl = "Slovenščina [sl]", - sr_Cyrl = "Српски [sr_Cyrl]", - sr_Latn = "Srpski (Latinica) [sr_Latn]", - sv = "Svenska [sv]", - sw = "Kiswahili [sw]", - --th = " [th]", blacklisted - tr = "Türkçe [tr]", - tt = "Tatarça [tt]", - uk = "Українська [uk]", - vi = "Tiếng Việt [vi]", - zh_CN = "中文 (简体) [zh_CN]", - zh_TW = "正體中文 (繁體) [zh_TW]", -} +get_setting_info("language").option_labels = function() + return { + [""] = fgettext_ne("(Use system language)"), + --ar = " [ar]", blacklisted + be = "Беларуская [be]", + bg = "Български [bg]", + ca = "Català [ca]", + cs = "Česky [cs]", + cy = "Cymraeg [cy]", + da = "Dansk [da]", + de = "Deutsch [de]", + --dv = " [dv]", blacklisted + el = "Ελληνικά [el]", + en = "English [en]", + eo = "Esperanto [eo]", + es = "Español [es]", + et = "Eesti [et]", + eu = "Euskara [eu]", + fi = "Suomi [fi]", + fil = "Wikang Filipino [fil]", + fr = "Français [fr]", + gd = "Gàidhlig [gd]", + gl = "Galego [gl]", + --he = " [he]", blacklisted + --hi = " [hi]", blacklisted + hu = "Magyar [hu]", + id = "Bahasa Indonesia [id]", + it = "Italiano [it]", + ja = "日本語 [ja]", + jbo = "Lojban [jbo]", + kk = "Қазақша [kk]", + --kn = " [kn]", blacklisted + ko = "한국어 [ko]", + ky = "Kırgızca / Кыргызча [ky]", + lt = "Lietuvių [lt]", + lv = "Latviešu [lv]", + mn = "Монгол [mn]", + mr = "मराठी [mr]", + ms = "Bahasa Melayu [ms]", + --ms_Arab = " [ms_Arab]", blacklisted + nb = "Norsk Bokmål [nb]", + nl = "Nederlands [nl]", + nn = "Norsk Nynorsk [nn]", + oc = "Occitan [oc]", + pl = "Polski [pl]", + pt = "Português [pt]", + pt_BR = "Português do Brasil [pt_BR]", + ro = "Română [ro]", + ru = "Русский [ru]", + sk = "Slovenčina [sk]", + sl = "Slovenščina [sl]", + sr_Cyrl = "Српски [sr_Cyrl]", + sr_Latn = "Srpski (Latinica) [sr_Latn]", + sv = "Svenska [sv]", + sw = "Kiswahili [sw]", + --th = " [th]", blacklisted + tr = "Türkçe [tr]", + tt = "Tatarça [tt]", + uk = "Українська [uk]", + vi = "Tiếng Việt [vi]", + zh_CN = "中文 (简体) [zh_CN]", + zh_TW = "正體中文 (繁體) [zh_TW]", + } +end -- See if setting matches keywords @@ -420,7 +427,7 @@ local function build_page_components(page) elseif item.get_formspec then retval[i] = item elseif item.heading then - retval[i] = component_funcs.heading(item.heading) + retval[i] = component_funcs.heading(fgettext_ne(item.heading)) end end return retval diff --git a/src/gettext.cpp b/src/gettext.cpp index 68b6f728a..972519116 100644 --- a/src/gettext.cpp +++ b/src/gettext.cpp @@ -164,57 +164,33 @@ static void MSVC_LocaleWorkaround(int argc, char* argv[]) #endif +namespace { + std::string system_language; +} + +/* + * System translations + * + * Gettext determines the current language by reading env variables in the following order: + * LANGUAGE, LC_ALL, LC_, LANG. + * + * To change the language, Minetest sets LANGUAGE. + * + * To set the language as the system language, LANGUAGE just needs to be restored + * to the value Minetest was started with. We don't need to touch any of the other + * environment variables + */ + /******************************************************************************/ void init_gettext(const char *path, const std::string &configured_language, int argc, char *argv[]) { #if USE_GETTEXT - // First, try to set user override environment - if (!configured_language.empty()) { - // Set LANGUAGE which overrides all others, see - // -#ifndef _MSC_VER - setenv("LANGUAGE", configured_language.c_str(), 1); + char *system_language_c = getenv("LANGUAGE"); + if (system_language_c) + system_language = system_language_c; - // Reload locale with changed environment - setlocale(LC_ALL, ""); -#else - std::string current_language; - const char *env_lang = getenv("LANGUAGE"); - if (env_lang) - current_language = env_lang; - - setenv("LANGUAGE", configured_language.c_str(), 1); - SetEnvironmentVariableA("LANGUAGE", configured_language.c_str()); - -#ifndef SERVER - // Hack to force gettext to see the right environment - if (current_language != configured_language) - MSVC_LocaleWorkaround(argc, argv); -#else - errorstream << "*******************************************************" << std::endl; - errorstream << "Can't apply locale workaround for server!" << std::endl; - errorstream << "Expect language to be broken!" << std::endl; - errorstream << "*******************************************************" << std::endl; -#endif - - setlocale(LC_ALL, configured_language.c_str()); -#endif // ifdef _MSC_VER - } else { - /* set current system default locale */ - setlocale(LC_ALL, ""); - } - -#if defined(_WIN32) - if (getenv("LANGUAGE") != 0) { - setlocale(LC_ALL, getenv("LANGUAGE")); - } -#ifdef _MSC_VER - else if (getenv("LANG") != 0) { - setlocale(LC_ALL, getenv("LANG")); - } -#endif -#endif + set_gettext_language(configured_language); std::string name = lowercase(PROJECT_NAME); infostream << "Gettext: domainname=\"" << name @@ -232,14 +208,73 @@ void init_gettext(const char *path, const std::string &configured_language, #endif #else - /* set current system default locale */ + /* If gettext is not enabled, still set the default locales */ setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); #endif // if USE_GETTEXT +} + + +void set_gettext_language(std::string configured_language) +{ +#if USE_GETTEXT + if (configured_language.empty()) + configured_language = system_language; + +#ifdef _MSC_VER + std::string current_language; + const char *env_lang = getenv("LANGUAGE"); + if (env_lang) + current_language = env_lang; +#endif + + if (!configured_language.empty()) { + // Set LANGUAGE which overrides all others, see + // + + setenv("LANGUAGE", configured_language.c_str(), 1); +#ifdef _MSC_VER + SetEnvironmentVariableA("LANGUAGE", configured_language.c_str()); + + // Reload locale with changed environment + setlocale(LC_ALL, configured_language.c_str()); +#else + // Reload locale with changed environment + setlocale(LC_ALL, ""); +#endif + } else { + unsetenv("LANGUAGE"); +#ifdef _MSC_VER + SetEnvironmentVariableA("LANGUAGE", NULL); +#endif + setlocale(LC_ALL, ""); + } + +#ifdef _MSC_VER +#ifndef SERVER + // Hack to force gettext to see the right environment + if (current_language != configured_language) + MSVC_LocaleWorkaround(argc, argv); +#else + errorstream << "*******************************************************" << std::endl; + errorstream << "Can't apply locale workaround for server!" << std::endl; + errorstream << "Expect language to be broken!" << std::endl; + errorstream << "*******************************************************" << std::endl; +#endif +#endif + + // Notify gettext that the language has changed + // Source: https://www.gnu.org/software/gettext/manual/html_node/gettext-grok.html + { + extern int _nl_msg_cat_cntr; + ++_nl_msg_cat_cntr; + } +#endif /* no matter what locale is used we need number format to be "C" */ /* to ensure formspec parameters are evaluated correctly! */ - setlocale(LC_NUMERIC, "C"); + infostream << "Message locale is now set to: " << setlocale(LC_ALL, 0) << std::endl; } diff --git a/src/gettext.h b/src/gettext.h index 042729c1a..3679a56bd 100644 --- a/src/gettext.h +++ b/src/gettext.h @@ -46,6 +46,13 @@ with this program; if not, write to the Free Software Foundation, Inc., void init_gettext(const char *path, const std::string &configured_language, int argc, char *argv[]); +/** + * Change the language + * + * @param configured_language Language code + */ +void set_gettext_language(std::string configured_language); + inline std::string strgettext(const char *str) { // We must check here that is not an empty string to avoid trying to translate it diff --git a/src/main.cpp b/src/main.cpp index e7ec6dd53..0c8f35b02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -708,6 +708,10 @@ static bool init_common(const Settings &cmd_args, int argc, char *argv[]) init_gettext(porting::path_locale.c_str(), g_settings->get("language"), argc, argv); + g_settings->registerChangedCallback("language", [](auto _name, auto _data) { + set_gettext_language(g_settings->get("language")); + }); + return true; } diff --git a/util/updatepo.sh b/util/updatepo.sh index 22593a5dc..e30f0a250 100755 --- a/util/updatepo.sh +++ b/util/updatepo.sh @@ -58,6 +58,7 @@ xgettext --package-name=minetest \ --keyword=fwgettext \ --keyword=fgettext \ --keyword=fgettext_ne \ + --keyword=gettext_lazy \ --keyword=strgettext \ --keyword=wstrgettext \ --keyword=core.gettext \ From c78c566ef3ee0cbd7cfccac2b519e8dcdc484225 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 3 Mar 2024 15:50:39 +0000 Subject: [PATCH 2/2] Fix main menu tab titles being cached --- builtin/mainmenu/tab_about.lua | 4 +++- builtin/mainmenu/tab_local.lua | 4 +++- builtin/mainmenu/tab_online.lua | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/builtin/mainmenu/tab_about.lua b/builtin/mainmenu/tab_about.lua index 8c5573399..d411754fe 100644 --- a/builtin/mainmenu/tab_about.lua +++ b/builtin/mainmenu/tab_about.lua @@ -116,7 +116,9 @@ end return { name = "about", - caption = fgettext("About"), + caption = function() + return fgettext("About") + end, cbf_formspec = function(tabview, name, tabdata) local logofile = defaulttexturedir .. "logo.png" diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index ff1a22398..a36944fc5 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -459,7 +459,9 @@ end -------------------------------------------------------------------------------- return { name = "local", - caption = fgettext("Start Game"), + caption = function() + return fgettext("Start Game") + end , cbf_formspec = get_formspec, cbf_button_handler = main_button_handler, on_change = on_change diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index d93f45dcf..930505fe8 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -425,7 +425,9 @@ end return { name = "online", - caption = fgettext("Join Game"), + caption = function() + return fgettext("Join Game") + end, cbf_formspec = get_formspec, cbf_button_handler = main_button_handler, on_change = on_change