<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Idan Levi</title>
    <description>The latest articles on DEV Community by Idan Levi (@idanlevi1).</description>
    <link>https://dev.to/idanlevi1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3848374%2Fb2682f34-712f-48d2-91ae-32af2c55d826.png</url>
      <title>DEV Community: Idan Levi</title>
      <link>https://dev.to/idanlevi1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/idanlevi1"/>
    <language>en</language>
    <item>
      <title>Stop Asking Users for Contact Permissions in React Native</title>
      <dc:creator>Idan Levi</dc:creator>
      <pubDate>Mon, 27 Apr 2026 12:57:27 +0000</pubDate>
      <link>https://dev.to/idanlevi1/stop-asking-users-for-contact-permissions-in-react-native-3j1l</link>
      <guid>https://dev.to/idanlevi1/stop-asking-users-for-contact-permissions-in-react-native-3j1l</guid>
      <description>&lt;p&gt;You know that moment when your app asks for contact permissions and the user just... closes the dialog? Yeah. Me too.&lt;/p&gt;

&lt;p&gt;Most contact picker libraries for React Native require &lt;code&gt;READ_CONTACTS&lt;/code&gt; — which means your app gets access to the &lt;strong&gt;entire&lt;/strong&gt; address book. Every name, every number, every email. All of it.&lt;/p&gt;

&lt;p&gt;Your app just needed one phone number.&lt;/p&gt;

&lt;h2&gt;
  
  
  There's a better way
&lt;/h2&gt;

&lt;p&gt;Both iOS and Android have &lt;strong&gt;native contact pickers&lt;/strong&gt; that let the user choose a single contact — and your app only sees what they picked. No permission dialog. No full address book access. The OS handles everything.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/idanlevi1/react-native-pick-contact" rel="noopener noreferrer"&gt;react-native-pick-contact&lt;/a&gt; to make this dead simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pickContact&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-pick-contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pickContact&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// { name: "John Appleseed", phone: "+1 (555) 012-3456" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No permissions to configure. No &lt;code&gt;Info.plist&lt;/code&gt; keys. No &lt;code&gt;AndroidManifest.xml&lt;/code&gt; entries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traditional library                    react-native-pick-contact
─────────────────────                  ─────────────────────────
1. User taps "Pick Contact"            1. User taps "Pick Contact"
2. 🔒 Permission dialog appears        2. 📱 Native picker opens instantly
3. 😬 User hesitates / denies          3. User picks a contact
4. ...or grants full address book       4. ✅ App receives name + phone
5. App reads entire contact list
6. App finds the one contact

Permissions: READ_CONTACTS             Permissions: NONE
Data exposed: EVERYTHING               Data exposed: 1 contact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;iOS&lt;/strong&gt; uses &lt;code&gt;CNContactPickerViewController&lt;/code&gt; — an out-of-process system UI. Your app never touches the Contacts database. The picker runs in a separate process, and only the selected contact's data is passed back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Android&lt;/strong&gt; uses &lt;code&gt;ActivityResultContracts.PickContact()&lt;/code&gt;. The system picker grants a temporary URI permission scoped to the single selected contact. Once you read it, the permission expires.&lt;/p&gt;

&lt;p&gt;In both cases, the OS is the gatekeeper — not your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for the New Architecture
&lt;/h2&gt;

&lt;p&gt;If you've upgraded to React Native 0.76+, you've probably noticed that some older contact libraries just... broke. The legacy Bridge is gone, and libraries that didn't migrate to TurboModules stopped working.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;react-native-pick-contact&lt;/code&gt; is built from scratch for the New Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TurboModule native module with Codegen type-safe specs&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;NativeModules["..."]&lt;/code&gt; hacks&lt;/li&gt;
&lt;li&gt;No manual bridging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install it. Run pod install. Done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-native-pick-contact
&lt;span class="nb"&gt;cd &lt;/span&gt;ios &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pod &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No other setup needed. Seriously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Users trust your app more&lt;/strong&gt; — no scary permission dialogs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Store reviewers don't flag you&lt;/strong&gt; — no justification needed for contact access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less data = less liability&lt;/strong&gt; — you never see contacts the user didn't pick&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better conversion&lt;/strong&gt; — users who deny permissions can't use the feature. Zero permissions = zero drop-off.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/idanlevi1/react-native-pick-contact" rel="noopener noreferrer"&gt;github.com/idanlevi1/react-native-pick-contact&lt;/a&gt;&lt;br&gt;
NPM: &lt;a href="https://www.npmjs.com/package/react-native-pick-contact" rel="noopener noreferrer"&gt;npmjs.com/package/react-native-pick-contact&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find it useful, a ⭐ on GitHub helps other developers find it too.&lt;/p&gt;

&lt;p&gt;Got questions or ideas? &lt;a href="https://github.com/idanlevi1/react-native-pick-contact/issues" rel="noopener noreferrer"&gt;Open an issue&lt;/a&gt; — happy to chat.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>contact</category>
      <category>privacy</category>
      <category>permission</category>
    </item>
    <item>
      <title>AI Coding Agents Are Great, but They Suck at RTL. Here's How I Fixed It</title>
      <dc:creator>Idan Levi</dc:creator>
      <pubDate>Sat, 28 Mar 2026 23:40:05 +0000</pubDate>
      <link>https://dev.to/idanlevi1/ai-coding-agents-are-great-but-they-suck-at-rtl-heres-how-i-fixed-it-2g0g</link>
      <guid>https://dev.to/idanlevi1/ai-coding-agents-are-great-but-they-suck-at-rtl-heres-how-i-fixed-it-2g0g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g3ybjhl9sjrd1vqt0au.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g3ybjhl9sjrd1vqt0au.png" alt="RTLify CLI demo showing the init and check commands in a terminal" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I build products for Hebrew-speaking users. Every time I ask an AI to generate a component, I get the same broken output — &lt;code&gt;margin-left&lt;/code&gt; instead of &lt;code&gt;margin-inline-start&lt;/code&gt;, &lt;code&gt;ml-4&lt;/code&gt; instead of &lt;code&gt;ms-4&lt;/code&gt;, arrows pointing the wrong way, and order numbers jumping around inside RTL sentences.&lt;/p&gt;

&lt;p&gt;I fix it. I ask for the next component. Same bugs. The AI doesn't learn — it's trained on LTR codebases and has zero awareness that RTL exists.&lt;/p&gt;

&lt;p&gt;After months of manually fixing the same 5-6 patterns in every single component, I built &lt;strong&gt;&lt;a href="https://github.com/idanlevi1/rtlify" rel="noopener noreferrer"&gt;RTLify&lt;/a&gt;&lt;/strong&gt; — a CLI that teaches your AI editor the rules once, so you never fix them again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx rtlify-ai init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command. Works with Claude Code, Cursor, Windsurf, Cline, GitHub Copilot, Gemini CLI, and Codex CLI.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bugs AI Keeps Making
&lt;/h2&gt;

&lt;p&gt;Let me show you exactly what goes wrong. If you've built anything in Hebrew, Arabic, Persian, or Urdu — you'll recognize every single one.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Physical CSS Instead of Logical
&lt;/h3&gt;

&lt;p&gt;AI writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In RTL, "left" is "right". The layout mirrors, but these properties don't. The correct version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;margin-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;padding-inline-end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;border-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inset-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logical properties work in both LTR and RTL. AI never uses them unless you tell it to.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Wrong Tailwind Classes
&lt;/h3&gt;

&lt;p&gt;This is the most common one. AI writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ml-4 pr-6 text-left border-l-2 rounded-tl-lg"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every single class here is wrong for RTL. The correct version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ms-4 pe-6 text-start border-s-2 rounded-ss-lg"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RTLify includes a full mapping table with 20+ conversions — &lt;code&gt;ml&lt;/code&gt; → &lt;code&gt;ms&lt;/code&gt;, &lt;code&gt;pr&lt;/code&gt; → &lt;code&gt;pe&lt;/code&gt;, &lt;code&gt;text-left&lt;/code&gt; → &lt;code&gt;text-start&lt;/code&gt;, &lt;code&gt;float-right&lt;/code&gt; → &lt;code&gt;float-end&lt;/code&gt;, &lt;code&gt;rounded-tl&lt;/code&gt; → &lt;code&gt;rounded-ss&lt;/code&gt;, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Numbers Jumping Around (Bidi Text)
&lt;/h3&gt;

&lt;p&gt;This is the sneaky one. Look at this Hebrew sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;ההזמנה שלך #12345 אושרה בהצלחה&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks fine in your code editor. Open it in a browser with &lt;code&gt;dir="rtl"&lt;/code&gt; — the number &lt;code&gt;#12345&lt;/code&gt; visually jumps to a completely wrong position. The sentence becomes unreadable.&lt;/p&gt;

&lt;p&gt;The fix is a &lt;code&gt;&amp;lt;bdi&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;ההזמנה שלך &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;bdi&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;#12345&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;bdi&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; אושרה בהצלחה&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same applies to phone numbers, dates, English brand names — any LTR content inside an RTL sentence needs &lt;code&gt;&amp;lt;bdi&amp;gt;&lt;/code&gt; wrapping. AI never does this.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Icons Pointing the Wrong Way
&lt;/h3&gt;

&lt;p&gt;In an RTL interface, a "next" arrow should point left, not right. But AI always generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChevronRight&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-5 h-5"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChevronRight&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-5 h-5 rtl:-scale-x-100"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Non-directional icons (home, settings, search) should NOT be flipped. AI doesn't know the difference — RTLify teaches it which ones to flip.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Currency and Dates
&lt;/h3&gt;

&lt;p&gt;AI formats prices like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`₪&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Wrong — symbol on wrong side&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;he-IL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;currency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ILS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → "42.90 ₪" — symbol on correct side, RTL mark included&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same for dates — AI defaults to &lt;code&gt;MM/DD/YYYY&lt;/code&gt; instead of &lt;code&gt;DD/MM/YYYY&lt;/code&gt; with &lt;code&gt;Intl.DateTimeFormat&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. React Native Gets It Even Worse
&lt;/h3&gt;

&lt;p&gt;On mobile, the same problems exist but with different APIs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AI writes:&lt;/span&gt;
&lt;span class="nx"&gt;paddingLeft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Should be:&lt;/span&gt;
&lt;span class="nx"&gt;paddingStart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;writingDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rtl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus you need &lt;code&gt;I18nManager.isRTL&lt;/code&gt; for conditional checks and &lt;code&gt;transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }]&lt;/code&gt; for icon flipping.&lt;/p&gt;




&lt;h2&gt;
  
  
  How RTLify Works
&lt;/h2&gt;

&lt;p&gt;There's no magic and no runtime dependency. Here's what happens when you run &lt;code&gt;npx rtlify-ai init&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It writes &lt;code&gt;.rtlify-rules.md&lt;/code&gt;&lt;/strong&gt; — a markdown file containing 8 RTL architecture rules with concrete "do this / not that" code examples. This is the full ruleset the AI will follow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It adds a 3-line pointer&lt;/strong&gt; to your editor's config file (&lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;.cursorrules&lt;/code&gt;, &lt;code&gt;.windsurfrules&lt;/code&gt;, etc.) that tells the AI: "read &lt;code&gt;.rtlify-rules.md&lt;/code&gt; before generating any UI code."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For Claude Code users&lt;/strong&gt; — it installs a global &lt;code&gt;/rtlify&lt;/code&gt; command. Just type &lt;code&gt;/rtlify&lt;/code&gt; in Claude Code and it scans, fixes, and verifies your entire project.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. The AI reads the rules on every conversation. No extra prompting needed.&lt;/p&gt;

&lt;p&gt;You can open &lt;code&gt;.rtlify-rules.md&lt;/code&gt; and read exactly what the AI sees. Full transparency — it's just a markdown file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Linter
&lt;/h3&gt;

&lt;p&gt;RTLify also ships with a scanner that catches violations in existing code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx rtlify-ai check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  src/components/Sidebar.tsx
       3 Tailwind Physical
         Use logical classes (ms-*, me-*, ps-*, pe-*)
         &amp;lt;div className="ml-4 pl-6 text-left"&amp;gt;

  src/components/OrderCard.tsx
       8 Tailwind Physical
         Use text-start / text-end
         &amp;lt;h2 className="text-left text-xl"&amp;gt;פרטי הזמנה&amp;lt;/h2&amp;gt;

  2 violations across 2 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exits with code 1 — plug it into your CI pipeline to catch RTL violations on every PR.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix Command (Cursor, Windsurf, Cline, Copilot)
&lt;/h3&gt;

&lt;p&gt;Claude Code users get the &lt;code&gt;/rtlify&lt;/code&gt; slash command. For every other editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx rtlify-ai fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It generates a ready-to-paste prompt and copies it to your clipboard. Paste it into Cursor, Windsurf, Cline, or any AI editor — it tells the AI to scan, fix, and verify all RTL violations.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 8 Rules
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;What the AI Learns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Logical CSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;margin-inline-start&lt;/code&gt; not &lt;code&gt;margin-left&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tailwind Mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20+ class conversions (&lt;code&gt;ml-*&lt;/code&gt; → &lt;code&gt;ms-*&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Icon Flipping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rtl:-scale-x-100&lt;/code&gt; on directional icons only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;BDI Safety&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;bdi&amp;gt;&lt;/code&gt; tags for numbers/English in RTL text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Localized Formats&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Intl.NumberFormat('he-IL')&lt;/code&gt; for currency &amp;amp; dates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Safe i18n&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Respects your i18n setup or lack of it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Complex Components&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Carousels, charts, sliders with &lt;code&gt;dir="rtl"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;React Native&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;I18nManager.isRTL&lt;/code&gt;, &lt;code&gt;paddingStart&lt;/code&gt;, &lt;code&gt;writingDirection&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx rtlify-ai init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask your AI to "build a checkout form in Hebrew" or "create a settings screen in Arabic". You'll see the difference immediately.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/idanlevi1/rtlify" rel="noopener noreferrer"&gt;github.com/idanlevi1/rtlify&lt;/a&gt;&lt;br&gt;
NPM: &lt;a href="https://www.npmjs.com/package/rtlify-ai" rel="noopener noreferrer"&gt;npmjs.com/package/rtlify-ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're building for RTL markets — what's the most annoying RTL bug an AI has generated for you? I'd love to hear in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rtl</category>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
