fix: translate the themes settings panel

This commit is contained in:
Linus Rath
2026-09-26 12:54:30 +02:00
parent 6d54b2e851
commit 3668618c2d
30 changed files with 345 additions and 16 deletions
+12 -8
View File
@@ -1,6 +1,7 @@
'use client';
import { useEffect, useState } from 'react';
import { useTranslations } from 'next-intl';
import { useThemeStore } from '@/stores/theme-store';
import { SettingsSection } from './settings-section';
import { cn } from '@/lib/utils';
@@ -9,6 +10,7 @@ import { toast } from '@/stores/toast-store';
import { usePolicyStore } from '@/stores/policy-store';
export function ThemesSettings() {
const t = useTranslations('settings.themes');
const { installedThemes, activeThemeId, activateTheme } = useThemeStore();
const { isThemeDisabled, getThemePolicy, getForcedThemeId, isThemeForceEnabled } = usePolicyStore();
const themePolicy = getThemePolicy();
@@ -53,20 +55,20 @@ export function ThemesSettings() {
if (forcedThemeId && id !== forcedThemeId) {
const forcedTheme = installedThemes.find((theme) => theme.id === forcedThemeId);
toast.info(`Theme "${forcedTheme?.name ?? 'Admin theme'}" is forced by admin and cannot be changed`);
toast.info(t('forced', { name: forcedTheme?.name ?? t('admin_theme') }));
return;
}
activateTheme(id);
toast.success(id ? 'Theme activated' : 'Default theme restored');
toast.success(id ? t('activated') : t('default_restored'));
};
return (
<SettingsSection title="Themes" description="Choose from themes deployed by your administrator and built-in presets.">
<SettingsSection title={t('title')} description={t('description')}>
{forcedThemeId && (
<div className="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800 dark:border-amber-900/40 dark:bg-amber-950/20 dark:text-amber-300">
Theme selection is locked by an administrator.
{t('locked')}
</div>
)}
@@ -74,7 +76,7 @@ export function ThemesSettings() {
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{/* Default theme card */}
<ThemeCard
name="Default"
name={t('default_name')}
author="Bulwark"
isDefaultTheme
variants={['light', 'dark']}
@@ -131,6 +133,8 @@ interface ThemeCardProps {
}
function ThemeCard({ name, author, preview, css, isDark, isDefaultTheme, isActive, isDefault, isForceEnabled, disabled, variants, onActivate }: ThemeCardProps) {
const t = useTranslations('settings.themes');
const tVariant = useTranslations('settings.appearance.theme');
const colors = resolveThemeColors({ css, variants, isDark: !!isDark, isDefaultTheme: !!isDefaultTheme });
return (
<div data-search-label={name} className="relative">
@@ -162,12 +166,12 @@ function ThemeCard({ name, author, preview, css, isDark, isDefaultTheme, isActiv
<span className="text-sm font-medium text-foreground truncate">{name}</span>
<div className="flex items-center gap-1 flex-shrink-0">
{isForceEnabled && (
<span className="text-[10px] px-1.5 py-0.5 rounded bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 font-medium flex items-center gap-0.5" title="Admin enforced">
<span className="text-[10px] px-1.5 py-0.5 rounded bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 font-medium flex items-center gap-0.5" title={t('admin_enforced')}>
<Lock className="w-2.5 h-2.5" />
</span>
)}
{isDefault && (
<span className="text-[10px] px-1.5 py-0.5 rounded bg-primary/10 text-primary font-medium">Default</span>
<span className="text-[10px] px-1.5 py-0.5 rounded bg-primary/10 text-primary font-medium">{t('default_badge')}</span>
)}
{isActive && <Check className="w-4 h-4 text-primary" />}
</div>
@@ -177,7 +181,7 @@ function ThemeCard({ name, author, preview, css, isDark, isDefaultTheme, isActiv
<div className="flex gap-1 mt-1">
{variants.map(v => (
<span key={v} className="text-[10px] px-1.5 py-0.5 rounded bg-muted text-muted-foreground">
{v}
{tVariant(v)}
</span>
))}
</div>
+1 -1
View File
@@ -128,7 +128,7 @@ export const tabSearchPaths: Record<Tab, string[]> = {
protocol_handlers: ['protocol_handlers'],
sidebar_apps: ['settings.sidebar_apps', 'sidebar_apps'],
about_data: ['settings.advanced'],
themes: [],
themes: ['settings.themes'],
plugins: [],
debug: ['settings.advanced'],
};
+12
View File
@@ -938,6 +938,18 @@
"about_data": "حول والبيانات",
"debug": "التصحيح"
},
"themes": {
"title": "السمات",
"description": "اختر من بين السمات التي نشرها المسؤول والإعدادات المسبقة المضمنة.",
"locked": "اختيار السمة مقفل من قِبل المسؤول.",
"forced": "السمة «{name}» مفروضة من قِبل المسؤول ولا يمكن تغييرها",
"admin_theme": "سمة المسؤول",
"activated": "تم تفعيل السمة",
"default_restored": "تمت استعادة السمة الافتراضية",
"default_name": "الافتراضية",
"default_badge": "افتراضية",
"admin_enforced": "مفروضة من المسؤول"
},
"tab_groups": {
"general": "عام",
"account": "الحساب والهوية",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "Quant a i dades",
"debug": "Depuració"
},
"themes": {
"title": "Temes",
"description": "Trieu entre els temes desplegats pel vostre administrador i els predefinits integrats.",
"locked": "La selecció de tema està bloquejada per un administrador.",
"forced": "El tema «{name}» l'imposa l'administrador i no es pot canviar",
"admin_theme": "Tema de l'administrador",
"activated": "Tema activat",
"default_restored": "S'ha restaurat el tema predeterminat",
"default_name": "Predeterminat",
"default_badge": "Predeterminat",
"admin_enforced": "Imposat per l'administrador"
},
"tab_groups": {
"general": "General",
"account": "Compte i identitat",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Info a data",
"debug": "Ladění"
},
"themes": {
"title": "Motivy",
"description": "Vyberte si z motivů nasazených vaším správcem a z vestavěných předvoleb.",
"locked": "Výběr motivu je uzamčen správcem.",
"forced": "Motiv „{name}“ je vynucen správcem a nelze jej změnit",
"admin_theme": "Motiv správce",
"activated": "Motiv aktivován",
"default_restored": "Výchozí motiv obnoven",
"default_name": "Výchozí",
"default_badge": "Výchozí",
"admin_enforced": "Vynuceno správcem"
},
"tab_groups": {
"general": "Obecné",
"account": "Účet a identita",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "Om & data",
"debug": "Debug"
},
"themes": {
"title": "Temaer",
"description": "Vælg mellem temaer, som din administrator har udrullet, og indbyggede forudindstillinger.",
"locked": "Temavalget er låst af en administrator.",
"forced": "Temaet »{name}« er gennemtvunget af administratoren og kan ikke ændres",
"admin_theme": "Administratortema",
"activated": "Tema aktiveret",
"default_restored": "Standardtema gendannet",
"default_name": "Standard",
"default_badge": "Standard",
"admin_enforced": "Gennemtvunget af administrator"
},
"tab_groups": {
"general": "Generelt",
"account": "Konto & identitet",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Über & Daten",
"debug": "Debug"
},
"themes": {
"title": "Designs",
"description": "Wählen Sie aus den von Ihrem Administrator bereitgestellten Designs und den integrierten Vorlagen.",
"locked": "Die Designauswahl wurde von einem Administrator gesperrt.",
"forced": "Das Design „{name}“ wird vom Administrator vorgegeben und kann nicht geändert werden",
"admin_theme": "Administrator-Design",
"activated": "Design aktiviert",
"default_restored": "Standarddesign wiederhergestellt",
"default_name": "Standard",
"default_badge": "Standard",
"admin_enforced": "Vom Administrator erzwungen"
},
"tab_groups": {
"general": "Allgemein",
"account": "Konto & Identität",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "About & Data",
"debug": "Debug"
},
"themes": {
"title": "Themes",
"description": "Choose from themes deployed by your administrator and built-in presets.",
"locked": "Theme selection is locked by an administrator.",
"forced": "Theme \"{name}\" is forced by admin and cannot be changed",
"admin_theme": "Admin theme",
"activated": "Theme activated",
"default_restored": "Default theme restored",
"default_name": "Default",
"default_badge": "Default",
"admin_enforced": "Admin enforced"
},
"tab_groups": {
"general": "General",
"account": "Account & Identity",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Acerca de y datos",
"debug": "Depuración"
},
"themes": {
"title": "Temas",
"description": "Elija entre los temas implementados por su administrador y los ajustes preestablecidos integrados.",
"locked": "La selección de tema está bloqueada por un administrador.",
"forced": "El tema «{name}» lo impone el administrador y no se puede cambiar",
"admin_theme": "Tema del administrador",
"activated": "Tema activado",
"default_restored": "Tema predeterminado restaurado",
"default_name": "Predeterminado",
"default_badge": "Predeterminado",
"admin_enforced": "Impuesto por el administrador"
},
"tab_groups": {
"general": "General",
"account": "Cuenta e identidad",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "درباره و داده‌ها",
"debug": "اشکال‌زدایی"
},
"themes": {
"title": "پوسته‌ها",
"description": "از میان پوسته‌هایی که مدیر شما منتشر کرده و پیش‌تنظیم‌های داخلی انتخاب کنید.",
"locked": "انتخاب پوسته توسط مدیر قفل شده است.",
"forced": "پوسته «{name}» توسط مدیر اجباری شده و قابل تغییر نیست",
"admin_theme": "پوسته مدیر",
"activated": "پوسته فعال شد",
"default_restored": "پوسته پیش‌فرض بازیابی شد",
"default_name": "پیش‌فرض",
"default_badge": "پیش‌فرض",
"admin_enforced": "اجباری از سوی مدیر"
},
"tab_groups": {
"general": "عمومی",
"account": "حساب و هویت",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "À propos et données",
"debug": "Débogage"
},
"themes": {
"title": "Thèmes",
"description": "Choisissez parmi les thèmes déployés par votre administrateur et les préréglages intégrés.",
"locked": "La sélection du thème est verrouillée par un administrateur.",
"forced": "Le thème « {name} » est imposé par l'administrateur et ne peut pas être modifié",
"admin_theme": "Thème de l'administrateur",
"activated": "Thème activé",
"default_restored": "Thème par défaut restauré",
"default_name": "Par défaut",
"default_badge": "Par défaut",
"admin_enforced": "Imposé par l'administrateur"
},
"tab_groups": {
"general": "Général",
"account": "Compte & Identité",
+12
View File
@@ -900,6 +900,18 @@
"about_data": "בערך וגדול",
"debug": "ניפוי שגיאות"
},
"themes": {
"title": "ערכות נושא",
"description": "בחר מתוך ערכות הנושא שפרס מנהל המערכת ומתוך ערכות מובנות.",
"locked": "בחירת ערכת הנושא ננעלה על ידי מנהל המערכת.",
"forced": "ערכת הנושא \"{name}\" נאכפת על ידי מנהל המערכת ולא ניתן לשנות אותה",
"admin_theme": "ערכת נושא של המנהל",
"activated": "ערכת הנושא הופעלה",
"default_restored": "ערכת הנושא המוגדרת כברירת מחדל שוחזרה",
"default_name": "ברירת מחדל",
"default_badge": "ברירת מחדל",
"admin_enforced": "נאכף על ידי המנהל"
},
"tab_groups": {
"general": "כללי",
"account": "חשבון וזהות",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "Névjegy és adatok",
"debug": "Hibakeresés"
},
"themes": {
"title": "Témák",
"description": "Válassz a rendszergazda által telepített témák és a beépített előbeállítások közül.",
"locked": "A témaválasztást egy rendszergazda zárolta.",
"forced": "A(z) „{name}” témát a rendszergazda írja elő, ezért nem módosítható",
"admin_theme": "Rendszergazdai téma",
"activated": "Téma aktiválva",
"default_restored": "Alapértelmezett téma visszaállítva",
"default_name": "Alapértelmezett",
"default_badge": "Alapértelmezett",
"admin_enforced": "Rendszergazda által előírva"
},
"tab_groups": {
"general": "Általános",
"account": "Fiók és azonosság",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Informazioni e dati",
"debug": "Debug"
},
"themes": {
"title": "Temi",
"description": "Scegli tra i temi distribuiti dal tuo amministratore e i preset integrati.",
"locked": "La selezione del tema è bloccata da un amministratore.",
"forced": "Il tema “{name}” è imposto dall'amministratore e non può essere modificato",
"admin_theme": "Tema dell'amministratore",
"activated": "Tema attivato",
"default_restored": "Tema predefinito ripristinato",
"default_name": "Predefinito",
"default_badge": "Predefinito",
"admin_enforced": "Imposto dall'amministratore"
},
"tab_groups": {
"general": "Generale",
"account": "Account e identità",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "情報とデータ",
"debug": "デバッグ"
},
"themes": {
"title": "テーマ",
"description": "管理者が配布したテーマと組み込みのプリセットから選択",
"locked": "テーマの選択は管理者によってロックされています。",
"forced": "テーマ「{name}」は管理者によって固定されているため、変更できません",
"admin_theme": "管理者テーマ",
"activated": "テーマを適用しました",
"default_restored": "デフォルトのテーマに戻しました",
"default_name": "デフォルト",
"default_badge": "デフォルト",
"admin_enforced": "管理者により固定"
},
"tab_groups": {
"general": "一般",
"account": "アカウントと身元",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "정보 및 데이터",
"debug": "디버그"
},
"themes": {
"title": "테마",
"description": "관리자가 배포한 테마와 기본 제공 프리셋 중에서 선택해 주세요.",
"locked": "관리자가 테마 선택을 잠갔습니다.",
"forced": "테마 “{name}”은(는) 관리자가 지정했으므로 변경할 수 없습니다",
"admin_theme": "관리자 테마",
"activated": "테마가 적용되었습니다",
"default_restored": "기본 테마로 복원되었습니다",
"default_name": "기본",
"default_badge": "기본",
"admin_enforced": "관리자가 지정함"
},
"tab_groups": {
"general": "일반",
"account": "계정 및 인증",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Par un dati",
"debug": "Atkļūdošana"
},
"themes": {
"title": "Tēmas",
"description": "Izvēlieties kādu no administratora izvietotajām tēmām vai iebūvētajiem priekšiestatījumiem.",
"locked": "Tēmas izvēli ir bloķējis administrators.",
"forced": "Tēmu “{name}” ir noteicis administrators, un to nevar mainīt",
"admin_theme": "Administratora tēma",
"activated": "Tēma aktivizēta",
"default_restored": "Noklusējuma tēma atjaunota",
"default_name": "Noklusējuma",
"default_badge": "Noklusējuma",
"admin_enforced": "Noteicis administrators"
},
"tab_groups": {
"general": "Vispārīgi",
"account": "Konts un identitāte",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "Тухай & Өгөгдөл",
"debug": "Дабаглах"
},
"themes": {
"title": "Сэдэв",
"description": "Администраторын байршуулсан сэдвүүд болон суулгасан бэлэн загваруудаас сонго.",
"locked": "Сэдэв сонголтыг администратор түгжсэн байна.",
"forced": "«{name}» сэдвийг администратор тогтоосон тул өөрчлөх боломжгүй",
"admin_theme": "Администраторын сэдэв",
"activated": "Сэдэв идэвхжлээ",
"default_restored": "Үндсэн сэдвийг сэргээлээ",
"default_name": "Үндсэн",
"default_badge": "Үндсэн",
"admin_enforced": "Администратор тогтоосон"
},
"tab_groups": {
"general": "Ерөнхий",
"account": "Бүртгэл & Иргэний үнэмлэх",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "Om programmet og data",
"debug": "Feilsøking"
},
"themes": {
"title": "Temaer",
"description": "Velg blant temaer som administratoren din har gjort tilgjengelige, og innebygde forhåndsinnstillinger.",
"locked": "Temavalget er låst av en administrator.",
"forced": "Temaet «{name}» er påtvunget av administratoren og kan ikke endres",
"admin_theme": "Administratortema",
"activated": "Tema aktivert",
"default_restored": "Standardtema gjenopprettet",
"default_name": "Standard",
"default_badge": "Standard",
"admin_enforced": "Påtvunget av administrator"
},
"tab_groups": {
"general": "Generelt",
"account": "Konto og identitet",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Over en gegevens",
"debug": "Debuggen"
},
"themes": {
"title": "Thema's",
"description": "Kies uit thema's die je beheerder heeft uitgerold en ingebouwde voorinstellingen.",
"locked": "Themakeuze is vergrendeld door een beheerder.",
"forced": "Thema “{name}” is afgedwongen door de beheerder en kan niet worden gewijzigd",
"admin_theme": "Beheerdersthema",
"activated": "Thema geactiveerd",
"default_restored": "Standaardthema hersteld",
"default_name": "Standaard",
"default_badge": "Standaard",
"admin_enforced": "Afgedwongen door beheerder"
},
"tab_groups": {
"general": "Algemeen",
"account": "Account & identiteit",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "O programie i dane",
"debug": "Debugowanie"
},
"themes": {
"title": "Motywy",
"description": "Wybierz jeden z motywów udostępnionych przez administratora lub jeden z wbudowanych.",
"locked": "Wybór motywu został zablokowany przez administratora.",
"forced": "Motyw „{name}” jest wymuszony przez administratora i nie można go zmienić",
"admin_theme": "Motyw administratora",
"activated": "Motyw aktywowany",
"default_restored": "Przywrócono domyślny motyw",
"default_name": "Domyślny",
"default_badge": "Domyślny",
"admin_enforced": "Wymuszony przez administratora"
},
"tab_groups": {
"general": "Ogólne",
"account": "Konto i tożsamość",
+13 -1
View File
@@ -935,6 +935,18 @@
"about_data": "Sobre e dados",
"debug": "Depuração"
},
"themes": {
"title": "Temas",
"description": "Escolha entre os temas disponibilizados pelo seu administrador e as predefinições integradas.",
"locked": "A seleção de tema está bloqueada por um administrador.",
"forced": "O tema “{name}” é imposto pelo administrador e não pode ser alterado",
"admin_theme": "Tema do administrador",
"activated": "Tema ativado",
"default_restored": "Tema padrão restaurado",
"default_name": "Padrão",
"default_badge": "Padrão",
"admin_enforced": "Imposto pelo administrador"
},
"tab_groups": {
"general": "Geral",
"account": "Conta e identidade",
@@ -2593,7 +2605,7 @@
"nickname": "Apelido",
"nickname_placeholder": "Apelido",
"email": "E-mail",
"email_placeholder": "email@examplo.com.br",
"email_placeholder": "email@example.com",
"phone": "Telefone",
"phone_placeholder": "+55 11 1234 5678",
"phone_type": "Tipo",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "Despre & Date",
"debug": "Depanare"
},
"themes": {
"title": "Teme",
"description": "Alegeți dintre temele implementate de administratorul dvs. și presetările integrate.",
"locked": "Selectarea temei este blocată de un administrator.",
"forced": "Tema „{name}” este impusă de administrator și nu poate fi schimbată",
"admin_theme": "Temă de administrator",
"activated": "Temă activată",
"default_restored": "Tema implicită a fost restabilită",
"default_name": "Implicită",
"default_badge": "Implicită",
"admin_enforced": "Impusă de administrator"
},
"tab_groups": {
"general": "Generalități",
"account": "Cont și identitate",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "О программе и данные",
"debug": "Отладка"
},
"themes": {
"title": "Темы",
"description": "Выберите тему, развёрнутую вашим администратором, или одну из встроенных.",
"locked": "Выбор темы заблокирован администратором.",
"forced": "Тема «{name}» задана администратором и не может быть изменена",
"admin_theme": "Тема администратора",
"activated": "Тема активирована",
"default_restored": "Тема по умолчанию восстановлена",
"default_name": "По умолчанию",
"default_badge": "По умолчанию",
"admin_enforced": "Задано администратором"
},
"tab_groups": {
"general": "Общие",
"account": "Аккаунт и удостоверение",
+12
View File
@@ -938,6 +938,18 @@
"about_data": "Info a dáta",
"debug": "Ladenie"
},
"themes": {
"title": "Motívy",
"description": "Vyberte si z motívov nasadených vaším správcom a zo vstavaných predvolieb.",
"locked": "Výber motívu je uzamknutý správcom.",
"forced": "Motív „{name}“ je vynútený správcom a nemožno ho zmeniť",
"admin_theme": "Motív správcu",
"activated": "Motív aktivovaný",
"default_restored": "Predvolený motív bol obnovený",
"default_name": "Predvolený",
"default_badge": "Predvolený",
"admin_enforced": "Vynútené správcom"
},
"tab_groups": {
"general": "Všeobecné",
"account": "Účet a identita",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Hakkında ve Veriler",
"debug": "Hata Ayıklama"
},
"themes": {
"title": "Temalar",
"description": "Yöneticinizin dağıttığı temalar ve yerleşik hazır ayarlar arasından seçim yapın.",
"locked": "Tema seçimi bir yönetici tarafından kilitlendi.",
"forced": "“{name}” teması yönetici tarafından zorunlu kılınmıştır ve değiştirilemez",
"admin_theme": "Yönetici teması",
"activated": "Tema etkinleştirildi",
"default_restored": "Varsayılan tema geri yüklendi",
"default_name": "Varsayılan",
"default_badge": "Varsayılan",
"admin_enforced": "Yönetici tarafından zorunlu"
},
"tab_groups": {
"general": "Genel",
"account": "Hesap ve Kimlik",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "Про програму та дані",
"debug": "Налагодження"
},
"themes": {
"title": "Теми",
"description": "Виберіть тему, розгорнуту вашим адміністратором, або одну з вбудованих.",
"locked": "Вибір теми заблоковано адміністратором.",
"forced": "Тему «{name}» встановлено адміністратором, і її не можна змінити",
"admin_theme": "Тема адміністратора",
"activated": "Тему активовано",
"default_restored": "Тему за замовчуванням відновлено",
"default_name": "За замовчуванням",
"default_badge": "За замовчуванням",
"admin_enforced": "Встановлено адміністратором"
},
"tab_groups": {
"general": "Загальний",
"account": "Обліковий запис і ідентифікатор",
+13 -1
View File
@@ -930,7 +930,7 @@
"sidebar_apps": "側邊欄應用程式",
"notifications": "通知",
"layout": "版面配置",
"themes": "主题",
"themes": "主題",
"reading": "閱讀",
"composing": "撰寫",
"downloads": "下載",
@@ -938,6 +938,18 @@
"about_data": "關於與資料",
"debug": "除錯"
},
"themes": {
"title": "主題",
"description": "從管理員部署的主題和內建預設集中選擇。",
"locked": "主題選擇已被管理員鎖定。",
"forced": "主題「{name}」由管理員強制指定,無法變更",
"admin_theme": "管理員主題",
"activated": "主題已啟用",
"default_restored": "已還原預設主題",
"default_name": "預設",
"default_badge": "預設",
"admin_enforced": "管理員強制"
},
"tab_groups": {
"general": "一般",
"account": "帳號與寄件身分",
+12
View File
@@ -935,6 +935,18 @@
"about_data": "关于和数据",
"debug": "调试"
},
"themes": {
"title": "主题",
"description": "从管理员部署的主题和内置预设中选择。",
"locked": "主题选择已被管理员锁定。",
"forced": "主题“{name}”由管理员强制指定,无法更改",
"admin_theme": "管理员主题",
"activated": "主题已启用",
"default_restored": "已恢复默认主题",
"default_name": "默认",
"default_badge": "默认",
"admin_enforced": "管理员强制"
},
"tab_groups": {
"general": "通用",
"account": "账户与身份",
+6 -5
View File
@@ -18,11 +18,12 @@ if (typeof window !== 'undefined' && typeof window.matchMedia !== 'function') {
}) as unknown as MediaQueryList;
}
// jsdom 28 (resolved by the ^27 caret in package.json) no longer provides a
// localStorage implementation; the zustand persist middleware writes through
// it on every setState and several stores read from it directly. Provide a
// minimal in-memory Storage so store-backed components render under test.
if (typeof window !== 'undefined' && typeof window.localStorage === 'undefined') {
// Node 25+ ships its own localStorage global, which shadows jsdom's under
// vitest; without --localstorage-file it is an empty object with no Storage
// methods. The zustand persist middleware writes through it on every
// setState and several stores read from it directly, so provide a minimal
// in-memory Storage when the real one is missing or unusable.
if (typeof window !== 'undefined' && typeof window.localStorage?.getItem !== 'function') {
const backing = new Map<string, string>();
const localStorage: Storage = {
get length() {