Browse Source

feat: add app.getSystemLocale() method (#35697)

* feat: add app.getSystemLocale() method

* Update shell/browser/electron_browser_main_parts.cc

Co-authored-by: Charles Kerr <[email protected]>

* Change methods to be const

* Apply PR feedback

* Fix mac compile

* Add missing scope

* Apply style changes

* Change note

* Add braces to get the comment indentation right

* Change to static

* Apply PR feedback

* Fix the documentation

* Remove extraneous file

Co-authored-by: Charles Kerr <[email protected]>
Raymond Zhao 2 years ago
parent
commit
8a0b4fa338

+ 7 - 1
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

+ 12 - 0
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<BrowserProcessImpl*>(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)

+ 1 - 0
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<const uint8_t> additional_data);

+ 8 - 0
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;
 }

+ 4 - 0
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<PrefService> local_state_;
   std::string locale_;
+  std::string system_locale_;
 };
 
 #endif  // ELECTRON_SHELL_BROWSER_BROWSER_PROCESS_IMPL_H_

+ 11 - 2
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<views::LayoutProvider>();
+  }
+
+  // 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);

+ 1 - 0
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)

+ 14 - 0
shell/browser/electron_browser_main_parts_mac.mm

@@ -4,6 +4,8 @@
 
 #include "shell/browser/electron_browser_main_parts.h"
 
+#include <string>
+
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "base/path_service.h"
@@ -74,4 +76,16 @@ void ElectronBrowserMainParts::InitializeMainNib() {
   [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

+ 6 - 0
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();

+ 4 - 2
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 () => {

+ 1 - 1
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();