diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 587af89e7d..31445bfa41 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -562,6 +562,10 @@ String OS::get_locale() const { return ::OS::get_singleton()->get_locale(); } +Vector OS::get_preferred_locales() const { + return ::OS::get_singleton()->get_preferred_locales(); +} + String OS::get_locale_language() const { return ::OS::get_singleton()->get_locale_language(); } @@ -818,6 +822,7 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &OS::delay_usec); ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &OS::delay_msec); ClassDB::bind_method(D_METHOD("get_locale"), &OS::get_locale); + ClassDB::bind_method(D_METHOD("get_preferred_locales"), &OS::get_preferred_locales); ClassDB::bind_method(D_METHOD("get_locale_language"), &OS::get_locale_language); ClassDB::bind_method(D_METHOD("get_model_name"), &OS::get_model_name); diff --git a/core/core_bind.h b/core/core_bind.h index 7a6f17d7c4..ad138a4a28 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -249,6 +249,7 @@ public: Vector get_video_adapter_driver_info() const; String get_locale() const; + Vector get_preferred_locales() const; String get_locale_language() const; String get_model_name() const; diff --git a/core/os/os.h b/core/os/os.h index fd7b0c896a..c523c9dd8b 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -308,6 +308,7 @@ public: bool is_separate_thread_rendering_enabled() const { return _separate_thread_render; } virtual String get_locale() const; + virtual Vector get_preferred_locales() const { return Vector{ get_locale() }; } String get_locale_language() const; virtual uint64_t get_embedded_pck_offset() const; diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index ba8a14b0a8..21a9262439 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -337,8 +337,8 @@ - [code]language[/code] - 2 or 3-letter [url=https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes]language code[/url], in lower case. - [code skip-lint]Script[/code] - 4-letter [url=https://en.wikipedia.org/wiki/ISO_15924]script code[/url], in title case. - [code]COUNTRY[/code] - 2 or 3-letter [url=https://en.wikipedia.org/wiki/ISO_3166-1]country code[/url], in upper case. - - [code]VARIANT[/code] - language variant, region and sort order. The variant can have any number of underscored keywords. - - [code]extra[/code] - semicolon separated list of additional key words. This may include currency, calendar, sort order and numbering system information. + - [code]VARIANT[/code] - language variant, region, and sort order. The variant can have any number of underscored keywords. + - [code]extra[/code] - semicolon separated list of additional key words. This may include currency, calendar, sort order, and numbering system information. If you want only the language code and not the fully specified locale from the OS, you can use [method get_locale_language]. @@ -433,6 +433,18 @@ [b]Note:[/b] On Web platforms, it is still possible to determine the host platform's OS with feature tags. See [method has_feature]. + + + + Returns an array of locales preferred by the user, in order of preference. Each locale is as a [String] on the form [code]language_Script_COUNTRY_VARIANT@extra[/code]. Every substring after [code]language[/code] is optional and may not exist. + - [code]language[/code] - 2 or 3-letter [url=https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes]language code[/url], in lower case. + - [code skip-lint]Script[/code] - 4-letter [url=https://en.wikipedia.org/wiki/ISO_15924]script code[/url], in title case. + - [code]COUNTRY[/code] - 2 or 3-letter [url=https://en.wikipedia.org/wiki/ISO_3166-1]country code[/url], in upper case. + - [code]VARIANT[/code] - language variant, region, and sort order. The variant can have any number of underscored keywords. + - [code]extra[/code] - semicolon separated list of additional key words. This may include currency, calendar, sort order, and numbering system information. + [b]Note:[/b] This method is implemented on macOS, iOS, and Windows. + + diff --git a/drivers/apple_embedded/os_apple_embedded.h b/drivers/apple_embedded/os_apple_embedded.h index d801a8652d..c7c3753508 100644 --- a/drivers/apple_embedded/os_apple_embedded.h +++ b/drivers/apple_embedded/os_apple_embedded.h @@ -123,6 +123,7 @@ public: virtual String get_bundle_resource_dir() const override; virtual String get_locale() const override; + virtual Vector get_preferred_locales() const override; virtual String get_unique_id() const override; virtual String get_processor_name() const override; diff --git a/drivers/apple_embedded/os_apple_embedded.mm b/drivers/apple_embedded/os_apple_embedded.mm index e624c7e4cd..6c91da7ad7 100644 --- a/drivers/apple_embedded/os_apple_embedded.mm +++ b/drivers/apple_embedded/os_apple_embedded.mm @@ -460,6 +460,18 @@ String OS_AppleEmbedded::get_locale() const { return String::utf8([localeIdentifier UTF8String]).replace_char('-', '_'); } +Vector OS_AppleEmbedded::get_preferred_locales() const { + Vector out; + for (NSString *locale_code in [NSLocale preferredLanguages]) { + out.push_back(String([locale_code UTF8String]).replace_char('-', '_')); + } + if (out.is_empty()) { + NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier]; + out.push_back(String::utf8([localeIdentifier UTF8String]).replace_char('-', '_')); + } + return out; +} + String OS_AppleEmbedded::get_unique_id() const { NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString; return String::utf8([uuid UTF8String]); diff --git a/editor/settings/editor_settings.cpp b/editor/settings/editor_settings.cpp index 9182680ec7..62474a53ef 100644 --- a/editor/settings/editor_settings.cpp +++ b/editor/settings/editor_settings.cpp @@ -59,7 +59,6 @@ #include "scene/gui/file_dialog.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" -#include "scene/main/window.h" #include "scene/resources/animation.h" #include "servers/display/display_server.h" @@ -423,32 +422,35 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { /* Languages */ { - String lang_hint; - const String host_lang = OS::get_singleton()->get_locale(); - // Skip locales which we can't render properly. const LocalVector locales_to_skip = _get_skipped_locales(); if (!locales_to_skip.is_empty()) { WARN_PRINT("Some locales are not properly supported by selected Text Server and are disabled."); } + String lang_hint; String best = "en"; int best_score = 0; - for (const String &locale : get_editor_locales()) { - // Test against language code without regional variants (e.g. ur_PK). - String lang_code = locale.get_slicec('_', 0); - if (locales_to_skip.has(lang_code)) { - continue; + for (const String &host_lang : OS::get_singleton()->get_preferred_locales()) { + for (const String &locale : get_editor_locales()) { + // Test against language code without regional variants (e.g. ur_PK). + String lang_code = locale.get_slicec('_', 0); + if (locales_to_skip.has(lang_code)) { + continue; + } + + lang_hint += ";"; + const String lang_name = TranslationServer::get_singleton()->get_locale_name(locale); + lang_hint += vformat("%s/[%s] %s", locale, locale, lang_name); + + int score = TranslationServer::get_singleton()->compare_locales(host_lang, locale); + if (score > 0 && score >= best_score) { + best = locale; + best_score = score; + } } - - lang_hint += ";"; - const String lang_name = TranslationServer::get_singleton()->get_locale_name(locale); - lang_hint += vformat("%s/[%s] %s", locale, locale, lang_name); - - int score = TranslationServer::get_singleton()->compare_locales(host_lang, locale); - if (score > 0 && score >= best_score) { - best = locale; - best_score = score; + if (best_score > 0) { + break; } } lang_hint = vformat(";auto/Auto (%s);en/[en] English", TranslationServer::get_singleton()->get_locale_name(best)) + lang_hint; @@ -2031,21 +2033,25 @@ String EditorSettings::get_language() const { if (auto_language.is_empty()) { // Skip locales which we can't render properly. const LocalVector locales_to_skip = _get_skipped_locales(); - const String host_lang = OS::get_singleton()->get_locale(); String best = "en"; int best_score = 0; - for (const String &locale : get_editor_locales()) { - // Test against language code without regional variants (e.g. ur_PK). - String lang_code = locale.get_slicec('_', 0); - if (locales_to_skip.has(lang_code)) { - continue; - } + for (const String &host_lang : OS::get_singleton()->get_preferred_locales()) { + for (const String &locale : get_editor_locales()) { + // Test against language code without regional variants (e.g. ur_PK). + String lang_code = locale.get_slicec('_', 0); + if (locales_to_skip.has(lang_code)) { + continue; + } - int score = TranslationServer::get_singleton()->compare_locales(host_lang, locale); - if (score > 0 && score >= best_score) { - best = locale; - best_score = score; + int score = TranslationServer::get_singleton()->compare_locales(host_lang, locale); + if (score > 0 && score >= best_score) { + best = locale; + best_score = score; + } + } + if (best_score > 0) { + break; } } auto_language = best; diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h index 5c311d1f34..08c26d992e 100644 --- a/platform/macos/os_macos.h +++ b/platform/macos/os_macos.h @@ -130,6 +130,7 @@ public: virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder) override; virtual String get_locale() const override; + virtual Vector get_preferred_locales() const override; virtual Vector get_system_fonts() const override; virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override; diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index 620666c35d..bd4df9c5a3 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -585,6 +585,14 @@ String OS_MacOS::get_locale() const { return String([locale_code UTF8String]).replace_char('-', '_'); } +Vector OS_MacOS::get_preferred_locales() const { + Vector out; + for (NSString *locale_code in [NSLocale preferredLanguages]) { + out.push_back(String([locale_code UTF8String]).replace_char('-', '_')); + } + return out; +} + Vector OS_MacOS::get_system_fonts() const { HashSet font_names; CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 04d4783ef9..1ee8718648 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -34,6 +34,7 @@ #include "lang_table.h" #include "windows_terminal_logger.h" #include "windows_utils.h" +#include "winrt_utils.h" #include "core/config/engine.h" #include "core/debugger/engine_debugger.h" @@ -2292,6 +2293,14 @@ String OS_Windows::get_locale() const { return "en"; } +Vector OS_Windows::get_preferred_locales() const { + Vector out = WinRTUtils::get_preferred_locales(); + if (out.is_empty()) { + out.push_back(get_locale()); + } + return out; +} + String OS_Windows::get_model_name() const { HKEY hkey; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Hardware\\Description\\System\\BIOS", 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 07c133ab73..2eeb11c871 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -220,6 +220,7 @@ public: virtual String get_executable_path() const override; virtual String get_locale() const override; + virtual Vector get_preferred_locales() const override; virtual String get_processor_name() const override; diff --git a/platform/windows/winrt_utils.cpp b/platform/windows/winrt_utils.cpp index 3b2401bdf3..08e58b0539 100644 --- a/platform/windows/winrt_utils.cpp +++ b/platform/windows/winrt_utils.cpp @@ -43,8 +43,10 @@ GODOT_CLANG_WARNING_PUSH GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor") #include +#include #include #include +#include #include #include #include @@ -109,7 +111,9 @@ GODOT_CLANG_WARNING_POP using namespace winrt::Windows::Graphics::Display; using namespace winrt::Windows::System; +using namespace winrt::Windows::System::UserProfile; using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::Foundation::Metadata; using namespace winrt::Windows::UI::ViewManagement::Core; @@ -143,6 +147,16 @@ void WinRTUtils::destroy_queue() { ERR_FAIL_COND_MSG(action.Status() == AsyncStatus::Error, "DispatcherQueueController shutdown failed."); } +Vector WinRTUtils::get_preferred_locales() { + Vector out; + IVectorView languages = GlobalizationPreferences::Languages(); + for (uint32_t i = 0; i < languages.Size(); i++) { + winrt::hstring lang = languages.GetAt(i); + out.push_back(String::utf16((const char16_t *)lang.c_str(), lang.size()).replace_char('-', '_')); + } + return out; +} + bool WinRTUtils::try_show_onecore_emoji_picker() { if (ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 7)) { // Windows 10, 1809+ return CoreInputView::GetForCurrentView().TryShow(CoreInputViewKind::Emoji); @@ -218,6 +232,10 @@ bool WinRTUtils::create_queue() { void WinRTUtils::destroy_queue() {} +Vector WinRTUtils::get_preferred_locales() { + return Vector(); +} + bool WinRTUtils::window_has_display_info(const WinRTWindowData *p_data) { return false; } diff --git a/platform/windows/winrt_utils.h b/platform/windows/winrt_utils.h index ea4f2b709f..611b0c5554 100644 --- a/platform/windows/winrt_utils.h +++ b/platform/windows/winrt_utils.h @@ -45,6 +45,8 @@ public: static bool create_queue(); static void destroy_queue(); + static Vector get_preferred_locales(); + static WinRTWindowData *create_wd(HWND p_window, const Callable &p_color_cb, int64_t p_window_id); static bool window_has_display_info(const WinRTWindowData *p_data); static void window_get_advanced_color_info(const WinRTWindowData *p_data, bool &r_hdr_supported, float &r_min_luminance, float &r_max_luminance, float &r_max_average_luminance, float &r_sdr_white_level);