在网页的user-agent中,会标注出当前系统的语言,但是有时获取的语言并不是系统语言,始终是默认语言,特意通过代码来学习获取系统语言的过程。
首先在网页的js代码中,通过navigator的language这个属性来获取,navigator可以理解为一个浏览器的对象,包含了浏览器的基本信息,其中language属性就是当前系统的默认语言。
在chromium中,有一个navigator类来对应js中引用的navigator,navigator的定义如下
在src/third_party/WebKit/Source/core/frame/Navigator.h中:
namespace blink {
class LocalFrame;
class CORE_EXPORT Navigator final : public GarbageCollected<Navigator>,
public NavigatorCPU,
public NavigatorID,
public NavigatorLanguage,
public NavigatorOnLine,
public ScriptWrappable,
public DOMWindowClient,
public Supplementable<Navigator> {
DEFINE_WRAPPERTYPEINFO();
USING_GARBAGE_COLLECTED_MIXIN(Navigator);
public:
static Navigator* create(LocalFrame* frame) { return new Navigator(frame); }
...
// NavigatorLanguage
Vector<String> languages() override;
...
DECLARE_VIRTUAL_TRACE();
private:
explicit Navigator(LocalFrame*);
};
} // namespace blink
以上代码中,主要是NavaigatorLanguage这个类,如下:
namespace blink {
class CORE_EXPORT NavigatorLanguage {
public:
NavigatorLanguage();
AtomicString language();
virtual Vector<String> languages() = 0;
bool hasLanguagesChanged();
void setLanguagesChanged();
private:
bool m_languagesChanged;
};
其中language()的实现如下:
AtomicString NavigatorLanguage::language() {
return defaultLanguage();
}
} // namespace blink
接下来,我们继续看看defaultLanguage()这个方法,
通过代码跳转,defaultLanguage()这个方法实现在 /src/third_party/Webkit/Source/platform/Language.cpp中,如下:
AtomicString defaultLanguage() {
Vector<AtomicString>& override = preferredLanguagesOverride();
if (!override.isEmpty())
return override[0];
return platformLanguage();
}
static const AtomicString& platformLanguage() {
DEFINE_STATIC_LOCAL(AtomicString, computedDefaultLanguage, ());
if (computedDefaultLanguage.isEmpty()) {
computedDefaultLanguage = AtomicString(
canonicalizeLanguageIdentifier(Platform::current()->defaultLocale()));
ASSERT(!computedDefaultLanguage.isEmpty());
}
return computedDefaultLanguage;
}
在defaultLanguage()中调用了同样在该文件中定义的platformLanguage()方法,而在platformLanguage()中,最终调用了defaultLocale()方法,
再次跳转到defaultLocale()的定义中,
/src/third_party/Webkit/public/platform/Platform.h 中:
// Returns a value such as "en-US".
virtual WebString defaultLocale() { return WebString();
}
从以上代码中知道,defaultLocale()被定义为一个虚函数,所以要找到override的地方,
根据子类列表中,确定了一个子类,/src/content/renderer/renderer_blink_platform_impl.cc中,如下:
WebString RendererBlinkPlatformImpl::defaultLocale() {
return WebString::fromASCII(RenderThread::Get()->GetLocale());
}
接下来,继续追踪GetLocale()这个方法,
/src/content/public/renderer/render_thread.h中, 看到其声明:
virtual std::string GetLocale() = 0;
可以看到,GetLocale()方法是一个纯虚函数,所以要继续找到override该函数的地方
/src/content/renderer/render_thread_impl.cc 中,进行了override
std::string RenderThreadImpl::GetLocale() {
// The browser process should have passed the locale to the renderer via the
// --lang command line flag.
const base::CommandLine& parsed_command_line =
*base::CommandLine::ForCurrentProcess();
const std::string& lang =
parsed_command_line.GetSwitchValueASCII(switches::kLang);
DCHECK(!lang.empty());
return lang;
}
在以上代码中,看到其中的注释,locale是browser进程通过command line 传递到render进程,所以现在代码就进入到browser进程中,从之前的文件路径可以看出,
之前的操作都是在render中,现在就要开始进入到browser进程中,查找对kLanguage引用:
/src/ui/base/ui_base_switches.cc中,有KLang的定义:
// The language file that we want to try to open. Of the form
// language[-country] where language is the 2 letter code from ISO-639.
const char kLang[] = "lang";
kLang的引用,如下:
/src/content/browser/renderer_host/render_process_host_impl.cc中:
void RenderProcessHostImpl::AppendRendererCommandLine(
base::CommandLine* command_line) {
// Pass the process type first, so it shows first in process listings.
command_line->AppendSwitchASCII(switches::kProcessType,
switches::kRendererProcess);
...
// Now send any options from our own command line we want to propagate.
const base::CommandLine& browser_command_line =
*base::CommandLine::ForCurrentProcess();
PropagateBrowserCommandLineToRenderer(browser_command_line, command_line);
// Pass on the browser locale.
const std::string locale =
GetContentClient()->browser()->GetApplicationLocale();
command_line->AppendSwitchASCII(switches::kLang, locale);
...
}
在以上函数中,可以看到klang的值是通过GetContentClient()->browser()->GetApplicationLocale()这个方法来获取的,那么接下来就看看这个方法
/src/content/public/browser/content_browser_client.h中
// Returns the locale used by the application.
// This is called on the UI and IO threads.
virtual std::string GetApplicationLocale();
其实现:
std::string ContentBrowserClient::GetApplicationLocale() {
return "en-US";
}
可以看到,此处默认的是 “en-US”
目前通过测试,chromium在实际运行中,就是通过这个默认方法来返回当前的语言,所以要想设置语言,必须override这个方法,从设置browser的语言,
所以此处可以参考,chrome_browser_client来看看chrome是怎么处理这个语言值的。