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')}