Usernames and user-generated content in RTL
Learn how to isolate usernames, display Arabic and English comments, and preserve direction through editing, previews and search results.
By Omran Khleifat, Founder of Ritla
Guide13 min read
A gardening community inserts the display name MossKeeper+ into an Arabic notification. The name appears as +MossKeeper: its final plus sign has moved to the other end. Another notification puts a review count after a Latin name, and that count becomes visually associated with the name instead of the Arabic word it counts.
The user did not necessarily type anything wrong. The interface inserted a value without telling the browser where that value ends. User content can be Arabic, English, mixed script or mostly punctuation, regardless of the language selected for the interface.
Give each inserted value a boundary suited to its role. A display name is an inline unit. A review contains paragraphs. A search query is editable text and may also appear inside a result summary. Applying one direction to the whole component does not describe all of those roles.
Reproduce a name that loses its punctuation#
The notification's source puts the complete display name between Arabic words:
Problem
<p lang="ar" dir="rtl">ردّ MossKeeper+ على مراجعتك.</p>The intended name is MossKeeper+, including the suffix. In this context, its displayed sequence is +MossKeeper. An Arabic reader should read the Arabic notification around a name whose own characters remain associated with that name.
Better
<p lang="ar" dir="rtl">ردّ <bdi>MossKeeper+</bdi> على مراجعتك.</p>The bdi keeps the name's punctuation within its own directional context. Its visible sequence remains MossKeeper+. A plain span without a direction declaration would not provide that isolation.
These rendering patterns were measured in Chromium 151 on September 24, 2026. Firefox and Safari were not included in those measurements, so verify the fixtures in the browsers your community supports.
The suffix is part of a permitted display name in this example. Do not remove it to make the notification easier to render. If a separate account handle has a restricted grammar, that is a different field contract. Apply isolation to the display-name component even when today's test accounts happen to use uncomplicated names.
Keep the name separate from the count#
A count following a Latin name can join the same LTR run. The Arabic sentence then loses the intended visual association between the count and its noun:
<p lang="ar" dir="rtl">نشر NurseryNotes 8 مراجعات هذا الأسبوع.</p>The reader should encounter an author and a count of reviews as distinct pieces of the sentence. Isolate the name, leaving the count with the surrounding Arabic text:
<p lang="ar" dir="rtl"> نشر <bdi>NurseryNotes</bdi> 8 مراجعات هذا الأسبوع.</p>Do not wrap the author, count and noun together as one automatic value. That would describe a larger phrase than the inserted name and leave its internal relationships to bidi inference.
The W3C guidance for inline bidi markup recommends tightly wrapping inserted opposite-direction phrases. Here the application knows that the author and review count are separate fields, even though it cannot predict the author's script. Preserve that distinction in the markup.
Count wording remains the translation system's responsibility. The boundary fixes visual grouping, not Arabic grammar. Test the sentence with the counts your product supports, but keep the author wrapper intact through those variations.
Automatic direction is appropriate for an unknown name#
A default bdi isolates its contents and determines direction automatically. The first strong character establishes that direction: a Latin letter selects LTR and an Arabic letter selects RTL. MDN's bdi reference explains this behavior.
The application's locale is not evidence about the user's name. An English interface can show an Arabic name, and an Arabic interface can show a Latin name. Do not choose the name's direction from the route or the current viewer's language.
Keep labels outside the automatic boundary:
<p lang="ar" dir="rtl"> اسم العضو: <bdi>بستان الندى</bdi></p>Putting the Arabic label inside the same wrapper would make its first strong letter influence the value's direction. The wrapper should contain the complete name and nothing else that belongs to the interface.
Leading digits, punctuation or an emoji do not necessarily settle direction. Test names that begin with those characters and then continue in Arabic or English. Also include a name containing only Western digits and punctuation: the default bdi resolves LTR in the measured behavior rather than adopting the parent's RTL direction.
Direction is not language detection. A name's LTR direction does not prove that it is English. Apply language metadata when the content's language is known, and avoid assigning English to every Latin-script value. MDN's dir reference distinguishes the available direction settings.
Choose review direction at the paragraph level#
A review body is not just a long username. One reviewer may write an Arabic paragraph followed by an English explanation for another member. Putting dir="auto" on a single outer container does not independently choose the base direction of every ordinary paragraph inside it.
If the product stores structured paragraphs, give each paragraph its own automatic direction:
<section dir="rtl"> <p lang="ar">كاتب المراجعة: <bdi>MossKeeper+</bdi></p> <p dir="auto" lang="ar">وصلت الشتلات بحالة جيدة.</p> <p dir="auto" lang="en">The roots were protected during delivery.</p></section>The interface header stays RTL. Each body paragraph establishes its own base direction. The language attributes here are known fixture metadata, not something dir="auto" computes. HTML's direction rules define how automatic direction is determined.
Do not include the translated review heading in the same automatic block as the body. An Arabic heading placed first can select RTL for the whole block even when the review itself begins in English.
For plain-text comments, decide how line breaks map to paragraphs. Preserving a newline visually is not the same design decision as creating separate paragraph elements with separate direction choices. Keep the stored text or structured paragraphs available rather than treating the rendered DOM as the authoritative comment record.
Very short comments need testing too. A punctuation-only reply has no letter from which to infer a language or a strong direction. Define a readable fallback without rewriting the user's characters.
A single plain-text block needs a different paragraph policy#
An older comment API may return one string with preserved line breaks rather than structured paragraphs. Do not pretend that the structured renderer below accepts that string unchanged. Choose either an explicit text-to-paragraph conversion under the product's rules or a plain-text presentation that handles bidi paragraphs.
For a dedicated plain-text block, CSS provides another option:
.plain-comment { white-space: pre-wrap; unicode-bidi: plaintext; overflow-wrap: anywhere;}The plaintext value determines base direction separately for the block's bidi paragraphs using Unicode's first-strong rules. Preserved paragraph breaks can therefore separate direction choices. It does not require splitting the stored comment into several database values. CSS Writing Modes defines this behavior.
Keep this rule on the dedicated comment text block. Do not apply it to a whole review card containing interface labels, buttons and author metadata. It also does not become an inherited direction policy for every descendant block in a rich-text document. Those blocks need their own structure.
Test a preserved line break between Arabic and English text, a blank line and a line containing only punctuation. A soft wrap caused by a narrow viewport is not an author-supplied paragraph boundary. Review both paragraph direction and alignment in supported browsers instead of treating those as one property.
For structured content, prefer the explicit paragraph markup already shown. The plain-text CSS path is an alternative for that content representation, not an additional rule to apply over every automatically directed field.
Render plain text as text and retain the boundaries#
Isolation does not make user-supplied HTML safe to insert. For a plain-text review feature, create the wrapper and assign the user's value through a text binding. Do not interpolate the value into a string of HTML merely because that string includes bdi.
This preview expects an author string and an array of paragraph strings. Each array item is already a paragraph under the application's content model:
<section id="review-preview" dir="rtl"></section>Run this JavaScript after the markup:
function renderReviewPreview(author, paragraphs) { if (typeof author !== "string" || !Array.isArray(paragraphs) || paragraphs.some(text => typeof text !== "string")) { throw new TypeError("Expected an author and paragraph strings"); } const preview = document.getElementById("review-preview"); if (!preview) throw new Error("Review preview element is missing"); const header = document.createElement("p"); header.lang = "ar"; const name = document.createElement("bdi"); name.lang = ""; name.textContent = author; header.append("كاتب المراجعة: ", name); const body = paragraphs.map(text => { const paragraph = document.createElement("p"); paragraph.className = "review-paragraph"; paragraph.dir = "auto"; paragraph.lang = ""; paragraph.textContent = text; return paragraph; }); preview.replaceChildren(header, ...body);} renderReviewPreview("MossKeeper+", [ "وصلت الشتلات بحالة جيدة.", "The roots were protected during delivery."]);The empty language attributes indicate that this example has no language metadata for the submitted fields. Reliable metadata could replace them. The code still declares the known language of the interface label and makes direction choices independently.
Setting textContent inserts the values as text. Markup-looking input remains text instead of becoming elements. The code validates only the input shape; account-name limits, content rules and empty-review validation remain the application's responsibility.
Every update recreates the same directional structure. Use that consistency in an edit preview as well as the published review. A correct initial render is insufficient if a later update replaces the protected fields with one unisolated string.
One outer boundary does not understand every phrase inside a comment#
Automatic direction selects a base direction. It does not parse a review into names, quotations, numbers and URLs. An Arabic review that mentions an English product name and then a quantity can contain an internal bidi ambiguity even though its paragraph is correctly isolated from the page.
Keep the outer boundary because it protects the relationship between the review and the interface. Address internal structures where the content model recognizes them. A structured mention can have its own name boundary; a known URL label can have its own direction policy.
Do not run a regular expression over arbitrary text and treat every Latin substring as a separate language or semantic phrase. The boundary can belong around several words and their punctuation, while a digit may belong to an Arabic noun outside that phrase. Splitting on script changes cannot establish those relationships reliably.
For plain text with ambiguous mixed-language composition, preserve the author's wording and provide a preview. If the product offers an explicit direction choice, scope it to the submitted paragraph or text block. Keep that content preference distinct from the viewer's interface language.
For richer mixed sentences, continue with Arabic and English mixed text. A review renderer needs to contain the user's content without pretending that one automatic setting interprets its meaning.
Preserve mention boundaries through rich-text rendering#
A rich-text review may contain mentions, links, quotations and lists. Treat the supported document structure as the source of those boundaries. Put automatic direction on the rendered paragraphs, and isolate a mention's display name within the paragraph where it appears.
When a mention becomes a link, retain the name wrapper inside the link or use a direction declaration that tightly wraps the displayed value. Keep the target account identity separate from its display label. A user changing the script of their name should not change which account a mention points to.
HTML sanitization and bidi structure need separate responsibilities. A sanitizer's allowed output should retain the direction attributes your renderer requires, while the renderer should not accept arbitrary user-controlled direction overrides merely because the feature permits formatting. Define which direction choices the document model supports and validate them there.
Test the final rendered tree, not just the Markdown or rich-text source. A highlighter, mention plugin or sanitizer can replace an element after the application supplied the right wrapper. An edited or quoted review can therefore behave differently from its original rendering.
Do not add directional characters to a display name in storage to compensate for a rich-text plugin dropping its markup. Repair the rendered mention component so every occurrence uses the same field contract.
Give the editor and search field an input policy#
An editor that accepts Arabic and English can use automatic direction for its entered text. Keep its Arabic label outside the control:
<form lang="ar" dir="rtl"> <label for="review-draft">اكتب مراجعتك</label> <textarea id="review-draft" name="review" dir="auto"></textarea> <button type="submit">نشر المراجعة</button></form>Test Arabic input, English input, leading punctuation and a change of the first strong character. Do not rely on the empty field's appearance as proof that an Arabic entry will render correctly. In the measured behavior, an empty automatic-direction control starts LTR, while typing Arabic selects RTL.
Use a persistent label instead of depending on the alignment of an Arabic placeholder to identify the field. Also test editing and selecting mixed text; a correct read-only preview does not establish usable input interaction.
A dedicated account handle with a known LTR grammar may need explicit LTR input. A multilingual display-name field has a different contract. Do not assign the same direction rule to both fields because they appear in the same profile form.
Search input deserves the same distinction. A member can search by an Arabic phrase or a Latin name. Automatic direction is a reasonable starting point for that general search field, with fixtures covering both. A query does not become Arabic merely because the page's search button is translated.
Isolate echoed queries and highlighted names#
The search field and the result summary are different rendering contexts. Even if the input chooses its direction correctly, the submitted query can affect the surrounding Arabic sentence when echoed without a boundary.
<p lang="ar" dir="rtl"> نتائج البحث عن <bdi>MossKeeper+</bdi></p>The query is one inserted value. Keep the whole query within its boundary, including punctuation that belongs to what the user typed. The interface's result count and explanatory wording remain outside it.
A search highlighter must retain that outer boundary:
<p lang="ar" dir="rtl"> العضو: <bdi><mark>Moss</mark>Keeper+</bdi></p>The highlighted substring is not a separate username. Wrapping only the marked fragment while leaving the rest exposed changes the structure the component needs. Compare the highlighted result with the unhighlighted one using the same full name.
Keep the original query as data. Search normalization, matching and the exact display of the submitted phrase are separate policies. Do not mutate the stored query's character order to make its result summary appear correct.
Re-evaluate direction for excerpts and edited text#
A collapsed review or search excerpt may omit the opening Arabic paragraph and begin with the English paragraph. Its first strong character is now different from the complete post's. Automatic direction on the excerpt should reflect the text it actually contains, not a direction cached from the original opening.
Treat the excerpt as a presentation of the source record. Keep its own value boundary in the surrounding summary, and retain the full post for expansion. Do not save a shortened display string back over the review or change its character order to match one preview.
If the author explicitly selected a direction for a paragraph, that preference needs a documented policy when you excerpt the paragraph. An author-selected preference is different from a direction your application inferred and cached. Do not silently convert an automatic choice into permanent metadata that survives unrelated edits.
Test editing the first word from Arabic to English and back. The same DOM node may be reused, so inspect whether it still has dir="auto" or whether a previous render left a fixed direction behind. Also test deletion until only punctuation remains, followed by new text in the other language.
Expansion should reveal the original paragraph boundaries, not merely append the rest of a mixed-language review into the excerpt's single direction context. The compact and expanded views can use different markup, but both should preserve the content model they display.
These transitions distinguish an intended direction change from a stale rendering bug. A name or excerpt changing direction after its first strong character changes can be correct. Interface labels and counts moving because their boundary was lost are a separate failure.
Long names and comments need a separate width policy#
A directional boundary does not guarantee that a long name fits. A gardening group can choose a lengthy display name or post an unbroken technical string in a review. Test those values in the actual author row, search result and moderation panel.
For prose that should remain readable, wrapping can be appropriate:
.review-paragraph { overflow-wrap: anywhere; white-space: pre-wrap;}overflow-wrap allows breaks within otherwise unbreakable strings. white-space: pre-wrap preserves whitespace while allowing wrapping. Apply these under the feature's whitespace policy, not as a substitute for deciding paragraph structure.
If an author row truncates names, provide a usable way to inspect the full name where identity matters. Do not permanently hide the punctuation or suffix that distinguishes two members. Changing the stored name to a shortened version is not a layout fix.
Check previews and collapsed comments at narrow widths. A name can keep its internal order yet lose the characters a moderator needs to distinguish accounts. That is a visibility failure, not evidence that automatic direction failed.
Keep moderation identity separate from visual appearance#
Isolation helps contain a user's text within the interface. It does not prove that two similar-looking names belong to the same account. Moderation actions should use stable account identifiers and explicit targets rather than infer identity from the visible name alone.
Directional formatting characters inside submitted content also need a field-specific policy. They can influence the internal display even when the outer wrapper is correct. Inspect suspect values as code points when a moderation report and a stored string seem inconsistent.
Do not remove every invisible character or every character outside ASCII as a supposed RTL repair. Legitimate writing can contain characters that a simplistic cleanup damages. Decide separately what a constrained account handle permits and what a verbatim comment preserves.
The Unicode Bidirectional Algorithm explains how directional formatting affects display. A moderation view can offer a visible diagnostic representation while retaining the submitted content and account record. Keep that representation clearly distinct from the normal displayed name so reviewers do not mistake diagnostics for the user's actual spelling.
When a name changes, review notifications, mentions and moderation previews against the intended account record. The same isolation component should handle both old and new scripts without changing the action target.
Test the boundaries through publication and editing#
Build a fixture set around the community's actual content model, not only around translated interface labels. Include an Arabic name, a Latin name with terminal punctuation, a name followed by a count, a numeric-only name, and a mixed-language review with separate paragraphs.
| Surface | What to inspect |
|---|---|
| Notification sentence | Name punctuation stays with the name; counts read with their nouns |
| Published review | Each paragraph establishes its intended base direction |
| Draft and edit preview | Direction structure survives every update |
| Search summary and highlights | Complete query and name boundaries remain intact |
| Mention and quoted review | The final rendered tree preserves field boundaries |
| Compact moderation view | Full identity is available and actions target the right account |
Compare stored values with DOM text to establish character preservation, then review the visible reading order. Copying or inspecting the accessibility tree follows logical text order and cannot replace the visual check. Review labels and keyboard interaction separately where controls are involved.
Repeat the journey after changing the first strong character, switching interface language and editing an existing post. Reused DOM nodes and alternate preview templates deserve attention because they can retain an old direction or omit a wrapper even when the published view was correct.
For Latin names inserted into Arabic text, a free Ritla scan can report : Unisolated bidi hazards: phones, emails, Latin tokens inside Arabic text. Paragraph meaning, editing behavior and moderation identity still need the targeted tests above. Continue with how to handle dynamic text direction or Arabic text overflow when the next problem concerns changing content or long values.