DEV Community

Cover image for I Built rtl-text-tools ( A Complete RTL Text Processing Toolkit for JavaScript )
Homayoun Mohammadi
Homayoun Mohammadi

Posted on

I Built rtl-text-tools ( A Complete RTL Text Processing Toolkit for JavaScript )

If you’ve ever worked with Arabic, Persian, Hebrew, Urdu, or any RTL (Right-to-Left) language on the web, you probably know the pain.

Mixed RTL/LTR text rendering breaks unexpectedly. Punctuation looks wrong. Numbers don’t match the locale. Ellipsis appears on the wrong side. URLs inside Arabic text become unreadable. And emails or plain-text environments completely destroy formatting.

After dealing with this problem repeatedly, I decided to build:

rtl-text-tools

A lightweight RTL text processing toolkit for JavaScript and TypeScript.

It handles:

  • RTL detection
  • Direction normalization
  • Arabic/Persian digit conversion
  • RTL punctuation conversion
  • Ellipsis fixing
  • Unicode bidi wrapping
  • CSS helpers
  • DOM helpers

And it works all the way back to IE11 with zero runtime dependencies.

Why This Exists


Most internationalization libraries focus on translations and formatting APIs.

But very few actually solve the rendering problems of RTL text itself.

For example:

"مرحبا, رقم 123..."

Visually, this often renders awkwardly in mixed-direction environments.

You usually want:

"...مرحبا، رقم ۱۲۳"

That means:

  • move ellipsis to the correct visual side
  • convert punctuation
  • convert digits
  • preserve RTL readability

That’s exactly what rtl-text-tools does.

Installation

npm install rtl-text-tools
Enter fullscreen mode Exit fullscreen mode

Quick Example

import { fixRTL } from 'rtl-text-tools';

fixRTL('مرحبا, رقم 123...');
// → "...مرحبا، رقم ۱۲۳"
Enter fullscreen mode Exit fullscreen mode

Arabic digits are also supported:

fixRTL('مرحبا, رقم 123...', { lang: 'arabic' });

// → "...مرحبا، رقم ١٢٣"
Enter fullscreen mode Exit fullscreen mode

If the text isn’t RTL, it returns the original string unchanged:

fixRTL('Hello world');

// → "Hello world"
Enter fullscreen mode Exit fullscreen mode

Features

1. RTL Detection

Detect whether text contains RTL scripts.

import { hasRTL } from 'rtl-text-tools';

hasRTL('مرحبا'); // true
hasRTL('שלום');  // true
hasRTL('Hello'); // false
Enter fullscreen mode Exit fullscreen mode

Supports:

  • Arabic
  • Hebrew
  • Persian/Farsi
  • Urdu
  • Syriac
  • Thaana
  • N’Ko
  • Samaritan
  • Mandaic
  • and more

2. Digit Conversion

Convert Latin digits into locale-specific numerals.

Persian digits

import { toPersianDigits } from 'rtl-text-tools';

toPersianDigits('Price 123');

// "Price ۱۲۳"
Enter fullscreen mode Exit fullscreen mode

Arabic-Indic digits

import { toArabicDigits } from 'rtl-text-tools';

toArabicDigits('Price 123');

// "Price ١٢٣"
Enter fullscreen mode Exit fullscreen mode

3. RTL Punctuation Conversion

Convert standard punctuation into RTL equivalents.

import { convertPunctuation } from 'rtl-text-tools';

convertPunctuation('مرحبا, كيف حالك?');

// "مرحبا، كيف حالك؟"
Enter fullscreen mode Exit fullscreen mode

Converts:

LTRRTL,،?؟;؛

4. Ellipsis Fixing

One of the most annoying RTL rendering issues.

import { moveEllipsis } from 'rtl-text-tools';

moveEllipsis('مرحبا...');

// "...مرحبا"
Enter fullscreen mode Exit fullscreen mode

Also supports the Unicode ellipsis character:

moveEllipsis('مرحبا…');

// "…مرحبا"
Enter fullscreen mode Exit fullscreen mode

5. Bidi Wrapping for Plain Text

CSS doesn’t exist in:

  • emails
  • terminals
  • textareas
  • logs
  • PDFs
  • some chat systems

For those environments, Unicode bidi markers are essential.

import { wrapRTL } from 'rtl-text-tools';

wrapRTL('مرحبا');
Enter fullscreen mode Exit fullscreen mode

It wraps text using invisible Unicode direction control characters for reliable RTL rendering.

You can also safely embed LTR content inside RTL paragraphs:

import { wrapLTR } from 'rtl-text-tools';

wrapLTR('https://example.com');
Enter fullscreen mode Exit fullscreen mode

6. React + CSS Helpers

import { getRTLStyles } from 'rtl-text-tools';

<div style={getRTLStyles()}>
  مرحبا بالعالم
</div>
Enter fullscreen mode Exit fullscreen mode

Returns:

{
  direction: 'rtl',
  unicodeBidi: 'embed'
}
Enter fullscreen mode Exit fullscreen mode

7. DOM Helper

import { setDirAttribute } from 'rtl-text-tools';

setDirAttribute(element, 'ar');
Enter fullscreen mode Exit fullscreen mode

Outputs:

<div dir="rtl" lang="ar">
Enter fullscreen mode Exit fullscreen mode

IE11 Compatibility


I intentionally made this package compatible with legacy environments.

The compiled output targets ES5:

  • no arrow functions
  • no replaceAll
  • no regex u flag
  • no modern syntax assumptions

So yes , it even works in IE11.

The Real Problem With RTL on the Web

Most developers only think about:

direction: rtl;
Enter fullscreen mode Exit fullscreen mode

But RTL support is actually much deeper than that.

The difficult part is:

  • mixed-direction content
  • Unicode bidi behavior
  • punctuation mirroring
  • locale digits
  • inline URLs
  • plain-text rendering
  • email clients
  • terminals
  • copy/paste consistency

This package tries to solve those practical problems in one place.

Example: Mixed RTL/LTR Text

Without normalization:

من در پارکی راه می رفتم Do not Park here

The rendering can become visually confusing depending on the environment.

With normalization:

normalizeDirection(text)
Enter fullscreen mode Exit fullscreen mode

The text becomes readable with proper RTL base direction.

Why Zero Dependencies?

Text utilities should be lightweight.

This package is:

  • dependency-free
  • tree-shakeable
  • framework-agnostic
  • TypeScript-friendly
  • browser-compatible
  • usable in Node.js

Open Source

The project is fully open source and contributions are welcome.

GitHub: https://github.com/homayounmmdy/rtl-text-tools

NPM: https://www.npmjs.com/package/rtl-text-tools

Final Thoughts

RTL support on the web is still surprisingly underdeveloped.

A lot of developers building multilingual products end up reinventing the same utilities repeatedly.

I wanted a small toolkit that handled the annoying RTL edge cases properly and could be dropped into any project easily.

If you work with Arabic, Persian, Hebrew, Urdu, or multilingual interfaces, I hope this package saves you some time.

Feedback, issues, and contributions are always welcome.

Top comments (1)

Collapse
 
tomas_2cbc42 profile image
Tomas

Such a great tool keep going