{"id":106680,"date":"2022-05-23T07:00:00","date_gmt":"2022-05-23T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106680"},"modified":"2022-05-23T09:04:58","modified_gmt":"2022-05-23T16:04:58","slug":"20220523-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220523-00\/?p=106680","title":{"rendered":"How does Windows decide whether a newly-created window should use LTR or RTL layout?"},"content":{"rendered":"<p>You can specify in a window&#8217;s extended styles whether it follows left-to-right (LTR) or right-to-left (RTL) layout. The right-to-left layout is used in languages that are written from right to left, of which the most widely used today are probably Arabic and Hebrew. You can request right-to-left layout by setting the <code>WS_<wbr \/>EX_<wbr \/>LAYOUTRTL<\/code> extended style.<\/p>\n<p>If you don&#8217;t specify the <code>WS_<wbr \/>EX_<wbr \/>LAYOUTRTL<\/code> extended style, the system may still apply that style automatically, based on the following rules:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th colspan=\"2\">Scenario<\/th>\n<th>Rule<\/th>\n<\/tr>\n<tr>\n<td rowspan=\"2\">Child window<\/td>\n<td>Parent omits <code>WS_<wbr \/>EX_<wbr \/>NO\u00adINHERIT\u00adLAYOUT<\/code><\/td>\n<td>Inherit <code>WS_<wbr \/>EX_<wbr \/>LAYOUTRTL<\/code> from parent.<\/td>\n<\/tr>\n<tr>\n<td>Parent has <code>WS_<wbr \/>EX_<wbr \/>NO\u00adINHERIT\u00adLAYOUT<\/code><\/td>\n<td>Remain LTR.<\/td>\n<\/tr>\n<tr>\n<td rowspan=\"2\">Top-level window<\/td>\n<td>Owned window<\/td>\n<td>Remain LTR.<\/td>\n<\/tr>\n<tr>\n<td>Unowned window<\/td>\n<td>Follow <code>Get\u00adProcess\u00adDefault\u00adLayout<\/code>.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the case of a top-level unowned window, the <code>WS_<wbr \/>EX_<wbr \/>LAYOUTRTL<\/code> extended style is automatically set if the process default layout has the <code>LAYOUT_<wbr \/>RTL<\/code> bit set.<\/p>\n<p><a title=\"Whether your application should display its content in RTL should be based on the content\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131218-00\/?p=2333\"> As I noted some time ago<\/a>, if a process never calls <code>Set\u00adProcess\u00adDefault\u00adLayout<\/code>, then the initial process default layout is inferred by inspecting the FileDescription version property of the primary executable: <a href=\"http:\/\/web.archive.org\/web\/20090101225524\/http:\/\/msdn.microsoft.com\/en-us\/goglobal\/bb688119.aspx#EDC\"> If it begins with two left-to-right marks<\/a> (LRMs, represented by Unicode code point U+200E), then the process default layout is set to <code>LAYOUT_<wbr \/>RTL<\/code>.<\/p>\n<p>I also noted some time ago that <a title=\"How do I determine programmatically whether a particular language is LTR or RTL?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20111222-00\/?p=8833\"> you can ask <code>Get\u00adLocale\u00adInfo\u00adEx<\/code> for the <code>LOCALE_<wbr \/>IREADING\u00adLAYOUT<\/code><\/a> to determine whether any particular language is LTR or RTL.<\/p>\n<pre>\/\/ <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/intl\/locale-ireadinglayout\">Direction values<\/a>:\r\n\/\/ 0 = left to right (e.g., English)\r\n\/\/ 1 = right to left (e.g., Arabic)\r\n\/\/ 2 = top to bottom, right to left (e.g., classical Chinese)\r\n\/\/ 3 = top to bottom, left to right (e.g., Mongolian)\r\n\r\nint GetLanguageReadingLayout(PCWSTR languageName)\r\n{\r\n    int direction = 0;\r\n    THROW_IF_WIN32_BOOL_FALSE(\r\n        GetLocaleInfoEx(languageName,\r\n                        LOCALE_IREADINGLAYOUT | LOCALE_RETURN_NUMBER,\r\n                        reinterpret_cast&lt;LPWSTR&gt;(&amp;direction),\r\n                        sizeof(direction) \/ sizeof(wchar_t)));\r\n    return direction;\r\n}\r\n\r\nint GetSystemDefaultLanguageReadingLayout()\r\n{\r\n    return GetLanguageReadingLayout(LOCALE_NAME_SYSTEM_DEFAULT);\r\n}\r\n\r\nint GetUserDefaultLanguageReadingLayout()\r\n{\r\n    return GetLanguageReadingLayout(LOCALE_NAME_USER_DEFAULT);\r\n}\r\n<\/pre>\n<p>You typically are interested in the primary language for the current thread, since that&#8217;s the one that most influences which language resources your program will use. You can use <code>Get\u00adThread\u00adPreferred\u00adUI\u00adLanguages<\/code> to get all the languages that apply to the current thread, and then pass the first one to <code>Get\u00adLanguage\u00adReading\u00adLayout<\/code>. Calling the <code>Get\u00adThread\u00adPreferred\u00adUI\u00adLanguages<\/code> function is a bit frustrating because the list of applicable languages can change asynchronously (if another thread calls <code>Set\u00adProcess\u00adPreferred\u00adUI\u00adLanguages<\/code> It is double annoying because the wil helper function <code>Adapt\u00adFixed\u00adSize\u00adTo\u00adAllocated\u00adResult<\/code> assumes null-terminated strings and doesn&#8217;t support double-null-terminated strings, so we have to write out the loop manually.<\/p>\n<pre>namespace wil\r\n{\r\n    template&lt;typename string_type, size_t stackBufferLength = 40&gt;\r\n    HRESULT GetThreadPreferredUILanguages(DWORD flags,\r\n        _Out_ PULONG languageCount, string_type&amp; result)\r\n    {\r\n        wchar_t stackBuffer[stackBufferLength];\r\n        ULONG required = ARRAYSIZE(stackBuffer);\r\n        if (::GetThreadPreferredUILanguages(flags, languageCount,\r\n                                         stackBuffer, &amp;required))\r\n        {\r\n            result = make_unique_string_nothrow&lt;string_type&gt;\r\n                                             (nullptr, required);\r\n            RETURN_IF_NULL_ALLOC(result);\r\n            memcpy(result.get(), stackBuffer,\r\n                   required * sizeof(wchar_t));\r\n            return S_OK;\r\n        }\r\n        DWORD error = ::GetLastError();\r\n        while (error == ERROR_INSUFFICIENT_BUFFER)\r\n        {\r\n            result = make_unique_string_nothrow&lt;string_type&gt;\r\n                                             (nullptr, required);\r\n            RETURN_IF_NULL_ALLOC(result);\r\n            if (::GetThreadPreferredUILanguages(flags,\r\n                         languageCount, result.get(), &amp;required))\r\n            {\r\n                return S_OK;\r\n            }\r\n            error = ::GetLastError();\r\n        }\r\n        RETURN_WIN32(error);\r\n    }\r\n\r\n    template &lt;typename string_type = wil::unique_cotaskmem_string,\r\n              size_t stackBufferLength = 40&gt;\r\n    string_type GetThreadPreferredUILanguages(DWORD flags,\r\n        _Out_ PULONG languageCount)\r\n    {\r\n        string_type result;\r\n        THROW_IF_FAILED<a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200508-52\/?p=103735\">((<\/a>wil::GetThreadPreferredUILanguages&lt;\r\n            string_type, stackBufferLength&gt;\r\n            (flags, languageCount, result)));\r\n        return result;\r\n    }\r\n}\r\n<\/pre>\n<p>We can now plug this into our existing function.<\/p>\n<pre>int GetDefaultThreadLanguageReadingLayout()\r\n{\r\n    ULONG count;\r\n    return GetLanguageReadingLayout(\r\n        wil::GetThreadPreferredUILanguages(MUI_LANGUAGE_NAME |\r\n            MUI_MERGE_UI_FALLBACK, &amp;count).get());\r\n}\r\n<\/pre>\n<p>Sorry it&#8217;s such a pain.<\/p>\n<p><b>Bonus chatter<\/b>: All of this logic assumes that your program has been translated into RTL languages in the first place. If your program is English-only, <a title=\"Whether your application should display its content in RTL should be based on the content\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131218-00\/?p=2333\"> don&#8217;t display your English strings in RTL<\/a>. As I noted in that article, you can leave a breadcrumb in the resources to tell you which direction the resources expect strings to read.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gathering the algorithmic details scattered throughout the documentation into one place.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-106680","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Gathering the algorithmic details scattered throughout the documentation into one place.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106680","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=106680"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106680\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=106680"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106680"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106680"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}