-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Expand file tree
/
Copy pathindex.ts
More file actions
993 lines (992 loc) · 48.6 KB
/
Copy pathindex.ts
File metadata and controls
993 lines (992 loc) · 48.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
export const enUS = {
common: {
search: "Search...",
create: "New",
new: "New",
cancel: "Cancel",
delete: "Delete",
edit: "Edit",
theme: "Theme",
signOut: "Sign Out",
noMatches: "No matches found",
tryDifferentSearch: "Try using a different search term.",
light: "Light",
dark: "Dark",
system: "System",
loading: "Loading...",
note: "Note",
insight: "Insight",
newSource: "New Source",
newNotebook: "New Notebook",
newPodcast: "New Podcast",
language: "Language",
english: "English",
chinese: "简体中文",
japanese: "日本語",
french: "Français",
russian: "Русский",
bengali: "বাংলা",
catalan: "Català",
spanish: "Español",
german: "Deutsch",
polish: "Polski",
turkish: "Türkçe",
source: "Source",
notebook: "Notebook",
podcast: "Podcast",
quickActions: "Quick actions",
quickActionsDesc: "Navigation, search, ask, theme",
appName: "Open Notebook",
add: "Add",
remove: "Remove",
confirm: "Confirm",
warning: "Warning",
error: "Error",
success: "Success",
model: "Model",
back: "Back",
next: "Next",
done: "Done",
processing: "Processing...",
creating: "Creating...",
linked: "Linked",
adding: "Adding...",
addSelected: "Add Selected",
customModel: "Custom Model",
failed: "failed",
current: "Current",
save: "Save",
writeNote: "Write Note",
batchMode: "Batch Mode",
optional: "Optional",
type: "Type",
title: "Title",
created: "Created {{time}}",
updated: "Updated {{time}}",
actions: "Actions",
noResults: "No results",
references: "References",
refreshPage: "Please try refreshing the page",
refresh: "Refresh",
aiGenerated: "AI Generated",
human: "Human",
unknown: "Unknown",
notes: "Notes",
chat: "Chat",
deleteForever: "Delete Forever",
connectionError: "Connection Error",
unableToConnect: "Unable to connect to the API server",
retryConnection: "Retry Connection",
diagnosticInfo: "Diagnostic Information",
version: "Version",
built: "Built",
apiUrl: "API URL",
frontendUrl: "Frontend URL",
checkConsoleLogs: "Check browser console for detailed logs (look for 🔧 [Config] messages)",
yes: "Yes",
no: "No",
saving: "Saving...",
description: "Description",
saveToNote: "Save to note",
copyToClipboard: "Copy to clipboard",
close: "Close",
insights: "Insights",
progress: "Progress",
deleting: "Deleting...",
created_label: "Created",
updated_label: "Updated",
download: "Download",
saveChanges: "Save Changes",
name: "Name",
default: "Default",
nameRequired: "Name is required",
modelConfiguration: "Model Configuration",
resetToDefault: "Reset to Default",
reasoning: "Reasoning",
searchTerms: "Search Terms",
strategy: "Strategy",
individualAnswers: "Individual Answers ({{count}})",
finalAnswer: "Final Answer",
notebookLabel: "Notebook: {{name}}",
itemNotFound: "This {{type}} could not be found",
contentUnavailable: {
notFoundTitle: "This content no longer exists",
notFoundDescription: "It may have been deleted. References in older messages can point to content that has since been removed.",
errorTitle: "Unable to load this content",
errorDescription: "Something went wrong while loading it. Please try again.",
},
accessibility: {
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",
ytVideo: "YouTube video",
askResponse: "Ask Response",
searchNotebooks: "Search notebooks",
},
url: "URL",
errorDetails: "Error Details",
editTransformation: "Edit Transformation",
retry: "Try Again",
traditionalChinese: "繁體中文",
portuguese: "Português",
completed: "completed",
saveSuccess: "Saved successfully",
contextModes: {
off: "Not included in chat",
insights: "Insights only",
full: "Full content",
clickToCycle: "Click to cycle",
},
clickToEdit: "Click to edit",
},
apiErrors: {
notebookNotFound: "Notebook not found",
sourceNotFound: "Source not found",
transformationNotFound: "Transformation not found",
fileUploadFailed: "File upload failed",
urlRequired: "URL is required for link type",
contentRequired: "Content is required for text type",
invalidSourceType: "Invalid source type",
processingFailed: "Processing failed",
failedToQueue: "Failed to queue processing",
invalidSortBy: "Sort field must be type, title, created, updated, insights_count, or embedded",
invalidSortOrder: "Sort order must be 'asc' or 'desc'",
accessDenied: "Access to file denied",
fileNotFoundOnServer: "File not found on server",
searchFailed: "Search failed",
askFailed: "Ask failed",
pleaseEnterQuestion: "Please enter a question",
pleaseConfigureModels: "Please configure all required models",
failedToCreateSession: "Failed to create session",
failedToUpdateSession: "Failed to update session",
failedToDeleteSession: "Failed to delete session",
failedToSendMessage: "Failed to send message",
unauthorized: "Unauthorized access, please check your password",
invalidPassword: "Invalid password",
embeddingModelRequired: "This feature requires an embedding model. Please configure one in the Models section.",
strategyModelNotFound: "Strategy model not found",
answerModelNotFound: "Answer model not found",
finalAnswerModelNotFound: "Final answer model not found",
noAnswerGenerated: "No answer could be generated",
genericError: "An unexpected error occurred",
},
connectionErrors: {
apiTitle: "Unable to Connect to API Server",
apiDesc: "The Open Notebook API server could not be reached",
dbTitle: "Database Connection Failed",
dbDesc: "The API server is running, but the database is not accessible",
troubleshooting: "This usually means:",
apiUnreachable1: "The API server is not running",
apiUnreachable2: "The API server is running on a different address",
apiUnreachable3: "Network connectivity issues",
dbFailed1: "SurrealDB is not running",
dbFailed2: "Database connection settings are incorrect",
dbFailed3: "Network issues between API and database",
quickFixes: "Quick fixes:",
setApiUrl: "Set the API_URL environment variable:",
checkSurreal: "Check if SurrealDB is running:",
seeDocumentation: "For detailed setup instructions, see:",
docLink: "Open Notebook Documentation",
showTechnical: "Show Technical Details",
attemptedUrl: "Attempted URL",
message: "Message",
technicalDetails: "Technical Details",
stackTrace: "Stack Trace",
retryLabel: "Retry Connection",
retryHint: "Press R or click the button to retry",
dockerLabel: "For Docker",
localDevLabel: "For local development",
},
auth: {
loginTitle: "Open Notebook",
loginDesc: "Enter your password to access the application",
passwordPlaceholder: "Password",
signingIn: "Signing in...",
signIn: "Sign In",
connectErrorHint: "Unable to connect to server. Please check if the API is running.",
},
navigation: {
collect: "Collect",
process: "Process",
create: "Create",
manage: "Manage",
sources: "Sources",
notebooks: "Notebooks",
askAndSearch: "Ask and Search",
podcasts: "Podcasts",
models: "Models",
transformations: "Transformations",
transformation: "Transformation",
settings: "Settings",
advanced: "Advanced",
nav: "Navigation",
language: "Toggle language",
theme: "Theme",
ask: "Ask",
},
notebooks: {
title: "Notebooks",
newNotebook: "New Notebook",
searchPlaceholder: "Search notebooks...",
archived: "Archived",
archive: "Archive",
unarchive: "Unarchive",
deleteNotebook: "Delete Notebook",
deleteNotebookDesc: "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
deleteNotebookLoading: "Loading deletion preview...",
deleteNotebookNotes: "{{count}} note(s) will be permanently deleted.",
deleteNotebookNoNotes: "No notes to delete.",
deleteNotebookExclusiveSources: "{{count}} source(s) exist only in this notebook.",
deleteNotebookSharedSources: "{{count}} source(s) are shared with other notebooks and will be unlinked.",
deleteNotebookNoSources: "No sources in this notebook.",
deleteExclusiveSourcesLabel: "Delete exclusive sources",
keepExclusiveSourcesLabel: "Unlink and keep them",
activeNotebooks: "Active Notebooks",
archivedNotebooks: "Archived Notebooks",
tileView: "Tile view",
listView: "List view",
notFound: "Notebook not found",
notFoundDesc: "The requested notebook does not exist.",
updated: "Updated",
namePlaceholder: "Notebook name",
addDescription: "Add description...",
noNotesYet: "No notes yet",
deleteNote: "Delete Note",
deleteNoteConfirm: "Are you sure you want to delete this note? This action cannot be undone.",
noteCreatedSuccess: "Note created successfully",
failedToCreateNote: "Failed to create note",
noteUpdatedSuccess: "Note updated successfully",
failedToUpdateNote: "Failed to update note",
noteDeletedSuccess: "Note deleted successfully",
failedToDeleteNote: "Failed to delete note",
createNew: "Create New Notebook",
createNewDesc: "Enter a name and optional description to get started.",
descPlaceholder: "Add more info about this notebook here...",
createSuccess: "Notebook created successfully",
updateSuccess: "Notebook updated successfully",
deleteSuccess: "Notebook deleted successfully",
recentlyViewed: "Recently Viewed",
toggleRecentlyViewed: "Toggle recently viewed",
recentlyViewedNotebook: "Notebook",
recentlyViewedSource: "Source",
lastViewed: "Viewed {{time}}",
},
sources: {
title: "Sources",
newSource: "New Source",
bulkContext: "Context",
includeAllInContext: "Include all in context",
includeAllInsights: "Include all (insights only)",
includeAllFull: "Include all (full content)",
excludeAllFromContext: "Exclude all from context",
add: "Add Source",
addNew: "Add New Source",
addExisting: "Add Existing Source",
delete: "Delete Source",
statusPreparing: "Preparing",
statusQueued: "Queued",
statusProcessing: "Processing",
statusCompleted: "Completed",
statusFailed: "Failed",
statusPreparingDesc: "Preparing to process",
statusQueuedDesc: "Waiting to be processed",
statusProcessingDesc: "Being processed",
statusCompletedDesc: "Successfully processed",
statusFailedDesc: "Processing failed",
failedToLoad: "Failed to load sources",
allSourcesDesc: "View all your sources here. You can add new sources or manage existing ones.",
allSources: "All Sources",
insights: "Insights",
yes: "Yes",
no: "No",
loadingMore: "Loading more...",
noSourcesYet: "No sources yet",
allSourcesDescShort: "View all your sources here.",
cannotSaveNoteNoNotebook: "Cannot save note: notebook ID not available",
createFirstSource: "Add your first source to start building your knowledge base.",
deleteSourceConfirm: "Are you sure you want to delete this source?",
deleteConfirm: "Are you sure you want to delete this?",
deleteConfirmWithTitle: "Are you sure you want to delete \"{{title}}\"?",
deleteSuccess: "Source deleted successfully. Note: To delete the file from storage, you must enable checking the \"delete file\" option in the settings page.",
failedToDelete: "Failed to delete source",
sourceQueued: "Source Queued",
sourceQueuedDesc: "Source submitted for background processing. You can monitor progress in the sources list.",
sourceAddedSuccess: "Source added successfully",
failedToAddSource: "Failed to add source",
sourceUpdatedSuccess: "Source updated successfully",
failedToUpdateSource: "Failed to update source",
sourceDeletedSuccess: "Source deleted successfully",
failedToDeleteSource: "Failed to delete source",
fileUploadedSuccess: "File uploaded successfully",
failedToUploadFile: "Failed to upload file",
sourceRequeued: "Source Retry Queued",
sourceRequeuedDesc: "The source has been requeued for processing.",
failedToRetry: "Retry Failed",
sourcesAddedToNotebook: "{{count}} source(s) added to notebook",
failedToAddSourcesToNotebook: "Failed to add sources to notebook",
partialAddSuccess: "{{success}} source(s) added, {{failed}} failed",
sourceRemovedFromNotebook: "Source removed from notebook successfully",
failedToRemoveSourceFromNotebook: "Failed to remove source from notebook",
removeConfirm: "Are you sure you want to remove this from the notebook?",
checking: "Checking...",
untitledSource: "Untitled Source",
maxItems: "max {{count}}",
insightsCount: "{{count}} insights",
details: "Details",
detailsTitle: "Source Details",
content: "Content",
metadata: "Metadata",
type: {
link: "Link",
file: "File",
text: "Text",
},
id: "Source ID",
topics: "Topics",
embedded: "Embedded",
notEmbedded: "Not Embedded",
embedContent: "Embed Content",
embedding: "Embedding...",
alreadyEmbedded: "Already Embedded",
downloadFile: "Download File",
fileUnavailable: "File unavailable",
preparing: "Preparing...",
generateNewInsight: "Generate New Insight",
selectTransformation: "Select a transformation...",
noInsightsYet: "No insights yet",
createFirstInsight: "Create your first insight using a transformation above",
viewInsight: "View Insight",
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.",
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.",
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.",
openOnYoutube: "Open on YouTube",
urlCopied: "URL copied to clipboard",
viewSource: "View Source",
noInsightSelected: "No insight selected",
sourceInsight: "Source Insight",
manageNotebooks: "Manage Notebooks",
manageNotebooksDesc: "Manage which notebooks contain this source",
noNotebooksAvailable: "No notebooks available",
removeFromNotebook: "Remove from Notebook",
retryProcessing: "Retry Processing",
refreshContent: "Refresh content",
deleteSource: "Delete Source",
retry: "Retry",
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...",
noNotebooksFound: "No notebooks found.",
showingFirst100: "Showing first 100 sources. Use search to find specific ones.",
selectedCount: "{{count}} sources selected",
added: "Added on {{date}}",
addUrl: "Add URL",
uploadFile: "Upload File",
enterText: "Enter Text",
processDescription: "Content will be processed and analyzed by AI.",
processingFiles: "Processing your files...",
titleRequired: "A title is required for text content",
titleGenerated: "If left empty, a title will be generated from the content",
batchCount: "{{count}} {{type}} will be processed",
enableEmbedding: "Enable embedding for search",
embeddingDesc: "Allows this source to be found in vector searches and AI queries",
embeddingAlways: "Embedding enabled automatically",
embeddingAlwaysDesc: "Your settings are configured to always embed content for vector search.",
embeddingNever: "Embedding disabled",
embeddingNeverDesc: "Your settings are configured to skip embedding. Vector search won't be available for this source.",
changeInSettings: "You can change this in Settings",
noContent: "No content available",
insightsDesc: "Insights generated from model analysis",
uploadedFile: "Uploaded file",
fileUnavailableDesc: "This file is currently unavailable due to storage system reasons.",
batchSuccess: "{{count}} source(s) created successfully",
batchFailed: "Failed to create all {{count}} sources",
batchPartial: "{{success}} succeeded, {{failed}} failed",
submittingSource: "Submitting source for processing...",
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",
},
chat: {
sessions: "Sessions",
sessionTitlePlaceholder: "Type a title here...",
noSessions: "No chat sessions yet",
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...",
sessionsTitle: "Chat Sessions",
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.",
messagesCount: "{{count}} messages",
sessionCreated: "Chat session created",
sessionUpdated: "Session updated",
sessionDeleted: "Session deleted",
stop: "Stop",
},
searchPage: {
askAndSearch: "Ask and Search",
chooseAMode: "Choose a mode",
askBeta: "Ask (beta)",
search: "Search",
askYourKb: "Ask Your Knowledge Base (beta)",
askYourKbDesc: "The LLM will answer your query based on the documents in your knowledge base.",
question: "Question",
enterQuestionPlaceholder: "Enter your question...",
pressToSubmit: "Press Cmd/Ctrl+Enter to submit",
noEmbeddingModel: "You can't use this feature because you have no embedding model selected. Please set one up in the Models page.",
usingCustomModels: "Using Custom Models",
usingDefaultModels: "Using Default Models",
advanced: "Advanced",
strategy: "Strategy",
answer: "Answer",
final: "Final",
ask: "Ask",
processing: "Processing...",
saveToNotebooks: "Save to Notebooks",
searchDesc: "Search your knowledge base for specific keywords or concepts",
enterSearchPlaceholder: "Enter search query...",
pressToSearch: "Press Enter to search",
searchCoverageText: "Text search matches source titles and content, insights, and note titles and content.",
searchCoverageVector: "Vector search matches source content, insights, and note content by semantic similarity. Titles are not matched.",
searchType: "Search Type",
vectorSearchWarning: "Vector search requires an embedding model. Only text search is available.",
textSearch: "Text Search",
vectorSearch: "Vector Search",
searchIn: "Search In",
searchSources: "Search Sources",
searchNotes: "Search Notes",
scopeNotebooks: "Notebooks",
scopeAllNotebooks: "All notebooks",
scopeNotebooksSelected: "{{count}} selected",
scopeClear: "Clear",
scopeHint: "Leave all unchecked to search your whole knowledge base.",
scopeNoNotebooks: "No notebooks yet.",
resultsFound: "{{count}} results found",
matches: "Matches ({{count}})",
noResultsFor: "No results found for “{{query}}”",
notSet: "Not set",
saveToNotebook: "Save to Notebook",
saveSuccess: "Successfully saved to notebook",
saveError: "Failed to save to notebook",
selectNotebook: "Select Notebook",
searchAndAsk: "Search & Ask",
searchResultsFor: "Search results for “{{query}}”",
askAbout: "Ask about “{{query}}”",
orSearchKb: "Or search your knowledge base",
saving: "Saving...",
advancedModelTitle: "Advanced Model Selection",
advancedModelDesc: "Choose specific models for each stage of the Ask process",
strategyModel: "Strategy Model",
answerModel: "Answer Model",
finalAnswerModel: "Final Answer Model",
selectStrategyPlaceholder: "Select strategy model",
selectAnswerPlaceholder: "Select answer model",
selectFinalPlaceholder: "Select final answer model",
saveChanges: "Save Changes",
processingQuestion: "Processing your question...",
},
podcasts: {
generateEpisode: "Generate Podcast Episode",
generateEpisodeDesc: "Select the content to include and configure the episode details before generating a new podcast episode.",
content: "Content",
contentDesc: "Pick notebooks, sources, and notes to include in this episode.",
itemsSelected: "{{count}} items selected",
tokens: "{{value}} tokens",
chars: "{{value}} chars",
loadingNotebooks: "Loading notebooks...",
noNotebooksFoundInPodcasts: "No notebooks found. Create a notebook and add content before generating a podcast.",
noContentSelected: "No content selected",
summary: "Summary",
fullContent: "Full content",
untitledSource: "Untitled source",
untitledNote: "Untitled note",
episodeSettings: "Episode Settings",
episodeProfile: "Episode profile",
episodeProfilePlaceholder: "Select an episode profile",
episodeName: "Episode name",
episodeNamePlaceholder: "e.g., AI and the Future of Work",
additionalInstructions: "Additional instructions",
instructionsPlaceholder: "Any supplementary advice to append to the episode briefing...",
generating: "Generating...",
generate: "Generate",
hostPlaceholder: "Host {{number}}",
profileRequired: "Episode Profile Required",
profileRequiredDesc: "Select an episode profile before generating a podcast.",
nameRequired: "Episode name required",
nameRequiredDesc: "Provide a name for the episode.",
addContext: "Add context",
addContextDesc: "Select at least one source or note to include in the episode.",
generationFailed: "Podcast generation failed",
speakerProfileMissing: "Speaker profile missing",
speakerProfileMissingDesc: "This episode profile references a speaker profile that no longer exists. Edit the profile and select a speaker profile.",
speakerProfile: "Speaker Profile",
usesSpeakerProfile: "Uses speaker profile",
sources: "Sources",
notes: "Notes",
noSources: "No sources available in this notebook.",
noNotes: "No notes available in this notebook.",
selectMode: "Select mode",
buildContextFailed: "Failed to build context. Please review your selections.",
podcastTaskStarted: "Podcast task started",
loadingProfiles: "Loading episode profiles...",
noProfilesFound: "No episode profiles found. Create an episode profile before generating a podcast.",
listTitle: "Podcasts",
listDesc: "Keep track of generated episodes and manage reusable profiles.",
chooseAView: "Choose a view",
episodesTab: "Episodes",
templatesTab: "Profiles",
overviewTitle: "Episodes overview",
overviewDesc: "Monitor podcast generation jobs and review the final artefacts.",
generateBtn: "Generate Podcast",
total: "Total",
processingLabel: "Processing",
completedLabel: "Completed",
failedLabel: "Failed",
pendingLabel: "Pending",
loadErrorTitle: "Failed to load episodes",
loadErrorDesc: "We could not fetch the latest podcast episodes. Try again shortly.",
loadingEpisodes: "Loading episodes…",
noEpisodesYet: "No podcast episodes yet. Generate your first one from the notebook or source chat interfaces.",
statusRunningTitle: "Currently Processing",
statusRunningDesc: "Episodes that are actively generating assets.",
statusPendingTitle: "Queued / Pending",
statusPendingDesc: "Submitted episodes waiting to start processing.",
statusCompletedTitle: "Completed Episodes",
statusCompletedDesc: "Ready to review, download, or publish.",
statusFailedTitle: "Failed Episodes",
statusFailedDesc: "Episodes that encountered issues during generation.",
templatesWorkspaceTitle: "Profiles workspace",
templatesWorkspaceDesc: "Build reusable episode and speaker configurations for fast podcast production.",
howTemplatesPowerTitle: "How profiles power podcast generation",
howTemplatesPowerDesc: "Profiles split the podcast workflow into two reusable building blocks. Mix and match them whenever you generate a new episode.",
episodeProfilesSetFormat: "Episode profiles set the format",
episodeProfilesList1: "Outline the number of segments and how the story flows",
episodeProfilesList2: "Pick the language models used for briefing, outlining, and script writing",
episodeProfilesList3: "Store default briefings so every episode starts with a consistent tone",
speakerProfilesBringVoices: "Speaker profiles bring voices to life",
speakerProfilesList1: "Choose the text-to-speech provider and model",
speakerProfilesList2: "Capture personality, backstory, and pronunciation notes per speaker",
speakerProfilesList3: "Reuse the same host or guest voices across different episode formats",
recommendedWorkflow: "Recommended workflow",
workflowStep1: "Create speaker profiles for each voice you need",
workflowStep2: "Build episode profiles that reference those speakers by name",
workflowStep3: "Generate podcasts by selecting the episode profile that fits the story",
workflowHint: "Episode profiles reference speaker profiles by name, so starting with speakers avoids missing voice assignments later.",
failedToLoadTemplates: "Failed to load profiles data",
failedToLoadTemplatesDesc: "Ensure the API is running and try again. Some sections may be incomplete.",
loadingTemplates: "Loading profiles…",
speakerProfilesTitle: "Speaker profiles",
speakerProfilesDesc: "Configure voices and personalities for generated episodes.",
createSpeaker: "Create speaker",
noSpeakerProfiles: "No speaker profiles yet. Create one to make episode profiles available.",
noDescription: "No description provided.",
usedByCount_one: "Used by 1 episode",
usedByCount_other: "Used by {{count}} episodes",
usedByCount: "Used by {{count}} episodes",
unused: "Unused",
voiceId: "Voice ID",
backstory: "Backstory",
personality: "Personality",
edit: "Edit",
duplicate: "Duplicate",
deleteSpeakerProfileTitle: "Delete speaker profile?",
deleteSpeakerProfileDesc: "Deleting “{{name}}” cannot be undone.",
deleteSpeakerDisabledHint: "Remove this speaker from episode profiles before deleting it.",
deleting: "Deleting…",
episodeProfilesTitle: "Episode profiles",
episodeProfilesDesc: "Define reusable generation settings for your shows.",
createProfile: "Create profile",
createSpeakerFirst: "Create a speaker profile before adding an episode profile.",
noEpisodeProfiles: "No episode profiles yet. Create one to kickstart podcast generation.",
speakerCreated: "Speaker Created",
speakerCreatedDesc: "The speaker \"{{name}}\" has been successfully added.",
failedToCreateSpeaker: "Failed to create speaker profile",
speakerUpdated: "Speaker Updated",
speakerUpdatedDesc: "The speaker \"{{name}}\" has been successfully updated.",
failedToUpdateSpeaker: "Failed to update speaker profile",
speakerDeleted: "Speaker Deleted",
speakerDeletedDesc: "The speaker \"{{name}}\" has been successfully removed.",
failedToDeleteSpeaker: "Failed to delete speaker profile",
speakerDuplicated: "Speaker Duplicated",
speakerDuplicatedDesc: "The speaker \"{{name}}\" has been successfully duplicated.",
failedToDuplicateSpeaker: "Failed to duplicate speaker profile",
generationStarted: "Generation Started",
generationStartedDesc: "Podcast generation has been queued.",
failedToStartGeneration: "Failed to start generation",
tryAgainMoment: "Please try again in a moment.",
deleteProfileTitle: "Delete profile?",
deleteProfileDesc: "This will remove “{{name}}”. Existing episodes keep their data, but new ones will no longer use this configuration.",
profileCreated: "Profile Created",
profileCreatedDesc: "The episode profile \"{{name}}\" has been successfully created.",
failedToCreateProfile: "Failed to create profile",
profileUpdated: "Profile Updated",
profileUpdatedDesc: "The episode profile \"{{name}}\" has been successfully updated.",
failedToUpdateProfile: "Failed to update profile",
profileDeleted: "Profile Deleted",
profileDeletedDesc: "The episode profile \"{{name}}\" has been successfully removed.",
failedToDeleteProfile: "Failed to delete profile",
failedToDeleteProfileDesc: "Failed to remove the episode profile.",
profileDuplicated: "Profile Duplicated",
profileDuplicatedDesc: "The episode profile \"{{name}}\" has been successfully duplicated.",
failedToDuplicateProfile: "Failed to duplicate profile",
episodeDeleted: "Episode Deleted",
episodeDeletedDesc: "The episode has been successfully deleted.",
failedToDeleteEpisode: "Failed to delete episode",
failedToDeleteSpeakerDesc: "Failed to remove the speaker profile.",
outlineModel: "Outline model",
transcriptModel: "Transcript model",
segments: "Segments",
maxTokens: "Max output tokens",
maxTokensPlaceholder: "Leave blank for defaults",
maxTokensHelp: "Optional. Caps how many tokens each generation step can produce — the outline and the full transcript — not a per-turn or conversation limit. Leave blank to use the built-in defaults (3000 for the outline, 5000 for the transcript). Raise it for longer episodes; a value that is too low can cut the transcript short.",
defaultBriefingTitle: "Default briefing",
created: "Created at {{time}}",
details: "Details",
summaryTab: "Summary",
outlineTab: "Outline",
transcriptTab: "Transcript",
briefing: "Briefing",
noOutline: "No outline available.",
noTranscript: "No transcript available.",
deleteEpisodeTitle: "Delete episode?",
deleteEpisodeDesc: "This will remove “{{name}}” and its audio file permanently.",
audioUnavailable: "Audio unavailable",
segment: "Segment",
speaker: "Speaker",
profile: "Profile",
link: "Link",
file: "File",
embedded: "Embedded",
notEmbedded: "Not embedded",
noSpeakerProfilesAvailable: "No speaker profiles available",
editEpisodeProfile: "Edit Episode Profile",
createEpisodeProfile: "Create Episode Profile",
episodeProfileFormDesc: "Define how episodes should be generated and which speaker configuration they use by default.",
noSpeakerProfilesDesc: "Create a speaker profile before configuring an episode profile.",
profileName: "Profile name",
profileNamePlaceholder: "e.g., Tech discussion",
descriptionPlaceholder: "Short summary of when to use this profile",
speakerConfig: "Speaker configuration",
selectSpeakerProfile: "Select a speaker profile",
outlineGeneration: "Outline generation",
transcriptGeneration: "Transcript generation",
defaultBriefingPlaceholder: "Outline the structure, tone, and goals for this episode format",
editSpeakerProfile: "Edit Speaker Profile",
createSpeakerProfile: "Create Speaker Profile",
speakerProfileFormDesc: "Configure text-to-speech settings and define up to four speakers.",
speakers: "Speakers",
speakersDesc: "Configure between one and four voices for this profile.",
addSpeaker: "Add speaker",
speakerNumber: "Speaker {{number}}",
backstoryPlaceholder: "Short biography or context for the speaker",
personalityPlaceholder: "Describe style and tone",
outlineModelRequired: "Outline model is required",
transcriptModelRequired: "Transcript model is required",
defaultBriefingRequired: "Default briefing is required",
segmentsInteger: "Must be an integer",
segmentsMin: "At least 3 segments",
segmentsMax: "Maximum 20 segments",
maxTokensInteger: "Must be an integer",
maxTokensPositive: "Must be a positive integer",
voiceIdRequired: "Voice ID is required",
backstoryRequired: "Backstory is required",
personalityRequired: "Personality is required",
speakerCountMin: "At least one speaker is required",
speakerCountMax: "You can configure up to 4 speakers",
delete: "Delete",
failedToDelete: "Failed to delete podcast",
retry: "Retry",
retrying: "Retrying…",
retryStarted: "Retry Started",
retryStartedDesc: "A new podcast generation job has been submitted.",
failedToRetry: "Failed to retry episode",
errorDetails: "Error details",
language: "Language",
languagePlaceholder: "Select a language (optional)",
podcastLanguage: "Podcast language",
selectOutlineModel: "Select outline model",
selectTranscriptModel: "Select transcript model",
voiceModel: "Voice model",
voiceModelRequired: "Voice model is required",
selectVoiceModel: "Select voice model",
perSpeakerTtsOverride: "Per-speaker TTS override (optional)",
useProfileDefault: "Use profile default",
setupRequired: "Setup required",
setupRequiredDesc:
"Some profiles don't have models configured yet. Edit them to select models before generating podcasts.",
notConfigured: "Not configured",
},
settings: {
contentProcessing: "Content Processing",
contentProcessingDesc: "Configure how documents and URLs are processed",
docEngine: "Document Processing Engine",
docEnginePlaceholder: "Select document processing engine",
urlEngine: "URL Processing Engine",
urlEnginePlaceholder: "Select URL processing engine",
autoRecommended: "Auto (Recommended)",
simple: "Simple",
docling: "Docling",
helpMeChoose: "Help me choose",
docHelp: "· Docling is a little slower but more accurate, specially if the documents contain tables and images. · Simple will extract any content from the document without formatting it. · Auto (recommended) will try to process through docling and default to simple.",
firecrawl: "Firecrawl",
jina: "Jina",
crawl4ai: "Crawl4AI",
enableDoclingHint: "Docling is optional. Enable it with OPEN_NOTEBOOK_ENABLE_DOCLING=true — it installs on first startup (a large download).",
enableCrawl4aiHint: "Local Crawl4AI is optional. Enable it with OPEN_NOTEBOOK_ENABLE_CRAWL4AI=true (installs on first startup), or set CRAWL4AI_API_URL to use a remote server.",
urlHelp: "· Firecrawl is a paid service (with a free tier), and very powerful. · Jina is a good option as well and also has a free tier. · Crawl4AI renders JavaScript pages locally (or via a Crawl4AI server when CRAWL4AI_API_URL is set) with no API key. · Simple will use basic HTTP extraction and will miss content on javascript-based websites. · Auto (recommended) will try Firecrawl, then Jina, then Crawl4AI, finally falling back to simple.",
embeddingAndSearch: "Embedding and Search",
embeddingAndSearchDesc: "Configure search and embedding options",
defaultEmbeddingOption: "Default Embedding Option",
embeddingOptionPlaceholder: "Select embedding option",
ask: "Ask",
always: "Always",
never: "Never",
embeddingHelp: "Embedding the content will make it easier to find by you and by your AI agents. If you are running a local embedding model (Ollama, for example), you shouldn't worry about cost and just embed everything.",
fileManagement: "File Management",
fileManagementDesc: "Configure file handling and storage options",
autoDeleteFiles: "Auto Delete Files",
ocrEnabled: "Enable OCR",
ocrHelp: "Extract text from scanned PDFs and images when using the Docling engine. Slower to process.",
formulasEnabled: "Extract formulas",
formulasHelp: "Extract mathematical formulas as structured markup when using the Docling engine. Adds processing time.",
visionEnabled: "Describe images and charts",
visionHelp: "Use a vision model to describe images and extract chart data when using the Docling engine. Significantly slower and may call a vision model.",
autoDeletePlaceholder: "Select auto delete option",
filesHelp: "Once your files are uploaded and processed, they are not required anymore. Most users should allow Open Notebook to delete uploaded files from the upload folder automatically.",
loadFailed: "Failed to load settings",
},
advanced: {
title: "AdvancedTools",
desc: "Advanced tools and utilities for power users",
systemInfo: "System Info",
rebuildEmbeddings: "Rebuild Embeddings",
rebuildEmbeddingsDesc: "Rebuild vector search index for all sources",
currentVersion: "Current Version",
latestVersion: "Latest Version",
status: "Status",
updateAvailable: "Version {{version}} Available",
updateAvailableDesc: "A new version of Open Notebook is available.",
upToDate: "Up to Date",
unknown: "Unknown",
viewOnGithub: "View on GitHub",
updateCheckFailed: "Unable to check for updates. GitHub may be unreachable.",
rebuild: {
mode: "Rebuild Mode",
existing: "Existing",
all: "All",
existingDesc: "Re-embed only items that already have embeddings (faster, for model switching)",
allDesc: "Re-embed existing items + create embeddings for items without any (slower, comprehensive)",
include: "Include in Rebuild",
selectOneError: "Please select at least one item type to rebuild",
starting: "Starting Rebuild...",
startBtn: "🚀 Start Rebuild",
queued: "Queued",
running: "Submitting jobs...",
completed: "Jobs Submitted!",
failed: "Failed",
leavePageHint: "You can leave this page as this will run in the background",
startNew: "Start New Rebuild",
itemsProcessed: "{{processed}}/{{total}} jobs submitted ({{percent}}%)",
failedItems: "{{count}} jobs failed to submit",
time: "Time",
whenToRebuild: "When should I rebuild embeddings?",
whenToRebuildAns: "You should rebuild when switching models, upgrading versions, fixing corruption, or after bulk imports.",
howLong: "How long does rebuilding take?",
howLongAns: "Processing time depends on item count, model speed, and API rate limits. Local models are usually very fast.",
isSafe: "Is it safe to rebuild while using the app?",
isSafeAns: "Yes, rebuilding is safe! It doesn't delete content, only replaces embeddings, and handles errors gracefully.",
},
},
transformations: {
title: "Transformations",
desc: "Transformations are prompts that will be used by the LLM to process a source and extract insights, summaries, etc.",
workspace: "Choose a workspace",
playground: "Playground",
defaultPrompt: "Default Transformation Prompt",
defaultPromptDesc: "This will be added to all your transformation prompts",
defaultPromptPlaceholder: "Enter your default transformation instructions...",
listTitle: "Custom Transformations",
createNew: "Create New",
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",
noTransformations: "No transformations yet",
createOne: "Create a transformation to get started",
selectModel: "Select a model",
deleteConfirm: "Are you sure you want to delete this transformation?",
model: "Model",
systemPrompt: "System Prompt",
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: {
embedding: "Embedding Models",
tts: "Text to Speech (TTS)",
stt: "Speech to Text (STT)",
apiKey: "API Key",
deleteSuccess: "Model deleted successfully",
saveSuccess: "Model saved successfully",
noModels: "No models",
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",
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",
noneOption: "None",
noneFallbackToChat: "Use fallback (chat default)",
usingChatModelHint: "Using chat model ({{model}})",
ttsUnsetHint: "Not set — audio generation unavailable until configured",
sttUnsetHint: "Not set — transcription unavailable until configured",
requiredModelPlaceholder: "⚠️ Required - Select a model",
chatModelLabel: "Chat Model",
chatModelDesc: "Used for chat conversations",
transformationModelLabel: "Transformation Model",
transformationModelDesc: "Used for summaries, insights, and transformations",
toolsModelLabel: "Tools Model",
toolsModelDesc: "Used for function calling - OpenAI or Anthropic recommended",
largeContextModelLabel: "Large Context Model",
largeContextModelDesc: "Used for processing large documents - Gemini recommended",
embeddingModelLabel: "Embedding Model",
embeddingModelDesc: "Used for semantic search and vector embeddings",
ttsModelLabel: "Text-to-Speech Model",
ttsModelDesc: "Used for podcast generation",
sttModelLabel: "Speech-to-Text Model",
sttModelDesc: "Used for audio transcription",
embeddingChangeTitle: "Embedding Model Change",
embeddingChangeConfirm: "You are about to change your embedding model from {{from}} to {{to}}.",
rebuildRequired: "Important: Rebuild Required",
rebuildReason: "Changing your embedding model requires rebuilding all existing embeddings to maintain consistency. Without rebuilding, your searches may return incorrect or incomplete results.",
whatHappensNext: "What happens next:",
step1: "Your default embedding model will be updated",
step2: "Existing embeddings will remain unchanged until rebuild",
step3: "New content will use the new embedding model",
step4: "You should rebuild embeddings as soon as possible",
proceedToRebuildPrompt: "Would you like to proceed to the Advanced page to start the rebuild now?",
changeModelOnly: "Change Model Only",
changeAndRebuild: "Change & Go to Rebuild",
autoAssign: "Auto-assign Defaults",
autoAssigning: "Assigning...",
autoAssignSuccess: "{{count}} default models automatically assigned",
autoAssignNoModels: "No models available to assign. Please sync models first.",
autoAssignAlreadySet: "All default models are already configured",
testModel: "Test Model",
testModelSuccess: "Model Test Passed",
testModelFailed: "Model Test Failed",
searchOrAddModel: "Search or type a model name...",
addCustomModel: "Add \"{{name}}\"",
},
apiKeys: {
title: "Models",
description: "Connect your AI providers and securely store their credentials.",
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",
providersLoadFailed: "Failed to load providers",
providersLoadFailedDescription: "Ensure the API is running and try again.",
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",
migrating: "Migrating...",
migrationSuccess: "{{count}} API key(s) migrated successfully",
migrationErrors: "{{count}} key(s) failed to migrate",
migrationNothingToMigrate: "All keys are already in the database",
learnMore: "Learn how to connect AI providers →",
testConnection: "Test Connection",
testSuccess: "Connection successful",
testFailed: "Connection test failed",
syncModels: "Sync Models",
syncSuccess: "Discovered {{discovered}} models, added {{new}} new",
syncNoNew: "Discovered {{count}} models, all already registered",
syncFailed: "Failed to sync models",
getApiKey: "Get API Key",
vertexProject: "GCP Project ID",
vertexLocation: "Region",
vertexCredentials: "Service Account JSON Path",
addConfig: "Add Configuration",
editConfig: "Edit Configuration",
deleteConfig: "Delete Configuration",
configName: "Configuration Name",
configNameHint: "A descriptive name for this configuration (e.g., 'Production', 'Development')",
baseUrl: "Base URL",
baseUrlOverrideHint: "Only change this if you need to override the provider's default API endpoint.",
openAICompatibleBaseUrlHint: "Include the API version path required by your server. For LM Studio in Docker, use http://host.docker.internal:1234/v1.",
numCtx: "Context Window (num_ctx)",
numCtxHint: "Maximum context window for Ollama models. Leave empty to use the default (8192). Increase only if your hardware can handle a larger window.",
deleteConfigConfirm: "Are you sure you want to delete '{{name}}'? This cannot be undone.",
configSaveSuccess: "Configuration saved successfully",
configUpdateSuccess: "Configuration updated successfully",
configDeleteSuccess: "Configuration deleted successfully",
apiKeyEditHint: "Leave blank to keep the existing API key",
decryptionError: "Decryption Error",
decryptionErrorDescription: "This credential's API key could not be decrypted. The encryption key may have changed. Delete this credential and re-create it with the correct key.",
},
setupBanner: {
encryptionRequired: "Encryption key not configured",
encryptionRequiredDescription: "Set the OPEN_NOTEBOOK_ENCRYPTION_KEY environment variable to enable secure credential storage.",
migrationAvailable: "API key migration available",
migrationDescription: "{{count}} provider(s) have API keys set via environment variables. Migrate them to the database for easier management.",
goToSettings: "Go to Settings",
viewDocs: "View docs",
},
}
// Compile-time shape of the en-US translations. Every other locale must
// `satisfies` this type so missing or extra keys fail `tsc`, not just the
// runtime parity test.
export type TranslationShape = typeof enUS;