From 29e16bdd42192f09e0b8c28538abc13243527920 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 28 Aug 2026 14:52:49 -0500 Subject: [PATCH] Harden RTL language detection --- package.json | 2 +- src/lib/strings/__tests__/bidi.test.ts | 7 +++++++ src/lib/strings/bidi.ts | 15 +++++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 32b58d9840..1d87a848b7 100644 --- a/package.json +++ b/package.json @@ -340,7 +340,7 @@ "^cborg$": "/node_modules/cborg/cborg.js" }, "transformIgnorePatterns": [ - "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|nanoid|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|normalize-url|react-native-svg|@sentry/.*|sentry-expo|bcp-47-match|@atproto/.*|@bsky/sdk|tlds|multiformats|uint8arrays|@ipld/.*|cborg|await-lock)" + "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|nanoid|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|normalize-url|react-native-svg|@sentry/.*|sentry-expo|bcp-47-match|@formatjs/.*|@formatjs_generated/.*|@atproto/.*|@bsky/sdk|tlds|multiformats|uint8arrays|@ipld/.*|cborg|await-lock)" ], "modulePathIgnorePatterns": [ "__tests__/.*/__mocks__", diff --git a/src/lib/strings/__tests__/bidi.test.ts b/src/lib/strings/__tests__/bidi.test.ts index 3be7cb3b60..0e744e4cf2 100644 --- a/src/lib/strings/__tests__/bidi.test.ts +++ b/src/lib/strings/__tests__/bidi.test.ts @@ -1,3 +1,5 @@ +import '@formatjs/intl-locale/polyfill-force.js' + import {describe, expect, it, jest} from '@jest/globals' /* @@ -33,6 +35,11 @@ describe('forceLTR', () => { }) describe('isRTL', () => { + it('uses the Intl.Locale implementation forced in production', () => { + expect(Intl.Locale).toHaveProperty('polyfilled', true) + expect(Intl.Locale.prototype).toHaveProperty('getTextInfo') + }) + it('recognizes right-to-left languages and scripts', () => { expect(isRTL('he')).toBe(true) expect(isRTL('ar')).toBe(true) diff --git a/src/lib/strings/bidi.ts b/src/lib/strings/bidi.ts index 273336ab94..7a3d0061ad 100644 --- a/src/lib/strings/bidi.ts +++ b/src/lib/strings/bidi.ts @@ -2,6 +2,7 @@ import {IS_WEB} from '#/env' const LEFT_TO_RIGHT_EMBEDDING = '\u202A' const POP_DIRECTIONAL_FORMATTING = '\u202C' +const languageDirectionCache = new Map() /* * Force LTR directionality in a string. @@ -24,10 +25,16 @@ export function forceLTR(str: string) { export function isRTL(language: string | undefined) { if (!language) return false + const cached = languageDirectionCache.get(language) + if (cached !== undefined) return cached + try { - return new Intl.Locale(language).getTextInfo().direction === 'rtl' - } catch (error) { - if (error instanceof RangeError) return false - throw error + const isRightToLeft = + new Intl.Locale(language).getTextInfo().direction === 'rtl' + languageDirectionCache.set(language, isRightToLeft) + return isRightToLeft + } catch { + languageDirectionCache.set(language, false) + return false } }