feat(i18n): Hebrew locale + full RTL support (#243)
# feat(i18n): Hebrew locale + full RTL support ## Summary This PR adds full Hebrew (he) locale support to the UI, including a complete translation of all user-facing strings and comprehensive RTL layout support across all components. ## What was done ### Hebrew translation - Full translation of all i18n message files for the `he` locale (17 translation files) - Registered the language in the i18n system and the language picker ### RTL support - Automatic direction detection (`dir="rtl"`) when Hebrew is selected - Replaced physical CSS properties (`left`/`right`) with logical equivalents (`inline-start`/`inline-end`) across the project - Fixed resize direction, file path alignment, and textarea padding - Fixed navigation button positioning in textarea for RTL - Fixed scrollbar direction in RTL - Fixed code block direction and selector alignment - Fixed Monaco editor direction in the file viewer - Auto-detect text direction in reasoning block (`dir="auto"` + `unicode-bidi: plaintext`) ### Adapted components - `session-layout` — sidebar and resize handle - `prompt-input` — text direction and buttons - `message-base` — message blocks and reasoning - `message-timeline` — timeline bar - `right-panel` — right side panel - `tool-call` — tool call display - `settings-screen` — settings page - `selector` — selection component - `instance-shell` — main shell ## New files ``` packages/ui/src/lib/i18n/messages/he/ advancedSettings.ts app.ts commands.ts dialogs.ts filesystem.ts folderSelection.ts index.ts instance.ts loadingScreen.ts logs.ts markdown.ts messaging.ts remoteAccess.ts session.ts settings.ts time.ts toolCall.ts ``` ## Suggested testing - Switch language to Hebrew and verify all strings are translated - Verify RTL layout is correct across all screens (session, settings, file viewer) - Verify that English text inside a reasoning block is displayed LTR - Switch back to English and verify everything returns to LTR --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Shantur Rathore <i@shantur.com>
This commit is contained in:
@@ -7,10 +7,11 @@ type Messages = Record<string, string>
|
||||
|
||||
export type TranslateParams = Record<string, unknown>
|
||||
|
||||
export type Locale = "en" | "es" | "fr" | "ru" | "ja" | "zh-Hans"
|
||||
export type Locale = "en" | "es" | "fr" | "ru" | "ja" | "zh-Hans" | "he"
|
||||
|
||||
const SUPPORTED_LOCALES: readonly Locale[] = ["en", "es", "fr", "ru", "ja", "zh-Hans"] as const
|
||||
const SUPPORTED_LOCALES: readonly Locale[] = ["en", "es", "fr", "ru", "ja", "zh-Hans", "he"] as const
|
||||
const SUPPORTED_LOCALES_BY_LOWER = new Map(SUPPORTED_LOCALES.map((locale) => [locale.toLowerCase(), locale]))
|
||||
const RTL_LOCALES = new Set<Locale>(["he"])
|
||||
|
||||
const localeMessagesCache = new Map<Locale, Messages>([["en", enMessages]])
|
||||
const localeMessagesPromises = new Map<Locale, Promise<Messages>>()
|
||||
@@ -22,6 +23,11 @@ const localeLoaders: Record<Locale, () => Promise<Messages>> = {
|
||||
ru: async () => (await import("./messages/ru")).ruMessages,
|
||||
ja: async () => (await import("./messages/ja")).jaMessages,
|
||||
"zh-Hans": async () => (await import("./messages/zh-Hans")).zhHansMessages,
|
||||
he: async () => (await import("./messages/he")).heMessages,
|
||||
}
|
||||
|
||||
function getLocaleDirection(locale: Locale): "ltr" | "rtl" {
|
||||
return RTL_LOCALES.has(locale) ? "rtl" : "ltr"
|
||||
}
|
||||
|
||||
function normalizeLocaleTag(value: string): string {
|
||||
@@ -149,6 +155,8 @@ export const I18nProvider: ParentComponent = (props) => {
|
||||
const [resolvedLocale, setResolvedLocale] = createSignal<Locale>(globalLocale)
|
||||
const previousGlobalMessages = globalMessages
|
||||
const previousGlobalLocale = globalLocale
|
||||
const previousDocumentLanguage = typeof document !== "undefined" ? document.documentElement.lang : ""
|
||||
const previousDocumentDirection = typeof document !== "undefined" ? document.documentElement.dir : ""
|
||||
|
||||
onMount(() => {
|
||||
const detected = detectNavigatorLocale()
|
||||
@@ -195,10 +203,21 @@ export const I18nProvider: ParentComponent = (props) => {
|
||||
})
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (typeof document === "undefined") return
|
||||
const activeLocale = locale()
|
||||
document.documentElement.dir = getLocaleDirection(activeLocale)
|
||||
document.documentElement.lang = activeLocale
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
globalMessages = previousGlobalMessages
|
||||
globalLocale = previousGlobalLocale
|
||||
setGlobalRevision((value) => value + 1)
|
||||
if (typeof document !== "undefined") {
|
||||
document.documentElement.lang = previousDocumentLanguage
|
||||
document.documentElement.dir = previousDocumentDirection
|
||||
}
|
||||
})
|
||||
|
||||
const value: I18nContextValue = {
|
||||
|
||||
Reference in New Issue
Block a user