chore: move i18n to shared package (#1066)

* chore: move i18n to shared package

* chore: move server translations to shared package and apply linter and prettier on entire shared package
This commit is contained in:
Julien G.
2026-05-26 20:27:29 +02:00
committed by GitHub
parent 324d930ca3
commit 126f2df21b
860 changed files with 56891 additions and 46377 deletions
+370
View File
@@ -0,0 +1,370 @@
import type { TranslationStrings } from '../types';
const admin: TranslationStrings = {
'admin.notifications.title': 'Уведомления',
'admin.notifications.hint':
'Выберите канал уведомлений. Одновременно может быть активен только один.',
'admin.notifications.none': 'Отключено',
'admin.notifications.email': 'Эл. почта (SMTP)',
'admin.notifications.webhook': 'Webhook',
'admin.notifications.save': 'Сохранить настройки уведомлений',
'admin.notifications.saved': 'Настройки уведомлений сохранены',
'admin.notifications.testWebhook': 'Отправить тестовый вебхук',
'admin.notifications.testWebhookSuccess': 'Тестовый вебхук успешно отправлен',
'admin.notifications.testWebhookFailed': 'Ошибка отправки тестового вебхука',
'admin.smtp.title': 'Почта и уведомления',
'admin.smtp.hint':
'Конфигурация SMTP для отправки уведомлений по электронной почте.',
'admin.smtp.testButton': 'Отправить тестовое письмо',
'admin.webhook.hint':
'Отправлять уведомления через внешний webhook (Discord, Slack и т.д.).',
'admin.smtp.testSuccess': 'Тестовое письмо успешно отправлено',
'admin.smtp.testFailed': 'Ошибка отправки тестового письма',
'admin.title': 'Администрирование',
'admin.subtitle': 'Управление пользователями и системные настройки',
'admin.tabs.users': 'Пользователи',
'admin.tabs.categories': 'Категории',
'admin.tabs.backup': 'Резервная копия',
'admin.tabs.audit': 'Аудит',
'admin.stats.users': 'Пользователи',
'admin.stats.trips': 'Поездки',
'admin.stats.places': 'Места',
'admin.stats.photos': 'Фото',
'admin.stats.files': 'Файлы',
'admin.table.user': 'Пользователь',
'admin.table.email': 'Эл. почта',
'admin.table.role': 'Роль',
'admin.table.created': 'Создан',
'admin.table.lastLogin': 'Последний вход',
'admin.table.actions': 'Действия',
'admin.you': '(Вы)',
'admin.editUser': 'Редактировать пользователя',
'admin.newPassword': 'Новый пароль',
'admin.newPasswordHint': 'Оставьте пустым, чтобы сохранить текущий пароль',
'admin.deleteUser':
'Удалить пользователя «{name}»? Все поездки будут безвозвратно удалены.',
'admin.deleteUserTitle': 'Удалить пользователя',
'admin.newPasswordPlaceholder': 'Введите новый пароль…',
'admin.toast.loadError': 'Не удалось загрузить данные администрирования',
'admin.toast.userUpdated': 'Пользователь обновлён',
'admin.toast.updateError': 'Ошибка обновления',
'admin.toast.userDeleted': 'Пользователь удалён',
'admin.toast.deleteError': 'Ошибка удаления',
'admin.toast.cannotDeleteSelf': 'Нельзя удалить собственный аккаунт',
'admin.toast.userCreated': 'Пользователь создан',
'admin.toast.createError': 'Ошибка создания пользователя',
'admin.toast.fieldsRequired':
'Имя пользователя, эл. почта и пароль обязательны',
'admin.createUser': 'Создать пользователя',
'admin.invite.title': 'Ссылки-приглашения',
'admin.invite.subtitle': 'Создание одноразовых ссылок для регистрации',
'admin.invite.create': 'Создать ссылку',
'admin.invite.createAndCopy': 'Создать и скопировать',
'admin.invite.empty': 'Ссылки-приглашения ещё не созданы',
'admin.invite.maxUses': 'Макс. использований',
'admin.invite.expiry': 'Действует',
'admin.invite.uses': 'использовано',
'admin.invite.expiresAt': 'истекает',
'admin.invite.createdBy': 'от',
'admin.invite.active': 'Активна',
'admin.invite.expired': 'Истекла',
'admin.invite.usedUp': 'Исчерпана',
'admin.invite.copied': 'Ссылка-приглашение скопирована',
'admin.invite.copyLink': 'Копировать ссылку',
'admin.invite.deleted': 'Ссылка-приглашение удалена',
'admin.invite.createError': 'Ошибка при создании ссылки',
'admin.invite.deleteError': 'Ошибка при удалении ссылки',
'admin.tabs.settings': 'Настройки',
'admin.allowRegistration': 'Разрешить регистрацию',
'admin.allowRegistrationHint':
'Новые пользователи могут регистрироваться самостоятельно',
'admin.authMethods': 'Authentication Methods',
'admin.passwordLogin': 'Password Login',
'admin.passwordLoginHint': 'Allow users to sign in with email and password',
'admin.passwordRegistration': 'Password Registration',
'admin.passwordRegistrationHint':
'Allow new users to register with email and password',
'admin.oidcLogin': 'SSO Login',
'admin.oidcLoginHint': 'Allow users to sign in with SSO',
'admin.oidcRegistration': 'SSO Auto-Provisioning',
'admin.oidcRegistrationHint':
'Automatically create accounts for new SSO users',
'admin.envOverrideHint':
'Password login settings are controlled by the OIDC_ONLY environment variable and cannot be changed here.',
'admin.lockoutWarning': 'At least one login method must remain enabled',
'admin.requireMfa': 'Требовать двухфакторную аутентификацию (2FA)',
'admin.requireMfaHint':
'Пользователи без 2FA должны завершить настройку в разделе «Настройки» перед использованием приложения.',
'admin.apiKeys': 'API-ключи',
'admin.apiKeysHint':
'Необязательно. Включает расширенные данные о местах, такие как фото и погода.',
'admin.mapsKey': 'API-ключ Google Maps',
'admin.mapsKeyHint':
'Необходим для поиска мест. Получите на console.cloud.google.com',
'admin.mapsKeyHintLong':
'Без API-ключа используется OpenStreetMap для поиска мест. С ключом Google API можно загружать фото, рейтинги и часы работы. Получите ключ на console.cloud.google.com.',
'admin.recommended': 'Рекомендуется',
'admin.weatherKey': 'API-ключ OpenWeatherMap',
'admin.weatherKeyHint':
'Для данных о погоде. Бесплатно на openweathermap.org',
'admin.validateKey': 'Проверить',
'admin.keyValid': 'Подключено',
'admin.keyInvalid': 'Недействителен',
'admin.keySaved': 'API-ключи сохранены',
'admin.oidcTitle': 'Единый вход (OIDC)',
'admin.oidcSubtitle':
'Разрешить вход через внешних провайдеров, таких как Google, Apple, Authentik или Keycloak.',
'admin.oidcDisplayName': 'Отображаемое имя',
'admin.oidcIssuer': 'URL издателя',
'admin.oidcIssuerHint':
'URL издателя OpenID Connect провайдера. Напр. https://accounts.google.com',
'admin.oidcSaved': 'Конфигурация OIDC сохранена',
'admin.oidcOnlyMode': 'Отключить вход по паролю',
'admin.oidcOnlyModeHint':
'При включении разрешён только вход через SSO. Вход и регистрация по паролю будут заблокированы.',
'admin.fileTypes': 'Разрешённые типы файлов',
'admin.fileTypesHint':
'Настройте, какие типы файлов могут загружать пользователи.',
'admin.fileTypesFormat':
'Расширения через запятую (напр. jpg,png,pdf,doc). Используйте * для разрешения всех типов.',
'admin.fileTypesSaved': 'Настройки типов файлов сохранены',
'admin.placesPhotos.title': 'Фотографии мест',
'admin.placesPhotos.subtitle':
'Загрузка фотографий из Google Places API. Отключите для экономии квоты API. Фотографии Wikimedia не затронуты.',
'admin.placesAutocomplete.title': 'Автодополнение мест',
'admin.placesAutocomplete.subtitle':
'Использование Google Places API для поисковых подсказок. Отключите для экономии квоты API.',
'admin.placesDetails.title': 'Сведения о месте',
'admin.placesDetails.subtitle':
'Загрузка подробной информации о месте (часы работы, рейтинг, веб-сайт) из Google Places API. Отключите для экономии квоты API.',
'admin.bagTracking.title': 'Отслеживание багажа',
'admin.bagTracking.subtitle': 'Включить вес и привязку к багажу для вещей',
'admin.collab.chat.title': 'Чат',
'admin.collab.chat.subtitle': 'Обмен сообщениями для совместной работы',
'admin.collab.notes.title': 'Заметки',
'admin.collab.notes.subtitle': 'Общие заметки и документы',
'admin.collab.polls.title': 'Опросы',
'admin.collab.polls.subtitle': 'Групповые опросы и голосования',
'admin.collab.whatsnext.title': 'Что дальше',
'admin.collab.whatsnext.subtitle': 'Предложения активностей и следующие шаги',
'admin.tabs.config': 'Персонализация',
'admin.tabs.defaults': 'Настройки по умолчанию',
'admin.defaultSettings.title': 'Настройки пользователей по умолчанию',
'admin.defaultSettings.description':
'Задайте значения по умолчанию для всего экземпляра. Пользователи, не изменившие параметр, увидят эти значения. Их собственные изменения всегда имеют приоритет.',
'admin.defaultSettings.saved': 'Значение по умолчанию сохранено',
'admin.defaultSettings.reset': 'Сбросить до встроенного значения',
'admin.defaultSettings.resetToBuiltIn': 'сбросить',
'admin.tabs.templates': 'Шаблоны упаковки',
'admin.packingTemplates.title': 'Шаблоны упаковки',
'admin.packingTemplates.subtitle':
'Создавайте многоразовые списки вещей для поездок',
'admin.packingTemplates.create': 'Новый шаблон',
'admin.packingTemplates.namePlaceholder':
'Название шаблона (напр. Пляжный отдых)',
'admin.packingTemplates.empty': 'Шаблоны ещё не созданы',
'admin.packingTemplates.items': 'вещей',
'admin.packingTemplates.categories': 'категорий',
'admin.packingTemplates.itemName': 'Название вещи',
'admin.packingTemplates.itemCategory': 'Категория',
'admin.packingTemplates.categoryName': 'Название категории (напр. Одежда)',
'admin.packingTemplates.addCategory': 'Добавить категорию',
'admin.packingTemplates.created': 'Шаблон создан',
'admin.packingTemplates.deleted': 'Шаблон удалён',
'admin.packingTemplates.loadError': 'Ошибка загрузки шаблонов',
'admin.packingTemplates.createError': 'Ошибка создания шаблона',
'admin.packingTemplates.deleteError': 'Ошибка удаления шаблона',
'admin.packingTemplates.saveError': 'Ошибка сохранения',
'admin.tabs.addons': 'Дополнения',
'admin.addons.title': 'Дополнения',
'admin.addons.subtitle':
'Включайте или отключайте функции для настройки TREK под себя.',
'admin.addons.catalog.memories.name': 'Фото (Immich)',
'admin.addons.catalog.memories.description':
'Делитесь фотографиями из поездок через Immich',
'admin.addons.catalog.mcp.name': 'MCP',
'admin.addons.catalog.mcp.description':
'Протокол контекста модели для интеграции с ИИ-ассистентами',
'admin.addons.catalog.packing.name': 'Списки',
'admin.addons.catalog.packing.description':
'Списки вещей и задачи для ваших поездок',
'admin.addons.catalog.budget.name': 'Бюджет',
'admin.addons.catalog.budget.description':
'Отслеживайте расходы и планируйте бюджет поездки',
'admin.addons.catalog.documents.name': 'Документы',
'admin.addons.catalog.documents.description':
'Храните и управляйте документами для путешествий',
'admin.addons.catalog.vacay.name': 'Vacay',
'admin.addons.catalog.vacay.description':
'Личный планировщик отпусков с календарём',
'admin.addons.catalog.atlas.name': 'Atlas',
'admin.addons.catalog.atlas.description':
'Карта мира с посещёнными странами и статистикой путешествий',
'admin.addons.catalog.collab.name': 'Collab',
'admin.addons.catalog.collab.description':
'Заметки в реальном времени, опросы и чат для планирования поездок',
'admin.addons.subtitleBefore':
'Включайте или отключайте функции для настройки ',
'admin.addons.subtitleAfter': ' под себя.',
'admin.addons.enabled': 'Включено',
'admin.addons.disabled': 'Отключено',
'admin.addons.type.trip': 'Поездка',
'admin.addons.type.global': 'Глобально',
'admin.addons.type.integration': 'Интеграция',
'admin.addons.tripHint': 'Доступно как вкладка внутри каждой поездки',
'admin.addons.globalHint':
'Доступно как отдельный раздел в основной навигации',
'admin.addons.integrationHint':
'Фоновые сервисы и API-интеграции без отдельной страницы',
'admin.addons.toast.updated': 'Дополнение обновлено',
'admin.addons.toast.error': 'Не удалось обновить дополнение',
'admin.addons.noAddons': 'Нет доступных дополнений',
'admin.weather.title': 'Данные о погоде',
'admin.weather.badge': 'С 24 марта 2026',
'admin.weather.description':
'TREK использует Open-Meteo как источник данных о погоде. Open-Meteo — бесплатный сервис с открытым кодом, API-ключ не требуется.',
'admin.weather.forecast': 'Прогноз на 16 дней',
'admin.weather.forecastDesc': 'Ранее 5 дней (OpenWeatherMap)',
'admin.weather.climate': 'Исторические климатические данные',
'admin.weather.climateDesc':
'Средние значения за последние 85 лет для дней за пределами 16-дневного прогноза',
'admin.weather.requests': '10 000 запросов / день',
'admin.weather.requestsDesc': 'Бесплатно, API-ключ не требуется',
'admin.weather.locationHint':
'Погода основана на первом месте с координатами в каждом дне. Если ни одно место не назначено на день, в качестве ориентира используется любое место из списка.',
'admin.tabs.mcpTokens': 'MCP-доступ',
'admin.mcpTokens.title': 'MCP-доступ',
'admin.mcpTokens.subtitle':
'Управление OAuth-сессиями и API-токенами всех пользователей',
'admin.mcpTokens.sectionTitle': 'API-токены',
'admin.mcpTokens.owner': 'Владелец',
'admin.mcpTokens.tokenName': 'Название токена',
'admin.mcpTokens.created': 'Создан',
'admin.mcpTokens.lastUsed': 'Последнее использование',
'admin.mcpTokens.never': 'Никогда',
'admin.mcpTokens.empty': 'MCP-токены ещё не созданы',
'admin.mcpTokens.deleteTitle': 'Удалить токен',
'admin.mcpTokens.deleteMessage':
'Токен будет немедленно отозван. Пользователь потеряет доступ к MCP через этот токен.',
'admin.mcpTokens.deleteSuccess': 'Токен удалён',
'admin.mcpTokens.deleteError': 'Не удалось удалить токен',
'admin.mcpTokens.loadError': 'Не удалось загрузить токены',
'admin.oauthSessions.sectionTitle': 'OAuth-сессии',
'admin.oauthSessions.clientName': 'Клиент',
'admin.oauthSessions.owner': 'Владелец',
'admin.oauthSessions.scopes': 'Права доступа',
'admin.oauthSessions.created': 'Создано',
'admin.oauthSessions.empty': 'Нет активных OAuth-сессий',
'admin.oauthSessions.revokeTitle': 'Отозвать сессию',
'admin.oauthSessions.revokeMessage':
'Эта OAuth-сессия будет немедленно отозвана. Клиент потеряет доступ к MCP.',
'admin.oauthSessions.revokeSuccess': 'Сессия отозвана',
'admin.oauthSessions.revokeError': 'Не удалось отозвать сессию',
'admin.oauthSessions.loadError': 'Не удалось загрузить OAuth-сессии',
'admin.tabs.github': 'GitHub',
'admin.audit.subtitle':
'События, связанные с безопасностью и администрированием (резервные копии, пользователи, MFA, настройки).',
'admin.audit.empty': 'Записей аудита пока нет.',
'admin.audit.refresh': 'Обновить',
'admin.audit.loadMore': 'Загрузить ещё',
'admin.audit.showing': 'Загружено: {count} · всего {total}',
'admin.audit.col.time': 'Время',
'admin.audit.col.user': 'Пользователь',
'admin.audit.col.action': 'Действие',
'admin.audit.col.resource': 'Объект',
'admin.audit.col.ip': 'IP',
'admin.audit.col.details': 'Подробности',
'admin.github.title': 'История релизов',
'admin.github.subtitle': 'Последние обновления из {repo}',
'admin.github.latest': 'Последний',
'admin.github.prerelease': 'Пре-релиз',
'admin.github.showDetails': 'Показать подробности',
'admin.github.hideDetails': 'Скрыть подробности',
'admin.github.loadMore': 'Загрузить ещё',
'admin.github.loading': 'Загрузка...',
'admin.github.support': 'Помогает продолжать разработку TREK',
'admin.github.error': 'Не удалось загрузить релизы',
'admin.github.by': 'от',
'admin.update.available': 'Доступно обновление',
'admin.update.text':
'Доступна версия TREK {version}. У вас установлена {current}.',
'admin.update.button': 'Посмотреть на GitHub',
'admin.update.install': 'Установить обновление',
'admin.update.confirmTitle': 'Установить обновление?',
'admin.update.confirmText':
'TREK будет обновлён с {current} до {version}. Сервер перезапустится автоматически.',
'admin.update.dataInfo':
'Все ваши данные (поездки, пользователи, API-ключи, загрузки, Vacay, Atlas, бюджеты) будут сохранены.',
'admin.update.warning':
'Приложение будет кратковременно недоступно во время перезапуска.',
'admin.update.confirm': 'Обновить сейчас',
'admin.update.installing': 'Обновление…',
'admin.update.success': 'Обновление установлено! Сервер перезапускается…',
'admin.update.failed': 'Ошибка обновления',
'admin.update.backupHint':
'Рекомендуем создать резервную копию перед обновлением.',
'admin.update.backupLink': 'Перейти к резервным копиям',
'admin.update.howTo': 'Как обновить',
'admin.update.dockerText':
'Ваш экземпляр TREK работает в Docker. Для обновления до {version} выполните следующие команды на сервере:',
'admin.update.reloadHint': 'Перезагрузите страницу через несколько секунд.',
'admin.tabs.permissions': 'Разрешения',
'admin.notifications.emailPanel.title': 'Email (SMTP)',
'admin.notifications.webhookPanel.title': 'Webhook',
'admin.notifications.inappPanel.title': 'In-App',
'admin.notifications.inappPanel.hint':
'Уведомления в приложении всегда активны и не могут быть отключены глобально.',
'admin.notifications.adminWebhookPanel.title': 'Вебхук администратора',
'admin.notifications.adminWebhookPanel.hint':
'Этот вебхук используется исключительно для уведомлений администратора (например, оповещения о версиях). Он независим от пользовательских вебхуков и отправляется автоматически при наличии URL.',
'admin.notifications.adminWebhookPanel.saved':
'URL вебхука администратора сохранён',
'admin.notifications.adminWebhookPanel.testSuccess':
'Тестовый вебхук успешно отправлен',
'admin.notifications.adminWebhookPanel.testFailed':
'Ошибка тестового вебхука',
'admin.notifications.adminWebhookPanel.alwaysOnHint':
'Вебхук администратора отправляется автоматически при наличии URL',
'admin.notifications.ntfy': 'Ntfy',
'admin.ntfy.hint':
'Позволяет пользователям настраивать собственные темы ntfy для push-уведомлений. Установите сервер по умолчанию ниже, чтобы предварительно заполнить настройки пользователей.',
'admin.notifications.testNtfy': 'Отправить тестовое Ntfy',
'admin.notifications.testNtfySuccess': 'Тестовое Ntfy успешно отправлено',
'admin.notifications.testNtfyFailed': 'Ошибка отправки тестового Ntfy',
'admin.notifications.adminNtfyPanel.title': 'Ntfy администратора',
'admin.notifications.adminNtfyPanel.hint':
'Эта тема Ntfy используется исключительно для уведомлений администратора (например, оповещения о версиях). Она независима от тем пользователей и всегда отправляется при наличии настройки.',
'admin.notifications.adminNtfyPanel.serverLabel': 'URL сервера Ntfy',
'admin.notifications.adminNtfyPanel.serverHint':
'Также используется как сервер по умолчанию для ntfy-уведомлений пользователей. Оставьте пустым, чтобы использовать ntfy.sh. Пользователи могут изменить это в своих настройках.',
'admin.notifications.adminNtfyPanel.serverPlaceholder': 'https://ntfy.sh',
'admin.notifications.adminNtfyPanel.topicLabel': 'Тема администратора',
'admin.notifications.adminNtfyPanel.topicPlaceholder': 'trek-admin-alerts',
'admin.notifications.adminNtfyPanel.tokenLabel':
'Токен доступа (необязательно)',
'admin.notifications.adminNtfyPanel.tokenCleared':
'Токен доступа администратора очищен',
'admin.notifications.adminNtfyPanel.saved':
'Настройки Ntfy администратора сохранены',
'admin.notifications.adminNtfyPanel.test': 'Отправить тестовое Ntfy',
'admin.notifications.adminNtfyPanel.testSuccess':
'Тестовое Ntfy успешно отправлено',
'admin.notifications.adminNtfyPanel.testFailed':
'Ошибка отправки тестового Ntfy',
'admin.notifications.adminNtfyPanel.alwaysOnHint':
'Ntfy администратора всегда отправляется при наличии настроенной темы',
'admin.notifications.adminNotificationsHint':
'Настройте, какие каналы доставляют уведомления администратора (например, оповещения о версиях). Вебхук отправляется автоматически, если задан URL вебхука администратора.',
'admin.notifications.tripReminders.title': 'Напоминания о поездках',
'admin.notifications.tripReminders.hint':
'Отправляет напоминание перед началом поездки (необходимо указать дни напоминания в параметрах поездки).',
'admin.notifications.tripReminders.enabled':
'Напоминания о поездках включены',
'admin.notifications.tripReminders.disabled':
'Напоминания о поездках отключены',
'admin.tabs.notifications': 'Уведомления',
'admin.addons.catalog.journey.name': 'Путешествие',
'admin.addons.catalog.journey.description':
'Отслеживание поездок и дневник путешествий с отметками, фото и ежедневными историями',
};
export default admin;
+6
View File
@@ -0,0 +1,6 @@
import type { TranslationStrings } from '../types';
const airport: TranslationStrings = {
'airport.searchPlaceholder': 'Код аэропорта или город (напр. FRA)',
};
export default airport;
+59
View File
@@ -0,0 +1,59 @@
import type { TranslationStrings } from '../types';
const atlas: TranslationStrings = {
'atlas.subtitle': 'Ваш след путешествий по всему миру',
'atlas.countries': 'Страны',
'atlas.trips': 'Поездки',
'atlas.places': 'Места',
'atlas.days': 'Дни',
'atlas.visitedCountries': 'Посещённые страны',
'atlas.cities': 'Города',
'atlas.noData': 'Данных о поездках пока нет',
'atlas.noDataHint':
'Создайте поездку и добавьте места, чтобы увидеть карту мира',
'atlas.lastTrip': 'Последняя поездка',
'atlas.nextTrip': 'Следующая поездка',
'atlas.daysLeft': 'дней осталось',
'atlas.streak': 'Серия',
'atlas.year': 'год',
'atlas.years': 'лет',
'atlas.yearInRow': 'год подряд',
'atlas.yearsInRow': 'лет подряд',
'atlas.tripIn': 'поездка в',
'atlas.tripsIn': 'поездок в',
'atlas.since': 'с',
'atlas.europe': 'Европа',
'atlas.asia': 'Азия',
'atlas.northAmerica': 'Сев. Америка',
'atlas.southAmerica': 'Юж. Америка',
'atlas.africa': 'Африка',
'atlas.oceania': 'Океания',
'atlas.other': 'Другое',
'atlas.firstVisit': 'Первая поездка',
'atlas.lastVisitLabel': 'Последняя поездка',
'atlas.tripSingular': 'Поездка',
'atlas.tripPlural': 'Поездки',
'atlas.placeVisited': 'Посещённое место',
'atlas.placesVisited': 'Посещённые места',
'atlas.statsTab': 'Статистика',
'atlas.bucketTab': 'Список желаний',
'atlas.addBucket': 'Добавить в список желаний',
'atlas.bucketNamePlaceholder': 'Место или направление...',
'atlas.bucketNotesPlaceholder': 'Заметки (необязательно)',
'atlas.bucketEmpty': 'Ваш список желаний пуст',
'atlas.bucketEmptyHint': 'Добавьте места, которые мечтаете посетить',
'atlas.unmark': 'Удалить',
'atlas.confirmMark': 'Отметить эту страну как посещённую?',
'atlas.confirmUnmark': 'Удалить эту страну из списка посещённых?',
'atlas.confirmUnmarkRegion': 'Удалить этот регион из списка посещённых?',
'atlas.markVisited': 'Отметить как посещённую',
'atlas.markVisitedHint': 'Добавить эту страну в список посещённых',
'atlas.markRegionVisitedHint': 'Добавить этот регион в список посещённых',
'atlas.addToBucket': 'В список желаний',
'atlas.addPoi': 'Добавить место',
'atlas.searchCountry': 'Поиск страны...',
'atlas.month': 'Месяц',
'atlas.addToBucketHint': 'Сохранить как место для посещения',
'atlas.bucketWhen': 'Когда вы планируете поехать?',
};
export default atlas;
+78
View File
@@ -0,0 +1,78 @@
import type { TranslationStrings } from '../types';
const backup: TranslationStrings = {
'backup.title': 'Резервная копия',
'backup.subtitle': 'База данных и все загруженные файлы',
'backup.refresh': 'Обновить',
'backup.upload': 'Загрузить копию',
'backup.uploading': 'Загрузка…',
'backup.create': 'Создать копию',
'backup.creating': 'Создание…',
'backup.empty': 'Резервных копий нет',
'backup.createFirst': 'Создать первую копию',
'backup.download': 'Скачать',
'backup.restore': 'Восстановить',
'backup.confirm.restore':
'Восстановить копию «{name}»?\n\nВсе текущие данные будут заменены данными из копии.',
'backup.confirm.uploadRestore':
'Загрузить и восстановить файл копии «{name}»?\n\nВсе текущие данные будут перезаписаны.',
'backup.confirm.delete': 'Удалить копию «{name}»?',
'backup.toast.loadError': 'Не удалось загрузить резервные копии',
'backup.toast.created': 'Резервная копия создана',
'backup.toast.createError': 'Не удалось создать резервную копию',
'backup.toast.restored': 'Копия восстановлена. Страница перезагрузится…',
'backup.toast.restoreError': 'Ошибка восстановления',
'backup.toast.uploadError': 'Ошибка загрузки',
'backup.toast.deleted': 'Резервная копия удалена',
'backup.toast.deleteError': 'Ошибка удаления',
'backup.toast.downloadError': 'Ошибка скачивания',
'backup.toast.settingsSaved': 'Настройки автокопирования сохранены',
'backup.toast.settingsError': 'Не удалось сохранить настройки',
'backup.auto.title': 'Автокопирование',
'backup.auto.subtitle': 'Автоматическое резервное копирование по расписанию',
'backup.auto.enable': 'Включить автокопирование',
'backup.auto.enableHint':
'Резервные копии будут создаваться автоматически по выбранному расписанию',
'backup.auto.interval': 'Интервал',
'backup.auto.hour': 'Запуск в час',
'backup.auto.hourHint': 'Местное время сервера (формат {format})',
'backup.auto.dayOfWeek': 'День недели',
'backup.auto.dayOfMonth': 'День месяца',
'backup.auto.dayOfMonthHint':
'Ограничено 1–28 для совместимости со всеми месяцами',
'backup.auto.scheduleSummary': 'Расписание',
'backup.auto.summaryDaily': 'Каждый день в {hour}:00',
'backup.auto.summaryWeekly': 'Каждый {day} в {hour}:00',
'backup.auto.summaryMonthly': '{day}-го числа каждого месяца в {hour}:00',
'backup.auto.envLocked': 'Docker',
'backup.auto.envLockedHint':
'Автокопирование настроено через переменные окружения Docker. Чтобы изменить параметры, обновите docker-compose.yml и перезапустите контейнер.',
'backup.auto.copyEnv': 'Скопировать переменные окружения Docker',
'backup.auto.envCopied':
'Переменные окружения Docker скопированы в буфер обмена',
'backup.auto.keepLabel': 'Удалять старые копии через',
'backup.dow.sunday': 'Вс',
'backup.dow.monday': 'Пн',
'backup.dow.tuesday': 'Вт',
'backup.dow.wednesday': 'Ср',
'backup.dow.thursday': 'Чт',
'backup.dow.friday': 'Пт',
'backup.dow.saturday': 'Сб',
'backup.interval.hourly': 'Каждый час',
'backup.interval.daily': 'Ежедневно',
'backup.interval.weekly': 'Еженедельно',
'backup.interval.monthly': 'Ежемесячно',
'backup.keep.1day': '1 день',
'backup.keep.3days': '3 дня',
'backup.keep.7days': '7 дней',
'backup.keep.14days': '14 дней',
'backup.keep.30days': '30 дней',
'backup.keep.forever': 'Хранить вечно',
'backup.restoreConfirmTitle': 'Восстановить копию?',
'backup.restoreWarning':
'Все текущие данные (поездки, места, пользователи, загрузки) будут безвозвратно заменены данными из копии. Это действие нельзя отменить.',
'backup.restoreTip':
'Совет: создайте резервную копию текущего состояния перед восстановлением.',
'backup.restoreConfirm': 'Да, восстановить',
};
export default backup;
+44
View File
@@ -0,0 +1,44 @@
import type { TranslationStrings } from '../types';
const budget: TranslationStrings = {
'budget.title': 'Бюджет',
'budget.exportCsv': 'Экспорт CSV',
'budget.emptyTitle': 'Бюджет ещё не создан',
'budget.emptyText':
'Создайте категории и записи для планирования бюджета поездки',
'budget.emptyPlaceholder': 'Введите название категории...',
'budget.createCategory': 'Создать категорию',
'budget.category': 'Категория',
'budget.categoryName': 'Название категории',
'budget.table.name': 'Название',
'budget.table.total': 'Итого',
'budget.table.persons': 'Человек',
'budget.table.days': 'Дней',
'budget.table.perPerson': 'На человека',
'budget.table.perDay': 'В день',
'budget.table.perPersonDay': 'Чел. / день',
'budget.table.note': 'Заметка',
'budget.table.date': 'Дата',
'budget.newEntry': 'Новая запись',
'budget.defaultEntry': 'Новая запись',
'budget.defaultCategory': 'Новая категория',
'budget.total': 'Итого',
'budget.totalBudget': 'Общий бюджет',
'budget.byCategory': 'По категориям',
'budget.editTooltip': 'Нажмите для редактирования',
'budget.linkedToReservation':
'Связано с бронированием — редактируйте название там',
'budget.confirm.deleteCategory':
'Вы уверены, что хотите удалить категорию «{name}» с {count} записями?',
'budget.deleteCategory': 'Удалить категорию',
'budget.perPerson': 'На человека',
'budget.paid': 'Оплачено',
'budget.open': 'Не оплачено',
'budget.noMembers': 'Участники не назначены',
'budget.settlement': 'Взаиморасчёт',
'budget.settlementInfo':
'Нажмите на аватар участника в строке бюджета, чтобы отметить его зелёным — это значит, что он заплатил. Взаиморасчёт покажет, кто кому и сколько должен.',
'budget.netBalances': 'Чистые балансы',
'budget.categoriesLabel': 'категорий',
};
export default budget;
+26
View File
@@ -0,0 +1,26 @@
import type { TranslationStrings } from '../types';
const categories: TranslationStrings = {
'categories.title': 'Категории',
'categories.subtitle': 'Управление категориями мест',
'categories.new': 'Новая категория',
'categories.empty': 'Категорий пока нет',
'categories.namePlaceholder': 'Название категории',
'categories.icon': 'Иконка',
'categories.color': 'Цвет',
'categories.customColor': 'Выбрать свой цвет',
'categories.preview': 'Предпросмотр',
'categories.defaultName': 'Категория',
'categories.update': 'Обновить',
'categories.create': 'Создать',
'categories.confirm.delete':
'Удалить категорию? Места в этой категории не будут удалены.',
'categories.toast.loadError': 'Не удалось загрузить категории',
'categories.toast.nameRequired': 'Введите название',
'categories.toast.updated': 'Категория обновлена',
'categories.toast.created': 'Категория создана',
'categories.toast.saveError': 'Ошибка сохранения',
'categories.toast.deleted': 'Категория удалена',
'categories.toast.deleteError': 'Ошибка удаления',
};
export default categories;
+75
View File
@@ -0,0 +1,75 @@
import type { TranslationStrings } from '../types';
const collab: TranslationStrings = {
'collab.tabs.chat': 'Чат',
'collab.tabs.notes': 'Заметки',
'collab.tabs.polls': 'Опросы',
'collab.whatsNext.title': 'Что дальше',
'collab.whatsNext.today': 'Сегодня',
'collab.whatsNext.tomorrow': 'Завтра',
'collab.whatsNext.empty': 'Нет предстоящих активностей',
'collab.whatsNext.until': 'до',
'collab.whatsNext.emptyHint':
'Активности со временем будут отображаться здесь',
'collab.chat.send': 'Отправить',
'collab.chat.placeholder': 'Введите сообщение...',
'collab.chat.empty': 'Начните разговор',
'collab.chat.emptyHint': 'Сообщения видны всем участникам поездки',
'collab.chat.emptyDesc':
'Делитесь идеями, планами и новостями с вашей группой',
'collab.chat.today': 'Сегодня',
'collab.chat.yesterday': 'Вчера',
'collab.chat.deletedMessage': 'удалил(а) сообщение',
'collab.chat.reply': 'Ответить',
'collab.chat.loadMore': 'Загрузить старые сообщения',
'collab.chat.justNow': 'только что',
'collab.chat.minutesAgo': '{n} мин. назад',
'collab.chat.hoursAgo': '{n} ч. назад',
'collab.notes.title': 'Заметки',
'collab.notes.new': 'Новая заметка',
'collab.notes.empty': 'Заметок пока нет',
'collab.notes.emptyHint': 'Начните записывать идеи и планы',
'collab.notes.all': 'Все',
'collab.notes.titlePlaceholder': 'Название заметки',
'collab.notes.contentPlaceholder': 'Напишите что-нибудь...',
'collab.notes.categoryPlaceholder': 'Категория',
'collab.notes.newCategory': 'Новая категория...',
'collab.notes.category': 'Категория',
'collab.notes.noCategory': 'Без категории',
'collab.notes.color': 'Цвет',
'collab.notes.save': 'Сохранить',
'collab.notes.cancel': 'Отмена',
'collab.notes.edit': 'Редактировать',
'collab.notes.delete': 'Удалить',
'collab.notes.pin': 'Закрепить',
'collab.notes.unpin': 'Открепить',
'collab.notes.daysAgo': '{n} дн. назад',
'collab.notes.categorySettings': 'Управление категориями',
'collab.notes.create': 'Создать',
'collab.notes.website': 'Сайт',
'collab.notes.websitePlaceholder': 'https://...',
'collab.notes.attachFiles': 'Прикрепить файлы',
'collab.notes.noCategoriesYet': 'Категорий пока нет',
'collab.notes.emptyDesc': 'Создайте заметку, чтобы начать',
'collab.polls.title': 'Опросы',
'collab.polls.new': 'Новый опрос',
'collab.polls.empty': 'Опросов пока нет',
'collab.polls.emptyHint': 'Задайте вопрос группе и голосуйте вместе',
'collab.polls.question': 'Вопрос',
'collab.polls.questionPlaceholder': 'Что нам делать?',
'collab.polls.addOption': '+ Добавить вариант',
'collab.polls.optionPlaceholder': 'Вариант {n}',
'collab.polls.create': 'Создать опрос',
'collab.polls.close': 'Закрыть',
'collab.polls.closed': 'Закрыт',
'collab.polls.votes': '{n} голосов',
'collab.polls.vote': '{n} голос',
'collab.polls.multipleChoice': 'Множественный выбор',
'collab.polls.multiChoice': 'Множественный выбор',
'collab.polls.deadline': 'Срок',
'collab.polls.option': 'Вариант',
'collab.polls.options': 'Варианты',
'collab.polls.delete': 'Удалить',
'collab.polls.closedSection': 'Закрытые',
};
export default collab;
+54
View File
@@ -0,0 +1,54 @@
import type { TranslationStrings } from '../types';
const common: TranslationStrings = {
'common.save': 'Сохранить',
'common.showMore': 'Показать больше',
'common.showLess': 'Показать меньше',
'common.cancel': 'Отмена',
'common.clear': 'Очистить',
'common.delete': 'Удалить',
'common.edit': 'Редактировать',
'common.add': 'Добавить',
'common.loading': 'Загрузка...',
'common.import': 'Импорт',
'common.select': 'Выбрать',
'common.selectAll': 'Выбрать всё',
'common.deselectAll': 'Снять выделение со всех',
'common.error': 'Ошибка',
'common.unknownError': 'Неизвестная ошибка',
'common.tooManyAttempts': 'Слишком много попыток. Попробуйте позже.',
'common.back': 'Назад',
'common.all': 'Все',
'common.close': 'Закрыть',
'common.open': 'Открыть',
'common.upload': 'Загрузить',
'common.search': 'Поиск',
'common.confirm': 'Подтвердить',
'common.ok': 'ОК',
'common.yes': 'Да',
'common.no': 'Нет',
'common.or': 'или',
'common.none': 'Нет',
'common.date': 'Дата',
'common.rename': 'Переименовать',
'common.discardChanges': 'Отменить изменения',
'common.discard': 'Отменить',
'common.name': 'Имя',
'common.email': 'Эл. почта',
'common.password': 'Пароль',
'common.saving': 'Сохранение...',
'common.saved': 'Сохранено',
'common.expand': 'Развернуть',
'common.collapse': 'Свернуть',
'common.update': 'Обновить',
'common.change': 'Изменить',
'common.uploading': 'Загрузка…',
'common.backToPlanning': 'Вернуться к планированию',
'common.reset': 'Сбросить',
'common.copy': 'Копировать',
'common.copied': 'Скопировано',
'common.justNow': 'только что',
'common.hoursAgo': '{count} ч назад',
'common.daysAgo': '{count} д назад',
};
export default common;
+107
View File
@@ -0,0 +1,107 @@
import type { TranslationStrings } from '../types';
const dashboard: TranslationStrings = {
'dashboard.title': 'Мои поездки',
'dashboard.subtitle.loading': 'Загрузка поездок...',
'dashboard.subtitle.trips': '{count} поездок ({archived} в архиве)',
'dashboard.subtitle.empty': 'Начните свою первую поездку',
'dashboard.subtitle.activeOne': '{count} активная поездка',
'dashboard.subtitle.activeMany': '{count} активных поездок',
'dashboard.subtitle.archivedSuffix': ' · {count} в архиве',
'dashboard.newTrip': 'Новая поездка',
'dashboard.gridView': 'Плитка',
'dashboard.listView': 'Список',
'dashboard.currency': 'Валюта',
'dashboard.timezone': 'Часовые пояса',
'dashboard.localTime': 'Местное',
'dashboard.timezoneCustomTitle': 'Свой часовой пояс',
'dashboard.timezoneCustomLabelPlaceholder': 'Название (необязательно)',
'dashboard.timezoneCustomTzPlaceholder': 'напр. America/New_York',
'dashboard.timezoneCustomAdd': 'Добавить',
'dashboard.timezoneCustomErrorEmpty': 'Введите идентификатор часового пояса',
'dashboard.timezoneCustomErrorInvalid':
'Неверный часовой пояс. Используйте формат Europe/Berlin',
'dashboard.timezoneCustomErrorDuplicate': 'Уже добавлен',
'dashboard.emptyTitle': 'Нет поездок',
'dashboard.emptyText': 'Создайте свою первую поездку и начните планировать!',
'dashboard.emptyButton': 'Создать первую поездку',
'dashboard.nextTrip': 'Следующая поездка',
'dashboard.shared': 'Общая',
'dashboard.sharedBy': 'Поделился {name}',
'dashboard.days': 'Дни',
'dashboard.places': 'Места',
'dashboard.members': 'Попутчики',
'dashboard.archive': 'Архивировать',
'dashboard.copyTrip': 'Копировать',
'dashboard.copySuffix': 'копия',
'dashboard.restore': 'Восстановить',
'dashboard.archived': 'В архиве',
'dashboard.status.ongoing': 'В процессе',
'dashboard.status.today': 'Сегодня',
'dashboard.status.tomorrow': 'Завтра',
'dashboard.status.past': 'Прошло',
'dashboard.status.daysLeft': 'осталось {count} дн.',
'dashboard.toast.loadError': 'Не удалось загрузить поездки',
'dashboard.toast.created': 'Поездка создана!',
'dashboard.toast.createError': 'Не удалось создать поездку',
'dashboard.toast.updated': 'Поездка обновлена!',
'dashboard.toast.updateError': 'Не удалось обновить поездку',
'dashboard.toast.deleted': 'Поездка удалена',
'dashboard.toast.deleteError': 'Не удалось удалить поездку',
'dashboard.toast.archived': 'Поездка архивирована',
'dashboard.toast.archiveError': 'Не удалось архивировать поездку',
'dashboard.toast.restored': 'Поездка восстановлена',
'dashboard.toast.restoreError': 'Не удалось восстановить поездку',
'dashboard.toast.copied': 'Поездка скопирована!',
'dashboard.toast.copyError': 'Не удалось скопировать поездку',
'dashboard.confirm.delete':
'Удалить поездку «{title}»? Все места и планы будут безвозвратно удалены.',
'dashboard.editTrip': 'Редактировать поездку',
'dashboard.createTrip': 'Создать новую поездку',
'dashboard.tripTitle': 'Название',
'dashboard.tripTitlePlaceholder': 'напр. Лето в Японии',
'dashboard.tripDescription': 'Описание',
'dashboard.tripDescriptionPlaceholder': 'О чём эта поездка?',
'dashboard.startDate': 'Дата начала',
'dashboard.endDate': 'Дата окончания',
'dashboard.dayCount': 'Количество дней',
'dashboard.dayCountHint':
'Сколько дней планировать, если даты поездки не указаны.',
'dashboard.noDateHint':
'Дата не указана — будет создано 7 дней по умолчанию. Вы можете изменить это в любое время.',
'dashboard.coverImage': 'Обложка',
'dashboard.addCoverImage': 'Добавить обложку',
'dashboard.addMembers': 'Попутчики',
'dashboard.addMember': 'Добавить участника',
'dashboard.coverSaved': 'Обложка сохранена',
'dashboard.coverUploadError': 'Ошибка загрузки',
'dashboard.coverRemoveError': 'Ошибка удаления',
'dashboard.titleRequired': 'Название обязательно',
'dashboard.endDateError': 'Дата окончания должна быть позже даты начала',
'dashboard.greeting.morning': 'Доброе утро,',
'dashboard.greeting.afternoon': 'Добрый день,',
'dashboard.greeting.evening': 'Добрый вечер,',
'dashboard.mobile.liveNow': 'Сейчас в пути',
'dashboard.mobile.tripProgress': 'Прогресс поездки',
'dashboard.mobile.daysLeft': 'осталось {count} дн.',
'dashboard.mobile.places': 'Места',
'dashboard.mobile.buddies': 'Попутчики',
'dashboard.mobile.newTrip': 'Новая поездка',
'dashboard.mobile.currency': 'Валюта',
'dashboard.mobile.timezone': 'Часовой пояс',
'dashboard.mobile.upcomingTrips': 'Предстоящие поездки',
'dashboard.mobile.yourTrips': 'Ваши поездки',
'dashboard.mobile.trips': 'поездок',
'dashboard.mobile.starts': 'Начало',
'dashboard.mobile.duration': 'Продолжительность',
'dashboard.mobile.day': 'день',
'dashboard.mobile.days': 'дней',
'dashboard.mobile.ongoing': 'В процессе',
'dashboard.mobile.startsToday': 'Начинается сегодня',
'dashboard.mobile.tomorrow': 'Завтра',
'dashboard.mobile.inDays': 'Через {count} дн.',
'dashboard.mobile.inMonths': 'Через {count} мес.',
'dashboard.mobile.completed': 'Завершено',
'dashboard.mobile.currencyConverter': 'Конвертер валют',
};
export default dashboard;
+26
View File
@@ -0,0 +1,26 @@
import type { TranslationStrings } from '../types';
const day: TranslationStrings = {
'day.precipProb': 'Вероятность осадков',
'day.precipitation': 'Осадки',
'day.wind': 'Ветер',
'day.sunrise': 'Восход',
'day.sunset': 'Закат',
'day.hourlyForecast': 'Почасовой прогноз',
'day.climateHint':
'Исторические средние — реальный прогноз доступен за 16 дней до этой даты.',
'day.noWeather': 'Данные о погоде недоступны. Добавьте место с координатами.',
'day.overview': 'Обзор дня',
'day.accommodation': 'Жильё',
'day.addAccommodation': 'Добавить жильё',
'day.hotelDayRange': 'Применить к дням',
'day.noPlacesForHotel': 'Сначала добавьте места в поездку',
'day.allDays': 'Все',
'day.checkIn': 'Заезд',
'day.checkInUntil': 'До',
'day.checkOut': 'Выезд',
'day.confirmation': 'Подтверждение',
'day.editAccommodation': 'Редактировать жильё',
'day.reservations': 'Бронирования',
};
export default day;
+46
View File
@@ -0,0 +1,46 @@
import type { TranslationStrings } from '../types';
const dayplan: TranslationStrings = {
'dayplan.icsTooltip': 'Экспорт календаря (ICS)',
'dayplan.emptyDay': 'На этот день мест не запланировано',
'dayplan.addNote': 'Добавить заметку',
'dayplan.editNote': 'Редактировать заметку',
'dayplan.noteAdd': 'Добавить заметку',
'dayplan.noteEdit': 'Редактировать заметку',
'dayplan.noteTitle': 'Заметка',
'dayplan.noteSubtitle': 'Заметка на день',
'dayplan.totalCost': 'Общая стоимость',
'dayplan.days': 'Дни',
'dayplan.dayN': 'День {n}',
'dayplan.calculating': 'Расчёт...',
'dayplan.route': 'Маршрут',
'dayplan.optimize': 'Оптимизировать',
'dayplan.optimized': 'Маршрут оптимизирован',
'dayplan.routeError': 'Не удалось рассчитать маршрут',
'dayplan.toast.needTwoPlaces':
'Для оптимизации маршрута нужно минимум два места',
'dayplan.toast.routeOptimized': 'Маршрут оптимизирован',
'dayplan.toast.noGeoPlaces':
'Не найдено мест с координатами для расчёта маршрута',
'dayplan.confirmed': 'Подтверждено',
'dayplan.pendingRes': 'Ожидание',
'dayplan.pdf': 'PDF',
'dayplan.pdfTooltip': 'Экспортировать план дня в PDF',
'dayplan.pdfError': 'Ошибка экспорта PDF',
'dayplan.cannotReorderTransport':
'Бронирования с фиксированным временем нельзя перемещать',
'dayplan.confirmRemoveTimeTitle': 'Удалить время?',
'dayplan.confirmRemoveTimeBody':
'У этого места фиксированное время ({time}). При перемещении время будет удалено, и станет доступна свободная сортировка.',
'dayplan.confirmRemoveTimeAction': 'Удалить время и переместить',
'dayplan.cannotDropOnTimed':
'Элементы нельзя размещать между записями с фиксированным временем',
'dayplan.cannotBreakChronology':
'Это нарушит хронологический порядок запланированных элементов и бронирований',
'dayplan.mobile.addPlace': 'Добавить место',
'dayplan.mobile.searchPlaces': 'Поиск мест...',
'dayplan.mobile.allAssigned': 'Все места распределены',
'dayplan.mobile.noMatch': 'Нет совпадений',
'dayplan.mobile.createNew': 'Создать новое место',
};
export default dayplan;
@@ -0,0 +1,63 @@
import type { NotificationLocale } from '../externalNotifications/types';
const ru: NotificationLocale = {
email: {
footer: 'Вы получили это, потому что у вас включены уведомления в TREK.',
manage: 'Управление настройками',
madeWith: 'Made with',
openTrek: 'Открыть TREK',
},
events: {
trip_invite: (p) => ({
title: `Приглашение в "${p.trip}"`,
body: `${p.actor} пригласил ${p.invitee || 'участника'} в поездку "${p.trip}".`,
}),
booking_change: (p) => ({
title: `Новое бронирование: ${p.booking}`,
body: `${p.actor} добавил бронирование "${p.booking}" (${p.type}) в "${p.trip}".`,
}),
trip_reminder: (p) => ({
title: `Напоминание: ${p.trip}`,
body: `Ваша поездка "${p.trip}" скоро начнётся!`,
}),
todo_due: (p) => ({
title: `Задача к сроку: ${p.todo}`,
body: `"${p.todo}" в поездке "${p.trip}" — срок ${p.due}.`,
}),
vacay_invite: (p) => ({
title: 'Приглашение Vacay Fusion',
body: `${p.actor} приглашает вас объединить планы отпуска. Откройте TREK для подтверждения.`,
}),
photos_shared: (p) => ({
title: `${p.count} фото`,
body: `${p.actor} поделился ${p.count} фото в "${p.trip}".`,
}),
collab_message: (p) => ({
title: `Новое сообщение в "${p.trip}"`,
body: `${p.actor}: ${p.preview}`,
}),
packing_tagged: (p) => ({
title: `Список вещей: ${p.category}`,
body: `${p.actor} назначил вас в категорию "${p.category}" в "${p.trip}".`,
}),
version_available: (p) => ({
title: 'Доступна новая версия TREK',
body: `TREK ${p.version} теперь доступен. Перейдите в панель администратора для обновления.`,
}),
synology_session_cleared: () => ({
title: 'Сессия Synology сброшена',
body: 'Ваш аккаунт или URL Synology изменился. Вы вышли из Synology Photos.',
}),
},
passwordReset: {
subject: 'Сброс пароля',
greeting: 'Здравствуйте',
body: 'Мы получили запрос на сброс пароля вашего аккаунта TREK. Нажмите кнопку ниже, чтобы установить новый пароль.',
ctaIntro: 'Сбросить пароль',
expiry: 'Ссылка действительна 60 минут.',
ignore:
'Если вы не запрашивали сброс — просто проигнорируйте это письмо, пароль останется прежним.',
},
};
export default ru;
+63
View File
@@ -0,0 +1,63 @@
import type { TranslationStrings } from '../types';
const files: TranslationStrings = {
'files.title': 'Файлы',
'files.pageTitle': 'Файлы и документы',
'files.subtitle': '{count} файлов для {trip}',
'files.download': 'Скачать',
'files.openError': 'Не удалось открыть файл',
'files.downloadPdf': 'Скачать PDF',
'files.count': '{count} файлов',
'files.countSingular': '1 файл',
'files.uploaded': '{count} загружено',
'files.uploadError': 'Ошибка загрузки',
'files.dropzone': 'Перетащите файлы сюда',
'files.dropzoneHint': 'или нажмите для выбора',
'files.allowedTypes':
'Изображения, PDF, DOC, DOCX, XLS, XLSX, TXT, CSV · Макс. 50 МБ',
'files.uploading': 'Загрузка...',
'files.filterAll': 'Все',
'files.filterPdf': 'PDF',
'files.filterImages': 'Изображения',
'files.filterDocs': 'Документы',
'files.filterCollab': 'Заметки Collab',
'files.sourceCollab': 'Из заметок Collab',
'files.empty': 'Файлов пока нет',
'files.emptyHint': 'Загрузите файлы, чтобы прикрепить их к поездке',
'files.openTab': 'Открыть в новой вкладке',
'files.confirm.delete': 'Вы уверены, что хотите удалить этот файл?',
'files.toast.deleted': 'Файл удалён',
'files.toast.deleteError': 'Не удалось удалить файл',
'files.sourcePlan': 'План дня',
'files.sourceBooking': 'Бронирование',
'files.sourceTransport': 'Транспорт',
'files.attach': 'Прикрепить',
'files.pasteHint':
'Также можно вставить изображения из буфера обмена (Ctrl+V)',
'files.trash': 'Корзина',
'files.trashEmpty': 'Корзина пуста',
'files.emptyTrash': 'Очистить корзину',
'files.restore': 'Восстановить',
'files.star': 'В избранное',
'files.unstar': 'Из избранного',
'files.assign': 'Назначить',
'files.assignTitle': 'Назначить файл',
'files.assignPlace': 'Место',
'files.assignBooking': 'Бронирование',
'files.assignTransport': 'Транспорт',
'files.unassigned': 'Не назначен',
'files.unlink': 'Удалить связь',
'files.toast.trashed': 'Перемещено в корзину',
'files.toast.restored': 'Файл восстановлен',
'files.toast.trashEmptied': 'Корзина очищена',
'files.toast.assigned': 'Файл назначен',
'files.toast.assignError': 'Ошибка назначения',
'files.toast.restoreError': 'Ошибка восстановления',
'files.confirm.permanentDelete':
'Безвозвратно удалить этот файл? Это действие нельзя отменить.',
'files.confirm.emptyTrash':
'Безвозвратно удалить все файлы из корзины? Это действие нельзя отменить.',
'files.noteLabel': 'Заметка',
'files.notePlaceholder': 'Добавить заметку...',
};
export default files;
+86
View File
@@ -0,0 +1,86 @@
import admin from './admin';
import airport from './airport';
import atlas from './atlas';
import backup from './backup';
import budget from './budget';
import categories from './categories';
import collab from './collab';
import common from './common';
import dashboard from './dashboard';
import day from './day';
import dayplan from './dayplan';
import files from './files';
import inspector from './inspector';
import journey from './journey';
import login from './login';
import map from './map';
import members from './members';
import memories from './memories';
import nav from './nav';
import notif from './notif';
import notifications from './notifications';
import oauth from './oauth';
import packing from './packing';
import pdf from './pdf';
import perm from './perm';
import photos from './photos';
import places from './places';
import planner from './planner';
import register from './register';
import reservations from './reservations';
import settings from './settings';
import share from './share';
import shared from './shared';
import stats from './stats';
import system_notice from './system_notice';
import todo from './todo';
import transport from './transport';
import trip from './trip';
import trips from './trips';
import undo from './undo';
import vacay from './vacay';
const locale = {
...common,
...trips,
...nav,
...dashboard,
...settings,
...admin,
...dayplan,
...share,
...shared,
...login,
...register,
...vacay,
...atlas,
...trip,
...places,
...inspector,
...reservations,
...airport,
...map,
...budget,
...files,
...packing,
...members,
...categories,
...backup,
...photos,
...pdf,
...planner,
...stats,
...day,
...memories,
...collab,
...perm,
...undo,
...notifications,
...todo,
...notif,
...journey,
...oauth,
...system_notice,
...transport,
};
export default locale;
+22
View File
@@ -0,0 +1,22 @@
import type { TranslationStrings } from '../types';
const inspector: TranslationStrings = {
'inspector.opened': 'Открыто',
'inspector.closed': 'Закрыто',
'inspector.openingHours': 'Часы работы',
'inspector.showHours': 'Показать часы работы',
'inspector.files': 'Файлы',
'inspector.filesCount': '{count} файлов',
'inspector.removeFromDay': 'Убрать из дня',
'inspector.remove': 'Удалить',
'inspector.addToDay': 'Добавить в день',
'inspector.confirmedRes': 'Подтверждённое бронирование',
'inspector.pendingRes': 'Ожидающее бронирование',
'inspector.google': 'Открыть в Google Maps',
'inspector.website': 'Открыть сайт',
'inspector.addRes': 'Бронирование',
'inspector.editRes': 'Редактировать бронирование',
'inspector.participants': 'Участники',
'inspector.trackStats': 'Данные маршрута',
};
export default inspector;
+240
View File
@@ -0,0 +1,240 @@
import type { TranslationStrings } from '../types';
const journey: TranslationStrings = {
'journey.search.placeholder': 'Поиск путешествий…',
'journey.search.noResults': 'Путешествий по запросу «{query}» не найдено',
'journey.title': 'Путешествие',
'journey.subtitle': 'Отслеживайте свои путешествия в реальном времени',
'journey.new': 'Новое путешествие',
'journey.create': 'Создать',
'journey.titlePlaceholder': 'Куда вы едете?',
'journey.empty': 'Пока нет путешествий',
'journey.emptyHint': 'Начните документировать свою следующую поездку',
'journey.deleted': 'Путешествие удалено',
'journey.createError': 'Не удалось создать путешествие',
'journey.deleteError': 'Не удалось удалить путешествие',
'journey.deleteConfirmTitle': 'Удалить',
'journey.deleteConfirmMessage':
'Удалить «{title}»? Это действие нельзя отменить.',
'journey.deleteConfirmGeneric': 'Вы уверены, что хотите удалить это?',
'journey.notFound': 'Путешествие не найдено',
'journey.photos': 'Фото',
'journey.timelineEmpty': 'Пока нет остановок',
'journey.timelineEmptyHint': 'Добавьте отметку или напишите запись в дневник',
'journey.status.draft': 'Черновик',
'journey.status.active': 'Активно',
'journey.status.completed': 'Завершено',
'journey.status.upcoming': 'Предстоящее',
'journey.status.archived': 'В архиве',
'journey.checkin.add': 'Отметиться',
'journey.checkin.namePlaceholder': 'Название места',
'journey.checkin.notesPlaceholder': 'Заметки (необязательно)',
'journey.checkin.save': 'Сохранить',
'journey.checkin.error': 'Не удалось сохранить отметку',
'journey.entry.add': 'Дневник',
'journey.entry.edit': 'Редактировать запись',
'journey.entry.titlePlaceholder': 'Заголовок (необязательно)',
'journey.entry.bodyPlaceholder': 'Что произошло сегодня?',
'journey.entry.save': 'Сохранить',
'journey.entry.error': 'Не удалось сохранить запись',
'journey.photo.add': 'Фото',
'journey.photo.uploadError': 'Загрузка не удалась',
'journey.share.share': 'Поделиться',
'journey.share.public': 'Публичный',
'journey.share.linkCopied': 'Публичная ссылка скопирована',
'journey.share.disabled': 'Публичный доступ отключён',
'journey.editor.titlePlaceholder': 'Дайте название этому моменту...',
'journey.editor.bodyPlaceholder': 'Расскажите историю этого дня...',
'journey.editor.placePlaceholder': 'Местоположение (необязательно)',
'journey.editor.tagsPlaceholder':
'Теги: скрытая жемчужина, лучшая еда, стоит вернуться...',
'journey.visibility.private': 'Приватный',
'journey.visibility.shared': 'Общий',
'journey.visibility.public': 'Публичный',
'journey.emptyState.title': 'Ваша история начинается здесь',
'journey.emptyState.subtitle':
'Отметьтесь в месте или напишите первую запись в дневник',
'journey.frontpage.subtitle':
'Превращайте поездки в истории, которые вы никогда не забудете',
'journey.frontpage.createJourney': 'Создать путешествие',
'journey.frontpage.activeJourney': 'Активное путешествие',
'journey.frontpage.allJourneys': 'Все путешествия',
'journey.frontpage.journeys': 'путешествий',
'journey.frontpage.createNew': 'Создать новое путешествие',
'journey.frontpage.createNewSub':
'Выберите поездки, пишите истории, делитесь приключениями',
'journey.frontpage.live': 'В эфире',
'journey.frontpage.synced': 'Синхронизировано',
'journey.frontpage.continueWriting': 'Продолжить писать',
'journey.frontpage.updated': 'Обновлено {time}',
'journey.frontpage.suggestionLabel': 'Поездка только что завершилась',
'journey.frontpage.suggestionText':
'Превратите <strong>{title}</strong> в путешествие',
'journey.frontpage.dismiss': 'Скрыть',
'journey.frontpage.journeyName': 'Название путешествия',
'journey.frontpage.namePlaceholder': 'напр. Юго-Восточная Азия 2026',
'journey.frontpage.selectTrips': 'Выбрать поездки',
'journey.frontpage.tripsSelected': 'поездок выбрано',
'journey.frontpage.trips': 'поездок',
'journey.frontpage.placesImported': 'мест будет импортировано',
'journey.frontpage.places': 'мест',
'journey.detail.backToJourney': 'Назад к путешествию',
'journey.detail.syncedWithTrips': 'Синхронизировано с поездками',
'journey.detail.addEntry': 'Добавить запись',
'journey.detail.newEntry': 'Новая запись',
'journey.detail.editEntry': 'Редактировать запись',
'journey.detail.noEntries': 'Пока нет записей',
'journey.detail.noEntriesHint':
'Добавьте поездку, чтобы начать с шаблонных записей',
'journey.detail.noPhotos': 'Пока нет фото',
'journey.detail.noPhotosHint':
'Загрузите фото в записи или просмотрите библиотеку Immich/Synology',
'journey.detail.journeyStats': 'Статистика путешествия',
'journey.detail.syncedTrips': 'Синхронизированные поездки',
'journey.detail.noTripsLinked': 'Пока нет привязанных поездок',
'journey.detail.contributors': 'Участники',
'journey.detail.readMore': 'Читать далее',
'journey.detail.prosCons': 'Плюсы и минусы',
'journey.detail.photos': 'фото',
'journey.detail.day': 'День {number}',
'journey.detail.places': 'мест',
'journey.stats.days': 'Дней',
'journey.stats.cities': 'Городов',
'journey.stats.entries': 'Записей',
'journey.stats.photos': 'Фото',
'journey.stats.places': 'Мест',
'journey.skeletons.show': 'Показать предложения',
'journey.skeletons.hide': 'Скрыть предложения',
'journey.verdict.lovedIt': 'Понравилось',
'journey.verdict.couldBeBetter': 'Могло быть лучше',
'journey.synced.places': 'мест',
'journey.synced.synced': 'синхронизировано',
'journey.editor.discardChangesConfirm':
'У вас есть несохранённые изменения. Отменить?',
'journey.editor.uploadFailed': 'Не удалось загрузить фото',
'journey.editor.uploadPhotos': 'Загрузить фото',
'journey.editor.uploading': 'Загрузка...',
'journey.editor.uploadingProgress': 'Загрузка {done}/{total}…',
'journey.editor.uploadPartialFailed':
'{failed} из {total} фото не удалось загрузить — сохраните снова для повтора',
'journey.editor.fromGallery': 'Из галереи',
'journey.editor.allPhotosAdded': 'Все фото уже добавлены',
'journey.editor.writeStory': 'Напишите свою историю...',
'journey.editor.prosCons': 'Плюсы и минусы',
'journey.editor.pros': 'Плюсы',
'journey.editor.cons': 'Минусы',
'journey.editor.proPlaceholder': 'Что-то отличное...',
'journey.editor.conPlaceholder': 'Не очень хорошо...',
'journey.editor.addAnother': 'Добавить ещё',
'journey.editor.date': 'Дата',
'journey.editor.location': 'Местоположение',
'journey.editor.searchLocation': 'Поиск местоположения...',
'journey.editor.mood': 'Настроение',
'journey.editor.weather': 'Погода',
'journey.editor.photoFirst': '1-е',
'journey.editor.makeFirst': 'Сделать 1-м',
'journey.editor.searching': 'Поиск...',
'journey.mood.amazing': 'Потрясающе',
'journey.mood.good': 'Хорошо',
'journey.mood.neutral': 'Нейтрально',
'journey.mood.rough': 'Тяжело',
'journey.weather.sunny': 'Солнечно',
'journey.weather.partly': 'Переменная облачность',
'journey.weather.cloudy': 'Облачно',
'journey.weather.rainy': 'Дождливо',
'journey.weather.stormy': 'Гроза',
'journey.weather.cold': 'Снежно',
'journey.trips.linkTrip': 'Привязать поездку',
'journey.trips.searchTrip': 'Поиск поездки',
'journey.trips.searchPlaceholder': 'Название поездки или направление...',
'journey.trips.noTripsAvailable': 'Нет доступных поездок',
'journey.trips.link': 'Привязать',
'journey.trips.tripLinked': 'Поездка привязана',
'journey.trips.linkFailed': 'Не удалось привязать поездку',
'journey.trips.addTrip': 'Добавить поездку',
'journey.trips.unlinkTrip': 'Отвязать поездку',
'journey.trips.unlinkMessage':
'Отвязать «{title}»? Все синхронизированные записи и фото из этой поездки будут безвозвратно удалены. Это действие нельзя отменить.',
'journey.trips.unlink': 'Отвязать',
'journey.trips.tripUnlinked': 'Поездка отвязана',
'journey.trips.unlinkFailed': 'Не удалось отвязать поездку',
'journey.trips.noTripsLinkedSettings': 'Нет привязанных поездок',
'journey.contributors.invite': 'Пригласить участника',
'journey.contributors.searchUser': 'Поиск пользователя',
'journey.contributors.searchPlaceholder': 'Имя пользователя или email...',
'journey.contributors.noUsers': 'Пользователи не найдены',
'journey.contributors.role': 'Роль',
'journey.contributors.added': 'Участник добавлен',
'journey.contributors.addFailed': 'Не удалось добавить участника',
'journey.share.publicShare': 'Публичный доступ',
'journey.share.createLink': 'Создать ссылку для общего доступа',
'journey.share.linkCreated': 'Ссылка создана',
'journey.share.createFailed': 'Не удалось создать ссылку',
'journey.share.copy': 'Копировать',
'journey.share.copied': 'Скопировано!',
'journey.share.timeline': 'Хронология',
'journey.share.gallery': 'Галерея',
'journey.share.map': 'Карта',
'journey.share.removeLink': 'Удалить ссылку',
'journey.share.linkDeleted': 'Ссылка удалена',
'journey.share.deleteFailed': 'Не удалось удалить',
'journey.share.updateFailed': 'Не удалось обновить',
'journey.invite.role': 'Роль',
'journey.invite.viewer': 'Наблюдатель',
'journey.invite.editor': 'Редактор',
'journey.invite.invite': 'Пригласить',
'journey.invite.inviting': 'Приглашаем...',
'journey.settings.title': 'Настройки путешествия',
'journey.settings.coverImage': 'Обложка',
'journey.settings.changeCover': 'Сменить обложку',
'journey.settings.addCover': 'Добавить обложку',
'journey.settings.name': 'Название',
'journey.settings.subtitle': 'Подзаголовок',
'journey.settings.subtitlePlaceholder': 'напр. Таиланд, Вьетнам и Камбоджа',
'journey.settings.endJourney': 'Архивировать путешествие',
'journey.settings.reopenJourney': 'Восстановить путешествие',
'journey.settings.archived': 'Путешествие архивировано',
'journey.settings.reopened': 'Путешествие возобновлено',
'journey.settings.endDescription':
'Скрывает значок «В эфире». Вы можете возобновить в любое время.',
'journey.settings.delete': 'Удалить',
'journey.settings.deleteJourney': 'Удалить путешествие',
'journey.settings.deleteMessage':
'Удалить «{title}»? Все записи и фото будут потеряны.',
'journey.settings.saved': 'Настройки сохранены',
'journey.settings.saveFailed': 'Не удалось сохранить',
'journey.settings.coverUpdated': 'Обложка обновлена',
'journey.settings.coverFailed': 'Загрузка не удалась',
'journey.settings.failedToDelete': 'Не удалось удалить',
'journey.entries.deleteTitle': 'Удалить запись',
'journey.photosUploaded': '{count} фото загружено',
'journey.photosUploadFailed': 'Некоторые фото не удалось загрузить',
'journey.photosAdded': '{count} фото добавлено',
'journey.public.notFound': 'Не найдено',
'journey.public.notFoundMessage':
'Это путешествие не существует или ссылка устарела.',
'journey.public.readOnly': 'Только для чтения · Публичное путешествие',
'journey.public.tagline':
'Инструмент планирования и исследования путешествий',
'journey.public.sharedVia': 'Опубликовано через',
'journey.public.madeWith': 'Сделано с помощью',
'journey.pdf.journeyBook': 'Книга путешествия',
'journey.pdf.madeWith': 'Сделано с помощью TREK',
'journey.pdf.day': 'День',
'journey.pdf.theEnd': 'Конец',
'journey.pdf.saveAsPdf': 'Сохранить как PDF',
'journey.pdf.pages': 'страниц',
'journey.picker.tripPeriod': 'Период поездки',
'journey.picker.dateRange': 'Диапазон дат',
'journey.picker.allPhotos': 'Все фото',
'journey.picker.albums': 'Альбомы',
'journey.picker.selected': 'выбрано',
'journey.picker.addTo': 'Добавить в',
'journey.picker.newGallery': 'Новая галерея',
'journey.picker.selectAll': 'Выбрать все',
'journey.picker.deselectAll': 'Снять выбор',
'journey.picker.noAlbums': 'Альбомы не найдены',
'journey.picker.selectDate': 'Выберите дату',
'journey.picker.search': 'Поиск',
};
export default journey;
+97
View File
@@ -0,0 +1,97 @@
import type { TranslationStrings } from '../types';
const login: TranslationStrings = {
'login.error': 'Ошибка входа. Проверьте свои учётные данные.',
'login.tagline': 'Ваши поездки.\nВаш план.',
'login.description':
'Планируйте поездки совместно с интерактивными картами, бюджетами и синхронизацией в реальном времени.',
'login.features.maps': 'Интерактивные карты',
'login.features.mapsDesc': 'Google Places, маршруты и кластеризация',
'login.features.realtime': 'Синхронизация в реальном времени',
'login.features.realtimeDesc': 'Планируйте вместе через WebSocket',
'login.features.budget': 'Контроль бюджета',
'login.features.budgetDesc': 'Категории, графики и расходы на человека',
'login.features.collab': 'Совместная работа',
'login.features.collabDesc': 'Многопользовательский режим с общими поездками',
'login.features.packing': 'Списки вещей',
'login.features.packingDesc': 'Категории, прогресс и подсказки',
'login.features.bookings': 'Бронирования',
'login.features.bookingsDesc': 'Авиабилеты, отели, рестораны и многое другое',
'login.features.files': 'Документы',
'login.features.filesDesc': 'Загружайте и управляйте документами',
'login.features.routes': 'Умные маршруты',
'login.features.routesDesc': 'Автооптимизация и экспорт в Google Maps',
'login.selfHosted':
'Самостоятельный хостинг · Открытый код · Ваши данные остаются у вас',
'login.title': 'Вход',
'login.subtitle': 'С возвращением',
'login.signingIn': 'Вход…',
'login.signIn': 'Войти',
'login.createAdmin': 'Создать аккаунт администратора',
'login.createAdminHint': 'Настройте первый аккаунт администратора для TREK.',
'login.setNewPassword': 'Установить новый пароль',
'login.setNewPasswordHint':
'Вы должны сменить пароль, прежде чем продолжить.',
'login.createAccount': 'Создать аккаунт',
'login.createAccountHint': 'Зарегистрируйте новый аккаунт.',
'login.creating': 'Создание…',
'login.noAccount': 'Нет аккаунта?',
'login.hasAccount': 'Уже есть аккаунт?',
'login.register': 'Регистрация',
'login.emailPlaceholder': 'ваш@email.com',
'login.username': 'Имя пользователя',
'login.oidc.registrationDisabled':
'Регистрация отключена. Обратитесь к администратору.',
'login.oidc.noEmail': 'Провайдер не предоставил адрес эл. почты.',
'login.mfaTitle': 'Двухфакторная аутентификация',
'login.mfaSubtitle': 'Введите 6-значный код из приложения-аутентификатора.',
'login.mfaCodeLabel': 'Код подтверждения',
'login.mfaCodeRequired': 'Введите код из приложения-аутентификатора.',
'login.mfaHint':
'Откройте Google Authenticator, Authy или другое TOTP-приложение.',
'login.mfaBack': '← Назад к входу',
'login.mfaVerify': 'Подтвердить',
'login.invalidInviteLink': 'Недействительная или истёкшая ссылка-приглашение',
'login.oidcFailed': 'Ошибка входа через OIDC',
'login.usernameRequired': 'Имя пользователя обязательно',
'login.passwordMinLength': 'Пароль должен содержать не менее 8 символов',
'login.forgotPassword': 'Забыли пароль?',
'login.forgotPasswordTitle': 'Сброс пароля',
'login.forgotPasswordBody':
'Введите e-mail, с которым вы регистрировались. Если аккаунт найдём — отправим ссылку для сброса.',
'login.forgotPasswordSubmit': 'Отправить ссылку',
'login.forgotPasswordSentTitle': 'Проверьте почту',
'login.forgotPasswordSentBody':
'Если аккаунт существует, ссылка для сброса уже летит к вам. Она действительна 60 минут.',
'login.forgotPasswordSmtpHintOff':
'Обратите внимание: администратор не настроил SMTP, поэтому ссылка для сброса будет записана в консоль сервера, а не отправлена по почте.',
'login.backToLogin': 'Вернуться ко входу',
'login.newPassword': 'Новый пароль',
'login.confirmPassword': 'Подтвердите новый пароль',
'login.passwordsDontMatch': 'Пароли не совпадают',
'login.mfaCode': 'Код 2FA',
'login.resetPasswordTitle': 'Задайте новый пароль',
'login.resetPasswordBody':
'Выберите надёжный пароль, который вы здесь ещё не использовали. Минимум 8 символов.',
'login.resetPasswordMfaBody':
'Введите код 2FA или резервный код, чтобы завершить сброс.',
'login.resetPasswordSubmit': 'Сбросить пароль',
'login.resetPasswordVerify': 'Проверить и сбросить',
'login.resetPasswordSuccessTitle': 'Пароль обновлён',
'login.resetPasswordSuccessBody': 'Теперь вы можете войти с новым паролем.',
'login.resetPasswordInvalidLink': 'Неверная ссылка сброса',
'login.resetPasswordInvalidLinkBody':
'Ссылка отсутствует или повреждена. Запросите новую, чтобы продолжить.',
'login.resetPasswordFailed':
'Сброс не удался. Возможно, срок действия ссылки истёк.',
'login.oidc.tokenFailed': 'Аутентификация не удалась.',
'login.oidc.invalidState': 'Недействительная сессия. Попробуйте снова.',
'login.demoFailed': 'Ошибка демо-входа',
'login.oidcSignIn': 'Войти через {name}',
'login.oidcOnly':
'Вход по паролю отключён. Используйте вашего провайдера SSO для входа.',
'login.oidcLoggedOut':
'Вы вышли из системы. Войдите снова через вашего провайдера SSO.',
'login.demoHint': 'Попробуйте демо — регистрация не требуется',
};
export default login;
+8
View File
@@ -0,0 +1,8 @@
import type { TranslationStrings } from '../types';
const map: TranslationStrings = {
'map.connections': 'Соединения',
'map.showConnections': 'Показать маршруты бронирований',
'map.hideConnections': 'Скрыть маршруты бронирований',
};
export default map;
+24
View File
@@ -0,0 +1,24 @@
import type { TranslationStrings } from '../types';
const members: TranslationStrings = {
'members.shareTrip': 'Поделиться поездкой',
'members.inviteUser': 'Пригласить пользователя',
'members.selectUser': 'Выберите пользователя…',
'members.invite': 'Пригласить',
'members.allHaveAccess': 'У всех пользователей уже есть доступ.',
'members.access': 'Доступ',
'members.person': 'человек',
'members.persons': 'человек',
'members.you': 'вы',
'members.owner': 'Владелец',
'members.leaveTrip': 'Покинуть поездку',
'members.removeAccess': 'Отозвать доступ',
'members.confirmLeave': 'Покинуть поездку? Вы потеряете доступ.',
'members.confirmRemove': 'Отозвать доступ у этого пользователя?',
'members.loadError': 'Не удалось загрузить участников',
'members.added': 'добавлен',
'members.addError': 'Ошибка добавления',
'members.removed': 'Участник удалён',
'members.removeError': 'Ошибка удаления',
};
export default members;
+80
View File
@@ -0,0 +1,80 @@
import type { TranslationStrings } from '../types';
const memories: TranslationStrings = {
'memories.title': 'Фото',
'memories.notConnected': 'Immich не подключён',
'memories.notConnectedHint':
'Подключите Immich в настройках, чтобы видеть фотографии из поездок.',
'memories.notConnectedMultipleHint':
'Подключите одного из этих фотопровайдеров: {provider_names} в Настройках, чтобы добавлять фотографии к этому путешествию.',
'memories.noDates': 'Добавьте даты поездки для загрузки фотографий.',
'memories.noPhotos': 'Фотографии не найдены',
'memories.noPhotosHint': 'В Immich нет фотографий за период этой поездки.',
'memories.photosFound': 'фото',
'memories.fromOthers': 'от других',
'memories.sharePhotos': 'Поделиться фото',
'memories.sharing': 'Общий доступ',
'memories.reviewTitle': 'Проверьте ваши фото',
'memories.reviewHint':
'Нажмите на фото, чтобы исключить его из общего доступа.',
'memories.shareCount': 'Поделиться ({count} фото)',
'memories.providerUrl': 'URL сервера',
'memories.providerApiKey': 'API-ключ',
'memories.providerUsername': 'Имя пользователя',
'memories.providerPassword': 'Пароль',
'memories.providerOTP': 'Код MFA (если включён)',
'memories.skipSSLVerification': 'Пропустить проверку SSL-сертификата',
'memories.immichAutoUpload': 'Дублировать фото journey в Immich при загрузке',
'memories.providerUrlHintSynology':
'Включите путь приложения Photos в URL, например https://nas:5001/photo',
'memories.testConnection': 'Проверить подключение',
'memories.testShort': 'Проверить',
'memories.testFirst': 'Сначала проверьте подключение',
'memories.connected': 'Подключено',
'memories.disconnected': 'Не подключено',
'memories.connectionSuccess': 'Подключение к Immich установлено',
'memories.connectionError': 'Не удалось подключиться к Immich',
'memories.saved': 'Настройки {provider_name} сохранены',
'memories.providerDisconnectedBanner':
'Соединение с {provider_name} потеряно. Переподключитесь в Настройках для просмотра фотографий.',
'memories.saveError': 'Не удалось сохранить настройки {provider_name}',
'memories.oldest': 'Сначала старые',
'memories.newest': 'Сначала новые',
'memories.allLocations': 'Все места',
'memories.addPhotos': 'Добавить фото',
'memories.linkAlbum': 'Привязать альбом',
'memories.selectAlbum': 'Выбрать альбом Immich',
'memories.selectAlbumMultiple': 'Выбрать альбом',
'memories.noAlbums': 'Альбомы не найдены',
'memories.syncAlbum': 'Синхронизировать',
'memories.unlinkAlbum': 'Отвязать',
'memories.photos': 'фото',
'memories.selectPhotos': 'Выбрать фото из Immich',
'memories.selectPhotosMultiple': 'Выбрать фотографии',
'memories.selectHint': 'Нажмите на фото, чтобы выбрать их.',
'memories.selected': 'выбрано',
'memories.addSelected': 'Добавить {count} фото',
'memories.alreadyAdded': 'Добавлено',
'memories.private': 'Приватное',
'memories.stopSharing': 'Прекратить доступ',
'memories.tripDates': 'Даты поездки',
'memories.allPhotos': 'Все фото',
'memories.confirmShareTitle': 'Поделиться с участниками поездки?',
'memories.confirmShareHint':
'{count} фото станут видны всем участникам этой поездки. Вы сможете сделать отдельные фото приватными позже.',
'memories.confirmShareButton': 'Поделиться фото',
'memories.error.loadAlbums': 'Не удалось загрузить альбомы',
'memories.error.linkAlbum': 'Не удалось привязать альбом',
'memories.error.unlinkAlbum': 'Не удалось отвязать альбом',
'memories.error.syncAlbum': 'Не удалось синхронизировать альбом',
'memories.error.loadPhotos': 'Не удалось загрузить фотографии',
'memories.error.addPhotos': 'Не удалось добавить фотографии',
'memories.error.removePhoto': 'Не удалось удалить фотографию',
'memories.error.toggleSharing': 'Не удалось обновить настройки доступа',
'memories.saveRouteNotConfigured':
'Маршрут сохранения не настроен для этого провайдера',
'memories.testRouteNotConfigured':
'Маршрут тестирования не настроен для этого провайдера',
'memories.fillRequiredFields': 'Пожалуйста, заполните все обязательные поля',
};
export default memories;
+20
View File
@@ -0,0 +1,20 @@
import type { TranslationStrings } from '../types';
const nav: TranslationStrings = {
'nav.trip': 'Поездка',
'nav.share': 'Поделиться',
'nav.settings': 'Настройки',
'nav.admin': 'Админ',
'nav.logout': 'Выйти',
'nav.lightMode': 'Светлая тема',
'nav.darkMode': 'Тёмная тема',
'nav.autoMode': 'Авто',
'nav.administrator': 'Администратор',
'nav.myTrips': 'Мои поездки',
'nav.profile': 'Профиль',
'nav.bottomSettings': 'Настройки',
'nav.bottomAdmin': 'Администрирование',
'nav.bottomLogout': 'Выйти',
'nav.bottomAdminBadge': 'Админ',
};
export default nav;
+41
View File
@@ -0,0 +1,41 @@
import type { TranslationStrings } from '../types';
const notif: TranslationStrings = {
'notif.test.title': '[Тест] Уведомление',
'notif.test.simple.text': 'Это простое тестовое уведомление.',
'notif.test.boolean.text': 'Вы принимаете это тестовое уведомление?',
'notif.test.navigate.text': 'Нажмите ниже для перехода на панель управления.',
'notif.trip_invite.title': 'Приглашение в поездку',
'notif.trip_invite.text': '{actor} пригласил вас в {trip}',
'notif.booking_change.title': 'Бронирование обновлено',
'notif.booking_change.text': '{actor} обновил бронирование в {trip}',
'notif.trip_reminder.title': 'Напоминание о поездке',
'notif.trip_reminder.text': 'Ваша поездка {trip} скоро начнётся!',
'notif.todo_due.title': 'Задача к сроку',
'notif.todo_due.text': '{todo} в {trip} — срок {due}',
'notif.vacay_invite.title': 'Приглашение Vacay Fusion',
'notif.vacay_invite.text': '{actor} приглашает вас объединить планы отпуска',
'notif.photos_shared.title': 'Фото опубликованы',
'notif.photos_shared.text': '{actor} поделился {count} фото в {trip}',
'notif.collab_message.title': 'Новое сообщение',
'notif.collab_message.text': '{actor} отправил сообщение в {trip}',
'notif.packing_tagged.title': 'Задание для упаковки',
'notif.packing_tagged.text': '{actor} назначил вас в {category} в {trip}',
'notif.version_available.title': 'Доступна новая версия',
'notif.version_available.text': 'TREK {version} теперь доступен',
'notif.action.view_trip': 'Открыть поездку',
'notif.action.view_collab': 'Открыть сообщения',
'notif.action.view_packing': 'Открыть упаковку',
'notif.action.view_photos': 'Открыть фото',
'notif.action.view_vacay': 'Открыть Vacay',
'notif.action.view_admin': 'Перейти в админ',
'notif.action.view': 'Открыть',
'notif.action.accept': 'Принять',
'notif.action.decline': 'Отклонить',
'notif.generic.title': 'Уведомление',
'notif.generic.text': 'У вас новое уведомление',
'notif.dev.unknown_event.title': '[DEV] Неизвестное событие',
'notif.dev.unknown_event.text':
'Тип события "{event}" не зарегистрирован в EVENT_NOTIFICATION_CONFIG',
};
export default notif;
+37
View File
@@ -0,0 +1,37 @@
import type { TranslationStrings } from '../types';
const notifications: TranslationStrings = {
'notifications.title': 'Уведомления',
'notifications.markAllRead': 'Отметить все прочитанными',
'notifications.deleteAll': 'Удалить все',
'notifications.showAll': 'Показать все уведомления',
'notifications.empty': 'Нет уведомлений',
'notifications.emptyDescription': 'Вы в курсе всех событий!',
'notifications.all': 'Все',
'notifications.unreadOnly': 'Непрочитанные',
'notifications.markRead': 'Отметить как прочитанное',
'notifications.markUnread': 'Отметить как непрочитанное',
'notifications.delete': 'Удалить',
'notifications.system': 'Система',
'notifications.synologySessionCleared.title': 'Synology Photos отключено',
'notifications.synologySessionCleared.text':
'Ваш сервер или аккаунт изменился — перейдите в Настройки, чтобы проверить соединение снова.',
'notifications.test.title': 'Тестовое уведомление от {actor}',
'notifications.test.text': 'Это простое тестовое уведомление.',
'notifications.test.booleanTitle': '{actor} запрашивает подтверждение',
'notifications.test.booleanText': 'Тестовое уведомление с выбором.',
'notifications.test.accept': 'Подтвердить',
'notifications.test.decline': 'Отклонить',
'notifications.test.navigateTitle': 'Посмотрите на это',
'notifications.test.navigateText': 'Тестовое уведомление с переходом.',
'notifications.test.goThere': 'Перейти',
'notifications.test.adminTitle': 'Рассылка администратора',
'notifications.test.adminText':
'{actor} отправил тестовое уведомление всем администраторам.',
'notifications.test.tripTitle': '{actor} написал в вашей поездке',
'notifications.test.tripText': 'Тестовое уведомление для поездки "{trip}".',
'notifications.versionAvailable.title': 'Доступно обновление',
'notifications.versionAvailable.text': 'TREK {version} теперь доступен.',
'notifications.versionAvailable.button': 'Подробнее',
};
export default notifications;
+99
View File
@@ -0,0 +1,99 @@
import type { TranslationStrings } from '../types';
const oauth: TranslationStrings = {
'oauth.scope.group.trips': 'Поездки',
'oauth.scope.group.places': 'Места',
'oauth.scope.group.atlas': 'Atlas',
'oauth.scope.group.packing': 'Вещи',
'oauth.scope.group.todos': 'Задачи',
'oauth.scope.group.budget': 'Бюджет',
'oauth.scope.group.reservations': 'Бронирования',
'oauth.scope.group.collab': 'Сотрудничество',
'oauth.scope.group.notifications': 'Уведомления',
'oauth.scope.group.vacay': 'Отпуск',
'oauth.scope.group.geo': 'Geo',
'oauth.scope.group.weather': 'Погода',
'oauth.scope.group.journey': 'Путешествия',
'oauth.scope.trips:read.label': 'Просмотр поездок и маршрутов',
'oauth.scope.trips:read.description':
'Чтение поездок, дней, заметок и участников',
'oauth.scope.trips:write.label': 'Редактирование поездок и маршрутов',
'oauth.scope.trips:write.description':
'Создание и обновление поездок, дней, заметок и управление участниками',
'oauth.scope.trips:delete.label': 'Удаление поездок',
'oauth.scope.trips:delete.description':
'Безвозвратное удаление поездок — это действие необратимо',
'oauth.scope.trips:share.label': 'Управление ссылками на совместный доступ',
'oauth.scope.trips:share.description':
'Создание, обновление и отзыв публичных ссылок на поездки',
'oauth.scope.places:read.label': 'Просмотр мест и данных карты',
'oauth.scope.places:read.description':
'Чтение мест, назначений по дням, тегов и категорий',
'oauth.scope.places:write.label': 'Управление местами',
'oauth.scope.places:write.description':
'Создание, обновление и удаление мест, назначений и тегов',
'oauth.scope.atlas:read.label': 'Просмотр Atlas',
'oauth.scope.atlas:read.description':
'Чтение посещённых стран, регионов и списка желаний',
'oauth.scope.atlas:write.label': 'Управление Atlas',
'oauth.scope.atlas:write.description':
'Отмечать посещённые страны и регионы, управлять списком желаний',
'oauth.scope.packing:read.label': 'Просмотр списков вещей',
'oauth.scope.packing:read.description':
'Чтение вещей, сумок и назначений категорий',
'oauth.scope.packing:write.label': 'Управление списками вещей',
'oauth.scope.packing:write.description':
'Добавление, обновление, удаление, отметка и переупорядочивание вещей и сумок',
'oauth.scope.todos:read.label': 'Просмотр списков задач',
'oauth.scope.todos:read.description':
'Чтение задач поездки и назначений категорий',
'oauth.scope.todos:write.label': 'Управление списками задач',
'oauth.scope.todos:write.description':
'Создание, обновление, отметка, удаление и переупорядочивание задач',
'oauth.scope.budget:read.label': 'Просмотр бюджета',
'oauth.scope.budget:read.description':
'Чтение статей бюджета и разбивки расходов',
'oauth.scope.budget:write.label': 'Управление бюджетом',
'oauth.scope.budget:write.description':
'Создание, обновление и удаление статей бюджета',
'oauth.scope.reservations:read.label': 'Просмотр бронирований',
'oauth.scope.reservations:read.description':
'Чтение бронирований и сведений о проживании',
'oauth.scope.reservations:write.label': 'Управление бронированиями',
'oauth.scope.reservations:write.description':
'Создание, обновление, удаление и переупорядочивание бронирований',
'oauth.scope.collab:read.label': 'Просмотр совместной работы',
'oauth.scope.collab:read.description':
'Чтение совместных заметок, опросов и сообщений',
'oauth.scope.collab:write.label': 'Управление совместной работой',
'oauth.scope.collab:write.description':
'Создание, обновление и удаление заметок, опросов и сообщений',
'oauth.scope.notifications:read.label': 'Просмотр уведомлений',
'oauth.scope.notifications:read.description':
'Чтение уведомлений в приложении и количества непрочитанных',
'oauth.scope.notifications:write.label': 'Управление уведомлениями',
'oauth.scope.notifications:write.description':
'Отмечать уведомления как прочитанные и отвечать на них',
'oauth.scope.vacay:read.label': 'Просмотр планов отпуска',
'oauth.scope.vacay:read.description':
'Чтение данных планирования отпуска, записей и статистики',
'oauth.scope.vacay:write.label': 'Управление планами отпуска',
'oauth.scope.vacay:write.description':
'Создание и управление записями отпуска, праздниками и командными планами',
'oauth.scope.geo:read.label': 'Карты и геокодирование',
'oauth.scope.geo:read.description':
'Поиск мест, разрешение URL карт и обратное геокодирование координат',
'oauth.scope.weather:read.label': 'Прогнозы погоды',
'oauth.scope.weather:read.description':
'Получение прогнозов погоды для мест и дат поездки',
'oauth.scope.journey:read.label': 'Просмотр путешествий',
'oauth.scope.journey:read.description':
'Чтение путешествий, записей и списка участников',
'oauth.scope.journey:write.label': 'Управление путешествиями',
'oauth.scope.journey:write.description':
'Создание, обновление и удаление путешествий и их записей',
'oauth.scope.journey:share.label': 'Управление ссылками на путешествия',
'oauth.scope.journey:share.description':
'Создание, обновление и отзыв публичных ссылок для путешествий',
};
export default oauth;
+186
View File
@@ -0,0 +1,186 @@
import type { TranslationStrings } from '../types';
const packing: TranslationStrings = {
'packing.title': 'Список вещей',
'packing.empty': 'Список вещей пуст',
'packing.import': 'Импорт',
'packing.importTitle': 'Импорт списка вещей',
'packing.importHint':
'Один предмет на строку. Категория и количество — через запятую, точку с запятой или табуляцию: Название, Категория, Количество',
'packing.importPlaceholder':
'Зубная щётка\nСолнцезащитный крем, Гигиена\nФутболки, Одежда, 5\nПаспорт, Документы',
'packing.importCsv': 'Загрузить CSV/TXT',
'packing.importAction': 'Импортировать {count}',
'packing.importSuccess': '{count} предметов импортировано',
'packing.importError': 'Ошибка импорта',
'packing.importEmpty': 'Нет предметов для импорта',
'packing.progress': '{packed} из {total} собрано ({percent}%)',
'packing.clearChecked': 'Удалить {count} отмеченных',
'packing.clearCheckedShort': 'Удалить {count}',
'packing.suggestions': 'Подсказки',
'packing.suggestionsTitle': 'Добавить подсказки',
'packing.allSuggested': 'Все подсказки добавлены',
'packing.allPacked': 'Всё собрано!',
'packing.addPlaceholder': 'Добавить вещь...',
'packing.categoryPlaceholder': 'Категория...',
'packing.filterAll': 'Все',
'packing.filterOpen': 'Не собрано',
'packing.filterDone': 'Собрано',
'packing.emptyTitle': 'Список вещей пуст',
'packing.emptyHint': 'Добавьте вещи или используйте подсказки',
'packing.emptyFiltered': 'Нет вещей, соответствующих фильтру',
'packing.menuRename': 'Переименовать',
'packing.menuCheckAll': 'Отметить все',
'packing.menuUncheckAll': 'Снять отметки',
'packing.menuDeleteCat': 'Удалить категорию',
'packing.addItem': 'Добавить вещь',
'packing.addItemPlaceholder': 'Название...',
'packing.addCategory': 'Добавить категорию',
'packing.newCategoryPlaceholder': 'Название категории (напр. Одежда)',
'packing.applyTemplate': 'Применить шаблон',
'packing.template': 'Шаблон',
'packing.templateApplied': '{count} вещей добавлено из шаблона',
'packing.templateError': 'Ошибка применения шаблона',
'packing.saveAsTemplate': 'Сохранить как шаблон',
'packing.templateName': 'Название шаблона',
'packing.templateSaved': 'Список вещей сохранён как шаблон',
'packing.noMembers': 'Нет участников',
'packing.bags': 'Багаж',
'packing.noBag': 'Не назначено',
'packing.totalWeight': 'Общий вес',
'packing.bagName': 'Название...',
'packing.addBag': 'Добавить багаж',
'packing.changeCategory': 'Изменить категорию',
'packing.confirm.clearChecked':
'Вы уверены, что хотите удалить {count} отмеченных вещей?',
'packing.confirm.deleteCat':
'Вы уверены, что хотите удалить категорию «{name}» с {count} вещами?',
'packing.defaultCategory': 'Другое',
'packing.toast.saveError': 'Ошибка сохранения',
'packing.toast.deleteError': 'Ошибка удаления',
'packing.toast.renameError': 'Ошибка переименования',
'packing.toast.addError': 'Ошибка добавления',
'packing.suggestions.items': [
{
name: 'Паспорт',
category: 'Документы',
},
{
name: 'Удостоверение личности',
category: 'Документы',
},
{
name: 'Страховка',
category: 'Документы',
},
{
name: 'Авиабилеты',
category: 'Документы',
},
{
name: 'Банковская карта',
category: 'Финансы',
},
{
name: 'Наличные',
category: 'Финансы',
},
{
name: 'Виза',
category: 'Документы',
},
{
name: 'Футболки',
category: 'Одежда',
},
{
name: 'Брюки',
category: 'Одежда',
},
{
name: 'Нижнее бельё',
category: 'Одежда',
},
{
name: 'Носки',
category: 'Одежда',
},
{
name: 'Куртка',
category: 'Одежда',
},
{
name: 'Пижама',
category: 'Одежда',
},
{
name: 'Купальник',
category: 'Одежда',
},
{
name: 'Дождевик',
category: 'Одежда',
},
{
name: 'Удобная обувь',
category: 'Одежда',
},
{
name: 'Зубная щётка',
category: 'Гигиена',
},
{
name: 'Зубная паста',
category: 'Гигиена',
},
{
name: 'Шампунь',
category: 'Гигиена',
},
{
name: 'Дезодорант',
category: 'Гигиена',
},
{
name: 'Солнцезащитный крем',
category: 'Гигиена',
},
{
name: 'Бритва',
category: 'Гигиена',
},
{
name: 'Зарядное устройство',
category: 'Электроника',
},
{
name: 'Внешний аккумулятор',
category: 'Электроника',
},
{
name: 'Наушники',
category: 'Электроника',
},
{
name: 'Адаптер для розеток',
category: 'Электроника',
},
{
name: 'Фотоаппарат',
category: 'Электроника',
},
{
name: 'Обезболивающее',
category: 'Здоровье',
},
{
name: 'Пластыри',
category: 'Здоровье',
},
{
name: 'Антисептик',
category: 'Здоровье',
},
],
};
export default packing;
+10
View File
@@ -0,0 +1,10 @@
import type { TranslationStrings } from '../types';
const pdf: TranslationStrings = {
'pdf.travelPlan': 'План поездки',
'pdf.planned': 'Запланировано',
'pdf.costLabel': 'Стоимость EUR',
'pdf.preview': 'Предпросмотр PDF',
'pdf.saveAsPdf': 'Сохранить как PDF',
};
export default pdf;
+63
View File
@@ -0,0 +1,63 @@
import type { TranslationStrings } from '../types';
const perm: TranslationStrings = {
'perm.title': 'Настройки разрешений',
'perm.subtitle': 'Управляйте тем, кто может выполнять действия в приложении',
'perm.saved': 'Настройки разрешений сохранены',
'perm.resetDefaults': 'Сбросить по умолчанию',
'perm.customized': 'изменено',
'perm.level.admin': 'Только администратор',
'perm.level.tripOwner': 'Владелец поездки',
'perm.level.tripMember': 'Участники поездки',
'perm.level.everybody': 'Все',
'perm.cat.trip': 'Управление поездками',
'perm.cat.members': 'Управление участниками',
'perm.cat.files': 'Файлы',
'perm.cat.content': 'Контент и расписание',
'perm.cat.extras': 'Бюджет, сборы и совместная работа',
'perm.action.trip_create': 'Создавать поездки',
'perm.action.trip_edit': 'Редактировать детали поездки',
'perm.action.trip_delete': 'Удалять поездки',
'perm.action.trip_archive': 'Архивировать / разархивировать поездки',
'perm.action.trip_cover_upload': 'Загружать обложку',
'perm.action.member_manage': 'Добавлять / удалять участников',
'perm.action.file_upload': 'Загружать файлы',
'perm.action.file_edit': 'Редактировать метаданные файлов',
'perm.action.file_delete': 'Удалять файлы',
'perm.action.place_edit': 'Добавлять / редактировать / удалять места',
'perm.action.day_edit': 'Редактировать дни, заметки и назначения',
'perm.action.reservation_edit': 'Управлять бронированиями',
'perm.action.budget_edit': 'Управлять бюджетом',
'perm.action.packing_edit': 'Управлять списками вещей',
'perm.action.collab_edit': 'Совместная работа (заметки, опросы, чат)',
'perm.action.share_manage': 'Управлять ссылками для обмена',
'perm.actionHint.trip_create': 'Кто может создавать новые поездки',
'perm.actionHint.trip_edit':
'Кто может менять название, даты, описание и валюту поездки',
'perm.actionHint.trip_delete': 'Кто может безвозвратно удалить поездку',
'perm.actionHint.trip_archive':
'Кто может архивировать или разархивировать поездку',
'perm.actionHint.trip_cover_upload': 'Кто может загружать или менять обложку',
'perm.actionHint.member_manage':
'Кто может приглашать или удалять участников поездки',
'perm.actionHint.file_upload': 'Кто может загружать файлы в поездку',
'perm.actionHint.file_edit':
'Кто может редактировать описания и ссылки файлов',
'perm.actionHint.file_delete':
'Кто может перемещать файлы в корзину или безвозвратно удалять',
'perm.actionHint.place_edit':
'Кто может добавлять, редактировать или удалять места',
'perm.actionHint.day_edit':
'Кто может редактировать дни, заметки к дням и назначения мест',
'perm.actionHint.reservation_edit':
'Кто может создавать, редактировать или удалять бронирования',
'perm.actionHint.budget_edit':
'Кто может создавать, редактировать или удалять статьи бюджета',
'perm.actionHint.packing_edit':
'Кто может управлять вещами для сборов и сумками',
'perm.actionHint.collab_edit':
'Кто может создавать заметки, опросы и отправлять сообщения',
'perm.actionHint.share_manage':
'Кто может создавать или удалять публичные ссылки для обмена',
};
export default perm;
+25
View File
@@ -0,0 +1,25 @@
import type { TranslationStrings } from '../types';
const photos: TranslationStrings = {
'photos.title': 'Фотографии',
'photos.subtitle': '{count} фото для {trip}',
'photos.dropHere': 'Перетащите фото сюда...',
'photos.dropHereActive': 'Перетащите фото сюда',
'photos.captionForAll': 'Подпись (для всех)',
'photos.captionPlaceholder': 'Необязательная подпись...',
'photos.addCaption': 'Добавить подпись...',
'photos.allDays': 'Все дни',
'photos.noPhotos': 'Фото пока нет',
'photos.uploadHint': 'Загрузите фото из путешествия',
'photos.clickToSelect': 'или нажмите для выбора',
'photos.linkPlace': 'Привязать место',
'photos.noPlace': 'Без места',
'photos.uploadN': '{n} фото загружено',
'photos.linkDay': 'Связать день',
'photos.noDay': 'Нет дня',
'photos.dayLabel': 'День {number}',
'photos.photoSelected': 'Фото выбрано',
'photos.photosSelected': 'Фото выбраны',
'photos.fileTypeHint': 'JPG, PNG, WebP · макс. 10 МБ · до 30 фото',
};
export default photos;
+92
View File
@@ -0,0 +1,92 @@
import type { TranslationStrings } from '../types';
const places: TranslationStrings = {
'places.addPlace': 'Добавить место/активность',
'places.importFile': 'Импортировать файл',
'places.sidebarDrop': 'Отпустите для импорта',
'places.importFileHint':
'Импортируйте файлы .gpx, .kml или .kmz из инструментов, таких как Google My Maps, Google Earth или GPS-трекер.',
'places.importFileDropHere':
'Нажмите для выбора файла или перетащите его сюда',
'places.importFileDropActive': 'Отпустите файл для выбора',
'places.importFileUnsupported':
'Неподдерживаемый тип файла. Используйте .gpx, .kml или .kmz.',
'places.importFileTooLarge':
'Файл слишком большой. Максимальный размер загрузки — {maxMb} MB.',
'places.importFileError': 'Ошибка импорта',
'places.importAllSkipped': 'Все места уже были в поездке.',
'places.gpxImported': '{count} мест импортировано из GPX',
'places.gpxImportTypes': 'Что импортировать?',
'places.gpxImportWaypoints': 'Путевые точки',
'places.gpxImportRoutes': 'Маршруты',
'places.gpxImportTracks': 'Треки (с геометрией пути)',
'places.gpxImportNoneSelected': 'Выберите хотя бы один тип для импорта.',
'places.kmlImportTypes': 'Что вы хотите импортировать?',
'places.kmlImportPoints': 'Точки (Placemarks)',
'places.kmlImportPaths': 'Маршруты (LineStrings)',
'places.kmlImportNoneSelected': 'Выберите хотя бы один тип.',
'places.selectionCount': '{count} выбрано',
'places.deleteSelected': 'Удалить выбранные',
'places.kmlKmzImported': '{count} мест импортировано из KMZ/KML',
'places.urlResolved': 'Место импортировано из URL',
'places.importList': 'Импорт списка',
'places.kmlKmzSummaryValues':
'Placemarks: {total} • Импортировано: {created} • Пропущено: {skipped}',
'places.importGoogleList': 'Список Google',
'places.importNaverList': 'Список Naver',
'places.googleListHint':
'Вставьте ссылку на общий список Google Maps для импорта всех мест.',
'places.googleListImported': '{count} мест импортировано из "{list}"',
'places.googleListError': 'Не удалось импортировать список Google Maps',
'places.naverListHint':
'Вставьте ссылку на общий список Naver Maps для импорта всех мест.',
'places.naverListImported': '{count} мест импортировано из "{list}"',
'places.naverListError': 'Не удалось импортировать список Naver Maps',
'places.viewDetails': 'Подробности',
'places.assignToDay': 'Добавить в какой день?',
'places.all': 'Все',
'places.unplanned': 'Незапланированные',
'places.filterTracks': 'Треки',
'places.search': 'Поиск мест...',
'places.allCategories': 'Все категории',
'places.categoriesSelected': 'категорий',
'places.clearFilter': 'Сбросить фильтр',
'places.count': '{count} мест',
'places.countSingular': '1 место',
'places.allPlanned': 'Все места запланированы',
'places.noneFound': 'Места не найдены',
'places.editPlace': 'Редактировать место',
'places.formName': 'Название',
'places.formNamePlaceholder': 'напр. Эйфелева башня',
'places.formDescription': 'Описание',
'places.formDescriptionPlaceholder': 'Краткое описание...',
'places.formAddress': 'Адрес',
'places.formAddressPlaceholder': 'Улица, город, страна',
'places.formLat': 'Широта (напр. 48.8566)',
'places.formLng': 'Долгота (напр. 2.3522)',
'places.formCategory': 'Категория',
'places.noCategory': 'Без категории',
'places.categoryNamePlaceholder': 'Название категории',
'places.formTime': 'Время',
'places.startTime': 'Начало',
'places.endTime': 'Конец',
'places.endTimeBeforeStart': 'Время окончания раньше времени начала',
'places.timeCollision': 'Пересечение по времени с:',
'places.formWebsite': 'Сайт',
'places.formNotes': 'Заметки',
'places.formNotesPlaceholder': 'Личные заметки...',
'places.formReservation': 'Бронирование',
'places.reservationNotesPlaceholder':
'Заметки о бронировании, номер подтверждения...',
'places.mapsSearchPlaceholder': 'Поиск мест...',
'places.mapsSearchError': 'Ошибка поиска мест.',
'places.loadingDetails': 'Загрузка данных о месте…',
'places.osmHint':
'Поиск через OpenStreetMap (без фото, часов работы и рейтингов). Добавьте API-ключ Google в настройках для полной информации.',
'places.osmActive':
'Поиск через OpenStreetMap (без фото, рейтингов и часов работы). Добавьте API-ключ Google в настройках для расширенных данных.',
'places.categoryCreateError': 'Не удалось создать категорию',
'places.nameRequired': 'Введите название',
'places.saveError': 'Ошибка сохранения',
};
export default places;
+68
View File
@@ -0,0 +1,68 @@
import type { TranslationStrings } from '../types';
const planner: TranslationStrings = {
'planner.places': 'Места',
'planner.bookings': 'Бронирования',
'planner.packingList': 'Список вещей',
'planner.documents': 'Документы',
'planner.dayPlan': 'План дня',
'planner.reservations': 'Бронирования',
'planner.minTwoPlaces': 'Нужно минимум 2 места с координатами',
'planner.noGeoPlaces': 'Нет мест с координатами',
'planner.routeCalculated': 'Маршрут рассчитан',
'planner.routeCalcFailed': 'Не удалось рассчитать маршрут',
'planner.routeError': 'Ошибка расчёта маршрута',
'planner.icsExportFailed': 'Не удалось экспортировать ICS',
'planner.routeOptimized': 'Маршрут оптимизирован',
'planner.reservationUpdated': 'Бронирование обновлено',
'planner.reservationAdded': 'Бронирование добавлено',
'planner.confirmDeleteReservation': 'Удалить бронирование?',
'planner.reservationDeleted': 'Бронирование удалено',
'planner.days': 'Дни',
'planner.allPlaces': 'Все места',
'planner.totalPlaces': 'Всего {n} мест',
'planner.noDaysPlanned': 'Дни ещё не запланированы',
'planner.editTrip': 'Редактировать поездку →',
'planner.placeOne': '1 место',
'planner.placeN': '{n} мест',
'planner.addNote': 'Добавить заметку',
'planner.noEntries': 'На этот день записей нет',
'planner.addPlace': 'Добавить место/активность',
'planner.addPlaceShort': '+ Добавить место/активность',
'planner.resPending': 'Бронирование ожидает · ',
'planner.resConfirmed': 'Бронирование подтверждено · ',
'planner.notePlaceholder': 'Заметка…',
'planner.noteTimePlaceholder': 'Время (необязательно)',
'planner.noteExamplePlaceholder':
'напр. S3 в 14:30 с вокзала, паром с причала 7, обеденный перерыв…',
'planner.totalCost': 'Общая стоимость',
'planner.searchPlaces': 'Поиск мест…',
'planner.allCategories': 'Все категории',
'planner.noPlacesFound': 'Места не найдены',
'planner.addFirstPlace': 'Добавить первое место',
'planner.noReservations': 'Нет бронирований',
'planner.addFirstReservation': 'Добавить первое бронирование',
'planner.new': 'Новое',
'planner.addToDay': '+ День',
'planner.calculating': 'Расчёт…',
'planner.route': 'Маршрут',
'planner.optimize': 'Оптимизировать',
'planner.openGoogleMaps': 'Открыть в Google Maps',
'planner.selectDayHint':
'Выберите день из списка слева для просмотра плана дня',
'planner.noPlacesForDay': 'На этот день мест пока нет',
'planner.addPlacesLink': 'Добавить места →',
'planner.minTotal': 'мин. всего',
'planner.noReservation': 'Нет бронирования',
'planner.removeFromDay': 'Убрать из дня',
'planner.addToThisDay': 'Добавить в день',
'planner.overview': 'Обзор',
'planner.noDays': 'Дней нет',
'planner.editTripToAddDays': 'Отредактируйте поездку для добавления дней',
'planner.dayCount': '{n} дней',
'planner.clickToUnlock': 'Нажмите для разблокировки',
'planner.keepPosition': 'Сохранить позицию при оптимизации маршрута',
'planner.dayDetails': 'Подробности дня',
'planner.dayN': 'День {n}',
};
export default planner;
+25
View File
@@ -0,0 +1,25 @@
import type { TranslationStrings } from '../types';
const register: TranslationStrings = {
'register.passwordMismatch': 'Пароли не совпадают',
'register.passwordTooShort': 'Пароль должен содержать не менее 8 символов',
'register.failed': 'Ошибка регистрации',
'register.getStarted': 'Начать',
'register.subtitle': 'Создайте аккаунт и начните планировать поездки мечты.',
'register.feature1': 'Неограниченные планы поездок',
'register.feature2': 'Интерактивная карта',
'register.feature3': 'Управление местами и категориями',
'register.feature4': 'Отслеживание бронирований',
'register.feature5': 'Создание списков вещей',
'register.feature6': 'Хранение фото и файлов',
'register.createAccount': 'Создать аккаунт',
'register.startPlanning': 'Начните планировать свои поездки',
'register.minChars': 'Мин. 6 символов',
'register.confirmPassword': 'Подтвердите пароль',
'register.repeatPassword': 'Повторите пароль',
'register.registering': 'Регистрация...',
'register.register': 'Зарегистрироваться',
'register.hasAccount': 'Уже есть аккаунт?',
'register.signIn': 'Войти',
};
export default register;
+119
View File
@@ -0,0 +1,119 @@
import type { TranslationStrings } from '../types';
const reservations: TranslationStrings = {
'reservations.title': 'Бронирования',
'reservations.empty': 'Пока нет бронирований',
'reservations.emptyHint':
'Добавьте бронирования на авиабилеты, отели и другое',
'reservations.add': 'Добавить бронирование',
'reservations.addManual': 'Ручное бронирование',
'reservations.placeHint':
'Совет: бронирования лучше создавать прямо из места, чтобы связать их с планом дня.',
'reservations.confirmed': 'Подтверждено',
'reservations.pending': 'Ожидание',
'reservations.summary': '{confirmed} подтв., {pending} ожид.',
'reservations.fromPlan': 'Из плана',
'reservations.showFiles': 'Показать файлы',
'reservations.editTitle': 'Редактировать бронирование',
'reservations.status': 'Статус',
'reservations.datetime': 'Дата и время',
'reservations.startTime': 'Время начала',
'reservations.endTime': 'Время окончания',
'reservations.date': 'Дата',
'reservations.time': 'Время',
'reservations.timeAlt': 'Время (альтернативное, напр. 19:30)',
'reservations.notes': 'Заметки',
'reservations.notesPlaceholder': 'Дополнительные заметки...',
'reservations.meta.airline': 'Авиакомпания',
'reservations.meta.flightNumber': 'Номер рейса',
'reservations.meta.from': 'Откуда',
'reservations.meta.to': 'Куда',
'reservations.needsReview': 'Проверить',
'reservations.needsReviewHint':
'Аэропорт не удалось определить автоматически — подтвердите местоположение.',
'reservations.searchLocation': 'Искать станцию, порт, адрес...',
'reservations.meta.trainNumber': 'Номер поезда',
'reservations.meta.platform': 'Платформа',
'reservations.meta.seat': 'Место',
'reservations.meta.checkIn': 'Заезд',
'reservations.meta.checkInUntil': 'Заселение до',
'reservations.meta.checkOut': 'Выезд',
'reservations.meta.linkAccommodation': 'Жильё',
'reservations.meta.pickAccommodation': 'Привязать к жилью',
'reservations.meta.noAccommodation': 'Нет',
'reservations.meta.hotelPlace': 'Жильё',
'reservations.meta.pickHotel': 'Выбрать жильё',
'reservations.meta.fromDay': 'С',
'reservations.meta.toDay': 'По',
'reservations.meta.selectDay': 'Выбрать день',
'reservations.type.flight': 'Авиабилет',
'reservations.type.hotel': 'Жильё',
'reservations.type.restaurant': 'Ресторан',
'reservations.type.train': 'Поезд',
'reservations.type.car': 'Автомобиль',
'reservations.type.cruise': 'Круиз',
'reservations.type.event': 'Мероприятие',
'reservations.type.tour': 'Экскурсия',
'reservations.type.other': 'Другое',
'reservations.confirm.delete':
'Вы уверены, что хотите удалить бронирование «{name}»?',
'reservations.confirm.deleteTitle': 'Удалить бронирование?',
'reservations.confirm.deleteBody': '«{name}» будет удалено навсегда.',
'reservations.toast.updated': 'Бронирование обновлено',
'reservations.toast.removed': 'Бронирование удалено',
'reservations.toast.fileUploaded': 'Файл загружен',
'reservations.toast.uploadError': 'Ошибка загрузки',
'reservations.newTitle': 'Новое бронирование',
'reservations.bookingType': 'Тип бронирования',
'reservations.titleLabel': 'Название',
'reservations.titlePlaceholder': 'напр. Lufthansa LH123, Hotel Adlon, ...',
'reservations.locationAddress': 'Местоположение / Адрес',
'reservations.locationPlaceholder': 'Адрес, аэропорт, отель...',
'reservations.confirmationCode': 'Код бронирования',
'reservations.confirmationPlaceholder': 'напр. ABC12345',
'reservations.day': 'День',
'reservations.noDay': 'Без дня',
'reservations.place': 'Место',
'reservations.noPlace': 'Без места',
'reservations.pendingSave': 'будет сохранено…',
'reservations.uploading': 'Загрузка...',
'reservations.attachFile': 'Прикрепить файл',
'reservations.linkExisting': 'Привязать существующий файл',
'reservations.toast.saveError': 'Ошибка сохранения',
'reservations.toast.updateError': 'Ошибка обновления',
'reservations.toast.deleteError': 'Ошибка удаления',
'reservations.confirm.remove': 'Удалить бронирование для «{name}»?',
'reservations.linkAssignment': 'Привязать к назначению дня',
'reservations.pickAssignment': 'Выберите назначение из вашего плана...',
'reservations.noAssignment': 'Без привязки (самостоятельное)',
'reservations.price': 'Цена',
'reservations.budgetCategory': 'Категория бюджета',
'reservations.budgetCategoryPlaceholder': 'напр. Транспорт, Проживание',
'reservations.budgetCategoryAuto': 'Авто (по типу бронирования)',
'reservations.budgetHint':
'При сохранении будет автоматически создана запись бюджета.',
'reservations.departureDate': 'Вылет',
'reservations.arrivalDate': 'Прилёт',
'reservations.departureTime': 'Время вылета',
'reservations.arrivalTime': 'Время прилёта',
'reservations.pickupDate': 'Получение',
'reservations.returnDate': 'Возврат',
'reservations.pickupTime': 'Время получения',
'reservations.returnTime': 'Время возврата',
'reservations.endDate': 'Дата окончания',
'reservations.meta.departureTimezone': 'TZ вылета',
'reservations.meta.arrivalTimezone': 'TZ прилёта',
'reservations.span.departure': 'Вылет',
'reservations.span.arrival': 'Прилёт',
'reservations.span.inTransit': 'В пути',
'reservations.span.pickup': 'Получение',
'reservations.span.return': 'Возврат',
'reservations.span.active': 'Активно',
'reservations.span.start': 'Начало',
'reservations.span.end': 'Конец',
'reservations.span.ongoing': 'Продолжается',
'reservations.validation.endBeforeStart':
'Дата/время окончания должны быть позже даты/времени начала',
'reservations.addBooking': 'Добавить бронирование',
};
export default reservations;
+299
View File
@@ -0,0 +1,299 @@
import type { TranslationStrings } from '../types';
const settings: TranslationStrings = {
'settings.title': 'Настройки',
'settings.subtitle': 'Настройте свои персональные параметры',
'settings.tabs.display': 'Дисплей',
'settings.tabs.map': 'Карта',
'settings.tabs.notifications': 'Уведомления',
'settings.tabs.integrations': 'Интеграции',
'settings.tabs.account': 'Аккаунт',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'О приложении',
'settings.map': 'Карта',
'settings.mapTemplate': 'Шаблон карты',
'settings.mapTemplatePlaceholder.select': 'Выберите шаблон...',
'settings.mapDefaultHint': 'Оставьте пустым для OpenStreetMap (по умолчанию)',
'settings.mapTemplatePlaceholder':
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
'settings.mapHint': 'URL-шаблон для тайлов карты',
'settings.mapProvider': 'Провайдер карты',
'settings.mapProviderHint':
'Применяется к Trip Planner и Journey. Atlas всегда использует Leaflet.',
'settings.mapLeafletSubtitle': 'Классические 2D, любые растровые тайлы',
'settings.mapMapboxSubtitle': 'Векторные тайлы, 3D-здания и рельеф',
'settings.mapExperimental': 'Экспериментально',
'settings.mapMapboxToken': 'Токен доступа Mapbox',
'settings.mapMapboxTokenHint': 'Публичный токен (pk.*) с',
'settings.mapMapboxTokenLink': 'mapbox.com → Токены доступа',
'settings.mapStyle': 'Стиль карты',
'settings.mapStylePlaceholder': 'Выберите стиль Mapbox',
'settings.mapStyleHint': 'Preset или собственный URL mapbox://styles/USER/ID',
'settings.map3dBuildings': '3D-здания и рельеф',
'settings.map3dHint':
'Наклон + настоящие 3D-здания — работает со всеми стилями, включая спутник.',
'settings.mapHighQuality': 'Режим высокого качества',
'settings.mapHighQualityHint':
'Сглаживание + проекция глобуса для более чётких краёв и реалистичного вида мира.',
'settings.mapHighQualityWarning':
'Может повлиять на производительность на слабых устройствах.',
'settings.mapTipLabel': 'Совет:',
'settings.mapTip':
'Зажмите правую кнопку мыши и перетащите, чтобы повернуть/наклонить карту. Клик средней кнопкой — добавить место (правая кнопка зарезервирована для вращения).',
'settings.latitude': 'Широта',
'settings.longitude': 'Долгота',
'settings.saveMap': 'Сохранить карту',
'settings.apiKeys': 'API-ключи',
'settings.mapsKey': 'API-ключ Google Maps',
'settings.mapsKeyHint':
'Для поиска мест. Требуется Places API (New). Получите на console.cloud.google.com',
'settings.weatherKey': 'API-ключ OpenWeatherMap',
'settings.weatherKeyHint':
'Для данных о погоде. Бесплатно на openweathermap.org/api',
'settings.keyPlaceholder': 'Введите ключ...',
'settings.configured': 'Настроено',
'settings.saveKeys': 'Сохранить ключи',
'settings.display': 'Отображение',
'settings.colorMode': 'Цветовая схема',
'settings.light': 'Светлая',
'settings.dark': 'Тёмная',
'settings.auto': 'Авто',
'settings.language': 'Язык',
'settings.temperature': 'Единица температуры',
'settings.timeFormat': 'Формат времени',
'settings.blurBookingCodes': 'Скрыть коды бронирования',
'settings.notifications': 'Уведомления',
'settings.notifyTripInvite': 'Приглашения в поездку',
'settings.notifyBookingChange': 'Изменения бронирований',
'settings.notifyTripReminder': 'Напоминания о поездке',
'settings.notifyTodoDue': 'Задача к сроку',
'settings.notifyVacayInvite': 'Приглашения слияния Vacay',
'settings.notifyPhotosShared': 'Общие фото (Immich)',
'settings.notifyCollabMessage': 'Сообщения чата (Collab)',
'settings.notifyPackingTagged': 'Список вещей: назначения',
'settings.notifyWebhook': 'Webhook-уведомления',
'settings.notificationsDisabled':
'Уведомления не настроены. Попросите администратора включить уведомления по электронной почте или webhook.',
'settings.notificationsActive': 'Активный канал',
'settings.notificationsManagedByAdmin':
'События уведомлений настраиваются администратором.',
'settings.on': 'Вкл.',
'settings.off': 'Выкл.',
'settings.mcp.title': 'Настройка MCP',
'settings.mcp.endpoint': 'MCP-эндпоинт',
'settings.mcp.clientConfig': 'Конфигурация клиента',
'settings.mcp.clientConfigHint':
'Замените <your_token> на API-токен из списка ниже. Путь к npx может потребовать настройки для вашей системы (например, C:\\PROGRA~1\\nodejs\\npx.cmd в Windows).',
'settings.mcp.clientConfigHintOAuth':
'Замените <your_client_id> и <your_client_secret> на учётные данные из созданного выше клиента OAuth 2.1. При первом подключении mcp-remote откроет браузер для завершения авторизации. Путь к npx может потребовать настройки для вашей системы (например, C:\\PROGRA~1\\nodejs\\npx.cmd в Windows).',
'settings.mcp.copy': 'Копировать',
'settings.mcp.copied': 'Скопировано!',
'settings.mcp.apiTokens': 'API-токены',
'settings.mcp.createToken': 'Создать токен',
'settings.mcp.noTokens':
'Токенов пока нет. Создайте один для подключения MCP-клиентов.',
'settings.mcp.tokenCreatedAt': 'Создан',
'settings.mcp.tokenUsedAt': 'Использован',
'settings.mcp.deleteTokenTitle': 'Удалить токен',
'settings.mcp.deleteTokenMessage':
'Этот токен перестанет работать немедленно. Любой MCP-клиент, использующий его, потеряет доступ.',
'settings.mcp.modal.createTitle': 'Создать API-токен',
'settings.mcp.modal.tokenName': 'Название токена',
'settings.mcp.modal.tokenNamePlaceholder':
'напр. Claude Desktop, Рабочий ноутбук',
'settings.mcp.modal.creating': 'Создание…',
'settings.mcp.modal.create': 'Создать токен',
'settings.mcp.modal.createdTitle': 'Токен создан',
'settings.mcp.modal.createdWarning':
'Этот токен будет показан только один раз. Скопируйте и сохраните его сейчас — восстановить его будет невозможно.',
'settings.mcp.modal.done': 'Готово',
'settings.mcp.toast.created': 'Токен создан',
'settings.mcp.toast.createError': 'Не удалось создать токен',
'settings.mcp.toast.deleted': 'Токен удалён',
'settings.mcp.toast.deleteError': 'Не удалось удалить токен',
'settings.mcp.apiTokensDeprecated':
'API-токены устарели и будут удалены в будущей версии. Пожалуйста, используйте клиенты OAuth 2.1.',
'settings.oauth.clients': 'Клиенты OAuth 2.1',
'settings.oauth.clientsHint':
'Зарегистрируйте клиенты OAuth 2.1, чтобы сторонние MCP-приложения (Claude Web, Cursor и др.) могли подключаться без статических токенов.',
'settings.oauth.createClient': 'Новый клиент',
'settings.oauth.noClients': 'Нет зарегистрированных клиентов OAuth.',
'settings.oauth.clientId': 'ID клиента',
'settings.oauth.clientSecret': 'Секрет клиента',
'settings.oauth.deleteClient': 'Удалить клиента',
'settings.oauth.deleteClientMessage':
'Этот клиент и все активные сессии будут удалены навсегда. Любое приложение, использующее его, немедленно потеряет доступ.',
'settings.oauth.rotateSecret': 'Обновить секрет',
'settings.oauth.rotateSecretMessage':
'Будет сгенерирован новый секрет клиента, а все существующие сессии будут немедленно аннулированы. Обновите приложение перед закрытием этого диалога.',
'settings.oauth.rotateSecretConfirm': 'Обновить',
'settings.oauth.rotateSecretConfirming': 'Обновление…',
'settings.oauth.rotateSecretDoneTitle': 'Новый секрет сгенерирован',
'settings.oauth.rotateSecretDoneWarning':
'Этот секрет отображается только один раз. Скопируйте его сейчас и обновите приложение — все предыдущие сессии были аннулированы.',
'settings.oauth.activeSessions': 'Активные сессии OAuth',
'settings.oauth.sessionScopes': 'Области доступа',
'settings.oauth.sessionExpires': 'Истекает',
'settings.oauth.revoke': 'Отозвать',
'settings.oauth.revokeSession': 'Отозвать сессию',
'settings.oauth.revokeSessionMessage':
'Это немедленно отзовёт доступ для данной сессии OAuth.',
'settings.oauth.modal.createTitle': 'Зарегистрировать клиент OAuth',
'settings.oauth.modal.presets': 'Быстрые настройки',
'settings.oauth.modal.clientName': 'Название приложения',
'settings.oauth.modal.clientNamePlaceholder':
'напр. Claude Web, Моё MCP-приложение',
'settings.oauth.modal.redirectUris': 'URI перенаправления',
'settings.oauth.modal.redirectUrisPlaceholder':
'https://your-app.com/callback\nhttps://your-app.com/auth',
'settings.oauth.modal.redirectUrisHint':
'Один URI на строку. Требуется HTTPS (localhost исключён). Требуется точное совпадение.',
'settings.oauth.modal.scopes': 'Разрешённые области доступа',
'settings.oauth.modal.scopesHint':
'list_trips и get_trip_summary всегда доступны — область не требуется. Они помогают ИИ находить нужные ID поездок.',
'settings.oauth.modal.selectAll': 'Выбрать все',
'settings.oauth.modal.deselectAll': 'Снять выбор',
'settings.oauth.modal.creating': 'Регистрация…',
'settings.oauth.modal.create': 'Зарегистрировать клиента',
'settings.oauth.modal.createdTitle': 'Клиент зарегистрирован',
'settings.oauth.modal.createdWarning':
'Секрет клиента отображается только один раз. Скопируйте его сейчас — его нельзя будет восстановить.',
'settings.oauth.toast.createError':
'Не удалось зарегистрировать клиент OAuth',
'settings.oauth.toast.deleted': 'Клиент OAuth удалён',
'settings.oauth.toast.deleteError': 'Не удалось удалить клиент OAuth',
'settings.oauth.toast.revoked': 'Сессия отозвана',
'settings.oauth.toast.revokeError': 'Не удалось отозвать сессию',
'settings.oauth.toast.rotateError': 'Не удалось обновить секрет клиента',
'settings.oauth.modal.machineClient':
'Машинный клиент (без входа через браузер)',
'settings.oauth.modal.machineClientHint':
'Использует грант client_credentials — URI перенаправления не требуются. Токен выдаётся напрямую через client_id + client_secret и действует от вашего имени в пределах выбранных областей.',
'settings.oauth.modal.machineClientUsage':
'Получить токен: POST /oauth/token с grant_type=client_credentials, client_id и client_secret. Без браузера, без токена обновления.',
'settings.oauth.badge.machine': 'машинный',
'settings.account': 'Аккаунт',
'settings.about': 'О приложении',
'settings.about.reportBug': 'Сообщить об ошибке',
'settings.about.reportBugHint': 'Нашли проблему? Сообщите нам',
'settings.about.featureRequest': 'Предложить функцию',
'settings.about.featureRequestHint': 'Предложите новую функцию',
'settings.about.wikiHint': 'Документация и руководства',
'settings.about.supporters.badge': 'Ежемесячные спонсоры',
'settings.about.supporters.title': 'Спутники TREK',
'settings.about.supporters.subtitle':
'Пока ты планируешь следующий маршрут, эти люди планируют вместе со мной будущее TREK. Их ежемесячный взнос идёт напрямую в разработку и реально потраченные часы — чтобы TREK оставался Open Source.',
'settings.about.supporters.since': 'спонсор с {date}',
'settings.about.supporters.tierEmpty': 'Стань первым',
'settings.about.supporter.tier.noReturnTicket': 'No Return Ticket',
'settings.about.supporter.tier.lostLuggageVip': 'Lost Luggage VIP',
'settings.about.supporter.tier.businessClassDreamer':
'Business Class Dreamer',
'settings.about.supporter.tier.budgetTraveller': 'Budget Traveller',
'settings.about.supporter.tier.hostelBunkmate': 'Hostel Bunkmate',
'settings.about.description':
'TREK — это самостоятельно размещаемый планировщик путешествий, который помогает организовать поездки от первой идеи до последнего воспоминания. Планирование по дням, бюджет, списки вещей, фото и многое другое — всё в одном месте, на вашем собственном сервере.',
'settings.about.madeWith': 'Сделано с',
'settings.about.madeBy': 'Морисом и растущим open-source сообществом.',
'settings.username': 'Имя пользователя',
'settings.email': 'Эл. почта',
'settings.role': 'Роль',
'settings.roleAdmin': 'Администратор',
'settings.oidcLinked': 'Связан с',
'settings.changePassword': 'Изменить пароль',
'settings.mustChangePassword':
'Вы должны сменить пароль перед продолжением. Пожалуйста, установите новый пароль ниже.',
'settings.currentPassword': 'Текущий пароль',
'settings.currentPasswordRequired': 'Текущий пароль обязателен',
'settings.newPassword': 'Новый пароль',
'settings.confirmPassword': 'Подтвердите новый пароль',
'settings.updatePassword': 'Обновить пароль',
'settings.passwordRequired': 'Введите текущий и новый пароль',
'settings.passwordTooShort': 'Пароль должен содержать не менее 8 символов',
'settings.passwordMismatch': 'Пароли не совпадают',
'settings.passwordWeak':
'Пароль должен содержать заглавные, строчные буквы, цифру и специальный символ',
'settings.passwordChanged': 'Пароль успешно изменён',
'settings.deleteAccount': 'Удалить аккаунт',
'settings.deleteAccountTitle': 'Удалить ваш аккаунт?',
'settings.deleteAccountWarning':
'Ваш аккаунт и все поездки, места и файлы будут безвозвратно удалены. Это действие нельзя отменить.',
'settings.deleteAccountConfirm': 'Удалить безвозвратно',
'settings.deleteBlockedTitle': 'Удаление невозможно',
'settings.deleteBlockedMessage':
'Вы единственный администратор. Назначьте другого пользователя администратором перед удалением своего аккаунта.',
'settings.roleUser': 'Пользователь',
'settings.saveProfile': 'Сохранить профиль',
'settings.mfa.title': 'Двухфакторная аутентификация (2FA)',
'settings.mfa.description':
'Добавляет второй шаг при входе. Используйте приложение-аутентификатор (Google Authenticator, Authy и др.).',
'settings.mfa.requiredByPolicy':
'Администратор требует двухфакторную аутентификацию. Настройте приложение-аутентификатор ниже, прежде чем продолжить.',
'settings.mfa.backupTitle': 'Резервные коды',
'settings.mfa.backupDescription':
'Используйте эти одноразовые коды, если потеряете доступ к приложению-аутентификатору.',
'settings.mfa.backupWarning':
'Сохраните их сейчас. Каждый код можно использовать только один раз.',
'settings.mfa.backupCopy': 'Скопировать коды',
'settings.mfa.backupDownload': 'Скачать TXT',
'settings.mfa.backupPrint': 'Печать / PDF',
'settings.mfa.backupCopied': 'Резервные коды скопированы',
'settings.mfa.enabled': '2FA включена для вашего аккаунта.',
'settings.mfa.disabled': '2FA не включена.',
'settings.mfa.setup': 'Настроить аутентификатор',
'settings.mfa.scanQr':
'Отсканируйте QR-код приложением или введите ключ вручную.',
'settings.mfa.secretLabel': 'Секретный ключ (ручной ввод)',
'settings.mfa.codePlaceholder': '6-значный код',
'settings.mfa.enable': 'Включить 2FA',
'settings.mfa.cancelSetup': 'Отмена',
'settings.mfa.disableTitle': 'Отключить 2FA',
'settings.mfa.disableHint':
'Введите пароль аккаунта и текущий код из аутентификатора.',
'settings.mfa.disable': 'Отключить 2FA',
'settings.mfa.toastEnabled': 'Двухфакторная аутентификация включена',
'settings.mfa.toastDisabled': 'Двухфакторная аутентификация отключена',
'settings.mfa.demoBlocked': 'Недоступно в демо-режиме',
'settings.toast.mapSaved': 'Настройки карты сохранены',
'settings.toast.keysSaved': 'API-ключи сохранены',
'settings.toast.displaySaved': 'Настройки отображения сохранены',
'settings.toast.profileSaved': 'Профиль сохранён',
'settings.uploadAvatar': 'Загрузить фото профиля',
'settings.removeAvatar': 'Удалить фото профиля',
'settings.avatarUploaded': 'Фото профиля обновлено',
'settings.avatarRemoved': 'Фото профиля удалено',
'settings.avatarError': 'Ошибка загрузки',
'settings.bookingLabels': 'Подписи маршрутов бронирований',
'settings.bookingLabelsHint':
'Отображает названия станций / аэропортов на карте. Если выключено, показывается только значок.',
'settings.notifyVersionAvailable': 'Доступна новая версия',
'settings.notificationPreferences.noChannels':
'Каналы уведомлений не настроены. Попросите администратора настроить уведомления по электронной почте или через webhook.',
'settings.webhookUrl.label': 'URL вебхука',
'settings.webhookUrl.placeholder': 'https://discord.com/api/webhooks/...',
'settings.webhookUrl.hint':
'Введите URL вашего вебхука Discord, Slack или пользовательского для получения уведомлений.',
'settings.webhookUrl.saved': 'URL вебхука сохранён',
'settings.webhookUrl.test': 'Тест',
'settings.webhookUrl.testSuccess': 'Тестовый вебхук успешно отправлен',
'settings.webhookUrl.testFailed': 'Ошибка тестового вебхука',
'settings.ntfyUrl.topicLabel': 'Тема Ntfy',
'settings.ntfyUrl.topicPlaceholder': 'my-trek-alerts',
'settings.ntfyUrl.serverLabel': 'URL сервера Ntfy (необязательно)',
'settings.ntfyUrl.serverPlaceholder': 'https://ntfy.sh',
'settings.ntfyUrl.hint':
'Введите тему Ntfy для получения push-уведомлений. Оставьте поле сервера пустым, чтобы использовать настройку по умолчанию, заданную администратором.',
'settings.ntfyUrl.tokenLabel': 'Токен доступа (необязательно)',
'settings.ntfyUrl.tokenHint': 'Требуется для тем, защищённых паролем.',
'settings.ntfyUrl.saved': 'Настройки Ntfy сохранены',
'settings.ntfyUrl.test': 'Тест',
'settings.ntfyUrl.testSuccess':
'Тестовое уведомление Ntfy успешно отправлено',
'settings.ntfyUrl.testFailed': 'Ошибка отправки тестового уведомления Ntfy',
'settings.ntfyUrl.tokenCleared': 'Токен доступа очищен',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.ntfy': 'Ntfy',
};
export default settings;
+16
View File
@@ -0,0 +1,16 @@
import type { TranslationStrings } from '../types';
const share: TranslationStrings = {
'share.linkTitle': 'Публичная ссылка',
'share.linkHint':
'Создайте ссылку, по которой любой сможет просмотреть эту поездку без входа в систему. Только чтение — редактирование невозможно.',
'share.createLink': 'Создать ссылку',
'share.deleteLink': 'Удалить ссылку',
'share.createError': 'Не удалось создать ссылку',
'share.permMap': 'Карта и план',
'share.permBookings': 'Бронирования',
'share.permPacking': 'Вещи',
'share.permBudget': 'Бюджет',
'share.permCollab': 'Чат',
};
export default share;
+21
View File
@@ -0,0 +1,21 @@
import type { TranslationStrings } from '../types';
const shared: TranslationStrings = {
'shared.expired': 'Ссылка устарела или недействительна',
'shared.expiredHint': 'Эта ссылка на поездку больше не активна.',
'shared.readOnly': 'Режим только для чтения',
'shared.tabPlan': 'План',
'shared.tabBookings': 'Бронирования',
'shared.tabPacking': 'Багаж',
'shared.tabBudget': 'Бюджет',
'shared.tabChat': 'Чат',
'shared.days': 'дней',
'shared.places': 'мест',
'shared.other': 'Прочее',
'shared.totalBudget': 'Общий бюджет',
'shared.messages': 'сообщений',
'shared.sharedVia': 'Поделено через',
'shared.confirmed': 'Подтверждено',
'shared.pending': 'Ожидает',
};
export default shared;
+13
View File
@@ -0,0 +1,13 @@
import type { TranslationStrings } from '../types';
const stats: TranslationStrings = {
'stats.countries': 'Страны',
'stats.cities': 'Города',
'stats.trips': 'Поездки',
'stats.places': 'Места',
'stats.worldProgress': 'Прогресс по миру',
'stats.visited': 'посещено',
'stats.remaining': 'осталось',
'stats.visitedCountries': 'Посещённые страны',
};
export default stats;
+59
View File
@@ -0,0 +1,59 @@
import type { TranslationStrings } from '../types';
const system_notice: TranslationStrings = {
'system_notice.welcome_v1.title': 'Добро пожаловать в TREK',
'system_notice.welcome_v1.body':
'Ваш универсальный планировщик путешествий. Создавайте маршруты, делитесь поездками с друзьями и оставайтесь организованными — онлайн и офлайн.',
'system_notice.welcome_v1.cta_label': 'Спланировать поездку',
'system_notice.welcome_v1.hero_alt':
'Живописное место назначения с интерфейсом TREK',
'system_notice.welcome_v1.highlight_plan': 'Маршруты по дням',
'system_notice.welcome_v1.highlight_share':
'Совместное планирование с партнёрами',
'system_notice.welcome_v1.highlight_offline': 'Работает офлайн на мобильном',
'system_notice.dev_test_modal.title': '[Dev] Test notice',
'system_notice.dev_test_modal.body': 'This is a dev-only test notice.',
'system_notice.pager.prev': 'Предыдущее уведомление',
'system_notice.pager.next': 'Следующее уведомление',
'system_notice.pager.counter': '{current} / {total}',
'system_notice.pager.goto': 'Перейти к уведомлению {n}',
'system_notice.pager.position': 'Уведомление {current} из {total}',
'system_notice.v3_photos.title': 'Фото перемещены в версии 3.0',
'system_notice.v3_photos.body':
'Вкладка **Фото** в Планировщике путешествий удалена. Ваши фото в безопасности — TREK никогда не изменял вашу библиотеку Immich или Synology.\n\nФото теперь доступны в дополнении **Journey**. Journey необязателен — если он ещё недоступен, попросите администратора включить его в разделе Admin → Дополнения.',
'system_notice.v3_journey.title': 'Знакомьтесь с Journey',
'system_notice.v3_journey.body':
'Документируйте путешествия в виде рассказов с хронологиями, фотогалереями и интерактивными картами.',
'system_notice.v3_journey.cta_label': 'Открыть Journey',
'system_notice.v3_journey.highlight_timeline':
'Ежедневная хронология и галерея',
'system_notice.v3_journey.highlight_photos': 'Импорт из Immich или Synology',
'system_notice.v3_journey.highlight_share': 'Общий доступ — без входа',
'system_notice.v3_journey.highlight_export': 'Экспорт в PDF-фотокнигу',
'system_notice.v3_features.title': 'Ещё нового в версии 3.0',
'system_notice.v3_features.body':
'Несколько других важных новшеств в этом релизе.',
'system_notice.v3_features.highlight_dashboard':
'Переработанная панель в mobile-first стиле',
'system_notice.v3_features.highlight_offline': 'Полный офлайн-режим как PWA',
'system_notice.v3_features.highlight_search':
'Автодополнение поиска мест в реальном времени',
'system_notice.v3_features.highlight_import': 'Импорт мест из KMZ/KML-файлов',
'system_notice.v3_mcp.title': 'MCP: обновление OAuth 2.1',
'system_notice.v3_mcp.body':
'Интеграция MCP была полностью переработана. OAuth 2.1 теперь является рекомендуемым методом аутентификации. Статические токены (trek_…) устарели и будут удалены в будущей версии.',
'system_notice.v3_mcp.highlight_oauth':
'OAuth 2.1 рекомендуется (mcp-remote)',
'system_notice.v3_mcp.highlight_scopes': '24 детальных области разрешений',
'system_notice.v3_mcp.highlight_deprecated':
'Статические токены trek_ устарели',
'system_notice.v3_mcp.highlight_tools': 'Расширенный набор инструментов',
'system_notice.v3_thankyou.title': 'Личное слово от меня',
'system_notice.v3_thankyou.body':
'Прежде чем продолжить — хочу остановиться на мгновение.\n\nTREK начинался как сторонний проект, который я создал для собственных поездок. Я никогда не думал, что он вырастет во что-то, чему 4 000 из вас доверяют планирование своих приключений. Каждая звёздочка, каждый issue, каждый запрос на фичу — я читаю их все, и именно они поддерживают меня в поздние ночи между основной работой и университетом.\n\nХочу, чтобы вы знали: TREK всегда будет open source, всегда self-hosted, всегда вашим. Никакого отслеживания, никаких подписок, никаких подвохов. Просто инструмент, созданный человеком, который любит путешествовать так же, как и вы.\n\nОсобая благодарность [jubnl](https://github.com/jubnl) — ты стал невероятным соратником. Многое из того, что делает версию 3.0 великолепной, несёт твой отпечаток. Спасибо, что поверил в этот проект, когда он был ещё сырым.\n\nИ каждому из вас, кто сообщил об ошибке, перевёл строку, поделился TREK с другом или просто использовал его для планирования поездки — **спасибо**. Вы — причина, по которой всё это существует.\n\nЗа множество новых приключений вместе.\n\n— Maurice\n\n---\n\n[Присоединяйся к сообществу в Discord](https://discord.gg/7Q6M6jDwzf)\n\nЕсли TREK делает твои путешествия лучше, [маленький кофе](https://ko-fi.com/mauriceboe) всегда помогает держать свет включённым.',
'system_notice.v3014_whitespace_collision.title':
'Требуется действие: конфликт учётных записей',
'system_notice.v3014_whitespace_collision.body':
'Обновление 3.0.14 обнаружило один или несколько конфликтов имён пользователей или адресов электронной почты, вызванных ведущими или завершающими пробелами в сохранённых значениях. Затронутые учётные записи были автоматически переименованы. Проверьте логи сервера на строки, начинающиеся с **[migration] WHITESPACE COLLISION**, чтобы определить учётные записи, требующие проверки.',
};
export default system_notice;
+40
View File
@@ -0,0 +1,40 @@
import type { TranslationStrings } from '../types';
const todo: TranslationStrings = {
'todo.subtab.packing': 'Список вещей',
'todo.subtab.todo': 'Задачи',
'todo.completed': 'выполнено',
'todo.filter.all': 'Все',
'todo.filter.open': 'Открытые',
'todo.filter.done': 'Выполненные',
'todo.uncategorized': 'Без категории',
'todo.namePlaceholder': 'Название задачи',
'todo.descriptionPlaceholder': 'Описание (необязательно)',
'todo.unassigned': 'Не назначено',
'todo.noCategory': 'Без категории',
'todo.hasDescription': 'Есть описание',
'todo.addItem': 'Новая задача',
'todo.sidebar.sortBy': 'Сортировать по',
'todo.priority': 'Приоритет',
'todo.newCategoryLabel': 'новая',
'todo.newCategory': 'Название категории',
'todo.addCategory': 'Добавить категорию',
'todo.newItem': 'Новая задача',
'todo.empty': 'Задач пока нет. Добавьте задачу, чтобы начать!',
'todo.filter.my': 'Мои задачи',
'todo.filter.overdue': 'Просроченные',
'todo.sidebar.tasks': 'Задачи',
'todo.sidebar.categories': 'Категории',
'todo.detail.title': 'Задача',
'todo.detail.description': 'Описание',
'todo.detail.category': 'Категория',
'todo.detail.dueDate': 'Срок выполнения',
'todo.detail.assignedTo': 'Назначено',
'todo.detail.delete': 'Удалить',
'todo.detail.save': 'Сохранить изменения',
'todo.detail.create': 'Создать задачу',
'todo.detail.priority': 'Приоритет',
'todo.detail.noPriority': 'Нет',
'todo.sortByPrio': 'Приоритет',
};
export default todo;
+10
View File
@@ -0,0 +1,10 @@
import type { TranslationStrings } from '../types';
const transport: TranslationStrings = {
'transport.addTransport': 'Добавить транспорт',
'transport.modalTitle.create': 'Добавить транспорт',
'transport.modalTitle.edit': 'Изменить транспорт',
'transport.title': 'Транспорт',
'transport.addManual': 'Ручной транспорт',
};
export default transport;
+31
View File
@@ -0,0 +1,31 @@
import type { TranslationStrings } from '../types';
const trip: TranslationStrings = {
'trip.tabs.plan': 'План',
'trip.tabs.transports': 'Транспорт',
'trip.tabs.reservations': 'Бронирования',
'trip.tabs.reservationsShort': 'Брони',
'trip.tabs.packing': 'Список вещей',
'trip.tabs.packingShort': 'Вещи',
'trip.tabs.lists': 'Списки',
'trip.tabs.listsShort': 'Списки',
'trip.tabs.budget': 'Бюджет',
'trip.tabs.files': 'Файлы',
'trip.loading': 'Загрузка поездки...',
'trip.loadingPhotos': 'Загрузка фото мест...',
'trip.mobilePlan': 'План',
'trip.mobilePlaces': 'Места',
'trip.toast.placeUpdated': 'Место обновлено',
'trip.toast.placeAdded': 'Место добавлено',
'trip.toast.placeDeleted': 'Место удалено',
'trip.toast.selectDay': 'Сначала выберите день',
'trip.toast.assignedToDay': 'Место назначено на день',
'trip.toast.reorderError': 'Ошибка изменения порядка',
'trip.toast.reservationUpdated': 'Бронирование обновлено',
'trip.toast.reservationAdded': 'Бронирование добавлено',
'trip.toast.deleted': 'Удалено',
'trip.confirm.deletePlace': 'Вы уверены, что хотите удалить это место?',
'trip.confirm.deletePlaces': 'Удалить {count} мест?',
'trip.toast.placesDeleted': '{count} мест удалено',
};
export default trip;
+17
View File
@@ -0,0 +1,17 @@
import type { TranslationStrings } from '../types';
const trips: TranslationStrings = {
'trips.memberRemoved': '{username} удалён',
'trips.memberRemoveError': 'Не удалось удалить',
'trips.memberAdded': '{username} добавлен',
'trips.memberAddError': 'Не удалось добавить',
'trips.reminder': 'Напоминание',
'trips.reminderNone': 'Нет',
'trips.reminderDay': 'день',
'trips.reminderDays': 'дней',
'trips.reminderCustom': 'Другое',
'trips.reminderDaysBefore': 'дней до отъезда',
'trips.reminderDisabledHint':
'Напоминания о поездках отключены. Включите их в Админ > Настройки > Уведомления.',
};
export default trips;
+21
View File
@@ -0,0 +1,21 @@
import type { TranslationStrings } from '../types';
const undo: TranslationStrings = {
'undo.button': 'Отменить',
'undo.tooltip': 'Отменить: {action}',
'undo.assignPlace': 'Место добавлено в день',
'undo.removeAssignment': 'Место удалено из дня',
'undo.reorder': 'Места переупорядочены',
'undo.optimize': 'Маршрут оптимизирован',
'undo.deletePlace': 'Место удалено',
'undo.deletePlaces': 'Места удалены',
'undo.moveDay': 'Место перемещено в другой день',
'undo.lock': 'Блокировка места изменена',
'undo.importGpx': 'Импорт GPX',
'undo.importKeyholeMarkup': 'Импорт KMZ/KML',
'undo.importGoogleList': 'Импорт из Google Maps',
'undo.importNaverList': 'Импорт из Naver Maps',
'undo.addPlace': 'Место добавлено',
'undo.done': 'Отменено: {action}',
};
export default undo;
+107
View File
@@ -0,0 +1,107 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'vacay.subtitle': 'Планируйте и управляйте днями отпуска',
'vacay.settings': 'Настройки',
'vacay.year': 'Год',
'vacay.addYear': 'Добавить следующий год',
'vacay.addPrevYear': 'Добавить предыдущий год',
'vacay.removeYear': 'Удалить год',
'vacay.removeYearConfirm': 'Удалить {year}?',
'vacay.removeYearHint':
'Все записи об отпуске и корпоративные выходные за этот год будут безвозвратно удалены.',
'vacay.remove': 'Удалить',
'vacay.persons': 'Люди',
'vacay.noPersons': 'Никто не добавлен',
'vacay.addPerson': 'Добавить человека',
'vacay.editPerson': 'Редактировать',
'vacay.removePerson': 'Удалить человека',
'vacay.removePersonConfirm': 'Удалить {name}?',
'vacay.removePersonHint':
'Все записи об отпуске этого человека будут безвозвратно удалены.',
'vacay.personName': 'Имя',
'vacay.personNamePlaceholder': 'Введите имя',
'vacay.color': 'Цвет',
'vacay.add': 'Добавить',
'vacay.legend': 'Легенда',
'vacay.publicHoliday': 'Государственный праздник',
'vacay.companyHoliday': 'Корпоративный выходной',
'vacay.weekend': 'Выходные',
'vacay.modeVacation': 'Отпуск',
'vacay.modeCompany': 'Корпоративный выходной',
'vacay.entitlement': 'Право на отпуск',
'vacay.entitlementDays': 'Дни',
'vacay.used': 'Использовано',
'vacay.remaining': 'Осталось',
'vacay.carriedOver': 'из {year}',
'vacay.blockWeekends': 'Блокировать выходные',
'vacay.blockWeekendsHint':
'Запретить записи об отпуске в субботу и воскресенье',
'vacay.weekendDays': 'Выходные дни',
'vacay.mon': 'Пн',
'vacay.tue': 'Вт',
'vacay.wed': 'Ср',
'vacay.thu': 'Чт',
'vacay.fri': 'Пт',
'vacay.sat': 'Сб',
'vacay.sun': 'Вс',
'vacay.publicHolidays': 'Государственные праздники',
'vacay.publicHolidaysHint': 'Отмечать государственные праздники в календаре',
'vacay.selectCountry': 'Выберите страну',
'vacay.selectRegion': 'Выберите регион (необязательно)',
'vacay.companyHolidays': 'Корпоративные выходные',
'vacay.companyHolidaysHint': 'Разрешить отмечать корпоративные выходные дни',
'vacay.companyHolidaysNoDeduct':
'Корпоративные выходные не вычитаются из дней отпуска.',
'vacay.weekStart': 'Неделя начинается с',
'vacay.weekStartHint':
'Выберите, начинается ли неделя с понедельника или воскресенья',
'vacay.carryOver': 'Перенос',
'vacay.carryOverHint':
'Автоматически переносить оставшиеся дни отпуска на следующий год',
'vacay.sharing': 'Общий доступ',
'vacay.sharingHint':
'Поделитесь планом отпуска с другими пользователями TREK',
'vacay.owner': 'Владелец',
'vacay.shareEmailPlaceholder': 'Эл. почта пользователя TREK',
'vacay.shareSuccess': 'План успешно предоставлен',
'vacay.shareError': 'Не удалось поделиться планом',
'vacay.dissolve': 'Разделить объединение',
'vacay.dissolveHint':
'Снова разделить календари. Ваши записи будут сохранены.',
'vacay.dissolveAction': 'Разделить',
'vacay.dissolved': 'Календарь разделён',
'vacay.fusedWith': 'Объединён с',
'vacay.you': 'вы',
'vacay.noData': 'Нет данных',
'vacay.changeColor': 'Изменить цвет',
'vacay.inviteUser': 'Пригласить пользователя',
'vacay.inviteHint':
'Пригласите другого пользователя TREK для совместного календаря отпусков.',
'vacay.selectUser': 'Выберите пользователя',
'vacay.sendInvite': 'Отправить приглашение',
'vacay.inviteSent': 'Приглашение отправлено',
'vacay.inviteError': 'Не удалось отправить приглашение',
'vacay.pending': 'ожидание',
'vacay.noUsersAvailable': 'Нет доступных пользователей',
'vacay.accept': 'Принять',
'vacay.decline': 'Отклонить',
'vacay.acceptFusion': 'Принять и объединить',
'vacay.inviteTitle': 'Запрос на объединение',
'vacay.inviteWantsToFuse': 'хочет объединить календарь отпусков с вами.',
'vacay.fuseInfo1':
'Вы оба будете видеть все записи об отпуске в одном общем календаре.',
'vacay.fuseInfo2':
'Обе стороны могут создавать и редактировать записи друг для друга.',
'vacay.fuseInfo3':
'Обе стороны могут удалять записи и изменять право на отпуск.',
'vacay.fuseInfo4':
'Настройки, такие как праздники и корпоративные выходные, становятся общими.',
'vacay.fuseInfo5':
'Объединение можно отменить в любое время любой из сторон. Ваши записи будут сохранены.',
'vacay.addCalendar': 'Добавить календарь',
'vacay.calendarColor': 'Цвет',
'vacay.calendarLabel': 'Название',
'vacay.noCalendars': 'Нет календарей',
};
export default vacay;