Best practices for dealing with right-to-left (RTL) languages such as Arabic and Hebrew.

Requirements

What special requirements are there for RTL languages?

  • Professional authoring and translation is needed unless you have native speakers on staff.
  • Use Unicode support for special characters. Unicode provides complete bidirectionality support.
  • The directionality of characters changes. "First ثان third." becomes ".third ثان First"
  • The directionality of documents changes. This determines the order in which elements appear in the document and on the page. For example, an RTL spreadsheet will have the A column on the right side, with the B column to its left and so forth. On a Web page, horizontal navigation menu starts from the right, left column becomes the right column and so on.

Scope

Should you do just a landing page or translate the whole site?

Landing page

You can start by creating a country-specific landing page rather than translating/localizing the complete site. For example, to serve visitors from the UAE create a page such as /landing/ae, buy the .ae country domain, and map it to the page in site definition or use virtual URI mapping. Translate only the landing page.

Examples:

Complete site

Content entry:

  • Set the dir and lang attributes in the html element.
  • Overwrite all horizontal CSS positioning attributes in a separate stylesheet file named rtl.css. (source: WordPress)
  • Float drop cap to the right using CSS when dir=rtl.

Examples:

Locales

Beware that you have to use the OLD locale code in your site's definition because else Magnolia can't resolve the locales. E.g. defining a "he" node in the site's definition, returns "iw" as language code and therefore won't fetch a messages file called messages_he.properties. So always check e.g. this list http://docs.oracle.com/javase/1.4.2/docs/guide/intl/locale.doc.html to be sure you use the right java locale.

  • hebrew -> iw instead of he
Locale.java
    /**
     * Construct a locale from language, country, variant.
     * NOTE:  ISO 639 is not a stable standard; some of the language codes it defines
     * (specifically iw, ji, and in) have changed.  This constructor accepts both the
     * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other 
     * API on Locale will return only the OLD codes. 
     * @param language lowercase two-letter ISO-639 code.
     * @param country uppercase two-letter ISO-3166 code.
     * @param variant vendor and browser specific code. See class description.
     * @exception NullPointerException thrown if any argument is null.
     */
    public Locale(String language, String country, String variant) {
        this.language = convertOldISOCodes(language);
        this.country = toUpperCase(country).intern();
        this.variant = variant.intern();
    }
new Locale("he").getLanguage() // -> iw
new Locale("iw").getLanguage() // -> iw

FCKEditor

To have the FCKEditor react on the Locale set in Magnolia, and not the browser language only, you can add the following script to your init file.

var url = document.location.href;
var languageStartIndex = url.indexOf('mgnlLocale=') + 'mgnlLocale='.length;
var languageEndIndex = languageStartIndex + 2;
var language = url.substring(languageStartIndex, languageEndIndex);
if(language == 'he' || language == 'iw') {
    fckInstance.Config['DefaultLanguage'] = 'he';
    fckInstance.Config['ContentLangDirection'] = 'rtl';
}
if(language == 'ar') {
    fckInstance.Config['DefaultLanguage'] = 'ar';
    fckInstance.Config['ContentLangDirection'] = 'rtl';
}

After a dialog with the FCKEditor is loaded this scripts checks the URL of for the mgnlLocale property and gets the language out of it.

Dialogs

To mirror the edit fields of the dialogs, you can use the following code samples:

public class BidirectionalDialogEdit extends DialogEdit {
    List<String> rtlLanguages = Arrays.asList(new String[]{"ar", "iw", "he"}); // he = iw = hebrew, ar = arabic
    @Override
    public void drawHtml(Writer out) throws IOException {
        Edit control = getLocaleDependentEditControl();
        //rest of this method is copied from info.magnolia.cms.gui.dialog.DialogEdit.drawHtml()
        control.setType(this.getConfigValue("type", PropertyType.TYPENAME_STRING));
        if (this.getConfigValue("saveInfo").equals("false")) {
            control.setSaveInfo(false);
        }
        control.setCssClass(CssConstants.CSSCLASS_EDIT);
        control.setRows(this.getConfigValue("rows", "1"));
        control.setCssStyles("width", this.getConfigValue("width", "100%"));
        if (this.getConfigValue("onchange", null) != null) {
            control.setEvent("onchange", this.getConfigValue("onchange"));
        }
        this.drawHtmlPre(out);
        out.write(control.getHtml());
        this.drawHtmlPost(out);
    }
    private Edit getLocaleDependentEditControl() {
        Edit control;
        String localeString = MgnlContext.getAttribute("mgnlLocale").toString();
        String language = StringUtils.substringBefore(localeString, "_");
        if (rtlLanguages.contains(language))
            control = new RTLEdit(this.getName(), this.getValue());
        else
            control = new Edit(this.getName(), this.getValue());
        return control;
    }
}

This class creates depending on the locale a normal Edit object or an RTLEdit object. The code for the RTLEdit class is as follows:

public class RTLEdit extends Edit {
    public RTLEdit() {
        super();
    }
    public RTLEdit(String name, Content websiteNode) {
        super(name, websiteNode);
    }
    public RTLEdit(String name, String value) {
        super(name, value);
    }
    @Override
    public String getHtml() {
        StringBuilder html = new StringBuilder(super.getHtml());
        html.insert(html.indexOf(" "), " dir=\"rtl\"");
        return html.toString();
    }
}

Set the new control class under /modules/adminInterface/controls/edit/class = my.package.BidirectionalDialogEdit and you are good to go! (big grin)

1 Comment

  1. A tutorial for this would be very helpful