Extension:RevisionSlider/Developing a RTL-accessible feature in MediaWiki - what we've learned while creating the RevisionSlider

From mediawiki.org

In this text we are presenting a few things we've learned about supporting languages written right to left (RTL) while developing the Revision Slider extension at Wikimedia Deutschland in 2016.

The main focus here is on creating RTL-friendly software and user interfaces in the Wikimedia/MediaWiki universe. There are plenty of more detailed and thorough resources about dealing with directionality issues in software in the Internet, both MediaWiki-specific and general ones. Some of these valuable resources are listed in the references section at the end. We hope future MediaWiki extension and gadget developers will find the few points covered in this text helpful.

Background

Along with all of the exciting diversity and variety that different languages and cultures bring to the global community, the way different scripts are being written also has significant technological implications. Although there has been noticeable progress to improve how right-to-left scripts are displayed on the computer screen (for example standardizing the Unicode Bidirectional Algorithm[1]) the software world still seems to be rather LTR-centric. Browsers and libraries often ignore some RTL-specific behaviour or simply lack a standard interpretation. For instance, different browsers handle DOM element's scrollLeft property differently. All of this leads to a need to use hacks and workarounds to make your software or website equally accessible for both LTR and RTL audience.

Native speakers of RTL languages form a significant part of mankind, with the number of speakers significantly exceeding 500 million people[2][3][4][5][6][7] and growing. Users of those languages are also actively present in the Wikimedia community. To give an overview, the table below displays statistics about a few of the most active RTL Wikipedia projects as of early December 2016. There are of course more than just these four.[8]

Language Wikipedia link Users Articles
Arabic ar.wikipedia.org 1,257,951 452,636
Persian fa.wikipedia.org 610,284 515,856
Hebrew he.wikipedia.org 319,399 198,814
Urdu ur.wikipedia.org 59,407 110,561

In the Wikimedia community, we value accessibility and making knowledge available to people regardless what language they speak, or what script they use to write. We believe it is important that we try to make our tools available for all readers and editors. Dealing with LTR and RTL differences and issues is one aspect that should not be overlooked.

Changing the perspective

Displaying RTL languages is not only about flipping text and printing everything from right to left (Unicode, HTML, and modern browsers are already quite good at this). Direction of the script does not only affect the way speakers of RTL languages read and write but it also influences the way they perceive the world, in this context, the way they use software and websites. To borrow a few points from the presentation by Moriel Schottlender, we highly recommend to have a look at, it has an impact on where users look at the screen (ie. websites do not start at left-top corner), where they naturally expect elements to appear. It also affects where the user would expect an element to go, "before" or "after" another one, or where the "start" and "end" are.

What we at Wikimedia Deutschland have found helpful and important while developing the interface of the Revision Slider, was to set the broader perspective while thinking about the interface, not just consider at what part of the screen the particular element appears. By this we mean focusing on the "semantics" of elements of the user interface, on what the particular UI element represents to the user, and what action it offers to them. With such an approach displaying the specific UI element in the particular language/directionality version of UI becomes an implementation detail that does not affect the general idea you have behind your interface. Having such an abstraction layer when creating a user interface might also pay off when some adjustments to the UI are required which are real implementation details, like changing the library used to render a UI or adapting to a new style guide etc.

To make this more concrete, let's have a look at what we have come across when we have tried to make Revision Slider work intuitively in the RTL environment. In the Revision Slider there are two tiny triangle pointers pointing toward two revisions that are currently compared in the diff (see screenshot below). They can be moved around to different revisions (represented as horizontal bars) to change the revisions shown in the diff.

Revision Slider user interface
Revision Slider user interface

Initially we called the yellow pointer the "left" pointer, and the blue pointer the "right" one. This makes some sense in the LTR language interface, as they correspond to the order and colour of diff columns below. Problems started when we tried out our prototype in the Hebrew version of UI. Our "left" pointer now became blue, and was referring the other column of the diff (the newer revision of those compared), and the similar change occurred for the "right" pointer. Pointers can also pass beside each other (ie. the "left" pointer becomes the "right" one). As the pointer is not only related to the colour but also to actual revision in the diff (the older or the newer), the code handling pointer change started to become a pile of conditions handling all combinations of mutual pointer position and UI directionality. At this point we realized we have been unnecessarily connecting some action of our extension (changing one of the revisions compared in the diff) to the position of the pointer (is it the "left" or "right" one). Instead what is really relevant for the Revision Slider is that one of the pointers is related to the older revision of the diff, and the other to the newer revision. What colour do they have and which of two is more on the left than the other is just a detail, which is different in LTR and RTL modes but the actual "semantics" of the pointers remain. So we renamed internally our pointers to now have the "older pointer" and the "newer pointer" and apply relevant colours to them depending on the direction of the UI and their position. This change made code far more clear and easier to change, while the behaviour in both LTR and RTL language versions of the UI is still meeting the user expectations.

We have also had similar observations about buttons that change the current revision period visible in the revision slider. Those buttons have small arrows, so it felt reasonable to think of them as of "left" and "right" buttons. But then the left button in the English UI moves the visible revision window backwards while the left button in the Arabic UI does the opposite. This again resulted in the pile of conditionals and messy code. The solution was again simple: we have just taken a step back and noticed that our buttons are actually a "backwards" and "forwards" buttons. What kind of icon is used is again an implementation detail.

While doing this internal refactoring of Revision Slider we had to throw away quite some code. We have also learned that the earlier you realize to focus on what your UI is supposed to do and not on how it looks like, the easier it makes to have a clean and easy-to-maintain code.

Directionality in MediaWiki

MediaWiki and other components we use in our extensions and gadgets already include quite a few features that make it easier to deal with directionality issues. Making the UI look good in right-to-languages is actually mostly automated in MediaWiki, you just have to be aware of some (simple) steps to make use of those features, and make your RTL users happy.

In next few paragraphs there is a handful of hints on how to make use of those features. More specific information, as well as many things that are not covered are described on the page dedicated to Directionality support.

Automatic flipping with CSSJanus

ResourceLoader includes CSSJanus[9] which automatically converts CSS stylesheets from left-to-right to right-to-left. CSSJanus takes care of "flipping" of direction-sensitive CSS rules, so you don't have to define a separate copy of the stylesheet for RTL languages. When user language is a RTL language Resource Loader converts CSS declarations and serves the converted form to the user. For instance, we defined the direction property of the main container of Revision Slider's UI to be ltr, as below.

.mw-revslider-container {
	/* This will flip with CSSJanus in case */
	/* the interface is in RTL */
	direction: ltr;
}

Having explicitly defined the direction of the Revision Slider allows CSSJanus to flip it when in RTL mode. When interface language is a RTL language Resource Loader will flip the above rule using CSSJanus and the CSS ruleset served to the user will contain the rule below instead. As a result whole Revision Slider interface will be served using right-to-left directionality.

.mw-revslider-container {
	direction: rtl;
}

CSSJanus can be always told not to flip particular CSS declarations using the @noflip directive. For example if some element should only have left border visible, no matter what is the UI language, it could be defined as in the following CSS rule used to declare pointer lines appearing in Revision Slider.

/* @noflip */
.mw-revslider-pointer-line .mw-revslider-left-line {
	border-left-width: 2px;
}

Flipping of CSS properties is not the only thing that CSSJanus can do. There is much more that you can read about in the Resource Loader's documentation.

Directionality-sensitive images in ResourceLoader

Icons are important parts of the user interface, and as such, they should also reflect directionality of the interface. Revision Slider offers a user a few-step overview of its features, explaining those using both textual description and a simple picture depicting, in a simplified way, the relevant part of UI. As the whole UI changes depending on whether the language of the interface is LTR or RTL, so should the icon. Resource Loader has a handy way of specifying what icon should be used for the given image depending on the directionality (or even language) of the user interface.[10]

With the use of following Resource Loader's image definition Revision Slider delivers a different icon depending on the directionality of the user interface.

"ext.RevisionSlider.dialogImages": {
	"position": "top",
	"class": "ResourceLoaderImageModule",
	"selector": ".mw-revslider-help-dialog-slide-{name}",
	"images": {
		"1": {
			"file": {
				"ltr": "resources/ext.RevisionSlider.helpDialog/slide1-ltr.svg",
				"rtl": "resources/ext.RevisionSlider.helpDialog/slide1-rtl.svg"
			}
		}
		
	}

The above code in action could be seen on the screenshots of Revision Slider's help dialogue on English and Arabic Wikipedias.

By the way, using similar functionality it is possible to have a "flipped" version of your Beta Feature icon in GetBetaFeaturePreferences hook.

public static function getBetaFeaturePreferences( User $user, array &$prefs ) {
	
	$prefs['revisionslider'] = [
		'label-message' => 'revisionslider-beta-feature-message',
		'desc-message' => 'revisionslider-beta-feature-description',
		'screenshot' => [
			'ltr' => "$extensionAssetsPath/RevisionSlider/resources/RevisionSlider-beta-features-ltr.svg",
			'rtl' => "$extensionAssetsPath/RevisionSlider/resources/RevisionSlider-beta-features-rtl.svg",
		],
		'info-link' => 'https://www.mediawiki.org/wiki/Extension:RevisionSlider',
		'discussion-link' => 'https://www.mediawiki.org/wiki/Extension_talk:RevisionSlider',
	];
	
}

Directionality in OOjs UI

The OOjs UI library that MediaWiki core and many extensions use to create a consistent user interface also saves (quite) some developer work when it comes to directionality. Many of the commonly used UI elements, for example arrows representing moving forwards or backwards in revision history in the Revision Slider, should be adjusted depending on whether it is used in LTR or RTL context. While the user of German Wikipedia expects a "forward" button to be an arrow pointing right, their colleague from Hebrew Wikipedia would expect the same button to point left.

This issue would generally be solved by using different versions of the icon for different languages of the interface (e.g. using Resource Loader's feature described in the previous section). OOjs UI saves much of the effort in this regard as its icons automatically flip when the user interface language is RTL.[11] All you have to do is pick the icon you want and the library will take care of all the work!

It's not only about flipping

We have mentioned flipping of the UI in RTL languages in many different ways. While RTL-compliance is often about flipping, it should not be assumed that you just blindly flip all characters in the UI, and have a UI intuitively accessible by people using a RTL language. For example readers of Hebrew and Arabic expect digits not to be flipped (ie. same as in LTR languages), as well as the sign of the negative number to be on the left of the digit. In other words, "-12" is displayed the same way in RTL texts, not as "21-". Similarly, when displaying time, quarter past ten should be displayed as "10:15", not as "15:10" (or "51:01"!).

Another interesting aspect is mixing texts written in LTR and RTL scripts. Revision Slider shows a popup window with information about the revision when hovering over the bar representing it. Information in the popup consists of several label-value pairs (like the author, edit summary, edit size). Labels are always in the UI language (ie. they're written left-to-right on English Wikipedia, and right-to-left on Hebrew Wikipedia). But when it comes to values all possible scripts are possible, for example when browsing Hebrew Wikipedia but with user language set to English. In such cases the direction of text in the popup could easily be mixed up, e.g. the label would be written correctly LTR and the Hebrew summary displayed also left to right, which would be incorrect. Luckily, there is a ‎<bdi> element in HTML (standing for Bi-Directional Isolation) which isolates a text from adjacent elements preventing a browser from erroneous mixing direction. For instance, HTML for the edit summary in Revision Slider's popup can be seen below. The ‎<bdi> element should be used when there is a possibility that the text in different language than the UI language could appear.

<p><strong>Comment: </strong><bdi><a>HEBREW_USERNAME</a></bdi></p>

A final thing to note is that as LTR languages differ between each other, RTL languages can differ too. The first RTL language we've been testing Revision Slider against is Hebrew. There is a little question mark icon in the corner of the Revison Slider which opens a popup with some help information. In Hebrew the question mark has the same form as in languages written in Latin script, so we didn't adjust the icon to be different in RTL case. We had believed we're doing it right until we heard from members of Arabic Wikipedia community that, unlike Hebrew, Arabic, as well as some other RTL languages like Persian and Urdu that use a flipped question mark.[12] And guess what? It turned out that OOjs UI has a help icon that already deals with different forms of question mark and takes care of displaying the right character. But nonetheless lesson learned here is again: do not expect the same works for all languages!

Summary

Although the Revision Slider is quite small feature, while working on it we have still bumped on several hurdles related to RTL display. Dealing with directionality issues might not be obvious sometimes but MediaWiki is already pretty good in RTL support. There is a dedicated documentation on Directionality support you could refer to while developing your code. Everybody is always welcome to expand this documentation, there are many aspects that are still not covered over there.

What we've learned many many times while developing Revision Slider is to not assume every language behaves like the languages that you have tested your UI against, and to make your interface flexible. Just the same as we have good practice when it comes to internationalization. Making a user interface that would be equally accessible and intuitive for both LTR and RTL users might be challenging sometimes but we strongly believe this is a goal we should be always trying to achieve.

Acknowledgements

We have been very lucky to receive a huge amount of help from RTL language developers and users during our work on the Revision Slider. We really appreciate that and we have no doubt that making this extension accessible for RTL languages would never be possible if it weren't for all of the people that shared their expertise and advise with us. Moriel Schottlender did a RTL review of our early prototype, provided us with uncountable amount of advice and she even proposed how to restructure the code to better deal with directionality issues. If you are interested in RTL issues make sure you check out Moriel's website dedicated to these topics![13] Amir Aharoni taught us not to mix directions of text. Guycn2 and IKhitron from Hebrew wiki communities have been patient filing bug reports and explaining intricacies of Hebrew to us. زكريا (Zack) from Arabic Wikipedia opened our eyes to some specifics of Arabic. Finally our special thanks go to communities of Arabic and Hebrew Wikipedias who have been the first wikis to get interested in the Revision Slider, and their users have been providing instant and valuable feedback which helped us improve the extension.

We want to thank all of you for your invaluable help and great patience for us RTL ignorants. We wouldn't have been able to achieve what we've had if it hadn't been you who have been helping us.

References