diff --git a/scripts/i18n/scan-hardcoded.ts b/scripts/i18n/scan-hardcoded.ts index 8b09b8b3..7dc5711a 100755 --- a/scripts/i18n/scan-hardcoded.ts +++ b/scripts/i18n/scan-hardcoded.ts @@ -85,6 +85,10 @@ function isHuman(value: string | undefined): value is string { // pattern because it starts with a capital and interpolates; excluded here because an auth scheme // followed by a credential is never something a person reads. if (/^(Bearer|Basic|Token|Digest)\s/.test(s)) return false; + // A single token carrying digits AND punctuation is a name, not a sentence — the OCR section's + // placeholder is the example model file `Qwen2.5-VL-7B-Instruct-q4_k_m.gguf`. Prose has spaces; the + // whitespace test is what keeps this from eating real one-word labels, which never look like this. + if (!/\s/.test(s) && /\d/.test(s) && /[-_.]/.test(s)) return false; // Tailwind, reached via a `description:`/`label:` key in a classNames object — e.g. sonner's // `description: 'group-[.toast]:text-muted-foreground'`. // diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/AIHarnessesSection.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/AIHarnessesSection.tsx index e3ea139c..d81e6892 100644 --- a/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/AIHarnessesSection.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/AIHarnessesSection.tsx @@ -13,6 +13,7 @@ import { CommandList, } from '@/components/ui/command'; import { useClient } from 'hooks/useClient'; +import { useTranslation } from '@/lib/i18n'; type VersionInfo = { version: string | null; path: string | null; globalPath: string | null }; type StoredApiKeys = { keys: { provider: string; value: string }[] }; @@ -38,6 +39,7 @@ type ChatProvider = { key: string; providerId: string }; type ProbeState = { step: 'url' } | { step: 'probing' } | { step: 'auth'; probe: ProbeResult } | { step: 'saving' }; export const AIHarnessesSection = () => { + const { t, tUi } = useTranslation(); const client = useClient(); const queryClient = useQueryClient(); const [installing, setInstalling] = useState(false); @@ -168,7 +170,7 @@ export const AIHarnessesSection = () => { // No auth needed — save directly await saveLocalProvider(result, auth); } catch { - toast.error('Failed to connect'); + toast.error(t('settings.server.harnesses.connectFailed')); setProbeState({ step: 'url' }); } }; @@ -192,13 +194,13 @@ export const AIHarnessesSection = () => { return; } if (result.needsAuth) { - toast.error('Authentication failed'); + toast.error(t('settings.server.harnesses.authFailed')); setProbeState({ step: 'auth', probe }); return; } await saveLocalProvider(result, auth); } catch { - toast.error('Failed to connect'); + toast.error(t('settings.server.harnesses.connectFailed')); setProbeState({ step: 'auth', probe }); } }; @@ -218,10 +220,10 @@ export const AIHarnessesSection = () => { queryClient.invalidateQueries({ queryKey: ['CHAT_PROVIDERS_LOCAL_PROVIDERS'] }); queryClient.invalidateQueries({ queryKey: ['CHAT_PROVIDERS_LOCAL_HEALTH'] }); queryClient.invalidateQueries({ queryKey: ['CHAT_MODELS'] }); - toast.success(`Connected to ${probe.name}`); + toast.success(t('settings.server.harnesses.connectedTo', { name: probe.name ?? '' })); resetLocalForm(); } catch { - toast.error('Failed to save provider'); + toast.error(t('settings.server.harnesses.saveFailed')); setProbeState({ step: 'url' }); } }; @@ -254,9 +256,9 @@ export const AIHarnessesSection = () => {
Pulls this server’s repository. Refuses if anything is uncommitted or the checkout has diverged — it will never discard local work.
-- Changes nothing that is running. Whatever the update needs is listed here, and stays your call. -
+{t('settings.server.maintenance.updateHint')}
{pulled && (@@ -163,7 +163,7 @@ export const MaintenanceSection = () => {
Picks up changes to the interface. Nothing restarts, no connection drops, and this chat keeps its session — refresh the page when it finishes. @@ -178,7 +178,7 @@ export const MaintenanceSection = () => {
The only way to load changed server code — Bun caches modules for the life of the process, so rebuilding cannot see an edit to the backend. diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/OCRSection.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/OCRSection.tsx index 440f61a6..f2d5e16d 100644 --- a/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/OCRSection.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Settings/ServerSettings/OCRSection.tsx @@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { useClient } from 'hooks/useClient'; +import { useTranslation } from '@/lib/i18n'; type OcrConfig = { url: string; @@ -15,6 +16,7 @@ type OcrConfig = { }; export const OCRSection = () => { + const { t } = useTranslation(); const client = useClient(); const queryClient = useQueryClient(); @@ -59,9 +61,9 @@ export const OCRSection = () => { try { await client.put('/server-settings/ocr', { url, model }); queryClient.invalidateQueries({ queryKey: ['OCR_CONFIG'] }); - toast.success('OCR settings saved'); + toast.success(t('settings.server.ocr.saved')); } catch { - toast.error('Failed to save OCR settings'); + toast.error(t('settings.server.ocr.saveFailed')); } finally { setIsSaving(false); } @@ -75,7 +77,7 @@ export const OCRSection = () => { if (res.error) { toast.error(res.error); } else { - toast.success('Vision model server is reachable'); + toast.success(t('settings.server.ocr.reachable')); } } catch (err: unknown) { const raw = (err as { message?: string })?.message; @@ -91,12 +93,12 @@ export const OCRSection = () => { } }; - if (isLoading) return
Loading...
; + if (isLoading) return{t('settings.server.loading')}
; return (