localization
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-11-09 15:25:03 +09:00
parent 341911a8d3
commit 79f74b83a8
15 changed files with 1524 additions and 16 deletions

View File

@@ -5,6 +5,7 @@ import { TemplatesSelector } from './TemplatesSelector'
import { ExportDataModal } from './ExportDataModal'
import { ImportDataModal } from './ImportDataModal'
import { designTemplates, DesignTemplate } from '../constants/designTemplates'
import { useLocale } from '../contexts/LocaleContext'
interface DesignSettings {
id?: number
@@ -77,6 +78,7 @@ interface CustomizationPanelProps {
}
export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, groups = [], onDataUpdate }: CustomizationPanelProps) {
const { t } = useLocale()
const [settings, setSettings] = useState<DesignSettings>({
theme_color: '#ffffff',
dashboard_layout: 'list',
@@ -278,7 +280,7 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
<div className="modal-header">
<h5 className="modal-title">
<i className="bi bi-palette me-2"></i>
Настройки дашборда
{t('customization.title')}
</h5>
<button
type="button"
@@ -296,7 +298,7 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
onClick={() => setActiveTab('layout')}
>
<i className="bi bi-layout-sidebar me-1"></i>
Макет
{t('customization.layout')}
</button>
</li>
<li className="nav-item">
@@ -305,7 +307,7 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
onClick={() => setActiveTab('colors')}
>
<i className="bi bi-palette-fill me-1"></i>
Цвета
{t('customization.colors')}
</button>
</li>
<li className="nav-item">
@@ -314,7 +316,7 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
onClick={() => setActiveTab('groups')}
>
<i className="bi bi-collection me-1"></i>
Группы
{t('customization.groups')}
</button>
</li>
<li className="nav-item">
@@ -323,7 +325,7 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
onClick={() => setActiveTab('templates')}
>
<i className="bi bi-palette me-1"></i>
Шаблоны
{t('customization.templates')}
</button>
</li>
<li className="nav-item">
@@ -332,7 +334,7 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
onClick={() => setActiveTab('advanced')}
>
<i className="bi bi-gear me-1"></i>
Дополнительно
{t('customization.advanced')}
</button>
</li>
<li className="nav-item">
@@ -341,7 +343,7 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
onClick={() => setActiveTab('data')}
>
<i className="bi bi-database me-1"></i>
Данные
{t('customization.data')}
</button>
</li>
</ul>
@@ -1215,10 +1217,10 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
}
}}
disabled={loading}
title="Сбросить все настройки к значениям по умолчанию"
title={t('customization.resetSettings')}
>
<i className="bi bi-arrow-counterclockwise me-2"></i>
Сбросить настройки
{t('customization.resetSettings')}
</button>
<button
type="button"
@@ -1229,12 +1231,12 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate, user, gr
{loading ? (
<>
<span className="spinner-border spinner-border-sm me-2"></span>
Сохранение...
{t('common.saving')}
</>
) : (
<>
<i className="bi bi-check-lg me-2"></i>
Сохранить
{t('common.save')}
</>
)}
</button>

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { useLocale, Locale } from '../contexts/LocaleContext';
const LanguageSelector: React.FC = () => {
const { locale, setLocale, t } = useLocale();
const languages: Array<{ code: Locale; name: string }> = [
{ code: 'en', name: 'English' },
{ code: 'ru', name: 'Русский' },
{ code: 'ko', name: '한국어' },
{ code: 'zh', name: '中文' },
{ code: 'ja', name: '日本語' },
];
return (
<div className="dropdown">
<button
className="btn btn-outline-secondary btn-sm dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
title={t('language.select')}
>
<i className="fas fa-globe me-1"></i>
{languages.find(lang => lang.code === locale)?.name || 'Language'}
</button>
<ul className="dropdown-menu">
{languages.map((language) => (
<li key={language.code}>
<button
className={`dropdown-item ${locale === language.code ? 'active' : ''}`}
onClick={() => setLocale(language.code)}
>
{language.name}
</button>
</li>
))}
</ul>
</div>
);
};
export default LanguageSelector;

View File

@@ -6,6 +6,11 @@ import { usePathname, useRouter } from 'next/navigation'
import Link from 'next/link'
import Image from 'next/image'
import Script from 'next/script'
import { useLocale } from '../contexts/LocaleContext'
import { useTheme } from '../contexts/ThemeContext'
import ThemeToggle from './ThemeToggle'
import LanguageSelector from './LanguageSelector'
import '../layout.css'
interface User {
username: string
@@ -18,6 +23,7 @@ export function LayoutWrapper({ children }: { children: ReactNode }) {
const isDashboard = pathname === '/dashboard'
const [user, setUser] = useState<User | null>(null)
const router = useRouter()
const { t } = useLocale()
// При монтировании пробуем загрузить профиль
useEffect(() => {
@@ -71,16 +77,19 @@ export function LayoutWrapper({ children }: { children: ReactNode }) {
type="button"
data-bs-toggle="collapse"
data-bs-target="#navcol-1"
title={t('common.menu')}
/>
<div className="collapse navbar-collapse" id="navcol-1">
{!user && (
<Link href="/auth/login" className="btn btn-primary ms-auto">
<i className="fa fa-user"></i>
<span className="d-none d-sm-inline"> Вход</span>
<span className="d-none d-sm-inline"> {t('common.login')}</span>
</Link>
)}
{user && (
<div className="ms-auto d-flex align-items-center gap-3">
<ThemeToggle />
<LanguageSelector />
<Image
src={
user.avatar && user.avatar.startsWith('http')
@@ -97,14 +106,14 @@ export function LayoutWrapper({ children }: { children: ReactNode }) {
<span>{user.username}</span>
{!isDashboard && (
<Link href="/dashboard" className="btn btn-outline-secondary btn-sm">
Дашборд
{t('dashboard.title')}
</Link>
)}
<button
onClick={handleLogout}
className="btn btn-outline-danger btn-sm"
>
Выход
{t('common.logout')}
</button>
</div>
)}
@@ -114,7 +123,7 @@ export function LayoutWrapper({ children }: { children: ReactNode }) {
)}
{/* отступ, чтобы контент не прятался под фиксированным хедером */}
{!isPublicUserPage && <div style={{ height: 70 }} />}
{!isPublicUserPage && <div className="navbar-spacing" />}
{children}

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { ThemeProvider } from '../contexts/ThemeContext';
import { LocaleProvider } from '../contexts/LocaleContext';
interface ProvidersProps {
children: React.ReactNode;
}
const Providers: React.FC<ProvidersProps> = ({ children }) => {
return (
<LocaleProvider>
<ThemeProvider>
{children}
</ThemeProvider>
</LocaleProvider>
);
};
export default Providers;

View File

@@ -0,0 +1,31 @@
'use client'
import React from 'react'
import { useTheme } from '../contexts/ThemeContext'
interface ThemeToggleProps {
className?: string
showLabel?: boolean
}
export const ThemeToggle: React.FC<ThemeToggleProps> = ({
className = '',
showLabel = true
}) => {
const { theme, toggleTheme } = useTheme()
return (
<button
onClick={toggleTheme}
className={`btn btn-outline-secondary d-flex align-items-center ${className}`}
title={theme === 'light' ? 'Переключить на темную тему' : 'Переключить на светлую тему'}
>
<i className={`bi ${theme === 'light' ? 'bi-moon' : 'bi-sun'} ${showLabel ? 'me-2' : ''}`}></i>
{showLabel && (
<span>{theme === 'light' ? 'Темная тема' : 'Светлая тема'}</span>
)}
</button>
)
}
export default ThemeToggle

View File

@@ -0,0 +1,97 @@
'use client'
import React, { createContext, useContext, useEffect, useState } from 'react'
export type Locale = 'en' | 'ru' | 'ko' | 'zh' | 'ja'
interface LocaleContextType {
locale: Locale
setLocale: (locale: Locale) => void
t: (key: string, params?: Record<string, string | number>) => string
}
const LocaleContext = createContext<LocaleContextType | undefined>(undefined)
export const useLocale = () => {
const context = useContext(LocaleContext)
if (!context) {
throw new Error('useLocale must be used within a LocaleProvider')
}
return context
}
interface LocaleProviderProps {
children: React.ReactNode
}
// Функция для загрузки переводов
const loadTranslations = async (locale: Locale): Promise<Record<string, string>> => {
try {
const translations = await import(`../locales/${locale}.json`)
return translations.default
} catch (error) {
console.warn(`Failed to load translations for ${locale}, falling back to English`)
const fallback = await import('../locales/en.json')
return fallback.default
}
}
export const LocaleProvider: React.FC<LocaleProviderProps> = ({ children }) => {
const [locale, setLocaleState] = useState<Locale>('ru')
const [translations, setTranslations] = useState<Record<string, string>>({})
// Загружаем локаль из localStorage и браузера
useEffect(() => {
if (typeof window !== 'undefined') {
const savedLocale = localStorage.getItem('locale') as Locale
const browserLocale = navigator.language.toLowerCase()
let initialLocale: Locale = 'en'
if (savedLocale) {
initialLocale = savedLocale
} else if (browserLocale.startsWith('ru')) {
initialLocale = 'ru'
} else if (browserLocale.startsWith('ko')) {
initialLocale = 'ko'
} else if (browserLocale.startsWith('zh')) {
initialLocale = 'zh'
} else if (browserLocale.startsWith('ja')) {
initialLocale = 'ja'
}
setLocaleState(initialLocale)
}
}, [])
// Загружаем переводы при изменении локали
useEffect(() => {
loadTranslations(locale).then(setTranslations)
}, [locale])
const setLocale = (newLocale: Locale) => {
setLocaleState(newLocale)
if (typeof window !== 'undefined') {
localStorage.setItem('locale', newLocale)
}
}
// Функция перевода с поддержкой интерполяции
const t = (key: string, params?: Record<string, string | number>): string => {
let translation = translations[key] || key
if (params) {
Object.entries(params).forEach(([paramKey, paramValue]) => {
translation = translation.replace(new RegExp(`{{${paramKey}}}`, 'g'), String(paramValue))
})
}
return translation
}
return (
<LocaleContext.Provider value={{ locale, setLocale, t }}>
{children}
</LocaleContext.Provider>
)
}

View File

@@ -0,0 +1,67 @@
'use client'
import React, { createContext, useContext, useEffect, useState } from 'react'
export type Theme = 'light' | 'dark'
interface ThemeContextType {
theme: Theme
setTheme: (theme: Theme) => void
toggleTheme: () => void
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
export const useTheme = () => {
const context = useContext(ThemeContext)
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider')
}
return context
}
interface ThemeProviderProps {
children: React.ReactNode
}
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
const [theme, setThemeState] = useState<Theme>('light')
// Загружаем тему из localStorage при инициализации
useEffect(() => {
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem('theme') as Theme
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
const initialTheme = savedTheme || systemTheme
setThemeState(initialTheme)
applyTheme(initialTheme)
}
}, [])
const applyTheme = (newTheme: Theme) => {
if (typeof document !== 'undefined') {
document.documentElement.classList.remove('light', 'dark')
document.documentElement.classList.add(newTheme)
document.documentElement.setAttribute('data-theme', newTheme)
}
}
const setTheme = (newTheme: Theme) => {
setThemeState(newTheme)
applyTheme(newTheme)
if (typeof window !== 'undefined') {
localStorage.setItem('theme', newTheme)
}
}
const toggleTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light'
setTheme(newTheme)
}
return (
<ThemeContext.Provider value={{ theme, setTheme, toggleTheme }}>
{children}
</ThemeContext.Provider>
)
}

View File

@@ -0,0 +1,4 @@
/* Layout spacing */
.navbar-spacing {
height: 70px;
}

View File

@@ -2,8 +2,10 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import "./themes.css";
import { ReactNode } from "react";
import { LayoutWrapper } from "./components/LayoutWrapper";
import Providers from "./components/Providers";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -73,7 +75,9 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<LayoutWrapper>{children}</LayoutWrapper>
<Providers>
<LayoutWrapper>{children}</LayoutWrapper>
</Providers>
</body>
</html>
);

View File

@@ -0,0 +1,195 @@
{
"common.cancel": "Cancel",
"common.save": "Save",
"common.saving": "Saving...",
"common.loading": "Loading...",
"common.error": "Error",
"common.success": "Success",
"common.close": "Close",
"common.edit": "Edit",
"common.delete": "Delete",
"common.add": "Add",
"common.create": "Create",
"common.update": "Update",
"common.search": "Search",
"common.settings": "Settings",
"common.profile": "Profile",
"common.logout": "Logout",
"common.login": "Login",
"common.register": "Register",
"common.back": "Back",
"common.next": "Next",
"common.previous": "Previous",
"common.submit": "Submit",
"common.reset": "Reset",
"common.clear": "Clear",
"common.confirm": "Confirm",
"common.yes": "Yes",
"common.no": "No",
"common.menu": "Menu",
"auth.login.title": "Login",
"auth.login.email": "Email",
"auth.login.password": "Password",
"auth.login.remember": "Remember me",
"auth.login.forgot": "Forgot password?",
"auth.login.noAccount": "Don't have an account?",
"auth.login.signUp": "Sign up",
"auth.register.title": "Register",
"auth.register.username": "Username",
"auth.register.email": "Email",
"auth.register.password": "Password",
"auth.register.confirmPassword": "Confirm password",
"auth.register.firstName": "First name",
"auth.register.lastName": "Last name",
"auth.register.haveAccount": "Already have an account?",
"auth.register.signIn": "Sign in",
"dashboard.title": "Dashboard",
"dashboard.welcome": "Welcome, {{name}}!",
"dashboard.groups": "Groups",
"dashboard.links": "Links",
"dashboard.settings": "Settings",
"dashboard.customize": "Customize",
"dashboard.addGroup": "Add Group",
"dashboard.addLink": "Add Link",
"dashboard.noGroups": "No groups yet",
"dashboard.noLinks": "No links yet",
"dashboard.createFirst": "Create your first",
"group.create": "Create Group",
"group.edit": "Edit Group",
"group.name": "Group name",
"group.description": "Description",
"group.icon": "Icon",
"group.background": "Background image",
"group.color": "Header color",
"group.public": "Public",
"group.favorite": "Favorite",
"group.expanded": "Expanded by default",
"group.removeIcon": "Remove icon",
"group.removeBackground": "Remove background",
"link.create": "Create Link",
"link.edit": "Edit Link",
"link.title": "Link title",
"link.url": "URL",
"link.description": "Description",
"link.icon": "Icon",
"link.removeIcon": "Remove icon",
"link.public": "Public",
"link.featured": "Featured",
"profile.edit": "Edit Profile",
"profile.username": "Username",
"profile.email": "Email",
"profile.firstName": "First name",
"profile.lastName": "Last name",
"profile.fullName": "Full name",
"profile.bio": "Bio",
"profile.avatar": "Avatar",
"profile.cover": "Cover image",
"profile.removeAvatar": "Remove avatar",
"profile.removeCover": "Remove cover",
"customization.title": "Customization",
"customization.templates": "Templates",
"customization.layout": "Layout",
"customization.colors": "Colors",
"customization.groups": "Groups",
"customization.advanced": "Advanced",
"customization.data": "Data",
"customization.layout.style": "Display style for groups and links",
"customization.layout.list": "List",
"customization.layout.grid": "Grid",
"customization.layout.cards": "Cards",
"customization.layout.compact": "Compact",
"customization.layout.masonry": "Masonry",
"customization.layout.timeline": "Timeline",
"customization.layout.magazine": "Magazine",
"customization.colors.theme": "Theme color",
"customization.colors.background": "Background color",
"customization.colors.backgroundImage": "Background image",
"customization.colors.removeBackground": "Remove background",
"customization.colors.header": "Header text color",
"customization.colors.group": "Group text color",
"customization.colors.link": "Link text color",
"customization.groups.showIcons": "Show group icons",
"customization.groups.showLinks": "Show link icons",
"customization.groups.defaultExpanded": "Groups expanded by default",
"customization.groups.showTitle": "Show groups title",
"customization.advanced.fonts": "Font settings",
"customization.advanced.mainFont": "Main font",
"customization.advanced.headingFont": "Heading font",
"customization.advanced.bodyFont": "Body font",
"customization.advanced.customCSS": "Custom CSS",
"customization.data.title": "Profile data export and import",
"customization.data.description": "Create backups of your profile data or restore them from archive",
"customization.data.export.title": "Export data",
"customization.data.export.description": "Create archive with profile data for backup or transfer",
"customization.data.export.button": "Create export",
"customization.data.import.title": "Import data",
"customization.data.import.description": "Upload and restore data from export archive",
"customization.data.import.file": "Select archive file (.zip)",
"customization.data.import.button": "Open import wizard",
"customization.data.history.title": "Operation history",
"customization.data.history.description": "Export and import history will be displayed here",
"customization.resetSettings": "Reset settings",
"customization.resetConfirm": "Are you sure you want to reset all interface settings to default values? This action cannot be undone.",
"export.title": "Export profile data",
"export.description": "Select data to include in export archive",
"export.general": "General data",
"export.profile": "Profile data (name, bio, avatar)",
"export.styles": "Design settings and styles",
"export.media": "Media files (images, icons)",
"export.groupsLinks": "Groups and links",
"export.selectedCount": "{{groups}} groups, {{links}} links",
"export.createButton": "Create and download",
"export.creating": "Creating export...",
"import.title": "Import profile data",
"import.selectFile": "Select archive for import",
"import.analyzing": "Analyzing archive...",
"import.content": "Archive content",
"import.exportInfo": "Export information",
"import.source": "Source",
"import.exportDate": "Export date",
"import.dataStats": "Data statistics",
"import.groups": "Groups",
"import.links": "Links",
"import.designSettings": "Design settings",
"import.mediaFiles": "Media files",
"import.yes": "Yes",
"import.no": "No",
"import.groupsPreview": "Groups (first 5)",
"import.linksPreview": "Links (first 10)",
"import.settings": "Import settings",
"import.importGroups": "Import groups ({{count}})",
"import.importLinks": "Import links ({{count}})",
"import.importStyles": "Import design settings",
"import.importMedia": "Import media files ({{count}})",
"import.overwriteExisting": "Overwrite existing data",
"import.overwriteHelp": "If disabled, existing groups and links with the same names will be skipped",
"import.unavailable": "(unavailable)",
"import.button": "Import",
"import.importing": "Importing...",
"theme.toggle": "Toggle theme",
"theme.light": "Light theme",
"theme.dark": "Dark theme",
"language.select": "Select language",
"language.en": "English",
"language.ru": "Русский",
"language.ko": "한국어",
"language.zh": "中文",
"language.ja": "日本語"
}

View File

@@ -0,0 +1,195 @@
{
"common.cancel": "キャンセル",
"common.save": "保存",
"common.saving": "保存中...",
"common.loading": "読み込み中...",
"common.error": "エラー",
"common.success": "成功",
"common.close": "閉じる",
"common.edit": "編集",
"common.delete": "削除",
"common.add": "追加",
"common.create": "作成",
"common.update": "更新",
"common.search": "検索",
"common.settings": "設定",
"common.profile": "プロフィール",
"common.logout": "ログアウト",
"common.login": "ログイン",
"common.register": "登録",
"common.back": "戻る",
"common.next": "次へ",
"common.previous": "前へ",
"common.submit": "送信",
"common.reset": "リセット",
"common.clear": "クリア",
"common.confirm": "確認",
"common.yes": "はい",
"common.no": "いいえ",
"common.menu": "メニュー",
"auth.login.title": "ログイン",
"auth.login.email": "メール",
"auth.login.password": "パスワード",
"auth.login.remember": "ログイン状態を保持",
"auth.login.forgot": "パスワードを忘れましたか?",
"auth.login.noAccount": "アカウントをお持ちでない方",
"auth.login.signUp": "新規登録",
"auth.register.title": "新規登録",
"auth.register.username": "ユーザー名",
"auth.register.email": "メール",
"auth.register.password": "パスワード",
"auth.register.confirmPassword": "パスワード確認",
"auth.register.firstName": "名",
"auth.register.lastName": "姓",
"auth.register.haveAccount": "既にアカウントをお持ちの方",
"auth.register.signIn": "ログイン",
"dashboard.title": "ダッシュボード",
"dashboard.welcome": "{{name}}さん、ようこそ!",
"dashboard.groups": "グループ",
"dashboard.links": "リンク",
"dashboard.settings": "設定",
"dashboard.customize": "カスタマイズ",
"dashboard.addGroup": "グループ追加",
"dashboard.addLink": "リンク追加",
"dashboard.noGroups": "グループがありません",
"dashboard.noLinks": "リンクがありません",
"dashboard.createFirst": "最初の作成",
"group.create": "グループ作成",
"group.edit": "グループ編集",
"group.name": "グループ名",
"group.description": "説明",
"group.icon": "アイコン",
"group.background": "背景画像",
"group.color": "ヘッダー色",
"group.public": "公開",
"group.favorite": "お気に入り",
"group.expanded": "デフォルトで展開",
"group.removeIcon": "アイコンを削除",
"group.removeBackground": "背景を削除",
"link.create": "リンク作成",
"link.edit": "リンク編集",
"link.title": "リンクタイトル",
"link.url": "URL",
"link.description": "説明",
"link.icon": "アイコン",
"link.removeIcon": "アイコンを削除",
"link.public": "公開",
"link.featured": "おすすめ",
"profile.edit": "プロフィール編集",
"profile.username": "ユーザー名",
"profile.email": "メール",
"profile.firstName": "名",
"profile.lastName": "姓",
"profile.fullName": "氏名",
"profile.bio": "自己紹介",
"profile.avatar": "アバター",
"profile.cover": "カバー画像",
"profile.removeAvatar": "アバターを削除",
"profile.removeCover": "カバーを削除",
"customization.title": "カスタマイゼーション",
"customization.templates": "テンプレート",
"customization.layout": "レイアウト",
"customization.colors": "色",
"customization.groups": "グループ",
"customization.advanced": "詳細設定",
"customization.data": "データ",
"customization.layout.style": "グループとリンクの表示スタイル",
"customization.layout.list": "リスト",
"customization.layout.grid": "グリッド",
"customization.layout.cards": "カード",
"customization.layout.compact": "コンパクト",
"customization.layout.masonry": "メイソンリー",
"customization.layout.timeline": "タイムライン",
"customization.layout.magazine": "マガジン",
"customization.colors.theme": "テーマカラー",
"customization.colors.background": "背景色",
"customization.colors.backgroundImage": "背景画像",
"customization.colors.removeBackground": "背景を削除",
"customization.colors.header": "ヘッダーテキスト色",
"customization.colors.group": "グループテキスト色",
"customization.colors.link": "リンクテキスト色",
"customization.groups.showIcons": "グループアイコンを表示",
"customization.groups.showLinks": "リンクアイコンを表示",
"customization.groups.defaultExpanded": "グループをデフォルトで展開",
"customization.groups.showTitle": "グループタイトルを表示",
"customization.advanced.fonts": "フォント設定",
"customization.advanced.mainFont": "メインフォント",
"customization.advanced.headingFont": "見出しフォント",
"customization.advanced.bodyFont": "本文フォント",
"customization.advanced.customCSS": "カスタムCSS",
"customization.data.title": "プロフィールデータのエクスポートとインポート",
"customization.data.description": "プロフィールデータのバックアップを作成するか、アーカイブから復元します",
"customization.data.export.title": "データエクスポート",
"customization.data.export.description": "バックアップや転送のためのプロフィールデータアーカイブを作成",
"customization.data.export.button": "エクスポート作成",
"customization.data.import.title": "データインポート",
"customization.data.import.description": "エクスポートアーカイブからデータをアップロードして復元",
"customization.data.import.file": "アーカイブファイルを選択 (.zip)",
"customization.data.import.button": "インポートウィザードを開く",
"customization.data.history.title": "操作履歴",
"customization.data.history.description": "エクスポートとインポートの履歴がここに表示されます",
"customization.resetSettings": "設定をリセット",
"customization.resetConfirm": "すべてのインターフェース設定をデフォルト値にリセットしますか?この操作は元に戻せません。",
"export.title": "プロフィールデータのエクスポート",
"export.description": "エクスポートアーカイブに含めるデータを選択",
"export.general": "一般データ",
"export.profile": "プロフィールデータ(名前、自己紹介、アバター)",
"export.styles": "デザイン設定とスタイル",
"export.media": "メディアファイル(画像、アイコン)",
"export.groupsLinks": "グループとリンク",
"export.selectedCount": "{{groups}}個のグループ、{{links}}個のリンク",
"export.createButton": "作成してダウンロード",
"export.creating": "エクスポート作成中...",
"import.title": "プロフィールデータのインポート",
"import.selectFile": "インポートするアーカイブを選択",
"import.analyzing": "アーカイブ分析中...",
"import.content": "アーカイブ内容",
"import.exportInfo": "エクスポート情報",
"import.source": "ソース",
"import.exportDate": "エクスポート日",
"import.dataStats": "データ統計",
"import.groups": "グループ",
"import.links": "リンク",
"import.designSettings": "デザイン設定",
"import.mediaFiles": "メディアファイル",
"import.yes": "あり",
"import.no": "なし",
"import.groupsPreview": "グループ最初の5個",
"import.linksPreview": "リンク最初の10個",
"import.settings": "インポート設定",
"import.importGroups": "グループをインポート({{count}}個)",
"import.importLinks": "リンクをインポート({{count}}個)",
"import.importStyles": "デザイン設定をインポート",
"import.importMedia": "メディアファイルをインポート({{count}}個)",
"import.overwriteExisting": "既存データを上書き",
"import.overwriteHelp": "無効にすると、同じ名前の既存グループとリンクはスキップされます",
"import.unavailable": "(利用不可)",
"import.button": "インポート",
"import.importing": "インポート中...",
"theme.toggle": "テーマ切り替え",
"theme.light": "ライトテーマ",
"theme.dark": "ダークテーマ",
"language.select": "言語を選択",
"language.en": "English",
"language.ru": "Русский",
"language.ko": "한국어",
"language.zh": "中文",
"language.ja": "日本語"
}

View File

@@ -0,0 +1,195 @@
{
"common.cancel": "취소",
"common.save": "저장",
"common.saving": "저장 중...",
"common.loading": "로딩 중...",
"common.error": "오류",
"common.success": "성공",
"common.close": "닫기",
"common.edit": "편집",
"common.delete": "삭제",
"common.add": "추가",
"common.create": "생성",
"common.update": "업데이트",
"common.search": "검색",
"common.settings": "설정",
"common.profile": "프로필",
"common.logout": "로그아웃",
"common.login": "로그인",
"common.register": "회원가입",
"common.back": "뒤로",
"common.next": "다음",
"common.previous": "이전",
"common.submit": "제출",
"common.reset": "재설정",
"common.clear": "지우기",
"common.confirm": "확인",
"common.yes": "예",
"common.no": "아니오",
"common.menu": "메뉴",
"auth.login.title": "로그인",
"auth.login.email": "이메일",
"auth.login.password": "비밀번호",
"auth.login.remember": "기억하기",
"auth.login.forgot": "비밀번호를 잊으셨나요?",
"auth.login.noAccount": "계정이 없으신가요?",
"auth.login.signUp": "회원가입",
"auth.register.title": "회원가입",
"auth.register.username": "사용자명",
"auth.register.email": "이메일",
"auth.register.password": "비밀번호",
"auth.register.confirmPassword": "비밀번호 확인",
"auth.register.firstName": "이름",
"auth.register.lastName": "성",
"auth.register.haveAccount": "이미 계정이 있으신가요?",
"auth.register.signIn": "로그인",
"dashboard.title": "대시보드",
"dashboard.welcome": "환영합니다, {{name}}님!",
"dashboard.groups": "그룹",
"dashboard.links": "링크",
"dashboard.settings": "설정",
"dashboard.customize": "커스터마이즈",
"dashboard.addGroup": "그룹 추가",
"dashboard.addLink": "링크 추가",
"dashboard.noGroups": "그룹이 없습니다",
"dashboard.noLinks": "링크가 없습니다",
"dashboard.createFirst": "첫 번째 생성하기",
"group.create": "그룹 생성",
"group.edit": "그룹 편집",
"group.name": "그룹 이름",
"group.description": "설명",
"group.icon": "아이콘",
"group.background": "배경 이미지",
"group.color": "헤더 색상",
"group.public": "공개",
"group.favorite": "즐겨찾기",
"group.expanded": "기본으로 펼침",
"group.removeIcon": "아이콘 제거",
"group.removeBackground": "배경 제거",
"link.create": "링크 생성",
"link.edit": "링크 편집",
"link.title": "링크 제목",
"link.url": "URL",
"link.description": "설명",
"link.icon": "아이콘",
"link.removeIcon": "아이콘 제거",
"link.public": "공개",
"link.featured": "추천",
"profile.edit": "프로필 편집",
"profile.username": "사용자명",
"profile.email": "이메일",
"profile.firstName": "이름",
"profile.lastName": "성",
"profile.fullName": "전체 이름",
"profile.bio": "자기소개",
"profile.avatar": "아바타",
"profile.cover": "커버 이미지",
"profile.removeAvatar": "아바타 제거",
"profile.removeCover": "커버 제거",
"customization.title": "커스터마이제이션",
"customization.templates": "템플릿",
"customization.layout": "레이아웃",
"customization.colors": "색상",
"customization.groups": "그룹",
"customization.advanced": "고급",
"customization.data": "데이터",
"customization.layout.style": "그룹 및 링크 표시 스타일",
"customization.layout.list": "목록",
"customization.layout.grid": "그리드",
"customization.layout.cards": "카드",
"customization.layout.compact": "컴팩트",
"customization.layout.masonry": "메이슨리",
"customization.layout.timeline": "타임라인",
"customization.layout.magazine": "매거진",
"customization.colors.theme": "테마 색상",
"customization.colors.background": "배경 색상",
"customization.colors.backgroundImage": "배경 이미지",
"customization.colors.removeBackground": "배경 제거",
"customization.colors.header": "헤더 텍스트 색상",
"customization.colors.group": "그룹 텍스트 색상",
"customization.colors.link": "링크 텍스트 색상",
"customization.groups.showIcons": "그룹 아이콘 표시",
"customization.groups.showLinks": "링크 아이콘 표시",
"customization.groups.defaultExpanded": "그룹 기본 펼침",
"customization.groups.showTitle": "그룹 제목 표시",
"customization.advanced.fonts": "폰트 설정",
"customization.advanced.mainFont": "기본 폰트",
"customization.advanced.headingFont": "제목 폰트",
"customization.advanced.bodyFont": "본문 폰트",
"customization.advanced.customCSS": "사용자 정의 CSS",
"customization.data.title": "프로필 데이터 내보내기 및 가져오기",
"customization.data.description": "프로필 데이터의 백업을 생성하거나 아카이브에서 복원하세요",
"customization.data.export.title": "데이터 내보내기",
"customization.data.export.description": "백업이나 이전을 위한 프로필 데이터 아카이브 생성",
"customization.data.export.button": "내보내기 생성",
"customization.data.import.title": "데이터 가져오기",
"customization.data.import.description": "내보내기 아카이브에서 데이터 업로드 및 복원",
"customization.data.import.file": "아카이브 파일 선택 (.zip)",
"customization.data.import.button": "가져오기 마법사 열기",
"customization.data.history.title": "작업 기록",
"customization.data.history.description": "내보내기 및 가져오기 기록이 여기에 표시됩니다",
"customization.resetSettings": "설정 재설정",
"customization.resetConfirm": "모든 인터페이스 설정을 기본값으로 재설정하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
"export.title": "프로필 데이터 내보내기",
"export.description": "내보내기 아카이브에 포함할 데이터 선택",
"export.general": "일반 데이터",
"export.profile": "프로필 데이터 (이름, 소개, 아바타)",
"export.styles": "디자인 설정 및 스타일",
"export.media": "미디어 파일 (이미지, 아이콘)",
"export.groupsLinks": "그룹 및 링크",
"export.selectedCount": "{{groups}}개 그룹, {{links}}개 링크",
"export.createButton": "생성 및 다운로드",
"export.creating": "내보내기 생성 중...",
"import.title": "프로필 데이터 가져오기",
"import.selectFile": "가져올 아카이브 선택",
"import.analyzing": "아카이브 분석 중...",
"import.content": "아카이브 내용",
"import.exportInfo": "내보내기 정보",
"import.source": "소스",
"import.exportDate": "내보내기 날짜",
"import.dataStats": "데이터 통계",
"import.groups": "그룹",
"import.links": "링크",
"import.designSettings": "디자인 설정",
"import.mediaFiles": "미디어 파일",
"import.yes": "있음",
"import.no": "없음",
"import.groupsPreview": "그룹 (첫 5개)",
"import.linksPreview": "링크 (첫 10개)",
"import.settings": "가져오기 설정",
"import.importGroups": "그룹 가져오기 ({{count}}개)",
"import.importLinks": "링크 가져오기 ({{count}}개)",
"import.importStyles": "디자인 설정 가져오기",
"import.importMedia": "미디어 파일 가져오기 ({{count}}개)",
"import.overwriteExisting": "기존 데이터 덮어쓰기",
"import.overwriteHelp": "비활성화하면 같은 이름의 기존 그룹 및 링크를 건너뜁니다",
"import.unavailable": "(사용 불가)",
"import.button": "가져오기",
"import.importing": "가져오기 중...",
"theme.toggle": "테마 전환",
"theme.light": "밝은 테마",
"theme.dark": "어두운 테마",
"language.select": "언어 선택",
"language.en": "English",
"language.ru": "Русский",
"language.ko": "한국어",
"language.zh": "中文",
"language.ja": "日本語"
}

View File

@@ -0,0 +1,195 @@
{
"common.cancel": "Отмена",
"common.save": "Сохранить",
"common.saving": "Сохранение...",
"common.loading": "Загрузка...",
"common.error": "Ошибка",
"common.success": "Успешно",
"common.close": "Закрыть",
"common.edit": "Редактировать",
"common.delete": "Удалить",
"common.add": "Добавить",
"common.create": "Создать",
"common.update": "Обновить",
"common.search": "Поиск",
"common.settings": "Настройки",
"common.profile": "Профиль",
"common.logout": "Выйти",
"common.login": "Войти",
"common.register": "Регистрация",
"common.back": "Назад",
"common.next": "Далее",
"common.previous": "Предыдущий",
"common.submit": "Отправить",
"common.reset": "Сбросить",
"common.clear": "Очистить",
"common.confirm": "Подтвердить",
"common.yes": "Да",
"common.no": "Нет",
"common.menu": "Меню",
"auth.login.title": "Вход",
"auth.login.email": "Email",
"auth.login.password": "Пароль",
"auth.login.remember": "Запомнить меня",
"auth.login.forgot": "Забыли пароль?",
"auth.login.noAccount": "Нет аккаунта?",
"auth.login.signUp": "Зарегистрироваться",
"auth.register.title": "Регистрация",
"auth.register.username": "Имя пользователя",
"auth.register.email": "Email",
"auth.register.password": "Пароль",
"auth.register.confirmPassword": "Подтвердите пароль",
"auth.register.firstName": "Имя",
"auth.register.lastName": "Фамилия",
"auth.register.haveAccount": "Уже есть аккаунт?",
"auth.register.signIn": "Войти",
"dashboard.title": "Панель управления",
"dashboard.welcome": "Добро пожаловать, {{name}}!",
"dashboard.groups": "Группы",
"dashboard.links": "Ссылки",
"dashboard.settings": "Настройки",
"dashboard.customize": "Настроить",
"dashboard.addGroup": "Добавить группу",
"dashboard.addLink": "Добавить ссылку",
"dashboard.noGroups": "Пока нет групп",
"dashboard.noLinks": "Пока нет ссылок",
"dashboard.createFirst": "Создайте вашу первую",
"group.create": "Создать группу",
"group.edit": "Редактировать группу",
"group.name": "Название группы",
"group.description": "Описание",
"group.icon": "Иконка",
"group.background": "Фоновое изображение",
"group.color": "Цвет заголовка",
"group.public": "Публичная",
"group.favorite": "Избранная",
"group.expanded": "Развернута по умолчанию",
"group.removeIcon": "Убрать иконку",
"group.removeBackground": "Убрать фон",
"link.create": "Создать ссылку",
"link.edit": "Редактировать ссылку",
"link.title": "Название ссылки",
"link.url": "URL",
"link.description": "Описание",
"link.icon": "Иконка",
"link.removeIcon": "Убрать иконку",
"link.public": "Публичная",
"link.featured": "Рекомендуемая",
"profile.edit": "Редактировать профиль",
"profile.username": "Имя пользователя",
"profile.email": "Email",
"profile.firstName": "Имя",
"profile.lastName": "Фамилия",
"profile.fullName": "Полное имя",
"profile.bio": "О себе",
"profile.avatar": "Аватар",
"profile.cover": "Обложка",
"profile.removeAvatar": "Убрать аватар",
"profile.removeCover": "Убрать обложку",
"customization.title": "Настройки",
"customization.templates": "Шаблоны",
"customization.layout": "Макет",
"customization.colors": "Цвета",
"customization.groups": "Группы",
"customization.advanced": "Дополнительно",
"customization.data": "Данные",
"customization.layout.style": "Стиль отображения групп и ссылок",
"customization.layout.list": "Список",
"customization.layout.grid": "Сетка",
"customization.layout.cards": "Карточки",
"customization.layout.compact": "Компактный",
"customization.layout.masonry": "Кирпичная кладка",
"customization.layout.timeline": "Временная шкала",
"customization.layout.magazine": "Журнал",
"customization.colors.theme": "Цвет темы",
"customization.colors.background": "Цвет фона",
"customization.colors.backgroundImage": "Фоновое изображение",
"customization.colors.removeBackground": "Убрать фон",
"customization.colors.header": "Цвет текста заголовков",
"customization.colors.group": "Цвет текста групп",
"customization.colors.link": "Цвет текста ссылок",
"customization.groups.showIcons": "Показывать иконки групп",
"customization.groups.showLinks": "Показывать иконки ссылок",
"customization.groups.defaultExpanded": "Группы развернуты по умолчанию",
"customization.groups.showTitle": "Показывать заголовки групп",
"customization.advanced.fonts": "Настройки шрифтов",
"customization.advanced.mainFont": "Основной шрифт",
"customization.advanced.headingFont": "Шрифт заголовков",
"customization.advanced.bodyFont": "Шрифт текста",
"customization.advanced.customCSS": "Пользовательский CSS",
"customization.data.title": "Экспорт и импорт данных профиля",
"customization.data.description": "Создавайте резервные копии данных профиля или восстанавливайте их из архива",
"customization.data.export.title": "Экспорт данных",
"customization.data.export.description": "Создать архив с данными профиля для резервного копирования или переноса",
"customization.data.export.button": "Создать экспорт",
"customization.data.import.title": "Импорт данных",
"customization.data.import.description": "Загрузить и восстановить данные из архива экспорта",
"customization.data.import.file": "Выберите файл архива (.zip)",
"customization.data.import.button": "Открыть мастер импорта",
"customization.data.history.title": "История операций",
"customization.data.history.description": "Здесь будет отображаться история экспортов и импортов",
"customization.resetSettings": "Сбросить настройки",
"customization.resetConfirm": "Вы уверены, что хотите сбросить все настройки интерфейса к значениям по умолчанию? Это действие нельзя отменить.",
"export.title": "Экспорт данных профиля",
"export.description": "Выберите данные для включения в архив экспорта",
"export.general": "Общие данные",
"export.profile": "Данные профиля (имя, био, аватар)",
"export.styles": "Настройки дизайна и стили",
"export.media": "Медиафайлы (изображения, иконки)",
"export.groupsLinks": "Группы и ссылки",
"export.selectedCount": "{{groups}} групп, {{links}} ссылок",
"export.createButton": "Создать и скачать",
"export.creating": "Создание экспорта...",
"import.title": "Импорт данных профиля",
"import.selectFile": "Выберите архив для импорта",
"import.analyzing": "Анализ архива...",
"import.content": "Содержимое архива",
"import.exportInfo": "Информация об экспорте",
"import.source": "Источник",
"import.exportDate": "Дата экспорта",
"import.dataStats": "Статистика данных",
"import.groups": "Групп",
"import.links": "Ссылок",
"import.designSettings": "Настройки дизайна",
"import.mediaFiles": "Медиафайлов",
"import.yes": "Есть",
"import.no": "Нет",
"import.groupsPreview": "Группы (первые 5)",
"import.linksPreview": "Ссылки (первые 10)",
"import.settings": "Настройки импорта",
"import.importGroups": "Импортировать группы ({{count}})",
"import.importLinks": "Импортировать ссылки ({{count}})",
"import.importStyles": "Импортировать настройки дизайна",
"import.importMedia": "Импортировать медиафайлы ({{count}})",
"import.overwriteExisting": "Перезаписать существующие данные",
"import.overwriteHelp": "Если отключено, существующие группы и ссылки с такими же названиями будут пропущены",
"import.unavailable": "(недоступно)",
"import.button": "Импортировать",
"import.importing": "Импорт...",
"theme.toggle": "Переключить тему",
"theme.light": "Светлая тема",
"theme.dark": "Темная тема",
"language.select": "Выберите язык",
"language.en": "English",
"language.ru": "Русский",
"language.ko": "한국어",
"language.zh": "中文",
"language.ja": "日本語"
}

View File

@@ -0,0 +1,195 @@
{
"common.cancel": "取消",
"common.save": "保存",
"common.saving": "保存中...",
"common.loading": "加载中...",
"common.error": "错误",
"common.success": "成功",
"common.close": "关闭",
"common.edit": "编辑",
"common.delete": "删除",
"common.add": "添加",
"common.create": "创建",
"common.update": "更新",
"common.search": "搜索",
"common.settings": "设置",
"common.profile": "个人资料",
"common.logout": "登出",
"common.login": "登录",
"common.register": "注册",
"common.back": "返回",
"common.next": "下一步",
"common.previous": "上一步",
"common.submit": "提交",
"common.reset": "重置",
"common.clear": "清除",
"common.confirm": "确认",
"common.yes": "是",
"common.no": "否",
"common.menu": "菜单",
"auth.login.title": "登录",
"auth.login.email": "邮箱",
"auth.login.password": "密码",
"auth.login.remember": "记住我",
"auth.login.forgot": "忘记密码?",
"auth.login.noAccount": "还没有账户?",
"auth.login.signUp": "注册",
"auth.register.title": "注册",
"auth.register.username": "用户名",
"auth.register.email": "邮箱",
"auth.register.password": "密码",
"auth.register.confirmPassword": "确认密码",
"auth.register.firstName": "名",
"auth.register.lastName": "姓",
"auth.register.haveAccount": "已有账户?",
"auth.register.signIn": "登录",
"dashboard.title": "仪表板",
"dashboard.welcome": "欢迎,{{name}}",
"dashboard.groups": "分组",
"dashboard.links": "链接",
"dashboard.settings": "设置",
"dashboard.customize": "自定义",
"dashboard.addGroup": "添加分组",
"dashboard.addLink": "添加链接",
"dashboard.noGroups": "暂无分组",
"dashboard.noLinks": "暂无链接",
"dashboard.createFirst": "创建您的第一个",
"group.create": "创建分组",
"group.edit": "编辑分组",
"group.name": "分组名称",
"group.description": "描述",
"group.icon": "图标",
"group.background": "背景图片",
"group.color": "标题颜色",
"group.public": "公开",
"group.favorite": "收藏",
"group.expanded": "默认展开",
"group.removeIcon": "移除图标",
"group.removeBackground": "移除背景",
"link.create": "创建链接",
"link.edit": "编辑链接",
"link.title": "链接标题",
"link.url": "网址",
"link.description": "描述",
"link.icon": "图标",
"link.removeIcon": "移除图标",
"link.public": "公开",
"link.featured": "精选",
"profile.edit": "编辑个人资料",
"profile.username": "用户名",
"profile.email": "邮箱",
"profile.firstName": "名",
"profile.lastName": "姓",
"profile.fullName": "全名",
"profile.bio": "个人简介",
"profile.avatar": "头像",
"profile.cover": "封面图片",
"profile.removeAvatar": "移除头像",
"profile.removeCover": "移除封面",
"customization.title": "自定义",
"customization.templates": "模板",
"customization.layout": "布局",
"customization.colors": "颜色",
"customization.groups": "分组",
"customization.advanced": "高级",
"customization.data": "数据",
"customization.layout.style": "分组和链接的显示样式",
"customization.layout.list": "列表",
"customization.layout.grid": "网格",
"customization.layout.cards": "卡片",
"customization.layout.compact": "紧凑",
"customization.layout.masonry": "瀑布流",
"customization.layout.timeline": "时间线",
"customization.layout.magazine": "杂志",
"customization.colors.theme": "主题颜色",
"customization.colors.background": "背景颜色",
"customization.colors.backgroundImage": "背景图片",
"customization.colors.removeBackground": "移除背景",
"customization.colors.header": "标题文字颜色",
"customization.colors.group": "分组文字颜色",
"customization.colors.link": "链接文字颜色",
"customization.groups.showIcons": "显示分组图标",
"customization.groups.showLinks": "显示链接图标",
"customization.groups.defaultExpanded": "分组默认展开",
"customization.groups.showTitle": "显示分组标题",
"customization.advanced.fonts": "字体设置",
"customization.advanced.mainFont": "主字体",
"customization.advanced.headingFont": "标题字体",
"customization.advanced.bodyFont": "正文字体",
"customization.advanced.customCSS": "自定义CSS",
"customization.data.title": "个人资料数据导出和导入",
"customization.data.description": "创建个人资料数据的备份或从归档文件中恢复",
"customization.data.export.title": "导出数据",
"customization.data.export.description": "为备份或转移创建包含个人资料数据的归档文件",
"customization.data.export.button": "创建导出",
"customization.data.import.title": "导入数据",
"customization.data.import.description": "从导出归档文件上传和恢复数据",
"customization.data.import.file": "选择归档文件 (.zip)",
"customization.data.import.button": "打开导入向导",
"customization.data.history.title": "操作历史",
"customization.data.history.description": "导出和导入历史将显示在这里",
"customization.resetSettings": "重置设置",
"customization.resetConfirm": "您确定要将所有界面设置重置为默认值吗?此操作无法撤消。",
"export.title": "导出个人资料数据",
"export.description": "选择要包含在导出归档中的数据",
"export.general": "通用数据",
"export.profile": "个人资料数据(姓名、简介、头像)",
"export.styles": "设计设置和样式",
"export.media": "媒体文件(图片、图标)",
"export.groupsLinks": "分组和链接",
"export.selectedCount": "{{groups}} 个分组,{{links}} 个链接",
"export.createButton": "创建并下载",
"export.creating": "创建导出中...",
"import.title": "导入个人资料数据",
"import.selectFile": "选择要导入的归档文件",
"import.analyzing": "分析归档中...",
"import.content": "归档内容",
"import.exportInfo": "导出信息",
"import.source": "来源",
"import.exportDate": "导出日期",
"import.dataStats": "数据统计",
"import.groups": "分组",
"import.links": "链接",
"import.designSettings": "设计设置",
"import.mediaFiles": "媒体文件",
"import.yes": "有",
"import.no": "无",
"import.groupsPreview": "分组前5个",
"import.linksPreview": "链接前10个",
"import.settings": "导入设置",
"import.importGroups": "导入分组({{count}}个)",
"import.importLinks": "导入链接({{count}}个)",
"import.importStyles": "导入设计设置",
"import.importMedia": "导入媒体文件({{count}}个)",
"import.overwriteExisting": "覆盖现有数据",
"import.overwriteHelp": "如果禁用,将跳过具有相同名称的现有分组和链接",
"import.unavailable": "(不可用)",
"import.button": "导入",
"import.importing": "导入中...",
"theme.toggle": "切换主题",
"theme.light": "浅色主题",
"theme.dark": "深色主题",
"language.select": "选择语言",
"language.en": "English",
"language.ru": "Русский",
"language.ko": "한국어",
"language.zh": "中文",
"language.ja": "日本語"
}

View File

@@ -0,0 +1,257 @@
/* CSS Custom Properties для темизации */
:root {
/* Светлая тема (по умолчанию) */
--background: #ffffff;
--background-secondary: #f8f9fa;
--background-tertiary: #e9ecef;
--text: #212529;
--text-secondary: #6c757d;
--text-muted: #868e96;
--border: #dee2e6;
--border-light: #e9ecef;
--primary: #0d6efd;
--primary-hover: #0b5ed7;
--secondary: #6c757d;
--success: #198754;
--danger: #dc3545;
--warning: #ffc107;
--info: #0dcaf0;
--card-bg: #ffffff;
--card-border: #dee2e6;
--input-bg: #ffffff;
--input-border: #ced4da;
--input-focus-border: #86b7fe;
--dropdown-bg: #ffffff;
--dropdown-border: #dee2e6;
--modal-bg: #ffffff;
--modal-backdrop: rgba(0, 0, 0, 0.5);
--navbar-bg: #ffffff;
--sidebar-bg: #f8f9fa;
--shadow: rgba(0, 0, 0, 0.175);
--shadow-lg: rgba(0, 0, 0, 0.15);
}
[data-theme="dark"] {
/* Темная тема */
--background: #0d1117;
--background-secondary: #161b22;
--background-tertiary: #21262d;
--text: #f0f6fc;
--text-secondary: #8b949e;
--text-muted: #656d76;
--border: #30363d;
--border-light: #21262d;
--primary: #4fb3ff;
--primary-hover: #58a6ff;
--secondary: #8b949e;
--success: #3fb950;
--danger: #f85149;
--warning: #d29922;
--info: #56d4dd;
--card-bg: #161b22;
--card-border: #30363d;
--input-bg: #0d1117;
--input-border: #30363d;
--input-focus-border: #58a6ff;
--dropdown-bg: #161b22;
--dropdown-border: #30363d;
--modal-bg: #161b22;
--modal-backdrop: rgba(0, 0, 0, 0.7);
--navbar-bg: #161b22;
--sidebar-bg: #0d1117;
--shadow: rgba(0, 0, 0, 0.4);
--shadow-lg: rgba(0, 0, 0, 0.3);
}
/* Применение темы к основным элементам */
body {
background-color: var(--background);
color: var(--text);
transition: background-color 0.3s ease, color 0.3s ease;
}
/* Bootstrap переопределения для темной темы */
.card {
background-color: var(--card-bg);
border-color: var(--card-border);
color: var(--text);
}
.modal-content {
background-color: var(--modal-bg);
border-color: var(--card-border);
color: var(--text);
}
.modal-header {
border-bottom-color: var(--border);
}
.modal-footer {
border-top-color: var(--border);
}
.form-control {
background-color: var(--input-bg);
border-color: var(--input-border);
color: var(--text);
}
.form-control:focus {
background-color: var(--input-bg);
border-color: var(--input-focus-border);
color: var(--text);
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
[data-theme="dark"] .form-control:focus {
box-shadow: 0 0 0 0.25rem rgba(88, 166, 255, 0.25);
}
.form-select {
background-color: var(--input-bg);
border-color: var(--input-border);
color: var(--text);
}
.form-select:focus {
background-color: var(--input-bg);
border-color: var(--input-focus-border);
color: var(--text);
}
.btn-outline-secondary {
color: var(--text);
border-color: var(--border);
}
.btn-outline-secondary:hover {
background-color: var(--background-secondary);
border-color: var(--border);
color: var(--text);
}
.dropdown-menu {
background-color: var(--dropdown-bg);
border-color: var(--dropdown-border);
}
.dropdown-item {
color: var(--text);
}
.dropdown-item:hover,
.dropdown-item:focus {
background-color: var(--background-secondary);
color: var(--text);
}
.nav-tabs {
border-bottom-color: var(--border);
}
.nav-tabs .nav-link {
color: var(--text-secondary);
border-color: transparent;
}
.nav-tabs .nav-link:hover {
border-color: var(--border-light) var(--border-light) var(--border);
color: var(--text);
}
.nav-tabs .nav-link.active {
color: var(--text);
background-color: var(--background);
border-color: var(--border) var(--border) var(--background);
}
.list-group-item {
background-color: var(--card-bg);
border-color: var(--border);
color: var(--text);
}
.text-muted {
color: var(--text-muted) !important;
}
.text-secondary {
color: var(--text-secondary) !important;
}
.border {
border-color: var(--border) !important;
}
.border-top {
border-top-color: var(--border) !important;
}
.border-bottom {
border-bottom-color: var(--border) !important;
}
.border-start {
border-left-color: var(--border) !important;
}
.border-end {
border-right-color: var(--border) !important;
}
/* Специальные стили для темной темы */
[data-theme="dark"] .bg-light {
background-color: var(--background-secondary) !important;
}
[data-theme="dark"] .bg-white {
background-color: var(--card-bg) !important;
}
[data-theme="dark"] .text-dark {
color: var(--text) !important;
}
/* Анимация переключения темы */
* {
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
/* Кастомные утилитарные классы для темизации */
.theme-bg {
background-color: var(--background);
}
.theme-bg-secondary {
background-color: var(--background-secondary);
}
.theme-bg-tertiary {
background-color: var(--background-tertiary);
}
.theme-text {
color: var(--text);
}
.theme-text-secondary {
color: var(--text-secondary);
}
.theme-text-muted {
color: var(--text-muted);
}
.theme-border {
border-color: var(--border);
}
.theme-shadow {
box-shadow: 0 0.125rem 0.25rem var(--shadow);
}
.theme-shadow-lg {
box-shadow: 0 1rem 3rem var(--shadow-lg);
}