feat: add CI test workflow and improve i18n validation (#580)

- Add GitHub Actions workflow to run backend (pytest) and frontend
  (vitest) tests on PRs and pushes to main
- Replace hardcoded locale parity tests with dynamic discovery from
  the resources registry, so new languages are tested automatically
- Add unused key detection test that scans source files for i18n
  key references, with optional chaining normalization
- Remove 149 genuinely unused translation keys from all 7 locale files
- Add missing keys to it-IT (7) and ru-RU (5) with English fallback
This commit is contained in:
Luis Novo 2026-02-14 20:14:08 -03:00 committed by GitHub
parent 9b507f111c
commit fb21f5e777
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 444 additions and 1417 deletions

59
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,59 @@
name: Tests
on:
pull_request:
branches: [main]
push:
branches: [main]
paths-ignore:
- '**.md'
- 'docs/**'
- '.github/workflows/claude*.yml'
permissions:
contents: read
jobs:
backend:
name: Backend Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
- name: Set up Python
run: uv python install
- name: Install dependencies
run: uv sync
- name: Run tests
run: uv run pytest tests/ -v
frontend:
name: Frontend Tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test

View file

@ -36,22 +36,16 @@ export const enUS = {
warning: "Warning",
error: "Error",
success: "Success",
sessions: "Sessions",
model: "Model",
send: "Send",
back: "Back",
next: "Next",
done: "Done",
processing: "Processing...",
creating: "Creating...",
tokenCount: "Tokens",
charCount: "Chars",
linked: "Linked",
added: "Added on {date}",
adding: "Adding...",
addSelected: "Add Selected",
customModel: "Custom Model",
messages: "Messages",
failed: "failed",
current: "Current",
save: "Save",
@ -72,7 +66,6 @@ export const enUS = {
unknown: "Unknown",
notes: "Notes",
chat: "Chat",
details: "Details",
deleteForever: "Delete Forever",
connectionError: "Connection Error",
unableToConnect: "Unable to connect to the API server",
@ -85,7 +78,6 @@ export const enUS = {
checkConsoleLogs: "Check browser console for detailed logs (look for 🔧 [Config] messages)",
yes: "Yes",
no: "No",
simple: "Simple",
saving: "Saving...",
description: "Description",
saveToNote: "Save to note",
@ -103,7 +95,6 @@ export const enUS = {
nameRequired: "Name is required",
modelConfiguration: "Model Configuration",
resetToDefault: "Reset to Default",
notFound: "Not found",
reasoning: "Reasoning",
searchTerms: "Search Terms",
strategy: "Strategy",
@ -112,14 +103,12 @@ export const enUS = {
notebookLabel: "Notebook: {name}",
itemNotFound: "This {type} could not be found",
accessibility: {
navigation: "Navigation",
transformationViews: "Transformation views",
searchKB: "Ask or search your knowledge base",
enterQuestion: "Enter your question to ask the knowledge base",
enterSearch: "Enter search query",
searchKBBtn: "Search knowledge base",
podcastViews: "Podcast views",
chatSessions: "Chat sessions",
ytVideo: "YouTube video",
askResponse: "Ask Response",
searchNotebooks: "Search notebooks",
@ -127,7 +116,6 @@ export const enUS = {
url: "URL",
errorDetails: "Error Details",
editTransformation: "Edit Transformation",
comingSoon: "Coming soon",
retry: "Try Again",
traditionalChinese: "繁體中文",
portuguese: "Português",
@ -165,7 +153,6 @@ export const enUS = {
failedToSendMessage: "Failed to send message",
unauthorized: "Unauthorized access, please check your password",
invalidPassword: "Invalid password",
missingAuth: "Missing authorization",
embeddingModelRequired: "This feature requires an embedding model. Please configure one in the Models section.",
strategyModelNotFound: "Strategy model not found",
answerModelNotFound: "Answer model not found",
@ -206,7 +193,6 @@ export const enUS = {
passwordPlaceholder: "Password",
signingIn: "Signing in...",
signIn: "Sign In",
unhandledError: "Unhandled error during login",
connectErrorHint: "Unable to connect to server. Please check if the API is running.",
},
navigation: {
@ -226,7 +212,6 @@ export const enUS = {
nav: "Navigation",
language: "Toggle language",
theme: "Theme",
search: "Search",
ask: "Ask",
},
notebooks: {
@ -248,12 +233,8 @@ export const enUS = {
keepExclusiveSourcesLabel: "Unlink and keep them",
activeNotebooks: "Active Notebooks",
archivedNotebooks: "Archived Notebooks",
emptyDescription: "Start by creating your first notebook to organize your research.",
noActiveNotebooks: "No active notebooks",
noArchivedNotebooks: "No archived notebooks",
notFound: "Notebook not found",
notFoundDesc: "The requested notebook does not exist.",
noDescription: "No description...",
updated: "Updated",
namePlaceholder: "Notebook name",
addDescription: "Add description...",
@ -278,10 +259,7 @@ export const enUS = {
add: "Add Source",
addNew: "Add New Source",
addExisting: "Add Existing Source",
empty: "No sources yet",
emptyDesc: "Add your first source to start building your knowledge base.",
delete: "Delete Source",
deleteMsg: "Are you sure you want to delete this source? This action cannot be undone.",
statusPreparing: "Preparing",
statusQueued: "Queued",
statusProcessing: "Processing",
@ -321,7 +299,6 @@ export const enUS = {
sourceRequeued: "Source Retry Queued",
sourceRequeuedDesc: "The source has been requeued for processing.",
failedToRetry: "Retry Failed",
failedToRetryDesc: "Failed to retry source processing. Please try again.",
sourcesAddedToNotebook: "{count} source(s) added to notebook",
failedToAddSourcesToNotebook: "Failed to add sources to notebook",
partialAddSuccess: "{success} source(s) added, {failed} failed",
@ -359,34 +336,31 @@ export const enUS = {
deleteInsight: "Delete Insight",
deleteInsightConfirm: "Are you sure you want to delete this insight? This action cannot be undone.",
insightGenerationStarted: "Insight generation started. It will appear shortly.",
deleteNoteConfirm: 'Are you sure you want to delete this note? This action cannot be undone.',
editNote: 'Edit note',
createNote: 'Create note',
addTitle: 'Add a title...',
untitledNote: 'Untitled Note',
writeNotePlaceholder: 'Write your note content here...',
saveNote: 'Save Note',
createNoteBtn: 'Create Note',
noNotesYet: "No notes yet",
editNote: "Edit note",
createNote: "Create note",
addTitle: "Add a title...",
untitledNote: "Untitled Note",
writeNotePlaceholder: "Write your note content here...",
saveNote: "Save Note",
createNoteBtn: "Create Note",
createFirstNote: "Create your first note to capture insights and observations.",
deleteNote: "Delete Note",
urlLabel: 'URL(s) *',
fileLabel: 'File(s) *',
textContentLabel: 'Text Content *',
enterUrlsPlaceholder: 'Enter URLs, one per line\nhttps://example.com/article1\nhttps://example.com/article2',
batchUrlHint: 'Paste multiple URLs (one per line) to batch import',
invalidUrlsDetected: 'Invalid URLs detected:',
lineLabel: 'Line {line}',
fixInvalidUrls: 'Please fix or remove invalid URLs to continue',
selectMultipleFilesHint: 'Select multiple files to batch import. Supported: Documents (PDF, DOC, DOCX, PPT, XLS, EPUB, TXT, MD), Media (MP4, MP3, WAV, M4A), Images (JPG, PNG), Archives (ZIP)',
selectedFiles: 'Selected files:',
textPlaceholder: 'Paste or type your content here...',
htmlDetected: 'HTML content detected. It will be converted to Markdown after processing.',
titlePlaceholder: 'Give your source a descriptive title',
batchTitlesAuto: 'Titles will be automatically generated for each source.',
batchCommonSettings: 'The same notebooks and transformations will be applied to all items.',
urlsCount: '{count} URL(s)',
filesCount: '{count} file(s)',
urlLabel: "URL(s) *",
fileLabel: "File(s) *",
textContentLabel: "Text Content *",
enterUrlsPlaceholder: "Enter URLs, one per line\nhttps://example.com/article1\nhttps://example.com/article2",
batchUrlHint: "Paste multiple URLs (one per line) to batch import",
invalidUrlsDetected: "Invalid URLs detected:",
lineLabel: "Line {line}",
fixInvalidUrls: "Please fix or remove invalid URLs to continue",
selectMultipleFilesHint: "Select multiple files to batch import. Supported: Documents (PDF, DOC, DOCX, PPT, XLS, EPUB, TXT, MD), Media (MP4, MP3, WAV, M4A), Images (JPG, PNG), Archives (ZIP)",
selectedFiles: "Selected files:",
textPlaceholder: "Paste or type your content here...",
htmlDetected: "HTML content detected. It will be converted to Markdown after processing.",
titlePlaceholder: "Give your source a descriptive title",
batchTitlesAuto: "Titles will be automatically generated for each source.",
batchCommonSettings: "The same notebooks and transformations will be applied to all items.",
urlsCount: "{count} URL(s)",
filesCount: "{count} file(s)",
addSource: "Add Source",
notEmbeddedAlert: "Content Not Embedded",
notEmbeddedDesc: "This content hasn't been embedded for vector search. Embedding enables advanced search capabilities and better content discovery.",
@ -403,7 +377,6 @@ export const enUS = {
retryProcessing: "Retry Processing",
deleteSource: "Delete Source",
retry: "Retry",
progress: "Progress",
addExistingTitle: "Add Existing Sources",
addExistingDesc: "Select existing sources from across all your notebooks to add to the current one.",
searchPlaceholder: "Search sources by name or URL...",
@ -435,8 +408,6 @@ export const enUS = {
batchFailed: "Failed to create all {count} sources",
batchPartial: "{success} succeeded, {failed} failed",
submittingSource: "Submitting source for processing...",
contentRequired: "Please provide the required content for the selected source type",
titleRequiredForText: "Title is required for text sources",
processingBatchSources: "Processing {count} sources. This may take a few moments.",
processingSource: "Your source is being processed. This may take a few moments.",
maxFilesAllowed: "Maximum {count} files allowed per batch",
@ -445,26 +416,20 @@ export const enUS = {
sessions: "Sessions",
sessionTitlePlaceholder: "Type a title here...",
noSessions: "No chat sessions yet",
startChatting: "Start chatting about your sources.",
deleteSession: "Delete Session",
deleteSessionDesc: "Are you sure you want to delete this chat session? This action cannot be undone.",
sendPlaceholder: "Ask anything about your sources...",
newChat: "New Chat",
sessionsTitle: "Chat Sessions",
clearhistory: "Clear History",
renameSession: "Rename Session",
noSourcesLinked: "No sources linked",
thinking: "AI is thinking...",
chatWith: "Chat with {name}",
startConversation: "Start a conversation about this {type}",
askQuestions: "Ask questions to understand the content better",
pressToSend: "Press {key} to send",
model: "Model",
createToStart: 'Create a session to start.',
chatWithNotebook: 'Chat with Notebook',
unableToLoadChat: 'Unable to load chat',
noDescription: 'No description',
startByCreating: 'Start by creating your first notebook to organize your research.',
createToStart: "Create a session to start.",
chatWithNotebook: "Chat with Notebook",
unableToLoadChat: "Unable to load chat",
noDescription: "No description",
startByCreating: "Start by creating your first notebook to organize your research.",
messagesCount: "{count} messages",
sessionCreated: "Chat session created",
sessionUpdated: "Session updated",
@ -508,8 +473,6 @@ export const enUS = {
saveSuccess: "Successfully saved to notebook",
saveError: "Failed to save to notebook",
selectNotebook: "Select Notebook",
createNewNotebook: "Create New Notebook",
cancel: "Cancel",
searchAndAsk: "Search & Ask",
searchResultsFor: "Search results for “{query}”",
askAbout: "Ask about “{query}”",
@ -734,8 +697,6 @@ export const enUS = {
speakerCountMin: "At least one speaker is required",
speakerCountMax: "You can configure up to 4 speakers",
delete: "Delete",
unknown: "Unknown",
deleteSuccess: "Podcast deleted successfully",
failedToDelete: "Failed to delete podcast",
},
settings: {
@ -772,13 +733,8 @@ export const enUS = {
title: "AdvancedTools",
desc: "Advanced tools and utilities for power users",
systemInfo: "System Info",
systemInfoDesc: "View the status of underlying system components",
rebuildEmbeddings: "Rebuild Embeddings",
rebuildEmbeddingsDesc: "Rebuild vector search index for all sources",
rebuildWarning: "This action can be very time-consuming depending on the number of sources you have. It will clear existing vector indices and re-generate embeddings for everything.",
startRebuild: "Start Rebuild",
rebuilding: "Rebuilding...",
rebuildSuccess: "Embedding rebuild started successfully",
currentVersion: "Current Version",
latestVersion: "Latest Version",
status: "Status",
@ -823,105 +779,53 @@ export const enUS = {
defaultPrompt: "Default Transformation Prompt",
defaultPromptDesc: "This will be added to all your transformation prompts",
defaultPromptPlaceholder: "Enter your default transformation instructions...",
saveDefault: "Save Default",
listTitle: "Custom Transformations",
createNew: "Create New",
testInPlayground: "Test in Playground",
inputLabel: "Input Text",
inputPlaceholder: 'Enter some text to transform...',
outputLabel: 'Output',
runTest: 'Run Transformation',
running: 'Running...',
selectToStart: 'Select a transformation to start',
name: 'Name',
namePlaceholder: 'Unique identifier, e.g. key_topics',
titlePlaceholder: 'Displayed title, defaults to name',
promptPlaceholder: 'Write the prompt that will power this transformation...',
descriptionPlaceholder: 'Describe what this transformation does.',
suggestDefault: 'Suggest by default on new sources',
promptHint: 'Prompts should be written with the source content in mind. You can ask the model to summarise, extract insights, or produce structured outputs such as tables.',
createSuccess: 'Transformation created successfully',
updateSuccess: 'Transformation updated successfully',
deleteSuccess: 'Transformation deleted successfully',
inputPlaceholder: "Enter some text to transform...",
outputLabel: "Output",
runTest: "Run Transformation",
running: "Running...",
selectToStart: "Select a transformation to start",
name: "Name",
namePlaceholder: "Unique identifier, e.g. key_topics",
titlePlaceholder: "Displayed title, defaults to name",
promptPlaceholder: "Write the prompt that will power this transformation...",
descriptionPlaceholder: "Describe what this transformation does.",
suggestDefault: "Suggest by default on new sources",
promptHint: "Prompts should be written with the source content in mind. You can ask the model to summarise, extract insights, or produce structured outputs such as tables.",
createSuccess: "Transformation created successfully",
updateSuccess: "Transformation updated successfully",
deleteSuccess: "Transformation deleted successfully",
noTransformations: "No transformations yet",
createOne: "Create a transformation to get started",
deleteDesc: "Deleting this transformation cannot be undone.",
selectModel: "Select a model",
deleteConfirm: "Are you sure you want to delete this transformation?",
model: "Model",
systemPrompt: "System Prompt",
type: "Type",
extraction: "Extraction",
summary: "Summary",
custom: "Custom",
saveChanges: "Save Changes",
overrideModelDesc: "Override the default model for this chat session. Leave empty to use the system default.",
sessionUseReplacement: "This session will use {name} instead of the default model.",
systemDefault: "System Default",
},
models: {
title: "Model Management",
desc: "Configure AI models for different purposes across Open Notebook",
failedToLoad: "Failed to load models data",
language: "Language Models",
embedding: "Embedding Models",
tts: "Text to Speech (TTS)",
stt: "Speech to Text (STT)",
providers: "Providers",
defaultModels: "Default Models",
status: "Status",
notConfigured: "Not configured",
active: "Active",
inactive: "Inactive",
configure: "Configure",
saveChanges: "Save Changes",
addModel: "Add Model",
modelName: "Model Name",
provider: "Provider",
apiKey: "API Key",
baseUrl: "Base URL",
capabilities: "Capabilities",
enabled: "Enabled",
disabled: "Disabled",
deleteConfirm: "Are you sure you want to delete this model?",
deleteSuccess: "Model deleted successfully",
saveSuccess: "Model saved successfully",
providerStatus: "Provider Status",
connectionOk: "Connection OK",
connectionFailed: "Connection failed",
changeEmbeddingWarning: "Changing the default embedding model will affect new sources. Existing sources might need to be re-indexed.",
changeEmbeddingTitle: "Change Default Embedding Model?",
aiProviders: "AI Providers",
providerConfigDesc: "Configure providers through environment variables to enable their models.",
configuredCount: "{count} of {total} configured",
noModels: "No models",
learnMore: "Learn how to configure providers →",
seeLess: "See less",
seeAll: "See all {count} providers",
language_models: "Language Models",
embedding_models: "Embedding Models",
text_to_speech: "Text to Speech (TTS)",
speech_to_text: "Speech to Text (STT)",
languageDesc: "Chat, transformations, and text generation",
embeddingDesc: "Semantic search and vector embeddings",
ttsDesc: "Generate audio from text",
sttDesc: "Transcribe audio to text",
all: "All",
noModelsConfigured: "No models configured",
noProviderModelsConfigured: "No {provider} models configured",
showMore: "Show {count} more",
discoverModels: "Discover Models",
noModelsFound: "No models found from this provider",
modelType: "Model Type",
modelTypeHint: "Select the type for the models you want to add. If you need different types, add them in separate batches.",
deleteModel: "Delete Model",
deleteModelDesc: "Are you sure you want to delete \"{name}\"? This action cannot be undone.",
defaultAssignments: "Default Model Assignments",
defaultAssignmentsDesc: "Configure which models to use for different purposes across Open Notebook",
missingRequiredModels: "Missing required models: {models}. Open Notebook may not function properly without these.",
selectModelPlaceholder: "Select a model",
requiredModelPlaceholder: "⚠️ Required - Select a model",
whichModelToChoose: "Which model should I choose? →",
chatModelLabel: "Chat Model",
chatModelDesc: "Used for chat conversations",
transformationModelLabel: "Transformation Model",
@ -936,16 +840,9 @@ export const enUS = {
ttsModelDesc: "Used for podcast generation",
sttModelLabel: "Speech-to-Text Model",
sttModelDesc: "Used for audio transcription",
addSpecificModel: "Add {type} Model",
addSpecificModelDesc: "Configure a new {type} model from available providers.",
noProvidersForType: "No providers available for {type} models",
selectProviderPlaceholder: "Select a provider",
providerRequired: "Provider is required",
modelNameRequired: "Model name is required",
modelRequired: "Model is required",
adding: "Adding...",
azureHint: "For Azure, use the deployment name as the model name",
enterModelName: "Enter model name",
embeddingChangeTitle: "Embedding Model Change",
embeddingChangeConfirm: "You are about to change your embedding model from {from} to {to}.",
rebuildRequired: "Important: Rebuild Required",
@ -959,7 +856,6 @@ export const enUS = {
changeModelOnly: "Change Model Only",
changeAndRebuild: "Change & Go to Rebuild",
autoAssign: "Auto-assign Defaults",
autoAssignDesc: "Automatically assign the best available model for each slot",
autoAssigning: "Assigning...",
autoAssignSuccess: "{count} default models automatically assigned",
autoAssignNoModels: "No models available to assign. Please sync models first.",
@ -967,25 +863,16 @@ export const enUS = {
testModel: "Test Model",
testModelSuccess: "Model Test Passed",
testModelFailed: "Model Test Failed",
testingModel: "Testing model...",
searchOrAddModel: "Search or type a model name...",
addCustomModel: 'Add "{name}"',
addCustomModel: "Add \"{name}\"",
},
apiKeys: {
title: "Configure your AI with your own API keys",
description: "Store API keys securely in the database to enable AI providers in Open Notebook.",
loadFailed: "Failed to load API keys status",
encryptionRequired: "Encryption key not configured",
encryptionRequiredDescription: "Set the OPEN_NOTEBOOK_ENCRYPTION_KEY environment variable to any secret string to enable storing API keys in the database.",
configured: "Configured",
notConfigured: "Not configured",
sourceDatabase: "Database",
sourceEnvironment: "Environment",
enterApiKey: "Enter your API key",
enterBaseUrl: "Enter the base URL",
saveSuccess: "API key saved successfully",
deleteSuccess: "API key deleted successfully",
fromEnvironmentHint: "This key is set via environment variable. Save a new key to override it in the database.",
migrationAvailable: "Environment Variables Detected",
migrationDescription: "{count} API key(s) are configured via environment variables and can be migrated to the database for easier management.",
migrateToDatabase: "Migrate to Database",
@ -993,67 +880,29 @@ export const enUS = {
migrationSuccess: "{count} API key(s) migrated successfully",
migrationErrors: "{count} key(s) failed to migrate",
migrationNothingToMigrate: "All keys are already in the database",
serviceType: "Service Type",
serviceLlm: "Language Model (LLM)",
serviceEmbedding: "Embedding",
serviceStt: "Speech to Text (STT)",
serviceTts: "Text to Speech (TTS)",
serviceEndpoints: "Service Endpoints (optional)",
azureEndpointsHint: "Configure different endpoints for each service type if needed.",
endpointPlaceholder: "https://your-resource.openai.azure.com/",
openaiCompatibleHint: "Configure an OpenAI-compatible API endpoint. Each service type can have its own configuration.",
baseUrlPlaceholder: "https://api.example.com/v1",
learnMore: "Learn how to configure API keys →",
testConnection: "Test Connection",
testing: "Testing...",
testSuccess: "Connection successful",
testFailed: "Connection test failed",
syncModels: "Sync Models",
syncing: "Syncing...",
syncSuccess: "Discovered {discovered} models, added {new} new",
syncNoNew: "Discovered {count} models, all already registered",
syncFailed: "Failed to sync models",
syncAllModels: "Sync All Providers",
syncAllSuccess: "Discovered {discovered} models across all providers, added {new} new",
modelsConfigured: "{count} models",
noModelsConfigured: "No models",
viewModels: "View Models",
supportedTypes: "Supported types",
typeLanguage: "Language",
typeEmbedding: "Embedding",
typeTts: "TTS",
typeStt: "STT",
apiEndpoint: "API Endpoint",
getApiKey: "Get API Key",
vertexProject: "GCP Project ID",
vertexLocation: "Region",
vertexCredentials: "Service Account JSON Path",
vertexCredentialsHint: "Path to your Google Cloud service account JSON file inside the container.",
// Multi-config translations
configsCount: "{count} configs",
configuredMultiple: "Configured",
addConfig: "Add Configuration",
editConfig: "Edit Configuration",
deleteConfig: "Delete Configuration",
setAsDefault: "Set as Default",
defaultBadge: "Default",
defaultDescription: "Default configuration for this provider",
configName: "Configuration Name",
configNameHint: "A descriptive name for this configuration (e.g., 'Production', 'Development')",
baseUrl: "Base URL",
baseUrlHint: "Default: {url}",
baseUrlOverrideHint: "Only change this if you need to override the provider's default API endpoint.",
ollamaApiKeyHint: "Only required for Ollama Cloud. Leave empty for local Ollama.",
noConfigs: "No configurations yet",
noConfigsHint: "Add a configuration to start using this provider",
deleteConfigConfirm: "Are you sure you want to delete '{name}'? This cannot be undone.",
setDefaultConfirm: "Set '{name}' as the default configuration?",
configSaveSuccess: "Configuration saved successfully",
configUpdateSuccess: "Configuration updated successfully",
configDeleteSuccess: "Configuration deleted successfully",
configSetDefaultSuccess: "Default configuration updated",
apiKeyHint: "Enter your API key for this configuration",
apiKeyEditHint: "Leave blank to keep the existing API key",
},
setupBanner: {

View file

@ -1,56 +1,68 @@
import { describe, it, expect } from 'vitest'
import fs from 'fs'
import path from 'path'
import { resources } from './index'
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[] => {
return Object.keys(obj).reduce((res: string[], el) => {
const val = obj[el]
if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
return [...res, ...getKeys(val as Record<string, unknown>, prefix + el + '.')]
}
return [...res, prefix + el]
}, [])
}
const getKeys = (obj: Record<string, unknown>, prefix = ''): string[] => {
return Object.keys(obj).reduce((res: string[], el) => {
const val = obj[el]
if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
return [...res, ...getKeys(val as Record<string, unknown>, prefix + el + '.')]
}
return [...res, prefix + el]
}, [])
}
describe('Locale Parity', () => {
const enKeys = getKeys(enUS)
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))
const extraInZhCN = zhCNKeys.filter(key => !enKeys.includes(key))
const locales = Object.entries(resources).filter(([code]) => code !== 'en-US')
expect(missingInZhCN, `Missing keys in zh-CN: ${missingInZhCN.join(', ')}`).toEqual([])
expect(extraInZhCN, `Extra keys in zh-CN: ${extraInZhCN.join(', ')}`).toEqual([])
})
it.each(locales.map(([code, resource]) => [code, resource] as const))(
'%s should have the same keys as en-US',
(code, resource) => {
const localeKeys = getKeys(resource.translation as Record<string, unknown>)
it('zh-TW should have the same keys as en-US', () => {
const missingInZhTW = enKeys.filter(key => !zhTWKeys.includes(key))
const extraInZhTW = zhTWKeys.filter(key => !enKeys.includes(key))
const missing = enKeys.filter(key => !localeKeys.includes(key))
const extra = localeKeys.filter(key => !enKeys.includes(key))
expect(missingInZhTW, `Missing keys in zh-TW: ${missingInZhTW.join(', ')}`).toEqual([])
expect(extraInZhTW, `Extra keys in zh-TW: ${extraInZhTW.join(', ')}`).toEqual([])
})
it('ja-JP should have the same keys as en-US', () => {
const missingInJaJP = enKeys.filter(key => !jaJPKeys.includes(key))
const extraInJaJP = jaJPKeys.filter(key => !enKeys.includes(key))
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([])
})
expect(missing, `Missing keys in ${code}: ${missing.join(', ')}`).toEqual([])
expect(extra, `Extra keys in ${code}: ${extra.join(', ')}`).toEqual([])
},
)
})
describe('Unused Key Detection', () => {
it(
'all en-US leaf keys should be referenced in source files',
() => {
const srcDir = path.resolve(__dirname, '../../..')
const localesDir = path.resolve(__dirname)
const files = fs.readdirSync(srcDir, { recursive: true }) as string[]
const sourceFiles = files.filter(f => {
const full = path.join(srcDir, f)
if (full.startsWith(localesDir)) return false
if (f.endsWith('.test.ts') || f.endsWith('.test.tsx')) return false
return f.endsWith('.ts') || f.endsWith('.tsx')
})
// Normalize optional chaining (t?.common?.key → t.common.key)
// so that keys like "common.errorDetails" match "common?.errorDetails"
const corpus = sourceFiles
.map(f => fs.readFileSync(path.join(srcDir, f), 'utf-8'))
.join('\n')
.replace(/\?\./g, '.')
const leafKeys = getKeys(enUS)
const unused = leafKeys.filter(key => !corpus.includes(key))
expect(
unused,
`Found ${unused.length} unused i18n key(s):\n${unused.join('\n')}`,
).toEqual([])
},
30_000,
)
})

View file

@ -23,7 +23,7 @@ export const itIT = {
english: "English",
chinese: "简体中文",
japanese: "日本語",
italian: "Italiano",
russian: "Русский",
source: "Fonte",
notebook: "Quaderno",
podcast: "Podcast",
@ -36,22 +36,16 @@ export const itIT = {
warning: "Attenzione",
error: "Errore",
success: "Successo",
sessions: "Sessioni",
model: "Modello",
send: "Invia",
back: "Indietro",
next: "Avanti",
done: "Fatto",
processing: "Elaborazione...",
creating: "Creazione...",
tokenCount: "Token",
charCount: "Caratteri",
linked: "Collegato",
added: "Aggiunto il {date}",
adding: "Aggiunta in corso...",
addSelected: "Aggiungi selezionati",
customModel: "Modello personalizzato",
messages: "Messaggi",
failed: "fallito",
current: "Corrente",
save: "Salva",
@ -72,7 +66,6 @@ export const itIT = {
unknown: "Sconosciuto",
notes: "Note",
chat: "Chat",
details: "Dettagli",
deleteForever: "Elimina definitivamente",
connectionError: "Errore di connessione",
unableToConnect: "Impossibile connettersi al server API",
@ -85,7 +78,6 @@ export const itIT = {
checkConsoleLogs: "Controlla la console del browser per log dettagliati (cerca i messaggi 🔧 [Config])",
yes: "Sì",
no: "No",
simple: "Semplice",
saving: "Salvataggio...",
description: "Descrizione",
saveToNote: "Salva come nota",
@ -103,7 +95,6 @@ export const itIT = {
nameRequired: "Il nome è obbligatorio",
modelConfiguration: "Configurazione modello",
resetToDefault: "Ripristina predefinito",
notFound: "Non trovato",
reasoning: "Ragionamento",
searchTerms: "Termini di ricerca",
strategy: "Strategia",
@ -112,14 +103,12 @@ export const itIT = {
notebookLabel: "Quaderno: {name}",
itemNotFound: "Questo {type} non è stato trovato",
accessibility: {
navigation: "Navigazione",
transformationViews: "Viste trasformazioni",
searchKB: "Chiedi o cerca nella tua base di conoscenza",
enterQuestion: "Inserisci la tua domanda per interrogare la base di conoscenza",
enterSearch: "Inserisci la query di ricerca",
searchKBBtn: "Cerca nella base di conoscenza",
podcastViews: "Viste podcast",
chatSessions: "Sessioni chat",
ytVideo: "Video YouTube",
askResponse: "Risposta alla domanda",
searchNotebooks: "Cerca quaderni",
@ -127,7 +116,6 @@ export const itIT = {
url: "URL",
errorDetails: "Dettagli errore",
editTransformation: "Modifica trasformazione",
comingSoon: "Prossimamente",
retry: "Riprova",
traditionalChinese: "繁體中文",
portuguese: "Português",
@ -165,7 +153,6 @@ export const itIT = {
failedToSendMessage: "Impossibile inviare il messaggio",
unauthorized: "Accesso non autorizzato, controlla la password",
invalidPassword: "Password non valida",
missingAuth: "Autenticazione mancante",
embeddingModelRequired: "Questa funzionalità richiede un modello di embedding. Configurane uno nella sezione Modelli.",
strategyModelNotFound: "Modello strategia non trovato",
answerModelNotFound: "Modello risposta non trovato",
@ -206,7 +193,6 @@ export const itIT = {
passwordPlaceholder: "Password",
signingIn: "Accesso in corso...",
signIn: "Accedi",
unhandledError: "Errore non gestito durante l'accesso",
connectErrorHint: "Impossibile connettersi al server. Verifica che l'API sia in esecuzione.",
},
navigation: {
@ -226,7 +212,6 @@ export const itIT = {
nav: "Navigazione",
language: "Cambia lingua",
theme: "Tema",
search: "Cerca",
ask: "Chiedi",
},
notebooks: {
@ -248,12 +233,8 @@ export const itIT = {
keepExclusiveSourcesLabel: "Scollega e mantieni",
activeNotebooks: "Quaderni attivi",
archivedNotebooks: "Quaderni archiviati",
emptyDescription: "Inizia creando il tuo primo quaderno per organizzare la tua ricerca.",
noActiveNotebooks: "Nessun quaderno attivo",
noArchivedNotebooks: "Nessun quaderno archiviato",
notFound: "Quaderno non trovato",
notFoundDesc: "Il quaderno richiesto non esiste.",
noDescription: "Nessuna descrizione...",
updated: "Aggiornato",
namePlaceholder: "Nome quaderno",
addDescription: "Aggiungi descrizione...",
@ -278,10 +259,7 @@ export const itIT = {
add: "Aggiungi fonte",
addNew: "Aggiungi nuova fonte",
addExisting: "Aggiungi fonte esistente",
empty: "Ancora nessuna fonte",
emptyDesc: "Aggiungi la tua prima fonte per iniziare a costruire la tua base di conoscenza.",
delete: "Elimina Fonte",
deleteMsg: "Sei sicuro di voler eliminare questa fonte? Questa azione non può essere annullata.",
statusPreparing: "In preparazione",
statusQueued: "In coda",
statusProcessing: "In elaborazione",
@ -321,7 +299,6 @@ export const itIT = {
sourceRequeued: "Fonte rimessa in coda",
sourceRequeuedDesc: "La fonte è stata rimessa in coda per l'elaborazione.",
failedToRetry: "Nuovo tentativo fallito",
failedToRetryDesc: "Impossibile ritentare l'elaborazione della fonte. Riprova.",
sourcesAddedToNotebook: "{count} fonte/i aggiunte al quaderno",
failedToAddSourcesToNotebook: "Impossibile aggiungere le fonti al quaderno",
partialAddSuccess: "{success} fonte/i aggiunte, {failed} fallite",
@ -358,7 +335,7 @@ export const itIT = {
viewInsight: "Visualizza approfondimento",
deleteInsight: "Elimina approfondimento",
deleteInsightConfirm: "Sei sicuro di voler eliminare questo approfondimento? Questa azione non può essere annullata.",
deleteNoteConfirm: "Sei sicuro di voler eliminare questa nota? Questa azione non può essere annullata.",
insightGenerationStarted: "Generazione dell'approfondimento avviata. Apparirà a breve.",
editNote: "Modifica nota",
createNote: "Crea nota",
addTitle: "Aggiungi un titolo...",
@ -366,9 +343,7 @@ export const itIT = {
writeNotePlaceholder: "Scrivi il contenuto della tua nota qui...",
saveNote: "Salva nota",
createNoteBtn: "Crea nota",
noNotesYet: "Ancora nessuna nota",
createFirstNote: "Crea la tua prima nota per catturare intuizioni e osservazioni.",
deleteNote: "Elimina nota",
urlLabel: "URL *",
fileLabel: "File *",
textContentLabel: "Contenuto testo *",
@ -402,7 +377,6 @@ export const itIT = {
retryProcessing: "Riprova elaborazione",
deleteSource: "Elimina fonte",
retry: "Riprova",
progress: "Progresso",
addExistingTitle: "Aggiungi fonti esistenti",
addExistingDesc: "Seleziona fonti esistenti da tutti i tuoi quaderni per aggiungerle a quello corrente.",
searchPlaceholder: "Cerca fonti per nome o URL...",
@ -434,8 +408,6 @@ export const itIT = {
batchFailed: "Impossibile creare tutte le {count} fonti",
batchPartial: "{success} riuscite, {failed} fallite",
submittingSource: "Invio fonte per l'elaborazione...",
contentRequired: "Fornisci il contenuto richiesto per il tipo di fonte selezionato",
titleRequiredForText: "Il titolo è obbligatorio per le fonti testuali",
processingBatchSources: "Elaborazione di {count} fonti. Potrebbe richiedere qualche istante.",
processingSource: "La tua fonte è in elaborazione. Potrebbe richiedere qualche istante.",
maxFilesAllowed: "Massimo {count} file consentiti per batch",
@ -444,16 +416,10 @@ export const itIT = {
sessions: "Sessioni",
sessionTitlePlaceholder: "Digita un titolo qui...",
noSessions: "Ancora nessuna sessione chat",
startChatting: "Inizia a chattare sulle tue fonti.",
deleteSession: "Elimina sessione",
deleteSessionDesc: "Sei sicuro di voler eliminare questa sessione chat? Questa azione non può essere annullata.",
sendPlaceholder: "Chiedi qualsiasi cosa sulle tue fonti...",
newChat: "Nuova chat",
sessionsTitle: "Sessioni chat",
clearhistory: "Cancella cronologia",
renameSession: "Rinomina sessione",
noSourcesLinked: "Nessuna fonte collegata",
thinking: "L'IA sta pensando...",
chatWith: "Chatta con {name}",
startConversation: "Inizia una conversazione su questo {type}",
askQuestions: "Fai domande per capire meglio il contenuto",
@ -507,8 +473,6 @@ export const itIT = {
saveSuccess: "Salvato con successo nel quaderno",
saveError: "Impossibile salvare nel quaderno",
selectNotebook: "Seleziona quaderno",
createNewNotebook: "Crea nuovo quaderno",
cancel: "Annulla",
searchAndAsk: "Cerca e chiedi",
searchResultsFor: "Risultati di ricerca per \"{query}\"",
askAbout: "Chiedi riguardo \"{query}\"",
@ -733,8 +697,6 @@ export const itIT = {
speakerCountMin: "È richiesto almeno uno speaker",
speakerCountMax: "Puoi configurare fino a 4 speaker",
delete: "Elimina",
unknown: "Sconosciuto",
deleteSuccess: "Podcast eliminato con successo",
failedToDelete: "Impossibile eliminare il podcast",
},
settings: {
@ -771,13 +733,8 @@ export const itIT = {
title: "Strumenti avanzati",
desc: "Strumenti e utilità avanzate per utenti esperti",
systemInfo: "Informazioni sistema",
systemInfoDesc: "Visualizza lo stato dei componenti di sistema sottostanti",
rebuildEmbeddings: "Ricostruisci indicizzazioni",
rebuildEmbeddingsDesc: "Ricostruisci l'indice di ricerca vettoriale per tutte le fonti",
rebuildWarning: "Questa azione può richiedere molto tempo a seconda del numero di fonti. Cancellerà gli indici vettoriali esistenti e rigenererà le indicizzazioni per tutto.",
startRebuild: "Avvia ricostruzione",
rebuilding: "Ricostruzione...",
rebuildSuccess: "Ricostruzione indicizzazioni avviata con successo",
currentVersion: "Versione corrente",
latestVersion: "Ultima versione",
status: "Stato",
@ -822,10 +779,8 @@ export const itIT = {
defaultPrompt: "Prompt trasformazione predefinito",
defaultPromptDesc: "Questo verrà aggiunto a tutti i tuoi prompt di trasformazione",
defaultPromptPlaceholder: "Inserisci le tue istruzioni di trasformazione predefinite...",
saveDefault: "Salva predefinito",
listTitle: "Trasformazioni personalizzate",
createNew: "Crea Nuova",
testInPlayground: "Testa nel playground",
inputLabel: "Testo di input",
inputPlaceholder: "Inserisci del testo da trasformare...",
outputLabel: "Output",
@ -844,83 +799,33 @@ export const itIT = {
deleteSuccess: "Trasformazione eliminata con successo",
noTransformations: "Ancora nessuna trasformazione",
createOne: "Crea una trasformazione per iniziare",
deleteDesc: "L'eliminazione di questa trasformazione non può essere annullata.",
selectModel: "Seleziona un modello",
deleteConfirm: "Sei sicuro di voler eliminare questa trasformazione?",
model: "Modello",
systemPrompt: "Prompt di sistema",
type: "Tipo",
extraction: "Estrazione",
summary: "Riepilogo",
custom: "Personalizzato",
saveChanges: "Salva modifiche",
overrideModelDesc: "Sovrascrivi il modello predefinito per questa sessione chat. Lascia vuoto per usare il default di sistema.",
sessionUseReplacement: "Questa sessione userà {name} invece del modello predefinito.",
systemDefault: "Predefinito di sistema",
},
models: {
title: "Gestione modelli",
desc: "Configura i modelli IA per diversi scopi in Open Notebook",
failedToLoad: "Impossibile caricare i dati dei modelli",
language: "Modelli linguistici",
embedding: "Modelli di embedding",
tts: "Text to Speech (TTS)",
stt: "Speech to Text (STT)",
providers: "Provider",
defaultModels: "Modelli predefiniti",
status: "Stato",
notConfigured: "Non configurato",
active: "Attivo",
inactive: "Inattivo",
configure: "Configura",
saveChanges: "Salva modifiche",
addModel: "Aggiungi modello",
modelName: "Nome modello",
provider: "Provider",
apiKey: "Chiave API",
baseUrl: "URL Base",
capabilities: "Capacità",
enabled: "Abilitato",
disabled: "Disabilitato",
deleteConfirm: "Sei sicuro di voler eliminare questo modello?",
deleteSuccess: "Modello eliminato con successo",
saveSuccess: "Modello salvato con successo",
providerStatus: "Stato provider",
connectionOk: "Connessione OK",
connectionFailed: "Connessione fallita",
changeEmbeddingWarning: "Cambiare il modello di embedding predefinito influenzerà le nuove fonti. Le fonti esistenti potrebbero dover essere re-indicizzate.",
changeEmbeddingTitle: "Cambiare il modello di embedding predefinito?",
aiProviders: "Provider IA",
providerConfigDesc: "Configura i provider tramite variabili d'ambiente per abilitare i loro modelli.",
configuredCount: "{count} di {total} configurati",
noModels: "Nessun modello",
learnMore: "Scopri come configurare i provider →",
seeLess: "Mostra meno",
seeAll: "Mostra tutti i {count} provider",
language_models: "Modelli linguistici",
embedding_models: "Modelli di embedding",
text_to_speech: "Text to Speech (TTS)",
speech_to_text: "Speech to Text (STT)",
languageDesc: "Chat, trasformazioni e generazione testo",
embeddingDesc: "Ricerca semantica e embedding vettoriali",
ttsDesc: "Genera audio da testo",
sttDesc: "Trascrivi audio in testo",
all: "Tutti",
noModelsConfigured: "Nessun modello configurato",
noProviderModelsConfigured: "Nessun modello {provider} configurato",
showMore: "Mostra altri {count}",
discoverModels: "Scopri Modelli",
noModelsFound: "Nessun modello trovato per questo provider",
modelType: "Tipo di Modello",
modelTypeHint: "Seleziona il tipo per i modelli che vuoi aggiungere. Se hai bisogno di tipi diversi, aggiungili in lotti separati.",
deleteModel: "Elimina modello",
deleteModelDesc: "Sei sicuro di voler eliminare \"{name}\"? Questa azione non può essere annullata.",
defaultAssignments: "Assegnazioni modelli predefiniti",
defaultAssignmentsDesc: "Configura quali modelli usare per diversi scopi in Open Notebook",
missingRequiredModels: "Modelli richiesti mancanti: {models}. Open Notebook potrebbe non funzionare correttamente senza questi.",
selectModelPlaceholder: "Seleziona un modello",
requiredModelPlaceholder: "⚠️ Richiesto - Seleziona un modello",
whichModelToChoose: "Quale modello dovrei scegliere? →",
chatModelLabel: "Modello chat",
chatModelDesc: "Usato per le conversazioni chat",
transformationModelLabel: "Modello trasformazione",
@ -935,16 +840,9 @@ export const itIT = {
ttsModelDesc: "Usato per la generazione podcast",
sttModelLabel: "Modello Speech-to-Text",
sttModelDesc: "Usato per la trascrizione audio",
addSpecificModel: "Aggiungi modello {type}",
addSpecificModelDesc: "Configura un nuovo modello {type} dai provider disponibili.",
noProvidersForType: "Nessun provider disponibile per modelli {type}",
selectProviderPlaceholder: "Seleziona un provider",
providerRequired: "Il provider è obbligatorio",
modelNameRequired: "Il nome del modello è obbligatorio",
modelRequired: "Il modello è obbligatorio",
adding: "Aggiunta...",
azureHint: "Per Azure, usa il nome del deployment come nome del modello",
enterModelName: "Inserisci nome modello",
embeddingChangeTitle: "Cambio modello di embedding",
embeddingChangeConfirm: "Stai per cambiare il modello di embedding da {from} a {to}.",
rebuildRequired: "Importante: ricostruzione richiesta",
@ -957,28 +855,24 @@ export const itIT = {
proceedToRebuildPrompt: "Vuoi procedere alla pagina avanzate per avviare la ricostruzione ora?",
changeModelOnly: "Cambia solo modello",
changeAndRebuild: "Cambia e vai a ricostruzione",
autoAssign: "Assegnazione automatica predefiniti",
autoAssigning: "Assegnazione in corso...",
autoAssignSuccess: "{count} modelli predefiniti assegnati automaticamente",
autoAssignNoModels: "Nessun modello disponibile da assegnare. Sincronizza prima i modelli.",
autoAssignAlreadySet: "Tutti i modelli predefiniti sono già configurati",
testModel: "Testa Modello",
testModelSuccess: "Test del Modello Superato",
testModelFailed: "Test del Modello Fallito",
testingModel: "Test del modello in corso...",
searchOrAddModel: "Cerca o digita un nome modello...",
addCustomModel: 'Aggiungi "{name}"',
addCustomModel: "Aggiungi \"{name}\"",
},
apiKeys: {
title: "Configura la tua IA con le tue chiavi API",
description: "Salva le chiavi API in modo sicuro nel database per abilitare i provider IA in Open Notebook.",
loadFailed: "Impossibile caricare lo stato delle chiavi API",
encryptionRequired: "Chiave di crittografia non configurata",
encryptionRequiredDescription: "Imposta la variabile d'ambiente OPEN_NOTEBOOK_ENCRYPTION_KEY su una stringa segreta qualsiasi per abilitare il salvataggio delle chiavi API nel database.",
configured: "Configurato",
notConfigured: "Non configurato",
sourceDatabase: "Database",
sourceEnvironment: "Variabile d'ambiente",
enterApiKey: "Inserisci la tua chiave API",
enterBaseUrl: "Inserisci l'URL base",
saveSuccess: "Chiave API salvata con successo",
deleteSuccess: "Chiave API eliminata con successo",
fromEnvironmentHint: "Questa chiave è impostata tramite variabile d'ambiente. Salva una nuova chiave per sovrascriverla nel database.",
migrationAvailable: "Variabili d'ambiente rilevate",
migrationDescription: "{count} chiave/i API configurata/e tramite variabili d'ambiente. Puoi migrarle nel database per una gestione più semplice.",
migrateToDatabase: "Migra nel database",
@ -986,67 +880,29 @@ export const itIT = {
migrationSuccess: "{count} chiave/i API migrata/e con successo",
migrationErrors: "{count} chiave/i non migrata/e",
migrationNothingToMigrate: "Tutte le chiavi sono già nel database",
serviceType: "Tipo di servizio",
serviceLlm: "Modello linguistico (LLM)",
serviceEmbedding: "Embedding",
serviceStt: "Riconoscimento vocale (STT)",
serviceTts: "Sintesi vocale (TTS)",
serviceEndpoints: "Endpoint dei servizi (opzionale)",
azureEndpointsHint: "Se necessario, configura endpoint diversi per ogni tipo di servizio.",
endpointPlaceholder: "https://your-resource.openai.azure.com/",
openaiCompatibleHint: "Configura un endpoint API compatibile con OpenAI. Ogni tipo di servizio può avere la propria configurazione.",
baseUrlPlaceholder: "https://api.example.com/v1",
learnMore: "Scopri come configurare le chiavi API →",
testConnection: "Testa connessione",
testing: "Test in corso...",
testSuccess: "Connessione riuscita",
testFailed: "Test di connessione fallito",
syncModels: "Sincronizza modelli",
syncing: "Sincronizzazione...",
syncSuccess: "Trovati {discovered} modelli, aggiunti {new} nuovi",
syncNoNew: "Trovati {count} modelli, tutti già registrati",
syncFailed: "Sincronizzazione modelli fallita",
syncAllModels: "Sincronizza tutti i provider",
syncAllSuccess: "Trovati {discovered} modelli da tutti i provider, aggiunti {new} nuovi",
modelsConfigured: "{count} modelli",
noModelsConfigured: "Nessun modello",
viewModels: "Visualizza modelli",
supportedTypes: "Tipi supportati",
typeLanguage: "Linguistico",
typeEmbedding: "Embedding",
typeTts: "TTS",
typeStt: "STT",
apiEndpoint: "Endpoint API",
getApiKey: "Ottieni chiave API",
vertexProject: "ID progetto GCP",
vertexLocation: "Regione",
vertexCredentials: "Percorso JSON account di servizio",
vertexCredentialsHint: "Percorso del file JSON dell'account di servizio Google Cloud all'interno del container.",
// Traduzioni multi-configurazione
configsCount: "{count} configurazioni",
configuredMultiple: "Configurato",
addConfig: "Aggiungi configurazione",
editConfig: "Modifica configurazione",
deleteConfig: "Elimina configurazione",
setAsDefault: "Imposta come predefinito",
defaultBadge: "Predefinito",
defaultDescription: "Configurazione predefinita per questo provider",
configName: "Nome configurazione",
configNameHint: "Un nome descrittivo per questa configurazione (es. 'Produzione', 'Sviluppo')",
baseUrl: "URL base",
baseUrlHint: "Predefinito: {url}",
baseUrlOverrideHint: "Modifica solo se devi sovrascrivere l'endpoint API predefinito del provider.",
ollamaApiKeyHint: "Necessaria solo per Ollama Cloud. Lascia vuoto per Ollama locale.",
noConfigs: "Nessuna configurazione presente",
noConfigsHint: "Aggiungi una configurazione per iniziare a usare questo provider",
deleteConfigConfirm: "Sei sicuro di voler eliminare '{name}'? Questa azione non può essere annullata.",
setDefaultConfirm: "Impostare '{name}' come configurazione predefinita?",
configSaveSuccess: "Configurazione salvata con successo",
configUpdateSuccess: "Configurazione aggiornata con successo",
configDeleteSuccess: "Configurazione eliminata con successo",
configSetDefaultSuccess: "Configurazione predefinita aggiornata",
apiKeyHint: "Inserisci la chiave API per questa configurazione",
apiKeyEditHint: "Lascia vuoto per mantenere la chiave API esistente",
},
setupBanner: {

View file

@ -36,22 +36,16 @@ export const jaJP = {
warning: "警告",
error: "エラー",
success: "成功",
sessions: "セッション",
model: "モデル",
send: "送信",
back: "戻る",
next: "次へ",
done: "完了",
processing: "処理中...",
creating: "作成中...",
tokenCount: "トークン数",
charCount: "文字数",
linked: "リンク済み",
added: "{date}に追加",
adding: "追加中...",
addSelected: "選択項目を追加",
customModel: "カスタムモデル",
messages: "メッセージ",
failed: "失敗",
current: "現在",
save: "保存",
@ -72,7 +66,6 @@ export const jaJP = {
unknown: "不明",
notes: "ノート",
chat: "チャット",
details: "詳細",
deleteForever: "完全に削除",
connectionError: "接続エラー",
unableToConnect: "APIサーバーに接続できません",
@ -85,7 +78,6 @@ export const jaJP = {
checkConsoleLogs: "ブラウザコンソールで詳細ログを確認してください(🔧 [Config] メッセージを探してください)",
yes: "はい",
no: "いいえ",
simple: "シンプル",
saving: "保存中...",
description: "説明",
saveToNote: "ノートに保存",
@ -103,7 +95,6 @@ export const jaJP = {
nameRequired: "名前は必須です",
modelConfiguration: "モデル設定",
resetToDefault: "デフォルトに戻す",
notFound: "見つかりません",
reasoning: "推論",
searchTerms: "検索ワード",
strategy: "戦略",
@ -112,14 +103,12 @@ export const jaJP = {
notebookLabel: "ノートブック: {name}",
itemNotFound: "この{type}は見つかりませんでした",
accessibility: {
navigation: "ナビゲーション",
transformationViews: "トランスフォーメーション表示",
searchKB: "ナレッジベースに質問・検索",
enterQuestion: "ナレッジベースへの質問を入力",
enterSearch: "検索クエリを入力",
searchKBBtn: "ナレッジベースを検索",
podcastViews: "ポッドキャスト表示",
chatSessions: "チャットセッション",
ytVideo: "YouTube動画",
askResponse: "質問への回答",
searchNotebooks: "ノートブックを検索",
@ -127,7 +116,6 @@ export const jaJP = {
url: "URL",
errorDetails: "エラー詳細",
editTransformation: "トランスフォーメーションを編集",
comingSoon: "近日公開",
retry: "再試行",
traditionalChinese: "繁體中文",
portuguese: "Português",
@ -165,7 +153,6 @@ export const jaJP = {
failedToSendMessage: "メッセージの送信に失敗しました",
unauthorized: "認証エラー。パスワードを確認してください",
invalidPassword: "パスワードが無効です",
missingAuth: "認証情報がありません",
embeddingModelRequired: "この機能にはEmbeddingモデルが必要です。モデルセクションで設定してください。",
strategyModelNotFound: "戦略モデルが見つかりません",
answerModelNotFound: "回答モデルが見つかりません",
@ -206,7 +193,6 @@ export const jaJP = {
passwordPlaceholder: "パスワード",
signingIn: "サインイン中...",
signIn: "サインイン",
unhandledError: "ログイン中に予期しないエラーが発生しました",
connectErrorHint: "サーバーに接続できません。APIが起動しているか確認してください。",
},
navigation: {
@ -226,7 +212,6 @@ export const jaJP = {
nav: "ナビゲーション",
language: "言語を切り替え",
theme: "テーマ",
search: "検索",
ask: "質問",
},
notebooks: {
@ -248,12 +233,8 @@ export const jaJP = {
keepExclusiveSourcesLabel: "リンク解除して保持",
activeNotebooks: "アクティブなノートブック",
archivedNotebooks: "アーカイブ済みノートブック",
emptyDescription: "最初のノートブックを作成してリサーチを整理しましょう。",
noActiveNotebooks: "アクティブなノートブックがありません",
noArchivedNotebooks: "アーカイブ済みノートブックがありません",
notFound: "ノートブックが見つかりません",
notFoundDesc: "指定されたノートブックは存在しません。",
noDescription: "説明なし...",
updated: "更新日時",
namePlaceholder: "ノートブック名",
addDescription: "説明を追加...",
@ -278,10 +259,7 @@ export const jaJP = {
add: "ソースを追加",
addNew: "新規ソースを追加",
addExisting: "既存ソースを追加",
empty: "ソースがまだありません",
emptyDesc: "最初のソースを追加してナレッジベースの構築を始めましょう。",
delete: "ソースを削除",
deleteMsg: "このソースを削除しますか?この操作は元に戻せません。",
statusPreparing: "準備中",
statusQueued: "キュー待ち",
statusProcessing: "処理中",
@ -321,7 +299,6 @@ export const jaJP = {
sourceRequeued: "ソースの再処理をキューに追加",
sourceRequeuedDesc: "ソースを再処理キューに追加しました。",
failedToRetry: "再試行に失敗",
failedToRetryDesc: "ソースの再処理に失敗しました。もう一度お試しください。",
sourcesAddedToNotebook: "{count}件のソースをノートブックに追加しました",
failedToAddSourcesToNotebook: "ノートブックへのソース追加に失敗しました",
partialAddSuccess: "{success}件追加成功、{failed}件失敗",
@ -359,34 +336,31 @@ export const jaJP = {
deleteInsight: "インサイトを削除",
deleteInsightConfirm: "このインサイトを削除しますか?この操作は元に戻せません。",
insightGenerationStarted: "インサイトの生成が開始されました。まもなく表示されます。",
deleteNoteConfirm: 'このノートを削除しますか?この操作は元に戻せません。',
editNote: 'ノートを編集',
createNote: 'ノートを作成',
addTitle: 'タイトルを追加...',
untitledNote: '無題のノート',
writeNotePlaceholder: 'ノートの内容をここに入力...',
saveNote: 'ノートを保存',
createNoteBtn: 'ノートを作成',
noNotesYet: "ノートがまだありません",
editNote: "ノートを編集",
createNote: "ノートを作成",
addTitle: "タイトルを追加...",
untitledNote: "無題のノート",
writeNotePlaceholder: "ノートの内容をここに入力...",
saveNote: "ノートを保存",
createNoteBtn: "ノートを作成",
createFirstNote: "最初のノートを作成してインサイトや気づきを記録しましょう。",
deleteNote: "ノートを削除",
urlLabel: 'URL *',
fileLabel: 'ファイル *',
textContentLabel: 'テキストコンテンツ *',
enterUrlsPlaceholder: 'URLを1行ずつ入力\nhttps://example.com/article1\nhttps://example.com/article2',
batchUrlHint: '複数のURLを貼り付けて一括インポート1行に1つ',
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: '{count}件のURL',
filesCount: '{count}件のファイル',
urlLabel: "URL *",
fileLabel: "ファイル *",
textContentLabel: "テキストコンテンツ *",
enterUrlsPlaceholder: "URLを1行ずつ入力\nhttps://example.com/article1\nhttps://example.com/article2",
batchUrlHint: "複数のURLを貼り付けて一括インポート1行に1つ",
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: "{count}件のURL",
filesCount: "{count}件のファイル",
addSource: "ソースを追加",
notEmbeddedAlert: "コンテンツが未Embedding",
notEmbeddedDesc: "このコンテンツはベクトル検索用にEmbeddingされていません。Embeddingを行うと高度な検索機能やコンテンツの発見性が向上します。",
@ -403,7 +377,6 @@ export const jaJP = {
retryProcessing: "処理を再試行",
deleteSource: "ソースを削除",
retry: "再試行",
progress: "進捗",
addExistingTitle: "既存ソースを追加",
addExistingDesc: "すべてのノートブックから既存のソースを選択して現在のノートブックに追加します。",
searchPlaceholder: "名前またはURLでソースを検索...",
@ -435,8 +408,6 @@ export const jaJP = {
batchFailed: "{count}件すべてのソース作成に失敗しました",
batchPartial: "{success}件成功、{failed}件失敗",
submittingSource: "ソースを処理に送信中...",
contentRequired: "選択したソースタイプに必要なコンテンツを入力してください",
titleRequiredForText: "テキストソースにはタイトルが必要です",
processingBatchSources: "{count}件のソースを処理中。しばらくお待ちください。",
processingSource: "ソースを処理中です。しばらくお待ちください。",
maxFilesAllowed: "一括処理は最大{count}件までです",
@ -445,26 +416,20 @@ export const jaJP = {
sessions: "セッション",
sessionTitlePlaceholder: "タイトルを入力...",
noSessions: "チャットセッションがまだありません",
startChatting: "ソースについてチャットを始めましょう。",
deleteSession: "セッションを削除",
deleteSessionDesc: "このチャットセッションを削除しますか?この操作は元に戻せません。",
sendPlaceholder: "ソースについて何でも質問してください...",
newChat: "新規チャット",
sessionsTitle: "チャットセッション",
clearhistory: "履歴をクリア",
renameSession: "セッション名を変更",
noSourcesLinked: "リンクされたソースがありません",
thinking: "AIが考え中...",
chatWith: "{name}とチャット",
startConversation: "この{type}について会話を始めましょう",
askQuestions: "コンテンツをより深く理解するために質問してください",
pressToSend: "{key}を押して送信",
model: "モデル",
createToStart: 'セッションを作成して開始',
chatWithNotebook: 'ノートブックとチャット',
unableToLoadChat: 'チャットを読み込めません',
noDescription: '説明なし',
startByCreating: '最初のノートブックを作成してリサーチを整理しましょう。',
createToStart: "セッションを作成して開始",
chatWithNotebook: "ノートブックとチャット",
unableToLoadChat: "チャットを読み込めません",
noDescription: "説明なし",
startByCreating: "最初のノートブックを作成してリサーチを整理しましょう。",
messagesCount: "{count}件のメッセージ",
sessionCreated: "チャットセッションを作成しました",
sessionUpdated: "セッションを更新しました",
@ -508,8 +473,6 @@ export const jaJP = {
saveSuccess: "ノートブックに保存しました",
saveError: "ノートブックへの保存に失敗しました",
selectNotebook: "ノートブックを選択",
createNewNotebook: "新規ノートブックを作成",
cancel: "キャンセル",
searchAndAsk: "検索と質問",
searchResultsFor: "「{query}」の検索結果",
askAbout: "「{query}」について質問",
@ -734,8 +697,6 @@ export const jaJP = {
speakerCountMin: "最低1人のスピーカーが必要です",
speakerCountMax: "最大4人まで設定できます",
delete: "削除",
unknown: "不明",
deleteSuccess: "ポッドキャストを削除しました",
failedToDelete: "ポッドキャストの削除に失敗しました",
},
settings: {
@ -772,13 +733,8 @@ export const jaJP = {
title: "詳細ツール",
desc: "パワーユーザー向けの詳細ツールとユーティリティ",
systemInfo: "システム情報",
systemInfoDesc: "基盤システムコンポーネントのステータスを表示",
rebuildEmbeddings: "Embeddingを再構築",
rebuildEmbeddingsDesc: "すべてのソースのベクトル検索インデックスを再構築",
rebuildWarning: "このアクションはソース数によっては非常に時間がかかる場合があります。既存のベクトルインデックスをクリアし、すべてのEmbeddingを再生成します。",
startRebuild: "再構築を開始",
rebuilding: "再構築中...",
rebuildSuccess: "Embedding再構築を開始しました",
currentVersion: "現在のバージョン",
latestVersion: "最新バージョン",
status: "ステータス",
@ -823,105 +779,53 @@ export const jaJP = {
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: 'トランスフォーメーションを削除しました',
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全体で異なる目的に使用するAIモデルを設定",
failedToLoad: "モデルデータの読み込みに失敗しました",
language: "言語モデル",
embedding: "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: "デフォルトのEmbeddingモデルを変更すると新しいソースに影響します。既存のソースは再インデックスが必要になる場合があります。",
changeEmbeddingTitle: "デフォルトEmbeddingモデルを変更しますか",
aiProviders: "AIプロバイダー",
providerConfigDesc: "環境変数を通じてプロバイダーを設定し、モデルを有効にしてください。",
configuredCount: "{total}件中{count}件設定済み",
noModels: "モデルなし",
learnMore: "プロバイダーの設定方法を見る →",
seeLess: "折りたたむ",
seeAll: "{count}件すべてのプロバイダーを表示",
language_models: "言語モデル",
embedding_models: "Embeddingモデル",
text_to_speech: "音声合成TTS",
speech_to_text: "音声認識STT",
languageDesc: "チャット、トランスフォーメーション、テキスト生成",
embeddingDesc: "セマンティック検索とベクトルEmbedding",
ttsDesc: "テキストから音声を生成",
sttDesc: "音声をテキストに書き起こし",
all: "すべて",
noModelsConfigured: "モデルが設定されていません",
noProviderModelsConfigured: "{provider}モデルが設定されていません",
showMore: "さらに{count}件表示",
discoverModels: "モデルを検出",
noModelsFound: "このプロバイダーからモデルが見つかりません",
modelType: "モデルタイプ",
modelTypeHint: "追加するモデルのタイプを選択してください。異なるタイプが必要な場合は、別々のバッチで追加してください。",
deleteModel: "モデルを削除",
deleteModelDesc: "「{name}」を削除しますか?この操作は元に戻せません。",
defaultAssignments: "デフォルトモデル割り当て",
defaultAssignmentsDesc: "Open Notebook全体で異なる目的に使用するモデルを設定",
missingRequiredModels: "必須モデルがありません: {models}。これらがないとOpen Notebookが正しく機能しない可能性があります。",
selectModelPlaceholder: "モデルを選択",
requiredModelPlaceholder: "⚠️ 必須 - モデルを選択",
whichModelToChoose: "どのモデルを選ぶべき? →",
chatModelLabel: "チャットモデル",
chatModelDesc: "チャット会話に使用",
transformationModelLabel: "トランスフォーメーションモデル",
@ -936,16 +840,9 @@ export const jaJP = {
ttsModelDesc: "ポッドキャスト生成に使用",
sttModelLabel: "音声認識モデル",
sttModelDesc: "音声の書き起こしに使用",
addSpecificModel: "{type}を追加",
addSpecificModelDesc: "利用可能なプロバイダーから新しい{type}を設定します。",
noProvidersForType: "{type}用のプロバイダーがありません",
selectProviderPlaceholder: "プロバイダーを選択",
providerRequired: "プロバイダーは必須です",
modelNameRequired: "モデル名は必須です",
modelRequired: "モデルは必須です",
adding: "追加中...",
azureHint: "Azureの場合、デプロイメント名をモデル名として使用してください",
enterModelName: "モデル名を入力",
embeddingChangeTitle: "Embeddingモデルの変更",
embeddingChangeConfirm: "Embeddingモデルを{from}から{to}に変更しようとしています。",
rebuildRequired: "重要:再構築が必要",
@ -959,7 +856,6 @@ export const jaJP = {
changeModelOnly: "モデルのみ変更",
changeAndRebuild: "変更して再構築へ",
autoAssign: "デフォルトを自動割り当て",
autoAssignDesc: "各スロットに最適なモデルを自動的に割り当てます",
autoAssigning: "割り当て中...",
autoAssignSuccess: "{count}件のデフォルトモデルを自動的に割り当てました",
autoAssignNoModels: "割り当て可能なモデルがありません。先にモデルを同期してください。",
@ -967,25 +863,16 @@ export const jaJP = {
testModel: "モデルをテスト",
testModelSuccess: "モデルテスト成功",
testModelFailed: "モデルテスト失敗",
testingModel: "モデルをテスト中...",
searchOrAddModel: "検索またはモデル名を入力...",
addCustomModel: '"{name}" を追加',
addCustomModel: "\"{name}\" を追加",
},
apiKeys: {
title: "独自のAPIキーでAIを設定",
description: "APIキーをデータベースに安全に保存し、Open NotebookでAIプロバイダーを有効にします。",
loadFailed: "APIキーのステータスの読み込みに失敗しました",
encryptionRequired: "暗号化キーが設定されていません",
encryptionRequiredDescription: "OPEN_NOTEBOOK_ENCRYPTION_KEY 環境変数に任意の秘密文字列を設定して、データベースへのAPIキーの保存を有効にしてください。",
configured: "設定済み",
notConfigured: "未設定",
sourceDatabase: "データベース",
sourceEnvironment: "環境変数",
enterApiKey: "APIキーを入力してください",
enterBaseUrl: "ベースURLを入力してください",
saveSuccess: "APIキーを保存しました",
deleteSuccess: "APIキーを削除しました",
fromEnvironmentHint: "このキーは環境変数で設定されています。新しいキーを保存するとデータベースで上書きされます。",
migrationAvailable: "環境変数を検出",
migrationDescription: "{count}個のAPIキーが環境変数で設定されています。管理を容易にするためにデータベースに移行できます。",
migrateToDatabase: "データベースに移行",
@ -993,67 +880,29 @@ export const jaJP = {
migrationSuccess: "{count}個のAPIキーを移行しました",
migrationErrors: "{count}個のキーの移行に失敗しました",
migrationNothingToMigrate: "すべてのキーはすでにデータベースにあります",
serviceType: "サービスタイプ",
serviceLlm: "言語モデルLLM",
serviceEmbedding: "Embedding",
serviceStt: "音声認識STT",
serviceTts: "音声合成TTS",
serviceEndpoints: "サービスエンドポイント(任意)",
azureEndpointsHint: "必要に応じて、各サービスタイプに異なるエンドポイントを設定します。",
endpointPlaceholder: "https://your-resource.openai.azure.com/",
openaiCompatibleHint: "OpenAI互換のAPIエンドポイントを設定します。各サービスタイプに独自の設定が可能です。",
baseUrlPlaceholder: "https://api.example.com/v1",
learnMore: "APIキーの設定方法を確認 →",
testConnection: "接続テスト",
testing: "テスト中...",
testSuccess: "接続成功",
testFailed: "接続テストに失敗",
syncModels: "モデル同期",
syncing: "同期中...",
syncSuccess: "{discovered} モデルを発見、{new} 個を新規追加",
syncNoNew: "{count} モデルを発見、すべて登録済み",
syncFailed: "モデルの同期に失敗",
syncAllModels: "全プロバイダーを同期",
syncAllSuccess: "全プロバイダーで {discovered} モデルを発見、{new} 個を新規追加",
modelsConfigured: "{count} モデル",
noModelsConfigured: "モデルなし",
viewModels: "モデルを表示",
supportedTypes: "対応タイプ",
typeLanguage: "言語",
typeEmbedding: "埋め込み",
typeTts: "TTS",
typeStt: "STT",
apiEndpoint: "APIエンドポイント",
getApiKey: "APIキーを取得",
vertexProject: "GCPプロジェクトID",
vertexLocation: "リージョン",
vertexCredentials: "サービスアカウントJSONパス",
vertexCredentialsHint: "コンテナ内のGoogle Cloudサービスアカウント JSON ファイルへのパス。",
// Multi-config translations
configsCount: "{count} 設定",
configuredMultiple: "設定済み",
addConfig: "設定を追加",
editConfig: "設定を編集",
deleteConfig: "設定を削除",
setAsDefault: "デフォルトに設定",
defaultBadge: "デフォルト",
defaultDescription: "このプロバイダーのデフォルト設定",
configName: "設定名",
configNameHint: "この設定の説明的な名前(例:本番環境、開発環境)",
baseUrl: "ベースURL",
baseUrlHint: "デフォルト:{url}",
baseUrlOverrideHint: "プロバイダーのデフォルト API エンドポイントを上書きする場合のみ変更してください。",
ollamaApiKeyHint: "Ollama Cloud でのみ必要です。ローカル Ollama の場合は空のままにしてください。",
noConfigs: "設定がありません",
noConfigsHint: "このプロバイダーの使用を開始するには設定を追加してください",
deleteConfigConfirm: "「{name}」を削除してもよろしいですか?この操作は元に戻せません。",
setDefaultConfirm: "「{name}」をデフォルト設定にしますか?",
configSaveSuccess: "設定が正常に保存されました",
configUpdateSuccess: "設定が正常に変更されました",
configDeleteSuccess: "設定が正常に削除されました",
configSetDefaultSuccess: "デフォルト設定が更新されました",
apiKeyHint: "この設定のAPIキーを入力してください",
apiKeyEditHint: "既存のAPIキーを維持するには空白のままにしてください",
},
setupBanner: {

View file

@ -36,22 +36,16 @@ export const ptBR = {
warning: "Aviso",
error: "Erro",
success: "Sucesso",
sessions: "Sessões",
model: "Modelo",
send: "Enviar",
back: "Voltar",
next: "Próximo",
done: "Concluído",
processing: "Processando...",
creating: "Criando...",
tokenCount: "Tokens",
charCount: "Caracteres",
linked: "Vinculado",
added: "Adicionado em {date}",
adding: "Adicionando...",
addSelected: "Adicionar Selecionados",
customModel: "Modelo Personalizado",
messages: "Mensagens",
failed: "falhou",
current: "Atual",
save: "Salvar",
@ -72,7 +66,6 @@ export const ptBR = {
unknown: "Desconhecido",
notes: "Notas",
chat: "Chat",
details: "Detalhes",
deleteForever: "Excluir Permanentemente",
connectionError: "Erro de Conexão",
unableToConnect: "Não foi possível conectar ao servidor da API",
@ -85,7 +78,6 @@ export const ptBR = {
checkConsoleLogs: "Verifique o console do navegador para logs detalhados (procure por mensagens 🔧 [Config])",
yes: "Sim",
no: "Não",
simple: "Simples",
saving: "Salvando...",
description: "Descrição",
saveToNote: "Salvar em nota",
@ -103,7 +95,6 @@ export const ptBR = {
nameRequired: "Nome é obrigatório",
modelConfiguration: "Configuração do Modelo",
resetToDefault: "Restaurar Padrão",
notFound: "Não encontrado",
reasoning: "Raciocínio",
searchTerms: "Termos de Busca",
strategy: "Estratégia",
@ -112,14 +103,12 @@ export const ptBR = {
notebookLabel: "Caderno: {name}",
itemNotFound: "Este {type} não foi encontrado",
accessibility: {
navigation: "Navegação",
transformationViews: "Visualizações de transformação",
searchKB: "Perguntar ou buscar na base de conhecimento",
enterQuestion: "Digite sua pergunta para a base de conhecimento",
enterSearch: "Digite sua busca",
searchKBBtn: "Buscar na base de conhecimento",
podcastViews: "Visualizações de podcast",
chatSessions: "Sessões de chat",
ytVideo: "Vídeo do YouTube",
askResponse: "Resposta da Consulta",
searchNotebooks: "Buscar cadernos",
@ -127,9 +116,9 @@ export const ptBR = {
url: "URL",
errorDetails: "Detalhes do Erro",
editTransformation: "Editar Transformação",
comingSoon: "Em breve",
retry: "Tentar Novamente",
traditionalChinese: "繁體中文",
portuguese: "Português",
completed: "concluído",
saveSuccess: "Salvo com sucesso",
contextModes: {
@ -139,7 +128,6 @@ export const ptBR = {
clickToCycle: "Clique para alternar",
},
clickToEdit: "Clique para editar",
portuguese: "Português",
},
apiErrors: {
notebookNotFound: "Caderno não encontrado",
@ -165,7 +153,6 @@ export const ptBR = {
failedToSendMessage: "Falha ao enviar mensagem",
unauthorized: "Acesso não autorizado, verifique sua senha",
invalidPassword: "Senha inválida",
missingAuth: "Autenticação ausente",
embeddingModelRequired: "Este recurso requer um modelo de embedding. Configure um na seção Modelos.",
strategyModelNotFound: "Modelo de estratégia não encontrado",
answerModelNotFound: "Modelo de resposta não encontrado",
@ -206,7 +193,6 @@ export const ptBR = {
passwordPlaceholder: "Senha",
signingIn: "Entrando...",
signIn: "Entrar",
unhandledError: "Erro não tratado durante login",
connectErrorHint: "Não foi possível conectar ao servidor. Verifique se a API está rodando.",
},
navigation: {
@ -226,7 +212,6 @@ export const ptBR = {
nav: "Navegação",
language: "Alternar idioma",
theme: "Tema",
search: "Buscar",
ask: "Perguntar",
},
notebooks: {
@ -248,12 +233,8 @@ export const ptBR = {
keepExclusiveSourcesLabel: "Desvincular e manter",
activeNotebooks: "Cadernos Ativos",
archivedNotebooks: "Cadernos Arquivados",
emptyDescription: "Comece criando seu primeiro caderno para organizar sua pesquisa.",
noActiveNotebooks: "Nenhum caderno ativo",
noArchivedNotebooks: "Nenhum caderno arquivado",
notFound: "Caderno não encontrado",
notFoundDesc: "O caderno solicitado não existe.",
noDescription: "Sem descrição...",
updated: "Atualizado",
namePlaceholder: "Nome do caderno",
addDescription: "Adicionar descrição...",
@ -278,10 +259,7 @@ export const ptBR = {
add: "Adicionar Fonte",
addNew: "Adicionar Nova Fonte",
addExisting: "Adicionar Fonte Existente",
empty: "Nenhuma fonte ainda",
emptyDesc: "Adicione sua primeira fonte para começar a construir sua base de conhecimento.",
delete: "Excluir Fonte",
deleteMsg: "Tem certeza que deseja excluir esta fonte? Esta ação não pode ser desfeita.",
statusPreparing: "Preparando",
statusQueued: "Na Fila",
statusProcessing: "Processando",
@ -321,7 +299,6 @@ export const ptBR = {
sourceRequeued: "Fonte Reenfileirada",
sourceRequeuedDesc: "A fonte foi reenfileirada para processamento.",
failedToRetry: "Falha ao Tentar Novamente",
failedToRetryDesc: "Falha ao tentar processar fonte novamente. Por favor, tente de novo.",
sourcesAddedToNotebook: "{count} fonte(s) adicionada(s) ao caderno",
failedToAddSourcesToNotebook: "Falha ao adicionar fontes ao caderno",
partialAddSuccess: "{success} fonte(s) adicionada(s), {failed} falhou(aram)",
@ -359,7 +336,6 @@ export const ptBR = {
deleteInsight: "Excluir Insight",
deleteInsightConfirm: "Tem certeza que deseja excluir este insight? Esta ação não pode ser desfeita.",
insightGenerationStarted: "Geração de insight iniciada. Aparecerá em breve.",
deleteNoteConfirm: "Tem certeza que deseja excluir esta nota? Esta ação não pode ser desfeita.",
editNote: "Editar nota",
createNote: "Criar nota",
addTitle: "Adicionar título...",
@ -367,9 +343,7 @@ export const ptBR = {
writeNotePlaceholder: "Escreva o conteúdo da sua nota aqui...",
saveNote: "Salvar Nota",
createNoteBtn: "Criar Nota",
noNotesYet: "Nenhuma nota ainda",
createFirstNote: "Crie sua primeira nota para capturar insights e observações.",
deleteNote: "Excluir Nota",
urlLabel: "URL(s) *",
fileLabel: "Arquivo(s) *",
textContentLabel: "Conteúdo de Texto *",
@ -403,7 +377,6 @@ export const ptBR = {
retryProcessing: "Tentar Processamento Novamente",
deleteSource: "Excluir Fonte",
retry: "Tentar Novamente",
progress: "Progresso",
addExistingTitle: "Adicionar Fontes Existentes",
addExistingDesc: "Selecione fontes existentes de todos os seus cadernos para adicionar ao atual.",
searchPlaceholder: "Buscar fontes por nome ou URL...",
@ -435,8 +408,6 @@ export const ptBR = {
batchFailed: "Falha ao criar todas as {count} fontes",
batchPartial: "{success} sucesso, {failed} falhou(aram)",
submittingSource: "Enviando fonte para processamento...",
contentRequired: "Por favor, forneça o conteúdo necessário para o tipo de fonte selecionado",
titleRequiredForText: "Título é obrigatório para fontes de texto",
processingBatchSources: "Processando {count} fontes. Isso pode levar alguns momentos.",
processingSource: "Sua fonte está sendo processada. Isso pode levar alguns momentos.",
maxFilesAllowed: "Máximo de {count} arquivos permitidos por lote",
@ -445,16 +416,10 @@ export const ptBR = {
sessions: "Sessões",
sessionTitlePlaceholder: "Digite um título aqui...",
noSessions: "Nenhuma sessão de chat ainda",
startChatting: "Comece a conversar sobre suas fontes.",
deleteSession: "Excluir Sessão",
deleteSessionDesc: "Tem certeza que deseja excluir esta sessão de chat? Esta ação não pode ser desfeita.",
sendPlaceholder: "Pergunte qualquer coisa sobre suas fontes...",
newChat: "Novo Chat",
sessionsTitle: "Sessões de Chat",
clearhistory: "Limpar Histórico",
renameSession: "Renomear Sessão",
noSourcesLinked: "Nenhuma fonte vinculada",
thinking: "IA está pensando...",
chatWith: "Conversar com {name}",
startConversation: "Inicie uma conversa sobre este {type}",
askQuestions: "Faça perguntas para entender melhor o conteúdo",
@ -508,8 +473,6 @@ export const ptBR = {
saveSuccess: "Salvo no caderno com sucesso",
saveError: "Falha ao salvar no caderno",
selectNotebook: "Selecionar Caderno",
createNewNotebook: "Criar Novo Caderno",
cancel: "Cancelar",
searchAndAsk: "Buscar e Perguntar",
searchResultsFor: "Resultados da busca para \"{query}\"",
askAbout: "Perguntar sobre \"{query}\"",
@ -734,8 +697,6 @@ export const ptBR = {
speakerCountMin: "Pelo menos um locutor é necessário",
speakerCountMax: "Você pode configurar até 4 locutores",
delete: "Excluir",
unknown: "Desconhecido",
deleteSuccess: "Podcast excluído com sucesso",
failedToDelete: "Falha ao excluir podcast",
},
settings: {
@ -772,13 +733,8 @@ export const ptBR = {
title: "Ferramentas Avançadas",
desc: "Ferramentas e utilitários avançados para usuários avançados",
systemInfo: "Informações do Sistema",
systemInfoDesc: "Visualize o status dos componentes subjacentes do sistema",
rebuildEmbeddings: "Reconstruir Embeddings",
rebuildEmbeddingsDesc: "Reconstruir índice de busca vetorial para todas as fontes",
rebuildWarning: "Esta ação pode ser muito demorada dependendo do número de fontes que você tem. Ela limpará os índices vetoriais existentes e regerará embeddings para tudo.",
startRebuild: "Iniciar Reconstrução",
rebuilding: "Reconstruindo...",
rebuildSuccess: "Reconstrução de embedding iniciada com sucesso",
currentVersion: "Versão Atual",
latestVersion: "Última Versão",
status: "Status",
@ -823,10 +779,8 @@ export const ptBR = {
defaultPrompt: "Prompt de Transformação Padrão",
defaultPromptDesc: "Isso será adicionado a todos os seus prompts de transformação",
defaultPromptPlaceholder: "Digite suas instruções padrão de transformação...",
saveDefault: "Salvar Padrão",
listTitle: "Transformações Personalizadas",
createNew: "Criar Nova",
testInPlayground: "Testar no Playground",
inputLabel: "Texto de Entrada",
inputPlaceholder: "Digite algum texto para transformar...",
outputLabel: "Saída",
@ -845,83 +799,33 @@ export const ptBR = {
deleteSuccess: "Transformação excluída com sucesso",
noTransformations: "Nenhuma transformação ainda",
createOne: "Crie uma transformação para começar",
deleteDesc: "Excluir esta transformação não pode ser desfeito.",
selectModel: "Selecione um modelo",
deleteConfirm: "Tem certeza que deseja excluir esta transformação?",
model: "Modelo",
systemPrompt: "Prompt do Sistema",
type: "Tipo",
extraction: "Extração",
summary: "Resumo",
custom: "Personalizado",
saveChanges: "Salvar Alterações",
overrideModelDesc: "Substitua o modelo padrão para esta sessão de chat. Deixe vazio para usar o padrão do sistema.",
sessionUseReplacement: "Esta sessão usará {name} em vez do modelo padrão.",
systemDefault: "Padrão do Sistema",
},
models: {
title: "Gerenciamento de Modelos",
desc: "Configure modelos de IA para diferentes propósitos no Open Notebook",
failedToLoad: "Falha ao carregar dados dos modelos",
language: "Modelos de Linguagem",
embedding: "Modelos de Embedding",
tts: "Text to Speech (TTS)",
stt: "Speech to Text (STT)",
providers: "Provedores",
defaultModels: "Modelos Padrão",
status: "Status",
notConfigured: "Não configurado",
active: "Ativo",
inactive: "Inativo",
configure: "Configurar",
saveChanges: "Salvar Alterações",
addModel: "Adicionar Modelo",
modelName: "Nome do Modelo",
provider: "Provedor",
apiKey: "Chave da API",
baseUrl: "URL Base",
capabilities: "Capacidades",
enabled: "Habilitado",
disabled: "Desabilitado",
deleteConfirm: "Tem certeza que deseja excluir este modelo?",
deleteSuccess: "Modelo excluído com sucesso",
saveSuccess: "Modelo salvo com sucesso",
providerStatus: "Status do Provedor",
connectionOk: "Conexão OK",
connectionFailed: "Conexão falhou",
changeEmbeddingWarning: "Alterar o modelo de embedding padrão afetará novas fontes. Fontes existentes podem precisar ser reindexadas.",
changeEmbeddingTitle: "Alterar Modelo de Embedding Padrão?",
aiProviders: "Provedores de IA",
providerConfigDesc: "Configure provedores através de variáveis de ambiente para habilitar seus modelos.",
configuredCount: "{count} de {total} configurados",
noModels: "Sem modelos",
learnMore: "Saiba como configurar provedores →",
seeLess: "Ver menos",
seeAll: "Ver todos os {count} provedores",
language_models: "Modelos de Linguagem",
embedding_models: "Modelos de Embedding",
text_to_speech: "Text to Speech (TTS)",
speech_to_text: "Speech to Text (STT)",
languageDesc: "Chat, transformações e geração de texto",
embeddingDesc: "Busca semântica e embeddings vetoriais",
ttsDesc: "Gerar áudio a partir de texto",
sttDesc: "Transcrever áudio para texto",
all: "Todos",
noModelsConfigured: "Nenhum modelo configurado",
noProviderModelsConfigured: "Nenhum modelo {provider} configurado",
showMore: "Mostrar mais {count}",
discoverModels: "Descobrir Modelos",
noModelsFound: "Nenhum modelo encontrado para este provedor",
modelType: "Tipo do Modelo",
modelTypeHint: "Selecione o tipo para os modelos que deseja adicionar. Se precisar de tipos diferentes, adicione em lotes separados.",
deleteModel: "Excluir Modelo",
deleteModelDesc: "Tem certeza que deseja excluir \"{name}\"? Esta ação não pode ser desfeita.",
defaultAssignments: "Atribuições de Modelo Padrão",
defaultAssignmentsDesc: "Configure quais modelos usar para diferentes propósitos no Open Notebook",
missingRequiredModels: "Modelos obrigatórios ausentes: {models}. O Open Notebook pode não funcionar corretamente sem eles.",
selectModelPlaceholder: "Selecione um modelo",
requiredModelPlaceholder: "⚠️ Obrigatório - Selecione um modelo",
whichModelToChoose: "Qual modelo devo escolher? →",
chatModelLabel: "Modelo de Chat",
chatModelDesc: "Usado para conversas de chat",
transformationModelLabel: "Modelo de Transformação",
@ -936,16 +840,9 @@ export const ptBR = {
ttsModelDesc: "Usado para geração de podcast",
sttModelLabel: "Modelo Speech-to-Text",
sttModelDesc: "Usado para transcrição de áudio",
addSpecificModel: "Adicionar Modelo de {type}",
addSpecificModelDesc: "Configure um novo modelo de {type} dos provedores disponíveis.",
noProvidersForType: "Nenhum provedor disponível para modelos de {type}",
selectProviderPlaceholder: "Selecione um provedor",
providerRequired: "Provedor é obrigatório",
modelNameRequired: "Nome do modelo é obrigatório",
modelRequired: "Modelo é obrigatório",
adding: "Adicionando...",
azureHint: "Para Azure, use o nome do deployment como nome do modelo",
enterModelName: "Digite o nome do modelo",
embeddingChangeTitle: "Alteração de Modelo de Embedding",
embeddingChangeConfirm: "Você está prestes a alterar seu modelo de embedding de {from} para {to}.",
rebuildRequired: "Importante: Reconstrução Necessária",
@ -959,7 +856,6 @@ export const ptBR = {
changeModelOnly: "Apenas Alterar Modelo",
changeAndRebuild: "Alterar e Ir para Reconstrução",
autoAssign: "Atribuir Automaticamente",
autoAssignDesc: "Atribuir automaticamente o melhor modelo disponível para cada slot",
autoAssigning: "Atribuindo...",
autoAssignSuccess: "{count} modelos padrão atribuídos automaticamente",
autoAssignNoModels: "Nenhum modelo disponível para atribuir. Por favor, sincronize os modelos primeiro.",
@ -967,25 +863,16 @@ export const ptBR = {
testModel: "Testar Modelo",
testModelSuccess: "Teste do Modelo Passou",
testModelFailed: "Teste do Modelo Falhou",
testingModel: "Testando modelo...",
searchOrAddModel: "Pesquisar ou digitar nome do modelo...",
addCustomModel: 'Adicionar "{name}"',
addCustomModel: "Adicionar \"{name}\"",
},
apiKeys: {
title: "Configure sua IA com suas próprias chaves de API",
description: "Armazene chaves de API com segurança no banco de dados para habilitar provedores de IA no Open Notebook.",
loadFailed: "Falha ao carregar status das chaves de API",
encryptionRequired: "Chave de criptografia não configurada",
encryptionRequiredDescription: "Configure a variável de ambiente OPEN_NOTEBOOK_ENCRYPTION_KEY com qualquer string secreta para armazenar chaves de API no banco de dados.",
configured: "Configurado",
notConfigured: "Não configurado",
sourceDatabase: "Banco de dados",
sourceEnvironment: "Ambiente",
enterApiKey: "Digite sua chave de API",
enterBaseUrl: "Digite a URL base",
saveSuccess: "Chave de API salva com sucesso",
deleteSuccess: "Chave de API excluída com sucesso",
fromEnvironmentHint: "Esta chave é definida via variável de ambiente. Salve uma nova chave para sobrescrevê-la no banco de dados.",
migrationAvailable: "Variáveis de Ambiente Detectadas",
migrationDescription: "{count} chave(s) de API estão configuradas via variáveis de ambiente e podem ser migradas para o banco de dados para facilitar o gerenciamento.",
migrateToDatabase: "Migrar para Banco de Dados",
@ -993,67 +880,29 @@ export const ptBR = {
migrationSuccess: "{count} chave(s) de API migrada(s) com sucesso",
migrationErrors: "{count} chave(s) falhou ao migrar",
migrationNothingToMigrate: "Todas as chaves já estão no banco de dados",
serviceType: "Tipo de Serviço",
serviceLlm: "Modelo de Linguagem (LLM)",
serviceEmbedding: "Embedding",
serviceStt: "Speech to Text (STT)",
serviceTts: "Text to Speech (TTS)",
serviceEndpoints: "Endpoints de Serviço (opcional)",
azureEndpointsHint: "Configure endpoints diferentes para cada tipo de serviço se necessário.",
endpointPlaceholder: "https://seu-recurso.openai.azure.com/",
openaiCompatibleHint: "Configure um endpoint de API compatível com OpenAI. Cada tipo de serviço pode ter sua própria configuração.",
baseUrlPlaceholder: "https://api.exemplo.com/v1",
learnMore: "Saiba como configurar chaves de API →",
testConnection: "Testar Conexão",
testing: "Testando...",
testSuccess: "Conexão bem-sucedida",
testFailed: "Falha no teste de conexão",
syncModels: "Sincronizar Modelos",
syncing: "Sincronizando...",
syncSuccess: "Descobertos {discovered} modelos, {new} novos adicionados",
syncNoNew: "Descobertos {count} modelos, todos já registrados",
syncFailed: "Falha ao sincronizar modelos",
syncAllModels: "Sincronizar Todos os Provedores",
syncAllSuccess: "Descobertos {discovered} modelos em todos os provedores, {new} novos adicionados",
modelsConfigured: "{count} modelos",
noModelsConfigured: "Sem modelos",
viewModels: "Ver Modelos",
supportedTypes: "Tipos suportados",
typeLanguage: "Linguagem",
typeEmbedding: "Embedding",
typeTts: "TTS",
typeStt: "STT",
apiEndpoint: "Endpoint da API",
getApiKey: "Obter Chave de API",
vertexProject: "ID do Projeto GCP",
vertexLocation: "Região",
vertexCredentials: "Caminho do JSON da Conta de Serviço",
vertexCredentialsHint: "Caminho para o arquivo JSON da conta de serviço do Google Cloud dentro do contêiner.",
// Multi-config translations
configsCount: "{count} configurações",
configuredMultiple: "Configurado",
addConfig: "Adicionar Configuração",
editConfig: "Editar Configuração",
deleteConfig: "Excluir Configuração",
setAsDefault: "Definir como Padrão",
defaultBadge: "Padrão",
defaultDescription: "Configuração padrão para este provedor",
configName: "Nome da Configuração",
configNameHint: "Um nome descritivo para esta configuração (ex.: 'Produção', 'Desenvolvimento')",
baseUrl: "URL Base",
baseUrlHint: "Padrão: {url}",
baseUrlOverrideHint: "Altere apenas se precisar sobrescrever o endpoint padrão do provedor.",
ollamaApiKeyHint: "Necessária apenas para Ollama Cloud. Deixe vazio para Ollama local.",
noConfigs: "Sem configurações ainda",
noConfigsHint: "Adicione uma configuração para começar a usar este provedor",
deleteConfigConfirm: "Tem certeza de que deseja excluir '{name}'? Esta ação não pode ser desfeita.",
setDefaultConfirm: "Definir '{name}' como a configuração padrão?",
configSaveSuccess: "Configuração salva com sucesso",
configUpdateSuccess: "Configuração atualizada com sucesso",
configDeleteSuccess: "Configuração excluída com sucesso",
configSetDefaultSuccess: "Configuração padrão atualizada",
apiKeyHint: "Digite sua chave de API para esta configuração",
apiKeyEditHint: "Deixe em branco para manter a chave de API existente",
},
setupBanner: {

View file

@ -36,22 +36,16 @@ export const ruRU = {
warning: "Предупреждение",
error: "Ошибка",
success: "Успешно",
sessions: "Сессии",
model: "Модель",
send: "Отправить",
back: "Назад",
next: "Далее",
done: "Готово",
processing: "Обработка...",
creating: "Создание...",
tokenCount: "Токены",
charCount: "Символы",
linked: "Связано",
added: "Добавлено {date}",
adding: "Добавление...",
addSelected: "Добавить выбранное",
customModel: "Своя модель",
messages: "Сообщения",
failed: "не удалось",
current: "Текущий",
save: "Сохранить",
@ -72,7 +66,6 @@ export const ruRU = {
unknown: "Неизвестно",
notes: "Заметки",
chat: "Чат",
details: "Подробности",
deleteForever: "Удалить навсегда",
connectionError: "Ошибка подключения",
unableToConnect: "Не удаётся подключиться к API-серверу",
@ -85,7 +78,6 @@ export const ruRU = {
checkConsoleLogs: "Проверьте консоль браузера для подробных логов (ищите сообщения 🔧 [Config])",
yes: "Да",
no: "Нет",
simple: "Простой",
saving: "Сохранение...",
description: "Описание",
saveToNote: "Сохранить в заметку",
@ -103,7 +95,6 @@ export const ruRU = {
nameRequired: "Название обязательно",
modelConfiguration: "Настройка модели",
resetToDefault: "Сбросить по умолчанию",
notFound: "Не найдено",
reasoning: "Рассуждение",
searchTerms: "Поисковые запросы",
strategy: "Стратегия",
@ -112,14 +103,12 @@ export const ruRU = {
notebookLabel: "Блокнот: {name}",
itemNotFound: "Этот {type} не найден",
accessibility: {
navigation: "Навигация",
transformationViews: "Представления трансформаций",
searchKB: "Спросить или найти в базе знаний",
enterQuestion: "Введите вопрос для базы знаний",
enterSearch: "Введите поисковый запрос",
searchKBBtn: "Поиск по базе знаний",
podcastViews: "Представления подкастов",
chatSessions: "Сессии чата",
ytVideo: "Видео YouTube",
askResponse: "Ответ на запрос",
searchNotebooks: "Поиск блокнотов",
@ -127,7 +116,6 @@ export const ruRU = {
url: "URL",
errorDetails: "Детали ошибки",
editTransformation: "Редактировать трансформацию",
comingSoon: "Скоро будет",
retry: "Повторить",
traditionalChinese: "繁體中文",
portuguese: "Português",
@ -165,7 +153,6 @@ export const ruRU = {
failedToSendMessage: "Не удалось отправить сообщение",
unauthorized: "Неавторизованный доступ, проверьте пароль",
invalidPassword: "Неверный пароль",
missingAuth: "Отсутствует авторизация",
embeddingModelRequired: "Для этой функции требуется модель эмбеддингов. Настройте её в разделе «Модели».",
strategyModelNotFound: "Модель стратегии не найдена",
answerModelNotFound: "Модель ответов не найдена",
@ -206,7 +193,6 @@ export const ruRU = {
passwordPlaceholder: "Пароль",
signingIn: "Вход...",
signIn: "Войти",
unhandledError: "Необработанная ошибка при входе",
connectErrorHint: "Не удаётся подключиться к серверу. Проверьте, запущен ли API.",
},
navigation: {
@ -226,7 +212,6 @@ export const ruRU = {
nav: "Навигация",
language: "Переключить язык",
theme: "Тема",
search: "Поиск",
ask: "Запрос",
},
notebooks: {
@ -248,12 +233,8 @@ export const ruRU = {
keepExclusiveSourcesLabel: "Отвязать и сохранить",
activeNotebooks: "Активные блокноты",
archivedNotebooks: "Архивные блокноты",
emptyDescription: "Начните с создания первого блокнота для организации исследований.",
noActiveNotebooks: "Нет активных блокнотов",
noArchivedNotebooks: "Нет архивных блокнотов",
notFound: "Блокнот не найден",
notFoundDesc: "Запрошенный блокнот не существует.",
noDescription: "Без описания...",
updated: "Обновлено",
namePlaceholder: "Название блокнота",
addDescription: "Добавить описание...",
@ -278,10 +259,7 @@ export const ruRU = {
add: "Добавить источник",
addNew: "Добавить новый источник",
addExisting: "Добавить существующий источник",
empty: "Пока нет источников",
emptyDesc: "Добавьте первый источник, чтобы начать создание базы знаний.",
delete: "Удалить источник",
deleteMsg: "Вы уверены, что хотите удалить этот источник? Это действие нельзя отменить.",
statusPreparing: "Подготовка",
statusQueued: "В очереди",
statusProcessing: "Обработка",
@ -321,7 +299,6 @@ export const ruRU = {
sourceRequeued: "Повторная обработка в очереди",
sourceRequeuedDesc: "Источник поставлен в очередь на повторную обработку.",
failedToRetry: "Повтор не удался",
failedToRetryDesc: "Не удалось повторить обработку источника. Попробуйте ещё раз.",
sourcesAddedToNotebook: "Добавлено источников в блокнот: {count}",
failedToAddSourcesToNotebook: "Не удалось добавить источники в блокнот",
partialAddSuccess: "Добавлено: {success}, не удалось: {failed}",
@ -359,7 +336,6 @@ export const ruRU = {
deleteInsight: "Удалить инсайт",
deleteInsightConfirm: "Вы уверены, что хотите удалить этот инсайт? Это действие нельзя отменить.",
insightGenerationStarted: "Генерация инсайта запущена. Скоро он появится.",
deleteNoteConfirm: "Вы уверены, что хотите удалить эту заметку? Это действие нельзя отменить.",
editNote: "Редактировать заметку",
createNote: "Создать заметку",
addTitle: "Добавьте название...",
@ -367,9 +343,7 @@ export const ruRU = {
writeNotePlaceholder: "Напишите содержимое заметки здесь...",
saveNote: "Сохранить заметку",
createNoteBtn: "Создать заметку",
noNotesYet: "Пока нет заметок",
createFirstNote: "Создайте первую заметку для записи идей и наблюдений.",
deleteNote: "Удалить заметку",
urlLabel: "URL(ы) *",
fileLabel: "Файл(ы) *",
textContentLabel: "Текстовое содержимое *",
@ -403,7 +377,6 @@ export const ruRU = {
retryProcessing: "Повторить обработку",
deleteSource: "Удалить источник",
retry: "Повторить",
progress: "Прогресс",
addExistingTitle: "Добавить существующие источники",
addExistingDesc: "Выберите существующие источники из всех блокнотов для добавления в текущий.",
searchPlaceholder: "Поиск источников по названию или URL...",
@ -435,8 +408,6 @@ export const ruRU = {
batchFailed: "Не удалось создать все источники: {count}",
batchPartial: "Успешно: {success}, не удалось: {failed}",
submittingSource: "Отправка источника на обработку...",
contentRequired: "Пожалуйста, предоставьте необходимое содержимое для выбранного типа источника",
titleRequiredForText: "Для текстовых источников требуется название",
processingBatchSources: "Обработка источников: {count}. Это может занять некоторое время.",
processingSource: "Источник обрабатывается. Это может занять некоторое время.",
maxFilesAllowed: "Максимальное количество файлов в пакете: {count}",
@ -445,16 +416,10 @@ export const ruRU = {
sessions: "Сессии",
sessionTitlePlaceholder: "Введите название...",
noSessions: "Пока нет сессий чата",
startChatting: "Начните общение о ваших источниках.",
deleteSession: "Удалить сессию",
deleteSessionDesc: "Вы уверены, что хотите удалить эту сессию чата? Это действие нельзя отменить.",
sendPlaceholder: "Задайте вопрос о ваших источниках...",
newChat: "Новый чат",
sessionsTitle: "Сессии чата",
clearhistory: "Очистить историю",
renameSession: "Переименовать сессию",
noSourcesLinked: "Нет связанных источников",
thinking: "ИИ размышляет...",
chatWith: "Чат с {name}",
startConversation: "Начните разговор об этом {type}",
askQuestions: "Задавайте вопросы, чтобы лучше понять содержимое",
@ -508,8 +473,6 @@ export const ruRU = {
saveSuccess: "Успешно сохранено в блокнот",
saveError: "Не удалось сохранить в блокнот",
selectNotebook: "Выберите блокнот",
createNewNotebook: "Создать новый блокнот",
cancel: "Отмена",
searchAndAsk: "Поиск и запрос",
searchResultsFor: "Результаты поиска для «{query}»",
askAbout: "Спросить о «{query}»",
@ -734,8 +697,6 @@ export const ruRU = {
speakerCountMin: "Требуется минимум один говорящий",
speakerCountMax: "Можно настроить до 4 говорящих",
delete: "Удалить",
unknown: "Неизвестно",
deleteSuccess: "Подкаст успешно удалён",
failedToDelete: "Не удалось удалить подкаст",
},
settings: {
@ -772,13 +733,8 @@ export const ruRU = {
title: "Дополнительные инструменты",
desc: "Расширенные инструменты и утилиты для опытных пользователей",
systemInfo: "Информация о системе",
systemInfoDesc: "Просмотр состояния системных компонентов",
rebuildEmbeddings: "Пересоздать эмбеддинги",
rebuildEmbeddingsDesc: "Пересоздать индекс векторного поиска для всех источников",
rebuildWarning: "Это действие может занять много времени в зависимости от количества источников. Существующие векторные индексы будут очищены и эмбеддинги будут созданы заново.",
startRebuild: "Начать пересоздание",
rebuilding: "Пересоздание...",
rebuildSuccess: "Пересоздание эмбеддингов успешно запущено",
currentVersion: "Текущая версия",
latestVersion: "Последняя версия",
status: "Статус",
@ -823,10 +779,8 @@ export const ruRU = {
defaultPrompt: "Промпт трансформации по умолчанию",
defaultPromptDesc: "Этот текст будет добавлен ко всем промптам трансформаций",
defaultPromptPlaceholder: "Введите инструкции трансформации по умолчанию...",
saveDefault: "Сохранить по умолчанию",
listTitle: "Пользовательские трансформации",
createNew: "Создать новую",
testInPlayground: "Тестировать в песочнице",
inputLabel: "Входной текст",
inputPlaceholder: "Введите текст для трансформации...",
outputLabel: "Результат",
@ -845,83 +799,33 @@ export const ruRU = {
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}",
discoverModels: "Обнаружение моделей",
noModelsFound: "Модели от этого провайдера не найдены",
modelType: "Тип модели",
modelTypeHint: "Выберите тип для добавляемых моделей. Если нужны разные типы, добавляйте их отдельными партиями.",
deleteModel: "Удалить модель",
deleteModelDesc: "Вы уверены, что хотите удалить «{name}»? Это действие нельзя отменить.",
defaultAssignments: "Назначение моделей по умолчанию",
defaultAssignmentsDesc: "Настройте, какие модели использовать для различных задач в Open Notebook",
missingRequiredModels: "Отсутствуют необходимые модели: {models}. Open Notebook может работать некорректно без них.",
selectModelPlaceholder: "Выберите модель",
requiredModelPlaceholder: "⚠️ Обязательно — выберите модель",
whichModelToChoose: "Какую модель выбрать? →",
chatModelLabel: "Модель чата",
chatModelDesc: "Используется для чат-разговоров",
transformationModelLabel: "Модель трансформаций",
@ -936,16 +840,9 @@ export const ruRU = {
ttsModelDesc: "Используется для генерации подкастов",
sttModelLabel: "Модель распознавания речи",
sttModelDesc: "Используется для транскрибации аудио",
addSpecificModel: "Добавить модель {type}",
addSpecificModelDesc: "Настройте новую модель {type} от доступных провайдеров.",
noProvidersForType: "Нет доступных провайдеров для моделей {type}",
selectProviderPlaceholder: "Выберите провайдера",
providerRequired: "Требуется провайдер",
modelNameRequired: "Требуется название модели",
modelRequired: "Требуется модель",
adding: "Добавление...",
azureHint: "Для Azure используйте название деплоймента как название модели",
enterModelName: "Введите название модели",
embeddingChangeTitle: "Изменение модели эмбеддинга",
embeddingChangeConfirm: "Вы собираетесь изменить модель эмбеддинга с {from} на {to}.",
rebuildRequired: "Важно: Требуется пересоздание",
@ -958,28 +855,24 @@ export const ruRU = {
proceedToRebuildPrompt: "Хотите перейти на страницу «Дополнительно», чтобы начать пересоздание сейчас?",
changeModelOnly: "Только изменить модель",
changeAndRebuild: "Изменить и перейти к пересозданию",
autoAssign: "Автоназначение по умолчанию",
autoAssigning: "Назначение...",
autoAssignSuccess: "{count} моделей по умолчанию автоматически назначено",
autoAssignNoModels: "Нет доступных моделей для назначения. Сначала синхронизируйте модели.",
autoAssignAlreadySet: "Все модели по умолчанию уже настроены",
testModel: "Тестировать модель",
testModelSuccess: "Тест модели пройден",
testModelFailed: "Тест модели не пройден",
testingModel: "Тестирование модели...",
searchOrAddModel: "Поиск или введите имя модели...",
addCustomModel: 'Добавить "{name}"',
addCustomModel: "Добавить \"{name}\"",
},
apiKeys: {
title: "Настройте ИИ с помощью собственных API-ключей",
description: "Храните API-ключи в базе данных для безопасного подключения провайдеров ИИ в Open Notebook.",
loadFailed: "Не удалось загрузить статус API-ключей",
encryptionRequired: "Ключ шифрования не настроен",
encryptionRequiredDescription: "Установите переменную окружения OPEN_NOTEBOOK_ENCRYPTION_KEY в любую секретную строку для хранения API-ключей в базе данных.",
configured: "Настроено",
notConfigured: "Не настроено",
sourceDatabase: "База данных",
sourceEnvironment: "Переменная окружения",
enterApiKey: "Введите ваш API-ключ",
enterBaseUrl: "Введите базовый URL",
saveSuccess: "API-ключ успешно сохранён",
deleteSuccess: "API-ключ успешно удалён",
fromEnvironmentHint: "Этот ключ задан через переменную окружения. Сохраните новый ключ, чтобы переопределить его в базе данных.",
migrationAvailable: "Обнаружены переменные окружения",
migrationDescription: "{count} API-ключ(ей) настроено через переменные окружения и может быть перенесено в базу данных для удобного управления.",
migrateToDatabase: "Перенести в базу данных",
@ -987,67 +880,29 @@ export const ruRU = {
migrationSuccess: "{count} API-ключ(ей) успешно перенесено",
migrationErrors: "{count} ключ(ей) не удалось перенести",
migrationNothingToMigrate: "Все ключи уже находятся в базе данных",
serviceType: "Тип сервиса",
serviceLlm: "Языковая модель (LLM)",
serviceEmbedding: "Эмбеддинг",
serviceStt: "Распознавание речи (STT)",
serviceTts: "Синтез речи (TTS)",
serviceEndpoints: "Эндпоинты сервисов (необязательно)",
azureEndpointsHint: "При необходимости настройте отдельные эндпоинты для каждого типа сервиса.",
endpointPlaceholder: "https://your-resource.openai.azure.com/",
openaiCompatibleHint: "Настройте совместимый с OpenAI API-эндпоинт. Каждый тип сервиса может иметь собственную конфигурацию.",
baseUrlPlaceholder: "https://api.example.com/v1",
learnMore: "Узнайте, как настроить API-ключи →",
testConnection: "Проверить подключение",
testing: "Проверка...",
testSuccess: "Подключение успешно",
testFailed: "Проверка подключения не удалась",
syncModels: "Синхронизировать модели",
syncing: "Синхронизация...",
syncSuccess: "Обнаружено {discovered} моделей, добавлено {new} новых",
syncNoNew: "Обнаружено {count} моделей, все уже зарегистрированы",
syncFailed: "Не удалось синхронизировать модели",
syncAllModels: "Синхронизировать всех провайдеров",
syncAllSuccess: "Обнаружено {discovered} моделей у всех провайдеров, добавлено {new} новых",
modelsConfigured: "{count} моделей",
noModelsConfigured: "Нет моделей",
viewModels: "Посмотреть модели",
supportedTypes: "Поддерживаемые типы",
typeLanguage: "Языковая",
typeEmbedding: "Эмбеддинг",
typeTts: "TTS",
typeStt: "STT",
apiEndpoint: "API-эндпоинт",
getApiKey: "Получить API-ключ",
vertexProject: "ID проекта GCP",
vertexLocation: "Регион",
vertexCredentials: "Путь к JSON сервисного аккаунта",
vertexCredentialsHint: "Путь к JSON-файлу сервисного аккаунта Google Cloud внутри контейнера.",
// Мультиконфигурация
configsCount: "{count} конфигураций",
configuredMultiple: "Настроено",
addConfig: "Добавить конфигурацию",
editConfig: "Редактировать конфигурацию",
deleteConfig: "Удалить конфигурацию",
setAsDefault: "Установить по умолчанию",
defaultBadge: "По умолчанию",
defaultDescription: "Конфигурация по умолчанию для этого провайдера",
configName: "Название конфигурации",
configNameHint: "Описательное название для этой конфигурации (например, «Продакшн», «Разработка»)",
baseUrl: "Базовый URL",
baseUrlHint: "По умолчанию: {url}",
baseUrlOverrideHint: "Изменяйте только если нужно переопределить стандартную конечную точку API провайдера.",
ollamaApiKeyHint: "Требуется только для Ollama Cloud. Оставьте пустым для локальной Ollama.",
noConfigs: "Конфигурации ещё не созданы",
noConfigsHint: "Добавьте конфигурацию, чтобы начать использовать этого провайдера",
deleteConfigConfirm: "Вы уверены, что хотите удалить «{name}»? Это действие необратимо.",
setDefaultConfirm: "Установить «{name}» как конфигурацию по умолчанию?",
configSaveSuccess: "Конфигурация успешно сохранена",
configUpdateSuccess: "Конфигурация успешно обновлена",
configDeleteSuccess: "Конфигурация успешно удалена",
configSetDefaultSuccess: "Конфигурация по умолчанию обновлена",
apiKeyHint: "Введите API-ключ для этой конфигурации",
apiKeyEditHint: "Оставьте пустым, чтобы сохранить текущий API-ключ",
},
setupBanner: {

View file

@ -1,16 +1,11 @@
export const zhCN = {
common: {
search: "搜索...",
chat: '聊天',
notes: '笔记',
create: "新建",
new: "新建",
cancel: "取消",
save: "保存",
nameRequired: "这是必填项",
delete: "删除",
edit: "编辑",
actions: "快捷操作",
theme: "主题",
signOut: "退出登录",
noMatches: "未找到匹配项",
@ -41,33 +36,27 @@ export const zhCN = {
warning: "警告",
error: "操作失败",
success: "操作成功",
modelConfiguration: "模型配置",
resetToDefault: "重置为默认",
sessions: "会话",
model: "模型",
send: "发送",
back: "返回",
next: "下一步",
done: "完成",
processing: "处理中...",
creating: "创建中...",
tokenCount: "Token",
charCount: "字符",
linked: "已关联",
added: "已于 {date} 添加",
adding: "正在添加...",
addSelected: "添加所选",
customModel: "自定义模型",
messages: "消息",
failed: "失败",
current: "当前",
writeNote: '撰写笔记',
save: "保存",
writeNote: "撰写笔记",
batchMode: "批量模式",
optional: "可选",
type: "类型",
title: "标题",
created: "创建于 {time}",
updated: "更新于 {time}",
actions: "快捷操作",
noResults: "未找到结果",
references: "引用",
refreshPage: "请重试刷新页面",
@ -75,7 +64,8 @@ export const zhCN = {
aiGenerated: "AI 生成",
human: "人类",
unknown: "未知",
details: "详情",
notes: "笔记",
chat: "聊天",
deleteForever: "永久删除",
connectionError: "连接错误",
unableToConnect: "无法连接到 API 服务器",
@ -88,7 +78,6 @@ export const zhCN = {
checkConsoleLogs: "请检查浏览器控制台获取详细日志(搜索 🔧 [Config] 消息)",
yes: "是",
no: "否",
simple: "简单",
saving: "正在保存...",
description: "描述",
saveToNote: "保存到笔记",
@ -103,7 +92,9 @@ export const zhCN = {
saveChanges: "保存更改",
name: "名称",
default: "默认",
notFound: "未找到",
nameRequired: "这是必填项",
modelConfiguration: "模型配置",
resetToDefault: "重置为默认",
reasoning: "推理过程",
searchTerms: "搜索词",
strategy: "策略",
@ -112,22 +103,19 @@ export const zhCN = {
notebookLabel: "笔记本: {name}",
itemNotFound: "未找到该 {type}",
accessibility: {
navigation: "导航",
transformationViews: "转换视图",
searchKB: "向知识库提问或搜索",
searchNotebooks: "搜索笔记本",
enterQuestion: "输入您的问题以询问知识库",
enterSearch: "输入搜索词",
searchKBBtn: "搜索知识库",
podcastViews: "播客视图",
chatSessions: "对话 Session",
ytVideo: "YouTube 视频",
askResponse: "提问回答",
searchNotebooks: "搜索笔记本",
},
url: "URL",
errorDetails: "错误详情",
editTransformation: "编辑转换规则",
comingSoon: "敬请期待",
retry: "重试",
traditionalChinese: "繁体中文",
portuguese: "葡萄牙语",
@ -155,17 +143,16 @@ export const zhCN = {
invalidSortOrder: "排序方向必须是 'asc' 或 'desc'",
accessDenied: "文件访问被拒绝",
fileNotFoundOnServer: "服务器上找不到该文件",
searchFailed: "搜索失败",
askFailed: "提问失败",
pleaseEnterQuestion: "请输入问题",
pleaseConfigureModels: "请配置所有必选模型",
failedToCreateSession: "创建对话失败",
failedToUpdateSession: "更新会话失败",
failedToDeleteSession: "删除会话失败",
failedToSendMessage: "发送消息失败",
pleaseEnterQuestion: "请输入问题",
pleaseConfigureModels: "请配置所有必选模型",
askFailed: "提问失败",
searchFailed: "搜索失败",
unauthorized: "无权访问,请检查您的密码",
invalidPassword: "密码错误",
missingAuth: "缺少身份验证信息",
embeddingModelRequired: "此功能需要嵌入模型。请在模型设置中配置一个。",
strategyModelNotFound: "未找到策略模型",
answerModelNotFound: "未找到回答模型",
@ -206,7 +193,6 @@ export const zhCN = {
passwordPlaceholder: "密码",
signingIn: "正在登录...",
signIn: "登录",
unhandledError: "登录过程中出现未处理的错误",
connectErrorHint: "无法连接到服务器。请检查 API 是否正在运行。",
},
navigation: {
@ -217,8 +203,6 @@ export const zhCN = {
sources: "来源",
notebooks: "笔记本",
askAndSearch: "询问与搜索",
search: "搜索",
ask: "提问",
podcasts: "播客",
models: "模型",
transformations: "转换",
@ -228,6 +212,7 @@ export const zhCN = {
nav: "导航",
language: "切换语言",
theme: "主题",
ask: "提问",
},
notebooks: {
title: "笔记本",
@ -248,12 +233,8 @@ export const zhCN = {
keepExclusiveSourcesLabel: "取消关联并保留",
activeNotebooks: "活动的笔记本",
archivedNotebooks: "归档的笔记本",
emptyDescription: "从创建您的第一个笔记本开始,组织您的研究。",
noActiveNotebooks: "没有活动的笔记本",
noArchivedNotebooks: "没有归档的笔记本",
notFound: "未找到笔记本",
notFoundDesc: "请求的笔记本不存在。",
noDescription: "暂无描述...",
updated: "已更新",
namePlaceholder: "笔记本名称",
addDescription: "添加描述...",
@ -278,12 +259,7 @@ export const zhCN = {
add: "添加来源",
addNew: "添加新来源",
addExisting: "添加现有来源",
allSourcesDescShort: "在此查看所有来源。",
cannotSaveNoteNoNotebook: "无法保存笔记:缺少笔记本 ID",
empty: "暂无来源",
emptyDesc: "添加您的第一个来源,开始构建您的知识库。",
delete: "删除来源",
deleteMsg: "确定要删除此来源吗?此操作无法撤销。",
statusPreparing: "正在准备",
statusQueued: "已排队",
statusProcessing: "正在处理",
@ -301,6 +277,11 @@ export const zhCN = {
yes: "是",
no: "否",
loadingMore: "正在加载更多...",
noSourcesYet: "暂无来源",
allSourcesDescShort: "在此查看所有来源。",
cannotSaveNoteNoNotebook: "无法保存笔记:缺少笔记本 ID",
createFirstSource: "添加您的第一个来源开始构建知识库。",
deleteSourceConfirm: "确定要删除此来源吗?",
deleteConfirm: "确定要删除吗?",
deleteConfirmWithTitle: "确定要删除 \"{title}\" 吗?",
deleteSuccess: "来源删除成功。注意:要从存储中删除文件,必须在设置页面中启用“删除文件”选项。",
@ -318,16 +299,12 @@ export const zhCN = {
sourceRequeued: "来源重试已加入队列",
sourceRequeuedDesc: "来源已重新加入处理队列。",
failedToRetry: "重试失败",
failedToRetryDesc: "重试来源处理失败。请重试。",
sourcesAddedToNotebook: "{count} 个来源已添加到笔记本",
failedToAddSourcesToNotebook: "添加来源到笔记本失败",
partialAddSuccess: "{success} 个来源已添加,{failed} 个失败",
sourceRemovedFromNotebook: "来源已成功从笔记本中移除",
failedToRemoveSourceFromNotebook: "从笔记本中移除来源失败",
removeConfirm: "确定要从此笔记本移除吗?",
noSourcesYet: "暂无来源",
createFirstSource: "添加您的第一个来源开始构建知识库。",
deleteSourceConfirm: "确定要删除此来源吗?",
checking: "正在检查...",
untitledSource: "未命名来源",
maxItems: "最多 {count} 个",
@ -356,25 +333,50 @@ export const zhCN = {
noInsightsYet: "暂无见解",
createFirstInsight: "使用上方的转换规则创建您的第一个见解",
viewInsight: "查看见解",
deleteInsight: "删除见解",
deleteInsightConfirm: "确定要删除此见解吗?此操作无法撤销。",
insightGenerationStarted: "见解生成已开始,稍后将显示。",
editNote: "编辑笔记",
createNote: "创建笔记",
addTitle: "添加标题...",
untitledNote: "无标题笔记",
writeNotePlaceholder: "在此处编写您的笔记内容...",
saveNote: "保存笔记",
createNoteBtn: "创建笔记",
createFirstNote: "创建您的第一条笔记,记录见解与观察。",
urlLabel: "URL(s) *",
fileLabel: "文件(s) *",
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: "{count} 个 URL",
filesCount: "{count} 个文件",
addSource: "添加来源",
notEmbeddedAlert: "内容未嵌入向量",
notEmbeddedDesc: "此内容尚未为了向量搜索进行嵌入。嵌入可以启用高级搜索功能并更好地发现内容。",
openOnYoutube: "在 YouTube 上打开",
urlCopied: "URL 已复制到剪贴板",
viewSource: "查看来源",
noInsightSelected: "未选择见解",
sourceInsight: "来源见解",
manageNotebooks: "管理所属笔记本",
manageNotebooksDesc: "管理包含此来源的笔记本",
noNotebooksAvailable: "暂无可用笔记本",
deleteInsight: "删除见解",
deleteInsightConfirm: "确定要删除此见解吗?此操作无法撤销。",
insightGenerationStarted: "见解生成已开始,稍后将显示。",
notEmbeddedAlert: "内容未嵌入向量",
notEmbeddedDesc: "此内容尚未为了向量搜索进行嵌入。嵌入可以启用高级搜索功能并更好地发现内容。",
openOnYoutube: "在 YouTube 上打开",
urlCopied: "URL 已复制到剪贴板",
loadFailed: "加载来源详情失败",
removeFromNotebook: "从笔记本移除",
retryProcessing: "重试处理",
deleteSource: "删除来源",
retry: "重试",
progress: "进度",
addExistingTitle: "添加现有来源",
addExistingDesc: "从您的所有笔记本中选择已有的来源添加到当前笔记本。",
searchPlaceholder: "通过名称或 URL 搜索来源...",
@ -382,35 +384,6 @@ export const zhCN = {
showingFirst100: "仅显示前 100 个来源。请使用搜索功能查找特定来源。",
selectedCount: "已选择 {count} 个来源",
added: "已添加于 {date}",
noNotesYet: '暂无笔记',
createFirstNote: '创建您的第一条笔记,记录见解与观察。',
deleteNote: '删除笔记',
deleteNoteConfirm: '您确定要删除此笔记吗?此操作无法撤销。',
editNote: '编辑笔记',
createNote: '创建笔记',
addTitle: '添加标题...',
untitledNote: '无标题笔记',
writeNotePlaceholder: '在此处编写您的笔记内容...',
saveNote: '保存笔记',
createNoteBtn: '创建笔记',
urlLabel: 'URL(s) *',
fileLabel: '文件(s) *',
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: '{count} 个 URL',
filesCount: '{count} 个文件',
addSource: "添加来源",
addUrl: "添加 URL",
uploadFile: "上传文件",
enterText: "输入文本",
@ -435,8 +408,6 @@ export const zhCN = {
batchFailed: "全部 {count} 个来源创建失败",
batchPartial: "{success} 个成功,{failed} 个失败",
submittingSource: "正在提交来源进行处理...",
contentRequired: "请提供所选来源类型所需的内容",
titleRequiredForText: "文本来源需要提供标题",
processingBatchSources: "正在处理 {count} 个来源,请稍候...",
processingSource: "正在处理您的来源,请稍候...",
maxFilesAllowed: "每批最多允许 {count} 个文件",
@ -445,26 +416,20 @@ export const zhCN = {
sessions: "会话",
sessionTitlePlaceholder: "在此输入标题...",
noSessions: "暂无会话",
startChatting: "开始针对您的来源进行聊天。",
deleteSession: "删除会话",
deleteSessionDesc: "确定要删除此聊天会话吗?此操作无法撤销。",
sendPlaceholder: "向您的来源提问...",
newChat: "新建对话",
sessionsTitle: "对话列表",
clearhistory: "清空历史",
renameSession: "重命名会话",
noSourcesLinked: "未关联来源",
thinking: "AI 正在思考...",
chatWith: "与{name}对话",
startConversation: "开始针对{type}进行对话",
askQuestions: "提出问题以更好地理解内容",
pressToSend: "按 {key} 发送",
model: "模型",
createToStart: '创建一个会话以开始。',
chatWithNotebook: '与笔记本对话',
unableToLoadChat: '无法加载聊天',
noDescription: '暂无描述',
startByCreating: '从创建您的第一个笔记本开始,组织您的研究。',
createToStart: "创建一个会话以开始。",
chatWithNotebook: "与笔记本对话",
unableToLoadChat: "无法加载聊天",
noDescription: "暂无描述",
startByCreating: "从创建您的第一个笔记本开始,组织您的研究。",
messagesCount: "{count} 条消息",
sessionCreated: "聊天会话已创建",
sessionUpdated: "会话已更新",
@ -508,8 +473,10 @@ export const zhCN = {
saveSuccess: "成功保存到笔记本",
saveError: "保存到笔记本失败",
selectNotebook: "选择笔记本",
createNewNotebook: "创建新笔记本",
cancel: "取消",
searchAndAsk: "搜索与提问",
searchResultsFor: "搜索 “{query}”",
askAbout: "提问关于 “{query}”",
orSearchKb: "或搜索您的知识库",
saving: "保存中...",
advancedModelTitle: "高级模型选择",
advancedModelDesc: "为提问过程的每个阶段选择特定的模型",
@ -520,10 +487,6 @@ export const zhCN = {
selectAnswerPlaceholder: "选择回答模型",
selectFinalPlaceholder: "选择最终回答模型",
saveChanges: "保存更改",
searchAndAsk: "搜索与提问",
searchResultsFor: "搜索 “{query}”",
askAbout: "提问关于 “{query}”",
orSearchKb: "或搜索您的知识库",
processingQuestion: "正在处理您的问题...",
},
podcasts: {
@ -637,8 +600,41 @@ export const zhCN = {
createProfile: "创建简介",
createSpeakerFirst: "在添加单集简介之前,请先创建一个发言人简介。",
noEpisodeProfiles: "暂无单集简介。创建一个以启动播客生成。",
speakerCreated: "发言人配置已创建",
speakerCreatedDesc: "发言人配置已准备就绪。",
failedToCreateSpeaker: "创建发言人配置失败",
speakerUpdated: "发言人配置已更新",
speakerUpdatedDesc: "更改已成功保存。",
failedToUpdateSpeaker: "更新发言人配置失败",
speakerDeleted: "发言人配置已删除",
speakerDeletedDesc: "配置已成功移除。",
failedToDeleteSpeaker: "删除发言人配置失败",
speakerDuplicated: "发言人配置已复制",
speakerDuplicatedDesc: "已创建配置副本。",
failedToDuplicateSpeaker: "复制发言人配置失败",
generationStarted: "播客启动生成",
generationStartedDesc: "剧集 \"{name}\" 正在创建中。",
failedToStartGeneration: "启动播客生成失败",
tryAgainMoment: "请稍后再试。",
deleteProfileTitle: "删除简介?",
deleteProfileDesc: "这将移除 “{name}”。现有单集将保留其数据,但新单集将不再使用此配置。",
profileCreated: "剧集配置已创建",
profileCreatedDesc: "新的剧集配置已准备就绪。",
failedToCreateProfile: "创建剧集配置失败",
profileUpdated: "剧集配置已更新",
profileUpdatedDesc: "更改已成功保存。",
failedToUpdateProfile: "更新剧集配置失败",
profileDeleted: "剧集配置已删除",
profileDeletedDesc: "配置已成功移除。",
failedToDeleteProfile: "删除剧集配置失败",
failedToDeleteProfileDesc: "请确保配置未在使用中并重试。",
profileDuplicated: "剧集配置已复制",
profileDuplicatedDesc: "已创建配置副本。",
failedToDuplicateProfile: "复制剧集配置失败",
episodeDeleted: "剧集已删除",
episodeDeletedDesc: "播客剧集已成功移除。",
failedToDeleteEpisode: "删除剧集失败",
failedToDeleteSpeakerDesc: "请确保配置未在使用中并重试。",
outlineModel: "大纲模型",
transcriptModel: "脚本模型",
segments: "分段数量",
@ -701,42 +697,7 @@ export const zhCN = {
speakerCountMin: "至少需要一个发言人",
speakerCountMax: "最多只能配置 4 个发言人",
delete: "删除",
unknown: "未知",
deleteSuccess: "播客删除成功",
failedToDelete: "删除播客失败",
episodeDeleted: "剧集已删除",
episodeDeletedDesc: "播客剧集已成功移除。",
failedToDeleteEpisode: "删除剧集失败",
profileCreated: "剧集配置已创建",
profileCreatedDesc: "新的剧集配置已准备就绪。",
failedToCreateProfile: "创建剧集配置失败",
profileUpdated: "剧集配置已更新",
profileUpdatedDesc: "更改已成功保存。",
failedToUpdateProfile: "更新剧集配置失败",
profileDeleted: "剧集配置已删除",
profileDeletedDesc: "配置已成功移除。",
failedToDeleteProfile: "删除剧集配置失败",
failedToDeleteProfileDesc: "请确保配置未在使用中并重试。",
profileDuplicated: "剧集配置已复制",
profileDuplicatedDesc: "已创建配置副本。",
failedToDuplicateProfile: "复制剧集配置失败",
speakerCreated: "发言人配置已创建",
speakerCreatedDesc: "发言人配置已准备就绪。",
failedToCreateSpeaker: "创建发言人配置失败",
speakerUpdated: "发言人配置已更新",
speakerUpdatedDesc: "更改已成功保存。",
failedToUpdateSpeaker: "更新发言人配置失败",
speakerDeleted: "发言人配置已删除",
speakerDeletedDesc: "配置已成功移除。",
failedToDeleteSpeaker: "删除发言人配置失败",
failedToDeleteSpeakerDesc: "请确保配置未在使用中并重试。",
speakerDuplicated: "发言人配置已复制",
speakerDuplicatedDesc: "已创建配置副本。",
failedToDuplicateSpeaker: "复制发言人配置失败",
generationStarted: "播客启动生成",
generationStartedDesc: "剧集 \"{name}\" 正在创建中。",
failedToStartGeneration: "启动播客生成失败",
tryAgainMoment: "请稍后再试。",
},
settings: {
contentProcessing: "内容处理",
@ -772,13 +733,8 @@ export const zhCN = {
title: "高级工具",
desc: "面向进阶用户的调试和实用工具",
systemInfo: "系统信息",
systemInfoDesc: "查看底层系统组件的状态",
rebuildEmbeddings: "重建索引",
rebuildEmbeddingsDesc: "为所有来源重建向量索引",
rebuildWarning: "此操作可能非常耗时,具体取决于您的来源数量。它将清除现有的向量索引并重新为所有内容生成嵌入。",
startRebuild: "开始重建",
rebuilding: "正在重建...",
rebuildSuccess: "索引重建已成功启动",
currentVersion: "当前版本",
latestVersion: "最新版本",
status: "状态",
@ -823,105 +779,53 @@ export const zhCN = {
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: '转换规则删除成功',
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 不同用途的 AI 模型",
failedToLoad: "加载模型数据失败",
language: "语言模型",
embedding: "嵌入模型",
tts: "文字转语音",
stt: "语音转文字",
providers: "服务商",
defaultModels: "默认模型",
status: "状态",
notConfigured: "未配置",
active: "活动",
inactive: "不活动",
configure: "配置",
saveChanges: "保存更改",
addModel: "添加模型",
modelName: "模型名称",
provider: "服务商",
apiKey: "API 密钥",
baseUrl: "基础 URL",
capabilities: "功能",
enabled: "已启用",
disabled: "已禁用",
deleteConfirm: "确定要删除此模型吗?",
deleteSuccess: "模型删除成功",
saveSuccess: "模型保存成功",
providerStatus: "服务商状态",
connectionOk: "连接正常",
connectionFailed: "连接失败",
changeEmbeddingWarning: "更改默认嵌入模型将影响新的来源。现有来源可能需要重建索引。",
changeEmbeddingTitle: "更改默认嵌入模型?",
aiProviders: "AI 服务商",
providerConfigDesc: "通过环境变量配置服务商以启用其模型。",
configuredCount: "已配置 {count} / {total}",
noModels: "暂无模型",
learnMore: "了解如何配置服务商 →",
seeLess: "收起",
seeAll: "查看全部 {count} 个服务商",
language_models: "语言模型",
embedding_models: "嵌入模型",
text_to_speech: "文字转语音",
speech_to_text: "语音转文字",
languageDesc: "聊天、内容转换和文本生成",
embeddingDesc: "语义搜索和向量嵌入",
ttsDesc: "根据文本生成音频",
sttDesc: "将语音转录为文本",
all: "全部",
noModelsConfigured: "未配置模型",
noProviderModelsConfigured: "未配置 {provider} 模型",
showMore: "显示更多 ({count})",
discoverModels: "发现模型",
noModelsFound: "未从此提供商找到模型",
modelType: "模型类型",
modelTypeHint: "选择要添加的模型类型。如果需要不同类型,请分批添加。",
deleteModel: "删除模型",
deleteModelDesc: "确定要删除模型 “{name}” 吗?该操作无法撤销。",
defaultAssignments: "默认模型分配",
defaultAssignmentsDesc: "配置用于 Open Notebook 不同用途的默认模型",
missingRequiredModels: "缺少必需的模型:{models}。如果没有这些模型Open Notebook 可能无法正常运行。",
selectModelPlaceholder: "选择一个模型",
requiredModelPlaceholder: "⚠️ 必需 - 请选择一个模型",
whichModelToChoose: "我该选择哪个模型?→",
chatModelLabel: "聊天模型",
chatModelDesc: "用于聊天对话",
transformationModelLabel: "转换模型",
@ -936,16 +840,9 @@ export const zhCN = {
ttsModelDesc: "用于生成播客",
sttModelLabel: "语音转文字模型",
sttModelDesc: "用于音频转录",
addSpecificModel: "添加 {type} 模型",
addSpecificModelDesc: "从可用服务商配置一个新的 {type} 模型。",
noProvidersForType: "暂无可用于 {type} 模型的服务商",
selectProviderPlaceholder: "选择服务商",
providerRequired: "服务商是必填项",
modelNameRequired: "模型名称是必填项",
modelRequired: "模型是必填项",
adding: "正在添加...",
azureHint: "对于 Azure请使用部署名称作为模型名称",
enterModelName: "输入模型名称",
embeddingChangeTitle: "嵌入模型变更",
embeddingChangeConfirm: "您即将将嵌入模型从 {from} 更改为 {to}。",
rebuildRequired: "重要提示:需要重建索引",
@ -959,7 +856,6 @@ export const zhCN = {
changeModelOnly: "仅更改模型",
changeAndRebuild: "更改并前往重建",
autoAssign: "自动分配默认值",
autoAssignDesc: "为每个槽位自动分配最佳可用模型",
autoAssigning: "正在分配...",
autoAssignSuccess: "已自动分配 {count} 个默认模型",
autoAssignNoModels: "没有可分配的模型。请先同步模型。",
@ -967,25 +863,16 @@ export const zhCN = {
testModel: "测试模型",
testModelSuccess: "模型测试通过",
testModelFailed: "模型测试失败",
testingModel: "正在测试模型...",
searchOrAddModel: "搜索或输入模型名称...",
addCustomModel: '添加 "{name}"',
addCustomModel: "添加 \"{name}\"",
},
apiKeys: {
title: "使用您自己的 API 密钥配置 AI",
description: "将 API 密钥安全地存储在数据库中,以在 Open Notebook 中启用 AI 服务商。",
loadFailed: "加载 API 密钥状态失败",
encryptionRequired: "未配置加密密钥",
encryptionRequiredDescription: "请将 OPEN_NOTEBOOK_ENCRYPTION_KEY 环境变量设置为任意密钥字符串,以启用将 API 密钥存储到数据库。",
configured: "已配置",
notConfigured: "未配置",
sourceDatabase: "数据库",
sourceEnvironment: "环境变量",
enterApiKey: "输入您的 API 密钥",
enterBaseUrl: "输入基础 URL",
saveSuccess: "API 密钥保存成功",
deleteSuccess: "API 密钥删除成功",
fromEnvironmentHint: "此密钥通过环境变量设置。保存新密钥将在数据库中覆盖它。",
migrationAvailable: "检测到环境变量",
migrationDescription: "{count} 个 API 密钥通过环境变量配置,可以迁移到数据库以便于管理。",
migrateToDatabase: "迁移到数据库",
@ -993,67 +880,29 @@ export const zhCN = {
migrationSuccess: "{count} 个 API 密钥迁移成功",
migrationErrors: "{count} 个密钥迁移失败",
migrationNothingToMigrate: "所有密钥已在数据库中",
serviceType: "服务类型",
serviceLlm: "语言模型 (LLM)",
serviceEmbedding: "嵌入",
serviceStt: "语音转文字 (STT)",
serviceTts: "文字转语音 (TTS)",
serviceEndpoints: "服务端点(可选)",
azureEndpointsHint: "如有需要,为每种服务类型配置不同的端点。",
endpointPlaceholder: "https://your-resource.openai.azure.com/",
openaiCompatibleHint: "配置 OpenAI 兼容的 API 端点。每种服务类型可以有自己的配置。",
baseUrlPlaceholder: "https://api.example.com/v1",
learnMore: "了解如何配置 API 密钥 →",
testConnection: "测试连接",
testing: "测试中...",
testSuccess: "连接成功",
testFailed: "连接测试失败",
syncModels: "同步模型",
syncing: "同步中...",
syncSuccess: "发现 {discovered} 个模型,新增 {new} 个",
syncNoNew: "发现 {count} 个模型,全部已注册",
syncFailed: "同步模型失败",
syncAllModels: "同步所有提供商",
syncAllSuccess: "在所有提供商中发现 {discovered} 个模型,新增 {new} 个",
modelsConfigured: "{count} 个模型",
noModelsConfigured: "无模型",
viewModels: "查看模型",
supportedTypes: "支持的类型",
typeLanguage: "语言",
typeEmbedding: "嵌入",
typeTts: "TTS",
typeStt: "STT",
apiEndpoint: "API 端点",
getApiKey: "获取 API 密钥",
vertexProject: "GCP 项目 ID",
vertexLocation: "区域",
vertexCredentials: "服务账户 JSON 路径",
vertexCredentialsHint: "容器内 Google Cloud 服务账户 JSON 文件的路径。",
// Multi-config translations
configsCount: "{count} 个配置",
configuredMultiple: "已配置",
addConfig: "添加配置",
editConfig: "编辑配置",
deleteConfig: "删除配置",
setAsDefault: "设为默认",
defaultBadge: "默认",
defaultDescription: "此提供商的默认配置",
configName: "配置名称",
configNameHint: "此配置的描述性名称(例如:'生产环境'、'开发环境'",
baseUrl: "基础 URL",
baseUrlHint: "默认:{url}",
baseUrlOverrideHint: "仅在需要覆盖提供商默认 API 端点时更改此项。",
ollamaApiKeyHint: "仅 Ollama Cloud 需要 API 密钥。本地 Ollama 请留空。",
noConfigs: "暂无配置",
noConfigsHint: "添加配置以开始使用此提供商",
deleteConfigConfirm: "确定要删除 '{name}' 吗?此操作无法撤销。",
setDefaultConfirm: "将 '{name}' 设为默认配置?",
configSaveSuccess: "配置保存成功",
configUpdateSuccess: "配置更新成功",
configDeleteSuccess: "配置删除成功",
configSetDefaultSuccess: "默认配置已更新",
apiKeyHint: "输入此配置的 API 密钥",
apiKeyEditHint: "留空以保留现有 API 密钥",
},
setupBanner: {

View file

@ -1,29 +1,24 @@
export const zhTW = {
common: {
search: "搜尋...",
chat: '聊天',
notes: '筆記',
create: "新增",
new: "新建",
cancel: "取消",
save: "儲存",
delete: "刪除",
edit: "編輯",
actions: "快捷操作",
theme: "主題",
signOut: "登出",
noMatches: "沒有找到匹配項",
tryDifferentSearch: "請嘗試使用不同的關鍵詞搜尋。",
light: "亮色",
dark: "暗色",
system: "系統",
loading: "載入中...",
note: "筆記",
insight: "洞察",
newSource: "新增來源",
newNotebook: "新增筆記本",
newPodcast: "新增播客",
nameRequired: "此為必填項",
noMatches: "沒有找到匹配項",
tryDifferentSearch: "請嘗試使用不同的關鍵詞搜尋。",
light: "亮色",
dark: "暗色",
system: "系統",
language: "語言",
english: "English",
chinese: "簡體中文",
@ -41,31 +36,27 @@ export const zhTW = {
warning: "警告",
error: "錯誤",
success: "成功",
sessions: "對話",
model: "模型",
send: "發送",
back: "返回",
next: "下一步",
done: "完成",
processing: "處理中...",
creating: "正在新增...",
tokenCount: "Token",
charCount: "字元",
linked: "已連結",
added: "已於 {date} 新增",
adding: "正在新增...",
addSelected: "新增所選",
customModel: "自訂模型",
messages: "訊息",
failed: "失敗",
current: "目前",
writeNote: '撰寫筆記',
save: "儲存",
writeNote: "撰寫筆記",
batchMode: "批次模式",
optional: "可選",
type: "類型",
title: "標題",
created: "建立於 {time}",
updated: "更新於 {time}",
actions: "快捷操作",
noResults: "未找到結果",
references: "引用",
refreshPage: "請嘗試重新整理頁面",
@ -73,7 +64,8 @@ export const zhTW = {
aiGenerated: "AI 生成",
human: "人類",
unknown: "未知",
details: "詳情",
notes: "筆記",
chat: "聊天",
deleteForever: "永久刪除",
connectionError: "連線錯誤",
unableToConnect: "無法連線至 API 伺服器",
@ -86,7 +78,6 @@ export const zhTW = {
checkConsoleLogs: "請檢查瀏覽器主控台以獲取詳細日誌(搜尋 🔧 [Config] 訊息)",
yes: "是",
no: "否",
simple: "簡單",
saving: "正在儲存...",
description: "描述",
saveToNote: "儲存到筆記",
@ -101,9 +92,9 @@ export const zhTW = {
saveChanges: "儲存更改",
name: "名稱",
default: "預設",
nameRequired: "此為必填項",
modelConfiguration: "模型設定",
resetToDefault: "重置為預設",
notFound: "未找到",
reasoning: "推理過程",
searchTerms: "搜尋詞",
strategy: "策略",
@ -112,22 +103,19 @@ export const zhTW = {
notebookLabel: "筆記本: {name}",
itemNotFound: "未找到該 {type}",
accessibility: {
navigation: "導覽",
transformationViews: "轉換視圖",
searchKB: "向知識庫提問或搜尋",
searchNotebooks: "搜尋筆記本",
enterQuestion: "輸入您的問題以詢問知識庫",
enterSearch: "輸入搜尋詞",
searchKBBtn: "搜尋知識庫",
podcastViews: "播客視圖",
chatSessions: "對話 Session",
ytVideo: "YouTube 影片",
askResponse: "提問回答",
searchNotebooks: "搜尋筆記本",
},
url: "URL",
errorDetails: "錯誤詳情",
editTransformation: "編輯轉換規則",
comingSoon: "敬請期待",
retry: "重試",
traditionalChinese: "繁體中文",
portuguese: "葡萄牙語",
@ -155,17 +143,16 @@ export const zhTW = {
invalidSortOrder: "排序方向必須是 'asc' 或 'desc'",
accessDenied: "檔案存取被拒絕",
fileNotFoundOnServer: "伺服器上找不到該檔案",
searchFailed: "搜尋失敗",
askFailed: "提問失敗",
pleaseEnterQuestion: "請輸入問題",
pleaseConfigureModels: "請設定所有必選模型",
failedToCreateSession: "新增對話失敗",
failedToUpdateSession: "更新對話失敗",
failedToDeleteSession: "刪除對話失敗",
failedToSendMessage: "發送訊息失敗",
pleaseEnterQuestion: "請輸入問題",
pleaseConfigureModels: "請設定所有必選模型",
askFailed: "提問失敗",
searchFailed: "搜尋失敗",
unauthorized: "無權存取,請檢查您的密碼",
invalidPassword: "密碼錯誤",
missingAuth: "缺少身分驗證資訊",
embeddingModelRequired: "此功能需要嵌入模型。請在模型設定中設定一個。",
strategyModelNotFound: "未找到策略模型",
answerModelNotFound: "未找到回答模型",
@ -206,7 +193,6 @@ export const zhTW = {
passwordPlaceholder: "密碼",
signingIn: "正在登入...",
signIn: "登入",
unhandledError: "登入過程中出現未處理的錯誤",
connectErrorHint: "無法連線至伺服器。請檢查 API 是否正在運行。",
},
navigation: {
@ -217,8 +203,6 @@ export const zhTW = {
sources: "來源",
notebooks: "筆記本",
askAndSearch: "詢問與搜尋",
search: "搜尋",
ask: "提問",
podcasts: "播客",
models: "模型",
transformations: "轉換",
@ -228,6 +212,7 @@ export const zhTW = {
nav: "導覽",
language: "切換語言",
theme: "主題",
ask: "提問",
},
notebooks: {
title: "筆記本",
@ -248,12 +233,8 @@ export const zhTW = {
keepExclusiveSourcesLabel: "取消關聯並保留",
activeNotebooks: "活動中的筆記本",
archivedNotebooks: "封存的筆記本",
emptyDescription: "從新增您的第一個筆記本開始,組織您的研究。",
noActiveNotebooks: "沒有活動中的筆記本",
noArchivedNotebooks: "沒有封存的筆記本",
notFound: "未找到筆記本",
notFoundDesc: "請求的筆記本不存在。",
noDescription: "暫無描述...",
updated: "已更新",
namePlaceholder: "筆記本名稱",
addDescription: "新增描述...",
@ -278,12 +259,7 @@ export const zhTW = {
add: "新增來源",
addNew: "新增新來源",
addExisting: "新增現有來源",
allSourcesDescShort: "在此檢視所有來源。",
cannotSaveNoteNoNotebook: "無法儲存筆記:缺少筆記本 ID",
empty: "暫無來源",
emptyDesc: "新增您的第一個來源,開始構建您的知識庫。",
delete: "刪除來源",
deleteMsg: "確定要刪除此來源嗎?此操作無法撤銷。",
statusPreparing: "正在準備",
statusQueued: "已排隊",
statusProcessing: "正在處理",
@ -301,6 +277,11 @@ export const zhTW = {
yes: "是",
no: "否",
loadingMore: "正在載入更多...",
noSourcesYet: "暫無來源",
allSourcesDescShort: "在此檢視所有來源。",
cannotSaveNoteNoNotebook: "無法儲存筆記:缺少筆記本 ID",
createFirstSource: "新增您的第一個來源開始構建知識庫。",
deleteSourceConfirm: "確定要刪除此來源嗎?",
deleteConfirm: "確定要刪除嗎?",
deleteConfirmWithTitle: "確定要刪除 \"{title}\" 嗎?",
deleteSuccess: "來源刪除成功。注意:要從儲存中刪除檔案,必須在設定頁面中啟用「刪除檔案」選項。",
@ -318,16 +299,12 @@ export const zhTW = {
sourceRequeued: "來源重試已加入隊列",
sourceRequeuedDesc: "來源已重新加入處理隊列。",
failedToRetry: "重試失敗",
failedToRetryDesc: "重試來源處理失敗。請重試。",
sourcesAddedToNotebook: "{count} 個來源已新增到筆記本",
failedToAddSourcesToNotebook: "新增來源到筆記本失敗",
partialAddSuccess: "{success} 個來源已新增,{failed} 個失敗",
sourceRemovedFromNotebook: "來源已成功從筆記本中移除",
failedToRemoveSourceFromNotebook: "從筆記本中移除來源失敗",
removeConfirm: "確定要從此筆記本移除嗎?",
noSourcesYet: "暫無來源",
createFirstSource: "新增您的第一個來源開始構建知識庫。",
deleteSourceConfirm: "確定要刪除此來源嗎?",
checking: "正在檢查...",
untitledSource: "未命名來源",
maxItems: "最多 {count} 個",
@ -356,25 +333,50 @@ export const zhTW = {
noInsightsYet: "暫無見解",
createFirstInsight: "使用上方的轉換規則新增您的第一個見解",
viewInsight: "查看見解",
deleteInsight: "刪除見解",
deleteInsightConfirm: "確定要刪除此見解嗎?此操作無法撤銷。",
insightGenerationStarted: "見解生成已開始,稍後將顯示。",
editNote: "編輯筆記",
createNote: "新增筆記",
addTitle: "新增標題...",
untitledNote: "無標題筆記",
writeNotePlaceholder: "在此處編寫您的筆記內容...",
saveNote: "儲存筆記",
createNoteBtn: "新增筆記",
createFirstNote: "新增您的第一條筆記,記錄見解與觀察。",
urlLabel: "URL(s) *",
fileLabel: "檔案(s) *",
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: "{count} 個 URL",
filesCount: "{count} 個檔案",
addSource: "新增來源",
notEmbeddedAlert: "內容未嵌入向量",
notEmbeddedDesc: "此內容尚未為了向量搜尋進行嵌入。嵌入可以啟用進階搜尋功能並更好地發現內容。",
openOnYoutube: "在 YouTube 上開啟",
urlCopied: "URL 已複製到剪貼簿",
viewSource: "查看來源",
noInsightSelected: "未選擇見解",
sourceInsight: "來源見解",
manageNotebooks: "管理所屬筆記本",
manageNotebooksDesc: "管理包含此來源的筆記本",
noNotebooksAvailable: "暫無可用筆記本",
deleteInsight: "刪除見解",
deleteInsightConfirm: "確定要刪除此見解嗎?此操作無法撤銷。",
insightGenerationStarted: "見解生成已開始,稍後將顯示。",
notEmbeddedAlert: "內容未嵌入向量",
notEmbeddedDesc: "此內容尚未為了向量搜尋進行嵌入。嵌入可以啟用進階搜尋功能並更好地發現內容。",
openOnYoutube: "在 YouTube 上開啟",
urlCopied: "URL 已複製到剪貼簿",
loadFailed: "載入來源詳情失敗",
removeFromNotebook: "從筆記本移除",
retryProcessing: "重試處理",
deleteSource: "刪除來源",
retry: "重試",
progress: "進度",
addExistingTitle: "新增現有來源",
addExistingDesc: "從您的所有筆記本中選擇已有的來源新增到當前筆記本。",
searchPlaceholder: "通過名稱或 URL 搜尋來源...",
@ -382,35 +384,6 @@ export const zhTW = {
showingFirst100: "僅顯示前 100 個來源。請使用搜尋功能查找特定來源。",
selectedCount: "已選擇 {count} 個來源",
added: "已新增於 {date}",
noNotesYet: '暫無筆記',
createFirstNote: "新增您的第一條筆記,記錄見解與觀察。",
deleteNote: '刪除筆記',
deleteNoteConfirm: '您確定要刪除此筆記嗎?此操作無法撤銷。',
editNote: '編輯筆記',
createNote: "新增筆記",
addTitle: '新增標題...',
untitledNote: '無標題筆記',
writeNotePlaceholder: '在此處編寫您的筆記內容...',
saveNote: '儲存筆記',
createNoteBtn: "新增筆記",
urlLabel: 'URL(s) *',
fileLabel: '檔案(s) *',
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: '{count} 個 URL',
filesCount: '{count} 個檔案',
addSource: "新增來源",
addUrl: "新增 URL",
uploadFile: "上傳檔案",
enterText: "輸入文字",
@ -435,8 +408,6 @@ export const zhTW = {
batchFailed: "全部 {count} 個來源新增失敗",
batchPartial: "{success} 個成功,{failed} 個失敗",
submittingSource: "正在提交來源進行處理...",
contentRequired: "請提供所選來源類型所需的內容",
titleRequiredForText: "文字來源需要提供標題",
processingBatchSources: "正在處理 {count} 個來源,請稍候...",
processingSource: "正在處理您的來源,請稍候...",
maxFilesAllowed: "每批最多允許 {count} 個檔案",
@ -445,25 +416,19 @@ export const zhTW = {
sessions: "對話",
sessionTitlePlaceholder: "在此輸入標題...",
noSessions: "暫無對話",
startChatting: "開始針對您的來源進行聊天。",
deleteSession: "刪除對話",
deleteSessionDesc: "確定要刪除此聊天會話嗎?此操作無法撤銷。",
sendPlaceholder: "向您的來源提問...",
newChat: "新建對話",
sessionsTitle: "對話列表",
clearhistory: "清空歷史",
renameSession: "重新命名對話",
noSourcesLinked: "未連結來源",
thinking: "AI 正在思考...",
chatWith: "與 {name} 對話",
startConversation: "開始針對 {type} 進行對話",
askQuestions: "提出問題以更好地理解內容",
pressToSend: "按 {key} 發送",
model: "模型",
createToStart: "新增一個會話以開始。",
chatWithNotebook: '與筆記本對話',
unableToLoadChat: '無法載入聊天',
noDescription: '暫無描述',
chatWithNotebook: "與筆記本對話",
unableToLoadChat: "無法載入聊天",
noDescription: "暫無描述",
startByCreating: "從新增您的第一個筆記本開始,組織您的研究。",
messagesCount: "{count} 條訊息",
sessionCreated: "聊天會話已建立",
@ -508,8 +473,10 @@ export const zhTW = {
saveSuccess: "成功儲存到筆記本",
saveError: "儲存到筆記本失敗",
selectNotebook: "選擇筆記本",
createNewNotebook: "建立新筆記本",
cancel: "取消",
searchAndAsk: "搜尋與提問",
searchResultsFor: "搜尋 “{query}”",
askAbout: "提問關於 “{query}”",
orSearchKb: "或搜尋您的知識庫",
saving: "儲存中...",
advancedModelTitle: "進階模型選擇",
advancedModelDesc: "為提問過程的每個階段選擇模型",
@ -520,10 +487,6 @@ export const zhTW = {
selectAnswerPlaceholder: "選擇回答模型",
selectFinalPlaceholder: "選擇最終回答模型",
saveChanges: "儲存更改",
searchAndAsk: "搜尋與提問",
searchResultsFor: "搜尋 “{query}”",
askAbout: "提問關於 “{query}”",
orSearchKb: "或搜尋您的知識庫",
processingQuestion: "正在處理您的問題...",
},
podcasts: {
@ -637,8 +600,41 @@ export const zhTW = {
createProfile: "建立簡介",
createSpeakerFirst: "在新增單集簡介之前,請先建立一個發言人簡介。",
noEpisodeProfiles: "暫無單集簡介。建立一個以啟動播客生成。",
speakerCreated: "發言人設定已建立",
speakerCreatedDesc: "發言人設定已準備就緒。",
failedToCreateSpeaker: "建立發言人設定失敗",
speakerUpdated: "發言人設定已更新",
speakerUpdatedDesc: "更改已成功儲存。",
failedToUpdateSpeaker: "更新發言人設定失敗",
speakerDeleted: "發言人設定已刪除",
speakerDeletedDesc: "設定已成功移除。",
failedToDeleteSpeaker: "刪除發言人設定失敗",
speakerDuplicated: "發言人設定已複製",
speakerDuplicatedDesc: "已建立設定副本。",
failedToDuplicateSpeaker: "複製發言人設定失敗",
generationStarted: "播客啟動生成",
generationStartedDesc: "劇集 \"{name}\" 正在建立中。",
failedToStartGeneration: "啟動播客生成失敗",
tryAgainMoment: "請稍後再試。",
deleteProfileTitle: "刪除簡介?",
deleteProfileDesc: "這將移除 “{name}”。現有單集將保留其資料,但新單集將不再使用此設定。",
profileCreated: "劇集設定已建立",
profileCreatedDesc: "新的劇集設定已準備就緒。",
failedToCreateProfile: "建立劇集設定失敗",
profileUpdated: "劇集設定已更新",
profileUpdatedDesc: "更改已成功儲存。",
failedToUpdateProfile: "更新劇集設定失敗",
profileDeleted: "劇集設定已刪除",
profileDeletedDesc: "設定已成功移除。",
failedToDeleteProfile: "刪除劇集設定失敗",
failedToDeleteProfileDesc: "請確保設定未在使用中並重試。",
profileDuplicated: "劇集設定已複製",
profileDuplicatedDesc: "已建立設定副本。",
failedToDuplicateProfile: "複製劇集設定失敗",
episodeDeleted: "劇集已刪除",
episodeDeletedDesc: "播客劇集已成功移除。",
failedToDeleteEpisode: "刪除劇集失敗",
failedToDeleteSpeakerDesc: "請確保設定未在使用中並重試。",
outlineModel: "大綱模型",
transcriptModel: "腳本模型",
segments: "分段數量",
@ -701,42 +697,7 @@ export const zhTW = {
speakerCountMin: "至少需要一個發言人",
speakerCountMax: "最多只能設定 4 個發言人",
delete: "刪除",
unknown: "未知",
deleteSuccess: "播客刪除成功",
failedToDelete: "刪除播客失敗",
episodeDeleted: "劇集已刪除",
episodeDeletedDesc: "播客劇集已成功移除。",
failedToDeleteEpisode: "刪除劇集失敗",
profileCreated: "劇集設定已建立",
profileCreatedDesc: "新的劇集設定已準備就緒。",
failedToCreateProfile: "建立劇集設定失敗",
profileUpdated: "劇集設定已更新",
profileUpdatedDesc: "更改已成功儲存。",
failedToUpdateProfile: "更新劇集設定失敗",
profileDeleted: "劇集設定已刪除",
profileDeletedDesc: "設定已成功移除。",
failedToDeleteProfile: "刪除劇集設定失敗",
failedToDeleteProfileDesc: "請確保設定未在使用中並重試。",
profileDuplicated: "劇集設定已複製",
profileDuplicatedDesc: "已建立設定副本。",
failedToDuplicateProfile: "複製劇集設定失敗",
speakerCreated: "發言人設定已建立",
speakerCreatedDesc: "發言人設定已準備就緒。",
failedToCreateSpeaker: "建立發言人設定失敗",
speakerUpdated: "發言人設定已更新",
speakerUpdatedDesc: "更改已成功儲存。",
failedToUpdateSpeaker: "更新發言人設定失敗",
speakerDeleted: "發言人設定已刪除",
speakerDeletedDesc: "設定已成功移除。",
failedToDeleteSpeaker: "刪除發言人設定失敗",
failedToDeleteSpeakerDesc: "請確保設定未在使用中並重試。",
speakerDuplicated: "發言人設定已複製",
speakerDuplicatedDesc: "已建立設定副本。",
failedToDuplicateSpeaker: "複製發言人設定失敗",
generationStarted: "播客啟動生成",
generationStartedDesc: "劇集 \"{name}\" 正在建立中。",
failedToStartGeneration: "啟動播客生成失敗",
tryAgainMoment: "請稍後再試。",
},
settings: {
contentProcessing: "內容處理",
@ -772,13 +733,8 @@ export const zhTW = {
title: "進階工具",
desc: "針對進階使用者的調試和實用工具",
systemInfo: "系統資訊",
systemInfoDesc: "查看底層系統組件的狀態",
rebuildEmbeddings: "重建索引",
rebuildEmbeddingsDesc: "為所有來源重建向量索引",
rebuildWarning: "此操作可能非常耗時,具體取決於您的來源數量。它將清除現有的向量索引並重新為所有內容生成嵌入。",
startRebuild: "開始重建",
rebuilding: "正在重建...",
rebuildSuccess: "索引重建已成功啟動",
currentVersion: "目前版本",
latestVersion: "最新版本",
status: "狀態",
@ -823,105 +779,53 @@ export const zhTW = {
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: '轉換規則刪除成功',
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 不同用途的 AI 模型",
failedToLoad: "載入模型資料失敗",
language: "語言模型",
embedding: "嵌入模型",
tts: "文字轉語音",
stt: "語音轉文字",
providers: "提供商",
defaultModels: "預設模型",
status: "狀態",
notConfigured: "未設定",
active: "活動",
inactive: "不活動",
configure: "設定",
saveChanges: "儲存更改",
addModel: "新增模型",
modelName: "模型名稱",
provider: "提供商",
apiKey: "API 密鑰",
baseUrl: "基礎 URL",
capabilities: "功能",
enabled: "已啟用",
disabled: "已禁用",
deleteConfirm: "確定要刪除此模型嗎?",
deleteSuccess: "模型刪除成功",
saveSuccess: "模型儲存成功",
providerStatus: "提供商狀態",
connectionOk: "連線正常",
connectionFailed: "連線失敗",
changeEmbeddingWarning: "更改預設嵌入模型將影響新的來源。現有來源可能需要重建索引。",
changeEmbeddingTitle: "更改預設嵌入模型?",
aiProviders: "AI 提供商",
providerConfigDesc: "通過環境變數設定提供商以啟用其模型。",
configuredCount: "已設定 {count} / {total}",
noModels: "暫無模型",
learnMore: "了解如何設定提供商 →",
seeLess: "收起",
seeAll: "查看全部 {count} 個提供商",
language_models: "語言模型",
embedding_models: "嵌入模型",
text_to_speech: "文字轉語音",
speech_to_text: "語音轉文字",
languageDesc: "聊天、內容轉換和文本生成",
embeddingDesc: "語義搜尋和向量嵌入",
ttsDesc: "根據文本生成音訊",
sttDesc: "將語音轉錄為文本",
all: "全部",
noModelsConfigured: "未設定模型",
noProviderModelsConfigured: "未設定 {provider} 模型",
showMore: "顯示更多 ({count})",
discoverModels: "探索模型",
noModelsFound: "未從此提供商找到模型",
modelType: "模型類型",
modelTypeHint: "選擇要新增的模型類型。如果需要不同類型,請分批新增。",
deleteModel: "刪除模型",
deleteModelDesc: "確定要刪除模型 “{name}” 嗎?該操作無法撤銷。",
defaultAssignments: "預設模型分配",
defaultAssignmentsDesc: "設定用於 Open Notebook 不同用途的預設模型",
missingRequiredModels: "缺少必需的模型:{models}。如果没有這些模型Open Notebook 可能無法正常運行。",
selectModelPlaceholder: "選擇一個模型",
requiredModelPlaceholder: "⚠️ 必需 - 請選擇一個模型",
whichModelToChoose: "我該選擇哪個模型?→",
chatModelLabel: "聊天模型",
chatModelDesc: "用於聊天對話",
transformationModelLabel: "轉換模型",
@ -936,16 +840,9 @@ export const zhTW = {
ttsModelDesc: "用於生成播客",
sttModelLabel: "語音轉文字模型",
sttModelDesc: "用於音訊轉錄",
addSpecificModel: "新增 {type} 模型",
addSpecificModelDesc: "從可用提供商設定一個新的 {type} 模型。",
noProvidersForType: "暫無可用於 {type} 模型的提供商",
selectProviderPlaceholder: "選擇提供商",
providerRequired: "提供商是必填項",
modelNameRequired: "模型名稱是必填項",
modelRequired: "模型是必填項",
adding: "正在新增...",
azureHint: "對於 Azure請使用部署名稱作為模型名稱",
enterModelName: "輸入模型名稱",
embeddingChangeTitle: "嵌入模型變更",
embeddingChangeConfirm: "您即將將嵌入模型從 {from} 更改為 {to}。",
rebuildRequired: "重要提示:需要重建索引",
@ -959,7 +856,6 @@ export const zhTW = {
changeModelOnly: "僅更改模型",
changeAndRebuild: "更改並前往重建",
autoAssign: "自動指派預設值",
autoAssignDesc: "為每個插槽自動指派最佳可用模型",
autoAssigning: "正在指派...",
autoAssignSuccess: "已自動指派 {count} 個預設模型",
autoAssignNoModels: "沒有可指派的模型。請先同步模型。",
@ -967,25 +863,16 @@ export const zhTW = {
testModel: "測試模型",
testModelSuccess: "模型測試通過",
testModelFailed: "模型測試失敗",
testingModel: "正在測試模型...",
searchOrAddModel: "搜尋或輸入模型名稱...",
addCustomModel: '新增 "{name}"',
addCustomModel: "新增 \"{name}\"",
},
apiKeys: {
title: "使用您自己的 API 金鑰設定 AI",
description: "將 API 金鑰安全地儲存在資料庫中,以在 Open Notebook 中啟用 AI 服務商。",
loadFailed: "載入 API 金鑰狀態失敗",
encryptionRequired: "未設定加密金鑰",
encryptionRequiredDescription: "請將 OPEN_NOTEBOOK_ENCRYPTION_KEY 環境變數設定為任意密鑰字串,以啟用將 API 金鑰儲存至資料庫。",
configured: "已設定",
notConfigured: "未設定",
sourceDatabase: "資料庫",
sourceEnvironment: "環境變數",
enterApiKey: "輸入您的 API 金鑰",
enterBaseUrl: "輸入基礎 URL",
saveSuccess: "API 金鑰儲存成功",
deleteSuccess: "API 金鑰刪除成功",
fromEnvironmentHint: "此金鑰通過環境變數設定。儲存新金鑰將在資料庫中覆蓋它。",
migrationAvailable: "偵測到環境變數",
migrationDescription: "{count} 個 API 金鑰通過環境變數設定,可以遷移到資料庫以便於管理。",
migrateToDatabase: "遷移到資料庫",
@ -993,67 +880,29 @@ export const zhTW = {
migrationSuccess: "{count} 個 API 金鑰遷移成功",
migrationErrors: "{count} 個金鑰遷移失敗",
migrationNothingToMigrate: "所有金鑰已在資料庫中",
serviceType: "服務類型",
serviceLlm: "語言模型 (LLM)",
serviceEmbedding: "嵌入",
serviceStt: "語音轉文字 (STT)",
serviceTts: "文字轉語音 (TTS)",
serviceEndpoints: "服務端點(選填)",
azureEndpointsHint: "如有需要,為每種服務類型設定不同的端點。",
endpointPlaceholder: "https://your-resource.openai.azure.com/",
openaiCompatibleHint: "設定 OpenAI 相容的 API 端點。每種服務類型可以有自己的設定。",
baseUrlPlaceholder: "https://api.example.com/v1",
learnMore: "瞭解如何設定 API 金鑰 →",
testConnection: "測試連線",
testing: "測試中...",
testSuccess: "連線成功",
testFailed: "連線測試失敗",
syncModels: "同步模型",
syncing: "同步中...",
syncSuccess: "發現 {discovered} 個模型,新增 {new} 個",
syncNoNew: "發現 {count} 個模型,全部已註冊",
syncFailed: "同步模型失敗",
syncAllModels: "同步所有供應商",
syncAllSuccess: "在所有供應商中發現 {discovered} 個模型,新增 {new} 個",
modelsConfigured: "{count} 個模型",
noModelsConfigured: "無模型",
viewModels: "查看模型",
supportedTypes: "支援的類型",
typeLanguage: "語言",
typeEmbedding: "嵌入",
typeTts: "TTS",
typeStt: "STT",
apiEndpoint: "API 端點",
getApiKey: "取得 API 金鑰",
vertexProject: "GCP 專案 ID",
vertexLocation: "區域",
vertexCredentials: "服務帳戶 JSON 路徑",
vertexCredentialsHint: "容器內 Google Cloud 服務帳戶 JSON 檔案的路徑。",
// Multi-config translations
configsCount: "{count} 個設定",
configuredMultiple: "已設定",
addConfig: "新增設定",
editConfig: "編輯設定",
deleteConfig: "刪除設定",
setAsDefault: "設為預設",
defaultBadge: "預設",
defaultDescription: "此供應商的預設設定",
configName: "設定名稱",
configNameHint: "此設定的描述性名稱(例如:'生產環境'、'開發環境'",
baseUrl: "基礎 URL",
baseUrlHint: "預設:{url}",
baseUrlOverrideHint: "僅在需要覆蓋提供商預設 API 端點時更改此項。",
ollamaApiKeyHint: "僅 Ollama Cloud 需要 API 金鑰。本地 Ollama 請留空。",
noConfigs: "暫無設定",
noConfigsHint: "新增設定以開始使用此供應商",
deleteConfigConfirm: "確定要刪除 '{name}' 嗎?此操作無法撤銷。",
setDefaultConfirm: "將 '{name}' 設為預設設定?",
configSaveSuccess: "設定儲存成功",
configUpdateSuccess: "設定更新成功",
configDeleteSuccess: "設定刪除成功",
configSetDefaultSuccess: "預設設定已更新",
apiKeyHint: "輸入此設定的 API 金鑰",
apiKeyEditHint: "留空以保留現有 API 金鑰",
},
setupBanner: {