Building Bilingual English/Arabic R Shiny Applications

RTL text, Arabic typography, and UI layout patterns for UAE institutional platforms

Engineering
R Shiny
Internationalisation
Arabic
UAE
Practical guide to implementing bilingual English/Arabic interfaces in R Shiny — font selection, RTL layout management, reactive language switching, and the patterns used in the BRASS OBF Compliance Platform.
Author

BRASS Digital Lab

Published

24 March 2026

Introduction

For regulatory technology platforms deployed in UAE higher education institutions, bilingual English/Arabic capability is not a nice-to-have — it is an institutional requirement. Government stakeholders, regulatory reviewers, and senior academic leadership routinely require Arabic-language interfaces and document outputs.

R Shiny is not natively designed for RTL (right-to-left) text or multilingual switching, but with the right patterns it handles both reliably. This post documents the approach used in the BRASS OBF Compliance Platform, which operates in both English and Arabic across all interface elements.


1. Font Selection for Arabic Web UIs

Not all Arabic fonts render well in web environments. After testing seven Arabic font options in bslib Shiny applications, two perform reliably:

Cairo — Modern, professional Arabic font with excellent web rendering. Matches well with Latin sans-serif typefaces like DM Sans or Source Sans Pro. Best for dashboards and institutional applications.

Noto Sans Arabic — Google’s universal font project. Excellent coverage of all Arabic Unicode characters. Slightly more compact than Cairo. Recommended when font consistency across devices is critical.

Load both in your custom.scss as a fallback stack:

// Custom SCSS — Arabic font loading
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700&family=Noto+Sans+Arabic:wght@400;600;700&display=swap');

// Apply to all Arabic-direction elements
[lang="ar"], .arabic, .rtl {
  font-family: 'Cairo', 'Noto Sans Arabic', 'Amiri', serif;
  direction: rtl;
  text-align: right;
}

Avoid: Amiri (beautiful but too literary for dashboards), Scheherazade (same issue), and system Arabic fonts (inconsistent rendering across Windows/Mac/iOS).


2. Language Switching Architecture

The cleanest implementation uses a reactive app_language value that all UI elements observe. This allows every label, help text, and message to switch simultaneously without page reload.

# global.R — Language string bundles
STRINGS <- list(
  en = list(
    page_title       = "OBF Compliance Platform",
    login_title      = "Sign In",
    login_btn        = "Sign In",
    username_label   = "Email Address",
    password_label   = "Password",
    dashboard_title  = "Compliance Dashboard",
    kpi_submit_btn   = "Submit KPI Data",
    submit_success   = "KPI data submitted successfully.",
    submit_error     = "Submission failed. Please check required fields."
  ),
  ar = list(
    page_title       = "منصة الامتثال لصندوق التعليم المبني على النتائج",
    login_title      = "تسجيل الدخول",
    login_btn        = "دخول",
    username_label   = "البريد الإلكتروني",
    password_label   = "كلمة المرور",
    dashboard_title  = "لوحة متابعة الامتثال",
    kpi_submit_btn   = "إرسال بيانات مؤشرات الأداء",
    submit_success   = "تم إرسال بيانات مؤشر الأداء بنجاح.",
    submit_error     = "فشل الإرسال. يرجى التحقق من الحقول المطلوبة."
  )
)

In the server:

server <- function(input, output, session) {
  
  # Reactive language state — default English
  app_lang <- reactiveVal("en")
  
  # Language toggle button
  observeEvent(input$toggle_language, {
    new_lang <- if (app_lang() == "en") "ar" else "en"
    app_lang(new_lang)
    
    # Switch body direction
    shinyjs::toggleClass("body", "rtl-mode", condition = (new_lang == "ar"))
  })
  
  # Reactive string lookup helper
  s <- reactive({
    function(key) STRINGS[[app_lang()]][[key]] %||% paste0("[", key, "]")
  })
  
  # Use in outputs
  output$page_title <- renderText(s()("page_title"))
  output$submit_btn_label <- renderText(s()("kpi_submit_btn"))
}

3. RTL Layout Management

The most common failure in Arabic Shiny UIs is broken layout — RTL text pushing LTR elements out of alignment, or columns appearing in the wrong order. The solution is a rtl-mode CSS class on the body that flips directional properties:

// custom.scss — RTL mode overrides
body.rtl-mode {
  direction: rtl;
  text-align: right;

  // Bootstrap grid in RTL
  .row { flex-direction: row-reverse; }
  .col, [class^="col-"] { float: right; }

  // Form elements
  .form-control, .form-select {
    text-align: right;
    direction: rtl;
  }

  // Navbar — reverse item order
  .navbar-nav { flex-direction: row-reverse; }

  // Sidebar — flip to right side
  .sidebar { border-right: none; border-left: 1px solid var(--border); }

  // Icon spacing — flip left/right margins
  .me-2 { margin-right: 0 !important; margin-left: 0.5rem !important; }
  .ms-2 { margin-left:  0 !important; margin-right: 0.5rem !important; }
}

For elements that should always be LTR regardless of page direction (e.g., numbers, codes, URLs):

# Wrap LTR-only elements in a span with explicit direction
textOutput_ltr <- function(id) {
  tags$span(textOutput(id), dir = "ltr", style = "display:inline;")
}

# Use for KPI values, percentages, dates
output$kpi_value_display <- renderUI({
  tags$span(
    input$kpi_value, "%",
    dir   = "ltr",
    style = "font-family: 'DM Mono', monospace; direction: ltr;"
  )
})

4. Bilingual Document Generation

For regulatory report generation, both languages are often required in the same document. In the OBF platform, we use R Markdown → LaTeX → PDF with explicit language regions:

% LaTeX preamble for bilingual document
\usepackage{polyglossia}
\setmainlanguage{english}
\setotherlanguage{arabic}

\newfontfamily\arabicfont[Script=Arabic]{Cairo}

% In the document body
\textbf{Compliance Report — Al Ain University}

\begin{Arabic}
\textbf{تقرير الامتثال - جامعة العين}
\end{Arabic}

This produces correctly typeset bilingual PDFs where Arabic sections are right-aligned with proper Arabic shaping and English sections remain left-aligned.


5. Testing Bilingual Interfaces

Before delivery, bilingual UIs should be tested for:

Bilingual QA Checklist
Test Method
Font loading Check Network tab in browser devtools — Cairo/Noto must be loaded
RTL layout Toggle to Arabic and verify all layout elements flip correctly
Long Arabic strings Arabic text is often 20-30% wider than English equivalents — test for overflow
Mixed-direction content Numbers and codes inside Arabic sentences must read LTR
Arabic PDF generation Test PDF output in both Adobe Reader and browser PDF viewer
Mobile RTL Test at 375px in both language modes

Conclusion

Bilingual English/Arabic R Shiny applications are entirely achievable with deliberate architecture decisions. The key choices are: Cairo or Noto Sans Arabic for typography, a reactive language string system rather than conditional UI elements, rtl-mode CSS toggling rather than per-element direction attributes, and explicit LTR preservation for numeric and code content.

These bilingual English/Arabic patterns are production-tested in all MoHESR’s OBF Compliance Platforms developed by BRASS DIGITAL LAB for UAE HEIs.

📧 brassbe1982@gmail.com · 🤝 Request a consultation

Back to top