Parcourir la source

feat(i18n): add Russian language support (#9882)

Alex Yaroshuk il y a 1 mois
Parent
commit
383c2787f9

+ 9 - 3
packages/app/src/context/language.tsx

@@ -11,6 +11,8 @@ import { dict as es } from "@/i18n/es"
 import { dict as fr } from "@/i18n/fr"
 import { dict as fr } from "@/i18n/fr"
 import { dict as da } from "@/i18n/da"
 import { dict as da } from "@/i18n/da"
 import { dict as ja } from "@/i18n/ja"
 import { dict as ja } from "@/i18n/ja"
+import { dict as pl } from "@/i18n/pl"
+import { dict as ru } from "@/i18n/ru"
 import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
 import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
 import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
 import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
 import { dict as uiKo } from "@opencode-ai/ui/i18n/ko"
 import { dict as uiKo } from "@opencode-ai/ui/i18n/ko"
@@ -19,15 +21,15 @@ import { dict as uiEs } from "@opencode-ai/ui/i18n/es"
 import { dict as uiFr } from "@opencode-ai/ui/i18n/fr"
 import { dict as uiFr } from "@opencode-ai/ui/i18n/fr"
 import { dict as uiDa } from "@opencode-ai/ui/i18n/da"
 import { dict as uiDa } from "@opencode-ai/ui/i18n/da"
 import { dict as uiJa } from "@opencode-ai/ui/i18n/ja"
 import { dict as uiJa } from "@opencode-ai/ui/i18n/ja"
-import { dict as pl } from "@/i18n/pl"
 import { dict as uiPl } from "@opencode-ai/ui/i18n/pl"
 import { dict as uiPl } from "@opencode-ai/ui/i18n/pl"
+import { dict as uiRu } from "@opencode-ai/ui/i18n/ru"
 
 
-export type Locale = "en" | "zh" | "ko" | "de" | "es" | "fr" | "da" | "ja" | "pl"
+export type Locale = "en" | "zh" | "ko" | "de" | "es" | "fr" | "da" | "ja" | "pl" | "ru"
 
 
 type RawDictionary = typeof en & typeof uiEn
 type RawDictionary = typeof en & typeof uiEn
 type Dictionary = i18n.Flatten<RawDictionary>
 type Dictionary = i18n.Flatten<RawDictionary>
 
 
-const LOCALES: readonly Locale[] = ["en", "zh", "ko", "de", "es", "fr", "da", "ja", "pl"]
+const LOCALES: readonly Locale[] = ["en", "zh", "ko", "de", "es", "fr", "da", "ja", "pl", "ru"]
 
 
 function detectLocale(): Locale {
 function detectLocale(): Locale {
   if (typeof navigator !== "object") return "en"
   if (typeof navigator !== "object") return "en"
@@ -43,6 +45,7 @@ function detectLocale(): Locale {
     if (language.toLowerCase().startsWith("da")) return "da"
     if (language.toLowerCase().startsWith("da")) return "da"
     if (language.toLowerCase().startsWith("ja")) return "ja"
     if (language.toLowerCase().startsWith("ja")) return "ja"
     if (language.toLowerCase().startsWith("pl")) return "pl"
     if (language.toLowerCase().startsWith("pl")) return "pl"
+    if (language.toLowerCase().startsWith("ru")) return "ru"
   }
   }
 
 
   return "en"
   return "en"
@@ -67,6 +70,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       if (store.locale === "da") return "da"
       if (store.locale === "da") return "da"
       if (store.locale === "ja") return "ja"
       if (store.locale === "ja") return "ja"
       if (store.locale === "pl") return "pl"
       if (store.locale === "pl") return "pl"
+      if (store.locale === "ru") return "ru"
       return "en"
       return "en"
     })
     })
 
 
@@ -86,6 +90,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       if (locale() === "da") return { ...base, ...i18n.flatten({ ...da, ...uiDa }) }
       if (locale() === "da") return { ...base, ...i18n.flatten({ ...da, ...uiDa }) }
       if (locale() === "ja") return { ...base, ...i18n.flatten({ ...ja, ...uiJa }) }
       if (locale() === "ja") return { ...base, ...i18n.flatten({ ...ja, ...uiJa }) }
       if (locale() === "pl") return { ...base, ...i18n.flatten({ ...pl, ...uiPl }) }
       if (locale() === "pl") return { ...base, ...i18n.flatten({ ...pl, ...uiPl }) }
+      if (locale() === "ru") return { ...base, ...i18n.flatten({ ...ru, ...uiRu }) }
       return { ...base, ...i18n.flatten({ ...ko, ...uiKo }) }
       return { ...base, ...i18n.flatten({ ...ko, ...uiKo }) }
     })
     })
 
 
@@ -101,6 +106,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       da: "language.da",
       da: "language.da",
       ja: "language.ja",
       ja: "language.ja",
       pl: "language.pl",
       pl: "language.pl",
+      ru: "language.ru",
     }
     }
 
 
     const label = (value: Locale) => t(labelKey[value])
     const label = (value: Locale) => t(labelKey[value])

+ 1 - 0
packages/app/src/i18n/da.ts

@@ -276,6 +276,7 @@ export const dict = {
   "language.fr": "Fransk",
   "language.fr": "Fransk",
   "language.ja": "Japansk",
   "language.ja": "Japansk",
   "language.da": "Dansk",
   "language.da": "Dansk",
+  "language.ru": "Russisk",
   "language.pl": "Polsk",
   "language.pl": "Polsk",
 
 
   "toast.language.title": "Sprog",
   "toast.language.title": "Sprog",

+ 1 - 0
packages/app/src/i18n/de.ts

@@ -281,6 +281,7 @@ export const dict = {
   "language.fr": "Französisch",
   "language.fr": "Französisch",
   "language.ja": "Japanisch",
   "language.ja": "Japanisch",
   "language.da": "Dänisch",
   "language.da": "Dänisch",
+  "language.ru": "Russisch",
   "language.pl": "Polnisch",
   "language.pl": "Polnisch",
 
 
   "toast.language.title": "Sprache",
   "toast.language.title": "Sprache",

+ 1 - 0
packages/app/src/i18n/en.ts

@@ -293,6 +293,7 @@ export const dict = {
   "language.fr": "French",
   "language.fr": "French",
   "language.ja": "Japanese",
   "language.ja": "Japanese",
   "language.da": "Danish",
   "language.da": "Danish",
+  "language.ru": "Russian",
   "language.pl": "Polish",
   "language.pl": "Polish",
 
 
   "toast.language.title": "Language",
   "toast.language.title": "Language",

+ 1 - 0
packages/app/src/i18n/es.ts

@@ -276,6 +276,7 @@ export const dict = {
   "language.fr": "Francés",
   "language.fr": "Francés",
   "language.ja": "Japonés",
   "language.ja": "Japonés",
   "language.da": "Danés",
   "language.da": "Danés",
+  "language.ru": "Ruso",
   "language.pl": "Polaco",
   "language.pl": "Polaco",
 
 
   "toast.language.title": "Idioma",
   "toast.language.title": "Idioma",

+ 1 - 0
packages/app/src/i18n/fr.ts

@@ -276,6 +276,7 @@ export const dict = {
   "language.fr": "Français",
   "language.fr": "Français",
   "language.ja": "Japonais",
   "language.ja": "Japonais",
   "language.da": "Danois",
   "language.da": "Danois",
+  "language.ru": "Russe",
   "language.pl": "Polonais",
   "language.pl": "Polonais",
 
 
   "toast.language.title": "Langue",
   "toast.language.title": "Langue",

+ 1 - 0
packages/app/src/i18n/ja.ts

@@ -274,6 +274,7 @@ export const dict = {
   "language.fr": "フランス語",
   "language.fr": "フランス語",
   "language.ja": "日本語",
   "language.ja": "日本語",
   "language.da": "デンマーク語",
   "language.da": "デンマーク語",
+  "language.ru": "ロシア語",
   "language.pl": "ポーランド語",
   "language.pl": "ポーランド語",
 
 
   "toast.language.title": "言語",
   "toast.language.title": "言語",

+ 1 - 0
packages/app/src/i18n/ko.ts

@@ -278,6 +278,7 @@ export const dict = {
   "language.fr": "프랑스어",
   "language.fr": "프랑스어",
   "language.ja": "일본어",
   "language.ja": "일본어",
   "language.da": "덴마크어",
   "language.da": "덴마크어",
+  "language.ru": "러시아어",
   "language.pl": "폴란드어",
   "language.pl": "폴란드어",
 
 
   "toast.language.title": "언어",
   "toast.language.title": "언어",

+ 642 - 0
packages/app/src/i18n/ru.ts

@@ -0,0 +1,642 @@
+export const dict = {
+  "command.category.suggested": "Предложено",
+  "command.category.view": "Просмотр",
+  "command.category.project": "Проект",
+  "command.category.provider": "Провайдер",
+  "command.category.server": "Сервер",
+  "command.category.session": "Сессия",
+  "command.category.theme": "Тема",
+  "command.category.language": "Язык",
+  "command.category.file": "Файл",
+  "command.category.terminal": "Терминал",
+  "command.category.model": "Модель",
+  "command.category.mcp": "MCP",
+  "command.category.agent": "Агент",
+  "command.category.permissions": "Разрешения",
+  "command.category.workspace": "Рабочее пространство",
+  "command.category.settings": "Настройки",
+
+  "theme.scheme.system": "Системная",
+  "theme.scheme.light": "Светлая",
+  "theme.scheme.dark": "Тёмная",
+
+  "command.sidebar.toggle": "Переключить боковую панель",
+  "command.project.open": "Открыть проект",
+  "command.provider.connect": "Подключить провайдера",
+  "command.server.switch": "Переключить сервер",
+  "command.settings.open": "Открыть настройки",
+  "command.session.previous": "Предыдущая сессия",
+  "command.session.next": "Следующая сессия",
+  "command.session.archive": "Архивировать сессию",
+
+  "command.palette": "Палитра команд",
+
+  "command.theme.cycle": "Цикл тем",
+  "command.theme.set": "Использовать тему: {{theme}}",
+  "command.theme.scheme.cycle": "Цикл цветовой схемы",
+  "command.theme.scheme.set": "Использовать цветовую схему: {{scheme}}",
+
+  "command.language.cycle": "Цикл языков",
+  "command.language.set": "Использовать язык: {{language}}",
+
+  "command.session.new": "Новая сессия",
+  "command.file.open": "Открыть файл",
+  "command.file.open.description": "Поиск файлов и команд",
+  "command.terminal.toggle": "Переключить терминал",
+  "command.review.toggle": "Переключить обзор",
+  "command.terminal.new": "Новый терминал",
+  "command.terminal.new.description": "Создать новую вкладку терминала",
+  "command.steps.toggle": "Переключить шаги",
+  "command.steps.toggle.description": "Показать или скрыть шаги для текущего сообщения",
+  "command.message.previous": "Предыдущее сообщение",
+  "command.message.previous.description": "Перейти к предыдущему сообщению пользователя",
+  "command.message.next": "Следующее сообщение",
+  "command.message.next.description": "Перейти к следующему сообщению пользователя",
+  "command.model.choose": "Выбрать модель",
+  "command.model.choose.description": "Выбрать другую модель",
+  "command.mcp.toggle": "Переключить MCP",
+  "command.mcp.toggle.description": "Переключить MCP",
+  "command.agent.cycle": "Цикл агентов",
+  "command.agent.cycle.description": "Переключиться к следующему агенту",
+  "command.agent.cycle.reverse": "Цикл агентов назад",
+  "command.agent.cycle.reverse.description": "Переключиться к предыдущему агенту",
+  "command.model.variant.cycle": "Цикл режимов мышления",
+  "command.model.variant.cycle.description": "Переключиться к следующему уровню усилий",
+  "command.permissions.autoaccept.enable": "Авто-принятие изменений",
+  "command.permissions.autoaccept.disable": "Прекратить авто-принятие изменений",
+  "command.session.undo": "Отменить",
+  "command.session.undo.description": "Отменить последнее сообщение",
+  "command.session.redo": "Повторить",
+  "command.session.redo.description": "Повторить отменённое сообщение",
+  "command.session.compact": "Сжать сессию",
+  "command.session.compact.description": "Сократить сессию для уменьшения размера контекста",
+  "command.session.fork": "Создать ответвление",
+  "command.session.fork.description": "Создать новую сессию из сообщения",
+  "command.session.share": "Поделиться сессией",
+  "command.session.share.description": "Поделиться сессией и скопировать URL в буфер обмена",
+  "command.session.unshare": "Отменить публикацию",
+  "command.session.unshare.description": "Прекратить публикацию сессии",
+
+  "palette.search.placeholder": "Поиск файлов и команд",
+  "palette.empty": "Ничего не найдено",
+  "palette.group.commands": "Команды",
+  "palette.group.files": "Файлы",
+
+  "dialog.provider.search.placeholder": "Поиск провайдеров",
+  "dialog.provider.empty": "Провайдеры не найдены",
+  "dialog.provider.group.popular": "Популярные",
+  "dialog.provider.group.other": "Другие",
+  "dialog.provider.tag.recommended": "Рекомендуемые",
+  "dialog.provider.anthropic.note": "Подключитесь с помощью Claude Pro/Max или API ключа",
+
+  "dialog.model.select.title": "Выбрать модель",
+  "dialog.model.search.placeholder": "Поиск моделей",
+  "dialog.model.empty": "Модели не найдены",
+  "dialog.model.manage": "Управление моделями",
+  "dialog.model.manage.description": "Настройте какие модели появляются в выборе модели",
+
+  "dialog.model.unpaid.freeModels.title": "Бесплатные модели от OpenCode",
+  "dialog.model.unpaid.addMore.title": "Добавьте больше моделей от популярных провайдеров",
+
+  "dialog.provider.viewAll": "Посмотреть всех провайдеров",
+
+  "provider.connect.title": "Подключить {{provider}}",
+  "provider.connect.title.anthropicProMax": "Войти с помощью Claude Pro/Max",
+  "provider.connect.selectMethod": "Выберите способ входа для {{provider}}.",
+  "provider.connect.method.apiKey": "API ключ",
+  "provider.connect.status.inProgress": "Авторизация...",
+  "provider.connect.status.waiting": "Ожидание авторизации...",
+  "provider.connect.status.failed": "Ошибка авторизации: {{error}}",
+  "provider.connect.apiKey.description":
+    "Введите ваш API ключ {{provider}} для подключения аккаунта и использования моделей {{provider}} в OpenCode.",
+  "provider.connect.apiKey.label": "{{provider}} API ключ",
+  "provider.connect.apiKey.placeholder": "API ключ",
+  "provider.connect.apiKey.required": "API ключ обязателен",
+  "provider.connect.opencodeZen.line1":
+    "OpenCode Zen даёт вам доступ к отобранным надёжным оптимизированным моделям для агентов программирования.",
+  "provider.connect.opencodeZen.line2":
+    "С одним API ключом вы получите доступ к таким моделям как Claude, GPT, Gemini, GLM и другим.",
+  "provider.connect.opencodeZen.visit.prefix": "Посетите ",
+  "provider.connect.opencodeZen.visit.link": "opencode.ai/zen",
+  "provider.connect.opencodeZen.visit.suffix": " чтобы получить ваш API ключ.",
+  "provider.connect.oauth.code.visit.prefix": "Посетите ",
+  "provider.connect.oauth.code.visit.link": "эту ссылку",
+  "provider.connect.oauth.code.visit.suffix":
+    " чтобы получить код авторизации для подключения аккаунта и использования моделей {{provider}} в OpenCode.",
+  "provider.connect.oauth.code.label": "{{method}} код авторизации",
+  "provider.connect.oauth.code.placeholder": "Код авторизации",
+  "provider.connect.oauth.code.required": "Код авторизации обязателен",
+  "provider.connect.oauth.code.invalid": "Неверный код авторизации",
+  "provider.connect.oauth.auto.visit.prefix": "Посетите ",
+  "provider.connect.oauth.auto.visit.link": "эту ссылку",
+  "provider.connect.oauth.auto.visit.suffix":
+    " и введите код ниже для подключения аккаунта и использования моделей {{provider}} в OpenCode.",
+  "provider.connect.oauth.auto.confirmationCode": "Код подтверждения",
+  "provider.connect.toast.connected.title": "{{provider}} подключён",
+  "provider.connect.toast.connected.description": "Модели {{provider}} теперь доступны.",
+
+  "model.tag.free": "Бесплатно",
+  "model.tag.latest": "Последняя",
+  "model.provider.anthropic": "Anthropic",
+  "model.provider.openai": "OpenAI",
+  "model.provider.google": "Google",
+  "model.provider.xai": "xAI",
+  "model.provider.meta": "Meta",
+  "model.input.text": "текст",
+  "model.input.image": "изображение",
+  "model.input.audio": "аудио",
+  "model.input.video": "видео",
+  "model.input.pdf": "pdf",
+  "model.tooltip.allows": "Разрешено: {{inputs}}",
+  "model.tooltip.reasoning.allowed": "Разрешает рассуждение",
+  "model.tooltip.reasoning.none": "Без рассуждения",
+  "model.tooltip.context": "Лимит контекста {{limit}}",
+
+  "common.search.placeholder": "Поиск",
+  "common.loading": "Загрузка",
+  "common.loading.ellipsis": "...",
+  "common.cancel": "Отмена",
+  "common.submit": "Отправить",
+  "common.save": "Сохранить",
+  "common.saving": "Сохранение...",
+  "common.default": "По умолчанию",
+  "common.attachment": "вложение",
+
+  "prompt.placeholder.shell": "Введите команду оболочки...",
+  "prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"',
+  "prompt.mode.shell": "Оболочка",
+  "prompt.mode.shell.exit": "esc для выхода",
+
+  "prompt.example.1": "Исправить TODO в коде",
+  "prompt.example.2": "Какой технологический стек этого проекта?",
+  "prompt.example.3": "Исправить сломанные тесты",
+  "prompt.example.4": "Объясни как работает аутентификация",
+  "prompt.example.5": "Найти и исправить уязвимости безопасности",
+  "prompt.example.6": "Добавить юнит-тесты для сервиса пользователя",
+  "prompt.example.7": "Рефакторить эту функцию для лучшей читаемости",
+  "prompt.example.8": "Что означает эта ошибка?",
+  "prompt.example.9": "Помоги мне отладить эту проблему",
+  "prompt.example.10": "Сгенерировать документацию API",
+  "prompt.example.11": "Оптимизировать запросы к базе данных",
+  "prompt.example.12": "Добавить валидацию ввода",
+  "prompt.example.13": "Создать новый компонент для...",
+  "prompt.example.14": "Как развернуть этот проект?",
+  "prompt.example.15": "Проверь мой код на лучшие практики",
+  "prompt.example.16": "Добавить обработку ошибок в эту функцию",
+  "prompt.example.17": "Объясни этот паттерн regex",
+  "prompt.example.18": "Конвертировать это в TypeScript",
+  "prompt.example.19": "Добавить логирование по всему проекту",
+  "prompt.example.20": "Какие зависимости устарели?",
+  "prompt.example.21": "Помоги написать скрипт миграции",
+  "prompt.example.22": "Реализовать кэширование для этой конечной точки",
+  "prompt.example.23": "Добавить пагинацию в этот список",
+  "prompt.example.24": "Создать CLI команду для...",
+  "prompt.example.25": "Как работают переменные окружения здесь?",
+
+  "prompt.popover.emptyResults": "Нет совпадений",
+  "prompt.popover.emptyCommands": "Нет совпадающих команд",
+  "prompt.dropzone.label": "Перетащите изображения или PDF сюда",
+  "prompt.slash.badge.custom": "своё",
+  "prompt.context.active": "активно",
+  "prompt.context.includeActiveFile": "Включить активный файл",
+  "prompt.action.attachFile": "Прикрепить файл",
+  "prompt.action.send": "Отправить",
+  "prompt.action.stop": "Остановить",
+
+  "prompt.toast.pasteUnsupported.title": "Неподдерживаемая вставка",
+  "prompt.toast.pasteUnsupported.description": "Сюда можно вставлять только изображения или PDF.",
+  "prompt.toast.modelAgentRequired.title": "Выберите агента и модель",
+  "prompt.toast.modelAgentRequired.description": "Выберите агента и модель перед отправкой запроса.",
+  "prompt.toast.worktreeCreateFailed.title": "Не удалось создать worktree",
+  "prompt.toast.sessionCreateFailed.title": "Не удалось создать сессию",
+  "prompt.toast.shellSendFailed.title": "Не удалось отправить команду оболочки",
+  "prompt.toast.commandSendFailed.title": "Не удалось отправить команду",
+  "prompt.toast.promptSendFailed.title": "Не удалось отправить запрос",
+
+  "dialog.mcp.title": "MCP",
+  "dialog.mcp.description": "{{enabled}} из {{total}} включено",
+  "dialog.mcp.empty": "MCP не настроены",
+
+  "mcp.status.connected": "подключено",
+  "mcp.status.failed": "ошибка",
+  "mcp.status.needs_auth": "требуется авторизация",
+  "mcp.status.disabled": "отключено",
+
+  "dialog.fork.empty": "Нет сообщений для ответвления",
+
+  "dialog.directory.search.placeholder": "Поиск папок",
+  "dialog.directory.empty": "Папки не найдены",
+
+  "dialog.server.title": "Серверы",
+  "dialog.server.description": "Переключите сервер OpenCode к которому подключается приложение.",
+  "dialog.server.search.placeholder": "Поиск серверов",
+  "dialog.server.empty": "Серверов пока нет",
+  "dialog.server.add.title": "Добавить сервер",
+  "dialog.server.add.url": "URL сервера",
+  "dialog.server.add.placeholder": "http://localhost:4096",
+  "dialog.server.add.error": "Не удалось подключиться к серверу",
+  "dialog.server.add.checking": "Проверка...",
+  "dialog.server.add.button": "Добавить",
+  "dialog.server.default.title": "Сервер по умолчанию",
+  "dialog.server.default.description":
+    "Подключаться к этому серверу при запуске приложения вместо запуска локального сервера. Требуется перезапуск.",
+  "dialog.server.default.none": "Сервер не выбран",
+  "dialog.server.default.set": "Установить текущий сервер по умолчанию",
+  "dialog.server.default.clear": "Очистить",
+
+  "dialog.project.edit.title": "Редактировать проект",
+  "dialog.project.edit.name": "Название",
+  "dialog.project.edit.icon": "Иконка",
+  "dialog.project.edit.icon.alt": "Иконка проекта",
+  "dialog.project.edit.icon.hint": "Нажмите или перетащите изображение",
+  "dialog.project.edit.icon.recommended": "Рекомендуется: 128x128px",
+  "dialog.project.edit.color": "Цвет",
+
+  "context.breakdown.title": "Разбивка контекста",
+  "context.breakdown.note":
+    'Приблизительная разбивка входных токенов. "Другое" включает определения инструментов и накладные расходы.',
+  "context.breakdown.system": "Система",
+  "context.breakdown.user": "Пользователь",
+  "context.breakdown.assistant": "Ассистент",
+  "context.breakdown.tool": "Вызовы инструментов",
+  "context.breakdown.other": "Другое",
+
+  "context.systemPrompt.title": "Системный промпт",
+  "context.rawMessages.title": "Исходные сообщения",
+
+  "context.stats.session": "Сессия",
+  "context.stats.messages": "Сообщения",
+  "context.stats.provider": "Провайдер",
+  "context.stats.model": "Модель",
+  "context.stats.limit": "Лимит контекста",
+  "context.stats.totalTokens": "Всего токенов",
+  "context.stats.usage": "Использование",
+  "context.stats.inputTokens": "Входные токены",
+  "context.stats.outputTokens": "Выходные токены",
+  "context.stats.reasoningTokens": "Токены рассуждения",
+  "context.stats.cacheTokens": "Токены кэша (чтение/запись)",
+  "context.stats.userMessages": "Сообщения пользователя",
+  "context.stats.assistantMessages": "Сообщения ассистента",
+  "context.stats.totalCost": "Общая стоимость",
+  "context.stats.sessionCreated": "Сессия создана",
+  "context.stats.lastActivity": "Последняя активность",
+
+  "context.usage.tokens": "Токены",
+  "context.usage.usage": "Использование",
+  "context.usage.cost": "Стоимость",
+  "context.usage.clickToView": "Нажмите для просмотра контекста",
+
+  "language.en": "Английский",
+  "language.zh": "Китайский",
+  "language.ko": "Корейский",
+  "language.de": "Немецкий",
+  "language.es": "Испанский",
+  "language.fr": "Французский",
+  "language.ja": "Японский",
+  "language.da": "Датский",
+  "language.ru": "Русский",
+
+  "toast.language.title": "Язык",
+  "toast.language.description": "Переключено на {{language}}",
+
+  "toast.theme.title": "Тема переключена",
+  "toast.scheme.title": "Цветовая схема",
+
+  "toast.permissions.autoaccept.on.title": "Авто-принятие изменений",
+  "toast.permissions.autoaccept.on.description": "Разрешения на редактирование и запись будут автоматически одобрены",
+  "toast.permissions.autoaccept.off.title": "Авто-принятие остановлено",
+  "toast.permissions.autoaccept.off.description": "Редактирование и запись потребуют подтверждения",
+
+  "toast.model.none.title": "Модель не выбрана",
+  "toast.model.none.description": "Подключите провайдера для суммаризации сессии",
+
+  "toast.file.loadFailed.title": "Не удалось загрузить файл",
+
+  "toast.session.share.copyFailed.title": "Не удалось скопировать URL в буфер обмена",
+  "toast.session.share.success.title": "Сессия опубликована",
+  "toast.session.share.success.description": "URL скопирован в буфер обмена!",
+  "toast.session.share.failed.title": "Не удалось опубликовать сессию",
+  "toast.session.share.failed.description": "Произошла ошибка при публикации сессии",
+
+  "toast.session.unshare.success.title": "Публикация отменена",
+  "toast.session.unshare.success.description": "Публикация успешно отменена!",
+  "toast.session.unshare.failed.title": "Не удалось отменить публикацию",
+  "toast.session.unshare.failed.description": "Произошла ошибка при отмене публикации",
+
+  "toast.session.listFailed.title": "Не удалось загрузить сессии для {{project}}",
+
+  "toast.update.title": "Доступно обновление",
+  "toast.update.description": "Новая версия OpenCode ({{version}}) доступна для установки.",
+  "toast.update.action.installRestart": "Установить и перезапустить",
+  "toast.update.action.notYet": "Пока нет",
+
+  "error.page.title": "Что-то пошло не так",
+  "error.page.description": "Произошла ошибка при загрузке приложения.",
+  "error.page.details.label": "Детали ошибки",
+  "error.page.action.restart": "Перезапустить",
+  "error.page.action.checking": "Проверка...",
+  "error.page.action.checkUpdates": "Проверить обновления",
+  "error.page.action.updateTo": "Обновить до {{version}}",
+  "error.page.report.prefix": "Пожалуйста, сообщите об этой ошибке команде OpenCode",
+  "error.page.report.discord": "в Discord",
+  "error.page.version": "Версия: {{version}}",
+
+  "error.dev.rootNotFound":
+    "Корневой элемент не найден. Вы забыли добавить его в index.html? Или, может быть, атрибут id был написан неправильно?",
+
+  "error.globalSync.connectFailed": "Не удалось подключиться к серверу. Запущен ли сервер по адресу `{{url}}`?",
+
+  "error.chain.unknown": "Неизвестная ошибка",
+  "error.chain.causedBy": "Причина:",
+  "error.chain.apiError": "Ошибка API",
+  "error.chain.status": "Статус: {{status}}",
+  "error.chain.retryable": "Повторная попытка: {{retryable}}",
+  "error.chain.responseBody": "Тело ответа:\n{{body}}",
+  "error.chain.didYouMean": "Возможно, вы имели в виду: {{suggestions}}",
+  "error.chain.modelNotFound": "Модель не найдена: {{provider}}/{{model}}",
+  "error.chain.checkConfig": "Проверьте названия провайдера/модели в конфиге (opencode.json)",
+  "error.chain.mcpFailed":
+    'MCP сервер "{{name}}" завершился с ошибкой. Обратите внимание, что OpenCode пока не поддерживает MCP авторизацию.',
+  "error.chain.providerAuthFailed": "Ошибка аутентификации провайдера ({{provider}}): {{message}}",
+  "error.chain.providerInitFailed":
+    'Не удалось инициализировать провайдера "{{provider}}". Проверьте учётные данные и конфигурацию.',
+  "error.chain.configJsonInvalid": "Конфигурационный файл по адресу {{path}} не является валидным JSON(C)",
+  "error.chain.configJsonInvalidWithMessage":
+    "Конфигурационный файл по адресу {{path}} не является валидным JSON(C): {{message}}",
+  "error.chain.configDirectoryTypo":
+    'Папка "{{dir}}" в {{path}} невалидна. Переименуйте папку в "{{suggestion}}" или удалите её. Это распространённая опечатка.',
+  "error.chain.configFrontmatterError": "Не удалось разобрать frontmatter в {{path}}:\n{{message}}",
+  "error.chain.configInvalid": "Конфигурационный файл по адресу {{path}} невалиден",
+  "error.chain.configInvalidWithMessage": "Конфигурационный файл по адресу {{path}} невалиден: {{message}}",
+
+  "notification.permission.title": "Требуется разрешение",
+  "notification.permission.description": "{{sessionTitle}} в {{projectName}} требуется разрешение",
+  "notification.question.title": "Вопрос",
+  "notification.question.description": "У {{sessionTitle}} в {{projectName}} есть вопрос",
+  "notification.action.goToSession": "Перейти к сессии",
+
+  "notification.session.responseReady.title": "Ответ готов",
+  "notification.session.error.title": "Ошибка сессии",
+  "notification.session.error.fallbackDescription": "Произошла ошибка",
+
+  "home.recentProjects": "Недавние проекты",
+  "home.empty.title": "Нет недавних проектов",
+  "home.empty.description": "Начните с открытия локального проекта",
+
+  "session.tab.session": "Сессия",
+  "session.tab.review": "Обзор",
+  "session.tab.context": "Контекст",
+  "session.review.filesChanged": "{{count}} файлов изменено",
+  "session.review.loadingChanges": "Загрузка изменений...",
+  "session.review.empty": "Изменений в этой сессии пока нет",
+  "session.messages.renderEarlier": "Показать предыдущие сообщения",
+  "session.messages.loadingEarlier": "Загрузка предыдущих сообщений...",
+  "session.messages.loadEarlier": "Загрузить предыдущие сообщения",
+  "session.messages.loading": "Загрузка сообщений...",
+  "session.messages.jumpToLatest": "Перейти к последнему",
+
+  "session.context.addToContext": "Добавить {{selection}} в контекст",
+
+  "session.new.worktree.main": "Основная ветка",
+  "session.new.worktree.mainWithBranch": "Основная ветка ({{branch}})",
+  "session.new.worktree.create": "Создать новый worktree",
+  "session.new.lastModified": "Последнее изменение",
+
+  "session.header.search.placeholder": "Поиск {{project}}",
+
+  "session.share.popover.title": "Опубликовать в интернете",
+  "session.share.popover.description.shared":
+    "Эта сессия общедоступна. Доступ к ней может получить любой, у кого есть ссылка.",
+  "session.share.popover.description.unshared":
+    "Опубликуйте сессию в интернете. Доступ к ней сможет получить любой, у кого есть ссылка.",
+  "session.share.action.share": "Поделиться",
+  "session.share.action.publish": "Опубликовать",
+  "session.share.action.publishing": "Публикация...",
+  "session.share.action.unpublish": "Отменить публикацию",
+  "session.share.action.unpublishing": "Отмена публикации...",
+  "session.share.action.view": "Посмотреть",
+  "session.share.copy.copied": "Скопировано",
+  "session.share.copy.copyLink": "Копировать ссылку",
+
+  "lsp.tooltip.none": "Нет LSP серверов",
+  "lsp.label.connected": "{{count}} LSP",
+
+  "prompt.loading": "Загрузка запроса...",
+  "terminal.loading": "Загрузка терминала...",
+  "terminal.title": "Терминал",
+  "terminal.title.numbered": "Терминал {{number}}",
+  "terminal.connectionLost.title": "Соединение потеряно",
+  "terminal.connectionLost.description":
+    "Соединение с терминалом прервано. Это может произойти при перезапуске сервера.",
+
+  "common.closeTab": "Закрыть вкладку",
+  "common.dismiss": "Закрыть",
+  "common.requestFailed": "Запрос не выполнен",
+  "common.moreOptions": "Дополнительные опции",
+  "common.learnMore": "Подробнее",
+  "common.rename": "Переименовать",
+  "common.reset": "Сбросить",
+  "common.delete": "Удалить",
+  "common.close": "Закрыть",
+  "common.edit": "Редактировать",
+  "common.loadMore": "Загрузить ещё",
+  "common.key.esc": "ESC",
+
+  "sidebar.menu.toggle": "Переключить меню",
+  "sidebar.settings": "Настройки",
+  "sidebar.help": "Помощь",
+  "sidebar.workspaces.enable": "Включить рабочие пространства",
+  "sidebar.workspaces.disable": "Отключить рабочие пространства",
+  "sidebar.gettingStarted.title": "Начало работы",
+  "sidebar.gettingStarted.line1": "OpenCode включает бесплатные модели, чтобы вы могли начать сразу.",
+  "sidebar.gettingStarted.line2":
+    "Подключите любого провайдера для использования моделей, включая Claude, GPT, Gemini и др.",
+  "sidebar.project.recentSessions": "Недавние сессии",
+  "sidebar.project.viewAllSessions": "Посмотреть все сессии",
+
+  "settings.section.desktop": "Приложение",
+  "settings.tab.general": "Основные",
+  "settings.tab.shortcuts": "Горячие клавиши",
+
+  "settings.general.section.appearance": "Внешний вид",
+  "settings.general.section.notifications": "Системные уведомления",
+  "settings.general.section.sounds": "Звуковые эффекты",
+
+  "settings.general.row.language.title": "Язык",
+  "settings.general.row.language.description": "Изменить язык отображения OpenCode",
+  "settings.general.row.appearance.title": "Внешний вид",
+  "settings.general.row.appearance.description": "Настройте как OpenCode выглядит на вашем устройстве",
+  "settings.general.row.theme.title": "Тема",
+  "settings.general.row.theme.description": "Настройте оформление OpenCode.",
+  "settings.general.row.font.title": "Шрифт",
+  "settings.general.row.font.description": "Настройте моноширинный шрифт для блоков кода",
+  "font.option.ibmPlexMono": "IBM Plex Mono",
+  "font.option.cascadiaCode": "Cascadia Code",
+  "font.option.firaCode": "Fira Code",
+  "font.option.hack": "Hack",
+  "font.option.inconsolata": "Inconsolata",
+  "font.option.intelOneMono": "Intel One Mono",
+  "font.option.jetbrainsMono": "JetBrains Mono",
+  "font.option.mesloLgs": "Meslo LGS",
+  "font.option.robotoMono": "Roboto Mono",
+  "font.option.sourceCodePro": "Source Code Pro",
+  "font.option.ubuntuMono": "Ubuntu Mono",
+  "sound.option.alert01": "Alert 01",
+  "sound.option.alert02": "Alert 02",
+  "sound.option.alert03": "Alert 03",
+  "sound.option.alert04": "Alert 04",
+  "sound.option.alert05": "Alert 05",
+  "sound.option.alert06": "Alert 06",
+  "sound.option.alert07": "Alert 07",
+  "sound.option.alert08": "Alert 08",
+  "sound.option.alert09": "Alert 09",
+  "sound.option.alert10": "Alert 10",
+  "sound.option.bipbop01": "Bip-bop 01",
+  "sound.option.bipbop02": "Bip-bop 02",
+  "sound.option.bipbop03": "Bip-bop 03",
+  "sound.option.bipbop04": "Bip-bop 04",
+  "sound.option.bipbop05": "Bip-bop 05",
+  "sound.option.bipbop06": "Bip-bop 06",
+  "sound.option.bipbop07": "Bip-bop 07",
+  "sound.option.bipbop08": "Bip-bop 08",
+  "sound.option.bipbop09": "Bip-bop 09",
+  "sound.option.bipbop10": "Bip-bop 10",
+  "sound.option.staplebops01": "Staplebops 01",
+  "sound.option.staplebops02": "Staplebops 02",
+  "sound.option.staplebops03": "Staplebops 03",
+  "sound.option.staplebops04": "Staplebops 04",
+  "sound.option.staplebops05": "Staplebops 05",
+  "sound.option.staplebops06": "Staplebops 06",
+  "sound.option.staplebops07": "Staplebops 07",
+  "sound.option.nope01": "Nope 01",
+  "sound.option.nope02": "Nope 02",
+  "sound.option.nope03": "Nope 03",
+  "sound.option.nope04": "Nope 04",
+  "sound.option.nope05": "Nope 05",
+  "sound.option.nope06": "Nope 06",
+  "sound.option.nope07": "Nope 07",
+  "sound.option.nope08": "Nope 08",
+  "sound.option.nope09": "Nope 09",
+  "sound.option.nope10": "Nope 10",
+  "sound.option.nope11": "Nope 11",
+  "sound.option.nope12": "Nope 12",
+  "sound.option.yup01": "Yup 01",
+  "sound.option.yup02": "Yup 02",
+  "sound.option.yup03": "Yup 03",
+  "sound.option.yup04": "Yup 04",
+  "sound.option.yup05": "Yup 05",
+  "sound.option.yup06": "Yup 06",
+
+  "settings.general.notifications.agent.title": "Агент",
+  "settings.general.notifications.agent.description":
+    "Показывать системное уведомление когда агент завершён или требует внимания",
+  "settings.general.notifications.permissions.title": "Разрешения",
+  "settings.general.notifications.permissions.description":
+    "Показывать системное уведомление когда требуется разрешение",
+  "settings.general.notifications.errors.title": "Ошибки",
+  "settings.general.notifications.errors.description": "Показывать системное уведомление когда происходит ошибка",
+
+  "settings.general.sounds.agent.title": "Агент",
+  "settings.general.sounds.agent.description": "Воспроизводить звук когда агент завершён или требует внимания",
+  "settings.general.sounds.permissions.title": "Разрешения",
+  "settings.general.sounds.permissions.description": "Воспроизводить звук когда требуется разрешение",
+  "settings.general.sounds.errors.title": "Ошибки",
+  "settings.general.sounds.errors.description": "Воспроизводить звук когда происходит ошибка",
+
+  "settings.shortcuts.title": "Горячие клавиши",
+  "settings.shortcuts.reset.button": "Сбросить к умолчаниям",
+  "settings.shortcuts.reset.toast.title": "Горячие клавиши сброшены",
+  "settings.shortcuts.reset.toast.description": "Горячие клавиши были сброшены к значениям по умолчанию.",
+  "settings.shortcuts.conflict.title": "Сочетание уже используется",
+  "settings.shortcuts.conflict.description": "{{keybind}} уже назначено для {{titles}}.",
+  "settings.shortcuts.unassigned": "Не назначено",
+  "settings.shortcuts.pressKeys": "Нажмите клавиши",
+  "settings.shortcuts.search.placeholder": "Поиск горячих клавиш",
+  "settings.shortcuts.search.empty": "Горячие клавиши не найдены",
+
+  "settings.shortcuts.group.general": "Основные",
+  "settings.shortcuts.group.session": "Сессия",
+  "settings.shortcuts.group.navigation": "Навигация",
+  "settings.shortcuts.group.modelAndAgent": "Модель и агент",
+  "settings.shortcuts.group.terminal": "Терминал",
+  "settings.shortcuts.group.prompt": "Запрос",
+
+  "settings.providers.title": "Провайдеры",
+  "settings.providers.description": "Настройки провайдеров будут доступны здесь.",
+  "settings.models.title": "Модели",
+  "settings.models.description": "Настройки моделей будут доступны здесь.",
+  "settings.agents.title": "Агенты",
+  "settings.agents.description": "Настройки агентов будут доступны здесь.",
+  "settings.commands.title": "Команды",
+  "settings.commands.description": "Настройки команд будут доступны здесь.",
+  "settings.mcp.title": "MCP",
+  "settings.mcp.description": "Настройки MCP будут доступны здесь.",
+
+  "settings.permissions.title": "Разрешения",
+  "settings.permissions.description": "Контролируйте какие инструменты сервер может использовать по умолчанию.",
+  "settings.permissions.section.tools": "Инструменты",
+  "settings.permissions.toast.updateFailed.title": "Не удалось обновить разрешения",
+
+  "settings.permissions.action.allow": "Разрешить",
+  "settings.permissions.action.ask": "Спрашивать",
+  "settings.permissions.action.deny": "Запретить",
+
+  "settings.permissions.tool.read.title": "Чтение",
+  "settings.permissions.tool.read.description": "Чтение файла (по совпадению пути)",
+  "settings.permissions.tool.edit.title": "Редактирование",
+  "settings.permissions.tool.edit.description":
+    "Изменение файлов, включая редактирование, запись, патчи и мульти-редактирование",
+  "settings.permissions.tool.glob.title": "Glob",
+  "settings.permissions.tool.glob.description": "Сопоставление файлов по паттернам glob",
+  "settings.permissions.tool.grep.title": "Grep",
+  "settings.permissions.tool.grep.description": "Поиск по содержимому файлов с использованием регулярных выражений",
+  "settings.permissions.tool.list.title": "Список",
+  "settings.permissions.tool.list.description": "Список файлов в директории",
+  "settings.permissions.tool.bash.title": "Bash",
+  "settings.permissions.tool.bash.description": "Выполнение команд оболочки",
+  "settings.permissions.tool.task.title": "Task",
+  "settings.permissions.tool.task.description": "Запуск под-агентов",
+  "settings.permissions.tool.skill.title": "Skill",
+  "settings.permissions.tool.skill.description": "Загрузить навык по имени",
+  "settings.permissions.tool.lsp.title": "LSP",
+  "settings.permissions.tool.lsp.description": "Выполнение запросов к языковому серверу",
+  "settings.permissions.tool.todoread.title": "Чтение списка задач",
+  "settings.permissions.tool.todoread.description": "Чтение списка задач",
+  "settings.permissions.tool.todowrite.title": "Запись списка задач",
+  "settings.permissions.tool.todowrite.description": "Обновление списка задач",
+  "settings.permissions.tool.webfetch.title": "Web Fetch",
+  "settings.permissions.tool.webfetch.description": "Получить содержимое по URL",
+  "settings.permissions.tool.websearch.title": "Web Search",
+  "settings.permissions.tool.websearch.description": "Поиск в интернете",
+  "settings.permissions.tool.codesearch.title": "Поиск кода",
+  "settings.permissions.tool.codesearch.description": "Поиск кода в интернете",
+  "settings.permissions.tool.external_directory.title": "Внешняя директория",
+  "settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта",
+  "settings.permissions.tool.doom_loop.title": "Doom Loop",
+  "settings.permissions.tool.doom_loop.description": "Обнаружение повторных вызовов инструментов с одинаковым вводом",
+
+  "workspace.new": "Новое рабочее пространство",
+  "workspace.type.local": "локальное",
+  "workspace.type.sandbox": "песочница",
+  "workspace.create.failed.title": "Не удалось создать рабочее пространство",
+  "workspace.delete.failed.title": "Не удалось удалить рабочее пространство",
+  "workspace.resetting.title": "Сброс рабочего пространства",
+  "workspace.resetting.description": "Это может занять минуту.",
+  "workspace.reset.failed.title": "Не удалось сбросить рабочее пространство",
+  "workspace.reset.success.title": "Рабочее пространство сброшено",
+  "workspace.reset.success.description": "Рабочее пространство теперь соответствует ветке по умолчанию.",
+  "workspace.status.checking": "Проверка наличия неслитых изменений...",
+  "workspace.status.error": "Не удалось проверить статус git.",
+  "workspace.status.clean": "Неслитые изменения не обнаружены.",
+  "workspace.status.dirty": "Обнаружены неслитые изменения в этом рабочем пространстве.",
+  "workspace.delete.title": "Удалить рабочее пространство",
+  "workspace.delete.confirm": 'Удалить рабочее пространство "{{name}}"?',
+  "workspace.delete.button": "Удалить рабочее пространство",
+  "workspace.reset.title": "Сбросить рабочее пространство",
+  "workspace.reset.confirm": 'Сбросить рабочее пространство "{{name}}"?',
+  "workspace.reset.button": "Сбросить рабочее пространство",
+  "workspace.reset.archived.none": "Никакие активные сессии не будут архивированы.",
+  "workspace.reset.archived.one": "1 сессия будет архивирована.",
+  "workspace.reset.archived.many": "{{count}} сессий будет архивировано.",
+  "workspace.reset.note": "Рабочее пространство будет сброшено в соответствие с веткой по умолчанию.",
+}

+ 1 - 0
packages/app/src/i18n/zh.ts

@@ -274,6 +274,7 @@ export const dict = {
   "language.fr": "法语",
   "language.fr": "法语",
   "language.ja": "日语",
   "language.ja": "日语",
   "language.da": "丹麦语",
   "language.da": "丹麦语",
+  "language.ru": "俄语",
   "language.pl": "波兰语",
   "language.pl": "波兰语",
 
 
   "toast.language.title": "语言",
   "toast.language.title": "语言",

+ 90 - 0
packages/ui/src/i18n/ru.ts

@@ -0,0 +1,90 @@
+export const dict = {
+  "ui.sessionReview.title": "Изменения сессии",
+  "ui.sessionReview.diffStyle.unified": "Объединённый",
+  "ui.sessionReview.diffStyle.split": "Разделённый",
+  "ui.sessionReview.expandAll": "Развернуть всё",
+  "ui.sessionReview.collapseAll": "Свернуть всё",
+
+  "ui.sessionTurn.steps.show": "Показать шаги",
+  "ui.sessionTurn.steps.hide": "Скрыть шаги",
+  "ui.sessionTurn.summary.response": "Ответ",
+  "ui.sessionTurn.diff.showMore": "Показать ещё изменений ({{count}})",
+
+  "ui.sessionTurn.retry.retrying": "повтор",
+  "ui.sessionTurn.retry.inSeconds": "через {{seconds}}с",
+
+  "ui.sessionTurn.status.delegating": "Делегирование работы",
+  "ui.sessionTurn.status.planning": "Планирование следующих шагов",
+  "ui.sessionTurn.status.gatheringContext": "Сбор контекста",
+  "ui.sessionTurn.status.searchingCodebase": "Поиск в кодовой базе",
+  "ui.sessionTurn.status.searchingWeb": "Поиск в интернете",
+  "ui.sessionTurn.status.makingEdits": "Внесение изменений",
+  "ui.sessionTurn.status.runningCommands": "Выполнение команд",
+  "ui.sessionTurn.status.thinking": "Размышление",
+  "ui.sessionTurn.status.thinkingWithTopic": "Размышление - {{topic}}",
+  "ui.sessionTurn.status.gatheringThoughts": "Сбор мыслей",
+  "ui.sessionTurn.status.consideringNextSteps": "Рассмотрение следующих шагов",
+
+  "ui.messagePart.diagnostic.error": "Ошибка",
+  "ui.messagePart.title.edit": "Редактировать",
+  "ui.messagePart.title.write": "Написать",
+  "ui.messagePart.option.typeOwnAnswer": "Введите свой ответ",
+  "ui.messagePart.review.title": "Проверьте ваши ответы",
+
+  "ui.list.loading": "Загрузка",
+  "ui.list.empty": "Нет результатов",
+  "ui.list.emptyWithFilter.prefix": "Нет результатов для",
+  "ui.list.emptyWithFilter.suffix": "",
+
+  "ui.messageNav.newMessage": "Новое сообщение",
+
+  "ui.textField.copyToClipboard": "Копировать в буфер обмена",
+  "ui.textField.copied": "Скопировано",
+
+  "ui.imagePreview.alt": "Предпросмотр изображения",
+
+  "ui.tool.read": "Чтение",
+  "ui.tool.list": "Список",
+  "ui.tool.glob": "Glob",
+  "ui.tool.grep": "Grep",
+  "ui.tool.webfetch": "Webfetch",
+  "ui.tool.shell": "Оболочка",
+  "ui.tool.patch": "Патч",
+  "ui.tool.todos": "Задачи",
+  "ui.tool.todos.read": "Читать задачи",
+  "ui.tool.questions": "Вопросы",
+  "ui.tool.agent": "Агент {{type}}",
+
+  "ui.common.file.one": "файл",
+  "ui.common.file.other": "файлов",
+  "ui.common.question.one": "вопрос",
+  "ui.common.question.other": "вопросов",
+
+  "ui.common.add": "Добавить",
+  "ui.common.cancel": "Отмена",
+  "ui.common.confirm": "Подтвердить",
+  "ui.common.dismiss": "Закрыть",
+  "ui.common.next": "Далее",
+  "ui.common.submit": "Отправить",
+
+  "ui.permission.deny": "Запретить",
+  "ui.permission.allowAlways": "Разрешить всегда",
+  "ui.permission.allowOnce": "Разрешить один раз",
+
+  "ui.message.expand": "Развернуть сообщение",
+  "ui.message.collapse": "Свернуть сообщение",
+  "ui.message.copy": "Копировать",
+  "ui.message.copied": "Скопировано!",
+  "ui.message.attachment.alt": "вложение",
+
+  "ui.patch.action.deleted": "Удалено",
+  "ui.patch.action.created": "Создано",
+  "ui.patch.action.moved": "Перемещено",
+  "ui.patch.action.patched": "Изменено",
+
+  "ui.question.subtitle.answered": "{{count}} отвечено",
+  "ui.question.answer.none": "(нет ответа)",
+  "ui.question.review.notAnswered": "(не отвечено)",
+  "ui.question.multiHint": "(выберите все подходящие)",
+  "ui.question.custom.placeholder": "Введите ваш ответ...",
+}