diff --git a/src/main/utils/locales.ts b/src/main/utils/locales.ts index d927f25211..2ab840aa14 100644 --- a/src/main/utils/locales.ts +++ b/src/main/utils/locales.ts @@ -1,11 +1,13 @@ import EnUs from '../../renderer/src/i18n/locales/en-us.json' +import RuRu from '../../renderer/src/i18n/locales/ru-ru.json' import ZhCn from '../../renderer/src/i18n/locales/zh-cn.json' import ZhTw from '../../renderer/src/i18n/locales/zh-tw.json' const locales = { 'en-US': EnUs, 'zh-CN': ZhCn, - 'zh-TW': ZhTw + 'zh-TW': ZhTw, + 'ru-RU': RuRu } export { locales } diff --git a/src/renderer/src/context/AntdProvider.tsx b/src/renderer/src/context/AntdProvider.tsx index 22c14f520c..a9266bb906 100644 --- a/src/renderer/src/context/AntdProvider.tsx +++ b/src/renderer/src/context/AntdProvider.tsx @@ -1,6 +1,10 @@ import { useSettings } from '@renderer/hooks/useSettings' +import { LanguageVarious } from '@renderer/types' import { ConfigProvider, theme } from 'antd' +import enUS from 'antd/locale/en_US' +import ruRU from 'antd/locale/ru_RU' import zhCN from 'antd/locale/zh_CN' +import zhTW from 'antd/locale/zh_TW' import { FC, PropsWithChildren } from 'react' import { useTheme } from './ThemeProvider' @@ -38,12 +42,17 @@ const AntdProvider: FC = ({ children }) => { ) } -function getAntdLocale(language: string) { +function getAntdLocale(language: LanguageVarious) { switch (language) { case 'zh-CN': return zhCN + case 'zh-TW': + return zhTW case 'en-US': - return undefined + return enUS + case 'ru-RU': + return ruRU + default: return zhCN } diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index 3430cb6501..13b49e9a4d 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -2,13 +2,15 @@ import i18n from 'i18next' import { initReactI18next } from 'react-i18next' import enUS from './locales/en-us.json' +import ruRU from './locales/ru-ru.json' import zhCN from './locales/zh-cn.json' import zhTW from './locales/zh-tw.json' const resources = { 'en-US': enUS, 'zh-CN': zhCN, - 'zh-TW': zhTW + 'zh-TW': zhTW, + 'ru-RU': ruRU } i18n.use(initReactI18next).init({ diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json new file mode 100644 index 0000000000..e42526c7a4 --- /dev/null +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -0,0 +1,449 @@ +{ + "translation": { + "common": { + "avatar": "Аватар", + "language": "Язык", + "model": "Модель", + "models": "Модели", + "topics": "Топики", + "docs": "Документы", + "and": "и", + "assistant": "Ассистент", + "name": "Имя", + "description": "Описание", + "prompt": "Промпт", + "rename": "Переименовать", + "delete": "Удалить", + "edit": "Редактировать", + "duplicate": "Дублировать", + "copy": "Копировать", + "paste": "Вставить", + "cut": "Вырезать", + "regenerate": "Пересоздать", + "provider": "Провайдер", + "you": "Вы", + "save": "Сохранить", + "footnotes": "Сноски", + "select": "Выбрать", + "search": "Поиск", + "default": "По умолчанию", + "warning": "Предупреждение", + "back": "Назад", + "chat": "Чат", + "close": "Закрыть", + "cancel": "Отмена", + "download": "Скачать" + }, + "button": { + "add": "Добавить", + "added": "Добавлено", + "manage": "Редактировать", + "select_model": "Выбрать модель", + "show.all": "Показать все", + "collapse": "Свернуть" + }, + "message": { + "copied": "Скопировано!", + "assistant.added.content": "Ассистент успешно добавлен", + "message.delete.title": "Удалить сообщение", + "message.delete.content": "Вы уверены, что хотите удалить это сообщение?", + "error.enter.api.key": "Пожалуйста, введите ваш API ключ", + "error.enter.api.host": "Пожалуйста, введите ваш API хост", + "error.enter.model": "Пожалуйста, выберите модель", + "error.invalid.proxy.url": "Неверный URL прокси", + "error.invalid.webdav": "Неверные настройки WebDAV", + "api.connection.failed": "Соединение не удалось", + "api.connection.success": "Соединение успешно", + "chat.completion.paused": "Завершение чата приостановлено", + "switch.disabled": "Переключение отключено, пока ассистент генерирует", + "restore.success": "Успешно восстановлено", + "backup.success": "Резервная копия успешно создана", + "backup.failed": "Создание резервной копии не удалось", + "reset.confirm.content": "Вы уверены, что хотите очистить все данные?", + "reset.double.confirm.title": "ДАННЫЕ БУДУТ УТЕРЯНЫ !!!", + "reset.double.confirm.content": "Все данные будут утеряны, хотите продолжить?", + "upgrade.success.title": "Обновление успешно", + "upgrade.success.content": "Пожалуйста, перезапустите приложение для завершения обновления", + "upgrade.success.button": "Перезапустить", + "topic.added": "Новый топик добавлен", + "save.success.title": "Успешно сохранено", + "message.code_style": "Стиль кода", + "message.style": "Стиль сообщения", + "message.style.bubble": "Пузырь", + "message.style.plain": "Простой" + }, + "chat": { + "save": "Сохранить", + "default.name": "⭐️ Ассистент по умолчанию", + "default.description": "Привет, я Ассистент по умолчанию. Вы можете начать общаться со мной прямо сейчас", + "default.topic.name": "Топик по умолчанию", + "topics.title": "Топики", + "topics.auto_rename": "Автопереименование", + "topics.edit.title": "Редактировать заголовок", + "topics.edit.placeholder": "Введите новый заголовок", + "topics.clear.title": "Очистить сообщения", + "topics.move_to": "Переместить в", + "topics.list": "Список топиков", + "topics.export.title": "Экспорт", + "topics.export.image": "Экспорт как изображение", + "topics.export.md": "Экспорт как markdown", + "topics.export.word": "Экспорт как Word", + "input.new_topic": "Новый топик", + "input.topics": " Топики ", + "input.clear": "Очистить", + "input.new.context": "Очистить контекст", + "input.expand": "Развернуть", + "input.collapse": "Свернуть", + "input.clear.title": "Очистить все сообщения?", + "input.clear.content": "Хотите очистить все сообщения текущего топика?", + "input.placeholder": "Введите ваше сообщение здесь...", + "input.send": "Отправить", + "input.pause": "Остановить", + "input.settings": "Настройки", + "input.upload": "Загрузить изображение или документ", + "input.context_count.tip": "Количество контекстов", + "input.estimated_tokens.tip": "Затраты токенов", + "settings.temperature": "Температура", + "settings.temperature.tip": "Меньшие значения делают модель более креативной и непредсказуемой, в то время как большие значения делают её более детерминированной и точной.", + "settings.context_count": "Контекст", + "settings.context_count.tip": "Количество предыдущих сообщений, которые нужно сохранить в контексте.", + "settings.max_tokens": "Включить лимит максимальных токенов", + "settings.max_tokens.tip": "Максимальное количество токенов, которые может сгенерировать модель. Обычный чат предполагает 500-800. Генерация короткого текста предполагает 800-2000. Генерация кода предполагает 2000-3600. Генерация длинного текста предполагает выше 4000.", + "settings.reset": "Сбросить", + "settings.set_as_default": "Применить к ассистенту по умолчанию", + "settings.max": "Максимум", + "settings.show_line_numbers": "Показать номера строк в коде", + "suggestions.title": "Предложенные вопросы", + "add.assistant.title": "Добавить ассистента", + "message.new.context": "Новый контекст", + "message.new.branch": "Новая ветка", + "message.new.branch.created": "Новая ветка создана", + "assistant.search.placeholder": "Поиск", + "artifacts.button.preview": "Предпросмотр", + "artifacts.button.download": "Скачать" + }, + "assistants": { + "title": "Ассистенты", + "abbr": "Ассистент", + "search": "Поиск ассистентов...", + "settings.prompt": "Настройки промптов", + "settings.model": "Настройки модели", + "settings.preset_messages": "Предустановленные сообщения", + "settings.default_model": "Модель по умолчанию", + "settings.auto_reset_model": "Автосброс модели", + "settings.auto_reset_model.tip": "Автоматически сбрасывать модель при создании нового топика.", + "edit.title": "Редактировать ассистента", + "copy.title": "Копировать ассистента", + "clear.title": "Очистить топики", + "clear.content": "Очистка топика удалит все топики и файлы в ассистенте. Вы уверены, что хотите продолжить?", + "save.title": "Сохранить в агента", + "save.success": "Успешно сохранено", + "delete.title": "Удалить ассистента", + "delete.content": "Удаление ассистента удалит все топики и файлы под ассистентом. Вы уверены, что хотите удалить его?" + }, + "model": { + "stream_output": "Потоковый вывод", + "search": "Поиск моделей...", + "pinned": "Закреплено" + }, + "paintings": { + "title": "Изображения", + "image.size": "Размер изображения", + "button.new.image": "Новое изображение", + "button.delete.image": "Удалить изображение", + "button.delete.image.confirm": "Вы уверены, что хотите удалить это изображение?", + "number_images": "Количество изображений", + "number_images_tip": "Количество изображений для генерации (1-4)", + "seed": "Ключ генерации", + "seed_tip": "Одинаковый ключ генерации и промпт могут производить похожие изображения", + "inference_steps": "Шаги вывода", + "inference_steps_tip": "Количество шагов вывода для выполнения. Больше шагов производят более высокое качество, но занимают больше времени", + "guidance_scale": "Масштаб руководства", + "guidance_scale_tip": "Без классификатора руководства. Насколько близко вы хотите, чтобы модель придерживалась вашего промпта при поиске связанного изображения для показа вам", + "negative_prompt": "Негативный промпт", + "negative_prompt_tip": "Опишите, что вы не хотите включать в изображение", + "prompt_placeholder": "Опишите изображение, которое вы хотите создать, например, Спокойное озеро на закате с горами на заднем плане", + "regenerate.confirm": "Это заменит ваши существующие сгенерированные изображения. Хотите продолжить?" + }, + "files": { + "title": "Файлы", + "file": "Файл", + "name": "Имя", + "size": "Размер", + "count": "Количество", + "created_at": "Дата создания", + "image": "Изображение", + "text": "Текст", + "document": "Документ", + "actions": "Действия", + "open": "Открыть", + "all": "Все файлы" + }, + "agents": { + "title": "Агенты", + "my_agents": "Мои агенты", + "add.title": "Создать агента", + "edit.title": "Редактировать агента", + "add.name": "Имя", + "add.name.placeholder": "Введите имя", + "add.prompt": "Промпт", + "add.prompt.placeholder": "Введите промпт", + "add.button": "Добавить к ассистенту", + "manage.title": "Редактировать агентов", + "delete.popup.content": "Вы уверены, что хотите удалить этого агента?", + "tag.default": "По умолчанию", + "tag.system": "Система", + "tag.agent": "Агент", + "edit.message.title": "Предустановленные сообщения", + "edit.message.add.title": "Добавить", + "edit.message.group.title": "Группа сообщений", + "edit.message.assistant.title": "Ассистент", + "edit.message.assistant.placeholder": "Введите сообщение ассистента", + "edit.message.user.title": "Пользователь", + "edit.message.user.placeholder": "Введите сообщение пользователя", + "edit.message.empty.content": "Содержание вводимого сообщения не может быть пустым", + "edit.model.select.title": "Выбрать модель", + "edit.settings.hide_preset_messages": "Скрыть предустановленные сообщения", + "search.no_results": "Результаты не найдены", + "sorting.title": "Сортировка" + }, + "minapp": { + "title": "Встроенные приложения" + }, + "history": { + "title": "Поиск топиков", + "search.placeholder": "Поиск топиков или сообщений...", + "continue_chat": "Продолжить чат", + "search.topics.empty": "Топики не найдены, нажмите Enter для поиска всех сообщений", + "search.messages": "Поиск всех сообщений", + "locate.message": "Найти сообщение" + }, + "provider": { + "jina": "Jina", + "mistral": "Mistral", + "hyperbolic": "Hyperbolic", + "grok": "Grok", + "nvidia": "Nvidia", + "hunyuan": "Tencent Hunyuan", + "zhinao": "360AI", + "fireworks": "Fireworks", + "together": "Together", + "openai": "OpenAI", + "gemini": "Gemini", + "deepseek": "DeepSeek", + "moonshot": "Moonshot", + "silicon": "SiliconFlow", + "openrouter": "OpenRouter", + "yi": "Yi", + "zhipu": "ZHIPU AI", + "groq": "Groq", + "ollama": "Ollama", + "baichuan": "Baichuan", + "dashscope": "Alibaba Cloud", + "anthropic": "Anthropic", + "aihubmix": "AiHubMix", + "stepfun": "StepFun", + "doubao": "Doubao", + "minimax": "MiniMax", + "graphrag-kylin-mountain": "GraphRAG", + "github": "GitHub Models", + "ocoolai": "ocoolAI", + "azure-openai": "Azure OpenAI" + }, + "settings": { + "title": "Настройки", + "general": "Общие настройки", + "data": "Настройки данных", + "model": "Модель по умолчанию", + "assistant": "Ассистент по умолчанию", + "about": "О программе и обратная связь", + "messages.model.title": "Настройки модели", + "messages.title": "Настройки сообщений", + "messages.divider": "Показывать разделитель между сообщениями", + "messages.use_serif_font": "Использовать serif шрифт", + "messages.input.title": "Настройки ввода", + "messages.input.show_estimated_tokens": "Показывать затраты токенов", + "messages.input.send_shortcuts": "Горячие клавиши для отправки", + "messages.input.paste_long_text_as_file": "Вставлять длинный текст как файл", + "messages.markdown_rendering_input_message": "Отображение ввода в формате Markdown", + "messages.math_engine": "Математический движок", + "general.title": "Общие настройки", + "general.user_name": "Имя пользователя", + "general.user_name.placeholder": "Введите ваше имя", + "general.backup.title": "Резервное копирование и восстановление данных", + "general.backup.button": "Резервное копирование", + "general.restore.button": "Восстановление", + "general.view_webdav_settings": "Просмотр настроек WebDAV", + "general.reset.title": "Сброс данных", + "general.reset.button": "Сброс", + "general.manually_check_update.title": "Отключить проверку обновлений", + "data.webdav.title": "WebDAV", + "data.webdav.host": "Хост WebDAV", + "data.webdav.host.placeholder": "http://localhost:8080", + "data.webdav.user": "Пользователь WebDAV", + "data.webdav.password": "Пароль WebDAV", + "data.webdav.path": "Путь WebDAV", + "data.webdav.path.placeholder": "/backup", + "data.webdav.backup.button": "Резервное копирование на WebDAV", + "data.webdav.restore.button": "Восстановление с WebDAV", + "advanced.title": "Расширенные настройки", + "advanced.click_assistant_switch_to_topics": "Автоматически переключаться на топик", + "provider.api_key": "Ключ API", + "provider.api_key.tip": "Несколько ключей, разделенных запятыми", + "provider.check": "Проверить", + "provider.get_api_key": "Получить ключ API", + "provider.api_host": "Хост API", + "provider.api_version": "Версия API", + "provider.docs_check": "Проверить", + "provider.docs_more_details": "для получения дополнительной информации", + "provider.search_placeholder": "Поиск по ID или имени модели", + "provider.api.url.reset": "Сброс", + "provider.api.url.preview": "Предпросмотр: {{url}}", + "provider.api.url.tip": "Заканчивая на / игнорирует v1, заканчивая на # принудительно использует введенный адрес", + "models.default_assistant_model": "Модель ассистента по умолчанию", + "models.topic_naming_model": "Модель именования топика", + "models.translate_model": "Модель перевода", + "models.add.add_model": "Добавить модель", + "models.add.model_id.placeholder": "Обязательно, например, gpt-3.5-turbo", + "models.add.model_id": "ID модели", + "models.add.model_id.tooltip": "Пример: gpt-3.5-turbo", + "models.add.model_name": "Имя модели", + "models.add.model_name.placeholder": "Необязательно, например, GPT-4", + "models.add.group_name": "Имя группы", + "models.add.group_name.tooltip": "Необязательно, например, ChatGPT", + "models.add.group_name.placeholder": "Необязательно, например, ChatGPT", + "models.empty": "Модели не найдены", + "assistant.title": "Ассистент по умолчанию", + "assistant.model_params": "Параметры модели", + "about.description": "Мощный AI-ассистент для созидания", + "about.updateNotAvailable": "Вы используете последнюю версию", + "about.checkingUpdate": "Проверка обновлений...", + "about.updateError": "Ошибка обновления", + "about.checkUpdate": "Проверить обновления", + "about.downloading": "Загрузка...", + "about.title": "О программе", + "about.releases.title": "Заметки о релизах", + "about.releases.button": "Релизы", + "about.website.title": "Официальный сайт", + "about.website.button": "Сайт", + "about.feedback.title": "Обратная связь", + "about.feedback.button": "Обратная связь", + "about.contact.title": "Контакты", + "about.license.title": "Лицензия", + "about.license.button": "Лицензия", + "about.contact.button": "Электронная почта", + "proxy.title": "Адрес прокси", + "tray.title": "Включить значок системного трея", + "theme.title": "Тема", + "theme.dark": "Темная", + "theme.light": "Светлая", + "theme.auto": "Автоматически", + "theme.window.style.title": "Стиль окна", + "theme.window.style.transparent": "Прозрачное окно", + "theme.window.style.opaque": "Непрозрачное окно", + "font_size.title": "Размер шрифта сообщений", + "topic.position": "Позиция топиков", + "topic.position.left": "Слева", + "topic.position.right": "Справа", + "topic.show.time": "Показывать время топика", + "display.title": "Настройки отображения", + "shortcuts": { + "title": "Горячие клавиши", + "action": "Действие", + "key": "Клавиша", + "new_topic": "Новый топик", + "zoom_in": "Увеличить", + "zoom_out": "Уменьшить", + "zoom_reset": "Сбросить масштаб" + }, + "provider": { + "title": "Провайдеры моделей", + "api_key": "Ключ API", + "api_key.tip": "Несколько ключей, разделенных запятыми", + "check": "Проверить", + "get_api_key": "Получить ключ API", + "api_host": "Хост API", + "api_version": "Версия API", + "docs_check": "Проверить", + "docs_more_details": "для получения дополнительной информации", + "search_placeholder": "Поиск по ID или имени модели", + "api.url.reset": "Сброс", + "api.url.preview": "Предпросмотр: {{url}}", + "api.url.tip": "Заканчивая на / игнорирует v1, заканчивая на # принудительно использует введенный адрес", + "check_multiple_keys": "Проверить несколько ключей API", + "check_all_keys": "Проверить все ключи", + "remove_invalid_keys": "Удалить недействительные ключи", + "remove_duplicate_keys": "Удалить дубликаты ключей", + "not_checked": "Не проверено", + "delete.title": "Удалить провайдер", + "delete.content": "Вы уверены, что хотите удалить этот провайдер?", + "edit.name": "Имя провайдера", + "edit.name.placeholder": "Пример: OpenAI", + "no_models": "Пожалуйста, добавьте модели перед проверкой соединения с API" + } + }, + "translate": { + "title": "Перевод", + "any.language": "Любой язык", + "button.translate": "Перевести", + "error.not_configured": "Модель перевода не настроена", + "input.placeholder": "Введите текст для перевода", + "output.placeholder": "Перевод", + "confirm": "Исходный текст скопирован в буфер обмена. Хотите заменить его переведенным текстом?" + }, + "languages": { + "english": "Английский", + "chinese": "Китайский", + "chinese-traditional": "Китайский традиционный", + "japanese": "Японский", + "korean": "Корейский", + "russian": "Русский", + "spanish": "Испанский", + "french": "Французский", + "italian": "Итальянский", + "portuguese": "Португальский", + "arabic": "Арабский" + }, + "ollama": { + "title": "Ollama", + "keep_alive_time.title": "Время жизни модели", + "keep_alive_time.placeholder": "Минуты", + "keep_alive_time.description": "Время в минутах, в течение которого модель остается активной, по умолчанию 5 минут." + }, + "error": { + "chat.response": "Что-то пошло не так. Пожалуйста, проверьте, установлен ли ваш ключ API в Настройки > Провайдеры", + "backup.file_format": "Ошибка формата файла резервной копии", + "provider_disabled": "Провайдер моделей не включен", + "no_api_key": "Ключ API не настроен" + }, + "words": { + "knowledgeGraph": "Граф знаний", + "visualization": "Визуализация" + }, + "export": { + "attached_files": "Прикрепленные файлы", + "user": "Пользователь", + "assistant": "Ассистент", + "created": "Создано", + "last_updated": "Последнее обновление", + "messages": "Сообщения", + "conversation_details": "Детали разговора", + "conversation_history": "История разговора" + }, + "mermaid": { + "title": "Диаграмма Mermaid", + "download": { + "svg": "Скачать SVG", + "png": "Скачать PNG" + }, + "tabs": { + "preview": "Предпросмотр", + "source": "Исходный код" + } + }, + "tray": { + "show_window": "Показать окно", + "quit": "Выйти" + } + } +} diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index f22baa1e59..a61142dced 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -6,7 +6,7 @@ import { setLanguage } from '@renderer/store/settings' import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings' import { LanguageVarious, ThemeMode } from '@renderer/types' import { isValidProxyUrl } from '@renderer/utils' -import { Input, Select, Switch } from 'antd' +import { Input, Select, Space, Switch } from 'antd' import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -50,22 +50,31 @@ const GeneralSettings: FC = () => { window.api.setProxy(proxyUrl) } + const languagesOptions: { value: LanguageVarious; label: string; flag: string }[] = [ + { value: 'zh-CN', label: '中文', flag: '🇨🇳' }, + { value: 'zh-TW', label: '中文(繁体)', flag: '🇹🇼' }, + { value: 'en-US', label: 'English', flag: '🇺🇸' }, + { value: 'ru-RU', label: 'Russian', flag: '🇷🇺' } + ] + return ( {t('settings.general.title')} {t('common.language')} - + {languagesOptions.map((lang) => ( + + + {lang.label} + + {lang.flag} + + + + ))} + diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index b055d4990a..1a85e6e227 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -137,7 +137,7 @@ export enum ThemeMode { dark = 'dark', auto = 'auto' } -export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' +export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' | 'ru-RU' export type CodeStyleVarious = BuiltinTheme | 'auto' export type WebDavConfig = {