diff --git a/build/check-bundle-size.ts b/build/check-bundle-size.ts index f8de21e2..ab39bb3c 100644 --- a/build/check-bundle-size.ts +++ b/build/check-bundle-size.ts @@ -146,7 +146,14 @@ const BUDGET_BYTES: Record = { // scripts/sync-supported-devices.mjs), plus the hw.* and // nav.hardwareTest strings in all ten locale tables. Measured aggregate // is 1,748.7 kB, leaving ~31 kB of headroom. - ".js": 1_780_000, + // Raised to 1,845 kB for the Arabic (ar) interface locale: i18n-ar.ts is a + // full native translation of all ~1,050 keys and ships as its own lazy + // chunk (i18n-ar-*.js, ~69 kB, loaded only when Arabic is selected) — the + // initial (English) bundle is untouched. This is the same lazy-chunk pattern + // as the pt/es/fr/de/zh/ja/ko/ru/vi locales above, and the aggregate counts + // it only because the check sums every emitted chunk. Measured aggregate is + // 1,817.7 kB, leaving ~27 kB of headroom. + ".js": 1_845_000, }; const ASSETS = join("dist", "assets"); diff --git a/src/app/App.tsx b/src/app/App.tsx index 752cca6a..cf685cee 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -67,12 +67,18 @@ export function App(): ReactNode { ko: "ko", ru: "ru", vi: "vi", + ar: "ar", en: "en", }; const nextLang = htmlLang[locale] ?? "en"; if (document.documentElement.lang !== nextLang) { document.documentElement.lang = nextLang; } + // Arabic reads right-to-left; every other locale is left-to-right. + const nextDir = locale === "ar" ? "rtl" : "ltr"; + if (document.documentElement.dir !== nextDir) { + document.documentElement.dir = nextDir; + } } catch { /* non-DOM environment (tests) */ } diff --git a/src/control.css b/src/control.css index 522e021a..48fb7354 100644 --- a/src/control.css +++ b/src/control.css @@ -239,9 +239,9 @@ button:focus-visible, a:focus-visible, input:focus-visible { outline: 2px solid var(--surface-bg); } /* A thin turquoise-to-pink line tops the sidebar, like a synth readout. */ -.control-shell[data-interface-theme="miku"] .sidebar { border-right-color: color-mix(in srgb, var(--ui-accent) 26%, var(--border)); } +.control-shell[data-interface-theme="miku"] .sidebar { border-inline-end-color: color-mix(in srgb, var(--ui-accent) 26%, var(--border)); } .control-shell[data-interface-theme="miku"] .sidebar::before { - position: absolute; top: 0; right: 0; left: 0; height: 2px; + position: absolute; top: 0; inset-inline-end: 0; inset-inline-start: 0; height: 2px; background: linear-gradient(90deg, transparent, var(--ui-accent) 32%, var(--ui-accent) 68%, var(--miku-pink)); content: ""; opacity: .85; pointer-events: none; } @@ -612,7 +612,7 @@ button:focus-visible, a:focus-visible, input:focus-visible { outline: 2px solid .control-shell.sidebar-hidden { grid-template-columns: minmax(0, 1fr); } .control-shell.sidebar-hidden .sidebar { display: none; } -.sidebar { position: sticky; top: 0; display: flex; height: 100vh; flex-direction: column; padding: 1.5rem; border-right: 1px solid var(--line); background: var(--sidebar); } +.sidebar { position: sticky; top: 0; display: flex; height: 100vh; flex-direction: column; padding: 1.5rem; border-inline-end: 1px solid var(--line); background: var(--sidebar); } .control-panel { position: relative; width: min(100% - 4rem, 1180px); margin: 0 auto; padding: 1.5rem 0 2rem; overflow-y: auto; scrollbar-width: none; } .control-panel::-webkit-scrollbar { display: none; width: 0; height: 0; } .panel-top { position: sticky; top: 0; z-index: 5; background: var(--ink); } @@ -623,8 +623,8 @@ button:focus-visible, a:focus-visible, input:focus-visible { outline: 2px solid .demo-wordmark img { width: auto; height: 1.5rem; } .sidebar > .build-badge { margin-top: auto; } .build-badge { color: var(--text-soft); font-family: var(--font-mono); font-size: .64rem; letter-spacing: .06em; white-space: nowrap; } -.demo-wordmark .build-badge { margin-left: .15rem; padding: .12rem .45rem; border: 1px solid var(--border-soft); border-radius: 1rem; background: var(--surface-raised); font-size: .56rem; } -.brand-links { display: flex; align-items: center; gap: .7rem; margin-left: auto; } +.demo-wordmark .build-badge { margin-inline-start: .15rem; padding: .12rem .45rem; border: 1px solid var(--border-soft); border-radius: 1rem; background: var(--surface-raised); font-size: .56rem; } +.brand-links { display: flex; align-items: center; gap: .7rem; margin-inline-start: auto; } .brand-links a { display: flex; color: var(--text-soft); transition: color .18s ease; } .brand-links a:hover { color: var(--ui-accent); } .brand-links svg { width: auto; height: .85rem; } @@ -633,14 +633,14 @@ button:focus-visible, a:focus-visible, input:focus-visible { outline: 2px solid .device-panel { overflow: hidden; border: 1px solid var(--border); border-radius: 10px; background: var(--raised); } .sidebar-device-list { max-height: min(34vh, 15rem); overflow-y: auto; overscroll-behavior: contain; scrollbar-color: var(--border-hover) transparent; scrollbar-width: thin; } -.device-row { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: .7rem; width: 100%; padding: .72rem .78rem; border: 0; border-bottom: 1px solid var(--line); background: transparent; text-align: left; } +.device-row { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: .7rem; width: 100%; padding: .72rem .78rem; border: 0; border-bottom: 1px solid var(--line); background: transparent; text-align: start; } .device-row:hover { background: var(--surface-hover); } .device-row.is-selected { background: var(--ui-accent-soft); } .device-row-copy { display: block; min-width: 0; } .device-row-copy strong { display: block; overflow: hidden; color: var(--bright); font-size: .82rem; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; } .device-row-copy small { display: block; margin-top: .2rem; overflow: hidden; color: var(--muted); font-size: .68rem; text-overflow: ellipsis; white-space: nowrap; } -.device-add { display: block; width: 100%; padding: .68rem .78rem; border: 0; background: transparent; color: var(--muted); text-align: left; font-size: .78rem; font-weight: 600; } -.device-add::before { margin-right: .5rem; color: var(--ui-accent); content: "+"; } +.device-add { display: block; width: 100%; padding: .68rem .78rem; border: 0; background: transparent; color: var(--muted); text-align: start; font-size: .78rem; font-weight: 600; } +.device-add::before { margin-inline-end: .5rem; color: var(--ui-accent); content: "+"; } .device-add:hover { background: var(--surface-hover); color: var(--text-hover); } .device-add:disabled { cursor: wait; opacity: .7; } @@ -656,7 +656,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .nav-item svg { width: 1.05rem; height: 1.05rem; } .nav-item.has-label { width: auto; grid-auto-flow: column; gap: .42rem; padding: 0 .6rem; place-items: center; font-size: .74rem; } .nav-item:hover { background: var(--surface-hover); color: var(--text-hover); } -.nav-item.is-debug { margin-left: auto; } +.nav-item.is-debug { margin-inline-start: auto; } .control-shell .nav-item[aria-current="true"] { background: var(--ui-accent-soft); color: var(--ui-accent); } /* Globe language picker: globe icon opens an upward popup menu. @@ -666,7 +666,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .locale-menu { position: absolute; bottom: calc(100% + .45rem); - left: 0; + inset-inline-start: 0; z-index: 50; display: grid; gap: .15rem; @@ -687,7 +687,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } background: transparent; color: var(--dim); font-size: .74rem; - text-align: left; + text-align: start; cursor: pointer; } .locale-menu button:hover { background: var(--surface-hover); color: var(--text-hover); } @@ -770,7 +770,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .gamereq-chip { justify-self: start; padding: .4rem .85rem; border: 0; border-radius: 999px; background: var(--ui-accent-soft); color: var(--bright); font-size: .75rem; font-weight: 650; cursor: pointer; } .gamereq-chip:hover { background: var(--hover); } .gamereq-apps { display: grid; gap: .25rem; max-height: 12rem; margin: 0; padding: 0; list-style: none; overflow-y: auto; } -.gamereq-apps button { display: flex; align-items: center; gap: .6rem; width: 100%; padding: .45rem .55rem; border: 0; border-radius: 8px; background: transparent; color: inherit; text-align: left; cursor: pointer; } +.gamereq-apps button { display: flex; align-items: center; gap: .6rem; width: 100%; padding: .45rem .55rem; border: 0; border-radius: 8px; background: transparent; color: inherit; text-align: start; cursor: pointer; } .gamereq-apps button:hover { background: var(--hover); } .gamereq-apps img, .gamereq-apps svg { flex: 0 0 auto; width: 1.5rem; height: 1.5rem; object-fit: contain; } .gamereq-apps svg { padding: .15rem; box-sizing: border-box; color: var(--muted); } @@ -781,7 +781,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .gamereq-command code { flex: 1; min-width: 0; color: var(--text-input); font-size: .72rem; line-height: 1.55; overflow-wrap: anywhere; white-space: pre-wrap; } .gamereq-command button { display: grid; flex: 0 0 auto; place-items: center; width: 1.9rem; height: 1.9rem; padding: 0; border: 0; border-radius: 6px; background: var(--surface-control); color: var(--dim); cursor: pointer; } .gamereq-command button:hover { background: var(--hover); color: var(--bright); } -.gamereq-embed { display: grid; gap: .75rem; padding: .9rem 1rem; border-left: 4px solid var(--ui-accent); border-radius: 6px; background: var(--surface-control); } +.gamereq-embed { display: grid; gap: .75rem; padding: .9rem 1rem; border-inline-start: 4px solid var(--ui-accent); border-radius: 6px; background: var(--surface-control); } .gamereq-embed-title { margin: 0; color: var(--bright); font-size: .92rem; font-weight: 700; } .gamereq-embed-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .75rem 1rem; } .gamereq-embed-fields > div { display: grid; gap: .2rem; min-width: 0; } @@ -824,7 +824,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .device-status { display: flex; flex: 0 0 auto; align-items: center; gap: .45rem; color: var(--text-soft); font-size: .75rem; white-space: nowrap; } .device-status > .status-dot { width: .45rem; height: .45rem; border-radius: 50%; background: var(--success); } .live-status { display: flex; align-items: center; gap: .5rem; margin: 0 0 .8rem; padding: .55rem .75rem; border: 1px solid var(--line); border-radius: 9px; background: var(--well); color: var(--dim); font-size: .72rem; } -.live-status-share { margin-left: auto; padding: .32rem .65rem; border: 1px solid var(--border-hover); border-radius: 6px; background: var(--surface-strong); color: var(--text-soft); font-size: .68rem; font-weight: 600; cursor: pointer; } +.live-status-share { margin-inline-start: auto; padding: .32rem .65rem; border: 1px solid var(--border-hover); border-radius: 6px; background: var(--surface-strong); color: var(--text-soft); font-size: .68rem; font-weight: 600; cursor: pointer; } .live-status i { width: .38rem; height: .38rem; flex: 0 0 auto; border-radius: 50%; background: var(--success); box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 10%, transparent); } .sidebar-menu-toggle { display: grid; width: 1.9rem; height: 1.75rem; flex: 0 0 auto; grid-template-rows: repeat(3, 1px); align-content: center; gap: .24rem; padding: .42rem .45rem; border: 1px solid var(--border); border-radius: 6px; background: var(--input); cursor: pointer; } @@ -836,16 +836,16 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .is-empty .empty-state { display: block; } .control-shell.is-empty .device-data { display: none !important; } .empty-connect-action { width: max-content; margin-top: 1.5rem; padding: .7rem 1rem; border: 1px solid var(--ui-accent); border-radius: 7px; background: var(--ui-accent); color: var(--ui-accent-ink); font-size: .72rem; font-weight: 750; } -.empty-connect-action::before { margin-right: .45rem; content: "+"; } +.empty-connect-action::before { margin-inline-end: .45rem; content: "+"; } .empty-connect-action:hover { background: color-mix(in srgb, var(--ui-accent) 80%, #ffffff); border-color: color-mix(in srgb, var(--ui-accent) 80%, #ffffff); transform: translateY(-1px); } .empty-connect-action:disabled { cursor: wait; opacity: .6; transform: none; } .device-overview { display: grid; grid-template-columns: 1.45fr 1fr; min-height: 330px; overflow: hidden; scroll-margin-top: 1.5rem; border: 1px solid var(--border); border-radius: 14px; background: var(--card); } -.device-overview .device-thumbnail { display: flex; overflow: hidden; align-items: center; justify-content: center; padding: .5rem; border-right: 1px solid var(--line-soft); background: radial-gradient(circle at 50% 45%, var(--surface-hover-soft) 0, var(--surface-menu) 72%); } +.device-overview .device-thumbnail { display: flex; overflow: hidden; align-items: center; justify-content: center; padding: .5rem; border-inline-end: 1px solid var(--line-soft); background: radial-gradient(circle at 50% 45%, var(--surface-hover-soft) 0, var(--surface-menu) 72%); } .device-overview .device-thumbnail[hidden] { display: none; } .device-overview .device-thumbnail img { max-width: 100%; max-height: 100%; object-fit: contain; filter: drop-shadow(0 5px 9px rgb(0 0 0 / 60%)); } -.device-overview .summary-stat { display: flex; min-width: 0; flex-direction: column; justify-content: center; padding: 1.1rem 1.35rem; border-right: 1px solid var(--line-soft); } -.device-overview .summary-stat:last-child { border-right: 0; } +.device-overview .summary-stat { display: flex; min-width: 0; flex-direction: column; justify-content: center; padding: 1.1rem 1.35rem; border-inline-end: 1px solid var(--line-soft); } +.device-overview .summary-stat:last-child { border-inline-end: 0; } .device-overview .summary-stat > span { margin-bottom: .45rem; color: var(--text-soft); font-family: var(--font-mono); font-size: .64rem; letter-spacing: .09em; } .device-overview .summary-stat strong { font-size: 1.3rem; font-weight: 500; letter-spacing: -.025em; } .device-overview .summary-stat small { margin-top: .2rem; color: var(--faint); font-size: .68rem; } @@ -871,10 +871,10 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .setting-card button:disabled { cursor: default; opacity: 1; } .dpi-card { grid-row: span 2; } .dpi-card.has-lift-off { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); gap: 1.1rem; } -.dpi-card.has-lift-off > .dpi-card-lift { padding-left: 1.1rem; border-left: 1px solid var(--line-soft); } +.dpi-card.has-lift-off > .dpi-card-lift { padding-inline-start: 1.1rem; border-inline-start: 1px solid var(--line-soft); } @media (max-width: 980px) { .dpi-card.has-lift-off { grid-template-columns: minmax(0, 1fr); } - .dpi-card.has-lift-off > .dpi-card-lift { padding-left: 0; padding-top: .9rem; border-left: 0; border-top: 1px solid var(--line-soft); } + .dpi-card.has-lift-off > .dpi-card-lift { padding-inline-start: 0; padding-top: .9rem; border-inline-start: 0; border-top: 1px solid var(--line-soft); } } .setting-heading { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 2rem; } .setting-heading.compact { margin-bottom: .55rem; } @@ -938,9 +938,9 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .lighting-strip { display:grid;grid-template-columns:repeat(auto-fit,minmax(2.6rem,4rem));gap:.3rem;align-items:center } .lighting-strip.is-lightstrip { grid-template-columns:1.15fr repeat(8,1fr) } .lighting-strip button { position:relative;min-width:0;height:2.2rem;padding:0;border:1px solid color-mix(in srgb,var(--zone-color) 45%,var(--border));border-radius:7px;background:color-mix(in srgb,var(--zone-color) 74%,#090b0d);color:#fff;font-family:var(--font-mono);font-size:.52rem;box-shadow:inset 0 0 14px color-mix(in srgb,var(--zone-color) 50%,transparent);cursor:pointer } -.lighting-strip.is-lightstrip button:first-child { margin-right:.2rem;background:var(--surface-control);box-shadow:none;color:var(--muted) } +.lighting-strip.is-lightstrip button:first-child { margin-inline-end:.2rem;background:var(--surface-control);box-shadow:none;color:var(--muted) } .lighting-strip button:hover,.lighting-strip button.is-selected { border-color:var(--ui-accent);outline:1px solid color-mix(in srgb,var(--ui-accent) 35%,transparent);outline-offset:1px } -.lighting-strip button.is-selected::after { position:absolute;right:25%;bottom:-.48rem;left:25%;height:2px;border-radius:2px;background:var(--ui-accent);content:"" } +.lighting-strip button.is-selected::after { position:absolute;inset-inline-end:25%;bottom:-.48rem;inset-inline-start:25%;height:2px;border-radius:2px;background:var(--ui-accent);content:"" } @media (max-width:760px) { .lighting-zone-picker { grid-template-columns:1fr }.lighting-strip { overflow-x:auto }.lighting-strip.is-lightstrip { grid-template-columns:3.5rem repeat(8,minmax(2.2rem,1fr)) } } .dpi-header-actions { display: flex; align-items: center; gap: .45rem; } @@ -977,7 +977,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .angle-tuning-readonly output { color: var(--text-input); font-family: var(--font-mono); } .angle-tuning-control { margin-top: .55rem; padding-top: .55rem; border-top: 1px solid var(--line); } .angle-tuning-head { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; color: var(--dim); font-size: .7rem; } -.angle-tuning-head output { min-width: 2.8rem; color: var(--text-input); font: 650 .72rem var(--font-mono); text-align: right; } +.angle-tuning-head output { min-width: 2.8rem; color: var(--text-input); font: 650 .72rem var(--font-mono); text-align: end; } .angle-tuning-inputs { display: grid; grid-template-columns: 1.65rem minmax(7rem, 1fr) 1.65rem; align-items: center; gap: .5rem; margin-top: .35rem; } .angle-tuning-inputs button { width: 1.65rem; height: 1.65rem; padding: 0; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-strong); color: var(--text-input); font-size: .9rem; line-height: 1; cursor: pointer; } .angle-tuning-inputs button:hover:not(:disabled) { border-color: var(--ui-accent); color: var(--ui-accent); } @@ -995,7 +995,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .egg-collapsible summary::-webkit-details-marker, .egg-experimental summary::-webkit-details-marker { display: none; } .egg-collapsible summary span small { display: block; margin-bottom: .12rem; color: var(--faint); font-family: var(--font-mono); font-size: .5rem; letter-spacing: .09em; } .egg-experimental summary small { display: block; margin-bottom: .18rem; color: color-mix(in srgb, var(--warning) 82%, var(--text-soft)); font-family: var(--font-mono); font-size: .52rem; letter-spacing: .09em; } -.egg-collapsible summary i, .egg-experimental summary i { border-right: 1px solid var(--text-soft); border-bottom: 1px solid var(--text-soft); transform: rotate(45deg); transition: transform .18s ease; } +.egg-collapsible summary i, .egg-experimental summary i { border-inline-end: 1px solid var(--text-soft); border-bottom: 1px solid var(--text-soft); transform: rotate(45deg); transition: transform .18s ease; } .egg-collapsible summary i { width: .42rem; height: .42rem; } .egg-experimental summary i { width: .45rem; height: .45rem; } .egg-collapsible[open] summary i, .egg-experimental[open] summary i { transform: rotate(225deg); } @@ -1113,7 +1113,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } flex: 0 0 auto; align-items: center; gap: 0.5rem; - margin-left: auto; + margin-inline-start: auto; } .apply-bar-actions button { display: inline-flex; @@ -1216,7 +1216,7 @@ nav { display: grid; gap: .3rem; margin-top: 1.4rem; } .news-banner a.news-banner-link:hover { text-decoration: underline; } .news-banner-dismiss { flex: 0 0 auto; - margin-left: auto; + margin-inline-start: auto; padding: 0.1rem 0.4rem; border: 0; border-radius: 6px; @@ -1447,7 +1447,7 @@ body { height: 100vh; overflow: hidden; } .sidebar-battery { display: flex; align-items: center; gap: .35rem; } .sidebar-battery::before { color: var(--ghost); content: "·"; } .sidebar-battery .battery-icon { width: 1.45rem; } -#sidebar-battery-value { min-width: 2.4em; color: var(--text-strong); text-align: left; font-variant-numeric: tabular-nums; } +#sidebar-battery-value { min-width: 2.4em; color: var(--text-strong); text-align: start; font-variant-numeric: tabular-nums; } .control-shell:not(.is-empty) .sidebar-product-status .status-dot { background: var(--ui-accent); box-shadow: 0 0 0 3px var(--ui-accent-soft); } .device-label { margin: auto .35rem .5rem; padding-top: .4rem; } @@ -1544,9 +1544,9 @@ body { height: 100vh; overflow: hidden; } .workspace-tabs button[aria-selected="true"] { color: var(--text-hover); } .workspace-tabs button[aria-selected="true"]::after { position: absolute; - right: .9rem; + inset-inline-end: .9rem; bottom: .12rem; - left: .9rem; + inset-inline-start: .9rem; height: 1px; background: var(--text-hover); content: ""; @@ -1604,7 +1604,7 @@ body { height: 100vh; overflow: hidden; } color: var(--text-hover); font: 500 2.2rem/1 var(--font-sans); letter-spacing: -.04em; - text-align: right; + text-align: end; } .dpi-header-actions button { border-radius: 5px; background: transparent; } .segmented { gap: 0; } @@ -1667,7 +1667,7 @@ body { height: 100vh; overflow: hidden; } .toast-stack { position: fixed; top: 1rem; - right: 1rem; + inset-inline-end: 1rem; z-index: 200; display: flex; width: min(24rem, calc(100vw - 2rem)); @@ -1839,7 +1839,7 @@ body { height: 100vh; overflow: hidden; } } .app-sidebar-brand-version { flex-shrink: 0; - margin-left: auto; + margin-inline-start: auto; white-space: nowrap; color: var(--faint); font-size: 0.78rem; @@ -1883,8 +1883,8 @@ body { height: 100vh; overflow: hidden; } } .app-sidebar-nav-item + .app-sidebar-nav-item { border-top: 1px solid var(--line-strong); - border-top-left-radius: 0; - border-top-right-radius: 0; + border-start-start-radius: 0; + border-start-end-radius: 0; } .app-sidebar-nav-item > .app-sidebar-nav-icon { flex-shrink: 0; @@ -1896,7 +1896,7 @@ body { height: 100vh; overflow: hidden; } flex-shrink: 0; width: 0.85rem; height: 0.85rem; - margin-left: auto; + margin-inline-start: auto; color: var(--ghost); opacity: 0.9; transition: color 0.15s ease, transform 0.15s ease; @@ -1931,7 +1931,7 @@ body { height: 100vh; overflow: hidden; } .app-sidebar-collapse-toggle { position: absolute; top: 50%; - right: -0.875rem; + inset-inline-end: -0.875rem; transform: translateY(-50%); z-index: 30; display: grid; @@ -2094,8 +2094,8 @@ body { height: 100vh; overflow: hidden; } width: 12.5rem; min-width: 0; } -.device-diagram-rail--left { text-align: left; } -.device-diagram-rail--right { text-align: right; } +.device-diagram-rail--left { text-align: start; } +.device-diagram-rail--right { text-align: end; } .device-diagram-canvas { position: relative; flex: 0 0 auto; @@ -2159,13 +2159,13 @@ body { height: 100vh; overflow: hidden; } white-space: nowrap; } .device-diagram-chip--left { - left: auto; - right: 0.4rem; + inset-inline-start: auto; + inset-inline-end: 0.4rem; animation: device-diagram-chip-in-l 0.32s ease both; } .device-diagram-chip--right { - left: 0.4rem; - right: auto; + inset-inline-start: 0.4rem; + inset-inline-end: auto; animation: device-diagram-chip-in-r 0.32s ease both; } .device-diagram-chip-label { @@ -2353,7 +2353,7 @@ body { height: 100vh; overflow: hidden; } background: none; color: inherit; font-family: inherit; - text-align: left; + text-align: start; cursor: pointer; } .games-tile-art { @@ -2383,7 +2383,7 @@ body { height: 100vh; overflow: hidden; } .games-tile-badge { position: absolute; top: 0.5rem; - right: 0.5rem; + inset-inline-end: 0.5rem; padding: 0.15rem 0.5rem; border-radius: 999px; font-size: 0.68rem; @@ -2695,7 +2695,7 @@ body { height: 100vh; overflow: hidden; } .game-profile-toggle-thumb { position: absolute; top: 2px; - left: 2px; + inset-inline-start: 2px; width: 18px; height: 18px; border-radius: 50%; @@ -2802,14 +2802,14 @@ body { height: 100vh; overflow: hidden; } .welcome-aurora i:nth-child(1) { width: 420px; height: 420px; - left: 12%; + inset-inline-start: 12%; top: 8%; background: color-mix(in srgb, var(--ui-accent, #5dde89) 34%, transparent); } .welcome-aurora i:nth-child(2) { width: 380px; height: 380px; - right: 8%; + inset-inline-end: 8%; bottom: 4%; background: color-mix(in srgb, var(--ui-accent, #5dde89) 22%, transparent); animation-delay: -5s; @@ -2817,7 +2817,7 @@ body { height: 100vh; overflow: hidden; } .welcome-aurora i:nth-child(3) { width: 300px; height: 300px; - left: 42%; + inset-inline-start: 42%; bottom: -10%; background: color-mix(in srgb, var(--bright) 14%, transparent); animation-delay: -9s; @@ -3098,7 +3098,7 @@ body { height: 100vh; overflow: hidden; } font-size: 0.88rem; font-weight: 650; position: absolute; - left: 0; + inset-inline-start: 0; transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease; top: 50%; transform: translateY(-50%); @@ -3113,7 +3113,7 @@ body { height: 100vh; overflow: hidden; } as the primary Call-To-Action in accent so the tab still stands out. */ .device-tab-profiles-nav { position: absolute; - right: 0; + inset-inline-end: 0; top: 50%; transform: translateY(-50%); } @@ -3427,7 +3427,7 @@ body { height: 100vh; overflow: hidden; } position: absolute; width: 14px; height: 14px; - left: 2px; + inset-inline-start: 2px; top: 2px; border-radius: 50%; background: var(--muted); @@ -3450,7 +3450,7 @@ body { height: 100vh; overflow: hidden; } padding: 0.1rem 0.45rem; border-radius: 999px; border: 1px solid var(--line); - margin-left: 0.5rem; + margin-inline-start: 0.5rem; color: var(--dim); } .theme-custom-editor { margin-top: 0.75rem; display: flex; flex-direction: column; gap: 0.6rem; } @@ -3518,7 +3518,7 @@ body { height: 100vh; overflow: hidden; } /* OpenMouse AI chat overlay + launcher button. */ .ai-launcher { position: absolute; - right: 1.25rem; + inset-inline-end: 1.25rem; bottom: 1.5rem; z-index: 60; display: inline-flex; @@ -3544,7 +3544,7 @@ body { height: 100vh; overflow: hidden; } .ai-overlay { position: absolute; - right: 1.25rem; + inset-inline-end: 1.25rem; bottom: 5.25rem; z-index: 60; display: flex; @@ -3631,13 +3631,13 @@ body { height: 100vh; overflow: hidden; } align-self: flex-start; background: var(--control); border: 1px solid var(--line); - border-bottom-left-radius: 4px; + border-end-start-radius: 4px; } .ai-message.is-user { align-self: flex-end; background: var(--ui-accent); color: var(--ui-accent-ink); - border-bottom-right-radius: 4px; + border-end-end-radius: 4px; } .ai-typing { display: inline-flex; @@ -3670,7 +3670,7 @@ body { height: 100vh; overflow: hidden; } background: var(--surface-bg-tint); } .ai-options button { - text-align: left; + text-align: start; padding: .55rem .75rem; border: 1px solid var(--line); border-radius: 10px; @@ -3762,8 +3762,8 @@ body { height: 100vh; overflow: hidden; } } .mouse-test-chart { position: absolute; - left: 0; - right: 0; + inset-inline-start: 0; + inset-inline-end: 0; bottom: 0; display: block; width: 100%; @@ -3788,16 +3788,16 @@ body { height: 100vh; overflow: hidden; } letter-spacing: 0.04em; } .mouse-test-chip-left { - left: 0.8rem; + inset-inline-start: 0.8rem; } .mouse-test-chip-right { - right: 0.8rem; + inset-inline-end: 0.8rem; font-variant-numeric: tabular-nums; } .mouse-test-area-note { position: absolute; bottom: 0.8rem; - left: 50%; + inset-inline-start: 50%; transform: translateX(-50%); padding: 0.3rem 0.9rem; border-radius: 999px; @@ -4023,7 +4023,7 @@ body { height: 100vh; overflow: hidden; } .mouse-test-device-val { color: var(--text); font-weight: 600; - text-align: right; + text-align: end; min-width: 0; } .mouse-test-device-val-wrap { @@ -4222,7 +4222,7 @@ body { height: 100vh; overflow: hidden; } text-transform: uppercase; } .hardware-test-terminal-state { - margin-left: auto; + margin-inline-start: auto; font-size: 0.68rem; font-weight: 650; color: var(--dim); @@ -4255,7 +4255,7 @@ body { height: 100vh; overflow: hidden; } .hardware-test-line-at { display: inline-block; min-width: 4.6rem; - margin-right: 0.6rem; + margin-inline-end: 0.6rem; color: var(--muted); user-select: none; font-variant-numeric: tabular-nums; @@ -4273,7 +4273,7 @@ body { height: 100vh; overflow: hidden; } width: 0.55em; height: 1.05em; vertical-align: text-bottom; - margin-left: 2px; + margin-inline-start: 2px; background: var(--text); animation: hardware-test-blink 1s steps(1) infinite; } @@ -4297,7 +4297,7 @@ body { height: 100vh; overflow: hidden; } .hardware-test-summary-fail { color: var(--destructive); } .hardware-test-summary-skip { color: var(--dim); } .hardware-test-summary strong { - margin-left: auto; + margin-inline-start: auto; letter-spacing: 0.03em; } .hardware-test-summary.pass strong { color: var(--success); } @@ -4347,7 +4347,7 @@ body { height: 100vh; overflow: hidden; } filter: brightness(1.12); } .hardware-test-btn-share { - margin-left: auto; + margin-inline-start: auto; background: var(--surface-control); border: 1px solid var(--line-strong); color: var(--text); @@ -4364,3 +4364,16 @@ body { height: 100vh; overflow: hidden; } height: clamp(420px, 58vh, 600px); } } + +/* Right-to-left (Arabic). The stylesheet uses CSS logical properties for + direction-sensitive spacing, borders and offsets, so most of the layout + mirrors on its own once is set. The rules below cover the + cases that cannot be expressed logically — transform-based direction — and + the directional chevron icons. */ +[dir="rtl"] .game-profile-toggle.is-on .game-profile-toggle-thumb { transform: translateX(-18px); } +[dir="rtl"] .switch input:checked + .switch-track::before { transform: translateX(-16px); } +[dir="rtl"] .app-sidebar-nav-arrow { transform: scaleX(-1); } +[dir="rtl"] .app-sidebar-nav-item:hover .app-sidebar-nav-arrow { transform: scaleX(-1) translateX(2px); } +[dir="rtl"] .app-sidebar-collapse-toggle:hover { transform: translateY(-50%) translateX(-2px); } +[dir="rtl"] .egg-collapsible summary i, +[dir="rtl"] .egg-experimental summary i { transform: rotate(-45deg); } diff --git a/src/i18n-ar.ts b/src/i18n-ar.ts new file mode 100644 index 00000000..1ccd0b8c --- /dev/null +++ b/src/i18n-ar.ts @@ -0,0 +1,1056 @@ +import type { I18nKey } from "./i18n.ts"; + +/** Arabic strings, split into a lazy chunk so the initial bundle ships + English only. Loaded via ensureLocale() before first use. */ +export const ar: Record = { + "nav.language": "اللغة", + "nav.settings": "الإعدادات", + "nav.support": "الدعم", + "nav.supportProject": "ادعم المشروع", + "nav.mouseCheck": "فحص الماوس", + "nav.home": "الرئيسية", + "nav.dashboard": "إعداد الماوس", + "nav.feedback": "الملاحظات", + "nav.docs": "المستندات", + "nav.supported": "الأجهزة المدعومة", + "nav.whatsNew": "الجديد", + "nav.games": "الألعاب", + "nav.hardwareTest": "فحص العتاد", + "nav.expand": "توسيع الشريط الجانبي", + "nav.collapse": "طي الشريط الجانبي", + "side.selectedDevice": "الجهاز المحدد", + "side.connectedMouse": "الماوس المتصل", + "side.connected": "متصل", + "side.connectedDevices": "الأجهزة المتصلة", + "bridge.title": "OpenMouse Bridge", + "bridge.connected": "متصل", + "games.subtitle": "اختر لعبة لتمنحها إعدادات ماوس خاصة بها — تُطبَّق فور تشغيل اللعبة.", + "games.autoApply": "تلقائي", + "games.settings": "الإعدادات", + "games.settingsHint": "تُحفظ في ملف هذه اللعبة فقط. يحتفظ ماوسك بإعداداته الحالية حتى تشغيل {name}، ثم يستعيدها عند إغلاقها.", + "games.changedCount": "إعدادات مخصّصة لهذه اللعبة: {n}", + "games.noChanges": "يستخدم إعدادات ماوسك الحالية", + "games.connectTarget": "وصّل {name} لتعديل إعداداته لهذه اللعبة.", + "games.noTarget": "اختر جهازًا مستهدفًا لتعديل إعدادات هذه اللعبة.", + "bridge.profileEnabled": "تم تفعيل التطبيق التلقائي", + "bridge.profileEnabledDetail": "سيدفع Bridge هذا الملف إلى ماوسك فور تشغيل {name}.", + "bridge.profileUpdated": "تم تحديث الملف", + "bridge.profileUpdatedDetail": "سيطبّق Bridge الإعدادات الجديدة في المرة التالية التي يكون فيها {name} في المقدمة.", + "bridge.profileDisabled": "تم إيقاف التطبيق التلقائي", + "bridge.profileDisabledDetail": "لن يُدفع هذا الملف إلى {name} بعد الآن.", + "bridge.profileSaveFailed": "تعذّر الوصول إلى OpenMouse Bridge", + "bridge.profileSaveFailedDetail": "تأكد من أن Bridge ما زال يعمل، ثم حاول مرة أخرى.", + "bridge.profileAppliedHere": "ملف اللعبة المطبَّق", + "bridge.profileRestored": "تمت استعادة إعدادات ماوسك السابقة", + "panel.overline": "التحكم بالجهاز", + "panel.connectMouse": "توصيل ماوس", + "panel.shareProfile": "مشاركة الملف", + "panel.deviceSections": "أقسام الجهاز", + "empty.previewsTitle": "معاينات التعريف", + "empty.title": "وصّل جهازك.", + "empty.previewsBody": "اعرض أي تعريف مدعوم بدون عتاده:", + "empty.body": "اختر ماوسك في نافذة المتصفح لضبط إعداداته المدمجة.", + "tab.overview": "نظرة عامة", + "tab.performance": "الأداء", + "tab.buttons": "الأزرار", + "tab.profiles": "الملفات", + "tab.advanced": "متقدم", + "tab.lighting": "الإضاءة", + "ov.battery": "البطارية", + "ov.firmware": "البرنامج الثابت", + "ov.fwSingle": "البرنامج الثابت المُبلَّغ من الماوس", + "ov.fwNone": "غير مُبلَّغ", + "ov.connection": "الاتصال", + "ov.connReceiver": "مستقبل 2.4 GHz", + "ov.conn24": "2.4 GHz", + "ov.profile": "الملف", + "ov.led": "مؤشر المستقبل", + "ov.on": "تشغيل", + "ov.off": "إيقاف", + "test.title": "اختبار معدل استقصاء الماوس", + "test.subtitle": "حرّك ماوسك في دوائر صغيرة لقياس معدل الاستقصاء الفعلي.", + "test.movePrompt": "حرّك ماوسك هنا لاكتشاف الحركة", + "test.ready": "جاهز", + "test.getReady": "استعد…", + "test.sampling": "جارٍ أخذ العينات…", + "test.areaNote": "استمر في التحريك بسلاسة أثناء العد التنازلي وأخذ العينات.", + "test.complete": "اكتمل الاختبار", + "test.start": "ابدأ الاختبار", + "test.reset": "إعادة تعيين", + "test.avgHz": "المتوسط", + "test.peakHz": "الذروة", + "test.low5": "أدنى 5%", + "test.stability": "الاستقرار", + "test.jitter": "الاهتزاز", + "test.dropouts": "الانقطاعات", + "test.events": "الأحداث", + "test.avgInterval": "متوسط الفاصل", + "test.deviceInfo": "معلومات الجهاز", + "test.deviceName": "الجهاز", + "test.connection": "الاتصال", + "test.reportedHz": "معدل الاستقصاء المُبلَّغ", + "test.dpi": "DPI", + "test.dpiStages": "مراحل DPI", + "test.battery": "البطارية", + "test.firmware": "البرنامج الثابت", + "test.noDevice": "لا يوجد جهاز متصل. اختبر معدل الاستقصاء على أي حال، أو افتح إعداد الماوس للاتصال.", + "test.buttonTest": "اختبار الأزرار", + "test.buttonHint": "انقر أي زر في الماوس لتراه يضيء هنا.", + "test.btn.left": "يسار", + "test.btn.right": "يمين", + "test.btn.middle": "أوسط", + "test.btn.back": "رجوع", + "test.btn.forward": "تقدّم", + "hw.title": "فحص العتاد", + "hw.subtitle": "يشغّل مجموعة التحقق الكاملة من التعريف على الماوس المتصل — وهي الأدلة المستخدمة لنقل طراز يحتاج اختبارًا إلى مدعوم في صفحة الأجهزة المدعومة.", + "hw.start": "ابدأ الفحص", + "hw.end": "إنهاء الفحص", + "hw.share": "مشاركة التقرير", + "hw.connect": "توصيل جهاز", + "hw.ready": "اضغط ابدأ الفحص لتشغيل مجموعة التحقق على الماوس المتصل.", + "hw.noDevice": "لا يوجد ماوس متصل", + "hw.noDeviceDetail": "وصّل ماوسًا لتشغيل اختبارات العتاد.", + "hw.deviceCard": "الجهاز", + "hw.termTitle": "طرفية الفحص", + "hw.identity": "VID / PID", + "hw.transport": "النقل", + "hw.driver": "التعريف", + "hw.rates": "المعدلات المدعومة", + "hw.lod": "مسافة الرفع", + "hw.mode": "وضع الجهاز", + "hw.receiver": "المستقبل", + "hw.signal": "الإشارة", + "hw.interfaces": "الواجهات", + "hw.verdictPass": "نجاح — اجتازت جميع الفحوص", + "hw.verdictFail": "فشل — فشل فحص واحد أو أكثر", + "hw.verdictIncomplete": "غير مكتمل — أُوقف الفحص مبكرًا", + "hw.reportSent": "تم إرسال التقرير", + "hw.reportSentDetail": "أُرسلت نتائج فحص العتاد إلى قناة ملاحظات OpenMouse.", + "hw.reportError": "تعذّر إرسال التقرير", + "hw.reportErrorDetail": "مرحّل الملاحظات غير متاح الآن — حاول بعد لحظات.", + "set.title": "الإعدادات", + "set.back": "العودة إلى الجهاز", + "set.profiles": "الملفات", + "set.profileKey": "مفتاح الملف", + "set.profileKeyBody": "مفتاح للنسخ واللصق ينقل إعدادات هذا الماوس إلى وحدة أخرى من الطراز نفسه. الصقه في الإعدادات هناك لتحميل الإعداد نفسه.", + "set.yourKey": "مفتاحك", + "set.noKey": "وصّل ماوسًا لإنشاء مفتاح.", + "set.copyKey": "نسخ المفتاح", + "set.copied": "تم النسخ", + "set.importKey": "استيراد مفتاح", + "set.importPlaceholder": "الصق مفتاح ملف هنا…", + "set.import": "استيراد", + "set.soundTitle": "الصوت", + "set.soundDescription": "تفعيل أصوات مثل نقرات الماوس والاتصالات الجديدة", + "set.importNote": "تُدرج الإعدادات المستوردة كأي تعديل آخر — لا يُكتب شيء حتى تثبّتها.", + "set.appearance": "المظهر", + "set.accentTheme": "سمة التمييز", + "set.accentBody": "كل بلاطة مصبوغة بسمتها الخاصة.", + "set.glass": "الزجاج السائل", + "set.glassTitle": "كثافة الزجاج", + "set.glassBody": "اضبط المادة الضبابية من لوحات شفافة تمامًا إلى لمسة الأكريليك الكاملة.", + "set.transparent": "شفاف", + "set.acrylic": "أكريليك", + "set.language": "اللغة", + "set.languageTitle": "اللغة", + "set.languageBody": "اختر لغة الواجهة. تبقى رسائل الجهاز بالإنجليزية حاليًا.", + "set.english": "English", + "set.portuguese": "Português", + "set.spanish": "Español", + "set.french": "Français", + "set.german": "Deutsch", + "set.chinese": "中文", + "set.japanese": "日本語", + "set.korean": "한국어", + "set.russian": "Русский", + "set.vietnamese": "Tiếng Việt", + "set.arabic": "العربية", + "set.motion": "الحركة", + "set.animations": "الرسوم المتحركة", + "set.animationsBody": "تفعيل انتقالات الواجهة وتغيّرات الحالة المتحركة.", + "set.enableAnimations": "تفعيل الرسوم المتحركة", + "set.writes": "الكتابة", + "set.instantFlash": "التثبيت الفوري", + "set.instantFlashBody": "اكتب كل تغيير إلى الماوس فور إجرائه، بدلًا من تجهيزه لشريط التثبيت.", + "set.flashImmediately": "ثبّت فورًا", + "set.sections": "الأقسام", + "set.advancedEditors": "المحررات المتقدمة", + "set.advancedEditorsBody": "اختر ما إذا كانت أقسام CPI وربط الأزرار والتجريبية تبدأ موسّعة.", + "set.expandByDefault": "توسيع افتراضيًا", + "set.experimental": "تجريبي", + "set.experimentalTitle": "عناصر تحكم تجريبية", + "set.experimentalBody": "إظهار أو إخفاء عناصر التحكم التي قد تختلف بين إصدارات البرنامج الثابت.", + "set.showExperimental": "إظهار الإعدادات التجريبية", + "set.dev": "التطوير", + "set.previewsTitle": "معاينات التعريف", + "set.previewsBody": "اعرض أي تعريف مدعوم بدون عتاده، لفحص تغيير مقابل كل علامة تجارية. لا يُكتب شيء إلى أي جهاز.", + "set.reset": "إعادة تعيين تفضيلات الواجهة", + "set.supportTitle": "ادعم المشروع", + "set.supportBody": "OpenMouse مجاني ومفتوح المصدر. انضم إلى المجتمع، تابع المشروع، أو ادعم العمل الذي يقف خلفه.", + "pend.region": "تغييرات غير محفوظة", + "pend.overline": "قيد الانتظار", + "pend.none": "لا تغييرات معلّقة", + "pend.one": "تغيير واحد غير محفوظ", + "pend.many": "تغييرات غير محفوظة", + "pend.idleHint": "اضبط إعدادًا لمعاينته قبل الكتابة.", + "pend.revert": "تراجع", + "pend.applying": "جارٍ التطبيق…", + "pend.apply": "تطبيق التغييرات", + "toast.region": "الإشعارات", + "toast.dismiss": "تجاهل الإشعار", + "bat.needsCharging": "بحاجة للشحن", + "bat.full": "مشحون بالكامل", + "bat.untilFull": "حتى الامتلاء", + "bat.remaining": "متبقٍ", + "bat.charging": "جارٍ الشحن", + "bat.chargingSlowly": "شحن بطيء", + "bat.almostFull": "شبه ممتلئ", + "bat.fullState": "ممتلئ", + "bat.discharging": "قيد التفريغ", + "bat.unknown": "غير معروف", + "fb.title": "إرسال ملاحظات", + "fb.description": "شارك تقرير خطأ أو طلب ميزة أو أي ملاحظة أخرى. تُرسل الملاحظات بشكل مجهول — لا يُرفق شيء إلا إذا فعّلت ذلك أدناه.", + "fb.placeholder": "اكتب ملاحظاتك هنا…", + "fb.handleLabel": "معرّف Discord (اختياري)", + "fb.handlePlaceholder": "username", + "fb.attachLabel": "إرفاق تفاصيل الجهاز وسجل التشخيص", + "fb.attachDetail": "يضيف معلومات جهازك وحركة WebHID الأخيرة لنتمكن من تصحيح الأخطاء أسرع. لا يُرسل إلا عند تحديد هذا المربع.", + "fb.send": "إرسال الملاحظات", + "fb.sent": "تم إرسال الملاحظات", + "fb.sentDetail": "شكرًا — تم تسليم ملاحظتك.", + "fb.error": "تعذّر إرسال الملاحظات", + "fb.errorDetail": "تحقق من اتصالك وحاول مرة أخرى.", + "fb.cooldown": "يمكنك إرسال ملاحظات مرة أخرى خلال {seconds} ث.", + "fb.limit": "لقد بلغت حد الملاحظات حاليًا. حاول لاحقًا.", + "artreq.request": "طلب رسمة", + "artreq.title": "طلب رسمة", + "artreq.description": "هل رسمة هذا الجهاز مفقودة؟ أرسل صورة مقصوصة من الأعلى وسنراجعها. لا يُرفع شيء إلى الموقع مباشرة — تذهب صورتك إلى فريقنا للتحقق والنشر.", + "artreq.deviceLabel": "الجهاز", + "artreq.drop": "اسحب رسمتك وأفلتها هنا، أو انقر للاختيار", + "artreq.hint": "PNG/WebP · خلفية شفافة أو بيضاء صافية · عرض للجهاز من الأعلى فقط · 5 ميغابايت كحد أقصى", + "artreq.noteLabel": "ملاحظة (اختياري)", + "artreq.notePlaceholder": "رابط المصدر أو صفحة المنتج أو أي شيء يساعدنا في التحقق…", + "artreq.send": "إرسال الطلب", + "artreq.sent": "تم إرسال طلب الرسمة", + "artreq.sentDetail": "سيراجعها فريقنا وينشر الرسمة بعد التحقق.", + "artreq.error": "تعذّر إرسال الطلب", + "artreq.errorDetail": "تحقق من اتصالك وحاول مرة أخرى.", + "artreq.sizeError": "الصورة كبيرة جدًا. 5 ميغابايت كحد أقصى.", + "artreq.typeError": "يرجى اختيار صورة PNG أو WebP.", + "artreq.screened": "تخضع المرفوعات لفحص تلقائي بحثًا عن محتوى غير لائق.", + "artreq.rejected": "لا يمكن إرسال هذه الصورة.", + "artreq.rejectedInvalid": "هذا الملف ليس صورة PNG أو WebP صالحة.", + "artreq.rejectedNsfw": "تم اكتشاف محتوى غير لائق.", + "artreq.rejectedHate": "تم اكتشاف محتوى كراهية أو مسيء.", + "artreq.rejectedGore": "تم اكتشاف محتوى دموي أو عنيف.", + "artreq.rejectedJunk": "لا تبدو هذه رسمة جهاز. ارفع صورة مقصوصة للمنتج من الأعلى.", + "artreq.rejectedNotArtwork": "هذه ليست رسمة ماوس — أرسل عرضًا أو قصة من الأعلى للجهاز وحده، دون أي شيء آخر في الإطار.", + "artreq.rejectedPhoto": "تبدو هذه صورة فوتوغرافية وليست عرضًا. لا تُقبل إلا العروض أو القصاصات الرقمية من الأعلى.", + "artreq.rejectedBackground": "يجب أن تكون الخلفية شفافة أو بيضاء صافية — بلا مشاهد أو مكاتب أو تدرجات أو ظلال.", + "artreq.rejectedUnconfirmed": "لم يتمكن التحقق بالذكاء الاصطناعي من تأكيد أن هذه رسمة جهاز صالحة.", + "artreq.rejectedUnscreened": "الفحص التلقائي غير متاح الآن. يرجى المحاولة لاحقًا.", + "artreq.lockedTitle": "عدد كبير جدًا من الطلبات المرفوضة.", + "artreq.lockedBody": "لم تستوفِ هذه الصورة إرشادات الرسمة وتم حظر الطلبات مؤقتًا. حاول بعد {minutes} دقيقة، أو افتح تذكرة يدوية على Discord لمراجعة بشرية.", + "artreq.lockedDiscord": "فتح تذكرة يدوية على Discord", + "gamereq.title": "طلب لعبة أو تطبيق", + "gamereq.previewTitle": "راجع طلبك", + "gamereq.description": "تريد ملفًا لشيء غير مُدرَج؟ أخبرنا باسمه وملفه التنفيذي وسنضيفه إلى قائمة Bridge.", + "gamereq.kindLabel": "نوع الطلب", + "gamereq.kindGame": "لعبة", + "gamereq.kindApp": "تطبيق", + "gamereq.nameLabel": "الاسم", + "gamereq.namePlaceholder": "مثال: Hunt: Showdown", + "gamereq.appNamePlaceholder": "مثال: Adobe Premiere Pro", + "gamereq.pathLabel": "مسار الملف التنفيذي", + "gamereq.findTitle": "كيف تجد الملف التنفيذي", + "gamereq.pickRunning": "اختر من التطبيقات قيد التشغيل", + "gamereq.noRunning": "لا يرى Bridge أي تطبيقات قيد التشغيل بمسار معروف. استخدم أمرًا أدناه بدلًا من ذلك.", + "gamereq.cmdWindowsHint": "افتحه أولًا، ثم نفّذ هذا في {shell} وانسخ المسار الظاهر بجانب عنوان نافذته.", + "gamereq.cmdMacHint": "نفّذ هذا في {shell}، ثم انتقل إلى اللعبة أو التطبيق خلال 5 ثوانٍ. سيطبع مسار الملف التنفيذي.", + "gamereq.cmdLinuxHint": "افتحه أولًا، ثم نفّذ هذا في {shell}. يسرد ألعاب Steam/Proton قيد التشغيل؛ انسخ مسار التي تريدها.", + "gamereq.copy": "نسخ الأمر", + "gamereq.explorerTip": "أو في مستكشف الملفات: Shift + النقر بالزر الأيمن على ملف .exe ← نسخ كمسار.", + "gamereq.discordLabel": "اسم مستخدم Discord", + "gamereq.discordInvalid": "أدخل اسم مستخدم Discord (2–32 حرفًا: حروف وأرقام و _ و .).", + "gamereq.preview": "معاينة", + "gamereq.previewDescription": "هذا بالضبط ما سيراه فريقنا. ارجع لتصحيح أي شيء قبل الإرسال.", + "gamereq.alreadySupported": "{name} موجود بالفعل في القائمة. يمكنك إرسال هذا إن لم يتم اكتشافه.", + "gamereq.error": "تعذّر إرسال الطلب.", + "gamereq.edit": "تعديل", + "gamereq.send": "إرسال الطلب", + "gamereq.sent": "تم إرسال الطلب", + "gamereq.sentDetail": "شكرًا! سننظر في إضافة {name} وسنتواصل عبر Discord إن احتجنا شيئًا.", + "gamereq.tile": "طلب لعبة أو تطبيق", + "gamereq.tileHint": "لا ترى لعبتك؟", + "wn.title": "تطبيق OpenMouse لسطح المكتب", + "wn.body": "متاح الآن في نسخة ألفا: تطبيق OpenMouse لسطح المكتب. شغّل OpenMouse عبر تطبيق مستقل قابل للتثبيت — دون الحاجة لإبقاء تبويب المتصفح مفتوحًا. يتضمن وصولًا أصليًا إلى HID وملفات الألعاب وواجهة موحدة على الويب وسطح المكتب. توجّه إلى صفحة الإصدارات لتنزيله لمنصتك.", + "wn.download": "تنزيل لسطح المكتب", + "wn.close": "ربما لاحقًا", + "ai.title": "OpenMouse AI", + "ai.subtitle": "إجابات سريعة واستكشاف الأخطاء", + "ai.greeting": "مرحبًا! أنا OpenMouse AI. اختر موضوعًا أدناه وسأساعدك في حلّه.", + "ai.topic1": "ماوسي لا يتصل", + "ai.topic1Ans": "تأكد من أن ماوسك موصول بالكهرباء (أو أن مستقبله في مكانه) وأنه قيد التشغيل. جرّب منفذ USB آخر، أو اتصالًا سلكيًا إن كان ماوسك يدعمه. على Linux، تحقق من أن عقد أجهزة HID قابلة للقراءة — يسرد README قواعد udev. ثم اضغط «اتصال» واختر جهازك من نافذة المتصفح.", + "ai.topic2": "DPI أو معدل الاستقصاء لا يتغير", + "ai.topic2Ans": "بعض الماوسات تقبل قيمًا محددة مسبقًا فقط، لذا جرّب أحد الإعدادات المسبقة إن رُفضت قيمة مخصّصة. اضغط «تطبيق» ودع الكتابة تكتمل قبل لمس أي شيء آخر. إذا عادت القيمة للوراء، فقد لا يكون هذا الإعداد قابلًا للكتابة على ماوسك بعد.", + "ai.topic3": "التطبيق لا يفتح في متصفحي", + "ai.topic3Ans": "يحتاج OpenMouse إلى WebHID، لذا يعمل في Chrome وEdge والمتصفحات الأخرى المبنية على Chromium. لا يدعم Firefox وSafari تقنية WebHID بعد، لذا لن يفتح التطبيق فيهما. جرّب أحدث إصدار من Chrome أو Edge.", + "ai.topic4": "إعداداتي لا تُحفظ", + "ai.topic4Ans": "تُكتب الإعدادات في الملف المخزّن للماوس وتُطبَّق على الجهاز، لا في السحابة. بعد تغيير إعداد، اضغط «تطبيق» وأبقِ الصفحة مفتوحة حتى تكتمل الكتابة. إذا استُعيدت القيمة بعد إعادة التشغيل، فقد يكون هذا الإعداد للقراءة فقط على طرازك.", + "ai.other": "شيء آخر", + "ai.otherAns": "لا بأس — انضم إلى Discord الخاص بـ OpenMouse وسيساعدك المجتمع.", + "ai.joinDiscord": "انضم إلى Discord", + "conn.add": "إضافة جهاز", + "conn.addMouse": "إضافة ماوس", + "conn.connecting": "جارٍ الاتصال…", + "conn.preview": "وضع المعاينة", + "common.apply": "تطبيق", + "common.custom": "مخصّص", + "common.current": "الحالي {v}", + "common.staged": "مُجهَّز {v}", + "common.active": "نشط", + "common.close": "إغلاق", + "common.back": "رجوع", + "common.cancel": "إلغاء", + "common.done": "تم", + "common.save": "حفظ", + "common.on": "تشغيل", + "common.off": "إيقاف", + "dpi.sensitivity": "الحساسية", + "dpi.perProfile": "لكل ملف", + "dpi.value": "قيمة DPI", + "dpi.presets": "قيم DPI الشائعة", + "dpi.presetsInUse": "الإعدادات المسبقة المستخدمة", + "dpi.slotsInUse": "الفتحات المستخدمة", + "dpi.slotsCount": "عدد فتحات DPI", + "dpi.startingSlot": "فتحة البداية", + "dpi.makeStarting": "اجعل هذه فتحة البداية", + "dpi.slotX": "DPI للمحور X للفتحة {n}", + "dpi.slotY": "DPI للمحور Y للفتحة {n}", + "dpi.linkXY": "ربط X و Y للفتحة {n}", + "dpi.linkedHint": "X و Y مرتبطان — انقر لضبطهما منفصلين", + "dpi.unlinkedHint": "X و Y منفصلان — انقر لربطهما", + "dpi.slotLiftOff": "مسافة الرفع للفتحة {n}", + "dpi.liftOffTitle": "ضبط مسافة الرفع", + "dpi.noLiftOff": "صيغة الملف هذه لا تحتوي على إعداد مسافة الرفع", + "dpi.slotReadonly": "للقراءة فقط: كتابة فتحات DPI إلى الملف غير مفعّلة بعد، لأن تسلسل كتابة التثبيت لم يُتحقق منه على العتاد.", + "dpi.slotNote": "تخزّن كل فتحة حساسية X/Y الخاصة بها ومستوى الرفع. {min}–{max} DPI بخطوات {step}. الفتحة المميّزة هي التي يبدأ عليها الماوس.", + "dpi.stagesInUse": "المراحل المستخدمة", + "dpi.stagesCount": "عدد مراحل DPI", + "dpi.activeStage": "المرحلة النشطة", + "dpi.makeActive": "اجعل هذه المرحلة النشطة", + "dpi.stageDpi": "DPI للمرحلة {n}", + "dpi.color": "اللون", + "dpi.stageColor": "لون المرحلة {n}", + "dpi.stageNoteCount": "تعدّل الإعدادات المسبقة و«مخصّص» المرحلة المميّزة. {min}–{max} DPI. يتنقل زر DPI في الماوس عبر هذه المراحل.", + "dpi.stageNoteSteps": "تعدّل الإعدادات المسبقة و«مخصّص» المرحلة المميّزة. {min}–{max} DPI بخطوات {step}. يتنقل زر DPI في الماوس عبر المراحل الـ{total}.", + "dpi.stageToggle": "تضمين مرحلة DPI هذه", + "dpi.editorCountNote": "صف واحد لكل مرحلة DPI. حدّد صفًا لتضمينه في دورة DPI؛ أزل التحديد لتعطيله والاحتفاظ بالقيمة لاحقًا. انقر رقمًا لجعله مرحلة البداية.", + "dpi.editorFixedNote": "صف واحد لكل مرحلة DPI، {total} ثابتة. عدّل القيمة أو المنزلق لتغييرها. انقر رقمًا لجعله المرحلة النشطة.", + "dpi.stagesReadOnly": "تأتي مراحل DPI هذه من الماوس للقراءة فقط، ولا يمكن تعديلها هنا. غيّرها في برنامج الشركة المصنّعة.", + "dpi.editorGenericNote": "صف واحد لكل DPI مسبق. حدّد صفًا (أو انقر رقمه) لجعله DPI الحالي؛ تبقى الصفوف غير المحددة كاختيارات لاحقًا.", + "dpi.xAxis": "المحور X", + "dpi.yAxis": "المحور Y", + "dpi.viewStages": "مراحل", + "dpi.viewSingle": "قيمة واحدة", + "dpi.viewSlots": "فتحات", + "dpi.viewSlotsToggle": "تعديل DPI كفتحات أو قيمة واحدة", + "dpi.viewToggle": "تعديل DPI كمراحل متعددة أو قيمة واحدة", + "perf.reportFrequency": "تكرار الإبلاغ", + "perf.reportRate": "معدل الإبلاغ", + "perf.sharedNote": "يُخزَّن في هذا الملف كفاصل مشترك واحد، حتى {max} هرتز.", + "perf.splitNote": "يُخزَّن في هذا الملف، معدل لكل وصلة. حتى {wireless} هرتز لاسلكيًا، و{wired} هرتز عبر الكابل.", + "perf.eggNote": "المعدلات الأعلى تحدّث حركة المؤشر أكثر وتزيد حمل معالجة المعالج/USB.", + "perf.note": "المعدلات الأعلى تحدّث حركة المؤشر أكثر، لكنها تستهلك بطارية أكثر.", + "perf.allConnections": "كل الاتصالات", + "perf.wired": "سلكي", + "perf.wireless": "لاسلكي", + "perf.gamingSurface": "سطح الألعاب", + "perf.surfaceNote": "يضبط المستشعر لوسائد الماوس المخصصة للألعاب. «تلقائي» يدع الماوس يقرر؛ أوقفه إذا ساء التتبع على سطح غير مخصص للألعاب.", + "perf.auto": "تلقائي", + "perf.liftOff": "مسافة الرفع", + "perf.liftOffMode": "وضع الرفع", + "perf.single": "مفرد", + "perf.asymmetric": "غير متماثل", + "perf.liftOffLabel": "الرفع", + "perf.landing": "الهبوط", + "perf.lodLow": "منخفض", + "perf.lodMedium": "متوسط", + "perf.lodHigh": "مرتفع", + "perf.lodNeedSurface": "فعّل سطح الألعاب أو اضبطه على «تلقائي» لضبط مسافة الرفع.", + "perf.lodNote": "يتحكم في مدى ارتفاعك للماوس قبل توقف التتبع. القيم الأعلى تُبقي التتبع أطول قليلًا.", + "perf.bunnyHop": "القفز المتكرر", + "perf.bunnyTime": "مدة القفز المتكرر بالمللي ثانية", + "perf.bunnyReadonly": "لم تُتحقق صيغة هذا الملف على العتاد، لذا فهي للقراءة فقط.", + "perf.bunnyNote": "يتجاهل النقرات المتكررة الواقعة خلال هذه النافذة، بحيث لا يُسجَّل إلا مرة واحدة عند ارتداد المفتاح أثناء النقر السريع المتكرر. الأوقات الأطول ترشّح أكثر؛ الأقصر تسمح بالنقرات السريعة الحقيقية. {min}–{max} مللي ثانية بخطوات {step}.", + "perf.hybrid": "هجين", + "perf.opticalOnly": "بصري فقط", + "perf.lightforceMode": "وضع مفتاح LightForce", + "perf.lightforceNote": "«هجين» يوفّر الطاقة باستخدام التلامس الميكانيكي ولا يوقظ المستشعر البصري إلا عند الحاجة. «بصري فقط» ثابت لكنه يستهلك بطارية أكثر.", + "light.receiverTitle": "إضاءة المستقبل", + "light.zoneTitle": "إضاءة {zone}", + "light.writeOnly": "كتابة فقط", + "light.rgbPart": "جزء RGB", + "light.rgbParts": "أجزاء RGB", + "light.stripLeds": "مصابيح الشريط الضوئي", + "light.rgbZones": "مناطق RGB", + "light.effect": "تأثير الإضاءة", + "light.brightness": "السطوع", + "light.brightnessAria": "سطوع الإضاءة", + "light.colour": "اللون", + "light.colour2": "اللون 2", + "light.colourAria": "لون الإضاءة", + "light.colour2Aria": "لون الإضاءة الثاني", + "light.speed": "سرعة التأثير", + "light.choose": "اختر تأثيرًا", + "light.writeOnlyNote": "لا يستطيع الماوس الإبلاغ عن تأثيره الحالي، لذا يعرض هذا آخر قيمة كُتبت.", + "light.zoneNote": "يختار تأثير إضاءة {zone}.", + "mx.haptics": "الاستجابة اللمسية", + "mx.hapticFeedback": "الاستجابة اللمسية", + "mx.hapticStrength": "قوة الاستجابة اللمسية", + "mx.batterySaving": "توفير البطارية", + "mx.friendlyName": "الاسم المألوف", + "mx.friendlyNameAria": "الاسم المألوف للجهاز", + "mx.stageName": "اسم المرحلة", + "mx.chars": "{n}/{max} حرفًا", + "mx.ratcheted": "بمسنّنات", + "mx.freeSpinning": "دوران حر", + "mx.wheelMode": "وضع العجلة", + "mx.ratchet": "مسنّن", + "mx.freespin": "دوران حر", + "mx.hiRes": "تمرير عالي الدقة", + "mx.invertVertical": "عكس التمرير الرأسي", + "mx.invertThumb": "عكس عجلة الإبهام", + "mx.switchThreshold": "عتبة المفتاح", + "mx.pairedComputers": "أجهزة الكمبيوتر المقترنة", + "mx.hostOf": "{a} من {b}", + "mx.switchConfirm": "التبديل إلى الكمبيوتر {n}؟ سينقطع اتصال الماوس فورًا. استخدم الزر في أسفله لإعادته.", + "mx.switch": "تبديل", + "mx.remapping": "إعادة الربط", + "mx.lockedOne": "{names} مقفل بواسطة البرنامج الثابت للماوس ولا يمكن إعادة ربطه.", + "mx.lockedMany": "{names} مقفلة بواسطة البرنامج الثابت للماوس ولا يمكن إعادة ربطها.", + "mx.divertedOne": "{name} يُدار بواسطة تطبيق آخر، لذا لا يفعل شيئًا هنا.", + "mx.divertedMany": "{names} تُدار بواسطة تطبيق آخر، لذا لا تفعل شيئًا هنا.", + "mx.restoreHardware": "استعادة التحكم بالعتاد", + "adv.sleepMin": "لا يمكن أن يقل وقت السكون عن دقيقة واحدة.", + "adv.sleepTimeout": "مهلة السكون التلقائي", + "adv.hours": "ساعات", + "adv.minutes": "دقائق", + "adv.seconds": "ثوانٍ", + "adv.signal": "قوة الإشارة", + "adv.signalUnavailable": "إشارة المستقبل غير متاحة.", + "adv.signal0": "ضعيفة جدًا", + "adv.signal1": "ضعيفة", + "adv.signal2": "مقبولة", + "adv.signal3": "جيدة", + "adv.signal4": "ممتازة", + "adv.signalLevel": "المستوى {n}", + "adv.debounce": "منع الارتداد", + "adv.autoSleep": "سكون تلقائي", + "adv.lowPower": "وضع الطاقة المنخفضة", + "adv.lowPowerNote": "يبطئ الماوس لتوفير البطارية دون هذا المستوى.", + "adv.lowPowerCeiling": "غير متاح فوق {max} هرتز. اخفض معدل الاستقصاء لتغييره.", + "adv.processing": "المعالجة", + "adv.angleSnapping": "تثبيت الزاوية", + "adv.straightLine": "تصحيح الخط المستقيم", + "adv.hyperMode": "الوضع الفائق", + "adv.turboMode": "وضع التوربو", + "adv.buttonCombos": "تركيبات الأزرار", + "adv.ultraRange": "مدى فائق الطول", + "adv.angleTune": "ضبط الزاوية", + "adv.highSpeed": "الوضع عالي السرعة", + "adv.competitive": "الوضع التنافسي", + "adv.highestPerf": "أعلى أداء", + "adv.perfMode": "وضع الأداء", + "adv.sensorMode": "وضع المستشعر", + "adv.ecoNote": "«اقتصادي» يوفّر الطاقة عند 125–1000 هرتز لاسلكيًا. «عالي» يستخدم ملف المستشعر عالي الأداء.", + "adv.lockedNote": "مقفل على {mode} عند {hz} هرتز {conn}.", + "adv.duration": "المدة", + "adv.dpiIndicator": "مؤشر DPI", + "adv.effect": "التأثير", + "adv.ledOff": "إيقاف", + "adv.ledSteady": "ثابت", + "adv.ledBreathing": "تنفّس", + "adv.brightness": "السطوع", + "adv.speed": "السرعة", + "adv.dpiStageNote": "يستخدم لون مرحلة DPI النشطة المخزّن بواسطة الماوس.", + "adv.sensorPerf": "أداء المستشعر", + "adv.systemMode": "وضع النظام", + "adv.opticalNote": "اختر أداء المستشعر وملف الطاقة. محرك «بصري» مقفل أثناء تفعيل الوضع الفائق.", + "adv.clickBehavior": "سلوك النقر", + "adv.clickNote": "يقلل HyperClick زمن استجابة النقر. يتحكم Slam-Click في الحماية من النقرات الناتجة عن الصدمات.", + "adv.dongleTournament": "الدونجل ووضع البطولات", + "adv.dongleLed": "مؤشر الدونجل", + "adv.receiverRapidFire": "المستقبل والنيران السريعة", + "adv.ledConnectRate": "الاتصال ومعدل الاستقصاء", + "adv.ledBatteryStatus": "حالة البطارية", + "adv.ledBatteryWarning": "تحذير البطارية", + "adv.fireKeyTimes": "نقرات لكل ضغطة", + "adv.fireKeyHold": "اضغط للتحرير", + "adv.fireKeyInterval": "الفاصل (مللي ثانية)", + "adv.batteryIndicator": "مؤشر البطارية", + "adv.solidWhite": "أبيض ثابت", + "adv.tournamentScroll": "تمرير البطولة", + "adv.scrollUp": "تمرير لأعلى", + "adv.scrollDown": "تمرير لأسفل", + "adv.bothDirections": "كلا الاتجاهين", + "adv.passthrough": "نافذة المرور", + "adv.oneSecond": "1 ثانية", + "adv.halfSeconds": "1.5 ثانية", + "adv.filters": "المرشحات", + "adv.slamclickFilter": "مرشح Slamclick", + "adv.jitterFilter": "مرشح اهتزاز الحركة", + "adv.gxMode": "وضع مفتاح GX", + "adv.leftButton": "الزر الأيسر", + "adv.rightButton": "الزر الأيمن", + "adv.dividerTitle": "مقسّم مخصّص", + "adv.dividerWarning": "قد تختلف المقسّمات غير القياسية لمعدل الاستقصاء بين إصدارات البرنامج الثابت.", + "adv.divider8k": "مقسّم 8K", + "adv.dividerResult": "النتيجة: {hz} هرتز", + "adv.dividerHint": "أدخل مقسّمًا من 1 إلى 255.", + "adv.applyDivider": "تطبيق المقسّم", + "adv.cpiStages": "مراحل CPI", + "adv.enabledStages": "المراحل المفعّلة", + "adv.stageCount": "{n} مرحلة", + "adv.stage": "المرحلة {n}", + "adv.separateXY": "فصل X/Y", + "adv.applyStage": "تطبيق المرحلة", + "adv.mapping": "الربط", + "adv.razerLocked": "{name} ثابت على الطبقة القياسية ولا يمكن إعادة تعيينه أو تعطيله.", + "adv.multiclick": "مرشح النقر المتعدد", + "adv.eggButtonTitle": "النقر المتعدد والربط", + "adv.proAdvanced": "متقدم", + "adv.wheelAccel": "تسارع العجلة", + "adv.angleTuning": "ضبط الزاوية", + "adv.onboardProfile": "الملف المدمج", + "adv.profileOpt": "الملف {n}", + "prof.onboardTitle": "الملف", + "prof.activeProfile": "الملف النشط", + "prof.profileStores": "يخزّن كل ملف مراحل DPI ومعدل الاستقصاء الخاصين به، لذا تتغير تلك القيم مع الملف.", + "map.remap": "إعادة ربط", + "map.defaultNote": "«افتراضي» يعيد وظيفة الزر الأصلية من المصنع.", + "pow.mode": "الوضع", + "pow.perfMode": "وضع الأداء", + "diag.details": "تفاصيل الجهاز", + "diag.diagnostics": "التشخيصات", + "diag.deviceDiag": "تشخيصات الجهاز", + "diag.download": "تنزيل التشخيصات", + "diag.verifyFormat": "التحقق من صيغة الملف", + "diag.reads": "القراءات", + "diag.rawSnapshot": "لقطة خام", + "diag.notReported": "غير مُبلَّغ", + "diag.unknown": "غير معروف", + "diag.noneHost": "لا شيء في وضع المضيف", + "diag.linkedXY": "X/Y مرتبطان", + "sup.support": "دعم الأجهزة", + "sup.requestMouse": "اطلب ماوسًا", + "sup.introVote": "أكمل فحص مكافحة البريد المزعج، ثم صوّت لماوس مُدرَج أو أرسل طلبًا جديدًا.", + "sup.introPaused": "التصويت والطلبات الجديدة متوقفة مؤقتًا أثناء إضافة حماية أقوى من إساءة الاستخدام.", + "sup.turnstile": "تحقق مكافحة البريد المزعج", + "sup.searchPlaceholder": "ابحث عن الشركة المصنّعة أو الطراز", + "sup.searchAria": "البحث في طلبات الماوسات", + "sup.protectionLoading": "جارٍ تحميل حماية التصويت", + "sup.completeCheck": "أكمل فحص مكافحة البريد المزعج أولًا", + "sup.vote": "تصويت", + "sup.loading": "جارٍ التحميل", + "sup.noMatches": "لا توجد طلبات مطابقة بعد.", + "sup.requestDifferent": "اطلب ماوسًا مختلفًا", + "sup.manufacturer": "الشركة المصنّعة", + "sup.model": "الطراز", + "sup.connection": "الاتصال", + "sup.notSure": "لست متأكدًا", + "sup.wiredUsb": "USB سلكي", + "sup.wirelessUsb": "مستقبل USB لاسلكي", + "sup.bluetooth": "بلوتوث", + "sup.wiredWireless": "سلكي ولاسلكي", + "sup.scope": "يغطي الطلب الماوس بالكامل: إعدادات الأداء والأزرار والملفات والإضاءة ومعلومات البطارية وكل قدرة أخرى يمكننا دعمها.", + "sup.consent": "يرسل هذا الحقول الظاهرة هنا فقط. لا تُرفق تشخيصات الجهاز تلقائيًا أبدًا.", + "sup.submitting": "جارٍ الإرسال…", + "sup.submit": "إرسال الطلب", + "sup.helpDiagnostics": "ساعد في إضافة الدعم عبر التشخيصات", + "sup.reviewTitle": "راجع التشخيصات قبل الرفع", + "sup.reviewBody": "قد تحتوي التقارير على البرنامج الثابت ومعرّفات المستقبل والإعدادات المدمجة وحركة HID الأخيرة. لا يُرفع أي شيء أدناه حتى توافق.", + "sup.reviewAria": "مراجعة رفع التشخيصات", + "sup.reviewConsent": "راجعت هذه البيانات وأوافق على رفعها لطلب الدعم هذا.", + "sup.upload": "رفع التشخيصات", + "sup.loadingRequests": "جارٍ تحميل الطلبات…", + "sup.loadFail": "تعذّر تحميل الطلبات.", + "sup.protectionMissing": "حماية التصويت غير مُهيأة.", + "sup.turnstileFail": "فشل تحميل تحقق مكافحة البريد المزعج.", + "sup.turnstileMissing": "لم يُحمَّل فحص مكافحة البريد المزعج.", + "sup.voteRecorded": "تم تسجيل تصويتك لـ{item}.", + "sup.alreadyVoted": "لقد صوّت لهذا الماوس بالفعل.", + "sup.voteFail": "تعذّر تسجيل تصويتك.", + "sup.saved": "تم حفظ الطلب. يمكنك الآن التصويت له من القائمة.", + "sup.saveFail": "تعذّر حفظ هذا الطلب.", + "sup.uploadedThanks": "رُفعت التشخيصات بشكل منفصل. شكرًا لمساعدتنا في التحقيق بشأن هذا الماوس.", + "sup.uploadFail": "تعذّر رفع التشخيصات.", + "cap.title": "التقاط HID++", + "cap.closeCapture": "إغلاق الالتقاط", + "cap.copyVerification": "نسخ بيانات التحقق", + "cap.verifyWrites": "التحقق من كتابات الملف", + "cap.snapshot": "لقطات الملفات", + "cap.compare": "مقارنة", + "cap.clear": "مسح", + "cap.copyComparison": "نسخ المقارنة", + "cap.whatChanged": "ما الذي غيّرته؟", + "cap.notesPlaceholder": "تفاصيل اختيارية، مثل الاستقصاء اللاسلكي من 8000 هرتز إلى 1000 هرتز", + "misc.devTesting": "اختبار التطوير", + "misc.devTestingBody": "سجّل معرّف الجهاز وإصدار البروتوكول وأي إعداد فاشل في المشكلة أو طلب السحب. لا تستخدم إعادة ضبط المصنع أثناء الاختبار الأولي.", + "prof.editing": "قيد التعديل", + "prof.active": "نشط", + "prof.editingTag": "قيد التعديل", + "prof.reload": "إعادة تحميل الملفات", + "prof.deleteReset": "حذف وإعادة تعيين كل ملف مدمج", + "prof.restoreDefaults": "استعادة كل ملف مدمج نهائيًا إلى إعدادات Logitech الافتراضية", + "prof.openHost": "فتح إعدادات المضيف", + "prof.hostAlready": "الماوس في وضع المضيف بالفعل", + "prof.switchHost": "تبديل الماوس إلى وضع المضيف", + "prof.switchHostAria": "التبديل إلى وضع المضيف", + "prof.layoutUnverified": "لم يُتحقق من تخطيط هذا الملف على العتاد", + "prof.openProfile": "افتح هذا الملف لتعديله", + "prof.noNameField": "صيغة هذا الملف لا تحتوي على حقل اسم", + "prof.contentUnverified": "لم تُتحقق كتابات محتوى الملف على العتاد", + "prof.renameProfile": "أعد تسمية هذا الملف", + "prof.runningProfile": "الماوس يشغّل هذا الملف", + "prof.enableFirst": "فعّل هذا الملف قبل التبديل إليه", + "prof.switchProfile": "بدّل الماوس إلى هذا الملف", + "prof.disable": "تعطيل", + "prof.enable": "تفعيل", + "prof.disableProfile": "عطّل هذا الملف", + "prof.enableProfile": "فعّل هذا الملف", + "prof.toggleProfile": "{a} الملف {n}", + "prof.assign": "التعيينات المدمجة", + "prof.assignBody": "اختر ما يفعله كل عنصر تحكم فعلي في هذا الملف.", + "prof.assignLayer": "طبقة التعيين", + "prof.normalTab": "عادي", + "prof.keyboardShortcut": "اختصار لوحة المفاتيح", + "prof.customPreserved": "ربط مخصّص (محفوظ)", + "prof.keyboardOpt": "اختصار لوحة المفاتيح…", + "prof.volUp": "رفع الصوت", + "prof.volDown": "خفض الصوت", + "prof.mute": "كتم", + "prof.playPause": "تشغيل / إيقاف مؤقت", + "prof.nextTrack": "المقطع التالي", + "prof.prevTrack": "المقطع السابق", + "prof.recorder": "مسجّل لوحة المفاتيح", + "prof.recordTitle": "تسجيل تسلسل أوامر", + "prof.recordBody": "ابدأ التسجيل، اضغط اختصارًا واحدًا أو تسلسلًا، ثم أوقف التسجيل.", + "prof.stopRec": "إيقاف التسجيل", + "prof.record": "تسجيل", + "prof.listening": "جارٍ الاستماع…", + "prof.nothingRec": "لم يُسجَّل شيء بعد", + "prof.macroNote": "سيُحفظ هذا التسلسل في ذاكرة الماكرو المدمجة للماوس.", + "prof.assignSeq": "تعيين التسلسل", + "prof.assignShortcut": "تعيين الاختصار", + "super.actuation": "نقطة التفعيل", + "super.both": "كلا الزرين", + "super.independent": "مستقل", + "super.bothPrimary": "كلا الزرين الأساسيين", + "super.bothBody": "طبّق القيم نفسها على الزرين الأيسر والأيمن.", + "super.applyBoth": "تطبيق على كلا الزرين", + "super.shortClick": "نقرة قصيرة واحدة", + "super.longClick": "{max} نقرة طويلة", + "super.fast": "سريع واحد", + "super.slow": "{max} بطيء", + "super.off0": "0 إيقاف", + "super.maxFeedback": "{max} أقصى استجابة", + "super.applyLeft": "تطبيق يسار", + "super.applyRight": "تطبيق يمين", + "super.clickHaptics": "استجابة النقر اللمسية", + "key.reload": "إعادة تحميل الطبقات", + "key.openLayer": "افتح هذه الطبقة", + "key.currentLayer": "الطبقة الحالية على الماوس", + "key.storedLayer": "مخزّنة على الماوس", + "key.alreadyLayer": "الماوس على هذه الطبقة بالفعل", + "key.switchLayer": "بدّل الماوس إلى {label}", + "key.switchTo": "التبديل إلى {label}", + "key.active": "نشط", + "key.editing": "قيد التعديل", + "key.layersStored": "طبقات مدمجة مخزّنة على Nape Pro", + "key.inspectHint": "انقر طبقة لفحصها. استخدم الدائرة لتبديل الماوس إليها.", + "key.reading": "جارٍ قراءة {label}…", + "key.layerNote": "تبقى التغييرات على هذه الطبقة حتى تطبّقها. يُخزَّن الاتجاه لكل طبقة بخطوات 45°. يمكن ضبط التمرير باتجاه عقارب الساعة وعكسها بشكل مستقل.", + "key.assignBody": "اختر اتجاه مستشعر هذه الطبقة وما يفعله كل عنصر تحكم فعلي.", + "ctl.noDevice": "لا يوجد جهاز متصل", + "ctl.addSidebar": "أضف جهازًا مدعومًا من الشريط الجانبي لقراءة حالته الحالية.", + "ctl.onboardIdle": "تُحمَّل الملفات عندما يكون الماوس في الوضع المدمج.", + "ctl.waitFlash": "انتظر انتهاء التثبيت الحالي.", + "ctl.alreadyMatches": "{label} يطابق الماوس بالفعل.", + "ctl.notInGameProfile": "لا يمكن حفظه في ملف لعبة", + "ctl.notInGameProfileDetail": "غيّر هذا من صفحة الجهاز بدلًا من ذلك.", + "ctl.stagedFlash": "{label} مُجهَّز. ثبّته لكتابته إلى الماوس.", + "ctl.discarded": "تم تجاهل التغييرات المُجهَّزة.", + "ctl.badKey": "تعذّرت قراءة مفتاح الملف هذا", + "ctl.noMouse": "لا يوجد ماوس متصل", + "ctl.noMouseDetail": "وصّل الماوس لاستيراد الإعدادات إليه.", + "ctl.keyMismatch": "مفتاح الملف لا يطابق هذا الماوس", + "ctl.mismatchDetail": "أُخذ هذا المفتاح من {brand} {name}، وليس من هذا الجهاز.", + "ctl.keyImported": "تم استيراد مفتاح الملف", + "ctl.keyImportedDetail": "راجع التغييرات المصطفّة، ثم ثبّتها لكتابتها.", + "ctl.switchingHost": "التبديل إلى الكمبيوتر {n}", + "ctl.switchHostDetail": "اضغط الزر أسفل الماوس لإعادته.", + "ctl.unableSwitch": "تعذّر طلب التبديل.", + "ctl.restoring": "جارٍ استعادة الأزرار إلى التحكم بالعتاد…", + "ctl.restored": "تمت استعادة الأزرار إلى التحكم بالعتاد.", + "ctl.unableRestore": "تعذّرت استعادة الأزرار.", + "ctl.waitRefresh": "بانتظار تحديث الجهاز الحالي…", + "ctl.flashFailed": "فشل التثبيت", + "ctl.unableFlash": "تعذّر تثبيت التغييرات المُجهَّزة.", + "ctl.flashedOne": "ثُبّت تغيير واحد إلى الماوس.", + "ctl.flashedMany": "ثُبّتت {n} تغييرات إلى الماوس.", + "ctl.connected": "متصل", + "ctl.batteryPct": "البطارية {n}%", + "ctl.currentLine": "الحالي: {dpi} DPI · {hz} هرتز", + "ctl.switching": "جارٍ التبديل", + "ctl.switchingPath": "مسار التبديل", + "ctl.newDevice": "تم اكتشاف جهاز جديد", + "ctl.connFailed": "فشل الاتصال", + "ctl.reading": "جارٍ قراءة {name}…", + "ctl.preferUsb": "تفضيل USB على المستقبل.", + "ctl.unableSwitchDevices": "تعذّر تبديل الأجهزة.", + "ctl.unableRead": "تعذّرت قراءة الماوس المتصل.", + "ctl.unableSwitchMouse": "تعذّر التبديل إلى ماوس متصل آخر.", + "ctl.requesting": "جارٍ طلب الإذن", + "ctl.choosePrompt": "اختر جهازك في نافذة المتصفح.", + "ctl.notConnected": "غير متصل", + "ctl.noSelection": "لم يُحدَّد أي جهاز في نافذة المتصفح.", + "ctl.opening": "جارٍ فتح الجهاز", + "ctl.notMouse": "ليس ماوسًا", + "ctl.reconnecting": "جارٍ إعادة الاتصال", + "ctl.readingPrev": "جارٍ قراءة الجهاز المصرّح به سابقًا.", + "ctl.useAdd": "استخدم «إضافة جهاز» إن لم يُعد الماوس الاتصال تلقائيًا.", + "ctl.couldNotReconnect": "تعذّرت إعادة اتصال الماوس", + "ctl.dpiUnsupported": "قيمة DPI هذه غير مدعومة من هذا الماوس.", + "ctl.closestStep": "لا يستطيع هذا الماوس تنفيذ {dpi} DPI. أقرب خطوة يدعمها هي {closest}.", + "ctl.dpiNotSupported": "{dpi} DPI غير مدعوم من هذا الماوس.", + "ctl.axesAdvertised": "يجب أن تكون قيمتا المحورين من قيم DPI المُعلَنة.", + "ctl.unableProfiles": "تعذّرت قراءة الملفات المدمجة.", + "ctl.switchingMode": "التبديل إلى وضع {mode}…", + "ctl.unableMode": "تعذّر تغيير الوضع المدمج.", + "ctl.switchingProfile": "التبديل إلى الملف {n}…", + "ctl.unableProfile": "تعذّر تحديد ذلك الملف.", + "ctl.unableKeychron": "تعذّرت قراءة تعيينات أزرار Keychron.", + "ctl.runningFrom": "يعمل من {label}. {layers}", + "ctl.layersCount": "{n} طبقة مدمجة", + "ctl.readingLayers": "جارٍ قراءة الطبقات المدمجة…", + "ctl.noLayers": "لم يُبلّغ الماوس عن طبقات مدمجة.", + "ctl.unableLayers": "تعذّرت قراءة الطبقات المدمجة.", + "ctl.previewSwitched": "معاينة: تم التبديل إلى {label}. لا يُكتب شيء.", + "ctl.runningFromLabel": "يعمل من {label}.", + "ctl.unableLayer": "تعذّر تبديل الطبقة.", + "ctl.unableProfileState": "تعذّر تغيير حالة الملف.", + "ctl.resetting": "جارٍ إعادة تعيين كل ملف مدمج…", + "ctl.writingDefaults": "جارٍ كتابة إعدادات Logitech الافتراضية…", + "ctl.resetDone": "أُعيد تعيين كل الملفات المدمجة إلى إعدادات Logitech الافتراضية.", + "ctl.resetComplete": "اكتملت إعادة التعيين. الملف 1 نشط؛ الملفات الأخرى معطّلة.", + "ctl.unableReset": "تعذّرت إعادة تعيين كل ملف مدمج.", + "ctl.rateSupport": "يدعم هذا الماوس {rates} هرتز لوصلة هذا الملف.", + "ctl.noLighting": "الإضاءة غير متاحة لهذا الماوس.", + "ctl.pickEffect": "اختر تأثيرًا أولًا.", + "ctl.waitingRefresh": "بانتظار التحديث", + "ctl.unableRefresh": "تعذّر تحديث حالة الماوس.", + "ctl.previewG502": "معاينة: أداء G502 X PLUS والملفات المدمجة وإعادة ربط الأزرار وإضاءة RGB.", + "ctl.previewLabel": "معاينة: {label}. لا يُكتب شيء.", + "ctl.connectedTo": "متصل بـ{name}", + "ctl.mouseGone": "انقطع اتصال الماوس", + "ctl.deviceRemoved": "تمت إزالة الجهاز.", + "ctl.savedName": "تم حفظ {name}", + "ctl.gone": "الماوس لم يعد متصلًا.", + "ctl.noWebHid": "WebHID غير متاح. استخدم Chrome أو Edge على سطح المكتب.", + "ctl.unableReconnect": "تعذّرت إعادة الاتصال بالماوس.", + "ctl.noProfileOpen": "لا يوجد ملف مفتوح للتعديل.", + "ctl.receiverGone": "المستقبل لم يعد متصلًا.", + "ctl.noLedControl": "جهاز Pulsar هذا لا يوفّر تحكمًا بمؤشر المستقبل.", + "ctl.teeGone": "ماوس Teevolution لم يعد متصلًا.", + "ctl.previewNoMouse": "وضع المعاينة: لا يوجد ماوس متصل، لذا لم يُكتب شيء.", + "ctl.atkPending": "طبّق أو تجاهل التغييرات المعلّقة قبل تبديل بنوك الإعدادات.", + "ctl.atkSwitching": "التبديل إلى بنك الإعدادات {n}…", + "ctl.atkActive": "بنك الإعدادات {n} نشط.", + "ctl.atkUnableSelect": "تعذّر تحديد بنك إعدادات ATK R1 هذا.", + "ctl.atkUnableBank": "تعذّر تحديد بنك الإعدادات هذا.", + "ctl.pairStarting": "جارٍ بدء نافذة اقتران مستقبل R1 SE+…", + "ctl.pairActive": "المستقبل لديه نافذة اقتران نشطة بالفعل. انتظر انتهاءها، ثم ابدأ اقتران R1 SE+.", + "ctl.pairActiveTitle": "الاقتران نشط بالفعل", + "ctl.pairActiveDetail": "ترك OpenMouse العد التنازلي للمستقبل الحالي دون تغيير.", + "ctl.pairProgress": "جارٍ اقتران R1 SE+… تبقّى {n} ث.", + "ctl.pairChecking": "جارٍ فحص الاقتران المكتمل…", + "ctl.pairWaiting": "بانتظار نافذة اقتران المستقبل…", + "ctl.pairDone": "اكتمل اقتران R1 SE+. الماوس متصل.", + "ctl.pairDoneTitle": "تم اقتران المستقبل", + "ctl.pairDoneDetail": "R1 SE+ متصل عبر مستقبل 1K.", + "ctl.pairEnded": "انتهى الاقتران دون ماوس متصل (الحالة {s}).", + "ctl.pairExpired": "انتهت نافذة اقتران المستقبل قبل الاكتمال.", + "ctl.pairUnable": "تعذّر اقتران مستقبل R1 SE+.", + "ctl.pairFailed": "فشل الاقتران", + "ctl.cmdPower": "تغيير وضع الأداء", + "ctl.progPower": "جارٍ تغيير الوضع…", + "ctl.cmdRemap": "إعادة ربط زر {button}", + "ctl.progRemap": "جارٍ إعادة الربط…", + "ctl.cmdProfile": "تغيير الملف النشط", + "ctl.progProfile": "جارٍ تبديل الملف…", + "ctl.switchingLayer": "التبديل إلى {label}…", + "ctl.readingProfiles": "جارٍ قراءة الملفات المدمجة…", + "ctl.noProfiles": "لم يُبلّغ هذا الماوس عن ملفات مدمجة.", + "ctl.noNameField": "صيغة هذا الملف لا تحتوي على حقل اسم.", + "ctl.selectedMouse": "الماوس المحدد", + "ctl.autoSleep": "سكون تلقائي {v}", + "ctl.autoSleepOff": "السكون التلقائي معطّل", + "ctl.highestPref": "أعلى أداء {v}", + "sleep.never": "أبدًا", + "sleep.second1": "ثانية واحدة", + "sleep.seconds": "{n} ثانية", + "sleep.minute1": "دقيقة واحدة", + "sleep.minutes": "{n} دقيقة", + "sleep.hour1": "ساعة واحدة", + "sleep.hours": "{n} ساعة", + "prof.hostName": "المضيف", + "prof.liveDetail": "إعدادات مباشرة، غير مخزّنة على الماوس", + "prof.dpiSlots": "{n} فتحة DPI", + "prof.noSlots": "لا فتحات DPI", + "prof.profileName": "الملف {n}", + "prof.disabledTag": "معطّل", + "key.layerName": "الطبقة {n}", + "conn.wired": "سلكي", + "conn.wireless": "لاسلكي", + "light.noEffect": "بلا تأثير", + "light.speedN": "السرعة {n}", + "page.locale": "اللغة", + "page.en": "EN", + "page.pt": "PT", + "page.es": "ES", + "page.fr": "FR", + "page.de": "DE", + "page.zh": "中文", + "page.ja": "日本語", + "page.ko": "한국어", + "page.ru": "РУС", + "off.banner": "أنت غير متصل بالإنترنت. لن يُحدَّث أي شيء يحتاج إلى الشبكة.", + "land.supported": "الماوسات المدعومة", + "land.contribute": "ساهم", + "land.donate": "تبرّع", + "land.openApp": "افتح التطبيق", + "land.eyebrow": "مجاني · مفتوح المصدر · بلا برامج الشركات المصنّعة", + "land.hero": "تطبيق واحد لكل ماوس.", + "land.lead": "يعمل OpenMouse بالكامل في متصفحك عبر WebHID — DPI ومعدل الاستقصاء والأزرار وإضاءة RGB، لعشرات الماوسات المخصصة للألعاب، بلا حسابات أو تثبيتات أو إرسال أي بيانات.", + "land.checkMouse": "افحص ماوسك", + "land.f1t": "يعمل في متصفحك", + "land.f1b": "لا مثبّت ولا خدمة خلفية ولا حساب. وصّل ماوسك، افتح الصفحة، وستجده هناك عبر WebHID.", + "land.f2t": "عشرات الماوسات، تطبيق واحد", + "land.f2b": "واجهة متسقة واحدة عبر العلامات التجارية والطرازات، بدلًا من تطبيق منتفخ مختلف لكل مصنّع.", + "land.f3t": "مفتوح المصدر بالكامل", + "land.f3b": "كل تعريف يُهندس عكسيًا في العلن ويُراجع على GitHub — لا شيء يتصل بالخارج، ويمكنك قراءة ما يفعله بالضبط.", + "land.contribTitle": "ساعد في إضافة دعم مزيد من الماوسات", + "land.contribBody": "لا يدعم OpenMouse إلا ما هندسه المجتمع عكسيًا واختبره. إن لم يكن ماوسك مُدرَجًا بعد، يشرح دليل المساهمة ممارسات الهندسة العكسية الآمنة وكيف تترابط مستودعات التعريفات.", + "land.contribCta": "اقرأ دليل المساهمة", + "chk.back": "← العودة إلى الموقع", + "chk.overline": "تشخيصات HID", + "chk.heroBody": "افحص ماوسات الألعاب المتصلة لديك لترى الواجهات المتاحة عبر WebHID — وأيها يحتاج إلى تعريف أصلي.", + "chk.webhidOk": "WebHID متاح في هذا المتصفح — فحص الأجهزة مدعوم.", + "chk.webhidMissing": "WebHID غير متاح. استخدم Chrome أو Edge على سطح المكتب لتشغيل التشخيصات.", + "chk.scanning": "جارٍ الفحص…", + "chk.scanAgain": "إعادة الفحص", + "chk.scanBtn": "افحص بحثًا عن ماوسات", + "chk.scanNote": "ستظهر لك نافذة المتصفح لاختيار الأجهزة.", + "chk.waitPrompt": "بانتظار نافذة المتصفح…", + "chk.cancelled": "أُلغي الفحص أو لم يُحدَّد أي جهاز.", + "chk.noMatch": "لم تُحدَّد أجهزة مطابقة.", + "chk.testing": "جارٍ اختبار {n} جهاز…", + "chk.scanError": "خطأ في الفحص: {msg}", + "chk.resultsOne": "النتائج — جهاز واحد", + "chk.resultsMany": "النتائج — {n} جهاز", + "chk.shareDiscord": "شارك هذه النتائج مع فريق OpenMouse على Discord.", + "chk.copied": "تم النسخ!", + "chk.copyDiscord": "نسخ لـ Discord", + "chk.collections": "مجموعات HID", + "chk.txTest": "اختبار RAZER TX-ID", + "chk.responded": "✓ استجاب", + "chk.noResponse": "✗ لا استجابة", + "chk.openedOk": "فُتح الجهاز بنجاح", + "chk.couldNotOpen": "تعذّر الفتح", + "chk.mouseFallback": "ماوس {brand}", + "chk.noteBlocked": "دونجل HyperPolling — يحجب mouhid.sys تقنية WebHID على Windows", + "chk.noteFull": "واجهة المصنّع مع تقارير الميزات ظاهرة", + "chk.notePartial": "وُجدت واجهة المصنّع — تختلف رؤية تقارير الميزات حسب نظام التشغيل/المتصفح", + "chk.noteLimited": "واجهة الماوس القياسية فقط ظاهرة لـ WebHID", + "chk.noteUnknown": "لا مجموعات ظاهرة — قد تكون الواجهة محجوبة", + "chk.noteRazerOk": "بروتوكول Razer HID يستجيب — اجتاز اختبار TX-ID", + "chk.noteRazerPartial": "فُتح الجهاز لكن اختبار TX-ID لم يتلقَّ استجابة صالحة", + "chk.noteSkipped": "{brand} — الواجهة متاحة (تم تخطي الفحص للسلامة)", + "chk.noteReadable": "تقرير الميزات قابل للقراءة على واجهة المصنّع", + "chk.noteReadFailed": "واجهة المصنّع متاحة لكن قراءة تقرير الميزات فشلت", + "chk.noteBlockedOpen": "حجب نظام التشغيل/المتصفح الفتح: {msg}", + "chk.couldNotOpenDevice": "تعذّر فتح الجهاز", + "chk.unknownVid": "غير معروف (VID {vid})", + "supp.devices": "الأجهزة", + "supp.contribute": "ساهم", + "supp.support": "الدعم", + "supp.theme": "تبديل السمة", + "supp.title": "الأجهزة المدعومة", + "supp.sub": "أي ماوسات الألعاب تعمل مع OpenMouse — الطرازات المدعومة وطلبات المجتمع وحالة التعريفات في لمحة.", + "supp.search": "ابحث حسب العلامة أو الطراز أو البروتوكول…", + "supp.legend": "المفتاح", + "supp.brands": "العلامات التجارية", + "supp.supportedNum": "{n} مدعوم", + "supp.totalNum": "{n} متتبَّع إجمالًا", + "supp.supportedWord": "مدعوم", + "supp.totalWord": "متتبَّع إجمالًا", + "supp.oneDevice": "جهاز واحد", + "supp.manyDevices": "{n} جهاز", + "supp.noResults": "لا أجهزة تطابق بحثك.", + "supp.noFilters": "لا أجهزة تطابق هذه المرشحات.", + "supp.clearFilters": "مسح مرشحات العلامة والوسم", + "supp.thStatus": "الحالة", + "supp.thModel": "الطراز", + "supp.thNotes": "ملاحظات", + "supp.thVotes": "الأصوات", + "supp.oneRequest": "(طلب واحد)", + "supp.manyRequests": "({n} طلب)", + "supp.follow": "تابعنا على X", + "supp.source": "عرض المصدر", + "supp.stSupported": "مدعوم", + "supp.stPr": "طلب سحب معلّق", + "supp.stQuickwin": "فوز سريع", + "supp.stLikely": "يحتاج اختبارًا", + "supp.stDriver": "يحتاج تعريفًا", + "supp.stUnknown": "غير معروف", + "supp.stBridge": "يحتاج Bridge", + "supp.stPending": "مطلوب", + "supp.tabAll": "الكل", + "supp.legSupported": "مؤكَّد عمله مع تعريف مسجَّل", + "supp.legPr": "طلب سحب يضيف التعريف مفتوح", + "supp.legQuickwin": "البروتوكول مُنفَّذ — ينقص فقط إدخال PID/الإعداد", + "supp.legLikely": "التعريف يغطيه على الأرجح — يحتاج اختبار عتاد", + "supp.legDriver": "لا يوجد تعريف بعد", + "supp.legUnknown": "لم يُحدَّد البروتوكول بعد", + "supp.legBridge": "غير متوافق مع WebHID — يحتاج إلى رفيق OpenMouse Bridge", + "supp.legPending": "طلب مجتمعي مباشر", + "supp.kicker": "كتالوج الأجهزة", + "supp.allBrands": "كل العلامات", + "supp.showEverything": "إظهار الكل", + "supp.brandWord": "العلامة", + "supp.noBrands": "لا علامات مطابقة.", + "supp.searchBrands": "ابحث في العلامات…", + "supp.filterBrand": "تصفية حسب العلامة", + "supp.allTags": "كل الوسوم", + "supp.tagsWord": "الوسوم", + "supp.tagWord": "الوسم", + "supp.noTags": "لا وسوم مطابقة.", + "supp.searchTags": "ابحث في الوسوم…", + "supp.filterTag": "تصفية حسب الوسم", + "supp.legendTitle": "ماذا تعني الوسوم؟", + "don.devices": "الأجهزة", + "don.support": "الدعم", + "don.check": "فحص الماوس", + "don.contribute": "ساهم", + "don.stars": "{n} نجمة", + "don.eyebrow": "مجاني · مفتوح المصدر · من بناء المجتمع", + "don.heroA": "ادعم هذا", + "don.heroB": "المشروع", + "don.lead": "OpenMouse مجاني ومفتوح المصدر ومبني للمجتمع. إن جعل إعدادك أفضل، أو وفّر عليك برامج مغلقة منتفخة، فكّر في دعم العمل الذي يقف خلفه.", + "don.profileTitle": "مشروع OpenMouse", + "don.profileRole": "المشرفون والمساهمون", + "don.profileB1": "مرحبًا! نحن المطوّرون خلف OpenMouse — مكوّن ماوس مجاني ومفتوح المصدر. نستثمر وقتًا كبيرًا في ضبط ماوسات جديدة وكتابة تعريفات لاسلكية وسلكية وإبقاء المشروع خاليًا من برامج الشركات المنتفخة.", + "don.profileB2": "نقدّر دعم المجتمع ومساهمينا. تساعدنا الرعاية في تغطية العتاد والاختبار والبنية التحتية، وتتيح لنا مواصلة تحسين OpenMouse. نقدّر أي تبرّع تقديرًا كبيرًا!", + "don.thanks": "شكرًا لقراءتك!", + "don.optional": "التبرّع اختياري.", + "don.type": "نوع التبرّع", + "don.once": "لمرة واحدة", + "don.monthly": "شهري", + "don.amount": "مبلغ التبرّع (دولار)", + "don.custom": "مبلغ مخصّص", + "don.customPh": "أدخل مبلغًا", + "don.chargeOnce": "ستتبرّع بمبلغ {v} مرة واحدة، عبر GitHub Sponsors.", + "don.chargeMonthly": "ستتبرّع بمبلغ {v} كل شهر، عبر GitHub Sponsors.", + "don.submit": "تبرّع على GitHub Sponsors", + "don.skip": "لا شكرًا، تابع إلى التطبيق", + "don.contribEyebrow": "بحب من", + "don.contribTitle": "مساهمونا", + "don.stale": "عرض بيانات محفوظة — ", + "don.apiFail": "تعذّر الوصول إلى GitHub ({msg}). جارٍ إعادة المحاولة تلقائيًا.", + "don.contribs": "{n} مساهمة", + "don.contribOne": "مساهمة واحدة", + "don.seeAll": "شاهد الجميع على GitHub ↗", + "don.tagline": "مكوّن الماوس مفتوح المصدر ومتعدد المنصات.", + "don.pages": "الصفحات", + "don.home": "الرئيسية", + "don.contributeCol": "ساهم", + "don.reportIssue": "أبلغ عن مشكلة", + "don.discussions": "النقاشات", + "don.source": "الشيفرة المصدرية", + "don.community": "المجتمع", + "don.rights": "© {year} مشروع OpenMouse. جميع الحقوق محفوظة.", + "don.privacy": "سياسة الخصوصية", + "don.terms": "شروط الخدمة", + "don.created": "صُنع بـ❤️ بواسطة", + "don.theCommunity": "المجتمع", + "don.and": "و", + "don.contributors": "المساهمون", + "faq.title": "الأسئلة الشائعة", + "faq.q1": "هل OpenMouse مجاني؟", + "faq.a1": "نعم. OpenMouse مجاني ومفتوح المصدر، بلا حسابات أو اشتراكات أو مستويات مدفوعة.", + "faq.q2": "هل يرسل OpenMouse أيًا من بياناتي إلى مكان ما؟", + "faq.a2": "لا. يعمل OpenMouse بالكامل في متصفحك عبر WebHID — لا تتبع ولا خدمة خلفية ولا أي اتصال بالخارج. يمكنك قراءة ما يفعله بالضبط، لأن كل تعريف مفتوح المصدر.", + "faq.q3": "ما الماوسات المدعومة؟", + "faq.a3a": "عشرات ماوسات الألعاب عبر عدة علامات تجارية. اطّلع على", + "faq.a3b": "قائمة الماوسات المدعومة", + "faq.a3c": "للمجموعة الحالية — يعتمد الدعم على ما هندسه المجتمع عكسيًا واختبره حتى الآن.", + "faq.q4": "ما المتصفحات التي تعمل مع OpenMouse؟", + "faq.a4": "يحتاج OpenMouse إلى متصفح يدعم WebHID، لذا يعمل Chrome وEdge والمتصفحات الأخرى المبنية على Chromium. لا يدعم Firefox وSafari تقنية WebHID حاليًا.", + "faq.q5": "ماوسي غير مُدرَج — هل يمكنني إضافة دعم له؟", + "faq.a5a": "نعم.", + "faq.a5b": "دليل المساهمة", + "faq.a5c": "يشرح ممارسات الهندسة العكسية الآمنة وكيف تترابط مستودعات التعريفات.", + "faq.q6": "هل أحتاج إلى تثبيت أي شيء؟", + "faq.a6": "لا مثبّت ولا تعريف ولا تطبيق خلفي. وصّل ماوسك، افتح الصفحة في متصفح مدعوم، وستجده هناك.", + "faq.q7": "هل يُخزَّن إعدادي في أي مكان؟", + "faq.a7": "تُخزَّن إعداداتك محليًا فقط، في متصفحك — لا حساب ولا مزامنة سحابية.", + "faq.q8": "كيف يمكنني دعم المشروع؟", + "faq.a8a": "ضع نجمة للمشروع على", + "faq.a8b": "انضم إلى", + "faq.a8c": "للمساعدة في تمويل عتاد الاختبار والتطوير.", + "adm.title": "إدارة OpenMouse", + "adm.password": "كلمة المرور", + "adm.checking": "جارٍ التحقق…", + "adm.signin": "تسجيل الدخول", + "adm.loginFailed": "فشل تسجيل الدخول.", + "adm.networkError": "خطأ في الشبكة.", + "adm.signout": "تسجيل الخروج", + "adm.loading": "جارٍ التحميل…", + "adm.updated": "حُدِّث {time}", + "adm.live": "مباشر الآن", + "adm.peak": "الذروة على الإطلاق", + "adm.ofPeak": "{n}% من الذروة", + "adm.dailyViews": "المشاهدات اليومية", + "adm.lastAvg": "آخر {n} يوم · المتوسط {avg}", + "adm.viewsDays": "المشاهدات، {n} يوم", + "adm.avgDay": "المتوسط / يوم", + "adm.medianAdj": "قريب من الوسيط", + "adm.bestDay": "أفضل يوم", + "adm.bestWeekday": "أفضل يوم من الأسبوع", + "adm.byViews": "حسب إجمالي المشاهدات", + "adm.regions": "المناطق", + "adm.regionsSub": "{views} مشاهدة · {n} منطقة", + "adm.country": "الدولة", + "adm.views": "المشاهدات", + "adm.mice": "أكثر الماوسات استخدامًا", + "adm.miceSub": "{n} اتصال · {m} طراز", + "adm.model": "الطراز", + "adm.uses": "الاستخدامات", + "adm.traffic": "الحركة حسب يوم الأسبوع", + "adm.trafficSub": "المشاهدات يوميًا، حسب الأسبوع", + "adm.session": "تفاصيل الجلسة", + "adm.sessionSub": "مستخلص من النطاق الحالي", + "adm.trackedSince": "متتبَّع منذ", + "adm.daysTracked": "الأيام المتتبَّعة", + "adm.quietest": "أهدأ يوم", + "adm.trend7d": "اتجاه 7 أيام", + "adm.noHistory": "لا سجل بعد.", + "adm.noData": "لا بيانات بعد.", + "adm.unknown": "غير معروف", + "adm.loadFail": "تعذّر تحميل الإحصاءات.", + "adm.viewsWord": "مشاهدات", + "adm.peakConcurrent": "ذروة متزامنة {n}", + "tab.unavailable": "عناصر تحكم {tab} غير متاحة لهذا الجهاز.", + "tab.chooseAnother": "اختر تبويبًا آخر لمتابعة ضبط الجهاز.", +}; diff --git a/src/i18n-de.ts b/src/i18n-de.ts index c09fe825..5938c870 100644 --- a/src/i18n-de.ts +++ b/src/i18n-de.ts @@ -165,6 +165,7 @@ export const de: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "BEWEGUNG", "set.animations": "Animationen", "set.animationsBody": "Aktiviert Oberflächenübergänge und animierte Zustandsänderungen.", diff --git a/src/i18n-es.ts b/src/i18n-es.ts index 23848eec..d4e960a9 100644 --- a/src/i18n-es.ts +++ b/src/i18n-es.ts @@ -165,6 +165,7 @@ export const es: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "MOVIMIENTO", "set.animations": "Animaciones", "set.animationsBody": "Activa las transiciones de la interfaz y los cambios de estado animados.", diff --git a/src/i18n-fr.ts b/src/i18n-fr.ts index a0a32dec..e041aed8 100644 --- a/src/i18n-fr.ts +++ b/src/i18n-fr.ts @@ -165,6 +165,7 @@ export const fr: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "MOUVEMENT", "set.animations": "Animations", "set.animationsBody": "Active les transitions de l'interface et les changements d'état animés.", diff --git a/src/i18n-ja.ts b/src/i18n-ja.ts index 93c853b4..0b45cfb7 100644 --- a/src/i18n-ja.ts +++ b/src/i18n-ja.ts @@ -165,6 +165,7 @@ export const ja: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "モーション", "set.animations": "アニメーション", "set.animationsBody": "インターフェースの遷移とアニメーション付き状態変化を有効にします。", diff --git a/src/i18n-ko.ts b/src/i18n-ko.ts index 91a50419..fd93d2c0 100644 --- a/src/i18n-ko.ts +++ b/src/i18n-ko.ts @@ -165,6 +165,7 @@ export const ko: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "모션", "set.animations": "애니메이션", "set.animationsBody": "인터페이스 전환 효과와 애니메이션 상태 변화를 활성화합니다.", diff --git a/src/i18n-pt.ts b/src/i18n-pt.ts index cb2c7c9d..c8de6066 100644 --- a/src/i18n-pt.ts +++ b/src/i18n-pt.ts @@ -165,6 +165,7 @@ export const pt: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "MOVIMENTO", "set.animations": "Animações", "set.animationsBody": "Ativa transições da interface e mudanças animadas de estado.", diff --git a/src/i18n-ru.ts b/src/i18n-ru.ts index 7d3b4677..5169f614 100644 --- a/src/i18n-ru.ts +++ b/src/i18n-ru.ts @@ -164,6 +164,7 @@ export const ru: Record = { "set.japanese": "日本語", "set.korean": "한국어", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "АНИМАЦИЯ", "set.animations": "Анимации", "set.animationsBody": "Включить переходы интерфейса и анимацию смены состояний.", diff --git a/src/i18n-vi.ts b/src/i18n-vi.ts index 91c87edb..8bb34c32 100644 --- a/src/i18n-vi.ts +++ b/src/i18n-vi.ts @@ -103,6 +103,7 @@ export const vi: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "CHUYỂN ĐỘNG", "set.animations": "Hiệu ứng chuyển động", "set.animationsBody": "Bật hiệu ứng chuyển giao diện và thay đổi trạng thái có chuyển động.", diff --git a/src/i18n-zh.ts b/src/i18n-zh.ts index 2693f7c0..4e84af6f 100644 --- a/src/i18n-zh.ts +++ b/src/i18n-zh.ts @@ -165,6 +165,7 @@ export const zh: Record = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "动效", "set.animations": "动画", "set.animationsBody": "启用界面过渡效果和动态状态变化。", diff --git a/src/i18n.ts b/src/i18n.ts index 4c5910ca..a6e272fe 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -168,6 +168,7 @@ const en = { "set.korean": "한국어", "set.russian": "Русский", "set.vietnamese": "Tiếng Việt", + "set.arabic" : "العربيّة", "set.motion": "MOTION", "set.animations": "Animations", "set.animationsBody": "Enable interface transitions and animated state changes.", @@ -1074,6 +1075,7 @@ export const LOCALE_NAME_KEYS: ReadonlyArray<[InterfaceLocale, I18nKey]> = [ ["ko", "set.korean"], ["ru", "set.russian"], ["vi", "set.vietnamese"], + ["ar", "set.arabic"], ]; /** Non-English tables load on demand so the initial bundle ships English @@ -1096,6 +1098,7 @@ const LOCALE_LOADERS: Record, () => Promise import("./i18n-ko.ts").then((m) => m.ko), ru: () => import("./i18n-ru.ts").then((m) => m.ru), vi: () => import("./i18n-vi.ts").then((m) => m.vi), + ar: () => import("./i18n-ar.ts").then((m) => m.ar), }; export function ensureLocale(locale: InterfaceLocale): Promise { diff --git a/src/interface-preferences.ts b/src/interface-preferences.ts index 7a900279..44dfb06a 100644 --- a/src/interface-preferences.ts +++ b/src/interface-preferences.ts @@ -13,7 +13,7 @@ export type InterfaceTheme = | "Light"; export type InterfaceColorMode = "Light" | "Dark" | "System"; export type InterfaceLocale = - "en" | "pt" | "es" | "fr" | "de" | "zh" | "ja" | "ko" | "ru" | "vi"; + "en" | "pt" | "es" | "fr" | "de" | "zh" | "ja" | "ko" | "ru" | "vi" | "ar"; /** Every supported locale, in picker order. Each entry's `match` decides which `navigator.language` prefixes resolve to it on first run — checked @@ -29,6 +29,7 @@ export const LOCALES: ReadonlyArray<{ code: InterfaceLocale; match: readonly str { code: "ru", match: ["ru"] }, { code: "vi", match: ["vi"] }, { code: "en", match: ["en"] }, + { code: "ar", match: ["ar"] }, ]; export interface InterfacePreferences {