feat: Complete internationalization and code cleanup
## Translation Files - Add 11 new language files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl) - Add 100+ missing translation keys across all 15 languages - New sections: notebook, pagination, ai.batchOrganization, ai.autoLabels - Update nav section with workspace, quickAccess, myLibrary keys ## Component Updates - Update 15+ components to use translation keys instead of hardcoded text - Components: notebook dialogs, sidebar, header, note-input, ghost-tags, etc. - Replace 80+ hardcoded English/French strings with t() calls - Ensure consistent UI across all supported languages ## Code Quality - Remove 77+ console.log statements from codebase - Clean up API routes, components, hooks, and services - Keep only essential error handling (no debugging logs) ## UI/UX Improvements - Update Keep logo to yellow post-it style (from-yellow-400 to-amber-500) - Change selection colors to #FEF3C6 (notebooks) and #EFB162 (nav items) - Make "+" button permanently visible in notebooks section - Fix grammar and syntax errors in multiple components ## Bug Fixes - Fix JSON syntax errors in it.json, nl.json, pl.json, zh.json - Fix syntax errors in notebook-suggestion-toast.tsx - Fix syntax errors in use-auto-tagging.ts - Fix syntax errors in paragraph-refactor.service.ts - Fix duplicate "fusion" section in nl.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Ou une version plus courte si vous préférez : feat(i18n): Add 15 languages, remove logs, update UI components - Create 11 new translation files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl) - Add 100+ translation keys: notebook, pagination, AI features - Update 15+ components to use translations (80+ strings) - Remove 77+ console.log statements from codebase - Fix JSON syntax errors in 4 translation files - Fix component syntax errors (toast, hooks, services) - Update logo to yellow post-it style - Change selection colors (#FEF3C6, #EFB162) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import { Badge } from "@/components/ui/badge"
|
||||
import { X, Loader2, Mail } from "lucide-react"
|
||||
import { addCollaborator, removeCollaborator, getNoteCollaborators } from "@/app/actions/notes"
|
||||
import { toast } from "sonner"
|
||||
import { useLanguage } from "@/lib/i18n"
|
||||
|
||||
interface Collaborator {
|
||||
id: string
|
||||
@@ -46,6 +47,7 @@ export function CollaboratorDialog({
|
||||
initialCollaborators = []
|
||||
}: CollaboratorDialogProps) {
|
||||
const router = useRouter()
|
||||
const { t } = useLanguage()
|
||||
const [collaborators, setCollaborators] = useState<Collaborator[]>([])
|
||||
const [localCollaboratorIds, setLocalCollaboratorIds] = useState<string[]>(initialCollaborators)
|
||||
const [email, setEmail] = useState('')
|
||||
@@ -66,7 +68,7 @@ export function CollaboratorDialog({
|
||||
setCollaborators(result)
|
||||
hasLoadedRef.current = true
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Error loading collaborators')
|
||||
toast.error(error.message || t('collaboration.errorLoading'))
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
@@ -103,9 +105,9 @@ export function CollaboratorDialog({
|
||||
setLocalCollaboratorIds(newIds)
|
||||
onCollaboratorsChange?.(newIds)
|
||||
setEmail('')
|
||||
toast.success(`${email} will be added as collaborator when note is created`)
|
||||
toast.success(t('collaboration.willBeAdded', { email }))
|
||||
} else {
|
||||
toast.warning('This email is already in the list')
|
||||
toast.warning(t('collaboration.alreadyInList'))
|
||||
}
|
||||
} else {
|
||||
// Existing note mode: use server action
|
||||
@@ -117,13 +119,13 @@ export function CollaboratorDialog({
|
||||
if (result.success) {
|
||||
setCollaborators([...collaborators, result.user])
|
||||
setEmail('')
|
||||
toast.success(`${result.user.name || result.user.email} now has access to this note`)
|
||||
toast.success(t('collaboration.nowHasAccess', { name: result.user.name || result.user.email }))
|
||||
// Don't refresh here - it would close the dialog!
|
||||
// The collaborator list is already updated in local state
|
||||
setJustAddedCollaborator(false)
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Failed to add collaborator')
|
||||
toast.error(error.message || t('collaboration.failedToAdd'))
|
||||
setJustAddedCollaborator(false)
|
||||
}
|
||||
})
|
||||
@@ -143,11 +145,11 @@ export function CollaboratorDialog({
|
||||
try {
|
||||
await removeCollaborator(noteId, userId)
|
||||
setCollaborators(collaborators.filter(c => c.id !== userId))
|
||||
toast.success('Access has been revoked')
|
||||
toast.success(t('collaboration.accessRevoked'))
|
||||
// Don't refresh here - it would close the dialog!
|
||||
// The collaborator list is already updated in local state
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Failed to remove collaborator')
|
||||
toast.error(error.message || t('collaboration.failedToRemove'))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -184,11 +186,11 @@ export function CollaboratorDialog({
|
||||
}}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Share with collaborators</DialogTitle>
|
||||
<DialogTitle>{t('collaboration.shareWithCollaborators')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{isOwner
|
||||
? "Add people to collaborate on this note by their email address."
|
||||
: "You have access to this note. Only the owner can manage collaborators."}
|
||||
? t('collaboration.addCollaboratorDescription')
|
||||
: t('collaboration.viewerDescription')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -196,11 +198,11 @@ export function CollaboratorDialog({
|
||||
{isOwner && (
|
||||
<form onSubmit={handleAddCollaborator} className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<Label htmlFor="email" className="sr-only">Email address</Label>
|
||||
<Label htmlFor="email" className="sr-only">{t('collaboration.emailAddress')}</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="Enter email address"
|
||||
placeholder={t('collaboration.enterEmailAddress')}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={isPending}
|
||||
@@ -212,7 +214,7 @@ export function CollaboratorDialog({
|
||||
) : (
|
||||
<>
|
||||
<Mail className="h-4 w-4 mr-2" />
|
||||
Invite
|
||||
{t('collaboration.invite')}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
@@ -220,7 +222,7 @@ export function CollaboratorDialog({
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>People with access</Label>
|
||||
<Label>{t('collaboration.peopleWithAccess')}</Label>
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center py-4">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
||||
@@ -229,7 +231,7 @@ export function CollaboratorDialog({
|
||||
// Creation mode: show emails
|
||||
localCollaboratorIds.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground text-center py-4">
|
||||
No collaborators yet. Add someone above!
|
||||
{t('collaboration.noCollaborators')}
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
@@ -247,13 +249,13 @@ export function CollaboratorDialog({
|
||||
</Avatar>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium truncate">
|
||||
Pending Invite
|
||||
{t('collaboration.pendingInvite')}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{emailOrId}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="outline" className="ml-2">Pending</Badge>
|
||||
<Badge variant="outline" className="ml-2">{t('collaboration.pending')}</Badge>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -261,7 +263,7 @@ export function CollaboratorDialog({
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => handleRemoveCollaborator(emailOrId)}
|
||||
disabled={isPending}
|
||||
aria-label="Remove"
|
||||
aria-label={t('collaboration.remove')}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -271,7 +273,7 @@ export function CollaboratorDialog({
|
||||
)
|
||||
) : collaborators.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground text-center py-4">
|
||||
No collaborators yet. {isOwner && "Add someone above!"}
|
||||
{t('collaboration.noCollaboratorsViewer')} {isOwner && t('collaboration.noCollaborators').split('.')[1]}
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
@@ -290,14 +292,14 @@ export function CollaboratorDialog({
|
||||
</Avatar>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium truncate">
|
||||
{collaborator.name || 'Unnamed User'}
|
||||
{collaborator.name || t('collaboration.unnamedUser')}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{collaborator.email}
|
||||
</p>
|
||||
</div>
|
||||
{collaborator.id === noteOwnerId && (
|
||||
<Badge variant="secondary" className="ml-2">Owner</Badge>
|
||||
<Badge variant="secondary" className="ml-2">{t('collaboration.owner')}</Badge>
|
||||
)}
|
||||
</div>
|
||||
{isOwner && collaborator.id !== noteOwnerId && (
|
||||
@@ -307,7 +309,7 @@ export function CollaboratorDialog({
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => handleRemoveCollaborator(collaborator.id)}
|
||||
disabled={isPending}
|
||||
aria-label="Remove"
|
||||
aria-label={t('collaboration.remove')}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -321,7 +323,7 @@ export function CollaboratorDialog({
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Done
|
||||
{t('collaboration.done')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
Reference in New Issue
Block a user