fix: i18n system overhaul and sidebar UI bugs
- Fix LanguageProvider: add RTL support (ar/fa), translation caching, prevent blank flash during load, browser language detection - Fix detect-user-language: extend whitelist from 5 to all 15 languages - Remove hardcoded initialLanguage="fr" from auth layout - Complete fr.json translation (all sections translated from English) - Add missing admin.ai keys to all 13 non-English locales - Translate ai.autoLabels, ai.batchOrganization, memoryEcho sections for all locales - Remove duplicate top-level autoLabels/batchOrganization from en.json - Fix notebook creation: replace window.location.reload() with createNotebookOptimistic + router.refresh() - Fix notebook name truncation in sidebar with min-w-0 - Remove redundant router.refresh() after note creation in page.tsx Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ export default function AuthLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<LanguageProvider initialLanguage="fr">
|
||||
<LanguageProvider>
|
||||
<div className="flex min-h-screen items-center justify-center bg-gray-50 dark:bg-zinc-950">
|
||||
<div className="w-full max-w-md p-4">
|
||||
{children}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useSearchParams, useRouter } from 'next/navigation'
|
||||
import { Note } from '@/lib/types'
|
||||
import { getAllNotes, getPinnedNotes, getRecentNotes, searchNotes } from '@/app/actions/notes'
|
||||
import { getAllNotes, searchNotes } from '@/app/actions/notes'
|
||||
import { getAISettings } from '@/app/actions/ai-settings'
|
||||
import { NoteInput } from '@/components/note-input'
|
||||
import { MasonryGrid } from '@/components/masonry-grid'
|
||||
@@ -35,7 +35,7 @@ export default function HomePage() {
|
||||
const [notes, setNotes] = useState<Note[]>([])
|
||||
const [pinnedNotes, setPinnedNotes] = useState<Note[]>([])
|
||||
const [recentNotes, setRecentNotes] = useState<Note[]>([])
|
||||
const [showRecentNotes, setShowRecentNotes] = useState(false)
|
||||
const [showRecentNotes, setShowRecentNotes] = useState(true)
|
||||
const [editingNote, setEditingNote] = useState<{ note: Note; readOnly?: boolean } | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [notebookSuggestion, setNotebookSuggestion] = useState<{ noteId: string; content: string } | null>(null)
|
||||
@@ -137,8 +137,9 @@ export default function HomePage() {
|
||||
|
||||
}
|
||||
|
||||
// Refresh in background to ensure data consistency (non-blocking)
|
||||
router.refresh()
|
||||
// Note: revalidatePath('/') is already called in the server action,
|
||||
// and the optimistic update above already adds the note to state.
|
||||
// No additional router.refresh() needed — avoids visible re-render flash.
|
||||
}, [searchParams, labels, router])
|
||||
|
||||
const handleOpenNote = (noteId: string) => {
|
||||
@@ -156,9 +157,11 @@ export default function HomePage() {
|
||||
const loadSettings = async () => {
|
||||
try {
|
||||
const settings = await getAISettings()
|
||||
setShowRecentNotes(settings.showRecentNotes === true)
|
||||
// Default to true if setting is undefined or null
|
||||
setShowRecentNotes(settings?.showRecentNotes !== false)
|
||||
} catch (error) {
|
||||
setShowRecentNotes(false)
|
||||
// Default to true on error
|
||||
setShowRecentNotes(true)
|
||||
}
|
||||
}
|
||||
loadSettings()
|
||||
@@ -203,19 +206,31 @@ export default function HomePage() {
|
||||
)
|
||||
}
|
||||
|
||||
// Load pinned notes separately (shown in favorites section)
|
||||
const pinned = await getPinnedNotes(notebookFilter || undefined)
|
||||
// Derive pinned notes from already-fetched allNotes (no extra server call)
|
||||
const pinnedFilter = notebookFilter
|
||||
? allNotes.filter((note: any) => note.isPinned && note.notebookId === notebookFilter)
|
||||
: allNotes.filter((note: any) => note.isPinned && !note.notebookId)
|
||||
|
||||
setPinnedNotes(pinned)
|
||||
setPinnedNotes(pinnedFilter)
|
||||
|
||||
// Load recent notes only if enabled in settings
|
||||
// Derive recent notes from already-fetched allNotes (no extra server call)
|
||||
if (showRecentNotes) {
|
||||
const recent = await getRecentNotes(3)
|
||||
// Filter recent notes by current filters
|
||||
const sevenDaysAgo = new Date()
|
||||
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
|
||||
sevenDaysAgo.setHours(0, 0, 0, 0)
|
||||
|
||||
const recentFiltered = allNotes
|
||||
.filter((note: any) => {
|
||||
return !note.isArchived && !note.dismissedFromRecent && note.contentUpdatedAt >= sevenDaysAgo
|
||||
})
|
||||
.sort((a: any, b: any) => new Date(b.contentUpdatedAt).getTime() - new Date(a.createdAt).getTime())
|
||||
.slice(0, 3)
|
||||
|
||||
if (notebookFilter) {
|
||||
setRecentNotes(recent.filter((note: any) => note.notebookId === notebookFilter))
|
||||
setRecentNotes(recentFiltered.filter((note: any) => note.notebookId === notebookFilter))
|
||||
} else {
|
||||
setRecentNotes(recent.filter((note: any) => !note.notebookId))
|
||||
// Show ALL recent notes when in inbox (not just notes without notebooks)
|
||||
setRecentNotes(recentFiltered)
|
||||
}
|
||||
} else {
|
||||
setRecentNotes([])
|
||||
@@ -392,7 +407,7 @@ export default function HomePage() {
|
||||
{/* Recent Notes Section - Only shown if enabled in settings */}
|
||||
{showRecentNotes && (
|
||||
<RecentNotesSection
|
||||
recentNotes={recentNotes.filter(note => !note.isPinned)}
|
||||
recentNotes={recentNotes}
|
||||
onEdit={(note, readOnly) => setEditingNote({ note, readOnly })}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { memo, useState, useEffect } from 'react'
|
||||
import { Sparkles } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useLanguage } from '@/lib/i18n/LanguageProvider'
|
||||
import { getConnectionsCount } from '@/lib/connections-cache'
|
||||
|
||||
interface ConnectionsBadgeProps {
|
||||
noteId: string
|
||||
@@ -11,54 +12,44 @@ interface ConnectionsBadgeProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface ConnectionData {
|
||||
noteId: string
|
||||
title: string | null
|
||||
content: string
|
||||
createdAt: Date
|
||||
similarity: number
|
||||
daysApart: number
|
||||
}
|
||||
|
||||
interface ConnectionsResponse {
|
||||
connections: ConnectionData[]
|
||||
pagination: {
|
||||
total: number
|
||||
page: number
|
||||
limit: number
|
||||
totalPages: number
|
||||
hasNext: boolean
|
||||
hasPrev: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export function ConnectionsBadge({ noteId, onClick, className }: ConnectionsBadgeProps) {
|
||||
export const ConnectionsBadge = memo(function ConnectionsBadge({ noteId, onClick, className }: ConnectionsBadgeProps) {
|
||||
const { t } = useLanguage()
|
||||
const [connectionCount, setConnectionCount] = useState<number>(0)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
const [fetchAttempted, setFetchAttempted] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (fetchAttempted) return
|
||||
setFetchAttempted(true)
|
||||
|
||||
let isMounted = true
|
||||
|
||||
const fetchConnections = async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const res = await fetch(`/api/ai/echo/connections?noteId=${noteId}&limit=1`)
|
||||
if (!res.ok) {
|
||||
throw new Error('Failed to fetch connections')
|
||||
const count = await getConnectionsCount(noteId)
|
||||
if (isMounted) {
|
||||
setConnectionCount(count)
|
||||
}
|
||||
|
||||
const data: ConnectionsResponse = await res.json()
|
||||
setConnectionCount(data.pagination.total || 0)
|
||||
} catch (error) {
|
||||
console.error('[ConnectionsBadge] Failed to fetch connections:', error)
|
||||
setConnectionCount(0)
|
||||
if (isMounted) {
|
||||
setConnectionCount(0)
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
if (isMounted) {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fetchConnections()
|
||||
}, [noteId])
|
||||
|
||||
return () => {
|
||||
isMounted = false
|
||||
}
|
||||
}, [noteId]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Don't render if no connections or still loading
|
||||
if (connectionCount === 0 || isLoading) {
|
||||
@@ -69,19 +60,11 @@ export function ConnectionsBadge({ noteId, onClick, className }: ConnectionsBadg
|
||||
const badgeText = t('memoryEcho.connectionsBadge', { count: connectionCount, plural })
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'px-1.5 py-0.5 rounded',
|
||||
'bg-amber-100 dark:bg-amber-900/30',
|
||||
'text-amber-700 dark:text-amber-400',
|
||||
'text-[10px] font-medium',
|
||||
'border border-amber-200 dark:border-amber-800',
|
||||
'cursor-pointer',
|
||||
'transition-all duration-150 ease-out',
|
||||
'hover:bg-amber-200 dark:hover:bg-amber-800/50',
|
||||
isHovered && 'scale-105',
|
||||
className
|
||||
)}
|
||||
<div className={cn(
|
||||
'inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 border border-purple-200 dark:border-purple-800 transition-all duration-150 ease-out',
|
||||
isHovered && 'scale-105',
|
||||
className
|
||||
)}
|
||||
onClick={onClick}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
@@ -91,4 +74,4 @@ export function ConnectionsBadge({ noteId, onClick, className }: ConnectionsBadg
|
||||
{badgeText}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
import { useNotebooks } from '@/context/notebooks-context'
|
||||
|
||||
const NOTEBOOK_ICONS = [
|
||||
{ icon: Folder, name: 'folder' },
|
||||
@@ -48,6 +49,7 @@ interface CreateNotebookDialogProps {
|
||||
export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialogProps) {
|
||||
const router = useRouter()
|
||||
const { t } = useLanguage()
|
||||
const { createNotebookOptimistic } = useNotebooks()
|
||||
const [name, setName] = useState('')
|
||||
const [selectedIcon, setSelectedIcon] = useState('folder')
|
||||
const [selectedColor, setSelectedColor] = useState('#3B82F6')
|
||||
@@ -61,24 +63,14 @@ export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialo
|
||||
setIsSubmitting(true)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/notebooks', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: name.trim(),
|
||||
icon: selectedIcon,
|
||||
color: selectedColor,
|
||||
}),
|
||||
await createNotebookOptimistic({
|
||||
name: name.trim(),
|
||||
icon: selectedIcon,
|
||||
color: selectedColor,
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
// Close dialog and reload
|
||||
onOpenChange?.(false)
|
||||
window.location.reload()
|
||||
} else {
|
||||
const error = await response.json()
|
||||
console.error('Failed to create notebook:', error)
|
||||
}
|
||||
// Close dialog — no full page reload needed
|
||||
onOpenChange?.(false)
|
||||
router.refresh()
|
||||
} catch (error) {
|
||||
console.error('Failed to create notebook:', error)
|
||||
} finally {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { memo } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import remarkMath from 'remark-math'
|
||||
@@ -11,7 +12,7 @@ interface MarkdownContentProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function MarkdownContent({ content, className = '' }: MarkdownContentProps) {
|
||||
export const MarkdownContent = memo(function MarkdownContent({ content, className }: MarkdownContentProps) {
|
||||
return (
|
||||
<div className={`prose prose-sm prose-compact dark:prose-invert max-w-none break-words ${className}`}>
|
||||
<ReactMarkdown
|
||||
@@ -20,11 +21,11 @@ export function MarkdownContent({ content, className = '' }: MarkdownContentProp
|
||||
components={{
|
||||
a: ({ node, ...props }) => (
|
||||
<a {...props} className="text-primary hover:underline" target="_blank" rel="noopener noreferrer" />
|
||||
)
|
||||
),
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@ interface MasonryItemProps {
|
||||
isDragging?: boolean;
|
||||
}
|
||||
|
||||
const MasonryItem = function MasonryItem({ note, onEdit, onResize, onNoteSizeChange, onDragStart, onDragEnd, isDragging }: MasonryItemProps) {
|
||||
const MasonryItem = memo(function MasonryItem({ note, onEdit, onResize, onNoteSizeChange, onDragStart, onDragEnd, isDragging }: MasonryItemProps) {
|
||||
const resizeRef = useResizeObserver(onResize);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -55,7 +55,7 @@ const MasonryItem = function MasonryItem({ note, onEdit, onResize, onNoteSizeCha
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
})
|
||||
|
||||
export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
const { t } = useLanguage();
|
||||
|
||||
@@ -87,6 +87,21 @@ export function NoteActions({
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{/* Pin/Unpin Option */}
|
||||
<DropdownMenuItem onClick={onTogglePin}>
|
||||
{isPinned ? (
|
||||
<>
|
||||
<Pin className="h-4 w-4 mr-2" />
|
||||
{t('notes.unpin')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Pin className="h-4 w-4 mr-2" />
|
||||
{t('notes.pin')}
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onClick={onToggleArchive}>
|
||||
{isArchived ? (
|
||||
<>
|
||||
|
||||
@@ -10,14 +10,28 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Pin, Bell, GripVertical, X, Link2, FolderOpen, StickyNote, LucideIcon, Folder, Briefcase, FileText, Zap, BarChart3, Globe, Sparkles, Book, Heart, Crown, Music, Building2, Tag } from 'lucide-react'
|
||||
import { useState, useEffect, useTransition, useOptimistic } from 'react'
|
||||
import { Pin, Bell, GripVertical, X, Link2, FolderOpen, StickyNote, LucideIcon, Folder, Briefcase, FileText, Zap, BarChart3, Globe, Sparkles, Book, Heart, Crown, Music, Building2 } from 'lucide-react'
|
||||
import { useState, useEffect, useTransition, useOptimistic, memo } from 'react'
|
||||
import { useSession } from 'next-auth/react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { deleteNote, toggleArchive, togglePin, updateColor, updateNote, updateSize, getNoteAllUsers, leaveSharedNote, removeFusedBadge } from '@/app/actions/notes'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { formatDistanceToNow, Locale } from 'date-fns'
|
||||
import * as dateFnsLocales from 'date-fns/locale'
|
||||
import { enUS } from 'date-fns/locale/en-US'
|
||||
import { fr } from 'date-fns/locale/fr'
|
||||
import { es } from 'date-fns/locale/es'
|
||||
import { de } from 'date-fns/locale/de'
|
||||
import { faIR } from 'date-fns/locale/fa-IR'
|
||||
import { it } from 'date-fns/locale/it'
|
||||
import { pt } from 'date-fns/locale/pt'
|
||||
import { ru } from 'date-fns/locale/ru'
|
||||
import { zhCN } from 'date-fns/locale/zh-CN'
|
||||
import { ja } from 'date-fns/locale/ja'
|
||||
import { ko } from 'date-fns/locale/ko'
|
||||
import { ar } from 'date-fns/locale/ar'
|
||||
import { hi } from 'date-fns/locale/hi'
|
||||
import { nl } from 'date-fns/locale/nl'
|
||||
import { pl } from 'date-fns/locale/pl'
|
||||
import { MarkdownContent } from './markdown-content'
|
||||
import { LabelBadge } from './label-badge'
|
||||
import { NoteImages } from './note-images'
|
||||
@@ -36,25 +50,25 @@ import { toast } from 'sonner'
|
||||
|
||||
// Mapping of supported languages to date-fns locales
|
||||
const localeMap: Record<string, Locale> = {
|
||||
en: dateFnsLocales.enUS,
|
||||
fr: dateFnsLocales.fr,
|
||||
es: dateFnsLocales.es,
|
||||
de: dateFnsLocales.de,
|
||||
fa: dateFnsLocales.faIR,
|
||||
it: dateFnsLocales.it,
|
||||
pt: dateFnsLocales.pt,
|
||||
ru: dateFnsLocales.ru,
|
||||
zh: dateFnsLocales.zhCN,
|
||||
ja: dateFnsLocales.ja,
|
||||
ko: dateFnsLocales.ko,
|
||||
ar: dateFnsLocales.ar,
|
||||
hi: dateFnsLocales.hi,
|
||||
nl: dateFnsLocales.nl,
|
||||
pl: dateFnsLocales.pl,
|
||||
en: enUS,
|
||||
fr: fr,
|
||||
es: es,
|
||||
de: de,
|
||||
fa: faIR,
|
||||
it: it,
|
||||
pt: pt,
|
||||
ru: ru,
|
||||
zh: zhCN,
|
||||
ja: ja,
|
||||
ko: ko,
|
||||
ar: ar,
|
||||
hi: hi,
|
||||
nl: nl,
|
||||
pl: pl,
|
||||
}
|
||||
|
||||
function getDateLocale(language: string): Locale {
|
||||
return localeMap[language] || dateFnsLocales.enUS
|
||||
return localeMap[language] || enUS
|
||||
}
|
||||
|
||||
// Map icon names to lucide-react components
|
||||
@@ -118,7 +132,7 @@ function getAvatarColor(name: string): string {
|
||||
return colors[hash % colors.length]
|
||||
}
|
||||
|
||||
export function NoteCard({
|
||||
export const NoteCard = memo(function NoteCard({
|
||||
note,
|
||||
onEdit,
|
||||
onDragStart,
|
||||
@@ -133,7 +147,7 @@ export function NoteCard({
|
||||
const { data: session } = useSession()
|
||||
const { t, language } = useLanguage()
|
||||
const { notebooks, moveNoteToNotebookOptimistic } = useNotebooks()
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const [, startTransition] = useTransition()
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
const [showCollaboratorDialog, setShowCollaboratorDialog] = useState(false)
|
||||
const [collaborators, setCollaborators] = useState<any[]>([])
|
||||
@@ -172,23 +186,33 @@ export function NoteCard({
|
||||
|
||||
// Load collaborators when note changes
|
||||
useEffect(() => {
|
||||
let isMounted = true
|
||||
|
||||
const loadCollaborators = async () => {
|
||||
if (note.userId) {
|
||||
if (note.userId && isMounted) {
|
||||
try {
|
||||
const users = await getNoteAllUsers(note.id)
|
||||
setCollaborators(users)
|
||||
// Owner is always first in the list
|
||||
if (users.length > 0) {
|
||||
setOwner(users[0])
|
||||
if (isMounted) {
|
||||
setCollaborators(users)
|
||||
// Owner is always first in the list
|
||||
if (users.length > 0) {
|
||||
setOwner(users[0])
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load collaborators:', error)
|
||||
setCollaborators([])
|
||||
if (isMounted) {
|
||||
setCollaborators([])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadCollaborators()
|
||||
|
||||
return () => {
|
||||
isMounted = false
|
||||
}
|
||||
}, [note.id, note.userId])
|
||||
|
||||
const handleDelete = async () => {
|
||||
@@ -525,47 +549,12 @@ export function NoteCard({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Labels - ONLY show if note belongs to a notebook (labels are contextual per PRD) */}
|
||||
{/* Labels - using shared LabelBadge component */}
|
||||
{optimisticNote.notebookId && optimisticNote.labels && optimisticNote.labels.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mt-3">
|
||||
{optimisticNote.labels.map((label) => {
|
||||
// Map label names to Keep style colors
|
||||
const getLabelColor = (labelName: string) => {
|
||||
if (labelName.includes('hôtels') || labelName.includes('réservations')) {
|
||||
return 'bg-indigo-50 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300'
|
||||
} else if (labelName.includes('vols') || labelName.includes('flight')) {
|
||||
return 'bg-sky-50 dark:bg-sky-900/30 text-sky-700 dark:text-sky-300'
|
||||
} else if (labelName.includes('restos') || labelName.includes('restaurant')) {
|
||||
return 'bg-orange-50 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300'
|
||||
} else {
|
||||
return 'bg-emerald-50 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300'
|
||||
}
|
||||
}
|
||||
|
||||
// Map label names to Keep style icons
|
||||
const getLabelIcon = (labelName: string) => {
|
||||
if (labelName.includes('hôtels')) return 'label'
|
||||
else if (labelName.includes('vols')) return 'flight'
|
||||
else if (labelName.includes('restos')) return 'restaurant'
|
||||
else return 'label'
|
||||
}
|
||||
|
||||
const icon = getLabelIcon(label)
|
||||
const colorClass = getLabelColor(label)
|
||||
|
||||
return (
|
||||
<span
|
||||
key={label}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold",
|
||||
colorClass
|
||||
)}
|
||||
>
|
||||
<Tag className="w-3 h-3" />
|
||||
{label}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
{optimisticNote.labels.map((label) => (
|
||||
<LabelBadge key={label} label={label} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -655,4 +644,4 @@ export function NoteCard({
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -234,7 +234,7 @@ export function NotebooksList() {
|
||||
)}
|
||||
>
|
||||
<NotebookIcon className="w-5 h-5 flex-shrink-0" />
|
||||
<span className="text-sm font-medium tracking-wide truncate flex-1 text-left">{notebook.name}</span>
|
||||
<span className="text-sm font-medium tracking-wide truncate min-w-0 text-left">{notebook.name}</span>
|
||||
{notebook.notesCount > 0 && (
|
||||
<span className="text-xs text-gray-400 ml-2 flex-shrink-0">({notebook.notesCount})</span>
|
||||
)}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useTransition, useOptimistic } from 'react'
|
||||
import { Note } from '@/lib/types'
|
||||
import { Clock } from 'lucide-react'
|
||||
import { Clock, Pin, FolderOpen, Trash2, Folder, X } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
import { Button } from './ui/button'
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from './ui/dropdown-menu'
|
||||
import { togglePin, deleteNote, dismissFromRecent } from '@/app/actions/notes'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useNotebooks } from '@/context/notebooks-context'
|
||||
import { useNoteRefresh } from '@/context/NoteRefreshContext'
|
||||
import { toast } from 'sonner'
|
||||
import { StickyNote } from 'lucide-react'
|
||||
|
||||
interface RecentNotesSectionProps {
|
||||
recentNotes: Note[]
|
||||
@@ -53,16 +62,89 @@ function CompactCard({
|
||||
onEdit?: (note: Note, readOnly?: boolean) => void
|
||||
}) {
|
||||
const { t } = useLanguage()
|
||||
const timeAgo = getCompactTime(note.contentUpdatedAt || note.updatedAt, t)
|
||||
const router = useRouter()
|
||||
const { notebooks, moveNoteToNotebookOptimistic } = useNotebooks()
|
||||
const { triggerRefresh } = useNoteRefresh()
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
const [showNotebookMenu, setShowNotebookMenu] = useState(false)
|
||||
const [, startTransition] = useTransition()
|
||||
|
||||
// Optimistic UI state
|
||||
const [optimisticNote, addOptimisticNote] = useOptimistic(
|
||||
note,
|
||||
(state: Note, newProps: Partial<Note>) => ({ ...state, ...newProps })
|
||||
)
|
||||
|
||||
const timeAgo = getCompactTime(optimisticNote.contentUpdatedAt || optimisticNote.updatedAt, t)
|
||||
const isFirstNote = index === 0
|
||||
|
||||
const handleTogglePin = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
startTransition(async () => {
|
||||
const newPinnedState = !optimisticNote.isPinned
|
||||
addOptimisticNote({ isPinned: newPinnedState })
|
||||
await togglePin(note.id, newPinnedState)
|
||||
|
||||
// Trigger global refresh to update lists
|
||||
triggerRefresh()
|
||||
router.refresh()
|
||||
|
||||
if (newPinnedState) {
|
||||
toast.success(t('notes.pinned') || 'Note pinned')
|
||||
} else {
|
||||
toast.info(t('notes.unpinned') || 'Note unpinned')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleMoveToNotebook = async (notebookId: string | null) => {
|
||||
await moveNoteToNotebookOptimistic(note.id, notebookId)
|
||||
setShowNotebookMenu(false)
|
||||
triggerRefresh()
|
||||
}
|
||||
|
||||
const handleDelete = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
if (confirm(t('notes.confirmDelete'))) {
|
||||
setIsDeleting(true)
|
||||
try {
|
||||
await deleteNote(note.id)
|
||||
triggerRefresh()
|
||||
router.refresh()
|
||||
} catch (error) {
|
||||
console.error('Failed to delete note:', error)
|
||||
setIsDeleting(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleDismiss = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
// Optimistic removal
|
||||
setIsDeleting(true)
|
||||
try {
|
||||
await dismissFromRecent(note.id)
|
||||
// Don't refresh list to prevent immediate replacement
|
||||
// triggerRefresh()
|
||||
// router.refresh()
|
||||
toast.success(t('notes.dismissed') || 'Note dismissed from recent')
|
||||
} catch (error) {
|
||||
console.error('Failed to dismiss note:', error)
|
||||
setIsDeleting(false)
|
||||
toast.error(t('common.error') || 'Failed to dismiss')
|
||||
}
|
||||
}
|
||||
|
||||
if (isDeleting) return null
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => onEdit?.(note)}
|
||||
<div
|
||||
className={cn(
|
||||
"group relative text-left p-4 bg-card border rounded-xl shadow-sm hover:shadow-md transition-all duration-200 min-h-[44px]",
|
||||
isFirstNote && "ring-2 ring-primary/20"
|
||||
"group relative flex flex-col p-4 bg-card border rounded-xl shadow-sm hover:shadow-md transition-all duration-200 min-h-[44px]",
|
||||
isFirstNote && "ring-2 ring-primary/20",
|
||||
"cursor-pointer"
|
||||
)}
|
||||
onClick={() => onEdit?.(note)}
|
||||
>
|
||||
<div className={cn(
|
||||
"absolute left-0 top-0 bottom-0 w-1 rounded-l-xl",
|
||||
@@ -73,14 +155,83 @@ function CompactCard({
|
||||
: "bg-muted dark:bg-muted/60"
|
||||
)} />
|
||||
|
||||
{/* Action Buttons Overlay - Ensure visible on hover and touch devices often handle this with tap */}
|
||||
<div className="absolute top-2 right-2 flex items-center gap-1 opacity-100 md:opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
||||
{/* Pin Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 bg-background/50 backdrop-blur-sm border shadow-sm md:shadow-none md:border-none md:bg-transparent"
|
||||
onClick={handleTogglePin}
|
||||
title={optimisticNote.isPinned ? t('notes.unpin') : t('notes.pin')}
|
||||
>
|
||||
<Pin className={cn("h-3.5 w-3.5", optimisticNote.isPinned ? "fill-current text-primary" : "text-muted-foreground")} />
|
||||
</Button>
|
||||
|
||||
{/* Move to Notebook */}
|
||||
<DropdownMenu open={showNotebookMenu} onOpenChange={setShowNotebookMenu}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 bg-background/50 backdrop-blur-sm border shadow-sm md:shadow-none md:border-none md:bg-transparent"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
title={t('notebookSuggestion.moveToNotebook')}
|
||||
>
|
||||
<FolderOpen className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
|
||||
{t('notebookSuggestion.moveToNotebook')}
|
||||
</div>
|
||||
<DropdownMenuItem onClick={() => handleMoveToNotebook(null)}>
|
||||
<StickyNote className="h-4 w-4 mr-2" />
|
||||
{t('notebookSuggestion.generalNotes')}
|
||||
</DropdownMenuItem>
|
||||
{notebooks.map((notebook: any) => (
|
||||
<DropdownMenuItem
|
||||
key={notebook.id}
|
||||
onClick={() => handleMoveToNotebook(notebook.id)}
|
||||
>
|
||||
<Folder className="h-4 w-4 mr-2" />
|
||||
{notebook.name}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
{/* Dismiss Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 hover:text-destructive bg-background/50 backdrop-blur-sm border shadow-sm md:shadow-none md:border-none md:bg-transparent"
|
||||
onClick={handleDismiss}
|
||||
title={t('common.close') || 'Dismiss'}
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
|
||||
{/* Delete Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 hover:text-destructive bg-background/50 backdrop-blur-sm border shadow-sm md:shadow-none md:border-none md:bg-transparent"
|
||||
onClick={handleDelete}
|
||||
title={t('notes.delete')}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="pl-2">
|
||||
<h3 className="text-sm font-semibold text-foreground line-clamp-1 mb-2">
|
||||
{note.title || t('notes.untitled')}
|
||||
{optimisticNote.title || t('notes.untitled')}
|
||||
</h3>
|
||||
|
||||
<p className="text-xs text-muted-foreground line-clamp-2 mb-3 min-h-[2.5rem]">
|
||||
{note.content?.substring(0, 80) || ''}
|
||||
{note.content && note.content.length > 80 && '...'}
|
||||
{(optimisticNote.content && typeof optimisticNote.content === 'string') ? optimisticNote.content.substring(0, 80) : ''}
|
||||
{optimisticNote.content && typeof optimisticNote.content === 'string' && optimisticNote.content.length > 80 && '...'}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between pt-2 border-t border-border">
|
||||
@@ -90,18 +241,19 @@ function CompactCard({
|
||||
</span>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
{note.notebookId && (
|
||||
{optimisticNote.isPinned && (
|
||||
<Pin className="w-3 h-3 text-primary fill-current" />
|
||||
)}
|
||||
{optimisticNote.notebookId && (
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary dark:bg-primary/70" title={t('notes.inNotebook')} />
|
||||
)}
|
||||
{note.labels && note.labels.length > 0 && (
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400" title={t('labels.count', { count: note.labels.length })} />
|
||||
{optimisticNote.labels && optimisticNote.labels.length > 0 && (
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400" title={t('labels.count', { count: optimisticNote.labels.length })} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute top-3 right-3 w-2 h-2 rounded-full bg-primary opacity-0 group-hover:opacity-100 transition-opacity duration-200" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { createContext, useContext, useEffect, useState, useCallback, useRef } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { SupportedLanguage, loadTranslations, getTranslationValue, Translations } from './load-translations'
|
||||
|
||||
@@ -13,18 +13,35 @@ type LanguageContextType = {
|
||||
|
||||
const LanguageContext = createContext<LanguageContextType | undefined>(undefined)
|
||||
|
||||
const RTL_LANGUAGES: SupportedLanguage[] = ['ar', 'fa']
|
||||
|
||||
function updateDocumentDirection(lang: SupportedLanguage) {
|
||||
document.documentElement.lang = lang
|
||||
document.documentElement.dir = RTL_LANGUAGES.includes(lang) ? 'rtl' : 'ltr'
|
||||
}
|
||||
|
||||
export function LanguageProvider({ children, initialLanguage = 'en' }: {
|
||||
children: ReactNode
|
||||
initialLanguage?: SupportedLanguage
|
||||
}) {
|
||||
const [language, setLanguageState] = useState<SupportedLanguage>(initialLanguage)
|
||||
const [translations, setTranslations] = useState<Translations | null>(null)
|
||||
const cacheRef = useRef<Map<SupportedLanguage, Translations>>(new Map())
|
||||
|
||||
// Load translations when language changes
|
||||
// Load translations when language changes (with caching)
|
||||
useEffect(() => {
|
||||
const cached = cacheRef.current.get(language)
|
||||
if (cached) {
|
||||
setTranslations(cached)
|
||||
updateDocumentDirection(language)
|
||||
return
|
||||
}
|
||||
|
||||
const loadLang = async () => {
|
||||
const loaded = await loadTranslations(language)
|
||||
cacheRef.current.set(language, loaded)
|
||||
setTranslations(loaded)
|
||||
updateDocumentDirection(language)
|
||||
}
|
||||
loadLang()
|
||||
}, [language])
|
||||
@@ -34,7 +51,6 @@ export function LanguageProvider({ children, initialLanguage = 'en' }: {
|
||||
const saved = localStorage.getItem('user-language') as SupportedLanguage
|
||||
if (saved) {
|
||||
setLanguageState(saved)
|
||||
document.documentElement.lang = saved
|
||||
} else {
|
||||
// Auto-detect from browser language
|
||||
const browserLang = navigator.language.split('-')[0] as SupportedLanguage
|
||||
@@ -43,35 +59,44 @@ export function LanguageProvider({ children, initialLanguage = 'en' }: {
|
||||
if (supportedLangs.includes(browserLang)) {
|
||||
setLanguageState(browserLang)
|
||||
localStorage.setItem('user-language', browserLang)
|
||||
document.documentElement.lang = browserLang
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setLanguage = (lang: SupportedLanguage) => {
|
||||
const setLanguage = useCallback((lang: SupportedLanguage) => {
|
||||
setLanguageState(lang)
|
||||
localStorage.setItem('user-language', lang)
|
||||
// Update HTML lang attribute for font styling
|
||||
document.documentElement.lang = lang
|
||||
}
|
||||
updateDocumentDirection(lang)
|
||||
}, [])
|
||||
|
||||
const t = (key: string, params?: Record<string, string | number>) => {
|
||||
const t = useCallback((key: string, params?: Record<string, string | number>) => {
|
||||
if (!translations) return key
|
||||
|
||||
let value: any = getTranslationValue(translations, key)
|
||||
|
||||
// Replace parameters like {count}, {percentage}, etc.
|
||||
if (params) {
|
||||
if (params && typeof value === 'string') {
|
||||
Object.entries(params).forEach(([param, paramValue]) => {
|
||||
value = value.replace(`{${param}}`, String(paramValue))
|
||||
})
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
return typeof value === 'string' ? value : key
|
||||
}, [translations])
|
||||
|
||||
// During initial load, show children with the initial language as fallback
|
||||
// to prevent blank flash
|
||||
if (!translations) {
|
||||
return null // Show loading state if needed
|
||||
return (
|
||||
<LanguageContext.Provider value={{
|
||||
language: initialLanguage,
|
||||
setLanguage,
|
||||
t: (key: string) => key,
|
||||
translations: {} as Translations
|
||||
}}>
|
||||
{children}
|
||||
</LanguageContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -49,7 +49,7 @@ export async function detectUserLanguage(): Promise<SupportedLanguage> {
|
||||
if (sortedLanguages.length > 0) {
|
||||
const topLanguage = sortedLanguages[0][0] as SupportedLanguage
|
||||
// Verify it's a supported language
|
||||
if (['fr', 'en', 'es', 'de', 'fa'].includes(topLanguage)) {
|
||||
if (['en', 'fr', 'es', 'de', 'fa', 'it', 'pt', 'ru', 'zh', 'ja', 'ko', 'ar', 'hi', 'nl', 'pl'].includes(topLanguage)) {
|
||||
return topLanguage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "مزود توليد الوسوم",
|
||||
"title": "تكوين الذكاء الاصطناعي",
|
||||
"updateFailed": "فشل تحديث إعدادات الذكاء الاصطناعي",
|
||||
"updateSuccess": "تم تحديث إعدادات الذكاء الاصطناعي بنجاح"
|
||||
"updateSuccess": "تم تحديث إعدادات الذكاء الاصطناعي بنجاح",
|
||||
"bestValue": "أفضل قيمة",
|
||||
"bestQuality": "أفضل جودة",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(تم الحفظ)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "اختبر مزودي الذكاء الاصطناعي لتوليد الوسوم وتضمينات البحث الدلالي",
|
||||
@@ -153,34 +159,36 @@
|
||||
"analyzing": "الذكاء الاصطناعي يحلل...",
|
||||
"assistant": "مساعد الذكاء الاصطناعي",
|
||||
"autoLabels": {
|
||||
"analyzing": "تحليل ملاحظاتك لاقتراحات التصنيفات...",
|
||||
"create": "إنشاء",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"created": "{count} labels created successfully",
|
||||
"creating": "إنشاء التصنيفات...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"error": "فشل في جلب اقتراحات التصنيفات",
|
||||
"noLabelsSelected": "لم يتم اختيار تصنيفات",
|
||||
"created": "تم إنشاء {count} تصنيفات بنجاح",
|
||||
"analyzing": "تحليل ملاحظاتك...",
|
||||
"title": "اقتراحات تصنيفات جديدة",
|
||||
"description": "اكتشفت مواضيع متكررة في \"{notebookName}\" ({totalNotes} ملاحظات). هل تريد إنشاء تصنيفات لها؟",
|
||||
"note": "ملاحظة",
|
||||
"notes": "ملاحظات",
|
||||
"typeContent": "اكتب محتوى للحصول على اقتراحات التصنيفات...",
|
||||
"createNewLabel": "إنشاء هذا التصنيف الجديد وإضافته",
|
||||
"new": "(جديد)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "اقتراحات التصنيفات",
|
||||
"typeContent": "Type content to get label suggestions..."
|
||||
"create": "إنشاء",
|
||||
"creating": "جارٍ إنشاء التصنيفات...",
|
||||
"notesCount": "{count} ملاحظات",
|
||||
"typeForSuggestions": "اكتب محتوى للحصول على اقتراحات التصنيفات..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Failed to apply organization plan",
|
||||
"applying": "Applying...",
|
||||
"description": "سيقوم الذكاء الاصطناعي بتحليل ملاحظاتك واقتراح تنظيمها في دفاتر.",
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Select all notes in {notebook}",
|
||||
"selectNote": "Select note: {title}",
|
||||
"success": "{count} notes moved successfully",
|
||||
"title": "Organize with AI"
|
||||
"title": "تنظيم بالذكاء الاصطناعي",
|
||||
"description": "سيحلل الذكاء الاصطناعي ملاحظاتك ويقترح تنظيمها في دفاتر.",
|
||||
"analyzing": "تحليل ملاحظاتك...",
|
||||
"noNotebooks": "لا توجد دفاتر متاحة. أنشئ دفاتر أولاً.",
|
||||
"noSuggestions": "لم يتمكن الذكاء الاصطناعي من إيجاد طريقة جيدة لتنظيم هذه الملاحظات.",
|
||||
"apply": "تطبيق",
|
||||
"applying": "جارٍ التطبيق...",
|
||||
"success": "تم نقل {count} ملاحظات بنجاح",
|
||||
"error": "فشل في إنشاء خطة التنظيم",
|
||||
"noNotesSelected": "لم يتم اختيار ملاحظات",
|
||||
"applyFailed": "فشل في تطبيق خطة التنظيم",
|
||||
"selectAllIn": "تحديد جميع الملاحظات في {notebook}",
|
||||
"selectNote": "تحديد ملاحظة: {title}"
|
||||
},
|
||||
"clarify": "توضيح",
|
||||
"clickToAddTag": "انقر لإضافة هذا الوسم",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "Tags-Generierungsanbieter",
|
||||
"title": "KI-Konfiguration",
|
||||
"updateFailed": "Fehler beim Aktualisieren der KI-Einstellungen",
|
||||
"updateSuccess": "KI-Einstellungen erfolgreich aktualisiert"
|
||||
"updateSuccess": "KI-Einstellungen erfolgreich aktualisiert",
|
||||
"bestValue": "Bestes Preis-Leistungs-Verhältnis",
|
||||
"bestQuality": "Beste Qualität",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(Gespeichert)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Testen Sie Ihre KI-Anbieter für Tag-Generierung und semantische Such-Embeddings",
|
||||
@@ -153,34 +159,36 @@
|
||||
"analyzing": "KI analysiert...",
|
||||
"assistant": "KI-Assistent",
|
||||
"autoLabels": {
|
||||
"analyzing": "Analysiere Ihre Notizen für Etikettenvorschläge...",
|
||||
"create": "Erstellen",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"created": "{count} labels created successfully",
|
||||
"creating": "Erstelle Etiketten...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"error": "Fehler beim Abrufen der Etikettvorschläge",
|
||||
"noLabelsSelected": "Keine Etiketten ausgewählt",
|
||||
"created": "{count} Etiketten erfolgreich erstellt",
|
||||
"analyzing": "Analysiere deine Notizen...",
|
||||
"title": "Neue Etikettvorschläge",
|
||||
"description": "Ich habe wiederkehrende Themen in \"{notebookName}\" ({totalNotes} Notizen) erkannt. Etiketten erstellen?",
|
||||
"note": "Notiz",
|
||||
"notes": "Notizen",
|
||||
"typeContent": "Inhalt eingeben für Etikettvorschläge...",
|
||||
"createNewLabel": "Dieses neue Etikett erstellen und hinzufügen",
|
||||
"new": "(neu)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "Etikettenvorschläge",
|
||||
"typeContent": "Type content to get label suggestions..."
|
||||
"create": "Erstellen",
|
||||
"creating": "Etiketten werden erstellt...",
|
||||
"notesCount": "{count} Notizen",
|
||||
"typeForSuggestions": "Inhalt eingeben für Etikettvorschläge..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Failed to apply organization plan",
|
||||
"applying": "Applying...",
|
||||
"description": "Die KI analysiert Ihre Notizen und schlägt vor, sie in Notizbüchern zu organisieren.",
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Select all notes in {notebook}",
|
||||
"selectNote": "Select note: {title}",
|
||||
"success": "{count} notes moved successfully",
|
||||
"title": "Organize with AI"
|
||||
"title": "Mit KI organisieren",
|
||||
"description": "Die KI analysiert deine Notizen und schlägt vor, sie in Notizbücher zu organisieren.",
|
||||
"analyzing": "Analysiere deine Notizen...",
|
||||
"noNotebooks": "Keine Notizbücher verfügbar. Erstelle zuerst Notizbücher.",
|
||||
"noSuggestions": "Die KI konnte keine gute Möglichkeit finden, diese Notizen zu organisieren.",
|
||||
"apply": "Anwenden",
|
||||
"applying": "Wird angewendet...",
|
||||
"success": "{count} Notizen erfolgreich verschoben",
|
||||
"error": "Fehler beim Erstellen des Organisationsplans",
|
||||
"noNotesSelected": "Keine Notizen ausgewählt",
|
||||
"applyFailed": "Fehler beim Anwenden des Organisationsplans",
|
||||
"selectAllIn": "Alle Notizen in {notebook} auswählen",
|
||||
"selectNote": "Notiz auswählen: {title}"
|
||||
},
|
||||
"clarify": "Klarstellen",
|
||||
"clickToAddTag": "Klicken Sie, um diesen Tag hinzuzufügen",
|
||||
@@ -193,7 +201,7 @@
|
||||
"languageDetected": "Sprache erkannt",
|
||||
"notebookSummary": {
|
||||
"regenerate": "Zusammenfassung neu generieren",
|
||||
"regenerating": "Generiere Zusammenfassung neu..."
|
||||
"regenerating": "Zusammenfassung wird neu generiert..."
|
||||
},
|
||||
"original": "Original",
|
||||
"poweredByAI": "Powered by KI",
|
||||
|
||||
@@ -291,34 +291,6 @@
|
||||
"regenerating": "Regenerating summary..."
|
||||
}
|
||||
},
|
||||
"batchOrganization": {
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"title": "Organize with AI",
|
||||
"description": "AI will analyze your notes and suggest organizing them into notebooks.",
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"notesToOrganize": "{count} notes to organize",
|
||||
"selected": "{count} selected",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"confidence": "confidence",
|
||||
"unorganized": "{count} notes couldn't be categorized and will stay in General Notes.",
|
||||
"applying": "Applying...",
|
||||
"apply": "Apply ({count})"
|
||||
},
|
||||
"autoLabels": {
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"created": "{count} labels created successfully",
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"title": "New Label Suggestions",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"typeContent": "Type content to get label suggestions...",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"new": "(new)"
|
||||
},
|
||||
"titleSuggestions": {
|
||||
"available": "Title suggestions",
|
||||
"title": "AI suggestions",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "Proveedor de generación de etiquetas",
|
||||
"title": "Configuración de IA",
|
||||
"updateFailed": "Error al actualizar la configuración de IA",
|
||||
"updateSuccess": "Configuración de IA actualizada correctamente"
|
||||
"updateSuccess": "Configuración de IA actualizada correctamente",
|
||||
"bestValue": "Mejor relación calidad/precio",
|
||||
"bestQuality": "Mejor calidad",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(Guardado)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Prueba tus proveedores de IA para generación de etiquetas y embeddings de búsqueda semántica",
|
||||
@@ -153,35 +159,36 @@
|
||||
"analyzing": "IA analizando...",
|
||||
"assistant": "Asistente IA",
|
||||
"autoLabels": {
|
||||
"analyzing": "Analizando tus notas para sugerencias de etiquetas...",
|
||||
"create": "Crear",
|
||||
"createNewLabel": "Crear nueva etiqueta",
|
||||
"created": "{count} labels created successfully",
|
||||
"creating": "Creando etiquetas...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"error": "Error al obtener sugerencias de etiquetas",
|
||||
"noLabelsSelected": "No se seleccionaron etiquetas",
|
||||
"created": "{count} etiquetas creadas exitosamente",
|
||||
"analyzing": "Analizando tus notas...",
|
||||
"title": "Nuevas sugerencias de etiquetas",
|
||||
"description": "He detectado temas recurrentes en \"{notebookName}\" ({totalNotes} notas). ¿Crear etiquetas para ellos?",
|
||||
"note": "nota",
|
||||
"notes": "notas",
|
||||
"typeContent": "Escribe contenido para obtener sugerencias de etiquetas...",
|
||||
"createNewLabel": "Crear esta nueva etiqueta y agregarla",
|
||||
"new": "(nuevo)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "Sugerencias de etiquetas",
|
||||
"typeContent": "Type content to get label suggestions...",
|
||||
"typeForSuggestions": "Escribe para obtener sugerencias"
|
||||
"create": "Crear",
|
||||
"creating": "Creando etiquetas...",
|
||||
"notesCount": "{count} notas",
|
||||
"typeForSuggestions": "Escribe contenido para obtener sugerencias de etiquetas..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Error al aplicar",
|
||||
"applying": "Applying...",
|
||||
"description": "La IA analizará tus notas y sugerirá organizarlas en libretas.",
|
||||
"error": "Error al organizar",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "Sin notas seleccionadas",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Seleccionar todo en",
|
||||
"selectNote": "Seleccionar nota",
|
||||
"success": "Organización completada",
|
||||
"title": "Organizar con IA"
|
||||
"title": "Organizar con IA",
|
||||
"description": "La IA analizará tus notas y sugerirá organizarlas en cuadernos.",
|
||||
"analyzing": "Analizando tus notas...",
|
||||
"noNotebooks": "No hay cuadernos disponibles. Crea cuadernos primero para organizar tus notas.",
|
||||
"noSuggestions": "La IA no pudo encontrar una buena manera de organizar estas notas.",
|
||||
"apply": "Aplicar",
|
||||
"applying": "Aplicando...",
|
||||
"success": "{count} notas movidas exitosamente",
|
||||
"error": "Error al crear el plan de organización",
|
||||
"noNotesSelected": "No se seleccionaron notas",
|
||||
"applyFailed": "Error al aplicar el plan de organización",
|
||||
"selectAllIn": "Seleccionar todas las notas en {notebook}",
|
||||
"selectNote": "Seleccionar nota: {title}"
|
||||
},
|
||||
"clarify": "Aclarar",
|
||||
"clickToAddTag": "Haz clic para agregar esta etiqueta",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "ارائهدهنده تولید برچسب",
|
||||
"title": "پیکربندی هوش مصنوعی",
|
||||
"updateFailed": "شکست در بهروزرسانی تنظیمات هوش مصنوعی",
|
||||
"updateSuccess": "تنظیمات هوش مصنوعی با موفقیت بهروزرسانی شد"
|
||||
"updateSuccess": "تنظیمات هوش مصنوعی با موفقیت بهروزرسانی شد",
|
||||
"bestValue": "بهترین ارزش",
|
||||
"bestQuality": "بهترین کیفیت",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(ذخیره شد)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "تست ارائهدهندگان هوش مصنوعی برای تولید برچسب و تعبیههای جستجوی معنایی",
|
||||
@@ -153,34 +159,36 @@
|
||||
"analyzing": "در حال تحلیل هوش مصنوعی...",
|
||||
"assistant": "دستیار هوش مصنوعی",
|
||||
"autoLabels": {
|
||||
"analyzing": "در حال تحلیل یادداشتهای شما برای پیشنهادات برچسب...",
|
||||
"analyzing": "تحلیل یادداشتهای شما...",
|
||||
"create": "ایجاد",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"created": "{count} labels created successfully",
|
||||
"createNewLabel": "ایجاد این برچسب جدید و اضافه کردن آن",
|
||||
"created": "{count} برچسب با موفقیت ایجاد شد",
|
||||
"creating": "در حال ایجاد برچسبها...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"description": "من موضوعات تکراری در \"{notebookName}\" ({totalNotes} یادداشت) تشخیص دادم. برچسب برای آنها ایجاد شود؟",
|
||||
"error": "دریافت پیشنهادهای برچسب ناموفق",
|
||||
"new": "(جدید)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "پیشنهادات برچسب",
|
||||
"typeContent": "Type content to get label suggestions..."
|
||||
"noLabelsSelected": "برچسبی انتخاب نشده",
|
||||
"note": "یادداشت",
|
||||
"notes": "یادداشت",
|
||||
"title": "پیشنهادهای برچسب جدید",
|
||||
"typeContent": "برای دریافت پیشنهاد برچسب محتوا وارد کنید...",
|
||||
"notesCount": "{count} یادداشت",
|
||||
"typeForSuggestions": "برای دریافت پیشنهاد برچسب محتوا وارد کنید..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Failed to apply organization plan",
|
||||
"applying": "Applying...",
|
||||
"description": "هوش مصنوعی یادداشتهای شما را تحلیل کرده و پیشنهاد میکند آنها را در دفترچهها سازماندهی کنید.",
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Select all notes in {notebook}",
|
||||
"selectNote": "Select note: {title}",
|
||||
"success": "{count} notes moved successfully",
|
||||
"title": "Organize with AI"
|
||||
"analyzing": "تحلیل یادداشتهای شما...",
|
||||
"apply": "اعمال",
|
||||
"applyFailed": "اعمال طرح سازماندهی ناموفق",
|
||||
"applying": "در حال اعمال...",
|
||||
"description": "هوش مصنوعی یادداشتهای شما را تحلیل و پیشنهاد سازماندهی در دفترچهها را میدهد.",
|
||||
"error": "ایجاد طرح سازماندهی ناموفق",
|
||||
"noNotebooks": "دفترچهای موجود نیست. ابتدا دفترچه ایجاد کنید.",
|
||||
"noNotesSelected": "یادداشتی انتخاب نشده",
|
||||
"noSuggestions": "هوش مصنوعی نتوانست روش خوبی برای سازماندهی این یادداشتها پیدا کند.",
|
||||
"selectAllIn": "انتخاب تمام یادداشتها در {notebook}",
|
||||
"selectNote": "انتخاب یادداشت: {title}",
|
||||
"success": "{count} یادداشت با موفقیت جابجا شد",
|
||||
"title": "سازماندهی با هوش مصنوعی"
|
||||
},
|
||||
"clarify": "شفافسازی",
|
||||
"clickToAddTag": "برای افزودن این برچسب کلیک کنید",
|
||||
@@ -192,8 +200,8 @@
|
||||
"improveStyle": "بهبود سبک",
|
||||
"languageDetected": "زبان شناسایی شد",
|
||||
"notebookSummary": {
|
||||
"regenerate": "تولید مجدد خلاصه",
|
||||
"regenerating": "در حال تولید مجدد خلاصه..."
|
||||
"regenerate": "بازسازی خلاصه",
|
||||
"regenerating": "در حال بازسازی خلاصه..."
|
||||
},
|
||||
"original": "اصلی",
|
||||
"poweredByAI": "قدرت گرفته از هوش مصنوعی",
|
||||
|
||||
@@ -45,16 +45,22 @@
|
||||
"baseUrl": "URL de base",
|
||||
"commonEmbeddingModels": "Modèles d'embeddings courants pour API compatibles OpenAI",
|
||||
"commonModelsDescription": "Modèles courants pour API compatibles OpenAI",
|
||||
"description": "Configurez les fournisseurs IA pour l'étiquetage auto et la recherche sémantique.",
|
||||
"description": "Configurez les fournisseurs IA pour l'étiquetage auto et la recherche sémantique. Utilisez différents fournisseurs pour des performances optimales.",
|
||||
"embeddingsDescription": "Fournisseur IA pour la recherche sémantique. Recommandé : OpenAI (meilleure qualité).",
|
||||
"embeddingsProvider": "Fournisseur d'embeddings",
|
||||
"model": "Modèle",
|
||||
"modelRecommendations": "gpt-4o-mini = Meilleur prix • gpt-4o = Meilleure qualité",
|
||||
"modelRecommendations": "gpt-4o-mini = Meilleur rapport qualité/prix • gpt-4o = Meilleure qualité",
|
||||
"openAIKeyDescription": "Votre clé API OpenAI depuis platform.openai.com",
|
||||
"openTestPanel": "Ouvrir le panneau de test IA",
|
||||
"provider": "Fournisseur",
|
||||
"providerEmbeddingRequired": "AI_PROVIDER_EMBEDDING est requis",
|
||||
"providerTagsRequired": "AI_PROVIDER_TAGS est requis",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Gratuit)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom Compatible OpenAI",
|
||||
"bestValue": "Meilleur rapport qualité/prix",
|
||||
"bestQuality": "Meilleure qualité",
|
||||
"saved": "(Enregistré)",
|
||||
"saveSettings": "Enregistrer les paramètres IA",
|
||||
"saving": "Enregistrement...",
|
||||
"selectEmbeddingModel": "Sélectionnez un modèle d'embedding installé localement",
|
||||
@@ -63,83 +69,77 @@
|
||||
"tagsGenerationProvider": "Fournisseur de génération d'étiquettes",
|
||||
"title": "Configuration IA",
|
||||
"updateFailed": "Échec de la mise à jour des paramètres IA",
|
||||
"updateSuccess": "Paramètres IA mis à jour avec succès",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Gratuit)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom Compatible OpenAI",
|
||||
"bestValue": "Meilleur rapport qualité/prix",
|
||||
"bestQuality": "Meilleure qualité",
|
||||
"saved": "(Enregistré)"
|
||||
"updateSuccess": "Paramètres IA mis à jour avec succès"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Test your AI providers for tag generation and semantic search embeddings",
|
||||
"embeddingDimensions": "Embedding Dimensions:",
|
||||
"embeddingsTestDescription": "Test the AI provider responsible for semantic search embeddings",
|
||||
"embeddingsTestTitle": "Embeddings Test",
|
||||
"error": "Error:",
|
||||
"first5Values": "First 5 values:",
|
||||
"generatedTags": "Generated Tags:",
|
||||
"howItWorksTitle": "How Testing Works",
|
||||
"model": "Model:",
|
||||
"provider": "Provider:",
|
||||
"responseTime": "Response time: {time}ms",
|
||||
"runTest": "Run Test",
|
||||
"tagsTestDescription": "Test the AI provider responsible for automatic tag suggestions",
|
||||
"tagsTestTitle": "Tags Generation Test",
|
||||
"testError": "Test Error: {error}",
|
||||
"testFailed": "Test Failed",
|
||||
"testPassed": "Test Passed",
|
||||
"testing": "Testing...",
|
||||
"tipDescription": "Use the AI Test Panel to diagnose configuration issues before testing.",
|
||||
"tipTitle": "Tip:",
|
||||
"title": "AI Provider Testing",
|
||||
"vectorDimensions": "vector dimensions"
|
||||
"description": "Testez vos fournisseurs IA pour la génération d'étiquettes et les embeddings de recherche sémantique",
|
||||
"embeddingDimensions": "Dimensions de l'embedding :",
|
||||
"embeddingsTestDescription": "Testez le fournisseur IA responsable des embeddings de recherche sémantique",
|
||||
"embeddingsTestTitle": "Test d'embeddings",
|
||||
"error": "Erreur :",
|
||||
"first5Values": "5 premières valeurs :",
|
||||
"generatedTags": "Étiquettes générées :",
|
||||
"howItWorksTitle": "Fonctionnement des tests",
|
||||
"model": "Modèle :",
|
||||
"provider": "Fournisseur :",
|
||||
"responseTime": "Temps de réponse : {time}ms",
|
||||
"runTest": "Lancer le test",
|
||||
"tagsTestDescription": "Testez le fournisseur IA responsable des suggestions d'étiquettes automatiques",
|
||||
"tagsTestTitle": "Test de génération d'étiquettes",
|
||||
"testError": "Erreur de test : {error}",
|
||||
"testFailed": "Test échoué",
|
||||
"testPassed": "Test réussi",
|
||||
"testing": "Test en cours...",
|
||||
"tipDescription": "Utilisez le panneau de test IA pour diagnostiquer les problèmes de configuration avant de tester.",
|
||||
"tipTitle": "Astuce :",
|
||||
"title": "Test des fournisseurs IA",
|
||||
"vectorDimensions": "dimensions vectorielles"
|
||||
},
|
||||
"aiTesting": "AI Testing",
|
||||
"aiTesting": "Test IA",
|
||||
"security": {
|
||||
"allowPublicRegistration": "Allow Public Registration",
|
||||
"allowPublicRegistrationDescription": "If disabled, new users can only be added by an Administrator via the User Management page.",
|
||||
"description": "Manage access control and registration policies.",
|
||||
"title": "Security Settings",
|
||||
"updateFailed": "Failed to update security settings",
|
||||
"updateSuccess": "Security Settings updated"
|
||||
"allowPublicRegistration": "Autoriser l'inscription publique",
|
||||
"allowPublicRegistrationDescription": "Si désactivé, les nouveaux utilisateurs ne peuvent être ajoutés que par un administrateur via la page de gestion des utilisateurs.",
|
||||
"description": "Gérez le contrôle d'accès et les politiques d'inscription.",
|
||||
"title": "Paramètres de sécurité",
|
||||
"updateFailed": "Échec de la mise à jour des paramètres de sécurité",
|
||||
"updateSuccess": "Paramètres de sécurité mis à jour"
|
||||
},
|
||||
"settings": "Admin Settings",
|
||||
"settings": "Paramètres administrateur",
|
||||
"smtp": {
|
||||
"description": "Configure email server for password resets.",
|
||||
"forceSSL": "Force SSL/TLS (usually for port 465)",
|
||||
"fromEmail": "From Email",
|
||||
"host": "Host",
|
||||
"ignoreCertErrors": "Ignore Certificate Errors (Self-hosted/Dev only)",
|
||||
"password": "Password",
|
||||
"description": "Configurez le serveur email pour les réinitialisations de mot de passe.",
|
||||
"forceSSL": "Forcer SSL/TLS (généralement pour le port 465)",
|
||||
"fromEmail": "Email d'expédition",
|
||||
"host": "Hôte",
|
||||
"ignoreCertErrors": "Ignorer les erreurs de certificat (Auto-hébergé/Dev uniquement)",
|
||||
"password": "Mot de passe",
|
||||
"port": "Port",
|
||||
"saveSettings": "Save SMTP Settings",
|
||||
"sending": "Sending...",
|
||||
"testEmail": "Test Email",
|
||||
"testFailed": "Failed: {error}",
|
||||
"testSuccess": "Test email sent successfully!",
|
||||
"title": "SMTP Configuration",
|
||||
"updateFailed": "Failed to update SMTP settings",
|
||||
"updateSuccess": "SMTP Settings updated",
|
||||
"username": "Username"
|
||||
"saveSettings": "Enregistrer les paramètres SMTP",
|
||||
"sending": "Envoi en cours...",
|
||||
"testEmail": "Email de test",
|
||||
"testFailed": "Échec : {error}",
|
||||
"testSuccess": "Email de test envoyé avec succès !",
|
||||
"title": "Configuration SMTP",
|
||||
"updateFailed": "Échec de la mise à jour des paramètres SMTP",
|
||||
"updateSuccess": "Paramètres SMTP mis à jour",
|
||||
"username": "Nom d'utilisateur"
|
||||
},
|
||||
"title": "Admin Dashboard",
|
||||
"userManagement": "User Management",
|
||||
"title": "Tableau de bord Admin",
|
||||
"userManagement": "Gestion des utilisateurs",
|
||||
"users": {
|
||||
"addUser": "Add User",
|
||||
"addUser": "Ajouter un utilisateur",
|
||||
"confirmDelete": "Êtes-vous sûr ? Cette action est irréversible.",
|
||||
"createFailed": "Failed to create user",
|
||||
"createSuccess": "User created successfully",
|
||||
"createUser": "Create User",
|
||||
"createUserDescription": "Add a new user to the system.",
|
||||
"createFailed": "Échec de la création de l'utilisateur",
|
||||
"createSuccess": "Utilisateur créé avec succès",
|
||||
"createUser": "Créer un utilisateur",
|
||||
"createUserDescription": "Ajouter un nouvel utilisateur au système.",
|
||||
"deleteFailed": "Échec de la suppression",
|
||||
"deleteSuccess": "Utilisateur supprimé",
|
||||
"demote": "Rétrograder en utilisateur",
|
||||
"email": "Email",
|
||||
"name": "Name",
|
||||
"password": "Password",
|
||||
"name": "Nom",
|
||||
"password": "Mot de passe",
|
||||
"promote": "Promouvoir en admin",
|
||||
"role": "Role",
|
||||
"role": "Rôle",
|
||||
"roleUpdateFailed": "Échec de la mise à jour du rôle",
|
||||
"roleUpdateSuccess": "Rôle de l'utilisateur mis à jour à {role}",
|
||||
"roles": {
|
||||
@@ -156,79 +156,80 @@
|
||||
}
|
||||
},
|
||||
"ai": {
|
||||
"analyzing": "AI analyzing...",
|
||||
"assistant": "AI Assistant",
|
||||
"analyzing": "Analyse IA en cours...",
|
||||
"assistant": "Assistant IA",
|
||||
"autoLabels": {
|
||||
"analyzing": "Analyse de vos notes pour les suggestions d'étiquettes...",
|
||||
"create": "Créer",
|
||||
"createNewLabel": "Créer cette nouvelle étiquette et l'ajouter",
|
||||
"created": "{count} labels created successfully",
|
||||
"created": "{count} étiquettes créées avec succès",
|
||||
"creating": "Création des étiquettes...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"description": "J'ai détecté des thèmes récurrents dans \"{notebookName}\" ({totalNotes} notes). Créer des étiquettes pour eux ?",
|
||||
"error": "Échec de la récupération des suggestions d'étiquettes",
|
||||
"new": "(nouveau)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"noLabelsSelected": "Aucune étiquette sélectionnée",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "Suggestions d'étiquettes",
|
||||
"typeContent": "Type content to get label suggestions...",
|
||||
"typeForSuggestions": "Tapez du contenu pour obtenir des suggestions d'étiquettes..."
|
||||
"typeContent": "Tapez du contenu pour obtenir des suggestions d'étiquettes...",
|
||||
"typeForSuggestions": "Tapez du contenu pour obtenir des suggestions d'étiquettes...",
|
||||
"notesCount": "{count} notes"
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"analyzing": "Analyse de vos notes...",
|
||||
"apply": "Appliquer",
|
||||
"applyFailed": "Échec de l'application du plan d'organisation",
|
||||
"applying": "Applying...",
|
||||
"applying": "Application...",
|
||||
"description": "L'IA analysera vos notes et suggérera de les organiser dans des carnets.",
|
||||
"error": "Échec de la création du plan d'organisation",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotebooks": "Aucun carnet disponible. Créez d'abord des carnets pour organiser vos notes.",
|
||||
"noNotesSelected": "Aucune note sélectionnée",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"noSuggestions": "L'IA n'a pas trouvé de bonne manière d'organiser ces notes.",
|
||||
"selectAllIn": "Sélectionner toutes les notes dans {notebook}",
|
||||
"selectNote": "Sélectionner la note : {title}",
|
||||
"success": "{count} notes déplacées avec succès",
|
||||
"title": "Organiser avec l'IA"
|
||||
},
|
||||
"clarify": "Clarify",
|
||||
"clickToAddTag": "Click to add this tag",
|
||||
"generateTitles": "Generate titles",
|
||||
"generateTitlesTooltip": "Generate titles with AI",
|
||||
"generating": "Generating...",
|
||||
"generatingTitles": "Generating titles...",
|
||||
"ignoreSuggestion": "Ignore this suggestion",
|
||||
"improveStyle": "Improve style",
|
||||
"languageDetected": "Language detected",
|
||||
"clarify": "Clarifier",
|
||||
"clickToAddTag": "Cliquer pour ajouter cette étiquette",
|
||||
"generateTitles": "Générer des titres",
|
||||
"generateTitlesTooltip": "Générer des titres avec l'IA",
|
||||
"generating": "Génération...",
|
||||
"generatingTitles": "Génération de titres...",
|
||||
"ignoreSuggestion": "Ignorer cette suggestion",
|
||||
"improveStyle": "Améliorer le style",
|
||||
"languageDetected": "Langue détectée",
|
||||
"notebookSummary": {
|
||||
"regenerate": "Régénérer le résumé",
|
||||
"regenerating": "Régénération du résumé..."
|
||||
},
|
||||
"original": "Original",
|
||||
"poweredByAI": "Powered by AI",
|
||||
"processing": "Processing...",
|
||||
"reformulateText": "Reformulate text",
|
||||
"reformulated": "Reformulated",
|
||||
"reformulating": "Reformulating...",
|
||||
"reformulationApplied": "Reformulated text applied!",
|
||||
"reformulationComparison": "Reformulation Comparison",
|
||||
"reformulationError": "Error during reformulation",
|
||||
"reformulationFailed": "Failed to reformulate text",
|
||||
"reformulationMaxWords": "Text must have maximum 500 words",
|
||||
"reformulationMinWords": "Text must have at least 10 words (current: {count} words)",
|
||||
"reformulationNoText": "Please select text or add content",
|
||||
"reformulationSelectionTooShort": "Selection too short, using full content",
|
||||
"shorten": "Shorten",
|
||||
"tagAdded": "Tag \"{tag}\" added",
|
||||
"titleApplied": "Title applied!",
|
||||
"titleGenerateWithAI": "Generate titles with AI",
|
||||
"titleGenerating": "Generating...",
|
||||
"titleGenerationError": "Error generating titles",
|
||||
"titleGenerationFailed": "Failed to generate titles",
|
||||
"titleGenerationMinWords": "Content must have at least 10 words to generate titles (current: {count} words)",
|
||||
"titlesGenerated": "💡 {count} titles generated!",
|
||||
"transformError": "Error during transformation",
|
||||
"transformMarkdown": "Transform to Markdown",
|
||||
"transformSuccess": "Text transformed to Markdown successfully!",
|
||||
"transforming": "Transforming..."
|
||||
"poweredByAI": "Propulsé par l'IA",
|
||||
"processing": "Traitement en cours...",
|
||||
"reformulateText": "Reformuler le texte",
|
||||
"reformulated": "Reformulé",
|
||||
"reformulating": "Reformulation...",
|
||||
"reformulationApplied": "Texte reformulé appliqué !",
|
||||
"reformulationComparison": "Comparaison de reformulation",
|
||||
"reformulationError": "Erreur lors de la reformulation",
|
||||
"reformulationFailed": "Échec de la reformulation du texte",
|
||||
"reformulationMaxWords": "Le texte doit avoir au maximum 500 mots",
|
||||
"reformulationMinWords": "Le texte doit avoir au moins 10 mots (actuel : {count} mots)",
|
||||
"reformulationNoText": "Veuillez sélectionner du texte ou ajouter du contenu",
|
||||
"reformulationSelectionTooShort": "Sélection trop courte, utilisation du contenu complet",
|
||||
"shorten": "Raccourcir",
|
||||
"tagAdded": "Étiquette \"{tag}\" ajoutée",
|
||||
"titleApplied": "Titre appliqué !",
|
||||
"titleGenerateWithAI": "Générer des titres avec l'IA",
|
||||
"titleGenerating": "Génération...",
|
||||
"titleGenerationError": "Erreur lors de la génération des titres",
|
||||
"titleGenerationFailed": "Échec de la génération des titres",
|
||||
"titleGenerationMinWords": "Le contenu doit avoir au moins 10 mots pour générer des titres (actuel : {count} mots)",
|
||||
"titlesGenerated": "💡 {count} titres générés !",
|
||||
"transformError": "Erreur lors de la transformation",
|
||||
"transformMarkdown": "Transformer en Markdown",
|
||||
"transformSuccess": "Texte transformé en Markdown avec succès !",
|
||||
"transforming": "Transformation..."
|
||||
},
|
||||
"aiSettings": {
|
||||
"description": "Configurez vos fonctionnalités IA et préférences",
|
||||
@@ -282,7 +283,7 @@
|
||||
"sending": "Envoi en cours...",
|
||||
"signIn": "Connexion",
|
||||
"signInToAccount": "Connectez-vous à votre compte",
|
||||
"signOut": "Sign out",
|
||||
"signOut": "Déconnexion",
|
||||
"signUp": "S'inscrire"
|
||||
},
|
||||
"autoLabels": {
|
||||
@@ -387,51 +388,51 @@
|
||||
},
|
||||
"dataManagement": {
|
||||
"cleanup": {
|
||||
"button": "Cleanup",
|
||||
"description": "Remove labels and connections that reference deleted notes.",
|
||||
"failed": "Error during cleanup",
|
||||
"title": "Cleanup Orphaned Data"
|
||||
"button": "Nettoyer",
|
||||
"description": "Supprimer les étiquettes et connexions qui référencent des notes supprimées.",
|
||||
"failed": "Erreur lors du nettoyage",
|
||||
"title": "Nettoyer les données orphelines"
|
||||
},
|
||||
"cleanupComplete": "Nettoyage terminé : {created} créés, {deleted} supprimés",
|
||||
"cleanupError": "Erreur lors du nettoyage",
|
||||
"dangerZone": "Zone de danger",
|
||||
"dangerZoneDescription": "Supprimer définitivement vos données",
|
||||
"delete": {
|
||||
"button": "Delete All Notes",
|
||||
"confirm": "Are you sure? This will permanently delete all your notes.",
|
||||
"description": "Permanently delete all your notes. This action cannot be undone.",
|
||||
"failed": "Failed to delete notes",
|
||||
"success": "All notes deleted",
|
||||
"title": "Delete All Notes"
|
||||
"button": "Supprimer toutes les notes",
|
||||
"confirm": "Êtes-vous sûr ? Cette action supprimera définitivement toutes vos notes.",
|
||||
"description": "Supprimer définitivement toutes vos notes. Cette action est irréversible.",
|
||||
"failed": "Échec de la suppression des notes",
|
||||
"success": "Toutes les notes ont été supprimées",
|
||||
"title": "Supprimer toutes les notes"
|
||||
},
|
||||
"deleting": "Suppression...",
|
||||
"export": {
|
||||
"button": "Export Notes",
|
||||
"description": "Download all your notes as a JSON file. This includes all content, labels, and metadata.",
|
||||
"failed": "Failed to export notes",
|
||||
"success": "Notes exported successfully",
|
||||
"title": "Export All Notes"
|
||||
"button": "Exporter les notes",
|
||||
"description": "Télécharger toutes vos notes au format JSON. Inclut tout le contenu, les étiquettes et les métadonnées.",
|
||||
"failed": "Échec de l'exportation des notes",
|
||||
"success": "Notes exportées avec succès",
|
||||
"title": "Exporter toutes les notes"
|
||||
},
|
||||
"exporting": "Exportation...",
|
||||
"import": {
|
||||
"button": "Import Notes",
|
||||
"description": "Upload a JSON file to import notes. This will add to your existing notes, not replace them.",
|
||||
"failed": "Failed to import notes",
|
||||
"success": "Imported {count} notes",
|
||||
"title": "Import Notes"
|
||||
"button": "Importer des notes",
|
||||
"description": "Téléchargez un fichier JSON pour importer des notes. Les notes seront ajoutées aux existantes, pas remplacées.",
|
||||
"failed": "Échec de l'importation des notes",
|
||||
"success": "{count} notes importées",
|
||||
"title": "Importer des notes"
|
||||
},
|
||||
"importing": "Importation...",
|
||||
"indexing": {
|
||||
"button": "Rebuild Index",
|
||||
"description": "Regenerate embeddings for all notes to improve semantic search.",
|
||||
"failed": "Error during indexing",
|
||||
"success": "Indexing complete: {count} notes processed",
|
||||
"title": "Rebuild Search Index"
|
||||
"button": "Reconstruire l'index",
|
||||
"description": "Régénérer les embeddings pour toutes les notes afin d'améliorer la recherche sémantique.",
|
||||
"failed": "Erreur lors de l'indexation",
|
||||
"success": "Indexation terminée : {count} notes traitées",
|
||||
"title": "Reconstruire l'index de recherche"
|
||||
},
|
||||
"indexingComplete": "Indexation terminée : {count} notes traitées",
|
||||
"indexingError": "Erreur lors de l'indexation",
|
||||
"title": "Data Management",
|
||||
"toolsDescription": "Tools to maintain your database health"
|
||||
"title": "Gestion des données",
|
||||
"toolsDescription": "Outils pour maintenir la santé de votre base de données"
|
||||
},
|
||||
"demoMode": {
|
||||
"activated": "Mode Démo activé ! Memory Echo fonctionnera maintenant instantanément.",
|
||||
@@ -501,53 +502,53 @@
|
||||
"title": "Paramètres généraux"
|
||||
},
|
||||
"labels": {
|
||||
"addLabel": "Add label",
|
||||
"allLabels": "All Labels",
|
||||
"changeColor": "Change Color",
|
||||
"changeColorTooltip": "Change color",
|
||||
"clearAll": "Clear all",
|
||||
"confirmDelete": "Are you sure you want to delete this label?",
|
||||
"addLabel": "Ajouter une étiquette",
|
||||
"allLabels": "Toutes les étiquettes",
|
||||
"changeColor": "Changer la couleur",
|
||||
"changeColorTooltip": "Changer la couleur",
|
||||
"clearAll": "Tout effacer",
|
||||
"confirmDelete": "Êtes-vous sûr de vouloir supprimer cette étiquette ?",
|
||||
"count": "{count} étiquettes",
|
||||
"createLabel": "Create label",
|
||||
"delete": "Delete",
|
||||
"deleteTooltip": "Delete label",
|
||||
"editLabels": "Edit Labels",
|
||||
"editLabelsDescription": "Create, edit colors, or delete labels.",
|
||||
"filter": "Filter by Label",
|
||||
"filterByLabel": "Filter by label",
|
||||
"labelColor": "Label color",
|
||||
"labelName": "Label name",
|
||||
"loading": "Loading...",
|
||||
"manage": "Manage Labels",
|
||||
"manageLabels": "Manage labels",
|
||||
"manageLabelsDescription": "Add or remove labels for this note. Click on a label to change its color.",
|
||||
"manageTooltip": "Manage Labels",
|
||||
"namePlaceholder": "Enter label name",
|
||||
"newLabelPlaceholder": "Create new label",
|
||||
"createLabel": "Créer une étiquette",
|
||||
"delete": "Supprimer",
|
||||
"deleteTooltip": "Supprimer l'étiquette",
|
||||
"editLabels": "Modifier les étiquettes",
|
||||
"editLabelsDescription": "Créer, modifier les couleurs ou supprimer des étiquettes.",
|
||||
"filter": "Filtrer par étiquette",
|
||||
"filterByLabel": "Filtrer par étiquette",
|
||||
"labelColor": "Couleur de l'étiquette",
|
||||
"labelName": "Nom de l'étiquette",
|
||||
"loading": "Chargement...",
|
||||
"manage": "Gérer les étiquettes",
|
||||
"manageLabels": "Gérer les étiquettes",
|
||||
"manageLabelsDescription": "Ajouter ou supprimer des étiquettes pour cette note. Cliquez sur une étiquette pour changer sa couleur.",
|
||||
"manageTooltip": "Gérer les étiquettes",
|
||||
"namePlaceholder": "Entrez le nom de l'étiquette",
|
||||
"newLabelPlaceholder": "Créer une nouvelle étiquette",
|
||||
"noLabels": "Aucune étiquette",
|
||||
"noLabelsFound": "No labels found.",
|
||||
"notebookRequired": "⚠️ Labels are only available in notebooks. Move this note to a notebook first.",
|
||||
"selectedLabels": "Selected Labels",
|
||||
"showLess": "Show less",
|
||||
"showMore": "Show more",
|
||||
"tagAdded": "Tag \"{tag}\" added",
|
||||
"title": "Labels"
|
||||
"noLabelsFound": "Aucune étiquette trouvée.",
|
||||
"notebookRequired": "⚠️ Les étiquettes sont uniquement disponibles dans les carnets. Déplacez cette note dans un carnet d'abord.",
|
||||
"selectedLabels": "Étiquettes sélectionnées",
|
||||
"showLess": "Voir moins",
|
||||
"showMore": "Voir plus",
|
||||
"tagAdded": "Étiquette \"{tag}\" ajoutée",
|
||||
"title": "Étiquettes"
|
||||
},
|
||||
"memoryEcho": {
|
||||
"clickToView": "Cliquer pour voir la note →",
|
||||
"comparison": {
|
||||
"clickToView": "Click to view note",
|
||||
"helpful": "Helpful",
|
||||
"helpfulQuestion": "Is this comparison helpful?",
|
||||
"highSimilarityInsight": "These notes deal with the same topic with a high degree of similarity. They could be merged or consolidated.",
|
||||
"notHelpful": "Not Helpful",
|
||||
"similarityInfo": "These notes are connected by {similarity}% similarity",
|
||||
"title": "💡 Note Comparison",
|
||||
"untitled": "Untitled"
|
||||
"clickToView": "Cliquer pour voir la note",
|
||||
"helpful": "Utile",
|
||||
"helpfulQuestion": "Cette comparaison est-elle utile ?",
|
||||
"highSimilarityInsight": "Ces notes traitent du même sujet avec un fort degré de similarité. Elles pourraient être fusionnées ou consolidées.",
|
||||
"notHelpful": "Pas utile",
|
||||
"similarityInfo": "Ces notes sont connectées par {similarity}% de similarité",
|
||||
"title": "💡 Comparaison de notes",
|
||||
"untitled": "Sans titre"
|
||||
},
|
||||
"connection": "connection",
|
||||
"connections": "Connections",
|
||||
"connectionsBadge": "{count} connection{plural}",
|
||||
"connection": "connexion",
|
||||
"connections": "Connexions",
|
||||
"connectionsBadge": "{count} connexion{plural}",
|
||||
"title": "💡 J'ai remarqué quelque chose...",
|
||||
"description": "Connexions proactives entre vos notes",
|
||||
"dailyInsight": "Aperçu quotidien de vos notes",
|
||||
@@ -568,8 +569,44 @@
|
||||
"sortSimilarity": "Similarité",
|
||||
"sortOldest": "Plus ancien"
|
||||
},
|
||||
"thanksFeedback": "Thanks for your feedback!",
|
||||
"thanksFeedbackImproving": "Thanks! We'll use this to improve."
|
||||
"thanksFeedback": "Merci pour votre retour !",
|
||||
"thanksFeedbackImproving": "Merci ! Nous l'utiliserons pour nous améliorer.",
|
||||
"fused": "Fusionné",
|
||||
"editorSection": {
|
||||
"title": "⚡ Notes connectées ({count})",
|
||||
"loading": "Chargement...",
|
||||
"view": "Voir",
|
||||
"compare": "Comparer",
|
||||
"merge": "Fusionner",
|
||||
"compareAll": "Tout comparer",
|
||||
"mergeAll": "Tout fusionner",
|
||||
"close": "Fermer"
|
||||
},
|
||||
"fusion": {
|
||||
"title": "🔗 Fusion intelligente",
|
||||
"mergeNotes": "Fusionner {count} note(s)",
|
||||
"notesToMerge": "📝 Notes à fusionner",
|
||||
"optionalPrompt": "💬 Prompt de fusion (optionnel)",
|
||||
"promptPlaceholder": "Instructions optionnelles pour l'IA (ex. : 'Garder le style formel de la note 1')...",
|
||||
"generateFusion": "Générer la fusion",
|
||||
"generating": "Génération...",
|
||||
"previewTitle": "📝 Aperçu de la note fusionnée",
|
||||
"edit": "Modifier",
|
||||
"modify": "Modifier",
|
||||
"finishEditing": "Terminer l'édition",
|
||||
"optionsTitle": "Options de fusion",
|
||||
"archiveOriginals": "Archiver les notes originales",
|
||||
"keepAllTags": "Conserver toutes les étiquettes",
|
||||
"useLatestTitle": "Utiliser la note la plus récente comme titre",
|
||||
"createBacklinks": "Créer un lien vers les notes originales",
|
||||
"cancel": "Annuler",
|
||||
"confirmFusion": "Confirmer la fusion",
|
||||
"success": "Notes fusionnées avec succès !",
|
||||
"error": "Échec de la fusion des notes",
|
||||
"generateError": "Échec de la génération de la fusion",
|
||||
"noContentReturned": "Aucun contenu de fusion retourné par l'API",
|
||||
"unknownDate": "Date inconnue"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"accountSettings": "Paramètres du compte",
|
||||
@@ -607,33 +644,33 @@
|
||||
"workspace": "Espace de travail"
|
||||
},
|
||||
"notebook": {
|
||||
"cancel": "Cancel",
|
||||
"create": "Create Notebook",
|
||||
"createDescription": "Start a new collection to organize your notes, ideas, and projects efficiently.",
|
||||
"createNew": "Create New Notebook",
|
||||
"creating": "Creating...",
|
||||
"delete": "Delete Notebook",
|
||||
"deleteConfirm": "Delete",
|
||||
"deleteWarning": "Are you sure you want to delete this notebook? Notes will be moved to General Notes.",
|
||||
"edit": "Edit Notebook",
|
||||
"editDescription": "Change the name, icon, and color of your notebook.",
|
||||
"generating": "Generating summary...",
|
||||
"cancel": "Annuler",
|
||||
"create": "Créer un carnet",
|
||||
"createDescription": "Commencez une nouvelle collection pour organiser vos notes, idées et projets efficacement.",
|
||||
"createNew": "Créer un nouveau carnet",
|
||||
"creating": "Création...",
|
||||
"delete": "Supprimer le carnet",
|
||||
"deleteConfirm": "Supprimer",
|
||||
"deleteWarning": "Êtes-vous sûr de vouloir supprimer ce carnet ? Les notes seront déplacées dans les Notes générales.",
|
||||
"edit": "Modifier le carnet",
|
||||
"editDescription": "Changer le nom, l'icône et la couleur de votre carnet.",
|
||||
"generating": "Génération du résumé...",
|
||||
"labels": "Étiquettes :",
|
||||
"name": "Notebook Name",
|
||||
"name": "Nom du carnet",
|
||||
"noLabels": "Aucune étiquette",
|
||||
"selectColor": "Color",
|
||||
"selectIcon": "Icon",
|
||||
"summary": "Notebook Summary",
|
||||
"summaryDescription": "Generate an AI-powered summary of all notes in this notebook.",
|
||||
"summaryError": "Error generating summary"
|
||||
"selectColor": "Couleur",
|
||||
"selectIcon": "Icône",
|
||||
"summary": "Résumé du carnet",
|
||||
"summaryDescription": "Générer un résumé alimenté par l'IA de toutes les notes de ce carnet.",
|
||||
"summaryError": "Erreur lors de la génération du résumé"
|
||||
},
|
||||
"notebookSuggestion": {
|
||||
"description": "Cette note semble appartenir à ce notebook",
|
||||
"description": "Cette note semble appartenir à ce carnet",
|
||||
"dismiss": "Rejeter",
|
||||
"dismissIn": "Rejeter (ferme dans {timeLeft}s)",
|
||||
"generalNotes": "Notes générales",
|
||||
"move": "Déplacer",
|
||||
"moveToNotebook": "Déplacer vers un notebook",
|
||||
"moveToNotebook": "Déplacer vers un carnet",
|
||||
"title": "Déplacer vers {icon} {name} ?"
|
||||
},
|
||||
"notebooks": {
|
||||
@@ -643,106 +680,106 @@
|
||||
"noNotebooks": "Aucun carnet encore"
|
||||
},
|
||||
"notes": {
|
||||
"add": "Add",
|
||||
"addCollaborators": "Add collaborators",
|
||||
"addImage": "Add image",
|
||||
"addItem": "Add item",
|
||||
"addLink": "Add link",
|
||||
"addListItem": "+ List item",
|
||||
"add": "Ajouter",
|
||||
"addCollaborators": "Ajouter des collaborateurs",
|
||||
"addImage": "Ajouter une image",
|
||||
"addItem": "Ajouter un élément",
|
||||
"addLink": "Ajouter un lien",
|
||||
"addListItem": "+ Élément de liste",
|
||||
"addNote": "Ajouter une note",
|
||||
"adding": "Adding...",
|
||||
"aiAssistant": "AI Assistant",
|
||||
"archive": "Archive",
|
||||
"backgroundOptions": "Background options",
|
||||
"changeColor": "Change color",
|
||||
"changeSize": "Change size",
|
||||
"adding": "Ajout...",
|
||||
"aiAssistant": "Assistant IA",
|
||||
"archive": "Archiver",
|
||||
"backgroundOptions": "Options d'arrière-plan",
|
||||
"changeColor": "Changer la couleur",
|
||||
"changeSize": "Changer la taille",
|
||||
"clarifyFailed": "Échec de la clarification du texte",
|
||||
"close": "Close",
|
||||
"color": "Color",
|
||||
"confirmDelete": "Are you sure you want to delete this note?",
|
||||
"confirmLeaveShare": "Are you sure you want to leave this shared note?",
|
||||
"contentOrMediaRequired": "Please enter some content or add a link/image",
|
||||
"copy": "Copy",
|
||||
"copyFailed": "Failed to copy note",
|
||||
"copySuccess": "Note copied successfully!",
|
||||
"createFirstNote": "Create your first note",
|
||||
"close": "Fermer",
|
||||
"color": "Couleur",
|
||||
"confirmDelete": "Êtes-vous sûr de vouloir supprimer cette note ?",
|
||||
"confirmLeaveShare": "Êtes-vous sûr de vouloir quitter cette note partagée ?",
|
||||
"contentOrMediaRequired": "Veuillez entrer du contenu ou ajouter un lien/image",
|
||||
"copy": "Copier",
|
||||
"copyFailed": "Échec de la copie de la note",
|
||||
"copySuccess": "Note copiée avec succès !",
|
||||
"createFirstNote": "Créez votre première note",
|
||||
"date": "Date",
|
||||
"delete": "Delete",
|
||||
"delete": "Supprimer",
|
||||
"dragToReorder": "Glisser pour réorganiser",
|
||||
"duplicate": "Duplicate",
|
||||
"edit": "Edit Note",
|
||||
"duplicate": "Dupliquer",
|
||||
"edit": "Modifier la note",
|
||||
"emptyState": "Aucune note encore. Créez votre première note !",
|
||||
"fileTooLarge": "File too large: {fileName}. Maximum size is {maxSize}.",
|
||||
"fileTooLarge": "Fichier trop volumineux : {fileName}. Taille maximale : {maxSize}.",
|
||||
"improveFailed": "Échec de l'amélioration du texte",
|
||||
"inNotebook": "Dans le carnet",
|
||||
"invalidDateTime": "Invalid date or time",
|
||||
"invalidFileType": "Invalid file type: {fileName}. Only JPEG, PNG, GIF, and WebP allowed.",
|
||||
"itemOrMediaRequired": "Please add at least one item or media",
|
||||
"large": "Large",
|
||||
"leaveShare": "Leave",
|
||||
"linkAddFailed": "Failed to add link",
|
||||
"linkAdded": "Link added",
|
||||
"linkMetadataFailed": "Could not fetch link metadata",
|
||||
"listItem": "List item",
|
||||
"makeCopy": "Make a copy",
|
||||
"invalidDateTime": "Date ou heure invalide",
|
||||
"invalidFileType": "Type de fichier invalide : {fileName}. Seuls JPEG, PNG, GIF et WebP sont autorisés.",
|
||||
"itemOrMediaRequired": "Veuillez ajouter au moins un élément ou média",
|
||||
"large": "Grande",
|
||||
"leaveShare": "Quitter",
|
||||
"linkAddFailed": "Échec de l'ajout du lien",
|
||||
"linkAdded": "Lien ajouté",
|
||||
"linkMetadataFailed": "Impossible de récupérer les métadonnées du lien",
|
||||
"listItem": "Élément de liste",
|
||||
"makeCopy": "Faire une copie",
|
||||
"markdown": "Markdown",
|
||||
"markdownMode": "Markdown",
|
||||
"markdownOff": "Markdown OFF",
|
||||
"markdownOn": "Markdown ON",
|
||||
"markdownPlaceholder": "Take a note... (Markdown supported)",
|
||||
"medium": "Medium",
|
||||
"markdownOff": "Markdown DÉSACTIVÉ",
|
||||
"markdownOn": "Markdown ACTIVÉ",
|
||||
"markdownPlaceholder": "Prenez une note... (Markdown supporté)",
|
||||
"medium": "Moyenne",
|
||||
"more": "Plus d'options",
|
||||
"moreOptions": "More options",
|
||||
"moreOptions": "Plus d'options",
|
||||
"moveFailed": "Échec du déplacement de la note. Veuillez réessayer.",
|
||||
"newChecklist": "New checklist",
|
||||
"newNote": "New note",
|
||||
"noContent": "No content",
|
||||
"noNotes": "No notes",
|
||||
"noNotesFound": "No notes found",
|
||||
"noteCreateFailed": "Failed to create note",
|
||||
"noteCreated": "Note created successfully",
|
||||
"others": "Others",
|
||||
"pin": "Pin",
|
||||
"pinned": "Pinned",
|
||||
"newChecklist": "Nouvelle checklist",
|
||||
"newNote": "Nouvelle note",
|
||||
"noContent": "Pas de contenu",
|
||||
"noNotes": "Aucune note",
|
||||
"noNotesFound": "Aucune note trouvée",
|
||||
"noteCreateFailed": "Échec de la création de la note",
|
||||
"noteCreated": "Note créée avec succès",
|
||||
"others": "Autres",
|
||||
"pin": "Épingler",
|
||||
"pinned": "Épinglées",
|
||||
"pinnedNotes": "Notes épinglées",
|
||||
"placeholder": "Take a note...",
|
||||
"preview": "Preview",
|
||||
"readOnly": "Read Only",
|
||||
"placeholder": "Prenez une note...",
|
||||
"preview": "Aperçu",
|
||||
"readOnly": "Lecture seule",
|
||||
"recent": "Récent",
|
||||
"redo": "Redo (Ctrl+Y)",
|
||||
"redo": "Rétablir (Ctrl+Y)",
|
||||
"redoShortcut": "Rétablir (Ctrl+Y)",
|
||||
"remindMe": "Remind me",
|
||||
"reminderDateTimeRequired": "Please enter date and time",
|
||||
"reminderMustBeFuture": "Reminder must be in the future",
|
||||
"reminderPastError": "Reminder must be in the future",
|
||||
"reminderRemoved": "Reminder removed",
|
||||
"reminderSet": "Reminder set for {datetime}",
|
||||
"remindMe": "Me rappeler",
|
||||
"reminderDateTimeRequired": "Veuillez entrer la date et l'heure",
|
||||
"reminderMustBeFuture": "Le rappel doit être dans le futur",
|
||||
"reminderPastError": "Le rappel doit être dans le futur",
|
||||
"reminderRemoved": "Rappel supprimé",
|
||||
"reminderSet": "Rappel défini pour {datetime}",
|
||||
"remove": "Supprimer",
|
||||
"saving": "Saving...",
|
||||
"setReminder": "Set reminder",
|
||||
"setReminderButton": "Set Reminder",
|
||||
"share": "Share",
|
||||
"shareWithCollaborators": "Share with collaborators",
|
||||
"sharedBy": "Shared by",
|
||||
"sharedReadOnly": "This note is shared with you in read-only mode",
|
||||
"saving": "Enregistrement...",
|
||||
"setReminder": "Définir un rappel",
|
||||
"setReminderButton": "Définir un rappel",
|
||||
"share": "Partager",
|
||||
"shareWithCollaborators": "Partager avec les collaborateurs",
|
||||
"sharedBy": "Partagé par",
|
||||
"sharedReadOnly": "Cette note est partagée avec vous en lecture seule",
|
||||
"shortenFailed": "Échec du raccourcissement du texte",
|
||||
"showCollaborators": "Show collaborators",
|
||||
"size": "Size",
|
||||
"small": "Small",
|
||||
"takeNote": "Take a note...",
|
||||
"takeNoteMarkdown": "Take a note... (Markdown supported)",
|
||||
"time": "Time",
|
||||
"showCollaborators": "Voir les collaborateurs",
|
||||
"size": "Taille",
|
||||
"small": "Petite",
|
||||
"takeNote": "Prenez une note...",
|
||||
"takeNoteMarkdown": "Prenez une note... (Markdown supporté)",
|
||||
"time": "Heure",
|
||||
"title": "Notes",
|
||||
"titlePlaceholder": "Title",
|
||||
"titlePlaceholder": "Titre",
|
||||
"transformFailed": "Échec de la transformation du texte",
|
||||
"unarchive": "Unarchive",
|
||||
"undo": "Undo (Ctrl+Z)",
|
||||
"unarchive": "Désarchiver",
|
||||
"undo": "Annuler (Ctrl+Z)",
|
||||
"undoShortcut": "Annuler (Ctrl+Z)",
|
||||
"unpin": "Unpin",
|
||||
"unpin": "Désépingler",
|
||||
"unpinned": "Désépinglées",
|
||||
"untitled": "Untitled",
|
||||
"untitled": "Sans titre",
|
||||
"uploadFailed": "Échec du téléchargement",
|
||||
"view": "View Note"
|
||||
"view": "Voir la note"
|
||||
},
|
||||
"pagination": {
|
||||
"next": "→",
|
||||
@@ -842,37 +879,37 @@
|
||||
"searching": "Recherche en cours..."
|
||||
},
|
||||
"settings": {
|
||||
"about": "About",
|
||||
"account": "Account",
|
||||
"appearance": "Appearance",
|
||||
"cleanTags": "Clean Orphan Tags",
|
||||
"cleanTagsDescription": "Remove tags that are no longer used by any notes",
|
||||
"description": "Manage your settings and preferences",
|
||||
"language": "Language",
|
||||
"about": "À propos",
|
||||
"account": "Compte",
|
||||
"appearance": "Apparence",
|
||||
"cleanTags": "Nettoyer les étiquettes orphelines",
|
||||
"cleanTagsDescription": "Supprimer les étiquettes qui ne sont plus utilisées par aucune note",
|
||||
"description": "Gérez vos paramètres et préférences",
|
||||
"language": "Langue",
|
||||
"languageAuto": "Langue définie sur Auto",
|
||||
"maintenance": "Maintenance",
|
||||
"maintenanceDescription": "Tools to maintain your database health",
|
||||
"maintenanceDescription": "Outils pour maintenir la santé de votre base de données",
|
||||
"notifications": "Notifications",
|
||||
"privacy": "Privacy",
|
||||
"privacy": "Confidentialité",
|
||||
"profile": "Profil",
|
||||
"searchNoResults": "Aucun paramètre trouvé",
|
||||
"security": "Security",
|
||||
"selectLanguage": "Select language",
|
||||
"semanticIndexing": "Semantic Indexing",
|
||||
"semanticIndexingDescription": "Generate vectors for all notes to enable intent-based search",
|
||||
"settingsError": "Error saving settings",
|
||||
"settingsSaved": "Settings saved",
|
||||
"theme": "Theme",
|
||||
"themeDark": "Dark",
|
||||
"themeLight": "Light",
|
||||
"themeSystem": "System",
|
||||
"title": "Settings",
|
||||
"security": "Sécurité",
|
||||
"selectLanguage": "Sélectionner une langue",
|
||||
"semanticIndexing": "Indexation sémantique",
|
||||
"semanticIndexingDescription": "Générer des vecteurs pour toutes les notes afin de permettre la recherche par intention",
|
||||
"settingsError": "Erreur lors de la sauvegarde des paramètres",
|
||||
"settingsSaved": "Paramètres enregistrés",
|
||||
"theme": "Thème",
|
||||
"themeDark": "Sombre",
|
||||
"themeLight": "Clair",
|
||||
"themeSystem": "Système",
|
||||
"title": "Paramètres",
|
||||
"version": "Version"
|
||||
},
|
||||
"sidebar": {
|
||||
"archive": "Archives",
|
||||
"editLabels": "Modifier les libellés",
|
||||
"labels": "Libellés",
|
||||
"editLabels": "Modifier les étiquettes",
|
||||
"labels": "Étiquettes",
|
||||
"notes": "Notes",
|
||||
"reminders": "Rappels",
|
||||
"trash": "Corbeille"
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "टैग जनरेशन प्रदाता",
|
||||
"title": "AI कॉन्फ़िगरेशन",
|
||||
"updateFailed": "AI सेटिंग्स अपडेट करने में विफल",
|
||||
"updateSuccess": "AI सेटिंग्स सफलतापूर्वक अपडेट की गईं"
|
||||
"updateSuccess": "AI सेटिंग्स सफलतापूर्वक अपडेट की गईं",
|
||||
"bestValue": "सर्वोत्तम मूल्य",
|
||||
"bestQuality": "सर्वोत्तम गुणवत्ता",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(सहेजा गया)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "टैग जनरेशन और सिमेंटिक खोज एम्बेडिंग्स के लिए अपने AI प्रदाताओं का परीक्षण करें",
|
||||
@@ -153,34 +159,36 @@
|
||||
"analyzing": "AI विश्लेषण जारी है...",
|
||||
"assistant": "AI सहायक",
|
||||
"autoLabels": {
|
||||
"analyzing": "लेबल सुझावों के लिए आपके नोट्स का विश्लेषण किया जा रहा है...",
|
||||
"analyzing": "आपके नोट्स का विश्लेषण हो रहा है...",
|
||||
"create": "बनाएं",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"created": "{count} labels created successfully",
|
||||
"createNewLabel": "यह नया लेबल बनाएं और जोड़ें",
|
||||
"created": "{count} लेबल सफलतापूर्वक बनाए गए",
|
||||
"creating": "लेबल बनाए जा रहे हैं...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"description": "मैंने \"{notebookName}\" ({totalNotes} नोट्स) में बार-बार आने वाले विषयों का पता लगाया। उनके लिए लेबल बनाएं?",
|
||||
"error": "लेबल सुझाव प्राप्त करने में विफल",
|
||||
"new": "(नया)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "लेबल सुझाव",
|
||||
"typeContent": "Type content to get label suggestions..."
|
||||
"noLabelsSelected": "कोई लेबल चयनित नहीं",
|
||||
"note": "नोट",
|
||||
"notes": "नोट्स",
|
||||
"title": "नए लेबल सुझाव",
|
||||
"typeContent": "लेबल सुझाव प्राप्त करने के लिए सामग्री लिखें...",
|
||||
"notesCount": "{count} नोट्स",
|
||||
"typeForSuggestions": "लेबल सुझाव प्राप्त करने के लिए सामग्री लिखें..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Failed to apply organization plan",
|
||||
"applying": "Applying...",
|
||||
"description": "AI आपके नोट्स का विश्लेषण करेगा और उन्हें नोटबुक में व्यवस्थित करने का सुझाव देगा।",
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Select all notes in {notebook}",
|
||||
"selectNote": "Select note: {title}",
|
||||
"success": "{count} notes moved successfully",
|
||||
"title": "Organize with AI"
|
||||
"analyzing": "आपके नोट्स का विश्लेषण हो रहा है...",
|
||||
"apply": "लागू करें",
|
||||
"applyFailed": "संगठन योजना लागू करने में विफल",
|
||||
"applying": "लागू हो रहा है...",
|
||||
"description": "AI आपके नोट्स का विश्लेषण करेगा और नोटबुक में व्यवस्थित करने का सुझाव देगा।",
|
||||
"error": "संगठन योजना बनाने में विफल",
|
||||
"noNotebooks": "कोई नोटबुक उपलब्ध नहीं। पहले नोटबुक बनाएं।",
|
||||
"noNotesSelected": "कोई नोट चयनित नहीं",
|
||||
"noSuggestions": "AI इन नोट्स को व्यवस्थित करने का अच्छा तरीका नहीं ढूंढ सका।",
|
||||
"selectAllIn": "{notebook} में सभी नोट्स चुनें",
|
||||
"selectNote": "नोट चुनें: {title}",
|
||||
"success": "{count} नोट्स सफलतापूर्वक स्थानांतरित",
|
||||
"title": "AI से व्यवस्थित करें"
|
||||
},
|
||||
"clarify": "स्पष्ट करें",
|
||||
"clickToAddTag": "इस टैग को जोड़ने के लिए क्लिक करें",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "Tags Generation Provider",
|
||||
"title": "AI Configuration",
|
||||
"updateFailed": "Failed to update AI settings",
|
||||
"updateSuccess": "AI Settings updated successfully"
|
||||
"updateSuccess": "AI Settings updated successfully",
|
||||
"bestValue": "Miglior rapporto qualità/prezzo",
|
||||
"bestQuality": "Miglior qualità",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(Salvato)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Test your AI providers for tag generation and semantic search embeddings",
|
||||
@@ -156,9 +162,9 @@
|
||||
"analyzing": "Analisi delle tue note per suggerimenti etichette...",
|
||||
"create": "Crea",
|
||||
"createNewLabel": "Crea nuova etichetta",
|
||||
"created": "{count} labels created successfully",
|
||||
"created": "{count} etichette create con successo",
|
||||
"creating": "Creazione etichette...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"description": "Ho rilevato temi ricorrenti in \"{notebookName}\" ({totalNotes} note). Creare etichette per essi?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(nuovo)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
@@ -580,14 +586,14 @@
|
||||
"memoryEcho": {
|
||||
"clickToView": "Clicca per visualizzare",
|
||||
"comparison": {
|
||||
"clickToView": "Click to view note",
|
||||
"helpful": "Helpful",
|
||||
"helpfulQuestion": "Is this comparison helpful?",
|
||||
"highSimilarityInsight": "These notes deal with the same topic with a high degree of similarity. They could be merged or consolidated.",
|
||||
"notHelpful": "Not Helpful",
|
||||
"similarityInfo": "These notes are connected by {similarity}% similarity",
|
||||
"title": "💡 Note Comparison",
|
||||
"untitled": "Untitled"
|
||||
"title": "💡 Confronto note",
|
||||
"similarityInfo": "Queste note sono collegate da {similarity}% di similarità",
|
||||
"highSimilarityInsight": "Queste note trattano lo stesso argomento con un alto grado di similarità. Potrebbero essere fuse o consolidate.",
|
||||
"untitled": "Senza titolo",
|
||||
"clickToView": "Clicca per visualizzare la nota",
|
||||
"helpfulQuestion": "Questo confronto è utile?",
|
||||
"helpful": "Utile",
|
||||
"notHelpful": "Non utile"
|
||||
},
|
||||
"connection": "connection",
|
||||
"connections": "Connections",
|
||||
@@ -596,40 +602,40 @@
|
||||
"description": "Proactive connections between your notes",
|
||||
"dismiss": "Dismiss for now",
|
||||
"editorSection": {
|
||||
"close": "Chiudi",
|
||||
"compare": "Compare",
|
||||
"compareAll": "Compare all",
|
||||
"loading": "Loading...",
|
||||
"merge": "Merge",
|
||||
"mergeAll": "Merge all",
|
||||
"title": "⚡ Connected Notes ({count})",
|
||||
"view": "View"
|
||||
"title": "⚡ Note connesse ({count})",
|
||||
"loading": "Caricamento...",
|
||||
"view": "Visualizza",
|
||||
"compare": "Confronta",
|
||||
"merge": "Unisci",
|
||||
"compareAll": "Confronta tutto",
|
||||
"mergeAll": "Unisci tutto",
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"fused": "Fused",
|
||||
"fused": "Fuso",
|
||||
"fusion": {
|
||||
"archiveOriginals": "Archive original notes",
|
||||
"cancel": "Cancel",
|
||||
"confirmFusion": "Confirm fusion",
|
||||
"createBacklinks": "Create backlink to original notes",
|
||||
"edit": "Edit",
|
||||
"error": "Failed to merge notes",
|
||||
"finishEditing": "Finish editing",
|
||||
"generateError": "Failed to generate fusion",
|
||||
"generateFusion": "Generate the fusion",
|
||||
"generating": "Generating...",
|
||||
"keepAllTags": "Keep all tags",
|
||||
"mergeNotes": "Merge {count} note(s)",
|
||||
"modify": "Modify",
|
||||
"noContentReturned": "No fusion content returned from API",
|
||||
"notesToMerge": "📝 Notes to merge",
|
||||
"optionalPrompt": "💬 Fusion prompt (optional)",
|
||||
"optionsTitle": "Fusion options",
|
||||
"previewTitle": "📝 Preview of merged note",
|
||||
"promptPlaceholder": "Optional instructions for AI (e.g., 'Keep the formal style of note 1')...",
|
||||
"success": "Notes merged successfully!",
|
||||
"title": "🔗 Intelligent Fusion",
|
||||
"unknownDate": "Unknown date",
|
||||
"useLatestTitle": "Use latest note as title"
|
||||
"title": "🔗 Fusione intelligente",
|
||||
"mergeNotes": "Unisci {count} nota/e",
|
||||
"notesToMerge": "📝 Note da unire",
|
||||
"optionalPrompt": "💬 Prompt di fusione (opzionale)",
|
||||
"promptPlaceholder": "Istruzioni opzionali per l'IA (es. 'Mantieni lo stile formale della nota 1')...",
|
||||
"generateFusion": "Genera la fusione",
|
||||
"generating": "Generazione...",
|
||||
"previewTitle": "📝 Anteprima della nota unita",
|
||||
"edit": "Modifica",
|
||||
"modify": "Modifica",
|
||||
"finishEditing": "Termina modifica",
|
||||
"optionsTitle": "Opzioni di fusione",
|
||||
"archiveOriginals": "Archivia note originali",
|
||||
"keepAllTags": "Mantieni tutti i tag",
|
||||
"useLatestTitle": "Usa la nota più recente come titolo",
|
||||
"createBacklinks": "Crea collegamento alle note originali",
|
||||
"cancel": "Annulla",
|
||||
"confirmFusion": "Conferma fusione",
|
||||
"success": "Note unite con successo!",
|
||||
"error": "Impossibile unire le note",
|
||||
"generateError": "Impossibile generare la fusione",
|
||||
"noContentReturned": "Nessun contenuto di fusione restituito dall'API",
|
||||
"unknownDate": "Data sconosciuta"
|
||||
},
|
||||
"helpful": "Helpful",
|
||||
"insightReady": "Your insight is ready!",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "タグ生成プロバイダー",
|
||||
"title": "AI設定",
|
||||
"updateFailed": "AI設定の更新に失敗しました",
|
||||
"updateSuccess": "AI設定が正常に更新されました"
|
||||
"updateSuccess": "AI設定が正常に更新されました",
|
||||
"bestValue": "最もコスパが良い",
|
||||
"bestQuality": "最高品質",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(保存済み)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "タグ生成とセマンティック検索埋め込みのAIプロバイダーをテストします",
|
||||
@@ -153,34 +159,36 @@
|
||||
"analyzing": "AI分析中...",
|
||||
"assistant": "AIアシスタント",
|
||||
"autoLabels": {
|
||||
"analyzing": "ラベル提案のためにノートを分析中...",
|
||||
"analyzing": "ノートを分析中...",
|
||||
"create": "作成",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"created": "{count} labels created successfully",
|
||||
"createNewLabel": "この新しいラベルを作成して追加",
|
||||
"created": "{count} 個のラベルを作成しました",
|
||||
"creating": "ラベルを作成中...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(新規)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "ラベルの提案",
|
||||
"typeContent": "Type content to get label suggestions..."
|
||||
"description": "\"{notebookName}\"({totalNotes} 件のノート)で繰り返し出てくるテーマを検出しました。ラベルを作成しますか?",
|
||||
"error": "ラベル候補の取得に失敗しました",
|
||||
"new": "(新規)",
|
||||
"noLabelsSelected": "ラベルが選択されていません",
|
||||
"note": "件",
|
||||
"notes": "件",
|
||||
"title": "新しいラベル候補",
|
||||
"typeContent": "ラベル候補を取得するにはコンテンツを入力...",
|
||||
"notesCount": "{count} 件",
|
||||
"typeForSuggestions": "ラベル候補を取得するにはコンテンツを入力..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Failed to apply organization plan",
|
||||
"applying": "Applying...",
|
||||
"description": "AIがノートを分析し、ノートブックに整理することを提案します。",
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Select all notes in {notebook}",
|
||||
"selectNote": "Select note: {title}",
|
||||
"success": "{count} notes moved successfully",
|
||||
"title": "Organize with AI"
|
||||
"analyzing": "ノートを分析中...",
|
||||
"apply": "適用",
|
||||
"applyFailed": "整理プランの適用に失敗しました",
|
||||
"applying": "適用中...",
|
||||
"description": "AI がノートを分析し、ノートブックに整理する方法を提案します。",
|
||||
"error": "整理プランの作成に失敗しました",
|
||||
"noNotebooks": "利用可能なノートブックがありません。先にノートブックを作成してください。",
|
||||
"noNotesSelected": "ノートが選択されていません",
|
||||
"noSuggestions": "AI はこれらのノートを整理する良い方法を見つけられませんでした。",
|
||||
"selectAllIn": "{notebook} のすべてのノートを選択",
|
||||
"selectNote": "ノートを選択:{title}",
|
||||
"success": "{count} 件のノートを移動しました",
|
||||
"title": "AI で整理"
|
||||
},
|
||||
"clarify": "明確にする",
|
||||
"clickToAddTag": "クリックしてこのタグを追加",
|
||||
@@ -192,8 +200,8 @@
|
||||
"improveStyle": "スタイルを改善",
|
||||
"languageDetected": "検出された言語",
|
||||
"notebookSummary": {
|
||||
"regenerate": "概要を再生成",
|
||||
"regenerating": "概要を再生成中..."
|
||||
"regenerate": "要約を再生成",
|
||||
"regenerating": "要約を再生成中..."
|
||||
},
|
||||
"original": "元のテキスト",
|
||||
"poweredByAI": "AI powered",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "태그 생성 공급자",
|
||||
"title": "AI 구성",
|
||||
"updateFailed": "AI 설정 업데이트 실패",
|
||||
"updateSuccess": "AI 설정이 성공적으로 업데이트되었습니다"
|
||||
"updateSuccess": "AI 설정이 성공적으로 업데이트되었습니다",
|
||||
"bestValue": "최고 가성비",
|
||||
"bestQuality": "최고 품질",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(저장됨)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "태그 생성 및 의미 검색 임베딩을 위한 AI 공급자 테스트",
|
||||
@@ -153,34 +159,36 @@
|
||||
"analyzing": "AI 분석 중...",
|
||||
"assistant": "AI 도우미",
|
||||
"autoLabels": {
|
||||
"analyzing": "라벨 제안을 위해 메모 분석 중...",
|
||||
"create": "생성",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"created": "{count} labels created successfully",
|
||||
"creating": "라벨 생성 중...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(새로운)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "라벨 제안",
|
||||
"typeContent": "Type content to get label suggestions..."
|
||||
"error": "라벨 제안을 가져오지 못했습니다",
|
||||
"noLabelsSelected": "선택된 라벨이 없습니다",
|
||||
"created": "{count}개의 라벨이 생성되었습니다",
|
||||
"analyzing": "노트를 분석 중...",
|
||||
"title": "새 라벨 제안",
|
||||
"description": "\"{notebookName}\"({totalNotes}개의 노트)에서 반복되는 테마를 감지했습니다. 라벨을 만들까요?",
|
||||
"note": "개",
|
||||
"notes": "개",
|
||||
"typeContent": "라벨 제안을 받으려면 내용을 입력하세요...",
|
||||
"createNewLabel": "이 새 라벨을 만들고 추가",
|
||||
"new": "(새)",
|
||||
"create": "만들기",
|
||||
"creating": "라벨 만드는 중...",
|
||||
"notesCount": "{count}개",
|
||||
"typeForSuggestions": "라벨 제안을 받으려면 내용을 입력하세요..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Failed to apply organization plan",
|
||||
"applying": "Applying...",
|
||||
"description": "AI가 메모를 분석하여 노트북으로 정리할 것을 제안합니다.",
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Select all notes in {notebook}",
|
||||
"selectNote": "Select note: {title}",
|
||||
"success": "{count} notes moved successfully",
|
||||
"title": "Organize with AI"
|
||||
"title": "AI로 정리",
|
||||
"description": "AI가 노트를 분석하고 노트북으로 정리할 방법을 제안합니다.",
|
||||
"analyzing": "노트를 분석 중...",
|
||||
"noNotebooks": "사용 가능한 노트북이 없습니다. 먼저 노트북을 만드세요.",
|
||||
"noSuggestions": "AI가 이 노트들을 정리할 좋은 방법을 찾지 못했습니다.",
|
||||
"apply": "적용",
|
||||
"applying": "적용 중...",
|
||||
"success": "{count}개의 노트를 성공적으로 이동했습니다",
|
||||
"error": "정리 계획 생성 실패",
|
||||
"noNotesSelected": "선택된 노트가 없습니다",
|
||||
"applyFailed": "정리 계획 적용 실패",
|
||||
"selectAllIn": "{notebook}의 모든 노트 선택",
|
||||
"selectNote": "노트 선택: {title}"
|
||||
},
|
||||
"clarify": "명확히 하기",
|
||||
"clickToAddTag": "클릭하여 이 태그 추가",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "Tags Generation Provider",
|
||||
"title": "AI Configuration",
|
||||
"updateFailed": "Failed to update AI settings",
|
||||
"updateSuccess": "AI Settings updated successfully"
|
||||
"updateSuccess": "AI Settings updated successfully",
|
||||
"bestValue": "Beste prijs-kwaliteit",
|
||||
"bestQuality": "Beste kwaliteit",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(Opgeslagen)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Test your AI providers for tag generation and semantic search embeddings",
|
||||
@@ -156,9 +162,9 @@
|
||||
"analyzing": "Uw notities analyseren voor labelsuggesties...",
|
||||
"create": "Maken",
|
||||
"createNewLabel": "Nieuw label maken",
|
||||
"created": "{count} labels created successfully",
|
||||
"created": "{count} labels succesvol aangemaakt",
|
||||
"creating": "Labels maken...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"description": "Ik heb terugkerende themas gedetecteerd in \"{notebookName}\" ({totalNotes} notities). Labels hiervoor maken?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(nieuw)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
@@ -580,14 +586,14 @@
|
||||
"memoryEcho": {
|
||||
"clickToView": "Klik om te bekijken",
|
||||
"comparison": {
|
||||
"clickToView": "Click to view note",
|
||||
"helpful": "Helpful",
|
||||
"helpfulQuestion": "Is this comparison helpful?",
|
||||
"highSimilarityInsight": "These notes deal with the same topic with a high degree of similarity. They could be merged or consolidated.",
|
||||
"notHelpful": "Not Helpful",
|
||||
"similarityInfo": "These notes are connected by {similarity}% similarity",
|
||||
"title": "💡 Note Comparison",
|
||||
"untitled": "Untitled"
|
||||
"title": "💡 Notitie vergelijking",
|
||||
"similarityInfo": "Deze notities zijn verbonden door {similarity}% overeenkomst",
|
||||
"highSimilarityInsight": "Deze notities gaan over hetzelfde onderwerp met een hoge mate van overeenkomst. Ze kunnen worden samengevoegd.",
|
||||
"untitled": "Naamloos",
|
||||
"clickToView": "Klik om notitie te bekijken",
|
||||
"helpfulQuestion": "Is deze vergelijking nuttig?",
|
||||
"helpful": "Nuttig",
|
||||
"notHelpful": "Niet nuttig"
|
||||
},
|
||||
"connection": "connection",
|
||||
"connections": "Connections",
|
||||
@@ -596,40 +602,40 @@
|
||||
"description": "Proactive connections between your notes",
|
||||
"dismiss": "Dismiss for now",
|
||||
"editorSection": {
|
||||
"close": "Sluiten",
|
||||
"compare": "Compare",
|
||||
"compareAll": "Compare all",
|
||||
"loading": "Loading...",
|
||||
"merge": "Merge",
|
||||
"mergeAll": "Merge all",
|
||||
"title": "⚡ Connected Notes ({count})",
|
||||
"view": "View"
|
||||
"title": "⚡ Verbinde notities ({count})",
|
||||
"loading": "Laden...",
|
||||
"view": "Bekijken",
|
||||
"compare": "Vergelijken",
|
||||
"merge": "Samenvoegen",
|
||||
"compareAll": "Alles vergelijken",
|
||||
"mergeAll": "Alles samenvoegen",
|
||||
"close": "Sluiten"
|
||||
},
|
||||
"fused": "Fused",
|
||||
"fused": "Samengevoegd",
|
||||
"fusion": {
|
||||
"archiveOriginals": "Archive original notes",
|
||||
"cancel": "Cancel",
|
||||
"confirmFusion": "Confirm fusion",
|
||||
"createBacklinks": "Create backlink to original notes",
|
||||
"edit": "Edit",
|
||||
"error": "Failed to merge notes",
|
||||
"finishEditing": "Finish editing",
|
||||
"generateError": "Failed to generate fusion",
|
||||
"generateFusion": "Generate the fusion",
|
||||
"generating": "Generating...",
|
||||
"keepAllTags": "Keep all tags",
|
||||
"mergeNotes": "Merge {count} note(s)",
|
||||
"modify": "Modify",
|
||||
"noContentReturned": "No fusion content returned from API",
|
||||
"notesToMerge": "📝 Notes to merge",
|
||||
"optionalPrompt": "💬 Fusion prompt (optional)",
|
||||
"optionsTitle": "Fusion options",
|
||||
"previewTitle": "📝 Preview of merged note",
|
||||
"promptPlaceholder": "Optional instructions for AI (e.g., 'Keep the formal style of note 1')...",
|
||||
"success": "Notes merged successfully!",
|
||||
"title": "🔗 Intelligent Fusion",
|
||||
"unknownDate": "Unknown date",
|
||||
"useLatestTitle": "Use latest note as title"
|
||||
"title": "🔗 Intelligente fusie",
|
||||
"mergeNotes": "Voeg {count} notitie(s) samen",
|
||||
"notesToMerge": "📝 Te samenvoegen notities",
|
||||
"optionalPrompt": "💬 Fusie prompt (optioneel)",
|
||||
"promptPlaceholder": "Optionele instructies voor AI (bijv. 'Behoud de formele stijl van notitie 1')...",
|
||||
"generateFusion": "Genereer fusie",
|
||||
"generating": "Genereren...",
|
||||
"previewTitle": "📝 Voorbeeld van samengevoegde notitie",
|
||||
"edit": "Bewerken",
|
||||
"modify": "Wijzigen",
|
||||
"finishEditing": "Bewerken voltooid",
|
||||
"optionsTitle": "Fusie-opties",
|
||||
"archiveOriginals": "Archiveer originele notities",
|
||||
"keepAllTags": "Bewaar alle tags",
|
||||
"useLatestTitle": "Gebruik meest recente notitie als titel",
|
||||
"createBacklinks": "Maak terugverwijzing naar originele notities",
|
||||
"cancel": "Annuleren",
|
||||
"confirmFusion": "Bevestig fusie",
|
||||
"success": "Notities succesvol samengevoegd!",
|
||||
"error": "Kan notities niet samenvoegen",
|
||||
"generateError": "Kan fusie niet genereren",
|
||||
"noContentReturned": "Geen fusie-inhoud ontvangen van API",
|
||||
"unknownDate": "Onbekende datum"
|
||||
},
|
||||
"helpful": "Helpful",
|
||||
"insightReady": "Your insight is ready!",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "Tags Generation Provider",
|
||||
"title": "AI Configuration",
|
||||
"updateFailed": "Failed to update AI settings",
|
||||
"updateSuccess": "AI Settings updated successfully"
|
||||
"updateSuccess": "AI Settings updated successfully",
|
||||
"bestValue": "Najlepszy stosunek jakości do ceny",
|
||||
"bestQuality": "Najwyższa jakość",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(Zapisano)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Test your AI providers for tag generation and semantic search embeddings",
|
||||
@@ -156,9 +162,9 @@
|
||||
"analyzing": "Analizowanie Twoich notatek pod kątem sugestii etykiet...",
|
||||
"create": "Utwórz",
|
||||
"createNewLabel": "Utwórz nową etykietę",
|
||||
"created": "{count} labels created successfully",
|
||||
"created": "{count} etykiet utworzono pomyślnie",
|
||||
"creating": "Tworzenie etykiet...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"description": "Wykryłem powtarzające się tematy w \"{notebookName}\" ({totalNotes} notatkach). Utworzyć dla nich etykiety?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(nowa)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
@@ -602,14 +608,14 @@
|
||||
"memoryEcho": {
|
||||
"clickToView": "Kliknij, aby wyświetlić",
|
||||
"comparison": {
|
||||
"clickToView": "Click to view note",
|
||||
"helpful": "Helpful",
|
||||
"helpfulQuestion": "Is this comparison helpful?",
|
||||
"highSimilarityInsight": "These notes deal with the same topic with a high degree of similarity. They could be merged or consolidated.",
|
||||
"notHelpful": "Not Helpful",
|
||||
"similarityInfo": "These notes are connected by {similarity}% similarity",
|
||||
"title": "💡 Note Comparison",
|
||||
"untitled": "Untitled"
|
||||
"title": "💡 Porównanie notatek",
|
||||
"similarityInfo": "Te notatki są połączone przez {similarity}% podobieństwa",
|
||||
"highSimilarityInsight": "Te notatki dotyczą tego samego tematu z wysokim stopniem podobieństwa. Mogą zostać połączone.",
|
||||
"untitled": "Bez tytułu",
|
||||
"clickToView": "Kliknij aby wyświetlić notatkę",
|
||||
"helpfulQuestion": "Czy to porównanie jest pomocne?",
|
||||
"helpful": "Pomocne",
|
||||
"notHelpful": "Nie pomocne"
|
||||
},
|
||||
"connection": "connection",
|
||||
"connections": "Connections",
|
||||
@@ -618,40 +624,40 @@
|
||||
"description": "Proactive connections between your notes",
|
||||
"dismiss": "Dismiss for now",
|
||||
"editorSection": {
|
||||
"close": "Zamknij",
|
||||
"compare": "Compare",
|
||||
"compareAll": "Compare all",
|
||||
"loading": "Loading...",
|
||||
"merge": "Merge",
|
||||
"mergeAll": "Merge all",
|
||||
"title": "⚡ Connected Notes ({count})",
|
||||
"view": "View"
|
||||
"title": "⚡ Połączone notatki ({count})",
|
||||
"loading": "Ładowanie...",
|
||||
"view": "Wyświetl",
|
||||
"compare": "Porównaj",
|
||||
"merge": "Połącz",
|
||||
"compareAll": "Porównaj wszystko",
|
||||
"mergeAll": "Połącz wszystko",
|
||||
"close": "Zamknij"
|
||||
},
|
||||
"fused": "Fused",
|
||||
"fused": "Połączono",
|
||||
"fusion": {
|
||||
"archiveOriginals": "Archive original notes",
|
||||
"cancel": "Cancel",
|
||||
"confirmFusion": "Confirm fusion",
|
||||
"createBacklinks": "Create backlink to original notes",
|
||||
"edit": "Edit",
|
||||
"error": "Failed to merge notes",
|
||||
"finishEditing": "Finish editing",
|
||||
"generateError": "Failed to generate fusion",
|
||||
"generateFusion": "Generate the fusion",
|
||||
"generating": "Generating...",
|
||||
"keepAllTags": "Keep all tags",
|
||||
"mergeNotes": "Merge {count} note(s)",
|
||||
"modify": "Modify",
|
||||
"noContentReturned": "No fusion content returned from API",
|
||||
"notesToMerge": "📝 Notes to merge",
|
||||
"optionalPrompt": "💬 Fusion prompt (optional)",
|
||||
"optionsTitle": "Fusion options",
|
||||
"previewTitle": "📝 Preview of merged note",
|
||||
"promptPlaceholder": "Optional instructions for AI (e.g., 'Keep the formal style of note 1')...",
|
||||
"success": "Notes merged successfully!",
|
||||
"title": "🔗 Intelligent Fusion",
|
||||
"unknownDate": "Unknown date",
|
||||
"useLatestTitle": "Use latest note as title"
|
||||
"title": "🔗 Inteligentne fuzja",
|
||||
"mergeNotes": "Połącz {count} notatkę/i",
|
||||
"notesToMerge": "📝 Notatki do połączenia",
|
||||
"optionalPrompt": "💬 Prompt fuzji (opcjonalny)",
|
||||
"promptPlaceholder": "Opcjonalne instrukcje dla AI (np. 'Zachowaj formalny styl notatki 1')...",
|
||||
"generateFusion": "Wygeneruj fuzję",
|
||||
"generating": "Generowanie...",
|
||||
"previewTitle": "📝 Podgląd połączonej notatki",
|
||||
"edit": "Edytuj",
|
||||
"modify": "Modyfikuj",
|
||||
"finishEditing": "Zakończ edycję",
|
||||
"optionsTitle": "Opcje fuzji",
|
||||
"archiveOriginals": "Archiwizuj oryginalne notatki",
|
||||
"keepAllTags": "Zachowaj wszystkie tagi",
|
||||
"useLatestTitle": "Użyj najnowszej notatki jako tytułu",
|
||||
"createBacklinks": "Utwórz link do oryginalnych notatek",
|
||||
"cancel": "Anuluj",
|
||||
"confirmFusion": "Potwierdź fuzję",
|
||||
"success": "Notatki połączone pomyślnie!",
|
||||
"error": "Nie udało się połączyć notatek",
|
||||
"generateError": "Nie udało się wygenerować fuzji",
|
||||
"noContentReturned": "Brak zawartości fuzji z API",
|
||||
"unknownDate": "Nieznana data"
|
||||
},
|
||||
"helpful": "Helpful",
|
||||
"insightReady": "Your insight is ready!",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "Tags Generation Provider",
|
||||
"title": "AI Configuration",
|
||||
"updateFailed": "Failed to update AI settings",
|
||||
"updateSuccess": "AI Settings updated successfully"
|
||||
"updateSuccess": "AI Settings updated successfully",
|
||||
"bestValue": "Melhor custo-benefício",
|
||||
"bestQuality": "Melhor qualidade",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(Salvo)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Test your AI providers for tag generation and semantic search embeddings",
|
||||
@@ -156,9 +162,9 @@
|
||||
"analyzing": "Analisando suas notas para sugestões de rótulos...",
|
||||
"create": "Criar",
|
||||
"createNewLabel": "Criar nova etiqueta",
|
||||
"created": "{count} labels created successfully",
|
||||
"created": "{count} etiquetas criadas com sucesso",
|
||||
"creating": "Criando rótulos...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"description": "Detectei temas recorrentes em \"{notebookName}\" ({totalNotes} notas). Criar etiquetas para eles?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(novo)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
@@ -530,14 +536,14 @@
|
||||
"memoryEcho": {
|
||||
"clickToView": "Clique para visualizar",
|
||||
"comparison": {
|
||||
"clickToView": "Click to view note",
|
||||
"helpful": "Helpful",
|
||||
"helpfulQuestion": "Is this comparison helpful?",
|
||||
"highSimilarityInsight": "These notes deal with the same topic with a high degree of similarity. They could be merged or consolidated.",
|
||||
"notHelpful": "Not Helpful",
|
||||
"similarityInfo": "These notes are connected by {similarity}% similarity",
|
||||
"title": "💡 Note Comparison",
|
||||
"untitled": "Untitled"
|
||||
"title": "💡 Comparação de notas",
|
||||
"similarityInfo": "Estas notas estão conectadas por {similarity}% de similaridade",
|
||||
"highSimilarityInsight": "Estas notas tratam do mesmo tema com alto grau de similaridade. Podem ser mescladas.",
|
||||
"untitled": "Sem título",
|
||||
"clickToView": "Clique para ver a nota",
|
||||
"helpfulQuestion": "Esta comparação é útil?",
|
||||
"helpful": "Útil",
|
||||
"notHelpful": "Não útil"
|
||||
},
|
||||
"connection": "connection",
|
||||
"connections": "Connections",
|
||||
@@ -546,40 +552,40 @@
|
||||
"description": "Proactive connections between your notes",
|
||||
"dismiss": "Dismiss for now",
|
||||
"editorSection": {
|
||||
"close": "Fechar",
|
||||
"compare": "Compare",
|
||||
"compareAll": "Compare all",
|
||||
"loading": "Loading...",
|
||||
"merge": "Merge",
|
||||
"mergeAll": "Merge all",
|
||||
"title": "⚡ Connected Notes ({count})",
|
||||
"view": "View"
|
||||
"title": "⚡ Notas conectadas ({count})",
|
||||
"loading": "Carregando...",
|
||||
"view": "Visualizar",
|
||||
"compare": "Comparar",
|
||||
"merge": "Mesclar",
|
||||
"compareAll": "Comparar tudo",
|
||||
"mergeAll": "Mesclar tudo",
|
||||
"close": "Fechar"
|
||||
},
|
||||
"fused": "Fused",
|
||||
"fused": "Mesclado",
|
||||
"fusion": {
|
||||
"archiveOriginals": "Archive original notes",
|
||||
"cancel": "Cancel",
|
||||
"confirmFusion": "Confirm fusion",
|
||||
"createBacklinks": "Create backlink to original notes",
|
||||
"edit": "Edit",
|
||||
"error": "Failed to merge notes",
|
||||
"finishEditing": "Finish editing",
|
||||
"generateError": "Failed to generate fusion",
|
||||
"generateFusion": "Generate the fusion",
|
||||
"generating": "Generating...",
|
||||
"keepAllTags": "Keep all tags",
|
||||
"mergeNotes": "Merge {count} note(s)",
|
||||
"modify": "Modify",
|
||||
"noContentReturned": "No fusion content returned from API",
|
||||
"notesToMerge": "📝 Notes to merge",
|
||||
"optionalPrompt": "💬 Fusion prompt (optional)",
|
||||
"optionsTitle": "Fusion options",
|
||||
"previewTitle": "📝 Preview of merged note",
|
||||
"promptPlaceholder": "Optional instructions for AI (e.g., 'Keep the formal style of note 1')...",
|
||||
"success": "Notes merged successfully!",
|
||||
"title": "🔗 Intelligent Fusion",
|
||||
"unknownDate": "Unknown date",
|
||||
"useLatestTitle": "Use latest note as title"
|
||||
"title": "🔗 Fusão inteligente",
|
||||
"mergeNotes": "Mesclar {count} nota(s)",
|
||||
"notesToMerge": "📝 Notas para mesclar",
|
||||
"optionalPrompt": "💬 Prompt de fusão (opcional)",
|
||||
"promptPlaceholder": "Instruções opcionais para IA (ex: 'Manter o estilo formal da nota 1')...",
|
||||
"generateFusion": "Gerar fusão",
|
||||
"generating": "Gerando...",
|
||||
"previewTitle": "📝 Prévia da nota mesclada",
|
||||
"edit": "Editar",
|
||||
"modify": "Modificar",
|
||||
"finishEditing": "Concluir edição",
|
||||
"optionsTitle": "Opções de fusão",
|
||||
"archiveOriginals": "Arquivar notas originais",
|
||||
"keepAllTags": "Manter todas as tags",
|
||||
"useLatestTitle": "Usar nota mais recente como título",
|
||||
"createBacklinks": "Criar link para notas originais",
|
||||
"cancel": "Cancelar",
|
||||
"confirmFusion": "Confirmar fusão",
|
||||
"success": "Notas mescladas com sucesso!",
|
||||
"error": "Falha ao mesclar notas",
|
||||
"generateError": "Falha ao gerar fusão",
|
||||
"noContentReturned": "Nenhum conteúdo de fusão retornado pela API",
|
||||
"unknownDate": "Data desconhecida"
|
||||
},
|
||||
"helpful": "Helpful",
|
||||
"insightReady": "Your insight is ready!",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "Tags Generation Provider",
|
||||
"title": "AI Configuration",
|
||||
"updateFailed": "Failed to update AI settings",
|
||||
"updateSuccess": "AI Settings updated successfully"
|
||||
"updateSuccess": "AI Settings updated successfully",
|
||||
"bestValue": "Лучшее соотношение цена/качество",
|
||||
"bestQuality": "Лучшее качество",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(Сохранено)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "Test your AI providers for tag generation and semantic search embeddings",
|
||||
@@ -156,9 +162,9 @@
|
||||
"analyzing": "Анализ ваших заметок для предложений меток...",
|
||||
"create": "Создать",
|
||||
"createNewLabel": "Создать новую метку",
|
||||
"created": "{count} labels created successfully",
|
||||
"created": "{count} тегов успешно создано",
|
||||
"creating": "Создание меток...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"description": "Я обнаружил повторяющиеся темы в \"{notebookName}\" ({totalNotes} заметках). Создать для них теги?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(новая)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
@@ -530,14 +536,14 @@
|
||||
"memoryEcho": {
|
||||
"clickToView": "Нажмите для просмотра",
|
||||
"comparison": {
|
||||
"clickToView": "Click to view note",
|
||||
"helpful": "Helpful",
|
||||
"helpfulQuestion": "Is this comparison helpful?",
|
||||
"highSimilarityInsight": "These notes deal with the same topic with a high degree of similarity. They could be merged or consolidated.",
|
||||
"notHelpful": "Not Helpful",
|
||||
"similarityInfo": "These notes are connected by {similarity}% similarity",
|
||||
"title": "💡 Note Comparison",
|
||||
"untitled": "Untitled"
|
||||
"title": "💡 Сравнение заметок",
|
||||
"similarityInfo": "Эти заметки связаны на {similarity}% подобия",
|
||||
"highSimilarityInsight": "Эти заметки относятся к одной теме с высокой степенью подобия. Их можно объединить.",
|
||||
"untitled": "Без названия",
|
||||
"clickToView": "Нажмите для просмотра заметки",
|
||||
"helpfulQuestion": "Полезно ли это сравнение?",
|
||||
"helpful": "Полезно",
|
||||
"notHelpful": "Не полезно"
|
||||
},
|
||||
"connection": "connection",
|
||||
"connections": "Connections",
|
||||
@@ -546,40 +552,40 @@
|
||||
"description": "Proactive connections between your notes",
|
||||
"dismiss": "Dismiss for now",
|
||||
"editorSection": {
|
||||
"close": "Закрыть",
|
||||
"compare": "Compare",
|
||||
"compareAll": "Compare all",
|
||||
"loading": "Loading...",
|
||||
"merge": "Merge",
|
||||
"mergeAll": "Merge all",
|
||||
"title": "⚡ Connected Notes ({count})",
|
||||
"view": "View"
|
||||
"title": "⚡ Связанные заметки ({count})",
|
||||
"loading": "Загрузка...",
|
||||
"view": "Просмотр",
|
||||
"compare": "Сравнить",
|
||||
"merge": "Объединить",
|
||||
"compareAll": "Сравнить всё",
|
||||
"mergeAll": "Объединить всё",
|
||||
"close": "Закрыть"
|
||||
},
|
||||
"fused": "Fused",
|
||||
"fused": "Объединено",
|
||||
"fusion": {
|
||||
"archiveOriginals": "Archive original notes",
|
||||
"cancel": "Cancel",
|
||||
"confirmFusion": "Confirm fusion",
|
||||
"createBacklinks": "Create backlink to original notes",
|
||||
"edit": "Edit",
|
||||
"error": "Failed to merge notes",
|
||||
"finishEditing": "Finish editing",
|
||||
"generateError": "Failed to generate fusion",
|
||||
"generateFusion": "Generate the fusion",
|
||||
"generating": "Generating...",
|
||||
"keepAllTags": "Keep all tags",
|
||||
"mergeNotes": "Merge {count} note(s)",
|
||||
"modify": "Modify",
|
||||
"noContentReturned": "No fusion content returned from API",
|
||||
"notesToMerge": "📝 Notes to merge",
|
||||
"optionalPrompt": "💬 Fusion prompt (optional)",
|
||||
"optionsTitle": "Fusion options",
|
||||
"previewTitle": "📝 Preview of merged note",
|
||||
"promptPlaceholder": "Optional instructions for AI (e.g., 'Keep the formal style of note 1')...",
|
||||
"success": "Notes merged successfully!",
|
||||
"title": "🔗 Intelligent Fusion",
|
||||
"unknownDate": "Unknown date",
|
||||
"useLatestTitle": "Use latest note as title"
|
||||
"title": "🔗 Умное слияние",
|
||||
"mergeNotes": "Объединить {count} заметку/и",
|
||||
"notesToMerge": "📝 Заметки для объединения",
|
||||
"optionalPrompt": "💬 Промпт слияния (необязательно)",
|
||||
"promptPlaceholder": "Необязательные инструкции для ИИ (напр. 'Сохранить формальный стиль заметки 1')...",
|
||||
"generateFusion": "Сгенерировать слияние",
|
||||
"generating": "Генерация...",
|
||||
"previewTitle": "📝 Предпросмотр объединённой заметки",
|
||||
"edit": "Редактировать",
|
||||
"modify": "Изменить",
|
||||
"finishEditing": "Завершить редактирование",
|
||||
"optionsTitle": "Параметры слияния",
|
||||
"archiveOriginals": "Архивировать оригинальные заметки",
|
||||
"keepAllTags": "Сохранить все теги",
|
||||
"useLatestTitle": "Использовать последнюю заметку как заголовок",
|
||||
"createBacklinks": "Создать обратную ссылку на оригинальные заметки",
|
||||
"cancel": "Отмена",
|
||||
"confirmFusion": "Подтвердить слияние",
|
||||
"success": "Заметки успешно объединены!",
|
||||
"error": "Не удалось объединить заметки",
|
||||
"generateError": "Не удалось создать слияние",
|
||||
"noContentReturned": "API не вернул содержимого слияния",
|
||||
"unknownDate": "Неизвестная дата"
|
||||
},
|
||||
"helpful": "Helpful",
|
||||
"insightReady": "Your insight is ready!",
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
"tagsGenerationProvider": "标签生成提供商",
|
||||
"title": "AI 配置",
|
||||
"updateFailed": "更新 AI 设置失败",
|
||||
"updateSuccess": "AI 设置更新成功"
|
||||
"updateSuccess": "AI 设置更新成功",
|
||||
"bestValue": "最佳性价比",
|
||||
"bestQuality": "最佳质量",
|
||||
"providerOllamaOption": "🦙 Ollama (Local & Free)",
|
||||
"providerOpenAIOption": "🤖 OpenAI (GPT-5, GPT-4)",
|
||||
"providerCustomOption": "🔧 Custom OpenAI-Compatible",
|
||||
"saved": "(已保存)"
|
||||
},
|
||||
"aiTest": {
|
||||
"description": "测试您的 AI 提供商的标签生成和语义搜索嵌入",
|
||||
@@ -153,34 +159,36 @@
|
||||
"analyzing": "AI 分析中...",
|
||||
"assistant": "AI 助手",
|
||||
"autoLabels": {
|
||||
"analyzing": "正在分析您的笔记以获取标签建议...",
|
||||
"error": "获取标签建议失败",
|
||||
"noLabelsSelected": "未选择标签",
|
||||
"created": "成功创建 {count} 个标签",
|
||||
"analyzing": "正在分析您的笔记...",
|
||||
"title": "新标签建议",
|
||||
"description": "我在\"{notebookName}\"({totalNotes} 条笔记)中检测到了重复主题。要为它们创建标签吗?",
|
||||
"note": "条笔记",
|
||||
"notes": "条笔记",
|
||||
"typeContent": "输入内容以获取标签建议...",
|
||||
"createNewLabel": "创建此新标签并添加",
|
||||
"new": "(新)",
|
||||
"create": "创建",
|
||||
"createNewLabel": "Create this new label and add it",
|
||||
"created": "{count} labels created successfully",
|
||||
"creating": "正在创建标签...",
|
||||
"description": "I've detected recurring themes in \"{notebookName}\" ({totalNotes} notes). Create labels for them?",
|
||||
"error": "Failed to fetch label suggestions",
|
||||
"new": "(新建)",
|
||||
"noLabelsSelected": "No labels selected",
|
||||
"note": "note",
|
||||
"notes": "notes",
|
||||
"title": "标签建议",
|
||||
"typeContent": "Type content to get label suggestions..."
|
||||
"notesCount": "{count} 条笔记",
|
||||
"typeForSuggestions": "输入内容以获取标签建议..."
|
||||
},
|
||||
"batchOrganization": {
|
||||
"analyzing": "Analyzing your notes...",
|
||||
"apply": "Apply",
|
||||
"applyFailed": "Failed to apply organization plan",
|
||||
"applying": "Applying...",
|
||||
"description": "AI将分析您的笔记并建议将它们组织到笔记本中。",
|
||||
"error": "Failed to create organization plan",
|
||||
"noNotebooks": "No notebooks available. Create notebooks first to organize your notes.",
|
||||
"noNotesSelected": "No notes selected",
|
||||
"noSuggestions": "AI could not find a good way to organize these notes.",
|
||||
"selectAllIn": "Select all notes in {notebook}",
|
||||
"selectNote": "Select note: {title}",
|
||||
"success": "{count} notes moved successfully",
|
||||
"title": "Organize with AI"
|
||||
"title": "使用 AI 整理",
|
||||
"description": "AI 将分析您的笔记并建议将它们整理到笔记本中。",
|
||||
"analyzing": "正在分析您的笔记...",
|
||||
"noNotebooks": "没有可用的笔记本。请先创建笔记本。",
|
||||
"noSuggestions": "AI 无法找到整理这些笔记的好方法。",
|
||||
"apply": "应用",
|
||||
"applying": "正在应用...",
|
||||
"success": "成功移动 {count} 条笔记",
|
||||
"error": "创建整理计划失败",
|
||||
"noNotesSelected": "未选择笔记",
|
||||
"applyFailed": "应用整理计划失败",
|
||||
"selectAllIn": "选择 {notebook} 中的所有笔记",
|
||||
"selectNote": "选择笔记:{title}"
|
||||
},
|
||||
"clarify": "澄清",
|
||||
"clickToAddTag": "点击添加此标签",
|
||||
|
||||
Reference in New Issue
Block a user