Why phone numbers get reversed in RTL
Learn why spaces and a leading plus reorder phone numbers in RTL text, how the bidi rules produce the result, and how to verify the isolation fix.
By Omran Khleifat, Founder of Ritla
Guide13 min read
An Arabic crew-booking screen shows a coordinator's phone number with the country code at the wrong end. Each group of digits is intact, but the groups have changed places and the plus sign trails the number. The API can still contain the correct value. This is visual reordering, not necessarily corrupted phone data.
Phone digits have weak bidi types rather than the strong left-to-right direction of Latin letters. Spaces separate the groups, and the leading plus does not establish an LTR run. In an unisolated RTL paragraph, the browser can therefore arrange the groups from right to left while keeping the digits within each group in their usual order.
The fix is to give the phone value its own LTR boundary. Understanding why that works also explains why removing spaces, right-aligning the text or checking only the copied value can leave the defect unresolved.
Reproduce the failure with the full contact line#
The booking interface uses this fictional coordinator number:
+44 7700 900682It is drawn from the mobile range reserved by Ofcom for drama use. Use it as a fixture, not a callable production contact.
An Arabic reader comparing the screen with a supplied contact number should see the plus before the country code, followed by the remaining groups in their recorded sequence. Reading the surrounding interface from the right does not turn the phone number into a different sequence.
Problem
<p dir="rtl">هاتف منسق الطاقم: <span>+44 7700 900682</span></p>The phone substring displays, from its leftmost to rightmost edge, as:
900682 7700 44+That result follows the supplied measurements in Chromium 151, last checked on 2026-09-24. Firefox and Safari were not part of those measurements; repeat the fixture in the browser versions your product supports.
The plain span is present so that the example resembles a styled contact value. It does not establish a bidi boundary. Moving the number into an otherwise identical unconfigured span cannot repair the failure.
Keep the Arabic label in the reproduction. Then test the number alone in an RTL paragraph as a separate case. Both matter, but the reason the digits acquire their working bidi types is not identical in those contexts.
What the browser sees before it sees a phone number#
The Unicode Bidirectional Algorithm processes characters and directional context. It does not infer a telephone-number data type from a spaced string in an ordinary paragraph.
For the Western-digit fixture, the starting types are:
| Content | Initial bidi type | Meaning here |
|---|---|---|
| Arabic letters in the label | AL | Arabic strong directional text |
| Western digits | EN | European Number, a weak type |
| Ordinary spaces | WS | Whitespace, resolved in context |
| Leading plus | ES | European Separator, a weak type |
The plus is therefore not initially a neutral character. Calling it neutral is a useful shorthand only after explaining how it is resolved in this particular position. That distinction matters when following the rules rather than merely describing the final screenshot.
The number contains no strong Latin or Arabic letter of its own. Its digits still read left to right within their groups, but that does not make the whole spaced value behave like one Latin word. W3C's bidi basics explains the difference between weak numeric content and strong directional runs.
The paragraph's RTL direction supplies the surrounding context. The spaces do not tell the browser that all three groups belong to a single telephone value. That missing content boundary is what the HTML needs to express.
Step through the Arabic-label case#
The following trace applies to the plain paragraph above: Western digits, ordinary spaces, a leading ASCII plus, an Arabic label and no extra controls or directional wrappers. It is not a prediction for every formatter or punctuation pattern.
The Arabic label changes the digits' working type#
Rule W2 looks backward from European numbers for the relevant strong directional context. After Arabic letters, the digits' working type changes from EN to AN.
The name “Arabic Number” describes a bidi type. It does not mean the browser replaces Western digit glyphs with Arabic-Indic digits. The stored 7700 remains the same characters and still appears as 7700 within its group.
This change happens during directional processing. It is not an API transformation, a font substitution or a localization formatter rewriting the number. Searching the application state for a reversed string may therefore find nothing.
Spaces and the leading plus do not join the groups#
The ordinary spaces remain separators between numeric groups. For neutral resolution, the numeric types influence adjacent separators as RTL context. The spaces consequently resolve with the surrounding RTL sequence rather than joining all the digits into one uninterrupted LTR unit.
The leading plus is not between two numeric groups. Its initial separator type does not make it part of an LTR phone token; it resolves as neutral and then with the RTL context in this example.
The final ordering reverses groups, not their internal digits#
In this simple RTL paragraph, the resolved numeric characters occupy an even bidi level while the separating RTL material occupies the surrounding odd level. The final reordering preserves each numeric group's internal order but places the groups in RTL sequence.
The consequence for the phone substring is:
Written order: +44 7700 900682Visual order: 900682 7700 44+The normative rules and level-based reordering are defined in the Unicode Bidirectional Algorithm. The useful diagnostic distinction is visible without calculating every level: intact digit groups with reordered separators point toward bidi context, whereas a reversed API value is a data problem.
Do not reverse the string in JavaScript to compensate. Reversing individual characters would also turn 7700 into 0077, a different problem from the group movement seen here. It would change the data used by every other rendering and copy path.
The same failure can appear without an Arabic label#
A developer may remove the label, see that the number still looks wrong, and conclude the diagnosis was incorrect. The missing distinction is between the Arabic-specific W2 step and the broader RTL treatment of separated numeric groups.
This bare value still has an RTL paragraph context:
<p dir="rtl">+44 7700 900682</p>The spaced phone value still displays as 900682 7700 44+ in the supplied measurements. There is no preceding Arabic letter in this paragraph to trigger the same W2 conversion, but the remaining numeric and neutral-resolution rules still permit the group sequence to resolve RTL.
Moving the phone to a bare table cell or its own line is therefore not a general fix for a spaced number. It may change the reproduction, but it does not necessarily establish the required direction boundary.
This is also why testing only a Latin-labelled contact card can miss the defect. Its surrounding strong text supplies a different context. Keep a fixture for the actual Arabic sentence and another for a value displayed alone in an RTL region.
If the real component has extra Latin words, hidden directional marks or an existing dir attribute, simplify one factor at a time. Preserve a record of the original DOM so that a reduced fixture does not accidentally remove the condition you need to explain.
Removing spaces fixes only part of the symptom#
Suppose a developer changes the displayed number to a compact international form:
<p dir="rtl">هاتف منسق الطاقم: +447700900682</p>There are no longer multiple digit groups for spaces to separate. But the leading plus still has the wrong directional context. The phone substring displays as:
447700900682+A quick screenshot review that checks only whether the digits are intact can miss that remaining error. The acceptance criterion includes the sign as well as the digit sequence.
Compact formatting can be a valid product choice. It should be chosen for readability or a destination's representation, not as a substitute for declaring direction. The same value still needs a boundary when inserted into an RTL sentence.
Changing spaces to hyphens is not a reliable repair either. Hyphens do not establish a phone token, and separated Western-digit groups after Arabic have their own reordering behavior. Do not change a contact's punctuation until the current screenshot looks acceptable and treat that as a directional policy.
Other separators, including nonbreaking spaces and parentheses, need fixtures using their actual code points. Do not assume that a typographically similar separator has exactly the same bidi behavior. Compare the real formatted string rather than retyping it with ordinary spaces.
Give the whole phone value an LTR boundary#
For a known-format Western-digit phone value, put direction on the element that owns that value:
<p dir="rtl"> هاتف منسق الطاقم: <span dir="ltr">+44 7700 900682</span></p>The phone now displays as +44 7700 900682. The Arabic label remains in the paragraph's RTL context. The value is processed inside its own directional boundary, so the label no longer supplies the internal context for the phone's spaces and sign.
An automatic isolate also works for this digit-only fixture:
<p dir="rtl">هاتف منسق الطاقم: <bdi>+44 7700 900682</bdi></p>Without strong letters, this bdi resolves to LTR. Explicit LTR is the clearer contract for a field whose phone representation is already defined. See MDN's bdi reference for its default automatic direction and isolation behavior.
Keep the label outside either boundary. Wrapping the complete Arabic label and phone inside an automatic element lets the label select RTL again. A page can contain a bdi tag and still leave the phone in the wrong internal context.
Also keep the phone whole. Separate isolates around the country code and subscriber groups tell the browser those are independent pieces, when the product wants one complete phone value. The boundary should follow the data field, not the visual spacing.
Do not force the entire contact panel LTR. Its heading, labels and explanatory Arabic text still belong in the Arabic interface. W3C's inline bidi markup guidance describes isolating the inserted phrase at the appropriate scope.
Alignment and row ordering do not repair the number#
A contact row has two kinds of order: the arrangement of its UI elements and the character order within its phone value. A CSS change to one is not evidence that the other is correct.
text-align: right can put the complete text nearer the right edge while leaving the phone's groups in the same wrong visual sequence. white-space: nowrap can keep the value on one line without isolating it. Those declarations may meet layout requirements, but they do not provide the missing directional boundary.
Likewise, reversing a flex row changes where its child boxes appear. If the phone is one child text element, that operation does not declare how the browser should process its internal digits, spaces and plus. Do not rebuild the contact line as a reversed row of digit groups to reproduce one desired screenshot.
That approach would turn a text-formatting problem into a layout protocol: the country code, separators and subscriber groups would have to be split into separate boxes, copied back into a string and updated together. The phone already has a logical sequence. Preserve it and supply its direction instead.
Third-party phone components sometimes introduce this split for a country selector or a prefix display. Inspect their actual DOM rather than assuming the overall control's direction applies identically to every part. The country selector can follow one layout policy while the editable or read-only phone value needs its own text boundary.
Test the final assembled component with the selector open and closed, then inspect the saved read-only value. Do not predict its exact group order from this guide's single-span fixture if the component renders several independent boxes. Establish which element owns the complete displayed number before applying the repair.
Keep presentation choices independent. If a contact card requires right alignment and no wrapping, retain those choices while adding the phone's LTR boundary. The successful comparison should show the same component doing the same job, with only the intended text-processing difference.
A dial link needs a boundary as well as a destination#
A tel: destination identifies what should be dialled. It does not, by itself, declare the direction of the link's visible text.
For the crew contact, put the boundary on the existing link:
<p dir="rtl"> هاتف منسق الطاقم: <a href="tel:+447700900682" dir="ltr">+44 7700 900682</a></p>The compact destination and spaced display are two representations of the same fixture. The tel URI specification, RFC 3966, defines telephone URI syntax. A correct destination does not establish that the visible sign and groups are in the correct order.
Inspect those requirements separately. Check the link text in the Arabic sentence, inspect the destination string, and verify the application's copy action against its promised representation. Do not actually dial a fixture to test directional rendering.
If a link combines the phone with an Arabic action label, the link no longer owns only the phone value. Keep an inner LTR boundary around the phone or separate the action label. Moving dir="ltr" to a larger ancestor changes the scope of the repair.
For implementation details such as display formatting and contact actions, continue with phone numbers in RTL interfaces. The cause of the reversal remains the inline directional context, independently of whether the value is clickable.
Input direction and display direction are separate paths#
The saved contact can look wrong even if its editing field looks correct. A telephone input and a paragraph are different rendering contexts; success in one does not establish the other.
input type="tel" uses LTR by default in the supplied measurements, whereas a text input normally inherits the page's direction. A reusable input component that renders type="text" can therefore show a different result from the telephone-specific editor.
Express the intended field explicitly:
<label for="crew-contact">هاتف منسق الطاقم</label><input id="crew-contact" name="crewContact" type="tel" dir="ltr" value="+44 7700 900682">This is a single contact field, not a direction change for the whole form. MDN's tel input reference covers the control's purpose and notes that it does not automatically validate every international telephone format.
Do not insert hidden directional marks into the editable value to force its appearance. Keep the phone data separate from presentation. Check the value after typing, pasting, clearing and saving, then inspect the read-only contact line produced after save.
If the product supports extensions or localized formatting, use fixtures for those actual strings. Their additional punctuation and words can change the directional context. This guide's simple three-group international number does not prove every accepted input format behaves identically.
Arabic-Indic digits are a separate edge case#
The working examples use Western digits. Do not assume that changing the displayed digit script preserves the same internal behavior under isolation.
The equivalent spaced Arabic-Indic fixture is:
+٤٤ ٧٧٠٠ ٩٠٠٦٨٢The supplied browser checks show that spaces between Arabic-Indic digit groups can resolve RTL even inside bdi or an element with dir="ltr", including in an English context. Isolating the value removes outside influence; it does not erase the internal bidi types of its characters.
That is the limit of “just wrap it in LTR.” The wrapper repairs the Western-digit reproduction, but it is not a promise that every digit script and separator combination displays in source order.
Define the accepted phone representation with the product team. If Arabic-Indic display is required, test that exact representation and its interactions with copying and dial destinations. An override can force order, but that is a stronger operation with consequences for any letters or extension labels included in its scope. Do not apply it to a whole contact card.
Keep the original phone value and its display representation distinguishable. A conversion of digit script is a formatting decision; W2's change of a working bidi type is not that conversion. Confusing the two leads to fixes in the wrong layer.
Debug the text, the boundary and the surrounding paragraph#
Select the phone element in DevTools and inspect its actual attributes. Then inspect the computed values on both the phone and its parent:
const phone = $0; // Select the phone value element in DevTools.const elements = [phone.parentElement, phone].filter(Boolean);console.table(elements.map(element => { const style = getComputedStyle(element); return { tag: element.localName, dir: element.getAttribute("dir"), direction: style.direction, unicodeBidi: style.unicodeBidi, text: element.textContent };}));Computed LTR alone is not sufficient evidence of an isolation boundary on a normal inline span. Inspect the HTML and unicode-bidi as well. A stylesheet can also interfere with otherwise useful markup, so locate winning declarations before adding another wrapper.
If appearance differs from the fixture, inspect the real characters. A formatter, copied contact or translation resource may contain nonbreaking spaces or directional controls. Log code points rather than relying on how an ordinary console string looks:
console.log(Array.from(phone.textContent, character => `U+${character.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`).join(" "));Run this after the preceding inspection block, which defines phone. Compare the string with the stored value. If the stored value is already wrong, fix that data path; if it is correct and only the display changes, investigate context and boundaries.
Copying the phone into a text editor is useful for checking logical order. It cannot prove the original display was correct: the supplied browser checks preserve logical order on copy even when the visible groups reorder. A DOM text assertion and a visual assertion test different properties.
Build a regression fixture that can expose the defect#
Keep the broken and repaired contact lines beside each other during development. The broken version should visibly demonstrate the problem before you rely on that fixture to verify the fix.
| Fixture | What to verify |
|---|---|
| Spaced phone after the Arabic label, no boundary | The fixture exposes group movement and a trailing plus |
| The same phone alone in an RTL paragraph | Removing Arabic letters does not make spaces a safe boundary |
| Compact phone with no isolation | The trailing-plus failure remains visible |
Complete phone inside dir="ltr" | The plus and every group match the required display sequence |
| Dial link with the same visible phone | Visible order and destination are independently correct |
| Telephone editor followed by saved contact display | Both rendering paths retain their intended direction |
| Arabic-Indic digit representation, if supported | Its separate internal behavior has an explicit acceptance test |
At narrow widths, inspect the full phone and the Arabic label again. A line wrap is not evidence that the groups became independent values. Check any compact card or mobile view that moves the value into a different container.
Update the contact without reloading, switch between Arabic and English routes, and inspect any masking or formatting state that changes separators. Record browser versions with the visual evidence. Do not assume that a telephone input screenshot covers the read-only link or that a successful copy test covers appearance.
Make the visual expectation explicit in a bug report: the plus precedes the country code, the country code precedes the remaining groups, and each group's digits retain their recorded order. “The phone should be left aligned” is a different requirement and can lead the implementer toward a CSS change that leaves the actual defect intact.
For automated coverage, a structural assertion can check that the value has a direction boundary and contains the original text. Pair it with visual coverage of the Arabic sentence. A shallow assertion that some ancestor has dir would accept the broken paragraph, since the paragraph already declares RTL. The assertion needs to identify the phone's own scope.
A free Ritla scan can report , unisolated bidi hazards: phones, emails, Latin tokens inside Arabic text. Use the spaced-number reproduction to verify the repair, then read phone numbers in RTL interfaces for the wider component implementation or how browsers render mixed RTL and LTR text for the surrounding algorithm.