From 63956c9c2d60f829a998237abe6ade675fdb01ed Mon Sep 17 00:00:00 2001 From: MjKey Date: Fri, 30 Jan 2026 07:50:37 +0300 Subject: [PATCH] feat(i18n): add Russian localization (#48) * feat(i18n): add Russian localization * fix: missing i18n keys --------- Co-authored-by: Violetta Co-authored-by: Draculabo --- src/components/AccountCard.tsx | 14 +- src/localization/i18n.ts | 321 ++++++++++++++++++++++++++++++++- src/routes/settings.tsx | 1 + 3 files changed, 329 insertions(+), 7 deletions(-) diff --git a/src/components/AccountCard.tsx b/src/components/AccountCard.tsx index 1cddf87..5520717 100644 --- a/src/components/AccountCard.tsx +++ b/src/components/AccountCard.tsx @@ -12,6 +12,7 @@ import { } from '@/components/ui/dropdown-menu'; import { MoreVertical, Trash2, RefreshCw } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; +import { enUS, zhCN, ru } from 'date-fns/locale'; import { cn } from '@/lib/utils'; import { useTranslation } from 'react-i18next'; @@ -32,18 +33,18 @@ export const AccountCard: React.FC = ({ isSwitching, isDeleting, }) => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const cardRef = useRef(null); const [transform, setTransform] = useState(''); const [glarePosition, setGlarePosition] = useState({ x: 50, y: 50 }); const initials = account.name ? account.name - .split(' ') - .map((n) => n[0]) - .join('') - .toUpperCase() - .slice(0, 2) + .split(' ') + .map((n) => n[0]) + .join('') + .toUpperCase() + .slice(0, 2) : account.email[0].toUpperCase(); // 处理鼠标移动,计算 3D 旋转角度 @@ -136,6 +137,7 @@ export const AccountCard: React.FC = ({ {t('account.lastUsed', { time: formatDistanceToNow(new Date(account.last_used), { addSuffix: true, + locale: { en: enUS, 'zh-CN': zhCN, ru: ru }[i18n.language] || enUS, }), })}

diff --git a/src/localization/i18n.ts b/src/localization/i18n.ts index f9dc224..d7fac41 100644 --- a/src/localization/i18n.ts +++ b/src/localization/i18n.ts @@ -12,7 +12,7 @@ i18n caches: ['localStorage'], lookupLocalStorage: 'lang', }, - supportedLngs: ['en', 'zh-CN'], + supportedLngs: ['en', 'zh-CN', 'ru'], load: 'currentOnly', // Only load the exact language code, not variants resources: { en: { @@ -83,6 +83,7 @@ i18n description: 'Select your preferred language.', english: 'English', chinese: 'Chinese (Simplified)', + russian: 'Russian', }, about: { title: 'About', @@ -311,6 +312,10 @@ i18n api_key: 'API Key', auto_start: 'Auto Start with App', auto_start_desc: 'Start proxy service when application launches', + local_access: 'Local network access:', + no_token_warning: '⚠️ API key is not set. Access is open to everyone!', + show_key: 'Show', + hide_key: 'Hide', }, mapping: { title: 'Model Mapping', @@ -393,6 +398,7 @@ i18n description: '选择您的首选语言。', english: '英语', chinese: '中文 (简体)', + russian: '俄语', }, about: { title: '关于', @@ -635,5 +641,318 @@ i18n }, }, }, + ru: { + translation: { + appName: 'Antigravity Manager', + status: { + checking: 'Проверка статуса...', + running: 'Antigravity работает в фоне', + stopped: 'Служба Antigravity остановлена', + }, + action: { + stop: 'Стоп', + start: 'Старт', + switch: 'Переключить', + deleteBackup: 'Удалить резервную копию', + backupCurrent: 'Создать бэкап текущего', + retry: 'Повторить', + openLogs: 'Открыть папку логов', + }, + error: { + generic: 'Произошла непредвиденная ошибка.', + keychainUnavailable: 'Keychain недоступен.', + keychainHint: { + translocation: + 'Обнаружено App Translocation (macOS). Переместите приложение в /Applications и откройте заново.', + keychainDenied: + 'Доступ к Keychain запрещен. Возможно, приложение не подписано. См. README для инструкции.', + signNotarize: 'Пожалуйста, используйте подписанную версию приложения.', + }, + dataMigrationFailed: 'Не удалось расшифровать данные старого аккаунта.', + dataMigrationHint: { + relogin: 'Пожалуйста, перезайдите в аккаунт или добавьте его заново.', + clearData: 'Если ошибка сохраняется, очистите локальные данные и войдите снова.', + }, + }, + nav: { + accounts: 'Аккаунты', + proxy: 'API Прокси', + settings: 'Настройки', + }, + account: { + current: 'Текущий', + lastUsed: 'Последнее использование {{time}}', + }, + home: { + title: 'Аккаунты', + description: 'Управление вашими аккаунтами Antigravity Google Gemini.', + noBackups: { + title: 'Бэкапы не найдены', + description: 'Создайте резервную копию вашего текущего аккаунта Antigravity, чтобы начать.', + action: 'Создать бэкап текущего аккаунта', + }, + }, + settings: { + title: 'Настройки', + description: 'Управление настройками приложения.', + general: 'Общие', + connection: 'Подключение', + models: 'Модели', + appearance: { + title: 'Внешний вид', + description: 'Настройте внешний вид Antigravity Manager.', + }, + darkMode: 'Темная тема', + darkModeDescription: 'Включить темную тему для комфортной работы ночью.', + language: { + title: 'Язык', + description: 'Выберите язык интерфейса.', + english: 'English', + chinese: '中文 (简体)', + russian: 'Русский', + }, + about: { + title: 'О программе', + description: 'Информация о приложении.', + }, + version: 'Версия', + platform: 'Платформа', + license: 'Лицензия', + openLogDir: 'Открыть', + account: { + title: 'Настройки аккаунта', + description: 'Настройка автоматического обновления и синхронизации.', + auto_refresh: 'Автообновление квот', + auto_refresh_desc: 'Периодически обновлять информацию о квотах для всех аккаунтов', + auto_sync: 'Автосинхронизация текущего', + auto_sync_desc: 'Периодически синхронизировать активный аккаунт', + }, + startup: { + title: 'Автозагрузка', + description: 'Управление запуском приложения при старте системы.', + auto_startup: 'Запускать вместе с системой', + auto_startup_desc: 'Запускать при входе и сворачивать в трей', + macos_hint: + 'macOS требует подписанное приложение для работы элементов входа. Если автозапуск не работает, подпишите приложение или включите его вручную в системных настройках.', + }, + privacy: { + title: 'Конфиденциальность', + description: 'Управление использованием данных для улучшения приложения.', + error_reporting: 'Отчеты об ошибках', + error_reporting_desc: + 'Отправляйте анонимные отчеты об ошибках, чтобы помочь нам улучшить приложение. Личные данные не собираются.', + restart_note: 'Изменения в отчетах об ошибках вступят в силу после перезапуска приложения.', + }, + proxy: { + title: 'Верхнеуровневый прокси', + description: 'Настроить прокси для исходящих запросов к Google/Gemini API.', + enable: 'Включить прокси', + url: 'URL прокси', + timeout: 'Тайм-аут запроса (сек)', + }, + modelMapping: { + title: 'Маппинг моделей', + description: + 'Сопоставьте модели Claude Code с моделями Antigravity. Оптимизируйте затраты и скорость, разумно маршрутизируя запросы.', + claudeKeyword: 'Модель Claude (Ключевое слово)', + targetGemini: 'Целевая модель Gemini', + addPlaceholderKey: 'например, op-3', + addPlaceholderValue: 'например, gemini-2.0-flash-exp', + noMappings: 'Нет пользовательских сопоставлений.', + mapsTo: 'Мапится в', + default: 'По умолчанию', + restoreDefaults: 'Сбросить по умолчанию', + }, + examples: { + title: 'Примеры использования', + description: 'Примеры команд для вызова локального API прокси.', + curl: 'cURL', + python: 'Python', + copy: 'Копировать', + copied: 'Скопировано!', + openai_protocol: 'Протокол OpenAI', + anthropic_protocol: 'Протокол Anthropic', + openai_tools: 'Cursor, Windsurf, NextChat', + anthropic_tools: 'Claude Code CLI', + flash: 'Быстрый (Flash)', + pro: 'Про (Pro)', + flash_preview: 'Превью (Flash Preview)', + pro_high: 'Лучший (Pro High)', + sonnet: 'Мыслящий (Sonnet)', + opus: 'Opus', + }, + gateway: { + title: 'Служба API Прокси', + description: 'Управление локальным прокси-сервером API.', + status_running: 'Запущено', + status_stopped: 'Остановлено', + action_start: 'Запустить службу', + action_stop: 'Остановить службу', + accounts_info: '{{count}} аккаунтов доступно', + port: 'Порт прослушивания', + port_hint: 'По умолчанию 8045, требуется перезапуск для применения', + timeout: 'Тайм-аут запроса', + timeout_hint: 'По умолчанию 120с, диапазон 30-600с', + api_key: 'API Ключ', + regenerate_key: 'Перегенерировать', + regenerateConfirm: { + title: 'Перегенерировать API ключ?', + description: + 'Это немедленно сделает текущий ключ недействительным. Все приложения, использующие старый ключ, перестанут работать.', + cancel: 'Отмена', + confirm: 'Перегенерировать', + }, + key_warning: 'Держите ваш API ключ в секрете. Не передавайте его никому.', + auto_start: 'Автозапуск с приложением', + auto_start_desc: 'Запускать прокси-службу при старте приложения', + }, + proxy_tab: 'Прокси', + save: 'Сохранить настройки', + }, + toast: { + backupSuccess: { + title: 'Успешно', + description: 'Бэкап аккаунта создан.', + }, + backupError: { + title: 'Ошибка', + description: 'Не удалось создать бэкап: {{error}}', + }, + switchSuccess: { + title: 'Успешно', + description: 'Аккаунт переключен.', + }, + switchError: { + title: 'Ошибка', + description: 'Не удалось переключить аккаунт: {{error}}', + }, + deleteSuccess: { + title: 'Успешно', + description: 'Бэкап удален.', + }, + deleteError: { + title: 'Ошибка', + description: 'Не удалось удалить бэкап: {{error}}', + }, + }, + cloud: { + title: 'Аккаунты', + description: 'Управление пулом аккаунтов Google Gemini.', + autoSwitch: 'Авто-переключение', + addAccount: 'Добавить аккаунт', + syncFromIDE: 'Синхр. из IDE', + checkQuota: 'Проверить квоту', + polling: 'Опрос запущен', + authDialog: { + title: 'Добавить Google Аккаунт', + description: 'Для добавления аккаунта необходимо авторизовать приложение.', + openLogin: 'Открыть страницу входа', + authCode: 'Код авторизации', + placeholder: 'Вставьте код, начинающийся с 4/...', + instruction: + 'Откроется браузер для входа в Google. Скопируйте код со страницы localhost и вставьте сюда.', + verify: 'Проверить и добавить', + }, + card: { + active: 'Активен', + use: 'Использовать', + rateLimited: 'Лимит исчерпан', + left: 'ост.', + used: 'Исп.', + unknown: 'Неизвестный', + actions: 'Действия', + useAccount: 'Использовать этот аккаунт', + refresh: 'Обновить квоту', + delete: 'Удалить аккаунт', + noQuota: 'Нет данных', + }, + list: { + noAccounts: 'Нет добавленных облачных аккаунтов.', + }, + error: { + loadFailed: 'Не удалось загрузить аккаунты.', + }, + toast: { + syncSuccess: { + title: 'Синхронизация успешна', + description: 'Импортирован {{email}} из IDE.', + }, + syncFailed: { + title: 'Синхронизация не удалась', + description: 'В базе данных IDE не найдено активных аккаунтов.', + }, + addSuccess: 'Аккаунт успешно добавлен!', + addFailed: { + title: 'Не удалось добавить аккаунт', + }, + quotaRefreshed: 'Квота обновлена', + refreshFailed: 'Не удалось обновить квоту', + pollFailed: 'Не удалось опросить квоты всех аккаунтов', + switched: { + title: 'Аккаунт переключен!', + description: 'Перезапуск Antigravity...', + }, + switchFailed: 'Не удалось переключить аккаунт', + deleted: 'Аккаунт удален', + deleteFailed: 'Не удалось удалить аккаунт', + deleteConfirm: 'Вы уверены, что хотите удалить этот аккаунт?', + autoSwitchOn: 'Авто-переключение включено', + autoSwitchOff: 'Авто-переключение выключено', + updateSettingsFailed: 'Не удалось обновить настройки', + }, + batch: { + selected: 'Выбрано {{count}}', + delete: 'Удалить выбранные', + refresh: 'Обновить выбранные', + selectAll: 'Выбрать все', + clear: 'Снять выделение', + confirmDelete: 'Вы уверены, что хотите удалить {{count}} аккаунтов?', + }, + }, + proxy: { + title: 'API Прокси', + description: 'Управление локальным прокси-сервером API.', + save: 'Сохранить', + copy: 'Копировать', + regenerate: 'Перегенерировать', + regenerateConfirm: { + title: 'Перегенерировать API ключ?', + description: + 'Это немедленно сделает текущий ключ недействительным. Все приложения, использующие старый ключ, перестанут работать.', + cancel: 'Отмена', + confirm: 'Перегенерировать', + }, + service: { + title: 'Статус сервиса', + description: 'Управление локальным сервером.', + running: 'Работает', + stopped: 'Остановлен', + start: 'Запустить', + stop: 'Остановить', + }, + config: { + port: 'Порт', + timeout: 'Тайм-аут', + api_key: 'API Ключ', + auto_start: 'Автозапуск', + auto_start_desc: 'Запускать прокси при старте приложения', + local_access: 'Доступ из локальной сети:', + no_token_warning: '⚠️ API ключ не установлен. Доступ открыт всем!', + show_key: 'Показать', + hide_key: 'Скрыть', + }, + mapping: { + title: 'Маппинг моделей', + description: 'Настройка маршрутизации моделей.', + maps_to: '->', + restore: 'Сброс', + }, + examples: { + title: 'Примеры', + description: 'Примеры использования локального прокси.', + }, + }, + }, + }, }, }); diff --git a/src/routes/settings.tsx b/src/routes/settings.tsx index 6c66c29..d1846ac 100644 --- a/src/routes/settings.tsx +++ b/src/routes/settings.tsx @@ -125,6 +125,7 @@ function SettingsPage() { {t('settings.language.english')} {t('settings.language.chinese')} + {t('settings.language.russian')}