i18n: add Persian (fa) as RTL and per-bubble bidi isolation for chat …#815
i18n: add Persian (fa) as RTL and per-bubble bidi isolation for chat …#815esbtc wants to merge 1 commit into
Conversation
Greptile SummaryThis PR adds Persian RTL support and per-message bidi handling for chat. The main changes are:
Confidence Score: 4/5The changed locale and stylesheet paths need fixes before merging.
src/shared/i18n/config.ts, src/shared/i18n/types.ts, src/renderer/src/assets/main.css, src/renderer/src/screens/Chat/MessageRow.tsx Important Files Changed
Reviews (1): Last reviewed commit: "i18n: add Persian (fa) as RTL and per-bu..." | Re-trigger Greptile |
| "tr", | ||
| "zh-CN", | ||
| "zh-TW", | ||
| "fa", |
There was a problem hiding this comment.
| color: var(--text-muted); | ||
| margin: 0; | ||
| } | ||
|
|
||
| /* ======================================================================== |
There was a problem hiding this comment.
main.css is the renderer stylesheet, but this hunk replaces most of it with a placeholder comment. Existing screens still emit deleted classes like layout, sidebar, button, setup, and install styles, and the theme picker still exposes theme ids whose CSS blocks were removed, so normal app screens can render unstyled or with missing theme variables.
| const bubbleTime = isTimeValid ? formatBubbleTime(epochMs) : null; | ||
|
|
||
| // Determine per-bubble direction to handle mixed LTR/RTL content gracefully. | ||
| const bubbleDir = looksRtl(bubbleContent ?? "") ? "rtl" : "ltr"; |
There was a problem hiding this comment.
looksRtl makes the whole bubble RTL when any RTL character appears anywhere in the message. An LTR message like Can you translate "שלום"? or an agent markdown reply with one Persian word after a code block will inherit dir="rtl" and textAlign: "right" across links, lists, tables, and code, causing readable LTR content to reorder or right-align incorrectly.
Description This PR fixes Persian (fa) rendering and mixed LTR/RTL chat bubble layout by making two small, focused changes:
Add "fa" to APP_LOCALES and RTL_LOCALES so the app sets document.dir = "rtl" when Persian is the active locale.
Improve per-message bidi handling in the chat UI:
Detect strong RTL characters (Hebrew/Arabic/Persian) in each chat bubble.
Set dir="rtl" / dir="ltr" per bubble, apply unicode-bidi: isolate and align text accordingly so mixed Persian/English messages keep correct visual order.
Why
Persian was not treated as an RTL locale, so switching the app to Persian did not mirror the UI.
Even when the app locale is LTR, individual messages containing Persian could be visually mangled (mixed-direction text displays incorrectly). This change fixes both the global and per-bubble cases.
Files changed
src/shared/i18n/config.ts
Add "fa" to APP_LOCALES and RTL_LOCALES.
src/renderer/src/screens/Chat/MessageRow.tsx
Add looksRtl(text) helper, set per-bubble dir and apply unicode-bidi isolation + textAlign style.
src/renderer/src/assets/main.css
Ensure .chat-bubble uses unicode-bidi: isolate and add dir-based alignment rules.
src/shared/i18n/index.test.ts
Add a unit test asserting getLocaleDirection("fa") === "rtl".
Testing / How to verify
Unit tests
Checkout branch: git fetch origin && git checkout fix/rtl-farsi
Install deps and run tests: npm install npm test
Expect the new i18n test to pass.
Dev smoke test (renderer / chat)
Start dev app: npm run dev
In the app (or in DevTools Console), set the locale to Persian: localStorage.setItem('hermes-locale','fa'); location.reload();
Verify:
document.documentElement.dir === 'rtl'
The UI (sidebar, header, etc.) mirrors appropriately.
Send a mixed message like: "سلام, this is a test" or "لینک: https://example.com/ برای تست"
Verify:
The chat bubble for Persian text has dir="rtl".
The bubble is right-aligned and inline English/URLs remain readable and in logical order.
Alternate check (if you cannot run full app)
Open DevTools for renderer, run the small test injector (previously provided) to apply per-bubble dir and observe behavior.
Notes on backwards compatibility
Non-RTL locales are unaffected.
The change is limited and opt-in: adding "fa" simply classifies Persian as RTL; per-bubble handling only sets dir on bubbles based on their content.
A unit test was added to prevent regression.
Implementation choices & possible follow-ups
unicode-bidi: isolate was chosen to isolate directionality of each bubble from neighboring UI. If we want to experiment, unicode-bidi: plaintext is an alternative that uses the first-strong-character heuristic; either is viable and can be adjusted if reviewers prefer.
Fonts: the project already includes Cairo scoped for Arabic-range glyphs; if Persian-specific glyph or shaping issues appear we can add/adjust a Persian-friendly font.
If maintainers prefer server-side or i18n-lib-driven detection, we can move the detection logic into a shared helper.
PR checklist (suggested)
Adds "fa" to APP_LOCALES and RTL_LOCALES
Adds per-bubble bidi handling and isolation
Adds unit test for getLocaleDirection("fa")
Manual smoke-tested in dev (instructions above)
How I tested locally
Added the changes on branch fix/rtl-farsi (esbtc fork), executed unit tests for i18n, and validated per-bubble dir behavior via DevTools injection in the renderer.