add Arabic (RTL) language support #58
Merged
overtrue merged 10 commits intorustfs:mainfrom Feb 21, 2026
Merged
Conversation
- Add Arabic (ar-MA) locale and RTL_LOCALES set to i18n config - Register العربية in language switcher - Wrap app in DirectionProvider driven by i18n language changes - Set document.documentElement.dir on every language switch - Flip components.json rtl flag to true for future shadcn installs
- Read direction from DirectionProvider via useDirection() - Set side="right" when dir is rtl so sidebar appears at inline-start - Override hardcoded text-left in shadcn variants with text-start! - Replace physical border-l on sub-menu with logical border-s
Convert all direction-sensitive utility classes to their logical property counterparts so layouts mirror correctly in RTL without needing explicit rtl: overrides: - ml-*/mr-* → ms-*/me-* - pl-*/pr-* → ps-*/pe-* - border-r → border-e - left-*/right-* (absolute) → start-*/end-* - ml-auto → ms-auto Add rtl:-scale-x-100 to directional arrow icons (prev/next page, visit-website link) so they flip to point the correct way in RTL.
- text-left → text-start on TableHead so headers align to inline-start (right in RTL, left in LTR) instead of always forcing left - [&:has([role=checkbox])]:pr-0 → pe-0 on TableHead and TableCell: in RTL the checkbox column flips to the right edge; removing the physical right padding was cutting into the outer gutter instead of closing the gap with the adjacent column
Add rtl:-scale-x-100 to the Switch root so the track flips horizontally in RTL: thumb starts at inline-start (right) when unchecked and slides to inline-end (left) when checked, matching native toggle behaviour for RTL locales.
- Input padding offsets pr-1.5/pl-1.5 → pe-1.5/ps-1.5 so the input gains breathing room on the correct side when an addon is present - Addon inline-start padding pl-2 → ps-2, negative margins ml → ms - Addon inline-end padding pr-2 → pe-2, negative margins mr → me Ensures icon/button addons sit at the correct edge in RTL without needing any changes at the call site.
The previous rtl:-scale-x-100 generated no CSS because negative scale utilities require arbitrary-value syntax. Also, inheriting dir="rtl" reversed the flex container so the thumb's translate-x values no longer matched its physical travel distance. Fix: pin the root to dir="ltr" so the internal thumb animation always works as designed, then apply rtl:scale-x-[-1] (arbitrary value, guaranteed to compile) to mirror the entire element visually in RTL — thumb starts on the right (inline-start) when unchecked and slides left (inline-end) when checked.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Description
Adds full right-to-left (RTL) layout support to the console with Arabic (
ar-MA) as thefirst RTL locale. Switching to العربية mirrors the entire UI — sidebar, spacing, icons,
table headers, and interactive controls — without any manual
rtl:overrides scatteredacross pages.
Type of Change
Changes
Arabic locale
i18n/locales/ar-MA.json— full Arabic translations for all UI stringslib/i18n.ts— addedar → ar-MAmapping and exportedRTL_LOCALESsetcomponents/language-switcher.tsx— registered العربيةRTL direction infrastructure (
components/providers/i18n-provider.tsx)DirectionProviderso every primitive (DropdownMenu,Dialog, Select, Popover, Tooltip, etc.) receives direction via React context
document.documentElement.dirand the React directionstate update atomically, keeping the DOM attribute and component context in sync
components.json→"rtl": truefor future shadcn CLI installsSidebar (
components/app-sidebar.tsx)useDirection()drivesside="right"in RTL — sidebar appears at inline-starttext-leftwithtext-start!on allmenu and sub-menu buttons, without modifying
sidebar.tsxborder-l→border-s(logical inline-start)Logical CSS properties — 15 custom component files
Replaced all physical direction utilities with logical equivalents:
ml-*/mr-*ms-*/me-*pl-*/pr-*ps-*/pe-*border-rborder-eleft-*/right-*(absolute)start-*/end-*ml-automs-autoDirectional arrow icons (
RiArrowLeftSLine,RiArrowRightSLine,RiArrowRightLongFill)received
rtl:-scale-x-100to point the correct way in RTL.Switch (
components/ui/switch.tsx)dir="ltr"on the root pins the internal flex layout so the thumb'stranslate-xanimation always travels the correct distance regardless of inherited page direction
rtl:scale-x-[-1](arbitrary-value syntax, guaranteed to compile) mirrors the trackvisually — thumb starts at inline-start (right) when unchecked, slides to inline-end
(left) when checked
Table (
components/ui/table.tsx)text-left→text-startonTableHeadso column headers align to inline-start[&:has([role=checkbox])]:pr-0→pe-0onTableHeadandTableCell— in RTL thecheckbox column flips to the right edge;
pe-0removes the gap toward the adjacentcolumn in both directions
InputGroup (
components/ui/input-group.tsx)pr-1.5/pl-1.5→pe-1.5/ps-1.5pl-2/pr-2→ps-2/pe-2ml-[...]/mr-[...]→ms-[...]/me-[...]Testing
Switch to العربية:
localStorage→ RTL applied immediately after hydrationChecklist
Related Issues
Closes #57