i18 cleanup

This commit is contained in:
2026-02-25 18:31:50 +03:00
parent 3f66a58b12
commit 1d8eadf796
48 changed files with 1431 additions and 808 deletions

View File

@@ -260,8 +260,8 @@
function buildConversationTitle(conversation) {
if (conversation?.title?.trim()) return conversation.title.trim();
if (!conversation?.conversation_id) return "Conversation";
return `Conversation ${conversation.conversation_id.slice(0, 8)}`;
if (!conversation?.conversation_id) return $t.assistant?.conversation;
return `${$t.assistant?.conversation} ${conversation.conversation_id.slice(0, 8)}`;
}
function setConversationFilter(filter) {
@@ -312,7 +312,7 @@
} catch (err) {
appendAssistantResponse({
response_id: `error-${Date.now()}`,
text: err.message || "Assistant request failed",
text: err.message || $t.assistant?.request_failed,
state: "failed",
created_at: new Date().toISOString(),
actions: [],
@@ -402,7 +402,7 @@
} catch (err) {
appendAssistantResponse({
response_id: `action-error-${Date.now()}`,
text: err.message || "Action failed",
text: err.message || $t.assistant?.action_failed,
state: "failed",
created_at: new Date().toISOString(),
actions: [],
@@ -484,13 +484,13 @@
<div class="flex items-center gap-2 text-slate-800">
<Icon name="clipboard" size={18} />
<h2 class="text-sm font-semibold">
{$t.assistant?.title || "AI Assistant"}
{$t.assistant?.title}
</h2>
</div>
<button
class="rounded-md p-1 text-slate-500 transition hover:bg-slate-100 hover:text-slate-900"
on:click={closeAssistantChat}
aria-label={$t.assistant?.close || "Close assistant"}
aria-label={$t.assistant?.close}
>
<Icon name="close" size={18} />
</button>
@@ -501,13 +501,13 @@
<div class="mb-2 flex items-center justify-between">
<span
class="text-xs font-semibold uppercase tracking-wide text-slate-500"
>Conversations</span
>{$t.assistant?.conversations}</span
>
<button
class="rounded-md border border-slate-300 px-2 py-1 text-[11px] font-medium text-slate-700 transition hover:bg-slate-100"
on:click={startNewConversation}
>
New
{$t.assistant?.new}
</button>
</div>
<div class="mb-2 flex items-center gap-1">
@@ -518,7 +518,7 @@
: 'border-slate-300 bg-white text-slate-700 hover:bg-slate-100'}"
on:click={() => setConversationFilter("active")}
>
Active ({activeConversationsTotal})
{$t.assistant?.active} ({activeConversationsTotal})
</button>
<button
class="rounded-md border px-2 py-1 text-[11px] font-medium transition {conversationFilter ===
@@ -527,7 +527,7 @@
: 'border-slate-300 bg-white text-slate-700 hover:bg-slate-100'}"
on:click={() => setConversationFilter("archived")}
>
Archived ({archivedConversationsTotal})
{$t.assistant?.archived} ({archivedConversationsTotal})
</button>
</div>
<div class="flex gap-2 overflow-x-auto pb-1">
@@ -560,7 +560,7 @@
class="rounded-lg border border-slate-300 px-2.5 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-100"
on:click={() => loadConversations(false)}
>
More
{$t.assistant?.more}
</button>
{/if}
</div>
@@ -575,24 +575,24 @@
<div
class="rounded-lg border border-slate-200 bg-slate-50 p-2 text-center text-xs text-slate-500"
>
Loading older messages...
{$t.assistant?.loading_older}
</div>
{/if}
{#if loadingHistory}
<div
class="rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-600"
>
{$t.assistant?.loading_history || "Loading history..."}
{$t.assistant?.loading_history}
</div>
{:else if messages.length === 0}
<div
class="rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-600"
>
{$t.assistant?.try_commands || "Try commands:"}
{$t.assistant?.try_commands}
<div class="mt-2 space-y-1 text-xs">
<div>сделай ветку feature/new-dashboard для дашборда 42</div>
<div>запусти миграцию с dev на prod для дашборда 42</div>
<div>проверь статус задачи task-123</div>
<div>{$t.assistant?.sample_command_branch}</div>
<div>{$t.assistant?.sample_command_migration}</div>
<div>{$t.assistant?.sample_command_status}</div>
</div>
</div>
{/if}
@@ -608,7 +608,7 @@
<span
class="text-[11px] font-semibold uppercase tracking-wide text-slate-500"
>
{message.role === "user" ? "You" : "Assistant"}
{message.role === "user" ? $t.assistant?.you : $t.assistant?.assistant}
</span>
{#if message.state}
<span
@@ -629,13 +629,13 @@
<div class="mt-2 flex items-center gap-2">
<span
class="rounded border border-slate-200 bg-slate-50 px-2 py-0.5 text-xs text-slate-700"
>task_id: {message.task_id}</span
>{$t.assistant?.task_id}: {message.task_id}</span
>
<button
class="text-xs font-medium text-sky-700 hover:text-sky-900"
on:click={() => openDrawerForTask(message.task_id)}
>
{$t.assistant?.open_task_drawer || "Open Task Drawer"}
{$t.assistant?.open_task_drawer}
</button>
</div>
{/if}
@@ -668,11 +668,11 @@
<span
class="text-[11px] font-semibold uppercase tracking-wide text-slate-500"
>
Assistant
{$t.assistant?.assistant}
</span>
</div>
<div class="flex items-center gap-2 text-sm text-slate-700">
<span>{$t.assistant?.thinking || "Думаю"}</span>
<span>{$t.assistant?.thinking}</span>
<span class="thinking-dots" aria-hidden="true">
<span></span><span></span><span></span>
</span>
@@ -687,7 +687,7 @@
<textarea
bind:value={input}
rows="2"
placeholder={$t.assistant?.input_placeholder || "Type a command..."}
placeholder={$t.assistant?.input_placeholder}
class="min-h-[52px] w-full resize-y rounded-lg border border-slate-300 px-3 py-2 text-sm outline-none transition focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
on:keydown={handleKeydown}
></textarea>
@@ -696,7 +696,7 @@
on:click={handleSend}
disabled={loading || !input.trim()}
>
{loading ? "..." : $t.assistant?.send || "Send"}
{loading ? "..." : $t.assistant?.send}
</button>
</div>
</div>

View File

@@ -30,7 +30,7 @@
*/
function getBreadcrumbs(pathname, maxVisible = 3) {
const segments = pathname.split("/").filter(Boolean);
const allItems = [{ label: $t.nav?.home || "Home", path: "/" }];
const allItems = [{ label: $t.nav?.home , path: "/" }];
let currentPath = "";
segments.forEach((segment, index) => {
@@ -136,7 +136,7 @@
<nav
class="mx-4 md:mx-6"
aria-label={$t.nav?.breadcrumb_nav || "Breadcrumb navigation"}
aria-label={$t.nav?.breadcrumb_nav }
>
<div class="inline-flex max-w-full items-center gap-1.5 rounded-xl border border-slate-200/80 bg-white/85 px-2 py-1.5 shadow-sm backdrop-blur">
{#each breadcrumbItems as item, index}

View File

@@ -30,56 +30,56 @@
return [
{
id: "dashboards",
label: $t.nav?.dashboards || "DASHBOARDS",
label: $t.nav?.dashboards ,
icon: "dashboard",
tone: "from-sky-100 to-sky-200 text-sky-700 ring-sky-200",
path: "/dashboards",
subItems: [
{ label: $t.nav?.overview || "Overview", path: "/dashboards" },
{ label: $t.nav?.overview , path: "/dashboards" },
],
},
{
id: "datasets",
label: $t.nav?.datasets || "DATASETS",
label: $t.nav?.datasets ,
icon: "database",
tone: "from-emerald-100 to-emerald-200 text-emerald-700 ring-emerald-200",
path: "/datasets",
subItems: [
{ label: $t.nav?.all_datasets || "All Datasets", path: "/datasets" },
{ label: $t.nav?.all_datasets , path: "/datasets" },
],
},
{
id: "storage",
label: $t.nav?.storage || "STORAGE",
label: $t.nav?.storage ,
icon: "storage",
tone: "from-amber-100 to-amber-200 text-amber-800 ring-amber-200",
path: "/storage",
subItems: [
{ label: $t.nav?.backups || "Backups", path: "/storage/backups" },
{ label: $t.nav?.backups , path: "/storage/backups" },
{
label: $t.nav?.repositories || "Repositories",
label: $t.nav?.repositories ,
path: "/storage/repos",
},
],
},
{
id: "reports",
label: $t.nav?.reports || "REPORTS",
label: $t.nav?.reports ,
icon: "reports",
tone: "from-violet-100 to-fuchsia-100 text-violet-700 ring-violet-200",
path: "/reports",
subItems: [{ label: $t.nav?.reports || "Reports", path: "/reports" }],
subItems: [{ label: $t.nav?.reports , path: "/reports" }],
},
{
id: "admin",
label: $t.nav?.admin || "ADMIN",
label: $t.nav?.admin ,
icon: "admin",
tone: "from-rose-100 to-rose-200 text-rose-700 ring-rose-200",
path: "/admin",
subItems: [
{ label: $t.nav?.admin_users || "Users", path: "/admin/users" },
{ label: $t.nav?.admin_roles || "Roles", path: "/admin/roles" },
{ label: $t.nav?.settings || "Settings", path: "/settings" },
{ label: $t.nav?.admin_users , path: "/admin/users" },
{ label: $t.nav?.admin_roles , path: "/admin/roles" },
{ label: $t.nav?.settings , path: "/settings" },
],
},
];
@@ -201,7 +201,7 @@
<span class="inline-flex h-6 w-6 items-center justify-center rounded-md bg-gradient-to-br from-slate-100 to-slate-200 text-slate-700 ring-1 ring-slate-200">
<Icon name="layers" size={14} />
</span>
{$t.nav?.menu || "Menu"}
{$t.nav?.menu }
</span>
{:else}
<span class="text-xs text-gray-500">M</span>
@@ -285,7 +285,7 @@
<span class="mr-2 inline-flex h-6 w-6 items-center justify-center rounded-md bg-slate-100 text-slate-600">
<Icon name="chevronLeft" size={14} />
</span>
{$t.nav?.collapse || "Collapse"}
{$t.nav?.collapse }
</button>
</div>
{:else}
@@ -293,10 +293,10 @@
<button
class="flex items-center justify-center w-full px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
on:click={handleToggleClick}
aria-label={$t.nav?.expand_sidebar || "Expand sidebar"}
aria-label={$t.nav?.expand_sidebar }
>
<Icon name="chevronRight" size={16} />
<span class="ml-2">{$t.nav?.expand || "Expand"}</span>
<span class="ml-2">{$t.nav?.expand }</span>
</button>
</div>
{/if}

View File

@@ -220,7 +220,7 @@
style={`right: ${assistantOffset};`}
role="dialog"
aria-modal="false"
aria-label={$t.tasks?.drawer || "Task drawer"}
aria-label={$t.tasks?.drawer }
>
<!-- Header -->
<div
@@ -237,15 +237,15 @@
<button
class="flex items-center justify-center p-1.5 rounded-md text-slate-500 bg-transparent border-none cursor-pointer transition-all hover:text-slate-100 hover:bg-slate-800"
on:click={goBackToList}
aria-label={$t.tasks?.back_to_list || "Back to task list"}
aria-label={$t.tasks?.back_to_list }
>
<Icon name="back" size={16} strokeWidth={2} />
</button>
{/if}
<h2 class="text-sm font-semibold tracking-tight text-slate-900">
{activeTaskId
? $t.tasks?.details_logs || "Task Details & Logs"
: $t.tasks?.recent || "Recent Tasks"}
? $t.tasks?.details_logs
: $t.tasks?.recent }
</h2>
{#if shortTaskId}
<span
@@ -270,12 +270,12 @@
class="rounded-md border border-slate-300 bg-slate-50 px-2.5 py-1 text-xs font-semibold text-slate-700 transition-colors hover:bg-slate-100"
on:click={goToReportsPage}
>
{$t.nav?.reports || "Reports"}
{$t.nav?.reports }
</button>
<button
class="p-1.5 rounded-md text-slate-500 bg-transparent border-none cursor-pointer transition-all hover:text-slate-100 hover:bg-slate-800"
on:click={handleClose}
aria-label={$t.tasks?.close_drawer || "Close drawer"}
aria-label={$t.tasks?.close_drawer }
>
<Icon name="close" size={18} strokeWidth={2} />
</button>
@@ -298,14 +298,14 @@
<div
class="mb-4 h-8 w-8 animate-spin rounded-full border-2 border-slate-200 border-t-blue-500"
></div>
<p>{$t.tasks?.loading || "Loading tasks..."}</p>
<p>{$t.tasks?.loading }</p>
</div>
{:else if recentTasks.length > 0}
<div class="p-4">
<h3
class="text-sm font-semibold text-slate-100 mb-4 pb-2 border-b border-slate-800"
>
{$t.tasks?.recent || "Recent Tasks"}
{$t.tasks?.recent }
</h3>
{#each recentTasks as task}
<button
@@ -318,7 +318,7 @@
"N/A"}...</span
>
<span class="flex-1 text-sm text-slate-100 font-medium"
>{task.plugin_id || $t.common?.unknown || "Unknown"}</span
>{task.plugin_id || $t.common?.unknown }</span
>
<span
class="text-xs font-semibold uppercase px-2 py-1 rounded-full {task.status?.toLowerCase() ===
@@ -331,7 +331,7 @@
task.status?.toLowerCase() === 'error'
? 'bg-red-500/15 text-red-400'
: 'bg-slate-500/15 text-slate-400'}"
>{task.status || $t.common?.unknown || "UNKNOWN"}</span
>{task.status || $t.common?.unknown }</span
>
</button>
{/each}
@@ -346,7 +346,7 @@
strokeWidth={1.6}
className="mb-3 text-slate-700"
/>
<p>{$t.tasks?.select_task || "No recent tasks"}</p>
<p>{$t.tasks?.select_task }</p>
</div>
{/if}
</div>
@@ -357,7 +357,7 @@
>
<div class="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse"></div>
<p class="text-xs text-slate-500">
{$t.tasks?.footer_text || "Task continues running in background"}
{$t.tasks?.footer_text }
</p>
</div>
</aside>

View File

@@ -106,7 +106,7 @@
<button
class="rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100 md:hidden"
on:click={handleHamburgerClick}
aria-label={$t.common?.toggle_menu || "Toggle menu"}
aria-label={$t.common?.toggle_menu }
>
<Icon name="menu" size={22} />
</button>
@@ -119,7 +119,7 @@
<span class="mr-2 inline-flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-sky-500 via-cyan-500 to-indigo-600 text-white shadow-sm">
<Icon name="layers" size={18} strokeWidth={2.1} />
</span>
<span>Superset Tools</span>
<span>{$t.common?.brand }</span>
</a>
</div>
@@ -129,7 +129,7 @@
type="text"
class="w-full px-4 py-2 bg-gray-100 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-ring transition-all
{isSearchFocused ? 'bg-white border border-primary-ring' : ''}"
placeholder={$t.common.search || "Search..."}
placeholder={$t.common.search }
on:focus={handleSearchFocus}
on:blur={handleSearchBlur}
/>
@@ -143,8 +143,8 @@
<button
class="rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100"
on:click={handleAssistantClick}
aria-label={$t.assistant?.open || "Open assistant"}
title={$t.assistant?.title || "AI Assistant"}
aria-label={$t.assistant?.open }
title={$t.assistant?.title }
>
<Icon name="clipboard" size={22} />
</button>
@@ -157,7 +157,7 @@
(e.key === "Enter" || e.key === " ") && handleActivityClick()}
role="button"
tabindex="0"
aria-label={$t.common?.activity || "Activity"}
aria-label={$t.common?.activity }
>
<Icon name="activity" size={22} />
{#if activeCount > 0}
@@ -177,7 +177,7 @@
(e.key === "Enter" || e.key === " ") && toggleUserMenu(e)}
role="button"
tabindex="0"
aria-label={$t.common?.user_menu || "User menu"}
aria-label={$t.common?.user_menu }
>
{#if user}
<span
@@ -195,7 +195,7 @@
: 'hidden'}"
>
<div class="px-4 py-2 text-sm text-gray-700">
<strong>{user?.username || ($t.common?.user || "User")}</strong>
<strong>{user?.username || ($t.common?.user )}</strong>
</div>
<div class="border-t border-gray-200 my-1"></div>
<div
@@ -209,7 +209,7 @@
role="button"
tabindex="0"
>
{$t.nav?.settings || "Settings"}
{$t.nav?.settings }
</div>
<div
class="px-4 py-2 text-sm text-destructive hover:bg-destructive-light cursor-pointer"
@@ -219,7 +219,7 @@
role="button"
tabindex="0"
>
{$t.common?.logout || "Logout"}
{$t.common?.logout }
</div>
</div>
</div>

View File

@@ -32,17 +32,17 @@
}
function getStatusLabel(status) {
if (status === 'success') return $t.reports?.status_success || 'Success';
if (status === 'failed') return $t.reports?.status_failed || 'Failed';
if (status === 'in_progress') return $t.reports?.status_in_progress || 'In progress';
if (status === 'partial') return $t.reports?.status_partial || 'Partial';
return status || ($t.reports?.not_provided || 'Not provided');
if (status === 'success') return $t.reports?.status_success ;
if (status === 'failed') return $t.reports?.status_failed ;
if (status === 'in_progress') return $t.reports?.status_in_progress ;
if (status === 'partial') return $t.reports?.status_partial ;
return status || ($t.reports?.not_provided );
}
function formatDate(value) {
if (!value) return $t.reports?.not_provided || 'Not provided';
if (!value) return $t.reports?.not_provided ;
const date = new Date(value);
if (Number.isNaN(date.getTime())) return $t.reports?.not_provided || 'Not provided';
if (Number.isNaN(date.getTime())) return $t.reports?.not_provided ;
return date.toLocaleString();
}
@@ -54,18 +54,18 @@
<button
class="w-full rounded-xl border p-4 text-left shadow-sm transition hover:border-slate-300 hover:bg-slate-50 hover:shadow {selected ? 'border-blue-400 bg-blue-50' : 'border-slate-200 bg-white'}"
on:click={onSelect}
aria-label={`Report ${report?.report_id || ''} type ${profileLabel || ($t.reports?.unknown_type || 'Other / Unknown Type')}`}
aria-label={`${$t.reports?.title} ${report?.report_id || ''} ${$t.reports?.type} ${profileLabel || $t.reports?.unknown_type}`}
>
<div class="mb-2 flex items-center justify-between gap-2">
<span class="rounded px-2 py-0.5 text-xs font-semibold {profile?.variant || 'bg-slate-100 text-slate-700'}">
{profileLabel || ($t.reports?.unknown_type || 'Other / Unknown Type')}
{profileLabel || $t.reports?.unknown_type}
</span>
<span class="rounded px-2 py-0.5 text-xs font-semibold {getStatusClass(report?.status)}">
{getStatusLabel(report?.status)}
</span>
</div>
<p class="text-sm font-medium text-slate-800">{report?.summary || ($t.reports?.not_provided || 'Not provided')}</p>
<p class="text-sm font-medium text-slate-800">{report?.summary || $t.reports?.not_provided}</p>
<p class="mt-1 text-xs text-slate-500">{formatDate(report?.updated_at)}</p>
</button>
<!-- [/DEF:ReportCard:Component] -->
<!-- [/DEF:ReportCard:Component] -->

View File

@@ -18,41 +18,41 @@
function notProvided(value) {
if (value === null || value === undefined || value === '') {
return $t.reports?.not_provided || 'Not provided';
return $t.reports?.not_provided;
}
return value;
}
function formatDate(value) {
if (!value) return $t.reports?.not_provided || 'Not provided';
if (!value) return $t.reports?.not_provided;
const date = new Date(value);
if (Number.isNaN(date.getTime())) return $t.reports?.not_provided || 'Not provided';
if (Number.isNaN(date.getTime())) return $t.reports?.not_provided;
return date.toLocaleString();
}
</script>
<div class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
<h3 class="mb-3 text-sm font-semibold text-slate-700">{$t.reports?.view_details || 'View details'}</h3>
<h3 class="mb-3 text-sm font-semibold text-slate-700">{$t.reports?.view_details}</h3>
{#if !detail || !detail.report}
<p class="text-sm text-slate-500">{$t.reports?.not_provided || 'Not provided'}</p>
<p class="text-sm text-slate-500">{$t.reports?.not_provided}</p>
{:else}
<div class="space-y-2 text-sm text-slate-700">
<p><span class="text-slate-500">ID:</span> {notProvided(detail.report.report_id)}</p>
<p><span class="text-slate-500">Type:</span> {notProvided(detail.report.task_type)}</p>
<p><span class="text-slate-500">Status:</span> {notProvided(detail.report.status)}</p>
<p><span class="text-slate-500">Summary:</span> {notProvided(detail.report.summary)}</p>
<p><span class="text-slate-500">Updated:</span> {formatDate(detail.report.updated_at)}</p>
<p><span class="text-slate-500">{$t.reports?.id}:</span> {notProvided(detail.report.report_id)}</p>
<p><span class="text-slate-500">{$t.reports?.type}:</span> {notProvided(detail.report.task_type)}</p>
<p><span class="text-slate-500">{$t.reports?.status}:</span> {notProvided(detail.report.status)}</p>
<p><span class="text-slate-500">{$t.reports?.summary}:</span> {notProvided(detail.report.summary)}</p>
<p><span class="text-slate-500">{$t.reports?.updated}:</span> {formatDate(detail.report.updated_at)}</p>
</div>
<div class="mt-4">
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">{$t.reports?.diagnostics || 'Diagnostics'}</p>
<pre class="max-h-48 overflow-auto rounded-lg border border-slate-200 bg-slate-50 p-2 text-xs text-slate-700">{JSON.stringify(detail.diagnostics || { note: $t.reports?.not_provided || 'Not provided' }, null, 2)}</pre>
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">{$t.reports?.diagnostics}</p>
<pre class="max-h-48 overflow-auto rounded-lg border border-slate-200 bg-slate-50 p-2 text-xs text-slate-700">{JSON.stringify(detail.diagnostics || { note: $t.reports?.not_provided }, null, 2)}</pre>
</div>
{#if (detail.next_actions && detail.next_actions.length > 0) || (detail.report.error_context && detail.report.error_context.next_actions && detail.report.error_context.next_actions.length > 0)}
<div class="mt-4">
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">{$t.reports?.next_actions || 'Next actions'}</p>
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">{$t.reports?.next_actions}</p>
<ul class="list-disc space-y-1 pl-5 text-sm text-slate-700">
{#each (detail.next_actions && detail.next_actions.length > 0 ? detail.next_actions : detail.report.error_context.next_actions) as action}
<li>{action}</li>
@@ -63,4 +63,4 @@
{/if}
</div>
<!-- [/DEF:ReportDetailPanel:Component] -->
<!-- [/DEF:ReportDetailPanel:Component] -->