feat(i18n): migrate hardcoded German strings to t() across 104 components/pages — AST-based batch with re-parse gate, 423 new de.json keys; tsc clean; vitest failures byte-identical to clean-tree baseline (pre-existing)
This commit is contained in:
@@ -12,6 +12,7 @@ import { ResizablePanel } from '@/components/ui/ResizablePanel';
|
||||
import { usePluginToolbarStore } from '@/store/pluginToolbarStore';
|
||||
import { apiClient } from '@/api/client';
|
||||
import { streamChat, fetchAgents, fetchMessages as fetchAiMessages, type AIAgent } from '@/api/ai';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ─── Types ───
|
||||
|
||||
@@ -216,6 +217,7 @@ interface ConversationTreeProps {
|
||||
}
|
||||
|
||||
function ConversationTree({ conversations, activeConvId, onSelect, onNewChat, loading }: ConversationTreeProps) {
|
||||
const { t } = useTranslation();
|
||||
const [expanded, setExpanded] = useState<Record<ConversationCategory, boolean>>({
|
||||
system: true,
|
||||
ai: true,
|
||||
@@ -293,7 +295,7 @@ function ConversationTree({ conversations, activeConvId, onSelect, onNewChat, lo
|
||||
</button>
|
||||
))}
|
||||
{expanded[section.category] && section.conversations.length === 0 && (
|
||||
<div className="px-6 py-2 text-xs text-secondary-400">Keine Konversationen</div>
|
||||
<div className="px-6 py-2 text-xs text-secondary-400">{t('communication.keinekonversationen')}</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
@@ -311,6 +313,7 @@ interface ChatWindowProps {
|
||||
}
|
||||
|
||||
function ChatWindow({ convId, category, onBack }: ChatWindowProps) {
|
||||
const { t } = useTranslation();
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [input, setInput] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -499,11 +502,11 @@ function ChatWindow({ convId, category, onBack }: ChatWindowProps) {
|
||||
<button
|
||||
onClick={() => setShowMiniApps(!showMiniApps)}
|
||||
className="p-1 hover:bg-secondary-100 rounded"
|
||||
title="Mini-Apps"
|
||||
title={t('communication.miniapps')}
|
||||
>
|
||||
<Sparkles className="w-4 h-4 text-secondary-400" />
|
||||
</button>
|
||||
<button className="p-1 hover:bg-secondary-100 rounded" title="In neuem Fenster">
|
||||
<button className="p-1 hover:bg-secondary-100 rounded" title={t('communication.inneuemfenster')}>
|
||||
<ExternalLink className="w-4 h-4 text-secondary-400" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -537,7 +540,7 @@ function ChatWindow({ convId, category, onBack }: ChatWindowProps) {
|
||||
{!loading && messages.length === 0 && !aiStreaming && (
|
||||
<div className="text-center text-secondary-400 py-8">
|
||||
<MessageSquare className="w-10 h-10 mx-auto mb-2 opacity-50" />
|
||||
<p className="text-sm">Keine Nachrichten. Schreibe die erste!</p>
|
||||
<p className="text-sm">{t('communication.keinenachrichtenschreibedieerste')}</p>
|
||||
</div>
|
||||
)}
|
||||
{messages.map(msg => (
|
||||
@@ -549,7 +552,7 @@ function ChatWindow({ convId, category, onBack }: ChatWindowProps) {
|
||||
<Bot className="w-4 h-4 text-primary-600" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-xs text-secondary-500 mb-1">KI antwortet...</div>
|
||||
<div className="text-xs text-secondary-500 mb-1">{t('communication.kiantwortet')}</div>
|
||||
<div className="ai-markdown max-w-none break-words">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{streamingContent || '...'}</ReactMarkdown>
|
||||
</div>
|
||||
@@ -561,10 +564,10 @@ function ChatWindow({ convId, category, onBack }: ChatWindowProps) {
|
||||
{/* Input */}
|
||||
<div className="border-t border-secondary-200 p-3">
|
||||
<div className="flex items-end gap-2">
|
||||
<button className="p-2 hover:bg-secondary-100 rounded-lg" title="Datei anhängen">
|
||||
<button className="p-2 hover:bg-secondary-100 rounded-lg" title={t('communication.dateianhängen')}>
|
||||
<Paperclip className="w-4 h-4 text-secondary-400" />
|
||||
</button>
|
||||
<button className="p-2 hover:bg-secondary-100 rounded-lg" title="Emoji">
|
||||
<button className="p-2 hover:bg-secondary-100 rounded-lg" title={t('communication.emoji')}>
|
||||
<Smile className="w-4 h-4 text-secondary-400" />
|
||||
</button>
|
||||
<textarea
|
||||
@@ -667,6 +670,7 @@ interface NewChatDialogProps {
|
||||
}
|
||||
|
||||
function NewChatDialog({ category, onClose, onCreate }: NewChatDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [title, setTitle] = useState('');
|
||||
const [users, setUsers] = useState<{id: string; name: string; email: string}[]>([]);
|
||||
const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
|
||||
@@ -707,7 +711,7 @@ function NewChatDialog({ category, onClose, onCreate }: NewChatDialogProps) {
|
||||
/>
|
||||
{category === 'colleague' && (
|
||||
<div>
|
||||
<label className="text-sm font-medium text-secondary-700 mb-1 block">Teilnehmer auswählen</label>
|
||||
<label className="text-sm font-medium text-secondary-700 mb-1 block">{t('communication.teilnehmerauswählen')}</label>
|
||||
{loading ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
@@ -745,6 +749,7 @@ function NewChatDialog({ category, onClose, onCreate }: NewChatDialogProps) {
|
||||
// ─── Main Page ───
|
||||
|
||||
export function CommunicationPage() {
|
||||
const { t } = useTranslation();
|
||||
const [conversations, setConversations] = useState<Conversation[]>([]);
|
||||
const [activeConvId, setActiveConvId] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -851,7 +856,7 @@ export function CommunicationPage() {
|
||||
<div className="flex items-center justify-center h-full text-secondary-400">
|
||||
<div className="text-center">
|
||||
<MessageSquare className="w-12 h-12 mx-auto mb-3 opacity-50" />
|
||||
<p>Wähle eine Konversation aus</p>
|
||||
<p>{t('communication.wähleeinekonversationaus')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user