refactor(gui): разделить app.go на binding-файлы по доменам, вынести sync apply
- app.go (1810→280 строк): только App struct, startup, DTOs, helpers
- bindings_{nodes,files,notes,actions,worklog,activity,sync,settings}.go
- sync_apply.go: все applyRemote* методы
- i18n: internal/i18n (Go, embed JSON) + frontend/src/lib/i18n (JS)
- core/sync/safe_path.go: SafeVaultPath
- scripts/check-i18n.sh: проверка хардкода кириллицы и bidi-символов
- build.sh: NVM loading, set -e
Все сборки (CLI, server, gui, frontend), go vet, go test проходят.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import ru from './locales/ru.js'
|
||||
import en from './locales/en.js'
|
||||
|
||||
const catalogs = { ru, en }
|
||||
let currentLocale = 'ru'
|
||||
|
||||
export function t(key, params) {
|
||||
const catalog = catalogs[currentLocale]
|
||||
let msg = catalog?.[key]
|
||||
if (msg == null && currentLocale !== 'ru') {
|
||||
msg = catalogs.ru?.[key]
|
||||
}
|
||||
if (msg == null) {
|
||||
msg = key
|
||||
}
|
||||
if (params != null) {
|
||||
for (const [k, v] of Object.entries(params)) {
|
||||
msg = msg.replace(`{${k}}`, String(v))
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
export function setLocale(locale) {
|
||||
if (catalogs[locale]) {
|
||||
currentLocale = locale
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocale() {
|
||||
return currentLocale
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
export default {
|
||||
'nav.today': 'Today',
|
||||
'nav.inbox': 'Inbox',
|
||||
'nav.activity': 'Activity',
|
||||
'nav.clients': 'Clients',
|
||||
'nav.projects': 'Projects',
|
||||
'nav.recipes': 'Recipes',
|
||||
'nav.documents': 'Documents',
|
||||
'nav.archive': 'Archive',
|
||||
|
||||
'tab.overview': 'Overview',
|
||||
'tab.notes': 'Notes',
|
||||
'tab.files': 'Files',
|
||||
'tab.actions': 'Actions',
|
||||
'tab.worklog': 'Work Log',
|
||||
'tab.activity': 'Activity',
|
||||
|
||||
'common.save': 'Save',
|
||||
'common.cancel': 'Cancel',
|
||||
'common.delete': 'Delete',
|
||||
'common.rename': 'Rename',
|
||||
'common.close': 'Close',
|
||||
'common.create': 'Create',
|
||||
'common.confirm': 'Confirm',
|
||||
'common.back': '← Back',
|
||||
'common.loading': 'Loading...',
|
||||
'common.error': 'Error:',
|
||||
'common.yes': 'Yes',
|
||||
'common.ok': 'OK',
|
||||
'common.run': 'Run',
|
||||
'common.name': 'Name',
|
||||
'common.settings': 'Settings',
|
||||
|
||||
'welcome.title': 'Verstak',
|
||||
'welcome.selectSection': 'Select a section in the sidebar.',
|
||||
'welcome.addCase': 'Add case',
|
||||
|
||||
'event.noteCreated': 'Note created',
|
||||
'event.noteUpdated': 'Note updated',
|
||||
'event.fileAdded': 'File added',
|
||||
'event.fileDeleted': 'File deleted',
|
||||
'event.fileRenamed': 'File renamed',
|
||||
'event.fileCopied': 'File copied',
|
||||
'event.fileMoved': 'File moved',
|
||||
'event.caseCreated': 'Case created',
|
||||
|
||||
'action.openUrl': 'Open URL',
|
||||
'action.openFile': 'Open file',
|
||||
'action.openFolder': 'Open folder',
|
||||
'action.runCommand': 'Run command',
|
||||
'action.runScript': 'Run script',
|
||||
'action.openTerminal': 'Open terminal',
|
||||
'action.launchApp': 'Launch app',
|
||||
|
||||
'note.add': '+ Add note',
|
||||
'note.noNotes': 'No notes',
|
||||
'note.title': 'Note title',
|
||||
'note.placeholder': 'Start writing...',
|
||||
|
||||
'file.addFile': '+ Add file',
|
||||
'file.addFolder': '+ Add folder',
|
||||
'file.preview': 'Preview',
|
||||
'file.openExternal': 'Open in external program',
|
||||
'file.openFolder': 'Open folder',
|
||||
'file.delete': 'Delete',
|
||||
'file.pickSingle': 'Select file',
|
||||
'file.pickDirectory': 'Select folder',
|
||||
|
||||
'sync.title': 'Sync',
|
||||
'sync.settings': 'Sync settings',
|
||||
'sync.status': 'Status',
|
||||
'sync.server': 'Server',
|
||||
'sync.device': 'Device',
|
||||
'sync.connected': 'Connected',
|
||||
'sync.notConnected': 'Not connected',
|
||||
'sync.disabled': 'Disabled',
|
||||
|
||||
'error.generic': 'An error occurred',
|
||||
'error.invalidCredentials': 'Invalid username or password',
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
export default {
|
||||
'nav.today': 'Сегодня',
|
||||
'nav.inbox': 'Неразобранное',
|
||||
'nav.activity': 'Активность',
|
||||
'nav.clients': 'Клиенты',
|
||||
'nav.projects': 'Проекты',
|
||||
'nav.recipes': 'Рецепты',
|
||||
'nav.documents': 'Документы',
|
||||
'nav.archive': 'Архив',
|
||||
'nav.sections': 'Разделы',
|
||||
'nav.cases': 'Дела',
|
||||
'nav.noCases': 'Нет дел',
|
||||
'nav.sync': 'Синхронизация',
|
||||
'nav.syncSettings': 'Настройки синхронизации',
|
||||
'nav.syncNow': 'Синхронизировать',
|
||||
'nav.selectPrompt': 'Выберите раздел или дело',
|
||||
'nav.brand': 'Верстак',
|
||||
|
||||
'tab.overview': 'Обзор',
|
||||
'tab.notes': 'Заметки',
|
||||
'tab.files': 'Файлы',
|
||||
'tab.actions': 'Действия',
|
||||
'tab.worklog': 'Журнал',
|
||||
'tab.activity': 'Активность',
|
||||
|
||||
'common.save': 'Сохранить',
|
||||
'common.cancel': 'Отмена',
|
||||
'common.delete': 'Удалить',
|
||||
'common.rename': 'Переименовать',
|
||||
'common.close': 'Закрыть',
|
||||
'common.create': 'Создать',
|
||||
'common.confirm': 'Подтверждение',
|
||||
'common.back': '← Назад',
|
||||
'common.loading': 'Загрузка...',
|
||||
'common.error': 'Ошибка:',
|
||||
'common.yes': 'Да',
|
||||
'common.ok': 'OK',
|
||||
'common.copy': 'Копировать',
|
||||
'common.cut': 'Вырезать',
|
||||
'common.paste': 'Вставить',
|
||||
'common.duplicate': 'Дублировать',
|
||||
'common.run': 'Запустить',
|
||||
'common.test': 'Test',
|
||||
'common.testAgain': 'Проверить',
|
||||
'common.connect': 'Подключиться',
|
||||
'common.disconnect': 'Отключиться',
|
||||
'common.settings': 'Настройки',
|
||||
'common.name': 'Название',
|
||||
'common.type': 'Тип',
|
||||
'common.section': 'Раздел',
|
||||
'common.created': 'Создано',
|
||||
'common.empty': 'Нет',
|
||||
'common.newName': 'Новое имя',
|
||||
|
||||
'welcome.title': 'Верстак',
|
||||
'welcome.selectSection': 'Выберите раздел в боковой панели.',
|
||||
'welcome.createCase': 'Или создайте новое дело кнопкой «+».',
|
||||
'welcome.addCase': 'Добавить дело',
|
||||
|
||||
'event.noteCreated': 'Заметка создана',
|
||||
'event.noteUpdated': 'Заметка изменена',
|
||||
'event.fileAdded': 'Файл добавлен',
|
||||
'event.fileDeleted': 'Файл удалён',
|
||||
'event.fileRenamed': 'Файл переименован',
|
||||
'event.fileCopied': 'Файл скопирован',
|
||||
'event.fileMoved': 'Файл перемещён',
|
||||
'event.folderAdded': 'Папка добавлена',
|
||||
'event.folderDeleted': 'Папка удалена',
|
||||
'event.folderRenamed': 'Папка переименована',
|
||||
'event.caseCreated': 'Дело создано',
|
||||
'event.caseUpdated': 'Дело изменено',
|
||||
|
||||
'kind.project': 'Проект',
|
||||
'kind.client': 'Клиент',
|
||||
'kind.document': 'Документ',
|
||||
'kind.recipe': 'Рецепт',
|
||||
'kind.archive': 'Архив',
|
||||
'kind.case': 'Дело',
|
||||
|
||||
'action.openUrl': 'Открыть URL',
|
||||
'action.openFile': 'Открыть файл',
|
||||
'action.openFolder': 'Открыть папку',
|
||||
'action.runCommand': 'Запустить команду',
|
||||
'action.runScript': 'Запустить скрипт',
|
||||
'action.openTerminal': 'Открыть терминал',
|
||||
'action.launchApp': 'Запустить приложение',
|
||||
'action.addAction': '+ Добавить действие',
|
||||
'action.newAction': 'Новое действие',
|
||||
'action.noActions': 'Действий пока нет',
|
||||
'action.run': 'Запустить',
|
||||
'action.dataUrl': 'URL',
|
||||
'action.dataPath': 'Путь',
|
||||
'action.dataCommand': 'Команда',
|
||||
'action.urlPlaceholder': 'https://example.com',
|
||||
'action.pathPlaceholder': '/path/to/file',
|
||||
'action.commandPlaceholder': 'команда',
|
||||
'action.namePlaceholder': 'Например: Открыть сайт',
|
||||
|
||||
'note.add': '+ Добавить заметку',
|
||||
'note.new': 'Новая заметка',
|
||||
'note.title': 'Название заметки',
|
||||
'note.noNotes': 'Нет заметок',
|
||||
'note.createFirst': 'Создайте первую заметку для этого дела.',
|
||||
'note.placeholder': 'Начните писать...',
|
||||
'note.unsavedTitle': 'Несохранённые изменения',
|
||||
'note.unsavedMessage': 'Закрыть редактор? Все несохранённые изменения будут потеряны.',
|
||||
'note.unsavedClose': 'Закрыть',
|
||||
|
||||
'file.addFile': '+ Добавить файл',
|
||||
'file.addFolder': '+ Добавить папку',
|
||||
'file.newFile': '+ Новый файл',
|
||||
'file.addFileSimple': 'Добавить файл',
|
||||
'file.addFolderSimple': 'Добавить папку',
|
||||
'file.noFiles': 'В этой папке пока нет файлов',
|
||||
'file.noFilesCase': 'В этом проекте пока нет файлов',
|
||||
'file.hint': 'Добавьте файл или папку, чтобы сохранить материалы проекта.',
|
||||
'file.root': 'Файлы',
|
||||
'file.preview': 'Предпросмотр',
|
||||
'file.openExternal': 'Открыть во внешней программе',
|
||||
'file.openFolder': 'Открыть папку',
|
||||
'file.showInExplorer': 'Показать в проводнике',
|
||||
'file.more': 'Ещё',
|
||||
'file.delete': 'Удалить',
|
||||
'file.ariaFolder': 'Папка',
|
||||
'file.ariaFile': 'Файл',
|
||||
'file.scanning': 'Сканирование...',
|
||||
'file.pickSingle': 'Выберите файл',
|
||||
'file.pickMultiple': 'Выберите файлы',
|
||||
'file.pickDirectory': 'Выберите папку',
|
||||
'file.importTitle': 'Добавить в',
|
||||
'file.importFiles': 'Файлов:',
|
||||
'file.importFolders': 'Папок:',
|
||||
'file.importSize': 'Размер:',
|
||||
'file.importCopy': 'Скопировать',
|
||||
'file.importLink': 'Привязать',
|
||||
'file.selectCaseFirst': 'Сначала выберите дело для добавления файлов',
|
||||
|
||||
'worklog.title': 'Журнал',
|
||||
'worklog.whatDone': 'Что сделано',
|
||||
'worklog.minutes': 'Мин',
|
||||
'worklog.min': 'мин',
|
||||
'worklog.log': 'Записать',
|
||||
'worklog.empty': 'Записей работы пока нет',
|
||||
|
||||
'sync.title': 'Синхронизация',
|
||||
'sync.settings': 'Настройки синхронизации',
|
||||
'sync.status': 'Статус',
|
||||
'sync.server': 'Сервер',
|
||||
'sync.device': 'Устройство',
|
||||
'sync.deviceId': 'ID устройства',
|
||||
'sync.unpushed': 'Неотправлено',
|
||||
'sync.lastSync': 'Последняя синх.',
|
||||
'sync.revoked': 'Отозвано',
|
||||
'sync.connected': 'Подключено',
|
||||
'sync.notConnected': 'Не подключено',
|
||||
'sync.disabled': 'Отключена',
|
||||
'sync.serverUrl': 'URL сервера',
|
||||
'sync.serverUrlPlaceholder': 'https://example.com:47732',
|
||||
'sync.username': 'Логин',
|
||||
'sync.usernamePlaceholder': 'username',
|
||||
'sync.password': 'Пароль',
|
||||
'sync.passwordPlaceholder': 'password',
|
||||
'sync.autoSync': 'Автосинхронизация (мин, 0 = отключено)',
|
||||
'sync.saveInterval': 'Сохранить интервал',
|
||||
'sync.syncNow': 'Синхронизировать',
|
||||
'sync.disconnect': 'Отключиться',
|
||||
'sync.connect': 'Подключиться',
|
||||
'sync.test': 'Проверить',
|
||||
'sync.settingsSaved': 'интервал сохранён',
|
||||
|
||||
'today.title': 'Сегодня',
|
||||
'today.changedCases': 'Изменён сегодня',
|
||||
'today.timeline': 'Лента за сегодня',
|
||||
'today.empty': 'Сегодня пока тихо',
|
||||
'today.emptyHint': 'Здесь появятся дела, заметки, файлы и действия, с которыми вы работали сегодня.',
|
||||
'today.plural.case_one': 'дело',
|
||||
'today.plural.case_few': 'дела',
|
||||
'today.plural.case_many': 'дел',
|
||||
'today.plural.note_one': 'заметка',
|
||||
'today.plural.note_few': 'заметки',
|
||||
'today.plural.note_many': 'заметок',
|
||||
'today.plural.file_one': 'файл',
|
||||
'today.plural.file_few': 'файла',
|
||||
'today.plural.file_many': 'файлов',
|
||||
'today.plural.event_one': 'событие',
|
||||
'today.plural.event_few': 'события',
|
||||
'today.plural.event_many': 'событий',
|
||||
|
||||
'activity.title': 'Активность',
|
||||
'activity.empty': 'Активность пока не зафиксирована',
|
||||
'activity.perCaseEmpty': 'Активность пока не зафиксирована',
|
||||
|
||||
'overview.type': 'Тип',
|
||||
'overview.section': 'Раздел',
|
||||
'overview.created': 'Создано',
|
||||
'overview.newNote': 'Новая заметка',
|
||||
'overview.addFile': 'Добавить файл',
|
||||
'overview.addAction': 'Добавить действие',
|
||||
'overview.logTime': 'Записать время',
|
||||
'overview.recentNotes': 'Последние заметки',
|
||||
'overview.recentEntries': 'Последние записи',
|
||||
|
||||
'rename.title': 'Переименовать',
|
||||
'rename.emptyError': 'Имя не может быть пустым',
|
||||
'rename.invalidError': 'Недопустимое имя',
|
||||
|
||||
'delete.confirmTitle': 'Удаление',
|
||||
'delete.confirmMessage': 'Удалить',
|
||||
'delete.folder': 'папку',
|
||||
'delete.file': 'файл',
|
||||
|
||||
'template.optionNone': 'Без шаблона',
|
||||
'template.optional': 'Шаблон (опционально)',
|
||||
|
||||
'mime.jpeg': 'Изображение JPEG',
|
||||
'mime.png': 'Изображение PNG',
|
||||
'mime.gif': 'Изображение GIF',
|
||||
'mime.webp': 'Изображение WebP',
|
||||
'mime.svg': 'Изображение SVG',
|
||||
'mime.bmp': 'Изображение BMP',
|
||||
'mime.tiff': 'Изображение TIFF',
|
||||
'mime.avif': 'Изображение AVIF',
|
||||
'mime.pdf': 'PDF документ',
|
||||
'mime.word': 'Документ Word',
|
||||
'mime.excel': 'Таблица Excel',
|
||||
'mime.ppt': 'Презентация PowerPoint',
|
||||
'mime.zip': 'ZIP архив',
|
||||
'mime.gzip': 'GZIP архив',
|
||||
'mime.tar': 'TAR архив',
|
||||
'mime.sevenz': '7z архив',
|
||||
'mime.rar': 'RAR архив',
|
||||
'mime.text': 'Текстовый файл',
|
||||
'mime.html': 'HTML файл',
|
||||
'mime.css': 'CSS файл',
|
||||
'mime.js': 'JavaScript файл',
|
||||
'mime.json': 'JSON файл',
|
||||
'mime.xml': 'XML файл',
|
||||
'mime.yaml': 'YAML файл',
|
||||
'mime.binary': 'Бинарный файл',
|
||||
'mime.executable': 'Исполняемый файл',
|
||||
'mime.folder': 'Папка',
|
||||
'mime.unknown': 'Неизвестно',
|
||||
'mime.file': 'Файл',
|
||||
|
||||
'error.nameEmpty': 'Имя не может быть пустым',
|
||||
'error.nameInvalid': 'Недопустимое имя',
|
||||
'error.selectCaseFirst': 'Сначала выберите дело',
|
||||
'error.generic': 'Произошла ошибка',
|
||||
'error.invalidCredentials': 'Неверный логин или пароль',
|
||||
'error.accountBlocked': 'Аккаунт заблокирован',
|
||||
'error.emailNotConfirmed': 'Email не подтверждён',
|
||||
'error.tokenInvalid': 'Неверный или просроченный токен',
|
||||
'error.tokenExpired': 'Срок действия токена истёк',
|
||||
}
|
||||
Reference in New Issue
Block a user