Building Bilingual English/Arabic R Shiny Applications
RTL text, Arabic typography, and UI layout patterns for UAE institutional platforms
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:
| 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.