Skip to content

AhmedBaset/eslint-plugin-rtl-friendly

Repository files navigation

eslint-plugin-rtl-friendly

Helps you write code that works the same for both LTR and RTL languages.

Important

Until 1.0.0 is released, this plugin is in Beta.

npm version Downloads/month CI

Why RTL matters? (Click to expand)

With a global audience of over 800 million people who speak right-to-left (RTL) languages, ensuring RTL readability is essential for international web apps. The eslint-plugin-rtl-friendly is a linter that helps you write RTL-friendly code.

Why RTL matters?

You read this text from left to right.

However, texts in RTL languages are read from right to left.

هذا النص يُقرأ من اليمين إلى اليسار.

Notice how GitHub's markdown aligns the text to the right. This isn’t a bug—it's how RTL languages are read.

Imagine you're writing code the traditional way, creating a button with text and an icon:

return (
  <button>
    <CheckIcon className="mr-2" />
    <span>{getTranslation("buttons.done")}</span>
  </button>
);

The code above will work fine for LTR languages, but for RTL languages, the icon will appear on the right side of the text, just like the margin (mr-2). This means there will be no space between the icon and the text, and there will be extra space at the start of the button.

LTR: [{icon} {text}]
RTL: [{text}{icon} ]

The solution is to use me-2 instead of mr-2. me-2 stands for margin-inline-end, which means "right" in LTR languages and "left" in RTL languages. The updated code should be:

return (
  <button>
    <CheckIcon className="me-2" />
    <span>{getTranslation("buttons.done")}</span>
  </button>
);

RTL languages:

  • (ar) Arabic - العربية
  • (arc) Aramaic - ܐܪܡܝܐ
  • (ckb) Sorani Kurdish - کوردی
  • (dv) Divehi - ދިވެހިބަސް
  • (fa) Persian - فارسی
  • (ha) Hausa - هَوُسَ
  • (he) Hebrew - עברית
  • (khw) Khowar - کھوار
  • (ks) Kashmiri - कॉशुर
  • (ps) Pashto - پښتو
  • (sd) Sindhi - سنڌي
  • (ur) Urdu - اردو
  • (uz-Af) Uzbeki Afghanistan - ازبیک
  • (yi) Yiddish - ייִדיש

The orange areas on the map below show where RTL languages are spoken.
map

Currently, the plugin has one rule for Tailwind classes (Feel free to contribute!). It reports and auto-fixes the usage of physical properties classes to their logical counterparts.

demo

The plugin comes with --fix support out of the box. Run eslint . --fix and it will automatically fix the issues regardless of how your classes are written. as simple as className="pl-1 mr-2" or className={clsx(some && "ps-1", {"me-2": isOpen})} or even referencing a variable className={variable} (It will report where variable is declared). Have a look at this test file to see what it can fix and what it won't. If you find a case that it doesn't handle, please open an issue.

Installation

$ pnpm add -D eslint-plugin-rtl-friendly]

Requirements

  • ESLint >= V9 (Flat Config)
  • Tailwindcss V3.3.0 or higher
import rtlFriendly from "eslint-plugin-rtl-friendly";

export default [
  // ...
  rtlFriendly.configs.recommended,
  // More aggressive?
  {
    rules: {
      "rtl-friendly/no-physical-properties": "error",
    },
  },
];

Rules

Rule ID --fix support
rtl-friendly/no-physical-properties

Contributing

Welcome your contribution!

TODO:

  • Tailwindcss physical properties to logical properties
  • Add support for advanced className like cn('pl-2', {...})
  • Add support for Template Literals tw... ${...}
  • New rule: dir-attribute to enforce dir in <html> and <code> tags
  • letter-spacing doesn't work well with RTL languages to disable it in rtl languages, we should warn to disable it in rtl rtl:*** (NOT SURE) - some other cases like absolute start-0 -translate-x-1/2 - space-x-*
  • Resources