feat: add Russian localization and independent STT languages
CI / Typecheck, tests, lint (push) Canceled after 0s
CI / Standalone (mac-arm64) (push) Canceled after 0s
CI / Standalone (linux-x64) (push) Canceled after 0s
CI / Standalone (windows-x64) (push) Canceled after 0s

This commit is contained in:
2026-10-25 17:18:44 +03:00
parent b277ee3e0f
commit a50ca4b867
93 changed files with 3261 additions and 904 deletions
+13 -7
View File
@@ -1,4 +1,6 @@
"use client";
import { useI18n } from "@/components/i18n";
/**
* Thin settings primitives (settings-app wave): the control kit the
* settings dialog needs. Sections → groups → rows; everything SSRs clean
@@ -25,10 +27,11 @@ export function SettingsSection({
children: React.ReactNode;
testid?: string;
}) {
const { t } = useI18n();
return (
<section data-testid={testid} className="px-6 py-5">
<h2 className="text-sm uppercase tracking-widest text-foreground">{title}</h2>
{hint && <p className="mt-1 text-xs text-muted-foreground">{hint}</p>}
<h2 className="text-sm uppercase tracking-widest text-foreground">{t(title)}</h2>
{hint && <p className="mt-1 text-xs text-muted-foreground">{t(hint)}</p>}
<div className="mt-4 flex flex-col gap-3">{children}</div>
</section>
);
@@ -45,9 +48,10 @@ export function SettingsGroup({
children: React.ReactNode;
testid?: string;
}) {
const { t } = useI18n();
return (
<div data-testid={testid} className="mt-5 border-t border-border/60 pt-4 first:mt-0 first:border-t-0 first:pt-0">
<p className="mb-2 text-xs uppercase tracking-widest text-muted-foreground">{label}</p>
<p className="mb-2 text-xs uppercase tracking-widest text-muted-foreground">{t(label)}</p>
<div className="flex flex-col gap-2">{children}</div>
</div>
);
@@ -70,11 +74,12 @@ export function SettingsRow({
* control is inert (parent off, no file yet, …). */
dimmed?: boolean;
}) {
const { t } = useI18n();
return (
<div data-testid={testid} className={cn("flex items-center justify-between gap-4 py-1", dimmed && "opacity-40")}>
<span className="min-w-0">
<span className="block font-prose text-(length:--lh-prose-size)">{label}</span>
{hint && <span className="block text-xs text-muted-foreground/70">{hint}</span>}
<span className="block font-prose text-(length:--lh-prose-size)">{t(label)}</span>
{hint && <span className="block text-xs text-muted-foreground/70">{t(hint)}</span>}
</span>
{children}
</div>
@@ -102,13 +107,14 @@ export function Toggle({
/** Optional native tooltip on the switch. */
title?: string;
}) {
const { t } = useI18n();
return (
<button
type="button"
role="switch"
aria-checked={checked}
aria-label={label}
{...(title ? { title } : {})}
aria-label={t(label)}
{...(title ? { title: t(title) } : {})}
disabled={disabled}
data-testid={`toggle-${label.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`}
onClick={() => onChange(!checked)}