diff --git a/docs/api/app.md b/docs/api/app.md index 960bed3dc8782..624c5a1ebb054 100755 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -715,7 +715,7 @@ To set the locale, you'll want to use a command line switch at app startup, whic **Note:** When distributing your packaged app, you have to also ship the `locales` folder. -**Note:** On Windows, you have to call it after the `ready` events gets emitted. +**Note:** This API must be called after the `ready` event is emitted. ### `app.getLocaleCountryCode()` @@ -723,6 +723,12 @@ Returns `string` - User operating system's locale two-letter [ISO 3166](https:// **Note:** When unable to detect locale country code, it returns empty string. +### `app.getSystemLocale()` + +Returns `string` - The current system locale. On Windows and Linux, it is fetched using Chromium's `i18n` library. On macOS, the `NSLocale` object is used instead. + +**Note:** This API must be called after the `ready` event is emitted. + ### `app.addRecentDocument(path)` _macOS_ _Windows_ * `path` string diff --git a/shell/browser/api/electron_api_app.cc b/shell/browser/api/electron_api_app.cc index c2986535e0da8..2ba1e0adbe872 100644 --- a/shell/browser/api/electron_api_app.cc +++ b/shell/browser/api/electron_api_app.cc @@ -47,6 +47,7 @@ #include "shell/browser/api/electron_api_session.h" #include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/api/gpuinfo_manager.h" +#include "shell/browser/browser_process_impl.h" #include "shell/browser/electron_browser_context.h" #include "shell/browser/electron_browser_main_parts.h" #include "shell/browser/javascript_environment.h" @@ -1039,6 +1040,16 @@ std::string App::GetLocale() { return g_browser_process->GetApplicationLocale(); } +std::string App::GetSystemLocale(gin_helper::ErrorThrower thrower) const { + if (!Browser::Get()->is_ready()) { + thrower.ThrowError( + "app.getSystemLocale() can only be called " + "after app is ready"); + return std::string(); + } + return static_cast(g_browser_process)->GetSystemLocale(); +} + std::string App::GetLocaleCountryCode() { std::string region; #if BUILDFLAG(IS_WIN) @@ -1785,6 +1796,7 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) { .SetMethod("setAppLogsPath", &App::SetAppLogsPath) .SetMethod("setDesktopName", &App::SetDesktopName) .SetMethod("getLocale", &App::GetLocale) + .SetMethod("getSystemLocale", &App::GetSystemLocale) .SetMethod("getLocaleCountryCode", &App::GetLocaleCountryCode) #if BUILDFLAG(USE_NSS_CERTS) .SetMethod("importCertificate", &App::ImportCertificate) diff --git a/shell/browser/api/electron_api_app.h b/shell/browser/api/electron_api_app.h index ad53ea929085c..40f76b19148b1 100644 --- a/shell/browser/api/electron_api_app.h +++ b/shell/browser/api/electron_api_app.h @@ -191,6 +191,7 @@ class App : public ElectronBrowserClient::Delegate, void SetDesktopName(const std::string& desktop_name); std::string GetLocale(); std::string GetLocaleCountryCode(); + std::string GetSystemLocale(gin_helper::ErrorThrower thrower) const; void OnSecondInstance(const base::CommandLine& cmd, const base::FilePath& cwd, const std::vector additional_data); diff --git a/shell/browser/browser_process_impl.cc b/shell/browser/browser_process_impl.cc index 31653e104f8c5..1d6fccb5e0b5c 100644 --- a/shell/browser/browser_process_impl.cc +++ b/shell/browser/browser_process_impl.cc @@ -293,6 +293,14 @@ HidPolicyAllowedDevices* BrowserProcessImpl::hid_policy_allowed_devices() { return nullptr; } +void BrowserProcessImpl::SetSystemLocale(const std::string& locale) { + system_locale_ = locale; +} + +const std::string& BrowserProcessImpl::GetSystemLocale() const { + return system_locale_; +} + void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) { locale_ = locale; } diff --git a/shell/browser/browser_process_impl.h b/shell/browser/browser_process_impl.h index 3a135cade0cbb..05bcd3f3ba3a0 100644 --- a/shell/browser/browser_process_impl.h +++ b/shell/browser/browser_process_impl.h @@ -49,6 +49,9 @@ class BrowserProcessImpl : public BrowserProcess { void PostDestroyThreads() {} void PostMainMessageLoopRun(); + void SetSystemLocale(const std::string& locale); + const std::string& GetSystemLocale() const; + void EndSession() override {} void FlushLocalStateAndReply(base::OnceClosure reply) override {} bool IsShuttingDown() override; @@ -110,6 +113,7 @@ class BrowserProcessImpl : public BrowserProcess { #endif std::unique_ptr local_state_; std::string locale_; + std::string system_locale_; }; #endif // ELECTRON_SHELL_BROWSER_BROWSER_PROCESS_IMPL_H_ diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index 698e957421818..20c2d611d839d 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -11,6 +11,7 @@ #include "base/base_switches.h" #include "base/command_line.h" #include "base/feature_list.h" +#include "base/i18n/rtl.h" #include "base/metrics/field_trial.h" #include "base/path_service.h" #include "base/run_loop.h" @@ -282,8 +283,16 @@ void ElectronBrowserMainParts::PostEarlyInitialization() { } int ElectronBrowserMainParts::PreCreateThreads() { - if (!views::LayoutProvider::Get()) + if (!views::LayoutProvider::Get()) { layout_provider_ = std::make_unique(); + } + + // Fetch the system locale for Electron. +#if BUILDFLAG(IS_MAC) + fake_browser_process_->SetSystemLocale(GetCurrentSystemLocale()); +#else + fake_browser_process_->SetSystemLocale(base::i18n::GetConfiguredLocale()); +#endif auto* command_line = base::CommandLine::ForCurrentProcess(); std::string locale = command_line->GetSwitchValueASCII(::switches::kLang); @@ -320,7 +329,7 @@ int ElectronBrowserMainParts::PreCreateThreads() { } #endif - // Initialize the app locale. + // Initialize the app locale for Electron and Chromium. std::string app_locale = l10n_util::GetApplicationLocale(loaded_locale); ElectronBrowserClient::SetApplicationLocale(app_locale); fake_browser_process_->SetApplicationLocale(app_locale); diff --git a/shell/browser/electron_browser_main_parts.h b/shell/browser/electron_browser_main_parts.h index 3c45ed29bfbb7..76d5d2d0774fc 100644 --- a/shell/browser/electron_browser_main_parts.h +++ b/shell/browser/electron_browser_main_parts.h @@ -130,6 +130,7 @@ class ElectronBrowserMainParts : public content::BrowserMainParts { void FreeAppDelegate(); void RegisterURLHandler(); void InitializeMainNib(); + static std::string GetCurrentSystemLocale(); #endif #if BUILDFLAG(IS_MAC) diff --git a/shell/browser/electron_browser_main_parts_mac.mm b/shell/browser/electron_browser_main_parts_mac.mm index f280f66579f06..4ed64a912d769 100644 --- a/shell/browser/electron_browser_main_parts_mac.mm +++ b/shell/browser/electron_browser_main_parts_mac.mm @@ -4,6 +4,8 @@ #include "shell/browser/electron_browser_main_parts.h" +#include + #include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" #include "base/path_service.h" @@ -74,4 +76,16 @@ [mainNib release]; } +std::string ElectronBrowserMainParts::GetCurrentSystemLocale() { + NSString* systemLocaleIdentifier = + [[NSLocale currentLocale] localeIdentifier]; + + // Mac OS X uses "_" instead of "-", so swap to get a real locale value. + std::string locale_value = [[systemLocaleIdentifier + stringByReplacingOccurrencesOfString:@"_" + withString:@"-"] UTF8String]; + + return locale_value; +} + } // namespace electron diff --git a/spec/api-app-spec.ts b/spec/api-app-spec.ts index 574db87f90b58..60f51fbe1b92e 100644 --- a/spec/api-app-spec.ts +++ b/spec/api-app-spec.ts @@ -118,6 +118,12 @@ describe('app module', () => { }); }); + describe('app.getSystemLocale()', () => { + it('should not be empty', () => { + expect(app.getSystemLocale()).to.not.equal(''); + }); + }); + describe('app.getLocaleCountryCode()', () => { it('should be empty or have length of two', () => { const localeCountryCode = app.getLocaleCountryCode(); diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 0ee64aa1602eb..4a995860868c2 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -374,6 +374,7 @@ describe('command line switches', () => { }); describe('--lang switch', () => { const currentLocale = app.getLocale(); + const currentSystemLocale = app.getSystemLocale(); const testLocale = async (locale: string, result: string, printEnv: boolean = false) => { const appPath = path.join(fixturesPath, 'api', 'locale-check'); const args = [appPath, `--set-lang=${locale}`]; @@ -396,8 +397,9 @@ describe('command line switches', () => { expect(output).to.equal(result); }; - it('should set the locale', async () => testLocale('fr', 'fr')); - it('should not set an invalid locale', async () => testLocale('asdfkl', currentLocale)); + it('should set the locale', async () => testLocale('fr', `fr|${currentSystemLocale}`)); + it('should set the locale with country code', async () => testLocale('zh-CN', `zh-CN|${currentSystemLocale}`)); + it('should not set an invalid locale', async () => testLocale('asdfkl', `${currentLocale}|${currentSystemLocale}`)); const lcAll = String(process.env.LC_ALL); ifit(process.platform === 'linux')('current process has a valid LC_ALL env', async () => { diff --git a/spec/fixtures/api/locale-check/main.js b/spec/fixtures/api/locale-check/main.js index 929a9e0e9519a..dd4e6317dbb61 100644 --- a/spec/fixtures/api/locale-check/main.js +++ b/spec/fixtures/api/locale-check/main.js @@ -9,7 +9,7 @@ app.whenReady().then(() => { if (process.argv[3] === '--print-env') { process.stdout.write(String(process.env.LC_ALL)); } else { - process.stdout.write(app.getLocale()); + process.stdout.write(`${app.getLocale()}|${app.getSystemLocale()}`); } process.stdout.end();