npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rtl-text-tools

v1.2.0

Published

Cross-browser RTL text utility — fixes digits, punctuation, ellipsis, and bidi markers for Arabic, Hebrew, Persian, Urdu, and other RTL scripts. Supports IE11+.

Readme

rtl-text-tools

npm version License: MIT npm downloads Coverage

A complete text processing toolkit for RTL (Right-to-Left) languages. Fix ellipsis, punctuation, digit conversion, brackets, bidi wrapping, and CSS helpers for Arabic, Hebrew, Persian, Urdu, and other RTL scripts.

Works back to IE11 with zero runtime dependencies. Fully tested and optimized for maximum browser compatibility.

📜 Features

  • RTL Detection — Identify if text contains RTL characters (Arabic, Persian, Syriac, Thaana, N'Ko, and more)
  • Strict Language Detection — Specifically detect Hebrew (with stricter rules), Kurdish, Pashto, Sindhi, Uyghur, and Punjabi
  • Direction Normalization — Fix mixed RTL/LTR text readability issues
  • Digit & Decimal Conversion — Convert Latin digits to Persian (۰–۹), Arabic-Indic (٠–٩), or smart Persian decimals (safely ignores IPs and version numbers)
  • Punctuation Conversion — Convert , ; ? to their RTL equivalents ، ؛ ؟ (Skips Hebrew to preserve Western punctuation)
  • Bracket Fixing — Convert and fix brackets for Arabic, including Quranic brackets ﴿ ﴾
  • Ellipsis Fixing — Move trailing ... or to the start of RTL text
  • Bidi Markers — Wrap text with Unicode RLE/PDF control characters for plain-text contexts
  • CSS & DOM Helpers — Ready-to-use style objects and attribute setters
  • Advanced Text Normalization — Language-specific normalizers for Persian, Arabic, Hebrew, and Urdu (diacritics, honorifics, final forms, etc.)
  • Enhanced DX — Strict TypeScript intellisense based on the selected language
  • 99% Test Coverage — Comprehensive test coverage maintained for all new features

🚀 Installation

npm install rtl-text-tools

or

yarn add rtl-text-tools

or

pnpm add rtl-text-tools

📖 Usage

Quick start fixRTL()

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

fixRTL('مرحبا, رقم 123...')
// → "...مرحبا، رقم ۱۲۳"  (Persian digits, RTL punctuation, ellipsis moved)

fixRTL('مرحبا, رقم 123...', { lang: 'arabic' })
// → "...مرحبا، رقم ١٢٣"  (Arabic-Indic digits)

fixRTL('Hello, world!')
// → "Hello, world!"  (no RTL characters → unchanged)

Options

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

// TypeScript now provides strict intellisense for options based on the selected language!
fixRTL(text, {
  lang: 'persian',            // 'persian' | 'arabic' | 'hebrew' | 'urdu'
  convertDigits: true,        // convert Latin 0–9 to locale digits
  convertPunctuation: true,   // convert , ; ? → ، ؛ ؟ (automatically skips Hebrew)
  fixEllipsis: true,          // move trailing ... to the start
  addBidiMarkers: false,      // wrap with Unicode RLE/PDF (for plain-text)
});

Individual functions

import {
  hasRTL,
  hasHebrew,
  hasKurdish,
  toArabicDigits,
  toPersianDigits,
  toPersianDecimal,
  convertPunctuation,
  fixBracket,
  moveEllipsis,
  wrapRTL,
  wrapLTR,
  getRTLStyles,
  getLTRStyles,
  setDirAttribute,
} from 'rtl-text-tools';

// Detection
hasRTL('مرحبا')   // true
hasRTL('Hello')   // false
hasRTL('שלום')    // true

hasHebrew('שלום') // true
hasKurdish('کوردی') // true

// Digit conversion
toArabicDigits('Price 123')   // 'Price ١٢٣'
toPersianDigits('Price 123')  // 'Price ۱۲۳'
toPersianDecimal('Version 1.2.0') // 'Version 1.2.0' (Safely ignores versions/IPs)

// Punctuation
convertPunctuation('مرحبا, كيف حالك?')  // 'مرحبا، كيف حالك؟'

// Bracket fixing
fixBracket('Hello (world)') // Fixes brackets for general RTL
fixBracket('مرحبا (بالعالم)', 'arabic') // Fixes brackets specifically for Arabic

// Ellipsis (supports both ... and the Unicode … character)
moveEllipsis('مرحبا...')  // '...مرحبا'
moveEllipsis('مرحبا…')   // '…مرحبا'

React

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

function ArabicPrice({ label, price }) {
  return (
    <span style={getRTLStyles()}>
      {fixRTL(`${label}: ${price}`)}
    </span>
  );
}

Plain-text / email / textarea

For contexts where CSS cannot be applied (email clients, textareas, terminals), use addBidiMarkers: true to inject invisible Unicode bidi control characters:

const text = fixRTL('مرحبا, 123', { addBidiMarkers: true });
// Wrapped with RLM + RLE … PDF — forces RTL in any Unicode-aware renderer

Embedding LTR content inside RTL text

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

const url = wrapLTR('https://example.com');
// Safe to embed inside an Arabic paragraph

DOM helper

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

setDirAttribute(document.getElementById('article'), 'ar');
// → <div id="article" dir="rtl" lang="ar">

🔧 API

hasRTL(text: string): boolean

Returns true if the text contains RTL characters.

RTL character ranges:

  • Arabic: \u0600-\u06FF
  • Hebrew: \u0590-\u05FF
  • Arabic Supplement: \u0750-\u077F
  • Arabic Extended-A: \u08A0-\u08FF
  • RTL Presentation Forms: \uFB1D-\uFDFF, \uFE70-\uFEFC
  • Syriac, Thaana, N'Ko, Samaritan, Mandaic

convertPunctuation(text: string): string

Converts LTR punctuation marks to their RTL equivalents when RTL text is present. Replaces all occurrences. Note: As of v1.2.0, this function automatically skips Hebrew text to preserve Western punctuation.

| LTR | RTL | Name | |-----|-----|------| | , | ، | Arabic Comma | | ? | ؟ | Arabic Question Mark | | ; | ؛ | Arabic Semicolon |

fixRTL(text: string, options?: FixRTLOptions | string): string

Main function - Applies all RTL text fixes at once.

Note: As of v1.1.0, fixRTL has been made stricter and more precise, ensuring transformations are only applied when strictly necessary to prevent over-processing.

interface FixRTLOptions {
  lang?: 'persian' | 'arabic' | 'hebrew' | 'urdu'; // Enhanced TS intellisense based on language
  convertDigits?: boolean;        // default: true
  convertPunctuation?: boolean;   // default: true (automatically skips Hebrew)
  fixEllipsis?: boolean;          // default: true
  addBidiMarkers?: boolean;       // default: false
}

Also accepts a plain language string for shorthand: fixRTL(text, 'arabic').


🇮🇷 Persian Specific Functions

normalizePersianChars(text: string): string

Normalizes Persian-specific characters.

toPersianDecimal(text: string): string

Converts Latin numbers to Extended Persian decimals. Note: As of v1.2.0, this function safely ignores IP addresses and version numbers (e.g., 192.168.1.1 or 1.2.0 remain unchanged).

normalizeTehMarbuta(text: string): string

Normalizes TehMarbuta characters in Persian text.

removePersianDiacritics(text: string): string

Removes diacritics from Persian text.

🇸🇦 Arabic Specific Functions

normalizeArabicAlef(text: string): string

Normalizes different forms of Alef in Arabic.

normalizeArabicYeh(text: string): string

Normalizes different forms of Yeh in Arabic.

expandArabicHonorifics(text: string): string

Expands Arabic honorifics (e.g., PBUH, SWA).

toQuranicBrackets(text: string): string

Converts standard brackets to Quranic brackets ﴿ ﴾.

🇮🇱 Hebrew Specific Functions

normalizeMaqaf(text: string): string

Normalizes the Hebrew Maqaf (hyphen) character.

fixHebrewFinalForms(text: string): string

Fixes and normalizes Hebrew final letter forms.

normalizeHebrewQuotes(text: string): string

Normalizes Hebrew quotation marks.

🇵🇰 Urdu Specific Functions

normalizeUrduTehMarbuta(text: string): string

Normalizes TehMarbuta characters in Urdu.

expandUrduHonorifics(text: string): string

Expands Urdu honorifics.

removeUrduDiacritics(text: string): string

Removes diacritics from Urdu text.

🌍 Minor Languages Detection

hasKurdish(text: string): boolean

Returns true if the text contains Kurdish characters.

hasPashto(text: string): boolean

Returns true if the text contains Pashto characters.

hasSindhi(text: string): boolean

Returns true if the text contains Sindhi characters.

hasUyghur(text: string): boolean

Returns true if the text contains Uyghur characters.

hasPunjabi(text: string): boolean

Returns true if the text contains Punjabi characters.


Other Helpers

toArabicDigits(text: string): string

Converts Latin numbers (0-9) to Arabic-Indic numerals (٠-٩).

toPersianDigits(text: string): string

Converts Latin numbers (0-9) to Extended Persian numerals (۰-۹).

fixBracket(text: string, type?: 'general' | 'arabic'): string

Fixes and converts brackets in RTL text.

moveEllipsis(text: string): string

Moves a trailing ... or to the beginning of RTL text.

wrapRTL(text: string): string & wrapLTR(text: string): string

Wraps text with Unicode bidi control characters to force RTL/LTR rendering in plain-text contexts.

getRTLStyles() & getLTRStyles()

Returns { direction: 'rtl'/'ltr', unicodeBidi: 'embed' } ready for React inline styles.

setDirAttribute(element: AttributeSettable, lang: string): void

Sets dir="rtl" and the given lang attribute on any element.

🌍 Supported RTL Languages

  • Arabic (العربية)
  • Hebrew (עברית)
  • Persian/Farsi (فارسی)
  • Urdu (اردو)
  • Pashto (پښتو)
  • Kurdish (سۆرانی)
  • Sindhi (سنڌي)
  • Uyghur (ئۇيغۇرچە)
  • Punjabi (ਪੰਜਾਬੀ)
  • Syriac
  • Thaana
  • N'Ko
  • Samaritan
  • Mandaic

🖥️ Browser Compatibility

| Browser | Version | |---------|---------| | Chrome | 4+ | | Firefox | 3.5+ | | Safari | 4+ | | Edge | 12+ | | IE | 11+ | | iOS Safari | 3.2+ | | Android WebView | 2.1+ |

The compiled output targets ES5: var, regular functions, no arrow functions, no const/let. All regex uses plain \uXXXX BMP escapes the ES6 u flag is never used. String.replaceAll() is never used. Further optimized in v1.1.0 for maximum legacy browser compatibility.

🔄 Changelog

For a detailed list of releases, new features, and bug fixes, please see the Changelog.

🤝 Contributing

Contributions, issues, and feature requests are welcome!
Please check out the Contributing Guide for details.

📜 Code of Conduct

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

🔒 Security

For information on how to report security vulnerabilities, please see our Security Policy.

🐛 Issues

Found a bug or have a suggestion? Please open an issue on GitHub.

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

👨‍💻 Author

Homayoun Mohammadi

Note: This package is actively maintained and new features are being added regularly. Star the repository to stay updated!