diff --git a/filenames.gni b/filenames.gni index b29f238fb2070..990ac47f5e4c8 100644 --- a/filenames.gni +++ b/filenames.gni @@ -487,6 +487,10 @@ filenames = { "shell/common/keyboard_util.h", "shell/common/deprecate_util.cc", "shell/common/deprecate_util.h", + "shell/common/language_util.h", + "shell/common/language_util_linux.cc", + "shell/common/language_util_mac.mm", + "shell/common/language_util_win.cc", "shell/common/mouse_util.cc", "shell/common/mouse_util.h", "shell/common/mac/main_application_bundle.h", diff --git a/shell/browser/api/atom_api_web_contents.cc b/shell/browser/api/atom_api_web_contents.cc index f2f8af84d89d4..39ee0f7ed7d8e 100644 --- a/shell/browser/api/atom_api_web_contents.cc +++ b/shell/browser/api/atom_api_web_contents.cc @@ -72,6 +72,7 @@ #include "shell/common/api/atom_api_native_image.h" #include "shell/common/api/event_emitter_caller.h" #include "shell/common/color_util.h" +#include "shell/common/language_util.h" #include "shell/common/mouse_util.h" #include "shell/common/native_mate_converters/blink_converter.h" #include "shell/common/native_mate_converters/content_converter.h" @@ -481,7 +482,22 @@ void WebContents::InitWithSessionAndOptions( managed_web_contents()->GetView()->SetDelegate(this); auto* prefs = web_contents()->GetMutableRendererPrefs(); - prefs->accept_languages = g_browser_process->GetApplicationLocale(); + + // Collect preferred languages from OS and browser process. accept_languages + // effects HTTP header, navigator.languages, and CJK fallback font selection. + // + // Note that an application locale set to the browser process might be + // different with the one set to the preference list. + // (e.g. overridden with --lang) + std::string accept_languages = + g_browser_process->GetApplicationLocale() + ","; + for (auto const& language : electron::GetPreferredLanguages()) { + if (language == g_browser_process->GetApplicationLocale()) + continue; + accept_languages += language + ","; + } + accept_languages.pop_back(); + prefs->accept_languages = accept_languages; #if defined(OS_LINUX) || defined(OS_WIN) // Update font settings. diff --git a/shell/common/language_util.h b/shell/common/language_util.h new file mode 100644 index 0000000000000..3e35073c142cc --- /dev/null +++ b/shell/common/language_util.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_COMMON_LANGUAGE_UTIL_H_ +#define SHELL_COMMON_LANGUAGE_UTIL_H_ + +#include +#include + +#include "base/strings/string16.h" + +namespace electron { + +// Return a list of user preferred languages from OS. The list doesn't include +// overrides from command line arguments. +std::vector GetPreferredLanguages(); + +#if defined(OS_WIN) +bool GetPreferredLanguagesUsingGlobalization( + std::vector* languages); +#endif + +} // namespace electron + +#endif // SHELL_COMMON_LANGUAGE_UTIL_H_ diff --git a/shell/common/language_util_linux.cc b/shell/common/language_util_linux.cc new file mode 100644 index 0000000000000..76bc557741ba0 --- /dev/null +++ b/shell/common/language_util_linux.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#include "ui/base/l10n/l10n_util.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + // Return empty as there's no API to use. You may be able to use + // GetApplicationLocale() of a browser process. + return std::vector{}; +} + +} // namespace electron diff --git a/shell/common/language_util_mac.mm b/shell/common/language_util_mac.mm new file mode 100644 index 0000000000000..205d7087bf1f8 --- /dev/null +++ b/shell/common/language_util_mac.mm @@ -0,0 +1,25 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#import +#include +#include + +#include "base/strings/sys_string_conversions.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + __block std::vector languages; + [[NSLocale preferredLanguages] + enumerateObjectsUsingBlock:^(NSString* language, NSUInteger i, + BOOL* stop) { + languages.push_back(base::SysNSStringToUTF8(language)); + }]; + return languages; +} + +} // namespace electron diff --git a/shell/common/language_util_win.cc b/shell/common/language_util_win.cc new file mode 100644 index 0000000000000..d6717b31aec21 --- /dev/null +++ b/shell/common/language_util_win.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#include +#include +#include + +#include "base/strings/sys_string_conversions.h" +#include "base/win/core_winrt_util.h" +#include "base/win/i18n.h" +#include "base/win/win_util.h" +#include "base/win/windows_version.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + std::vector languages16; + + // Attempt to use API available on Windows 10 or later, which + // returns the full list of language preferences. + if (!GetPreferredLanguagesUsingGlobalization(&languages16)) { + base::win::i18n::GetThreadPreferredUILanguageList(&languages16); + } + + std::vector languages; + for (const auto& language : languages16) { + languages.push_back(base::SysWideToUTF8(language)); + } + return languages; +} + +bool GetPreferredLanguagesUsingGlobalization( + std::vector* languages) { + if (base::win::GetVersion() < base::win::Version::WIN10) + return false; + if (!base::win::ResolveCoreWinRTDelayload() || + !base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) + return false; + + base::win::ScopedHString guid = base::win::ScopedHString::Create( + RuntimeClass_Windows_System_UserProfile_GlobalizationPreferences); + Microsoft::WRL::ComPtr< + ABI::Windows::System::UserProfile::IGlobalizationPreferencesStatics> + prefs; + + HRESULT hr = + base::win::RoGetActivationFactory(guid.get(), IID_PPV_ARGS(&prefs)); + if (FAILED(hr)) + return false; + + ABI::Windows::Foundation::Collections::IVectorView* langs; + hr = prefs->get_Languages(&langs); + if (FAILED(hr)) + return false; + + unsigned size; + hr = langs->get_Size(&size); + if (FAILED(hr)) + return false; + + for (unsigned i = 0; i < size; ++i) { + HSTRING hstr; + hr = langs->GetAt(i, &hstr); + if (SUCCEEDED(hr)) { + base::WStringPiece str = base::win::ScopedHString(hstr).Get(); + languages->emplace_back(str.data(), str.size()); + } + } + + return true; +} + +} // namespace electron diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index fd2456b5f70ff..63d637187a0d2 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -187,10 +187,14 @@ describe('chromium feature', () => { }); }); - describe('navigator.languages', (done) => { - it('should return the system locale only', () => { + describe('navigator.languages', () => { + it('should return the system locale only', async () => { const appLocale = app.getLocale(); - expect(navigator.languages).to.deep.equal([appLocale]); + w = new BrowserWindow({ show: false }); + await w.loadURL('about:blank'); + const languages = await w.webContents.executeJavaScript('navigator.languages'); + expect(languages.length).to.be.greaterThan(0); + expect(languages).to.contain(appLocale); }); });