Harden RTL language detection

This commit is contained in:
Eric Bailey
2026-08-28 14:52:49 -05:00
parent 5bde4cddb2
commit 29e16bdd42
3 changed files with 19 additions and 5 deletions
+1 -1
View File
@@ -340,7 +340,7 @@
"^cborg$": "<rootDir>/node_modules/cborg/cborg.js" "^cborg$": "<rootDir>/node_modules/cborg/cborg.js"
}, },
"transformIgnorePatterns": [ "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": [ "modulePathIgnorePatterns": [
"__tests__/.*/__mocks__", "__tests__/.*/__mocks__",
+7
View File
@@ -1,3 +1,5 @@
import '@formatjs/intl-locale/polyfill-force.js'
import {describe, expect, it, jest} from '@jest/globals' import {describe, expect, it, jest} from '@jest/globals'
/* /*
@@ -33,6 +35,11 @@ describe('forceLTR', () => {
}) })
describe('isRTL', () => { 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', () => { it('recognizes right-to-left languages and scripts', () => {
expect(isRTL('he')).toBe(true) expect(isRTL('he')).toBe(true)
expect(isRTL('ar')).toBe(true) expect(isRTL('ar')).toBe(true)
+11 -4
View File
@@ -2,6 +2,7 @@ import {IS_WEB} from '#/env'
const LEFT_TO_RIGHT_EMBEDDING = '\u202A' const LEFT_TO_RIGHT_EMBEDDING = '\u202A'
const POP_DIRECTIONAL_FORMATTING = '\u202C' const POP_DIRECTIONAL_FORMATTING = '\u202C'
const languageDirectionCache = new Map<string, boolean>()
/* /*
* Force LTR directionality in a string. * Force LTR directionality in a string.
@@ -24,10 +25,16 @@ export function forceLTR(str: string) {
export function isRTL(language: string | undefined) { export function isRTL(language: string | undefined) {
if (!language) return false if (!language) return false
const cached = languageDirectionCache.get(language)
if (cached !== undefined) return cached
try { try {
return new Intl.Locale(language).getTextInfo().direction === 'rtl' const isRightToLeft =
} catch (error) { new Intl.Locale(language).getTextInfo().direction === 'rtl'
if (error instanceof RangeError) return false languageDirectionCache.set(language, isRightToLeft)
throw error return isRightToLeft
} catch {
languageDirectionCache.set(language, false)
return false
} }
} }