feat(i18n): add Russian localization (#48)
* feat(i18n): add Russian localization * fix: missing i18n keys --------- Co-authored-by: Violetta <[email protected]> Co-authored-by: Draculabo <[email protected]>
This commit is contained in:
3 files changed
+324
-2
No files matched your search
@@ -12,6 +12,7 @@ import {
|
|||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { MoreVertical, Trash2, RefreshCw } from 'lucide-react';
|
import { MoreVertical, Trash2, RefreshCw } from 'lucide-react';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
|
import { enUS, zhCN, ru } from 'date-fns/locale';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ export const AccountCard: React.FC<AccountCardProps> = ({
|
|||||||
isSwitching,
|
isSwitching,
|
||||||
isDeleting,
|
isDeleting,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const cardRef = useRef<HTMLDivElement>(null);
|
const cardRef = useRef<HTMLDivElement>(null);
|
||||||
const [transform, setTransform] = useState('');
|
const [transform, setTransform] = useState('');
|
||||||
const [glarePosition, setGlarePosition] = useState({ x: 50, y: 50 });
|
const [glarePosition, setGlarePosition] = useState({ x: 50, y: 50 });
|
||||||
@@ -136,6 +137,7 @@ export const AccountCard: React.FC<AccountCardProps> = ({
|
|||||||
{t('account.lastUsed', {
|
{t('account.lastUsed', {
|
||||||
time: formatDistanceToNow(new Date(account.last_used), {
|
time: formatDistanceToNow(new Date(account.last_used), {
|
||||||
addSuffix: true,
|
addSuffix: true,
|
||||||
|
locale: { en: enUS, 'zh-CN': zhCN, ru: ru }[i18n.language] || enUS,
|
||||||
}),
|
}),
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
+320
-1
@@ -12,7 +12,7 @@ i18n
|
|||||||
caches: ['localStorage'],
|
caches: ['localStorage'],
|
||||||
lookupLocalStorage: 'lang',
|
lookupLocalStorage: 'lang',
|
||||||
},
|
},
|
||||||
supportedLngs: ['en', 'zh-CN'],
|
supportedLngs: ['en', 'zh-CN', 'ru'],
|
||||||
load: 'currentOnly', // Only load the exact language code, not variants
|
load: 'currentOnly', // Only load the exact language code, not variants
|
||||||
resources: {
|
resources: {
|
||||||
en: {
|
en: {
|
||||||
@@ -83,6 +83,7 @@ i18n
|
|||||||
description: 'Select your preferred language.',
|
description: 'Select your preferred language.',
|
||||||
english: 'English',
|
english: 'English',
|
||||||
chinese: 'Chinese (Simplified)',
|
chinese: 'Chinese (Simplified)',
|
||||||
|
russian: 'Russian',
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
title: 'About',
|
title: 'About',
|
||||||
@@ -311,6 +312,10 @@ i18n
|
|||||||
api_key: 'API Key',
|
api_key: 'API Key',
|
||||||
auto_start: 'Auto Start with App',
|
auto_start: 'Auto Start with App',
|
||||||
auto_start_desc: 'Start proxy service when application launches',
|
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: {
|
mapping: {
|
||||||
title: 'Model Mapping',
|
title: 'Model Mapping',
|
||||||
@@ -393,6 +398,7 @@ i18n
|
|||||||
description: '选择您的首选语言。',
|
description: '选择您的首选语言。',
|
||||||
english: '英语',
|
english: '英语',
|
||||||
chinese: '中文 (简体)',
|
chinese: '中文 (简体)',
|
||||||
|
russian: '俄语',
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
title: '关于',
|
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: 'Примеры использования локального прокси.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -125,6 +125,7 @@ function SettingsPage() {
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="en">{t('settings.language.english')}</SelectItem>
|
<SelectItem value="en">{t('settings.language.english')}</SelectItem>
|
||||||
<SelectItem value="zh-CN">{t('settings.language.chinese')}</SelectItem>
|
<SelectItem value="zh-CN">{t('settings.language.chinese')}</SelectItem>
|
||||||
|
<SelectItem value="ru">{t('settings.language.russian')}</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user