import { writable, derived } from 'svelte/store'; export const locale = writable('ru'); export const translations = { ru: { // Navigation nav: { timer: 'Таймер', setup: 'Участники', history: 'История', settings: 'Настройки', }, // Setup page setup: { title: 'Название собрания', participants: 'Участники', addParticipant: 'Добавить участника', namePlaceholder: 'Имя участника', noParticipants: 'Добавьте участников для начала собрания', selectAll: 'Выбрать всех', deselectAll: 'Снять выбор', startMeeting: 'Начать собрание', speakerTime: 'Время на спикера', totalTime: 'Общее время', minutes: 'мин', unlimited: 'Без ограничения', dragHint: 'перетащите для изменения порядка, ✓/✗ присутствие', }, // Timer page timer: { currentSpeaker: 'Текущий спикер', currentlySpeaking: 'Сейчас говорит', speakerTime: 'Время спикера', totalTime: 'Общее время', remaining: 'Осталось', queue: 'Очередь', participants: 'Участники', finished: 'Выступили', noSpeaker: 'Нет спикера', noActiveMeeting: 'Собрание не запущено', goToParticipants: 'Перейдите в раздел Участники, чтобы добавить участников', readyToStart: 'Всё готово к началу', editParticipants: 'Редактировать участников', noParticipants: 'Участники не добавлены', registeredParticipants: 'Зарегистрированные участники', }, // Controls controls: { next: 'Следующий', skip: 'Пропустить', pause: 'Пауза', resume: 'Продолжить', stop: 'Завершить', }, // History page history: { title: 'История собраний', noHistory: 'История пуста', date: 'Дата', duration: 'Длительность', participants: 'Участники', avgTime: 'Среднее время', export: 'Экспорт', exportJSON: 'Экспорт JSON', exportCSV: 'Экспорт CSV', delete: 'Удалить', deleteAll: 'Удалить историю', deleteSession: 'Удалить запись', confirmDelete: 'Удалить эту запись?', confirmDeleteTitle: 'Подтверждение удаления', confirmDeleteSession: 'Вы уверены, что хотите удалить эту запись? Действие необратимо.', confirmDeleteAllTitle: 'Удалить всю историю?', confirmDeleteAll: 'Вы уверены, что хотите удалить ВСЮ историю собраний? Это действие необратимо!', overtimeRate: 'Процент превышения', avgAttendance: 'Средняя явка', recentSessions: 'Последние собрания', noSessions: 'Собраний пока нет', participantBreakdown: 'Статистика по участникам', name: 'Имя', sessions: 'Собрания', overtime: 'Превышение', attendance: 'Явка', }, // Settings page settings: { title: 'Настройки собрания', language: 'Язык', sound: 'Звуковые уведомления', soundEnabled: 'Включены', soundDisabled: 'Выключены', warningTime: 'Предупреждение за', seconds: 'сек', defaultSpeakerTime: 'Время на спикера по умолчанию', defaultTotalTime: 'Общее время собрания (мин)', theme: 'Тема оформления', themeDark: 'Тёмная', themeLight: 'Светлая', save: 'Сохранить', saved: 'Сохранено!', windowWidth: 'Настройка окна', windowWidthHint: 'Ширина окна (мин. 480 пикселей)', windowFullHeight: 'Окно на всю высоту экрана', }, // Updates updates: { title: 'Обновления', currentVersion: 'Текущая версия', checkingForUpdates: 'Проверка обновлений...', updateAvailable: 'Доступно обновление', rebuildAvailable: 'Доступна пересборка', upToDate: 'У вас последняя версия', downloadAndInstall: 'Скачать и установить', downloading: 'Загрузка...', installing: 'Установка...', restartRequired: 'Для завершения обновления требуется перезапуск', restart: 'Перезапустить', later: 'Позже', error: 'Ошибка проверки обновлений', downloadError: 'Ошибка загрузки обновления', checkNow: 'Проверить сейчас', }, // Participant management participants: { title: 'Управление участниками', add: 'Добавить', edit: 'Редактировать', delete: 'Удалить', name: 'Имя', stats: 'Статистика', avgSpeakTime: 'Среднее время выступления', totalMeetings: 'Всего собраний', confirmDelete: 'Удалить участника?', }, // Common common: { cancel: 'Отмена', confirm: 'Подтвердить', save: 'Сохранить', close: 'Закрыть', delete: 'Удалить', yes: 'Да', no: 'Нет', loading: 'Загрузка...', error: 'Ошибка', errorStartMeeting: 'Ошибка запуска планёрки', }, // Time formats time: { hours: 'ч', minutes: 'мин', seconds: 'сек', }, // Hotkeys hotkeys: { title: 'Горячие клавиши', next: 'Следующий спикер', skip: 'Пропустить', pauseResume: 'Пауза/Продолжить', stop: 'Остановить митинг', }, }, en: { // Navigation nav: { timer: 'Timer', setup: 'Participants', history: 'History', settings: 'Settings', }, // Setup page setup: { title: 'Meeting Name', participants: 'Participants', addParticipant: 'Add Participant', namePlaceholder: 'Participant name', noParticipants: 'Add participants to start a meeting', selectAll: 'Select All', deselectAll: 'Deselect All', startMeeting: 'Start Meeting', speakerTime: 'Speaker Time', totalTime: 'Total Time', minutes: 'min', unlimited: 'Unlimited', dragHint: 'drag to reorder, ✓/✗ attendance', }, // Timer page timer: { currentSpeaker: 'Current Speaker', currentlySpeaking: 'Currently speaking', speakerTime: 'Speaker Time', totalTime: 'Total Time', remaining: 'Remaining', queue: 'Queue', participants: 'Participants', finished: 'Finished', noSpeaker: 'No speaker', noActiveMeeting: 'No active meeting', goToParticipants: 'Go to Participants to add participants', readyToStart: 'Ready to start', editParticipants: 'Edit participants', noParticipants: 'No participants added', registeredParticipants: 'Registered participants', }, // Controls controls: { next: 'Next', skip: 'Skip', pause: 'Pause', resume: 'Resume', stop: 'Stop', }, // History page history: { title: 'Meeting History', noHistory: 'No history yet', date: 'Date', duration: 'Duration', participants: 'Participants', avgTime: 'Avg Time', export: 'Export', exportJSON: 'Export JSON', exportCSV: 'Export CSV', delete: 'Delete', deleteAll: 'Delete History', deleteSession: 'Delete session', confirmDelete: 'Delete this record?', confirmDeleteTitle: 'Confirm Deletion', confirmDeleteSession: 'Are you sure you want to delete this session? This action cannot be undone.', confirmDeleteAllTitle: 'Delete All History?', confirmDeleteAll: 'Are you sure you want to delete ALL meeting history? This action cannot be undone!', overtimeRate: 'Overtime Rate', avgAttendance: 'Avg. Attendance', recentSessions: 'Recent Sessions', noSessions: 'No sessions yet', participantBreakdown: 'Participant Breakdown', name: 'Name', sessions: 'Sessions', overtime: 'Overtime', attendance: 'Attendance', }, // Settings page settings: { title: 'Meeting Settings', language: 'Language', sound: 'Sound Notifications', soundEnabled: 'Enabled', soundDisabled: 'Disabled', warningTime: 'Warning before', seconds: 'sec', defaultSpeakerTime: 'Default Speaker Time', defaultTotalTime: 'Total meeting time (min)', theme: 'Theme', themeDark: 'Dark', themeLight: 'Light', save: 'Save', saved: 'Saved!', windowWidth: 'Window Settings', windowWidthHint: 'Window width (min. 480 pixels)', windowFullHeight: 'Full screen height window', }, // Updates updates: { title: 'Updates', currentVersion: 'Current version', checkingForUpdates: 'Checking for updates...', updateAvailable: 'Update available', rebuildAvailable: 'Rebuild available', upToDate: 'You have the latest version', downloadAndInstall: 'Download and install', downloading: 'Downloading...', installing: 'Installing...', restartRequired: 'Restart required to complete the update', restart: 'Restart', later: 'Later', error: 'Error checking for updates', downloadError: 'Error downloading update', checkNow: 'Check now', }, // Participant management participants: { title: 'Manage Participants', add: 'Add', edit: 'Edit', delete: 'Delete', name: 'Name', stats: 'Statistics', avgSpeakTime: 'Avg Speaking Time', totalMeetings: 'Total Meetings', confirmDelete: 'Delete participant?', }, // Common common: { cancel: 'Cancel', confirm: 'Confirm', save: 'Save', close: 'Close', delete: 'Delete', yes: 'Yes', no: 'No', loading: 'Loading...', error: 'Error', errorStartMeeting: 'Failed to start meeting', }, // Time formats time: { hours: 'h', minutes: 'min', seconds: 'sec', }, // Hotkeys hotkeys: { title: 'Hotkeys', next: 'Next speaker', skip: 'Skip speaker', pauseResume: 'Pause/Resume', stop: 'Stop meeting', }, }, }; export const t = derived(locale, ($locale) => { return (key) => { const keys = key.split('.'); let value = translations[$locale]; for (const k of keys) { if (value && typeof value === 'object' && k in value) { value = value[k]; } else { console.warn(`Translation missing: ${key} for locale ${$locale}`); return key; } } return value; }; }); export function setLocale(lang) { if (translations[lang]) { locale.set(lang); localStorage.setItem('daily-timer-locale', lang); } } export function initLocale() { const saved = localStorage.getItem('daily-timer-locale'); if (saved && translations[saved]) { locale.set(saved); } else { const browserLang = navigator.language.split('-')[0]; if (translations[browserLang]) { locale.set(browserLang); } } }