Right-to-left layouts
A Runic window shows a web page, and the WebView mirrors the layout and
orders mixed-direction text once the page says which language and direction
it uses. Runic does not set them for you: the page chooses its lang and
dir. Native pieces around the page, such as the window frame, menus, dialogs
and notifications, follow each platform's own rules, which this guide lists
with what the application can and cannot change.
Checked with: Runic CI runs the GTK 4 smoke with an Arabic process locale
(--rtl, Linux x64 and arm64): the page reads ar-EG and lays out right to
left, GTK 4's direction is right to left, and the override below works. The
Windows and macOS rows have not been run.
Set the page's language and direction #
Set both on the root element whenever the language changes. With
Runic Translations, the generated runtime's
localeDirection gives the direction of any locale tag:
import {
getLocale,
localeDirection,
} from 'virtual:runic-translations/app/runtime';
const locale = getLocale();
document.documentElement.lang = locale;
document.documentElement.dir = localeDirection(locale);
It decides by the locale's script: ar, he, fa, ur, pa-PK and
ku-Arab are right-to-left; ku, he-Latn and sd-Deva are not. Without
Translations, apply the same rule with new Intl.Locale(tag).maximize().script
and CLDR's right-to-left scripts (Arabic, Hebrew, Syriac, Thaana, N'Ko, Adlam
and others). In .NET, CultureInfo.TextInfo.IsRightToLeft answers the same
question, but it is always false with InvariantGlobalization.
Then let CSS follow the direction:
- Use logical properties (
margin-inline-start,padding-inline-end,inset-inline-start,text-align: start) instead of left and right, and flex or grid layouts, which mirror on their own. - Mirror icons that point somewhere, such as back arrows, with
:dir(rtl); leave logos, media controls and clocks alone. - Wrap values of the other direction in right-to-left text, such as file
names, paths and identifiers, in
<bdi>, or in the isolates U+2068 and U+2069 in plain text (isolateBidiin@runic-artifex/translations-svelte/translations/testing). Translations does not isolate message inputs for you. - Use
dir="auto"on inputs and on text the user wrote.
Where the language comes from #
Prefer the application's own language setting, for example a ViewModel's
state, to navigator.language, which each WebView takes from somewhere else:
| WebView | navigator.language |
|---|---|
| WebKitGTK (Linux) | The process locale, from LANGUAGE, LC_ALL, LC_MESSAGES and LANG, for example ar-EG. Under the C locale it is C, which is not a language tag, so guard against it. |
| WebView2 (Windows) | The Windows display language. Runic sets no WebView2 Language. |
| WKWebView (macOS) | The user's preferred languages for the application. |
Native pieces #
| Piece | Linux | Windows | macOS |
|---|---|---|---|
| Window frame and GTK header bar | GTK's direction, from the process locale | Not mirrored (#389) | AppKit's direction for the application |
| Menu bar | None | None | AppKit's direction; Runic's default items have English titles |
| Status icon menu | Drawn by the panel, in the session's language | Not mirrored (#389) | AppKit's direction for the application |
| File dialogs | Drawn by the desktop portal, in the session's language | Windows display language | AppKit's direction for the application |
| Notifications | Drawn by the notification server | Windows display language | Drawn by the system |
On Linux, GTK 4 takes its direction from the script of the process locale's
language, and GTK 3 from its own translation for the locale, which on Ubuntu
comes with the language-pack-gnome-* packages. A user whose session is
Arabic gets a mirrored header bar; an application that only switches its page
to Arabic does not. An application whose language differs from the session's
can set GTK 4's direction itself; GTK applies it on its own thread:
GLib.Functions.IdleAdd(GLib.Constants.PRIORITY_DEFAULT, () =>
{
Gtk.Widget.SetDefaultDirection(Gtk.TextDirection.Rtl);
return false;
});
On macOS, AppKit mirrors an application only for a language the bundle
declares: list it in CFBundleLocalizations in Info.plist, or ship an
<language>.lproj folder. See Ship a Desktop application for
the bundle.
Text you pass to native pieces, such as notification titles, window titles and menu items, is shown as given. Isolate embedded values of the other direction there as well.