Browse Source

feat(app): add debug layout direction toggle (#40393)

Luke Parker 1 week ago
parent
commit
c387fe190b

+ 3 - 1
packages/app/src/app.tsx

@@ -234,7 +234,9 @@ function ResolvedDraftRoute(props: { draft: DraftTab }) {
 function UiI18nBridge(props: ParentProps) {
 function UiI18nBridge(props: ParentProps) {
   const language = useLanguage()
   const language = useLanguage()
   return (
   return (
-    <I18nProvider value={{ locale: language.intl, t: language.t, plural: language.plural }}>
+    <I18nProvider
+      value={{ locale: language.intl, layoutLocale: language.layoutLocale, t: language.t, plural: language.plural }}
+    >
       {props.children}
       {props.children}
     </I18nProvider>
     </I18nProvider>
   )
   )

+ 34 - 15
packages/app/src/components/debug-bar.tsx

@@ -60,7 +60,7 @@ function Cell(props: {
   label: string
   label: string
   tip: string
   tip: string
   value: string
   value: string
-  wide?: boolean
+  span?: 2 | 3
 }) {
 }) {
   const content = () => (
   const content = () => (
     <div
     <div
@@ -69,7 +69,8 @@ function Cell(props: {
         "min-h-[20px] w-fit justify-start px-1.5 py-0.5 text-left": !!props.inline,
         "min-h-[20px] w-fit justify-start px-1.5 py-0.5 text-left": !!props.inline,
         "justify-center text-center": !props.inline,
         "justify-center text-center": !props.inline,
         "min-h-[42px] w-full flex-col rounded-[8px] px-0.5 py-1": !props.inline,
         "min-h-[42px] w-full flex-col rounded-[8px] px-0.5 py-1": !props.inline,
-        "col-span-2": !!props.wide && !props.inline,
+        "col-span-2": props.span === 2 && !props.inline,
+        "col-span-3": props.span === 3 && !props.inline,
       }}
       }}
     >
     >
       <div
       <div
@@ -116,11 +117,18 @@ function Cell(props: {
   )
   )
 }
 }
 
 
-function FocusCell(props: { active: boolean; inline?: boolean; onClick: () => void }) {
+function ToggleCell(props: {
+  active: boolean
+  inline?: boolean
+  label: string
+  onClick: () => void
+  tip: string
+  value: string
+}) {
   const content = () => (
   const content = () => (
     <button
     <button
       type="button"
       type="button"
-      aria-label="Force focus styles on all interactive elements"
+      aria-label={`${props.label}: ${props.value}`}
       aria-pressed={props.active}
       aria-pressed={props.active}
       classList={{
       classList={{
         "flex min-w-0 items-center font-mono uppercase hover:bg-surface-raised-base focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-border-focus": true,
         "flex min-w-0 items-center font-mono uppercase hover:bg-surface-raised-base focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-border-focus": true,
@@ -137,26 +145,22 @@ function FocusCell(props: { active: boolean; inline?: boolean; onClick: () => vo
           "flex-col items-center": !props.inline,
           "flex-col items-center": !props.inline,
         }}
         }}
       >
       >
-        <span class="text-[10px] leading-none font-black tracking-[0.04em] opacity-70">FOCUS</span>
-        <span
-          classList={{ "leading-none font-bold": true, "text-[11px]": !!props.inline, "text-[13px]": !props.inline }}
-        >
-          {props.active ? "ON" : "OFF"}
-        </span>
+        <span class="text-[10px] leading-none font-black tracking-[0.04em] opacity-70">{props.label}</span>
+        <span class="text-[11px] leading-none font-bold">{props.value}</span>
       </span>
       </span>
     </button>
     </button>
   )
   )
 
 
   if (props.inline) {
   if (props.inline) {
     return (
     return (
-      <TooltipV2 value="Force focus styles on all interactive elements" placement="top">
+      <TooltipV2 value={props.tip} placement="top">
         {content()}
         {content()}
       </TooltipV2>
       </TooltipV2>
     )
     )
   }
   }
 
 
   return (
   return (
-    <Tooltip value="Force focus styles on all interactive elements" placement="top">
+    <Tooltip value={props.tip} placement="top">
       {content()}
       {content()}
     </Tooltip>
     </Tooltip>
   )
   )
@@ -475,7 +479,7 @@ export function DebugBar(props: { inline?: boolean } = {}) {
           "gap-[9px]": !!props.inline,
           "gap-[9px]": !!props.inline,
           "gap-px": !props.inline,
           "gap-px": !props.inline,
           "flex w-full flex-nowrap items-center justify-start": !!props.inline,
           "flex w-full flex-nowrap items-center justify-start": !!props.inline,
-          "grid-cols-5": !props.inline,
+          "grid-cols-4": !props.inline,
           grid: !props.inline,
           grid: !props.inline,
         }}
         }}
       >
       >
@@ -557,10 +561,25 @@ export function DebugBar(props: { inline?: boolean } = {}) {
           bad={bad(heap(), 0.8)}
           bad={bad(heap(), 0.8)}
           dim={state.heap.used === undefined}
           dim={state.heap.used === undefined}
           inline={props.inline}
           inline={props.inline}
-          wide={!platform.setForceFocus}
+          span={platform.setForceFocus ? 2 : 3}
+        />
+        <ToggleCell
+          active={language.direction() === "rtl"}
+          inline={props.inline}
+          label={language.t("debugBar.direction.label")}
+          tip={language.t("debugBar.direction.tip")}
+          value={language.t(`debugBar.direction.${language.direction()}`)}
+          onClick={() => language.setDirection(language.direction() === "rtl" ? "ltr" : "rtl")}
         />
         />
         {platform.setForceFocus && (
         {platform.setForceFocus && (
-          <FocusCell active={state.focus} inline={props.inline} onClick={() => void toggleFocus()} />
+          <ToggleCell
+            active={state.focus}
+            inline={props.inline}
+            label={language.t("debugBar.focus.label")}
+            tip={language.t("debugBar.focus.tip")}
+            value={language.t(state.focus ? "debugBar.focus.on" : "debugBar.focus.off")}
+            onClick={() => void toggleFocus()}
+          />
         )}
         )}
       </div>
       </div>
     </aside>
     </aside>

+ 18 - 1
packages/app/src/context/language.tsx

@@ -15,9 +15,14 @@ import {
 } from "@/i18n/desktop-native"
 } from "@/i18n/desktop-native"
 
 
 export type Locale = DesktopNativeLocale
 export type Locale = DesktopNativeLocale
+export type Direction = "ltr" | "rtl"
 
 
 const RTL_LOCALES: ReadonlySet<Locale> = new Set(["ar", "ur", "pa"])
 const RTL_LOCALES: ReadonlySet<Locale> = new Set(["ar", "ur", "pa"])
 
 
+function localeDirection(locale: Locale): Direction {
+  return RTL_LOCALES.has(locale) ? "rtl" : "ltr"
+}
+
 type RawDictionary = typeof en & typeof uiEn
 type RawDictionary = typeof en & typeof uiEn
 type Dictionary = i18n.Flatten<RawDictionary>
 type Dictionary = i18n.Flatten<RawDictionary>
 type PluralKey =
 type PluralKey =
@@ -242,6 +247,13 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
 
 
     const locale = createMemo<Locale>(() => normalizeLocale(store.locale))
     const locale = createMemo<Locale>(() => normalizeLocale(store.locale))
     const intl = createMemo(() => INTL[locale()])
     const intl = createMemo(() => INTL[locale()])
+    const [layout, setLayout] = createStore({ direction: undefined as Direction | undefined })
+    const direction = createMemo(() => layout.direction ?? localeDirection(locale()))
+    const layoutLocale = createMemo(() => {
+      if (!layout.direction) return intl()
+      // Kobalte derives menu direction from locale rather than accepting a direction override.
+      return layout.direction === "rtl" ? "ar" : "en"
+    })
 
 
     const [dict] = createResource(locale, loadDict, {
     const [dict] = createResource(locale, loadDict, {
       initialValue: dicts.get(initial) ?? base,
       initialValue: dicts.get(initial) ?? base,
@@ -270,7 +282,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       if (typeof document !== "object") return
       if (typeof document !== "object") return
       const value = locale()
       const value = locale()
       document.documentElement.lang = value
       document.documentElement.lang = value
-      document.documentElement.dir = RTL_LOCALES.has(value) ? "rtl" : "ltr"
+      document.documentElement.dir = direction()
       document.cookie = cookie(value)
       document.cookie = cookie(value)
     })
     })
 
 
@@ -287,6 +299,8 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       ready,
       ready,
       locale,
       locale,
       intl,
       intl,
+      direction,
+      layoutLocale,
       locales: LOCALES,
       locales: LOCALES,
       label,
       label,
       t,
       t,
@@ -294,6 +308,9 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       setLocale(next: Locale) {
       setLocale(next: Locale) {
         setStore("locale", normalizeLocale(next))
         setStore("locale", normalizeLocale(next))
       },
       },
+      setDirection(next: Direction) {
+        setLayout("direction", next === localeDirection(locale()) ? undefined : next)
+      },
     }
     }
   },
   },
 })
 })

+ 8 - 0
packages/app/src/i18n/ar.ts

@@ -1124,6 +1124,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "كومة JS المستخدمة مقابل حد الكومة. Chromium فقط.",
   "debugBar.mem.tipUnavailable": "كومة JS المستخدمة مقابل حد الكومة. Chromium فقط.",
   "debugBar.mem.tip": "كومة JS المستخدمة مقابل حد الكومة. {{used}} من {{limit}}.",
   "debugBar.mem.tip": "كومة JS المستخدمة مقابل حد الكومة. {{used}} من {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "فرض أنماط التركيز على جميع العناصر التفاعلية",
+  "debugBar.focus.on": "تشغيل",
+  "debugBar.focus.off": "إيقاف",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "فرض اتجاه التخطيط على التطبيق بالكامل دون تغيير اللغة المحددة",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/az.ts

@@ -896,6 +896,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "İstifadə olunmuş JS yığını və yığın limiti. Yalnız Chromium.",
   "debugBar.mem.tipUnavailable": "İstifadə olunmuş JS yığını və yığın limiti. Yalnız Chromium.",
   "debugBar.mem.tip": "İstifadə olunmuş JS yığını və yığın limiti. {{used}} və {{limit}}.",
   "debugBar.mem.tip": "İstifadə olunmuş JS yığını və yığın limiti. {{used}} və {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Bütün interaktiv elementlərə fokus üslublarını məcburi tətbiq et",
+  "debugBar.focus.on": "AÇIQ",
+  "debugBar.focus.off": "QAPALI",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Seçilmiş dili dəyişmədən bütün tətbiq üçün tərtibat istiqamətini məcburi təyin et",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
   "settings.section.desktop": "Masaüstü",
   "settings.section.desktop": "Masaüstü",
   "settings.section.server": "Server",
   "settings.section.server": "Server",

+ 8 - 0
packages/app/src/i18n/br.ts

@@ -1134,6 +1134,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Heap JS usado vs limite de heap. Apenas Chromium.",
   "debugBar.mem.tipUnavailable": "Heap JS usado vs limite de heap. Apenas Chromium.",
   "debugBar.mem.tip": "Heap JS usado vs limite de heap. {{used}} de {{limit}}.",
   "debugBar.mem.tip": "Heap JS usado vs limite de heap. {{used}} de {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Forçar estilos de foco em todos os elementos interativos",
+  "debugBar.focus.on": "LIGADO",
+  "debugBar.focus.off": "DESLIGADO",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Forçar a direção do layout de todo o aplicativo sem alterar o idioma selecionado",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/bs.ts

@@ -1210,6 +1210,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Korišteni JS heap naspram limita heapa. Samo Chromium.",
   "debugBar.mem.tipUnavailable": "Korišteni JS heap naspram limita heapa. Samo Chromium.",
   "debugBar.mem.tip": "Korišteni JS heap naspram limita heapa. {{used}} od {{limit}}.",
   "debugBar.mem.tip": "Korišteni JS heap naspram limita heapa. {{used}} od {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Prisilno primijeni stilove fokusa na sve interaktivne elemente",
+  "debugBar.focus.on": "UKLJUČENO",
+  "debugBar.focus.off": "ISKLJUČENO",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Prisilno postavi smjer rasporeda cijele aplikacije bez promjene odabranog jezika",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/da.ts

@@ -1084,6 +1084,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Brugt JS-heap sammenholdt med heapgrænsen. Kun Chromium.",
   "debugBar.mem.tipUnavailable": "Brugt JS-heap sammenholdt med heapgrænsen. Kun Chromium.",
   "debugBar.mem.tip": "Brugt JS-heap sammenholdt med heapgrænsen. {{used}} af {{limit}}.",
   "debugBar.mem.tip": "Brugt JS-heap sammenholdt med heapgrænsen. {{used}} af {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Gennemtving fokusstile for alle interaktive elementer",
+  "debugBar.focus.on": "TIL",
+  "debugBar.focus.off": "FRA",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Gennemtving layoutretningen for hele appen uden at ændre det valgte sprog",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/de.ts

@@ -1029,6 +1029,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Belegter JS-Heap im Verhältnis zum Heap-Limit. Nur Chromium.",
   "debugBar.mem.tipUnavailable": "Belegter JS-Heap im Verhältnis zum Heap-Limit. Nur Chromium.",
   "debugBar.mem.tip": "Belegter JS-Heap im Verhältnis zum Heap-Limit. {{used}} von {{limit}}.",
   "debugBar.mem.tip": "Belegter JS-Heap im Verhältnis zum Heap-Limit. {{used}} von {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Fokusstile für alle interaktiven Elemente erzwingen",
+  "debugBar.focus.on": "EIN",
+  "debugBar.focus.off": "AUS",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Layoutrichtung für die gesamte App erzwingen, ohne die ausgewählte Sprache zu ändern",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Strg",
   "common.key.ctrl": "Strg",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Umschalt",
   "common.key.shift": "Umschalt",

+ 8 - 0
packages/app/src/i18n/en.ts

@@ -868,6 +868,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Used JS heap vs heap limit. Chromium only.",
   "debugBar.mem.tipUnavailable": "Used JS heap vs heap limit. Chromium only.",
   "debugBar.mem.tip": "Used JS heap vs heap limit. {{used}} of {{limit}}.",
   "debugBar.mem.tip": "Used JS heap vs heap limit. {{used}} of {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Force focus styles on all interactive elements",
+  "debugBar.focus.on": "ON",
+  "debugBar.focus.off": "OFF",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Force the full app layout direction without changing the selected language",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
 
 
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
 
 

+ 8 - 0
packages/app/src/i18n/es.ts

@@ -1218,6 +1218,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Heap JS usado vs límite de heap. Solo Chromium.",
   "debugBar.mem.tipUnavailable": "Heap JS usado vs límite de heap. Solo Chromium.",
   "debugBar.mem.tip": "Heap JS usado vs límite de heap. {{used}} de {{limit}}.",
   "debugBar.mem.tip": "Heap JS usado vs límite de heap. {{used}} de {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Forzar los estilos de foco en todos los elementos interactivos",
+  "debugBar.focus.on": "SÍ",
+  "debugBar.focus.off": "NO",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Forzar la dirección del diseño de toda la aplicación sin cambiar el idioma seleccionado",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Mayús",
   "common.key.shift": "Mayús",

+ 8 - 0
packages/app/src/i18n/fi.ts

@@ -786,6 +786,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Käytetty JS-keko suhteessa keon rajaan. Vain Chromium.",
   "debugBar.mem.tipUnavailable": "Käytetty JS-keko suhteessa keon rajaan. Vain Chromium.",
   "debugBar.mem.tip": "Käytetty JS-keko suhteessa keon rajaan. {{used}} / {{limit}}.",
   "debugBar.mem.tip": "Käytetty JS-keko suhteessa keon rajaan. {{used}} / {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Pakota kohdistustyylit kaikkiin vuorovaikutteisiin elementteihin",
+  "debugBar.focus.on": "KÄYTÖSSÄ",
+  "debugBar.focus.off": "EI KÄYTÖSSÄ",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Pakota koko sovelluksen asettelun suunta valittua kieltä muuttamatta",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
   "settings.section.desktop": "Työpöytä",
   "settings.section.desktop": "Työpöytä",
   "settings.section.server": "Palvelin",
   "settings.section.server": "Palvelin",

+ 8 - 0
packages/app/src/i18n/fr.ts

@@ -1145,6 +1145,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Tas JS utilisé vs limite de tas. Chromium uniquement.",
   "debugBar.mem.tipUnavailable": "Tas JS utilisé vs limite de tas. Chromium uniquement.",
   "debugBar.mem.tip": "Tas JS utilisé vs limite de tas. {{used}} sur {{limit}}.",
   "debugBar.mem.tip": "Tas JS utilisé vs limite de tas. {{used}} sur {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Forcer les styles de focus sur tous les éléments interactifs",
+  "debugBar.focus.on": "ACTIVÉ",
+  "debugBar.focus.off": "DÉSACTIVÉ",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Forcer le sens de disposition de toute l’application sans changer la langue sélectionnée",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Maj",
   "common.key.shift": "Maj",

+ 8 - 0
packages/app/src/i18n/hi.ts

@@ -895,6 +895,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "उपयोग किया गया JS हीप बनाम हीप सीमा। केवल Chromium।",
   "debugBar.mem.tipUnavailable": "उपयोग किया गया JS हीप बनाम हीप सीमा। केवल Chromium।",
   "debugBar.mem.tip": "उपयोग किया गया JS हीप बनाम हीप सीमा। {{limit}} में से {{used}}।",
   "debugBar.mem.tip": "उपयोग किया गया JS हीप बनाम हीप सीमा। {{limit}} में से {{used}}।",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "सभी इंटरैक्टिव एलिमेंट पर फ़ोकस स्टाइल ज़बरदस्ती लागू करें",
+  "debugBar.focus.on": "चालू",
+  "debugBar.focus.off": "बंद",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "चुनी गई भाषा बदले बिना पूरे ऐप की लेआउट दिशा ज़बरदस्ती सेट करें",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
   "settings.section.desktop": "डेस्कटॉप",
   "settings.section.desktop": "डेस्कटॉप",
   "settings.section.server": "सर्वर",
   "settings.section.server": "सर्वर",

+ 8 - 0
packages/app/src/i18n/id.ts

@@ -963,6 +963,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Heap JS terpakai vs batas heap. Khusus Chromium.",
   "debugBar.mem.tipUnavailable": "Heap JS terpakai vs batas heap. Khusus Chromium.",
   "debugBar.mem.tip": "Heap JS terpakai vs batas heap. {{used}} dari {{limit}}.",
   "debugBar.mem.tip": "Heap JS terpakai vs batas heap. {{used}} dari {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Paksa gaya fokus pada semua elemen interaktif",
+  "debugBar.focus.on": "AKTIF",
+  "debugBar.focus.off": "NONAKTIF",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Paksa arah tata letak seluruh aplikasi tanpa mengubah bahasa yang dipilih",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
 
 
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
 
 

+ 8 - 0
packages/app/src/i18n/it.ts

@@ -807,6 +807,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Heap JS utilizzato rispetto al limite dell'heap. Solo Chromium.",
   "debugBar.mem.tipUnavailable": "Heap JS utilizzato rispetto al limite dell'heap. Solo Chromium.",
   "debugBar.mem.tip": "Heap JS utilizzato rispetto al limite dell'heap. {{used}} di {{limit}}.",
   "debugBar.mem.tip": "Heap JS utilizzato rispetto al limite dell'heap. {{used}} di {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Forza gli stili di focus su tutti gli elementi interattivi",
+  "debugBar.focus.on": "ON",
+  "debugBar.focus.off": "OFF",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Forza la direzione del layout dell’intera app senza cambiare la lingua selezionata",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
   "settings.section.desktop": "Desktop",
   "settings.section.desktop": "Desktop",
   "settings.section.server": "Server",
   "settings.section.server": "Server",

+ 8 - 0
packages/app/src/i18n/ja.ts

@@ -1116,6 +1116,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "使用中の JS ヒープ対ヒープ制限。Chromium のみ。",
   "debugBar.mem.tipUnavailable": "使用中の JS ヒープ対ヒープ制限。Chromium のみ。",
   "debugBar.mem.tip": "使用中の JS ヒープ対ヒープ制限。{{limit}} 中 {{used}}。",
   "debugBar.mem.tip": "使用中の JS ヒープ対ヒープ制限。{{limit}} 中 {{used}}。",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "すべてのインタラクティブ要素にフォーカススタイルを強制適用",
+  "debugBar.focus.on": "オン",
+  "debugBar.focus.off": "オフ",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "選択中の言語を変更せずに、アプリ全体のレイアウト方向を強制設定",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/ko.ts

@@ -840,6 +840,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "사용된 JS 힙 대 힙 제한. Chromium 전용.",
   "debugBar.mem.tipUnavailable": "사용된 JS 힙 대 힙 제한. Chromium 전용.",
   "debugBar.mem.tip": "사용된 JS 힙 대 힙 제한. {{limit}} 중 {{used}}.",
   "debugBar.mem.tip": "사용된 JS 힙 대 힙 제한. {{limit}} 중 {{used}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "모든 대화형 요소에 포커스 스타일 강제 적용",
+  "debugBar.focus.on": "켬",
+  "debugBar.focus.off": "끔",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "선택한 언어를 변경하지 않고 전체 앱의 레이아웃 방향 강제 설정",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/nl.ts

@@ -896,6 +896,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Gebruikte JS-heap versus heap-limiet. Alleen Chromium.",
   "debugBar.mem.tipUnavailable": "Gebruikte JS-heap versus heap-limiet. Alleen Chromium.",
   "debugBar.mem.tip": "Gebruikte JS-heap versus heaplimiet. {{used}} van {{limit}}.",
   "debugBar.mem.tip": "Gebruikte JS-heap versus heaplimiet. {{used}} van {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Dwing focusstijlen af voor alle interactieve elementen",
+  "debugBar.focus.on": "AAN",
+  "debugBar.focus.off": "UIT",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Dwing de lay-outrichting voor de hele app af zonder de geselecteerde taal te wijzigen",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
   "settings.section.desktop": "Desktop",
   "settings.section.desktop": "Desktop",
   "settings.section.server": "Server",
   "settings.section.server": "Server",

+ 8 - 0
packages/app/src/i18n/no.ts

@@ -1037,6 +1037,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Brukt JS-heap kontra heap-grense. Kun Chromium.",
   "debugBar.mem.tipUnavailable": "Brukt JS-heap kontra heap-grense. Kun Chromium.",
   "debugBar.mem.tip": "Brukt JS-heap kontra heap-grense. {{used}} av {{limit}}.",
   "debugBar.mem.tip": "Brukt JS-heap kontra heap-grense. {{used}} av {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Tving fokusstiler på alle interaktive elementer",
+  "debugBar.focus.on": "PÅ",
+  "debugBar.focus.off": "AV",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Tving layoutretningen for hele appen uten å endre valgt språk",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/pa.ts

@@ -893,6 +893,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "جے ایس ہیپ بمقابلہ ہیپ لیمٹ استعمال کیتا گیا۔ صرف Chromium۔",
   "debugBar.mem.tipUnavailable": "جے ایس ہیپ بمقابلہ ہیپ لیمٹ استعمال کیتا گیا۔ صرف Chromium۔",
   "debugBar.mem.tip": "ورتیا JS ہیپ تے ہیپ دی حد۔ {{limit}} وچوں {{used}}۔",
   "debugBar.mem.tip": "ورتیا JS ہیپ تے ہیپ دی حد۔ {{limit}} وچوں {{used}}۔",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "سارے انٹرایکٹو عناصر اُتے فوکس سٹائل لازمی لاگو کرو",
+  "debugBar.focus.on": "چالو",
+  "debugBar.focus.off": "بند",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "چُنی ہوئی زبان نوں بدلے بغیر پوری ایپ دے لے آؤٹ دی سمت لازمی مقرر کرو",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode ڈیسک ٹاپ",
   "app.name.desktop": "OpenCode ڈیسک ٹاپ",
   "settings.section.desktop": "ڈیسک ٹاپ",
   "settings.section.desktop": "ڈیسک ٹاپ",
   "settings.section.server": "سرور",
   "settings.section.server": "سرور",

+ 8 - 0
packages/app/src/i18n/pl.ts

@@ -1137,6 +1137,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Wykorzystana pamięć sterty JS względem jej limitu. Tylko Chromium.",
   "debugBar.mem.tipUnavailable": "Wykorzystana pamięć sterty JS względem jej limitu. Tylko Chromium.",
   "debugBar.mem.tip": "Wykorzystana pamięć sterty JS względem jej limitu: {{used}} z {{limit}}.",
   "debugBar.mem.tip": "Wykorzystana pamięć sterty JS względem jej limitu: {{used}} z {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Wymuś style fokusu dla wszystkich elementów interaktywnych",
+  "debugBar.focus.on": "WŁ.",
+  "debugBar.focus.off": "WYŁ.",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Wymuś kierunek układu całej aplikacji bez zmiany wybranego języka",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/ru.ts

@@ -1215,6 +1215,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Используемая куча JS по сравнению с лимитом кучи. Только Chromium.",
   "debugBar.mem.tipUnavailable": "Используемая куча JS по сравнению с лимитом кучи. Только Chromium.",
   "debugBar.mem.tip": "Используемая куча JS по сравнению с лимитом кучи. {{used}} из {{limit}}.",
   "debugBar.mem.tip": "Используемая куча JS по сравнению с лимитом кучи. {{used}} из {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Принудительно применить стили фокуса ко всем интерактивным элементам",
+  "debugBar.focus.on": "ВКЛ.",
+  "debugBar.focus.off": "ВЫКЛ.",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Принудительно задать направление макета всего приложения, не меняя выбранный язык",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/sv.ts

@@ -892,6 +892,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Använd JS-heap jämfört med heapgränsen. Endast Chromium.",
   "debugBar.mem.tipUnavailable": "Använd JS-heap jämfört med heapgränsen. Endast Chromium.",
   "debugBar.mem.tip": "Använd JS-heap jämfört med heapgränsen. {{used}} av {{limit}}.",
   "debugBar.mem.tip": "Använd JS-heap jämfört med heapgränsen. {{used}} av {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Tvinga fram fokusstilar på alla interaktiva element",
+  "debugBar.focus.on": "PÅ",
+  "debugBar.focus.off": "AV",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Tvinga fram hela appens layoutriktning utan att ändra valt språk",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
   "settings.section.desktop": "Skrivbord",
   "settings.section.desktop": "Skrivbord",
   "settings.section.server": "Server",
   "settings.section.server": "Server",

+ 8 - 0
packages/app/src/i18n/th.ts

@@ -1191,6 +1191,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "JS heap ที่ใช้เทียบกับขีดจำกัด heap เฉพาะ Chromium",
   "debugBar.mem.tipUnavailable": "JS heap ที่ใช้เทียบกับขีดจำกัด heap เฉพาะ Chromium",
   "debugBar.mem.tip": "JS heap ที่ใช้เทียบกับขีดจำกัด heap {{used}} จาก {{limit}}",
   "debugBar.mem.tip": "JS heap ที่ใช้เทียบกับขีดจำกัด heap {{used}} จาก {{limit}}",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "บังคับใช้สไตล์โฟกัสกับองค์ประกอบแบบโต้ตอบทั้งหมด",
+  "debugBar.focus.on": "เปิด",
+  "debugBar.focus.off": "ปิด",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "บังคับกำหนดทิศทางเค้าโครงของทั้งแอปโดยไม่เปลี่ยนภาษาที่เลือก",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/tr.ts

@@ -1213,6 +1213,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Kullanılan JS yığınının yığın sınırına oranı. Yalnızca Chromium.",
   "debugBar.mem.tipUnavailable": "Kullanılan JS yığınının yığın sınırına oranı. Yalnızca Chromium.",
   "debugBar.mem.tip": "Kullanılan JS yığınının yığın sınırına oranı: {{used}} / {{limit}}.",
   "debugBar.mem.tip": "Kullanılan JS yığınının yığın sınırına oranı: {{used}} / {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Tüm etkileşimli öğelere odak stillerini zorla uygula",
+  "debugBar.focus.on": "AÇIK",
+  "debugBar.focus.off": "KAPALI",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Seçili dili değiştirmeden tüm uygulamanın düzen yönünü zorla ayarla",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/uk.ts

@@ -977,6 +977,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Використана купа JS проти ліміту купи. Тільки Chromium.",
   "debugBar.mem.tipUnavailable": "Використана купа JS проти ліміту купи. Тільки Chromium.",
   "debugBar.mem.tip": "Використана купа JS проти ліміту купи. {{used}} з {{limit}}.",
   "debugBar.mem.tip": "Використана купа JS проти ліміту купи. {{used}} з {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Примусово застосувати стилі фокуса до всіх інтерактивних елементів",
+  "debugBar.focus.on": "УВІМК.",
+  "debugBar.focus.off": "ВИМК.",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Примусово задати напрямок макета всієї програми, не змінюючи вибрану мову",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
 
 
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
 
 

+ 8 - 0
packages/app/src/i18n/ur.ts

@@ -896,6 +896,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "استعمال شدہ JS ہیپ بمقابلہ ہیپ کی حد۔ صرف Chromium۔",
   "debugBar.mem.tipUnavailable": "استعمال شدہ JS ہیپ بمقابلہ ہیپ کی حد۔ صرف Chromium۔",
   "debugBar.mem.tip": "استعمال شدہ JS ہیپ بمقابلہ ہیپ کی حد۔ {{limit}} میں سے {{used}}۔",
   "debugBar.mem.tip": "استعمال شدہ JS ہیپ بمقابلہ ہیپ کی حد۔ {{limit}} میں سے {{used}}۔",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "تمام تفاعلی عناصر پر فوکس اسٹائلز لازماً لاگو کریں",
+  "debugBar.focus.on": "چالو",
+  "debugBar.focus.off": "بند",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "منتخب زبان تبدیل کیے بغیر پوری ایپ کے لے آؤٹ کی سمت لازماً مقرر کریں",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode ڈیسک ٹاپ",
   "app.name.desktop": "OpenCode ڈیسک ٹاپ",
   "settings.section.desktop": "ڈیسک ٹاپ",
   "settings.section.desktop": "ڈیسک ٹاپ",
   "settings.section.server": "سرور",
   "settings.section.server": "سرور",

+ 8 - 0
packages/app/src/i18n/vi.ts

@@ -898,6 +898,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "Bộ nhớ heap JS đã dùng so với giới hạn heap. Chỉ hỗ trợ Chromium.",
   "debugBar.mem.tipUnavailable": "Bộ nhớ heap JS đã dùng so với giới hạn heap. Chỉ hỗ trợ Chromium.",
   "debugBar.mem.tip": "Đã sử dụng JS heap so với giới hạn heap. {{used}} của {{limit}}.",
   "debugBar.mem.tip": "Đã sử dụng JS heap so với giới hạn heap. {{used}} của {{limit}}.",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "Buộc áp dụng kiểu tiêu điểm cho mọi phần tử tương tác",
+  "debugBar.focus.on": "BẬT",
+  "debugBar.focus.off": "TẮT",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "Buộc đặt hướng bố cục cho toàn bộ ứng dụng mà không thay đổi ngôn ngữ đã chọn",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "app.name.desktop": "OpenCode Desktop",
   "app.name.desktop": "OpenCode Desktop",
   "settings.section.desktop": "Desktop",
   "settings.section.desktop": "Desktop",
   "settings.section.server": "Máy chủ",
   "settings.section.server": "Máy chủ",

+ 8 - 0
packages/app/src/i18n/zh.ts

@@ -1180,6 +1180,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "使用的 JS 堆与堆限制。仅限 Chromium。",
   "debugBar.mem.tipUnavailable": "使用的 JS 堆与堆限制。仅限 Chromium。",
   "debugBar.mem.tip": "使用的 JS 堆与堆限制。{{used}} / {{limit}}。",
   "debugBar.mem.tip": "使用的 JS 堆与堆限制。{{used}} / {{limit}}。",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "强制所有交互元素显示焦点样式",
+  "debugBar.focus.on": "开",
+  "debugBar.focus.off": "关",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "在不更改所选语言的情况下,强制设置整个应用的布局方向",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 8 - 0
packages/app/src/i18n/zht.ts

@@ -1176,6 +1176,14 @@ export const dict = {
   "debugBar.mem.label": "MEM",
   "debugBar.mem.label": "MEM",
   "debugBar.mem.tipUnavailable": "使用的 JS 堆積與堆積限制。僅限 Chromium。",
   "debugBar.mem.tipUnavailable": "使用的 JS 堆積與堆積限制。僅限 Chromium。",
   "debugBar.mem.tip": "使用的 JS 堆積與堆積限制。{{used}} / {{limit}}。",
   "debugBar.mem.tip": "使用的 JS 堆積與堆積限制。{{used}} / {{limit}}。",
+  "debugBar.focus.label": "FOCUS",
+  "debugBar.focus.tip": "強制所有互動元素顯示焦點樣式",
+  "debugBar.focus.on": "開啟",
+  "debugBar.focus.off": "關閉",
+  "debugBar.direction.label": "DIR",
+  "debugBar.direction.tip": "在不變更所選語言的情況下,強制設定整個應用程式的版面配置方向",
+  "debugBar.direction.ltr": "LTR",
+  "debugBar.direction.rtl": "RTL",
   "common.key.ctrl": "Ctrl",
   "common.key.ctrl": "Ctrl",
   "common.key.alt": "Alt",
   "common.key.alt": "Alt",
   "common.key.shift": "Shift",
   "common.key.shift": "Shift",

+ 2 - 1
packages/ui/src/context/i18n.tsx

@@ -18,6 +18,7 @@ export type UiI18nParams = Record<string, string | number | boolean>
 
 
 export type UiI18n = {
 export type UiI18n = {
   locale: Accessor<string>
   locale: Accessor<string>
+  layoutLocale?: Accessor<string>
   t: (key: UiI18nKey, params?: UiI18nParams) => string
   t: (key: UiI18nKey, params?: UiI18nParams) => string
   plural: (key: UiI18nPluralKey, count: number, params?: UiI18nParams) => string
   plural: (key: UiI18nPluralKey, count: number, params?: UiI18nParams) => string
 }
 }
@@ -60,7 +61,7 @@ const Context = createContext<UiI18n>(fallback)
 
 
 function UiI18nProvider(props: ParentProps<{ value: UiI18n }>) {
 function UiI18nProvider(props: ParentProps<{ value: UiI18n }>) {
   return (
   return (
-    <I18nProvider locale={props.value.locale()}>
+    <I18nProvider locale={(props.value.layoutLocale ?? props.value.locale)()}>
       <Context.Provider value={props.value}>{props.children}</Context.Provider>
       <Context.Provider value={props.value}>{props.children}</Context.Provider>
     </I18nProvider>
     </I18nProvider>
   )
   )