feat(i18n): add Russian (ru-RU) language support (#524)

Add complete Russian language support to Open Notebook following the
established i18n patterns from ja-JP and pt-BR implementations.

Changes:
- Create ru-RU locale file with all translation keys
- Register ru-RU in locales/index.ts resources and languages array
- Add ru-RU key integrity test
- Add Russian date-fns locale mapping
- Add Russian option to LanguageToggle dropdown
- Add `russian: "Русский"` key to all existing locales
- Update README.md supported languages list
- Update locales CLAUDE.md documentation

Fixes #523

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Luis Novo <lfnovo@gmail.com>
This commit is contained in:
Nikita 2026-02-02 00:48:54 +03:00 committed by GitHub
parent 0f76c93b00
commit d0816caab1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 989 additions and 5 deletions

View file

@ -63,7 +63,7 @@ In a world dominated by Artificial Intelligence, having the ability to think
- 🎙️ **Generate professional podcasts** - Advanced multi-speaker podcast generation
- 🔍 **Search intelligently** - Full-text and vector search across all your content
- 💬 **Chat with context** - AI conversations powered by your research
- 🌐 **Multi-language UI** - English, Portuguese, Chinese (Simplified & Traditional), and Japanese support
- 🌐 **Multi-language UI** - English, Portuguese, Chinese (Simplified & Traditional), Japanese, and Russian support
Learn more about our project at [https://www.open-notebook.ai](https://www.open-notebook.ai)

View file

@ -64,6 +64,12 @@ export function LanguageToggle({ iconOnly = false }: LanguageToggleProps) {
>
<span>{t.common.japanese}</span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => setLanguage('ru-RU')}
className={currentLang === 'ru-RU' || currentLang.startsWith('ru') ? 'bg-accent' : ''}
>
<span>{t.common.russian}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)

View file

@ -18,7 +18,8 @@ lib/
├── pt-BR/index.ts # Brazilian Portuguese translations
├── zh-CN/index.ts # Simplified Chinese translations
├── zh-TW/index.ts # Traditional Chinese translations
└── ja-JP/index.ts # Japanese translations
├── ja-JP/index.ts # Japanese translations
└── ru-RU/index.ts # Russian translations
```
## Key Components

View file

@ -23,6 +23,7 @@ export const enUS = {
english: "English",
chinese: "简体中文",
japanese: "日本語",
russian: "Русский",
source: "Source",
notebook: "Notebook",
podcast: "Podcast",

View file

@ -3,6 +3,7 @@ import { enUS } from './en-US'
import { zhCN } from './zh-CN'
import { zhTW } from './zh-TW'
import { jaJP } from './ja-JP'
import { ruRU } from './ru-RU'
describe('Internationalization Locales Integrity', () => {
const getKeys = (obj: Record<string, unknown>, prefix = ''): string[] => {
@ -19,6 +20,7 @@ describe('Internationalization Locales Integrity', () => {
const zhCNKeys = getKeys(zhCN)
const zhTWKeys = getKeys(zhTW)
const jaJPKeys = getKeys(jaJP)
const ruRUKeys = getKeys(ruRU)
it('zh-CN should have the same keys as en-US', () => {
const missingInZhCN = enKeys.filter(key => !zhCNKeys.includes(key))
@ -43,4 +45,12 @@ describe('Internationalization Locales Integrity', () => {
expect(missingInJaJP, `Missing keys in ja-JP: ${missingInJaJP.join(', ')}`).toEqual([])
expect(extraInJaJP, `Extra keys in ja-JP: ${extraInJaJP.join(', ')}`).toEqual([])
})
it('ru-RU should have the same keys as en-US', () => {
const missingInRuRU = enKeys.filter(key => !ruRUKeys.includes(key))
const extraInRuRU = ruRUKeys.filter(key => !enKeys.includes(key))
expect(missingInRuRU, `Missing keys in ru-RU: ${missingInRuRU.join(', ')}`).toEqual([])
expect(extraInRuRU, `Extra keys in ru-RU: ${extraInRuRU.join(', ')}`).toEqual([])
})
})

View file

@ -4,6 +4,7 @@ import { zhTW } from './zh-TW';
import { ptBR } from './pt-BR';
import { jaJP } from './ja-JP';
import { itIT } from './it-IT';
import { ruRU } from './ru-RU';
export const resources = {
'zh-CN': { translation: zhCN },
@ -12,11 +13,12 @@ export const resources = {
'pt-BR': { translation: ptBR },
'ja-JP': { translation: jaJP },
'it-IT': { translation: itIT },
'ru-RU': { translation: ruRU },
} as const;
export type TranslationKeys = typeof enUS;
export type LanguageCode = 'zh-CN' | 'en-US' | 'zh-TW' | 'pt-BR' | 'ja-JP' | 'it-IT';
export type LanguageCode = 'zh-CN' | 'en-US' | 'zh-TW' | 'pt-BR' | 'ja-JP' | 'it-IT' | 'ru-RU';
export type Language = {
code: LanguageCode;
@ -30,6 +32,7 @@ export const languages: Language[] = [
{ code: 'pt-BR', label: 'Português' },
{ code: 'ja-JP', label: '日本語' },
{ code: 'it-IT', label: 'Italiano' },
{ code: 'ru-RU', label: 'Русский' },
];
export { zhCN, enUS, zhTW, ptBR, jaJP, itIT };
export { zhCN, enUS, zhTW, ptBR, jaJP, itIT, ruRU };

View file

@ -23,6 +23,7 @@ export const jaJP = {
english: "English",
chinese: "简体中文",
japanese: "日本語",
russian: "Русский",
source: "ソース",
notebook: "ノートブック",
podcast: "ポッドキャスト",

View file

@ -23,6 +23,7 @@ export const ptBR = {
english: "English",
chinese: "简体中文",
japanese: "日本語",
russian: "Русский",
source: "Fonte",
notebook: "Caderno",
podcast: "Podcast",

View file

@ -0,0 +1,958 @@
export const ruRU = {
common: {
search: "Поиск...",
create: "Создать",
new: "Новый",
cancel: "Отмена",
delete: "Удалить",
edit: "Редактировать",
theme: "Тема",
signOut: "Выйти",
noMatches: "Совпадений не найдено",
tryDifferentSearch: "Попробуйте другой поисковый запрос.",
light: "Светлая",
dark: "Тёмная",
system: "Системная",
loading: "Загрузка...",
note: "Заметка",
insight: "Инсайт",
newSource: "Новый источник",
newNotebook: "Новый блокнот",
newPodcast: "Новый подкаст",
language: "Язык",
english: "English",
chinese: "简体中文",
japanese: "日本語",
russian: "Русский",
source: "Источник",
notebook: "Блокнот",
podcast: "Подкаст",
quickActions: "Быстрые действия",
quickActionsDesc: "Навигация, поиск, запрос, тема",
appName: "Open Notebook",
add: "Добавить",
remove: "Удалить",
confirm: "Подтвердить",
warning: "Предупреждение",
error: "Ошибка",
success: "Успешно",
sessions: "Сессии",
model: "Модель",
send: "Отправить",
back: "Назад",
next: "Далее",
done: "Готово",
processing: "Обработка...",
creating: "Создание...",
tokenCount: "Токены",
charCount: "Символы",
linked: "Связано",
added: "Добавлено {date}",
adding: "Добавление...",
addSelected: "Добавить выбранное",
customModel: "Своя модель",
messages: "Сообщения",
failed: "не удалось",
current: "Текущий",
save: "Сохранить",
writeNote: "Написать заметку",
batchMode: "Пакетный режим",
optional: "Необязательно",
type: "Тип",
title: "Название",
created: "Создано {time}",
updated: "Обновлено {time}",
actions: "Действия",
noResults: "Нет результатов",
references: "Ссылки",
refreshPage: "Попробуйте обновить страницу",
refresh: "Обновить",
aiGenerated: "Сгенерировано ИИ",
human: "Человек",
unknown: "Неизвестно",
notes: "Заметки",
chat: "Чат",
details: "Подробности",
deleteForever: "Удалить навсегда",
connectionError: "Ошибка подключения",
unableToConnect: "Не удаётся подключиться к API-серверу",
retryConnection: "Повторить подключение",
diagnosticInfo: "Диагностическая информация",
version: "Версия",
built: "Сборка",
apiUrl: "URL API",
frontendUrl: "URL фронтенда",
checkConsoleLogs: "Проверьте консоль браузера для подробных логов (ищите сообщения 🔧 [Config])",
yes: "Да",
no: "Нет",
simple: "Простой",
saving: "Сохранение...",
description: "Описание",
saveToNote: "Сохранить в заметку",
copyToClipboard: "Копировать в буфер обмена",
close: "Закрыть",
insights: "Инсайты",
progress: "Прогресс",
deleting: "Удаление...",
created_label: "Создано",
updated_label: "Обновлено",
download: "Скачать",
saveChanges: "Сохранить изменения",
name: "Название",
default: "По умолчанию",
nameRequired: "Название обязательно",
modelConfiguration: "Настройка модели",
resetToDefault: "Сбросить по умолчанию",
notFound: "Не найдено",
reasoning: "Рассуждение",
searchTerms: "Поисковые запросы",
strategy: "Стратегия",
individualAnswers: "Отдельные ответы ({count})",
finalAnswer: "Итоговый ответ",
notebookLabel: "Блокнот: {name}",
itemNotFound: "Этот {type} не найден",
accessibility: {
navigation: "Навигация",
transformationViews: "Представления трансформаций",
searchKB: "Спросить или найти в базе знаний",
enterQuestion: "Введите вопрос для базы знаний",
enterSearch: "Введите поисковый запрос",
searchKBBtn: "Поиск по базе знаний",
podcastViews: "Представления подкастов",
chatSessions: "Сессии чата",
ytVideo: "Видео YouTube",
askResponse: "Ответ на запрос",
searchNotebooks: "Поиск блокнотов",
},
url: "URL",
errorDetails: "Детали ошибки",
editTransformation: "Редактировать трансформацию",
comingSoon: "Скоро будет",
retry: "Повторить",
traditionalChinese: "繁體中文",
portuguese: "Português",
completed: "завершено",
saveSuccess: "Успешно сохранено",
contextModes: {
off: "Не включено в чат",
insights: "Только инсайты",
full: "Полное содержимое",
clickToCycle: "Нажмите для переключения",
},
clickToEdit: "Нажмите для редактирования",
},
apiErrors: {
notebookNotFound: "Блокнот не найден",
sourceNotFound: "Источник не найден",
transformationNotFound: "Трансформация не найдена",
fileUploadFailed: "Не удалось загрузить файл",
urlRequired: "URL обязателен для типа «ссылка»",
contentRequired: "Содержимое обязательно для типа «текст»",
invalidSourceType: "Недопустимый тип источника",
processingFailed: "Обработка не удалась",
failedToQueue: "Не удалось поставить в очередь обработки",
invalidSortBy: "Поле сортировки должно быть 'created' или 'updated'",
invalidSortOrder: "Порядок сортировки должен быть 'asc' или 'desc'",
accessDenied: "Доступ к файлу запрещён",
fileNotFoundOnServer: "Файл не найден на сервере",
searchFailed: "Поиск не удался",
askFailed: "Запрос не удался",
pleaseEnterQuestion: "Пожалуйста, введите вопрос",
pleaseConfigureModels: "Пожалуйста, настройте все необходимые модели",
failedToCreateSession: "Не удалось создать сессию",
failedToUpdateSession: "Не удалось обновить сессию",
failedToDeleteSession: "Не удалось удалить сессию",
failedToSendMessage: "Не удалось отправить сообщение",
unauthorized: "Неавторизованный доступ, проверьте пароль",
invalidPassword: "Неверный пароль",
missingAuth: "Отсутствует авторизация",
embeddingModelRequired: "Для этой функции требуется модель эмбеддингов. Настройте её в разделе «Модели».",
strategyModelNotFound: "Модель стратегии не найдена",
answerModelNotFound: "Модель ответов не найдена",
finalAnswerModelNotFound: "Модель итогового ответа не найдена",
noAnswerGenerated: "Не удалось сгенерировать ответ",
genericError: "Произошла непредвиденная ошибка",
},
connectionErrors: {
apiTitle: "Не удаётся подключиться к API-серверу",
apiDesc: "API-сервер Open Notebook недоступен",
dbTitle: "Ошибка подключения к базе данных",
dbDesc: "API-сервер работает, но база данных недоступна",
troubleshooting: "Обычно это означает:",
apiUnreachable1: "API-сервер не запущен",
apiUnreachable2: "API-сервер работает по другому адресу",
apiUnreachable3: "Проблемы с сетевым подключением",
dbFailed1: "SurrealDB не запущен",
dbFailed2: "Неверные настройки подключения к базе данных",
dbFailed3: "Сетевые проблемы между API и базой данных",
quickFixes: "Быстрые решения:",
setApiUrl: "Установите переменную окружения API_URL:",
checkSurreal: "Проверьте, запущен ли SurrealDB:",
seeDocumentation: "Подробные инструкции по настройке см. в:",
docLink: "Документация Open Notebook",
showTechnical: "Показать техническую информацию",
attemptedUrl: "Использованный URL",
message: "Сообщение",
technicalDetails: "Технические детали",
stackTrace: "Стек вызовов",
retryLabel: "Повторить подключение",
retryHint: "Нажмите R или кнопку для повторной попытки",
dockerLabel: "Для Docker",
localDevLabel: "Для локальной разработки",
},
auth: {
loginTitle: "Open Notebook",
loginDesc: "Введите пароль для доступа к приложению",
passwordPlaceholder: "Пароль",
signingIn: "Вход...",
signIn: "Войти",
unhandledError: "Необработанная ошибка при входе",
connectErrorHint: "Не удаётся подключиться к серверу. Проверьте, запущен ли API.",
},
navigation: {
collect: "Собрать",
process: "Обработать",
create: "Создать",
manage: "Управление",
sources: "Источники",
notebooks: "Блокноты",
askAndSearch: "Запрос и поиск",
podcasts: "Подкасты",
models: "Модели",
transformations: "Трансформации",
transformation: "Трансформация",
settings: "Настройки",
advanced: "Дополнительно",
nav: "Навигация",
language: "Переключить язык",
theme: "Тема",
search: "Поиск",
ask: "Запрос",
},
notebooks: {
title: "Блокноты",
newNotebook: "Новый блокнот",
searchPlaceholder: "Поиск блокнотов...",
archived: "Архивные",
archive: "Архивировать",
unarchive: "Разархивировать",
deleteNotebook: "Удалить блокнот",
deleteNotebookDesc: "Вы уверены, что хотите удалить «{name}»? Это действие нельзя отменить.",
deleteNotebookLoading: "Загрузка предварительного просмотра удаления...",
deleteNotebookNotes: "Будет удалено заметок: {count}.",
deleteNotebookNoNotes: "Нет заметок для удаления.",
deleteNotebookExclusiveSources: "Источников только в этом блокноте: {count}.",
deleteNotebookSharedSources: "Источников, связанных с другими блокнотами (будут отвязаны): {count}.",
deleteNotebookNoSources: "В этом блокноте нет источников.",
deleteExclusiveSourcesLabel: "Удалить эксклюзивные источники",
keepExclusiveSourcesLabel: "Отвязать и сохранить",
activeNotebooks: "Активные блокноты",
archivedNotebooks: "Архивные блокноты",
emptyDescription: "Начните с создания первого блокнота для организации исследований.",
noActiveNotebooks: "Нет активных блокнотов",
noArchivedNotebooks: "Нет архивных блокнотов",
notFound: "Блокнот не найден",
notFoundDesc: "Запрошенный блокнот не существует.",
noDescription: "Без описания...",
updated: "Обновлено",
namePlaceholder: "Название блокнота",
addDescription: "Добавить описание...",
noNotesYet: "Пока нет заметок",
deleteNote: "Удалить заметку",
deleteNoteConfirm: "Вы уверены, что хотите удалить эту заметку? Это действие нельзя отменить.",
noteCreatedSuccess: "Заметка успешно создана",
failedToCreateNote: "Не удалось создать заметку",
noteUpdatedSuccess: "Заметка успешно обновлена",
failedToUpdateNote: "Не удалось обновить заметку",
noteDeletedSuccess: "Заметка успешно удалена",
failedToDeleteNote: "Не удалось удалить заметку",
createNew: "Создать новый блокнот",
createNewDesc: "Введите название и необязательное описание для начала.",
descPlaceholder: "Добавьте дополнительную информацию о блокноте...",
createSuccess: "Блокнот успешно создан",
updateSuccess: "Блокнот успешно обновлён",
deleteSuccess: "Блокнот успешно удалён",
},
sources: {
title: "Источники",
add: "Добавить источник",
addNew: "Добавить новый источник",
addExisting: "Добавить существующий источник",
empty: "Пока нет источников",
emptyDesc: "Добавьте первый источник, чтобы начать создание базы знаний.",
delete: "Удалить источник",
deleteMsg: "Вы уверены, что хотите удалить этот источник? Это действие нельзя отменить.",
statusPreparing: "Подготовка",
statusQueued: "В очереди",
statusProcessing: "Обработка",
statusCompleted: "Завершено",
statusFailed: "Ошибка",
statusPreparingDesc: "Подготовка к обработке",
statusQueuedDesc: "Ожидание обработки",
statusProcessingDesc: "Выполняется обработка",
statusCompletedDesc: "Успешно обработано",
statusFailedDesc: "Обработка не удалась",
failedToLoad: "Не удалось загрузить источники",
allSourcesDesc: "Просмотр всех источников. Вы можете добавлять новые или управлять существующими.",
allSources: "Все источники",
insights: "Инсайты",
yes: "Да",
no: "Нет",
loadingMore: "Загрузка...",
noSourcesYet: "Пока нет источников",
allSourcesDescShort: "Просмотр всех ваших источников.",
cannotSaveNoteNoNotebook: "Невозможно сохранить заметку: ID блокнота недоступен",
createFirstSource: "Добавьте первый источник, чтобы начать создание базы знаний.",
deleteSourceConfirm: "Вы уверены, что хотите удалить этот источник?",
deleteConfirm: "Вы уверены, что хотите удалить это?",
deleteConfirmWithTitle: "Вы уверены, что хотите удалить «{title}»?",
deleteSuccess: "Источник успешно удалён. Примечание: Чтобы удалить файл из хранилища, необходимо включить опцию «Удалить файл» в настройках.",
failedToDelete: "Не удалось удалить источник",
sourceQueued: "Источник в очереди",
sourceQueuedDesc: "Источник отправлен на фоновую обработку. Отслеживайте прогресс в списке источников.",
sourceAddedSuccess: "Источник успешно добавлен",
failedToAddSource: "Не удалось добавить источник",
sourceUpdatedSuccess: "Источник успешно обновлён",
failedToUpdateSource: "Не удалось обновить источник",
sourceDeletedSuccess: "Источник успешно удалён",
failedToDeleteSource: "Не удалось удалить источник",
fileUploadedSuccess: "Файл успешно загружен",
failedToUploadFile: "Не удалось загрузить файл",
sourceRequeued: "Повторная обработка в очереди",
sourceRequeuedDesc: "Источник поставлен в очередь на повторную обработку.",
failedToRetry: "Повтор не удался",
failedToRetryDesc: "Не удалось повторить обработку источника. Попробуйте ещё раз.",
sourcesAddedToNotebook: "Добавлено источников в блокнот: {count}",
failedToAddSourcesToNotebook: "Не удалось добавить источники в блокнот",
partialAddSuccess: "Добавлено: {success}, не удалось: {failed}",
sourceRemovedFromNotebook: "Источник успешно удалён из блокнота",
failedToRemoveSourceFromNotebook: "Не удалось удалить источник из блокнота",
removeConfirm: "Вы уверены, что хотите удалить это из блокнота?",
checking: "Проверка...",
untitledSource: "Без названия",
maxItems: "макс. {count}",
insightsCount: "Инсайтов: {count}",
details: "Детали",
detailsTitle: "Детали источника",
content: "Содержимое",
metadata: "Метаданные",
type: {
link: "Ссылка",
file: "Файл",
text: "Текст",
},
id: "ID источника",
topics: "Темы",
embedded: "С эмбеддингом",
notEmbedded: "Без эмбеддинга",
embedContent: "Создать эмбеддинг",
embedding: "Создание эмбеддинга...",
alreadyEmbedded: "Эмбеддинг уже создан",
downloadFile: "Скачать файл",
fileUnavailable: "Файл недоступен",
preparing: "Подготовка...",
generateNewInsight: "Сгенерировать новый инсайт",
selectTransformation: "Выберите трансформацию...",
noInsightsYet: "Пока нет инсайтов",
createFirstInsight: "Создайте первый инсайт с помощью трансформации выше",
viewInsight: "Просмотреть инсайт",
deleteInsight: "Удалить инсайт",
deleteInsightConfirm: "Вы уверены, что хотите удалить этот инсайт? Это действие нельзя отменить.",
insightGenerationStarted: "Генерация инсайта запущена. Скоро он появится.",
deleteNoteConfirm: "Вы уверены, что хотите удалить эту заметку? Это действие нельзя отменить.",
editNote: "Редактировать заметку",
createNote: "Создать заметку",
addTitle: "Добавьте название...",
untitledNote: "Без названия",
writeNotePlaceholder: "Напишите содержимое заметки здесь...",
saveNote: "Сохранить заметку",
createNoteBtn: "Создать заметку",
noNotesYet: "Пока нет заметок",
createFirstNote: "Создайте первую заметку для записи идей и наблюдений.",
deleteNote: "Удалить заметку",
urlLabel: "URL(ы) *",
fileLabel: "Файл(ы) *",
textContentLabel: "Текстовое содержимое *",
enterUrlsPlaceholder: "Введите URL-адреса, по одному на строку\nhttps://example.com/article1\nhttps://example.com/article2",
batchUrlHint: "Вставьте несколько URL (по одному на строку) для пакетного импорта",
invalidUrlsDetected: "Обнаружены недопустимые URL:",
lineLabel: "Строка {line}",
fixInvalidUrls: "Исправьте или удалите недопустимые URL для продолжения",
selectMultipleFilesHint: "Выберите несколько файлов для пакетного импорта. Поддерживаются: Документы (PDF, DOC, DOCX, PPT, XLS, EPUB, TXT, MD), Медиа (MP4, MP3, WAV, M4A), Изображения (JPG, PNG), Архивы (ZIP)",
selectedFiles: "Выбранные файлы:",
textPlaceholder: "Вставьте или введите содержимое здесь...",
htmlDetected: "Обнаружено HTML-содержимое. После обработки оно будет преобразовано в Markdown.",
titlePlaceholder: "Дайте источнику понятное название",
batchTitlesAuto: "Названия будут автоматически сгенерированы для каждого источника.",
batchCommonSettings: "Одинаковые блокноты и трансформации будут применены ко всем элементам.",
urlsCount: "URL: {count}",
filesCount: "Файлов: {count}",
addSource: "Добавить источник",
notEmbeddedAlert: "Содержимое без эмбеддинга",
notEmbeddedDesc: "Для этого содержимого не создан эмбеддинг для векторного поиска. Эмбеддинг обеспечивает расширенные возможности поиска и лучшее обнаружение контента.",
openOnYoutube: "Открыть на YouTube",
urlCopied: "URL скопирован в буфер обмена",
viewSource: "Просмотреть источник",
noInsightSelected: "Инсайт не выбран",
sourceInsight: "Инсайт источника",
manageNotebooks: "Управление блокнотами",
manageNotebooksDesc: "Управление блокнотами, содержащими этот источник",
noNotebooksAvailable: "Нет доступных блокнотов",
loadFailed: "Не удалось загрузить детали источника",
removeFromNotebook: "Удалить из блокнота",
retryProcessing: "Повторить обработку",
deleteSource: "Удалить источник",
retry: "Повторить",
progress: "Прогресс",
addExistingTitle: "Добавить существующие источники",
addExistingDesc: "Выберите существующие источники из всех блокнотов для добавления в текущий.",
searchPlaceholder: "Поиск источников по названию или URL...",
noNotebooksFound: "Блокноты не найдены.",
showingFirst100: "Показаны первые 100 источников. Используйте поиск для конкретных результатов.",
selectedCount: "Выбрано источников: {count}",
added: "Добавлено {date}",
addUrl: "Добавить URL",
uploadFile: "Загрузить файл",
enterText: "Ввести текст",
processDescription: "Содержимое будет обработано и проанализировано ИИ.",
processingFiles: "Обработка файлов...",
titleRequired: "Для текстового содержимого требуется название",
titleGenerated: "Если оставить пустым, название будет сгенерировано из содержимого",
batchCount: "{count} {type} будет обработано",
enableEmbedding: "Включить эмбеддинг для поиска",
embeddingDesc: "Позволяет находить этот источник в векторном поиске и запросах ИИ",
embeddingAlways: "Эмбеддинг включён автоматически",
embeddingAlwaysDesc: "В ваших настройках включено автоматическое создание эмбеддинга для векторного поиска.",
embeddingNever: "Эмбеддинг отключён",
embeddingNeverDesc: "В ваших настройках отключено создание эмбеддинга. Векторный поиск будет недоступен для этого источника.",
changeInSettings: "Вы можете изменить это в Настройках",
notFound: "Источник не найден",
noContent: "Содержимое недоступно",
insightsDesc: "Инсайты, сгенерированные анализом модели",
uploadedFile: "Загруженный файл",
fileUnavailableDesc: "Этот файл временно недоступен из-за проблем с хранилищем.",
batchSuccess: "Успешно создано источников: {count}",
batchFailed: "Не удалось создать все источники: {count}",
batchPartial: "Успешно: {success}, не удалось: {failed}",
submittingSource: "Отправка источника на обработку...",
contentRequired: "Пожалуйста, предоставьте необходимое содержимое для выбранного типа источника",
titleRequiredForText: "Для текстовых источников требуется название",
processingBatchSources: "Обработка источников: {count}. Это может занять некоторое время.",
processingSource: "Источник обрабатывается. Это может занять некоторое время.",
maxFilesAllowed: "Максимальное количество файлов в пакете: {count}",
},
chat: {
sessions: "Сессии",
sessionTitlePlaceholder: "Введите название...",
noSessions: "Пока нет сессий чата",
startChatting: "Начните общение о ваших источниках.",
deleteSession: "Удалить сессию",
deleteSessionDesc: "Вы уверены, что хотите удалить эту сессию чата? Это действие нельзя отменить.",
sendPlaceholder: "Задайте вопрос о ваших источниках...",
newChat: "Новый чат",
sessionsTitle: "Сессии чата",
clearhistory: "Очистить историю",
renameSession: "Переименовать сессию",
noSourcesLinked: "Нет связанных источников",
thinking: "ИИ размышляет...",
chatWith: "Чат с {name}",
startConversation: "Начните разговор об этом {type}",
askQuestions: "Задавайте вопросы, чтобы лучше понять содержимое",
pressToSend: "Нажмите {key} для отправки",
model: "Модель",
createToStart: "Создайте сессию для начала.",
chatWithNotebook: "Чат с блокнотом",
unableToLoadChat: "Не удалось загрузить чат",
noDescription: "Без описания",
startByCreating: "Начните с создания первого блокнота для организации исследований.",
messagesCount: "Сообщений: {count}",
sessionCreated: "Сессия чата создана",
sessionUpdated: "Сессия обновлена",
sessionDeleted: "Сессия удалена",
},
searchPage: {
askAndSearch: "Запрос и поиск",
chooseAMode: "Выберите режим",
askBeta: "Запрос (бета)",
search: "Поиск",
askYourKb: "Спросите базу знаний (бета)",
askYourKbDesc: "LLM ответит на ваш запрос на основе документов в базе знаний.",
question: "Вопрос",
enterQuestionPlaceholder: "Введите ваш вопрос...",
pressToSubmit: "Нажмите Cmd/Ctrl+Enter для отправки",
noEmbeddingModel: "Вы не можете использовать эту функцию, потому что не выбрана модель эмбеддинга. Настройте её на странице «Модели».",
usingCustomModels: "Используются пользовательские модели",
usingDefaultModels: "Используются модели по умолчанию",
advanced: "Расширенные",
strategy: "Стратегия",
answer: "Ответ",
final: "Итоговый",
ask: "Спросить",
processing: "Обработка...",
saveToNotebooks: "Сохранить в блокноты",
searchDesc: "Поиск в базе знаний по ключевым словам или концепциям",
enterSearchPlaceholder: "Введите поисковый запрос...",
pressToSearch: "Нажмите Enter для поиска",
searchType: "Тип поиска",
vectorSearchWarning: "Векторный поиск требует модель эмбеддинга. Доступен только текстовый поиск.",
textSearch: "Текстовый поиск",
vectorSearch: "Векторный поиск",
searchIn: "Искать в",
searchSources: "Искать в источниках",
searchNotes: "Искать в заметках",
resultsFound: "Найдено результатов: {count}",
matches: "Совпадения ({count})",
noResultsFor: "Нет результатов для «{query}»",
notSet: "Не задано",
saveToNotebook: "Сохранить в блокнот",
saveSuccess: "Успешно сохранено в блокнот",
saveError: "Не удалось сохранить в блокнот",
selectNotebook: "Выберите блокнот",
createNewNotebook: "Создать новый блокнот",
cancel: "Отмена",
searchAndAsk: "Поиск и запрос",
searchResultsFor: "Результаты поиска для «{query}»",
askAbout: "Спросить о «{query}»",
orSearchKb: "Или выполнить поиск по базе знаний",
saving: "Сохранение...",
advancedModelTitle: "Расширенный выбор моделей",
advancedModelDesc: "Выберите конкретные модели для каждого этапа процесса запроса",
strategyModel: "Модель стратегии",
answerModel: "Модель ответа",
finalAnswerModel: "Модель итогового ответа",
selectStrategyPlaceholder: "Выберите модель стратегии",
selectAnswerPlaceholder: "Выберите модель ответа",
selectFinalPlaceholder: "Выберите модель итогового ответа",
saveChanges: "Сохранить изменения",
processingQuestion: "Обработка вашего вопроса...",
},
podcasts: {
generateEpisode: "Сгенерировать эпизод подкаста",
generateEpisodeDesc: "Выберите контент для включения и настройте параметры эпизода перед генерацией.",
content: "Контент",
contentDesc: "Выберите блокноты, источники и заметки для этого эпизода.",
itemsSelected: "Выбрано элементов: {count}",
tokens: "Токенов: {count}",
chars: "Символов: {count}",
loadingNotebooks: "Загрузка блокнотов...",
noNotebooksFoundInPodcasts: "Блокноты не найдены. Создайте блокнот и добавьте контент перед генерацией подкаста.",
noContentSelected: "Контент не выбран",
summary: "Краткое содержание",
fullContent: "Полное содержимое",
untitledSource: "Без названия",
untitledNote: "Без названия",
episodeSettings: "Настройки эпизода",
episodeProfile: "Профиль эпизода",
episodeProfilePlaceholder: "Выберите профиль эпизода",
episodeName: "Название эпизода",
episodeNamePlaceholder: "напр., ИИ и будущее работы",
additionalInstructions: "Дополнительные инструкции",
instructionsPlaceholder: "Любые дополнительные указания для брифинга эпизода...",
generating: "Генерация...",
generate: "Сгенерировать",
hostPlaceholder: "Ведущий {number}",
profileRequired: "Требуется профиль эпизода",
profileRequiredDesc: "Выберите профиль эпизода перед генерацией подкаста.",
nameRequired: "Требуется название эпизода",
nameRequiredDesc: "Укажите название эпизода.",
addContext: "Добавить контекст",
addContextDesc: "Выберите хотя бы один источник или заметку для включения в эпизод.",
generationFailed: "Генерация подкаста не удалась",
speakerProfile: "Профиль говорящего",
usesSpeakerProfile: "Использует профиль говорящего",
sources: "Источники",
notes: "Заметки",
noSources: "В этом блокноте нет доступных источников.",
noNotes: "В этом блокноте нет доступных заметок.",
selectMode: "Выберите режим",
buildContextFailed: "Не удалось построить контекст. Проверьте выбранные элементы.",
podcastTaskStarted: "Задача подкаста запущена",
loadingProfiles: "Загрузка профилей эпизодов...",
noProfilesFound: "Профили эпизодов не найдены. Создайте профиль перед генерацией подкаста.",
listTitle: "Подкасты",
listDesc: "Отслеживайте сгенерированные эпизоды и управляйте шаблонами.",
chooseAView: "Выберите представление",
episodesTab: "Эпизоды",
templatesTab: "Шаблоны",
overviewTitle: "Обзор эпизодов",
overviewDesc: "Отслеживайте задачи генерации подкастов и просматривайте готовые материалы.",
generateBtn: "Сгенерировать подкаст",
total: "Всего",
processingLabel: "В обработке",
completedLabel: "Завершено",
failedLabel: "Ошибка",
pendingLabel: "Ожидание",
loadErrorTitle: "Не удалось загрузить эпизоды",
loadErrorDesc: "Не удалось получить последние эпизоды подкастов. Попробуйте позже.",
loadingEpisodes: "Загрузка эпизодов…",
noEpisodesYet: "Пока нет эпизодов подкастов. Сгенерируйте первый из интерфейса чата блокнота или источника.",
statusRunningTitle: "В процессе",
statusRunningDesc: "Эпизоды, для которых активно генерируются материалы.",
statusPendingTitle: "В очереди / Ожидание",
statusPendingDesc: "Отправленные эпизоды, ожидающие начала обработки.",
statusCompletedTitle: "Завершённые эпизоды",
statusCompletedDesc: "Готовы к просмотру, загрузке или публикации.",
statusFailedTitle: "Неудачные эпизоды",
statusFailedDesc: "Эпизоды с ошибками во время генерации.",
templatesWorkspaceTitle: "Рабочее пространство шаблонов",
templatesWorkspaceDesc: "Создавайте переиспользуемые конфигурации эпизодов и говорящих для быстрого производства подкастов.",
howTemplatesPowerTitle: "Как шаблоны ускоряют генерацию подкастов",
howTemplatesPowerDesc: "Шаблоны разделяют процесс на два переиспользуемых компонента. Комбинируйте их при генерации нового эпизода.",
episodeProfilesSetFormat: "Профили эпизодов задают формат",
episodeProfilesList1: "Определяют количество сегментов и структуру повествования",
episodeProfilesList2: "Выбирают языковые модели для брифинга, планирования и написания сценария",
episodeProfilesList3: "Хранят стандартные брифинги для единообразного стиля эпизодов",
speakerProfilesBringVoices: "Профили говорящих оживляют голоса",
speakerProfilesList1: "Выбирают провайдера и модель озвучивания",
speakerProfilesList2: "Фиксируют личность, биографию и заметки о произношении для каждого говорящего",
speakerProfilesList3: "Переиспользуют одни и те же голоса ведущих или гостей в разных форматах эпизодов",
recommendedWorkflow: "Рекомендуемый рабочий процесс",
workflowStep1: "Создайте профили говорящих для каждого нужного голоса",
workflowStep2: "Создайте профили эпизодов со ссылками на говорящих по имени",
workflowStep3: "Генерируйте подкасты, выбирая подходящий профиль эпизода",
workflowHint: "Профили эпизодов ссылаются на профили говорящих по имени, поэтому начинайте с говорящих, чтобы избежать пропущенных назначений голосов.",
failedToLoadTemplates: "Не удалось загрузить данные шаблонов",
failedToLoadTemplatesDesc: "Убедитесь, что API работает, и попробуйте снова. Некоторые разделы могут быть неполными.",
loadingTemplates: "Загрузка шаблонов…",
speakerProfilesTitle: "Профили говорящих",
speakerProfilesDesc: "Настройте голоса и личности для генерируемых эпизодов.",
createSpeaker: "Создать говорящего",
noSpeakerProfiles: "Пока нет профилей говорящих. Создайте один, чтобы шаблоны эпизодов стали доступны.",
noDescription: "Описание не указано.",
usedByCount_one: "Используется в 1 эпизоде",
usedByCount_other: "Используется в {count} эпизодах",
usedByCount: "Используется в {count} эпизодах",
unused: "Не используется",
voiceId: "ID голоса",
backstory: "Биография",
personality: "Личность",
edit: "Редактировать",
duplicate: "Дублировать",
deleteSpeakerProfileTitle: "Удалить профиль говорящего?",
deleteSpeakerProfileDesc: "Удаление «{name}» нельзя отменить.",
deleteSpeakerDisabledHint: "Сначала удалите этого говорящего из профилей эпизодов.",
deleting: "Удаление…",
episodeProfilesTitle: "Профили эпизодов",
episodeProfilesDesc: "Определите переиспользуемые настройки генерации для ваших шоу.",
createProfile: "Создать профиль",
createSpeakerFirst: "Сначала создайте профиль говорящего перед добавлением профиля эпизода.",
noEpisodeProfiles: "Пока нет профилей эпизодов. Создайте один для запуска генерации подкастов.",
speakerCreated: "Говорящий создан",
speakerCreatedDesc: "Говорящий «{name}» успешно добавлен.",
failedToCreateSpeaker: "Не удалось создать профиль говорящего",
speakerUpdated: "Говорящий обновлён",
speakerUpdatedDesc: "Говорящий «{name}» успешно обновлён.",
failedToUpdateSpeaker: "Не удалось обновить профиль говорящего",
speakerDeleted: "Говорящий удалён",
speakerDeletedDesc: "Говорящий «{name}» успешно удалён.",
failedToDeleteSpeaker: "Не удалось удалить профиль говорящего",
speakerDuplicated: "Говорящий дублирован",
speakerDuplicatedDesc: "Говорящий «{name}» успешно дублирован.",
failedToDuplicateSpeaker: "Не удалось дублировать профиль говорящего",
generationStarted: "Генерация запущена",
generationStartedDesc: "Генерация подкаста поставлена в очередь.",
failedToStartGeneration: "Не удалось запустить генерацию",
tryAgainMoment: "Попробуйте ещё раз через некоторое время.",
deleteProfileTitle: "Удалить профиль?",
deleteProfileDesc: "Это удалит «{name}». Существующие эпизоды сохранят свои данные, но новые больше не смогут использовать эту конфигурацию.",
profileCreated: "Профиль создан",
profileCreatedDesc: "Профиль эпизода «{name}» успешно создан.",
failedToCreateProfile: "Не удалось создать профиль",
profileUpdated: "Профиль обновлён",
profileUpdatedDesc: "Профиль эпизода «{name}» успешно обновлён.",
failedToUpdateProfile: "Не удалось обновить профиль",
profileDeleted: "Профиль удалён",
profileDeletedDesc: "Профиль эпизода «{name}» успешно удалён.",
failedToDeleteProfile: "Не удалось удалить профиль",
failedToDeleteProfileDesc: "Не удалось удалить профиль эпизода.",
profileDuplicated: "Профиль дублирован",
profileDuplicatedDesc: "Профиль эпизода «{name}» успешно дублирован.",
failedToDuplicateProfile: "Не удалось дублировать профиль",
episodeDeleted: "Эпизод удалён",
episodeDeletedDesc: "Эпизод успешно удалён.",
failedToDeleteEpisode: "Не удалось удалить эпизод",
failedToDeleteSpeakerDesc: "Не удалось удалить профиль говорящего.",
outlineModel: "Модель плана",
transcriptModel: "Модель транскрипта",
segments: "Сегменты",
defaultBriefingTitle: "Брифинг по умолчанию",
created: "Создано {time}",
details: "Детали",
summaryTab: "Краткое",
outlineTab: "План",
transcriptTab: "Транскрипт",
briefing: "Брифинг",
noOutline: "План недоступен.",
noTranscript: "Транскрипт недоступен.",
deleteEpisodeTitle: "Удалить эпизод?",
deleteEpisodeDesc: "Это навсегда удалит «{name}» и его аудиофайл.",
audioUnavailable: "Аудио недоступно",
segment: "Сегмент",
speaker: "Говорящий",
profile: "Профиль",
link: "Ссылка",
file: "Файл",
embedded: "С эмбеддингом",
notEmbedded: "Без эмбеддинга",
noSpeakerProfilesAvailable: "Нет доступных профилей говорящих",
noLanguageModelsAvailable: "Нет доступных языковых моделей",
editEpisodeProfile: "Редактировать профиль эпизода",
createEpisodeProfile: "Создать профиль эпизода",
episodeProfileFormDesc: "Определите, как должны генерироваться эпизоды и какую конфигурацию говорящих использовать по умолчанию.",
noSpeakerProfilesDesc: "Создайте профиль говорящего перед настройкой профиля эпизода.",
noLanguageModelsDesc: "Добавьте языковые модели в разделе «Модели» для настройки генерации плана и транскрипта.",
profileName: "Название профиля",
profileNamePlaceholder: "напр., Техническая дискуссия",
descriptionPlaceholder: "Краткое описание, когда использовать этот профиль",
speakerConfig: "Конфигурация говорящих",
selectSpeakerProfile: "Выберите профиль говорящего",
outlineGeneration: "Генерация плана",
transcriptGeneration: "Генерация транскрипта",
defaultBriefingPlaceholder: "Опишите структуру, тон и цели для этого формата эпизода",
editSpeakerProfile: "Редактировать профиль говорящего",
createSpeakerProfile: "Создать профиль говорящего",
speakerProfileFormDesc: "Настройте параметры озвучивания и определите до четырёх говорящих.",
noTtsModelsAvailable: "Нет доступных моделей озвучивания",
noTtsModelsDesc: "Добавьте TTS-модели в разделе «Модели» перед созданием профиля говорящего.",
speakers: "Говорящие",
speakersDesc: "Настройте от одного до четырёх голосов для этого профиля.",
addSpeaker: "Добавить говорящего",
speakerNumber: "Говорящий {number}",
backstoryPlaceholder: "Краткая биография или контекст для говорящего",
personalityPlaceholder: "Опишите стиль и тон",
outlineProviderRequired: "Требуется провайдер плана",
outlineModelRequired: "Требуется модель плана",
transcriptProviderRequired: "Требуется провайдер транскрипта",
transcriptModelRequired: "Требуется модель транскрипта",
defaultBriefingRequired: "Требуется брифинг по умолчанию",
segmentsInteger: "Должно быть целым числом",
segmentsMin: "Минимум 3 сегмента",
segmentsMax: "Максимум 20 сегментов",
voiceIdRequired: "Требуется ID голоса",
backstoryRequired: "Требуется биография",
personalityRequired: "Требуется описание личности",
speakerCountMin: "Требуется минимум один говорящий",
speakerCountMax: "Можно настроить до 4 говорящих",
delete: "Удалить",
unknown: "Неизвестно",
deleteSuccess: "Подкаст успешно удалён",
failedToDelete: "Не удалось удалить подкаст",
},
settings: {
contentProcessing: "Обработка контента",
contentProcessingDesc: "Настройте обработку документов и URL",
docEngine: "Движок обработки документов",
docEnginePlaceholder: "Выберите движок обработки документов",
urlEngine: "Движок обработки URL",
urlEnginePlaceholder: "Выберите движок обработки URL",
autoRecommended: "Авто (рекомендуется)",
simple: "Простой",
docling: "Docling",
helpMeChoose: "Помогите выбрать",
docHelp: "· Docling немного медленнее, но точнее, особенно для документов с таблицами и изображениями. · Simple извлекает содержимое без форматирования. · Авто (рекомендуется) попробует Docling и переключится на Simple при необходимости.",
firecrawl: "Firecrawl",
jina: "Jina",
urlHelp: "· Firecrawl — платный сервис (есть бесплатный уровень), очень мощный. · Jina тоже хороший вариант с бесплатным уровнем. · Simple использует базовое HTTP-извлечение и пропустит контент на JavaScript-сайтах. · Авто (рекомендуется) попробует Firecrawl, затем Jina, затем Simple.",
embeddingAndSearch: "Эмбеддинг и поиск",
embeddingAndSearchDesc: "Настройте параметры поиска и эмбеддинга",
defaultEmbeddingOption: "Опция эмбеддинга по умолчанию",
embeddingOptionPlaceholder: "Выберите опцию эмбеддинга",
ask: "Спрашивать",
always: "Всегда",
never: "Никогда",
embeddingHelp: "Эмбеддинг контента упрощает поиск для вас и ИИ-агентов. Если вы используете локальную модель эмбеддинга (например, Ollama), можно не беспокоиться о стоимости и создавать эмбеддинг для всего.",
fileManagement: "Управление файлами",
fileManagementDesc: "Настройте обработку и хранение файлов",
autoDeleteFiles: "Автоудаление файлов",
autoDeletePlaceholder: "Выберите опцию автоудаления",
filesHelp: "После загрузки и обработки файлы больше не нужны. Большинству пользователей следует разрешить Open Notebook автоматически удалять загруженные файлы из папки загрузок.",
loadFailed: "Не удалось загрузить настройки",
},
advanced: {
title: "Дополнительные инструменты",
desc: "Расширенные инструменты и утилиты для опытных пользователей",
systemInfo: "Информация о системе",
systemInfoDesc: "Просмотр состояния системных компонентов",
rebuildEmbeddings: "Пересоздать эмбеддинги",
rebuildEmbeddingsDesc: "Пересоздать индекс векторного поиска для всех источников",
rebuildWarning: "Это действие может занять много времени в зависимости от количества источников. Существующие векторные индексы будут очищены и эмбеддинги будут созданы заново.",
startRebuild: "Начать пересоздание",
rebuilding: "Пересоздание...",
rebuildSuccess: "Пересоздание эмбеддингов успешно запущено",
currentVersion: "Текущая версия",
latestVersion: "Последняя версия",
status: "Статус",
updateAvailable: "Доступна версия {version}",
updateAvailableDesc: "Доступна новая версия Open Notebook.",
upToDate: "Актуальная версия",
unknown: "Неизвестно",
viewOnGithub: "Посмотреть на GitHub",
updateCheckFailed: "Не удалось проверить обновления. GitHub может быть недоступен.",
rebuild: {
mode: "Режим пересоздания",
existing: "Существующие",
all: "Все",
existingDesc: "Пересоздать эмбеддинги только для элементов с существующими эмбеддингами (быстрее, для смены модели)",
allDesc: "Пересоздать существующие + создать эмбеддинги для элементов без них (медленнее, полный охват)",
include: "Включить в пересоздание",
selectOneError: "Выберите хотя бы один тип элементов для пересоздания",
starting: "Запуск пересоздания...",
startBtn: "🚀 Начать пересоздание",
queued: "В очереди",
running: "Отправка задач...",
completed: "Задачи отправлены!",
failed: "Ошибка",
leavePageHint: "Вы можете покинуть эту страницу — процесс выполняется в фоне",
startNew: "Начать новое пересоздание",
itemsProcessed: "Отправлено задач: {processed}/{total} ({percent}%)",
failedItems: "Не удалось отправить задач: {count}",
time: "Время",
whenToRebuild: "Когда нужно пересоздавать эмбеддинги?",
whenToRebuildAns: "При смене модели, обновлении версии, исправлении повреждений или после массового импорта.",
howLong: "Сколько времени занимает пересоздание?",
howLongAns: "Время зависит от количества элементов, скорости модели и лимитов API. Локальные модели обычно очень быстрые.",
isSafe: "Безопасно ли пересоздавать во время работы с приложением?",
isSafeAns: "Да, пересоздание безопасно! Оно не удаляет контент, только заменяет эмбеддинги, и корректно обрабатывает ошибки.",
},
},
transformations: {
title: "Трансформации",
desc: "Трансформации — это промпты, которые LLM использует для обработки источника и извлечения инсайтов, резюме и т.д.",
workspace: "Выберите рабочее пространство",
playground: "Песочница",
defaultPrompt: "Промпт трансформации по умолчанию",
defaultPromptDesc: "Этот текст будет добавлен ко всем промптам трансформаций",
defaultPromptPlaceholder: "Введите инструкции трансформации по умолчанию...",
saveDefault: "Сохранить по умолчанию",
listTitle: "Пользовательские трансформации",
createNew: "Создать новую",
testInPlayground: "Тестировать в песочнице",
inputLabel: "Входной текст",
inputPlaceholder: "Введите текст для трансформации...",
outputLabel: "Результат",
runTest: "Запустить трансформацию",
running: "Выполнение...",
selectToStart: "Выберите трансформацию для начала",
name: "Название",
namePlaceholder: "Уникальный идентификатор, напр. key_topics",
titlePlaceholder: "Отображаемое название, по умолчанию совпадает с именем",
promptPlaceholder: "Напишите промпт для этой трансформации...",
descriptionPlaceholder: "Опишите, что делает эта трансформация.",
suggestDefault: "Предлагать по умолчанию для новых источников",
promptHint: "Промпты должны учитывать содержимое источника. Вы можете попросить модель резюмировать, извлечь инсайты или создать структурированный вывод, например таблицы.",
createSuccess: "Трансформация успешно создана",
updateSuccess: "Трансформация успешно обновлена",
deleteSuccess: "Трансформация успешно удалена",
noTransformations: "Пока нет трансформаций",
createOne: "Создайте трансформацию для начала",
deleteDesc: "Удаление трансформации нельзя отменить.",
selectModel: "Выберите модель",
deleteConfirm: "Вы уверены, что хотите удалить эту трансформацию?",
model: "Модель",
systemPrompt: "Системный промпт",
type: "Тип",
extraction: "Извлечение",
summary: "Резюме",
custom: "Пользовательский",
saveChanges: "Сохранить изменения",
overrideModelDesc: "Переопределить модель по умолчанию для этой сессии чата. Оставьте пустым для использования системной модели.",
sessionUseReplacement: "Эта сессия будет использовать {name} вместо модели по умолчанию.",
systemDefault: "Системная по умолчанию",
},
models: {
title: "Управление моделями",
desc: "Настройте ИИ-модели для различных задач в Open Notebook",
failedToLoad: "Не удалось загрузить данные моделей",
language: "Языковые модели",
embedding: "Модели эмбеддинга",
tts: "Озвучивание (TTS)",
stt: "Распознавание речи (STT)",
providers: "Провайдеры",
defaultModels: "Модели по умолчанию",
status: "Статус",
notConfigured: "Не настроено",
active: "Активно",
inactive: "Неактивно",
configure: "Настроить",
saveChanges: "Сохранить изменения",
addModel: "Добавить модель",
modelName: "Название модели",
provider: "Провайдер",
apiKey: "API-ключ",
baseUrl: "Базовый URL",
capabilities: "Возможности",
enabled: "Включено",
disabled: "Отключено",
deleteConfirm: "Вы уверены, что хотите удалить эту модель?",
deleteSuccess: "Модель успешно удалена",
saveSuccess: "Модель успешно сохранена",
providerStatus: "Статус провайдера",
connectionOk: "Подключение OK",
connectionFailed: "Ошибка подключения",
changeEmbeddingWarning: "Изменение модели эмбеддинга по умолчанию повлияет на новые источники. Существующие источники могут потребовать переиндексации.",
changeEmbeddingTitle: "Изменить модель эмбеддинга по умолчанию?",
aiProviders: "ИИ-провайдеры",
providerConfigDesc: "Настройте провайдеров через переменные окружения для включения их моделей.",
configuredCount: "Настроено {count} из {total}",
noModels: "Нет моделей",
learnMore: "Узнать, как настроить провайдеров →",
seeLess: "Свернуть",
seeAll: "Показать все {count} провайдеров",
language_models: "Языковые модели",
embedding_models: "Модели эмбеддинга",
text_to_speech: "Озвучивание (TTS)",
speech_to_text: "Распознавание речи (STT)",
languageDesc: "Чат, трансформации и генерация текста",
embeddingDesc: "Семантический поиск и векторные эмбеддинги",
ttsDesc: "Генерация аудио из текста",
sttDesc: "Транскрибация аудио в текст",
all: "Все",
noModelsConfigured: "Модели не настроены",
noProviderModelsConfigured: "Модели {provider} не настроены",
showMore: "Показать ещё {count}",
deleteModel: "Удалить модель",
deleteModelDesc: "Вы уверены, что хотите удалить «{name}»? Это действие нельзя отменить.",
defaultAssignments: "Назначение моделей по умолчанию",
defaultAssignmentsDesc: "Настройте, какие модели использовать для различных задач в Open Notebook",
missingRequiredModels: "Отсутствуют необходимые модели: {models}. Open Notebook может работать некорректно без них.",
selectModelPlaceholder: "Выберите модель",
requiredModelPlaceholder: "⚠️ Обязательно — выберите модель",
whichModelToChoose: "Какую модель выбрать? →",
chatModelLabel: "Модель чата",
chatModelDesc: "Используется для чат-разговоров",
transformationModelLabel: "Модель трансформаций",
transformationModelDesc: "Используется для резюме, инсайтов и трансформаций",
toolsModelLabel: "Модель инструментов",
toolsModelDesc: "Используется для вызова функций — рекомендуется OpenAI или Anthropic",
largeContextModelLabel: "Модель для большого контекста",
largeContextModelDesc: "Используется для обработки больших документов — рекомендуется Gemini",
embeddingModelLabel: "Модель эмбеддинга",
embeddingModelDesc: "Используется для семантического поиска и векторных эмбеддингов",
ttsModelLabel: "Модель озвучивания",
ttsModelDesc: "Используется для генерации подкастов",
sttModelLabel: "Модель распознавания речи",
sttModelDesc: "Используется для транскрибации аудио",
addSpecificModel: "Добавить модель {type}",
addSpecificModelDesc: "Настройте новую модель {type} от доступных провайдеров.",
noProvidersForType: "Нет доступных провайдеров для моделей {type}",
selectProviderPlaceholder: "Выберите провайдера",
providerRequired: "Требуется провайдер",
modelNameRequired: "Требуется название модели",
modelRequired: "Требуется модель",
adding: "Добавление...",
azureHint: "Для Azure используйте название деплоймента как название модели",
enterModelName: "Введите название модели",
embeddingChangeTitle: "Изменение модели эмбеддинга",
embeddingChangeConfirm: "Вы собираетесь изменить модель эмбеддинга с {from} на {to}.",
rebuildRequired: "Важно: Требуется пересоздание",
rebuildReason: "Изменение модели эмбеддинга требует пересоздания всех существующих эмбеддингов для сохранения согласованности. Без пересоздания поиск может возвращать некорректные или неполные результаты.",
whatHappensNext: "Что произойдёт далее:",
step1: "Модель эмбеддинга по умолчанию будет обновлена",
step2: "Существующие эмбеддинги останутся неизменными до пересоздания",
step3: "Новый контент будет использовать новую модель эмбеддинга",
step4: "Вам следует пересоздать эмбеддинги как можно скорее",
proceedToRebuildPrompt: "Хотите перейти на страницу «Дополнительно», чтобы начать пересоздание сейчас?",
changeModelOnly: "Только изменить модель",
changeAndRebuild: "Изменить и перейти к пересозданию",
}
}

View file

@ -28,6 +28,7 @@ export const zhCN = {
english: "English",
chinese: "简体中文",
japanese: "日本語",
russian: "Русский",
source: "来源",
notebook: "笔记本",
podcast: "播客",

View file

@ -28,6 +28,7 @@ export const zhTW = {
english: "English",
chinese: "簡體中文",
japanese: "日本語",
russian: "Русский",
source: "來源",
notebook: "筆記本",
podcast: "播客",

View file

@ -1,4 +1,4 @@
import { zhCN, enUS, zhTW, ptBR, ja, Locale } from 'date-fns/locale'
import { zhCN, enUS, zhTW, ptBR, ja, ru, Locale } from 'date-fns/locale'
/**
* Mapping of language codes to date-fns locales.
@ -10,6 +10,7 @@ const LOCALE_MAP: Record<string, Locale> = {
'en-US': enUS,
'pt-BR': ptBR,
'ja-JP': ja,
'ru-RU': ru,
}
/**