{"id":126,"date":"2008-06-10T16:20:48","date_gmt":"2008-06-11T00:20:48","guid":{"rendered":"http:\/\/www.curlybrace.com\/words\/?p=126"},"modified":"2010-12-15T19:56:13","modified_gmt":"2010-12-16T03:56:13","slug":"setthreadlocale-and-setthreaduilanguage-for-localization-on-windows-xp-and-vista","status":"publish","type":"post","link":"https:\/\/www.curlybrace.com\/words\/2008\/06\/setthreadlocale-and-setthreaduilanguage-for-localization-on-windows-xp-and-vista\/","title":{"rendered":"SetThreadLocale and SetThreadUILanguage for Localization on Windows XP and Vista"},"content":{"rendered":"<p><\/p>\n<h3>Simple Localization<\/h3>\n<p>In classic Windows programming, the quickest way to handle localized resources is to put all languages into the same resource file, then use <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms776285.aspx\"><tt>SetThreadLocale<\/tt><\/a> to tell Windows that it should return resources tagged with the specified <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms776324(VS.85).aspx\">language identifier<\/a>.  Subsequently, any attempt to load a resource by that thread will cause Windows to prefer resources corresponding to the specified language code.<\/p>\n<h3>Vista Complications<\/h3>\n<p>As of Windows Vista, calling <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms776285.aspx\"><tt>SetThreadLocale<\/tt><\/a> will <i>appear<\/i> to work, but will have no effect.  Your application will continue to use load resources using whatever language identifier is set as the user&#8217;s default.<\/p>\n<p>Under Windows Vista, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms776236(VS.85).aspx\"><tt>SetThreadUILanguage<\/tt><\/a> must be called instead of SetThreadLocale.  This function is in Kernel32.dll.  It appears in Vista, Windows 2008, and some versions of Windows XP.  Under Windows XP, this function will <b>not<\/b> have the desired effect, and I have no idea what it does under Windows 2008.<\/p>\n<p>Since the function isn&#8217;t available on all systems, you will probably want to use <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms683212(VS.85).aspx\">GetProcAddress<\/a> to locate the function, and call it using a function pointer.  However, since the function appears on Windows XP, but does not behave in the desired way, it&#8217;s important to only call it on Windows Vista.<\/p>\n<h3>Resource Storage<\/h3>\n<p>To use the <tt>SetThreadLocale<\/tt> or <tt>SetThreadUILanguage<\/tt> methods, the resource file must first be populated with all required languages.  Each translated resource file should be appended to the <tt>.rc<\/tt> file, each one following an appropriate <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa381019(VS.85).aspx\"><tt>LANGUAGE<\/tt> statement<\/a> and corresponding <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms776324(VS.85).aspx\">language identifier<\/a>.  (note that you could use separate <tt>.rc<\/tt> files and include them, in which case <a href=\"http:\/\/support.microsoft.com\/kb\/264156\">there are gotchas<\/a>).<\/p>\n<h3>Is it Vista?<\/h3>\n<p>This function should reliably indicate whether the current OS is Windows Vista.  I use <a href=\"http:\/\/www.finalgear.com\/news\/2008\/06\/10\/commercial-for-the-new-season-of-top-gear\/\"><tt>GetVersionEx<\/tt><\/a>.   Note that, even though we&#8217;re using an <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms724833(VS.85).aspx\"><tt>OSVERSIONINFOEX<\/tt><\/a> structure, we have to cast its pointer so that it appears as a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms724834(VS.85).aspx\"><tt>OSVERSIONINFO<\/tt><\/a> pointer.  No guarantee of future compatibility is implied:<\/p>\n<blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">bool IsOSVerWindowsVista()\r\n{\r\n    OSVERSIONINFOEX osver;\r\n    ZeroMemory(&amp;osver, sizeof(osver);\r\n    osver.dwOSVersionInfoSize = sizeof(osver);\r\n\r\n    if (!GetVersionEx((OSVERSIONINFO *)&amp;osver))\r\n        return false;\r\n\r\n    \/\/ dwMajorVersion 6 and dwMinorVersion 0 means Vista or 2k8:\r\n    if ( (osver.dwMajorVersion != 6) || (osver.dwMinorVersion != 0) )\r\n        return false;\r\n\r\n    \/\/ wProductType of NT_WORKSTATION means it's Vista:\r\n    if (osver.wProductType != VER_NT_WORKSTATION)\r\n        return false;\r\n\r\n    return true;   \r\n}<\/pre>\n<\/blockquote>\n<h3>Calling SetThreadUILanguage Dynamically<\/h3>\n<p>The trickiest part of using <tt>GetProcAddress<\/tt> to dynamically call a function at run-time is getting the function pointer declaration correct.  This type definition should work for <tt>SetThreadUILanguage<\/tt>:<\/p>\n<blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">typedef LANGID (WINAPI *FP_SetThreadUILanguage)(LANGID LangId);<\/pre>\n<\/blockquote>\n<p>Now, you have a function pointer type called <tt>FP_SetThreadUILanguage<\/tt> which can be used like any other type.<\/p>\n<p>This function pointer can be used to dynamically call any function within a loaded module (DLL).  <tt>SetThreadUILanguage<\/tt> lives inside Kernel32.dll.  Since Kernel32.dll is required by all Win32 processes, we can simply use <tt>GetModuleHandle<\/tt> to find it, we don&#8217;t have to use <tt>LoadLibrary<\/tt>.  A pointer to <tt>SetThreadUILanguage<\/tt> can therefore be retrieved like this (unnecessarily verbose code):<\/p>\n<blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">HMODULE hKernel32 = GetModuleHandle(_T(&quot;Kernel32.dll&quot;));\r\nFARPROC pFn = GetProcAddress(hKernel32, &quot;SetThreadUILanguage&quot;);\r\n\r\nFP_SetThreadUILanguage pSetThreadUILanguage = (FP_SetThreadUILanguage)pFn<\/pre>\n<\/blockquote>\n<p>The function can now be called through the function pointer variable:<\/p>\n<blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">LANGID langid = pSetThreadUILanguage(myLangId);<\/pre>\n<\/blockquote>\n<p><tt>SetThreadUILanguage<\/tt> returns the <tt>LANGID<\/tt> which has been set.  If the call was successful, it will return the same <tt>LANGID<\/tt> that was specified.<\/p>\n<h3>Constructing a Language Identifier<\/h3>\n<p>Use the <tt>MAKELANGID<\/tt> macro from <tt>Winnt.h<\/tt>, and supply <tt>LANG_Xxx<\/tt> constants from same.  For example, Simplified Chinese would require:<\/p>\n<blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">DWORD dwSimplifiedChinese =\r\n   MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );<\/pre>\n<\/blockquote>\n<h3>Non-Vista Platforms<\/h3>\n<p>On non-Vista platforms, <tt>SetThreadLocale<\/tt> can be used.  Instead of a language identifier, it accepts a locale identifier.  To generate the locale identifier, simply pass your language identifier to the <tt>MAKELCID<\/tt> macro, and supply <tt>SORT_DEFAULT<\/tt> as the second parameter:<\/p>\n<blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">SetThreadLocale(MAKELCID(myLangId, SORT_DEFAULT));<\/pre>\n<\/blockquote>\n<p>This example unconditionally sets the thread locale to Simplified Chinese in a single line of code:<\/p>\n<blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">SetThreadLocale(\r\n   MAKELCID(\r\n      MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), \r\n      SORT_DEFAULT));<\/pre>\n<\/blockquote>\n<p><tt>SetThreadLocale<\/tt> returns <tt>TRUE<\/tt> on success, <tt>FALSE<\/tt> on error.<\/p>\n<h3>Reference<\/h3>\n<ul>\n<li \/><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms776324(VS.85).aspx\">Multiple-Language Resources<\/a> (MSDN)\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Simple Localization In classic Windows programming, the quickest way to handle localized resources is to put all languages into the same resource file, then use SetThreadLocale to tell Windows that it should return resources tagged with the specified language identifier. &hellip; <a href=\"https:\/\/www.curlybrace.com\/words\/2008\/06\/setthreadlocale-and-setthreaduilanguage-for-localization-on-windows-xp-and-vista\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,199],"tags":[],"class_list":["post-126","post","type-post","status-publish","format-standard","hentry","category-technology","category-win32-technology-2"],"_links":{"self":[{"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/posts\/126","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/comments?post=126"}],"version-history":[{"count":8,"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/posts\/126\/revisions"}],"predecessor-version":[{"id":1422,"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/posts\/126\/revisions\/1422"}],"wp:attachment":[{"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/media?parent=126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/categories?post=126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.curlybrace.com\/words\/wp-json\/wp\/v2\/tags?post=126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}