← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Bidirectional Text (BiDi)

i18n Intermediate
debt(d8/e6/b5/t7)
d8 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9), scored d8 because while no listed detection tools exist and BiDi bugs typically surface only when actual RTL users submit content, careful review with RTL test data can catch some issues before production.

e6 Effort Remediation debt — work required to fix once spotted

Closest to 'cross-cutting refactor across the codebase' (e7), scored e6 because the fix (CSS logical properties, dir='auto', <bdi>) is well-defined but must be applied across all stylesheets and templates — more than one component but not architectural.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5), because once RTL support is needed, every new UI component must consider logical properties, icon mirroring, and direction — slowing many work streams without redefining system shape.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7), grounded in the misconception that dir='rtl' on body is sufficient — the obvious approach handles text direction but silently fails to mirror layout, contradicting developer intuition that a single direction attribute should cascade.

About DEBT scoring →

Also Known As

BiDi bidirectional algorithm Unicode Bidirectional Algorithm UBA

TL;DR

The handling of text that mixes left-to-right (LTR) and right-to-left (RTL) scripts in the same document — governed by the Unicode Bidirectional Algorithm, with CSS and HTML direction attributes controlling display.

Explanation

Unicode's Bidirectional Algorithm (UBA) determines the visual order of characters in mixed-direction text. Arabic and Hebrew scripts write right-to-left; Latin, CJK, and most scripts write left-to-right. When LTR text appears inline within an RTL paragraph (or vice versa), the browser's BiDi algorithm reorders characters for display. Developers encounter BiDi issues when: concatenating strings of mixed directionality, rendering user-generated RTL content in LTR UI (or vice versa), displaying numbers alongside RTL text (numbers are always LTR), and using text-align or float assumptions that break in RTL layouts. CSS dir='rtl' or the direction property sets the base direction for a block; the dir='auto' attribute lets the browser infer direction from content. PHP's mb_convert_encoding and intl's Normalizer handle Unicode correctly; the main PHP concern is ensuring UTF-8 throughout the stack.

How It's Exploited

BiDi can be exploited to hide malicious content or intent through text reordering. An attacker may craft a string that appears benign when read left-to-right but contains hidden commands or URLs when the Unicode Bidirectional Algorithm reorders it for display in RTL contexts. For example, a URL or filename can be visually reversed to mask its true destination—`evilsite.com` rendered as `.moc.elivis` when displayed in an RTL context. This is particularly dangerous in filenames, URLs in user-generated content, or any context where the visual presentation is trusted for security decisions. Mitigations include: explicit directional markup (dir attribute) to lock direction, validating and sanitizing URLs before display, avoiding reliance on visual appearance for security decisions, and using the Unicode Left-to-Right Mark (U+200E) or Right-to-Left Mark (U+200F) to disambiguate critical text.

Common Misconception

Adding dir='rtl' to the body element is sufficient for RTL language support. RTL support requires systematic changes throughout the UI: all floats, margins, paddings, absolute positions, and text alignments must mirror. CSS logical properties (margin-inline-start instead of margin-left, padding-inline-end instead of padding-right) automatically adapt to writing direction. Icons that imply directionality (back arrows, progress indicators) must flip. Setting dir='rtl' on the body changes text direction but does not automatically mirror layout — that requires CSS logical properties or manual RTL stylesheet overrides.

Why It Matters

Arabic and Hebrew together represent over 400 million native speakers. A PHP application that does not handle BiDi correctly renders Arabic or Hebrew user content as a jumbled mix of incorrectly ordered characters, renders form inputs in the wrong direction, and misaligns UI elements for RTL users. The majority of BiDi issues in PHP applications stem from string concatenation without direction markers and CSS layout assumptions that do not adapt to writing direction — both fixable with standard tools.

Common Mistakes

  • Concatenating RTL and LTR strings without Unicode directional formatting characters — the BiDi algorithm can produce surprising reordering at string boundaries.
  • Using margin-left/margin-right instead of margin-inline-start/margin-inline-end — physical properties do not flip for RTL layouts.
  • Not testing with actual RTL content — placeholder text in a dev environment is usually LTR; BiDi bugs only appear with real RTL user content.
  • Assuming text-align: right is the same as dir='rtl' — direction affects more than alignment: it affects punctuation placement, list marker position, and line wrapping.

Avoid When

  • Describing single-direction text layouts (e.g., a purely English document or a purely Arabic document) where no mixing of LTR and RTL content occurs.
  • Troubleshooting font rendering or character encoding errors that are unrelated to text direction—BiDi applies to logical ordering, not glyph display or byte encoding.
  • Addressing keyboard input or IME (input method editor) behavior, which is a separate i18n concern from visual text directionality.
  • Discussing CSS layout features like flexbox or grid that happen to adapt to RTL contexts via the direction property alone, without mixing script directions.

When To Use

  • Your application supports Arabic, Hebrew, or Persian users and must display user-generated content or mixed LTR/RTL text without breaking layout or readability.
  • You're concatenating strings programmatically (usernames with timestamps, search queries with results) and some inputs may be in RTL scripts—use explicit directionality markers or the dir attribute to prevent character reordering bugs.
  • Your UI includes inline numbers, punctuation, or LTR brand names within RTL paragraphs, and you need the Unicode Bidirectional Algorithm to handle visual ordering correctly.
  • You're localizing a web app for multiple markets and need CSS or HTML direction controls that adapt reliably across LTR and RTL locales without duplicating layout code.

Code Examples

💡 Note
The BAD CODE uses physical CSS properties (float: left, margin-right, padding-left) that don't adapt to RTL layouts and omits direction hints on user content, causing Arabic and Hebrew text to render with incorrect visual order; the GOOD CODE switches to logical CSS properties (float: inline-start, margin-inline-end, padding-inline-start) that automatically flip based on writing direction, adds dir="auto" for automatic direction detection, and wraps usernames in <bdi> to isolate them from the surrounding direction context.
✗ Vulnerable
<!-- Physical CSS — breaks in RTL layouts -->
<style>
.sidebar { float: left; margin-right: 20px; }  /* won't flip for RTL */
.icon    { padding-left: 8px; }                 /* won't flip for RTL */
</style>

<!-- User content without direction hint -->
<p><?= htmlspecialchars($userContent) ?></p>  <!-- Arabic content renders wrong -->
✓ Fixed
<!-- Logical CSS — automatically adapts to writing direction -->
<style>
.sidebar { float: inline-start; margin-inline-end: 20px; }  /* flips for RTL */
.icon    { padding-inline-start: 8px; }                      /* flips for RTL */
</style>

<!-- User content with automatic direction detection -->
<p dir="auto"><?= htmlspecialchars($userContent) ?></p>
<!-- <bdi> isolates inline user content from surrounding direction -->
<span>Posted by <bdi><?= htmlspecialchars($username) ?></bdi></span>

Added 23 Mar 2026
Edited 14 Aug 2026
Views 106
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings S 0 pings M 1 ping T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 2 pings S 1 ping S 0 pings M
No pings yet today
ChatGPT 1
Amazonbot 15 Perplexity 9 Ahrefs 8 PetalBot 8 ChatGPT 7 Google 5 Scrapy 5 Meta AI 2 Brave Search 2 Applebot 2 Bing 2 Twitter/X 1
crawler 63 crawler_json 3
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: High
⚡ Quick Fix
Use CSS logical properties (margin-inline-*, padding-inline-*, inset-inline-*), add dir='auto' to user-generated content blocks, wrap mixed-direction inline text in <bdi> or <span dir='auto'>
🔗 Prerequisites
⚠ Related Problems


✓ schema.org compliant