Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions web/src/lib/components/explorer/MemoryActions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import PinOff from "@lucide/svelte/icons/pin-off";
import Trash2 from "@lucide/svelte/icons/trash-2";
import Button from "$lib/components/ui/button.svelte";
import { useI18n } from "$lib/i18n/context.svelte";
import { ICON_SM } from "$lib/ui/styles";

type Props = {
Expand All @@ -29,6 +30,8 @@
onEdit,
onDelete,
}: Props = $props();

const i18n = useI18n();
</script>

<div class="flex shrink-0 items-center gap-1">
Expand All @@ -38,8 +41,8 @@
variant="ghost"
size="icon-xs"
class="text-primary hover:text-primary"
title="Unpin"
aria-label="Unpin"
title={i18n.t("btn-unpin")}
aria-label={i18n.t("btn-unpin")}
onclick={() => onUnpin?.(id)}
>
<PinOff class={ICON_SM} aria-hidden="true" />
Expand All @@ -48,8 +51,8 @@
<Button
variant="ghost"
size="icon-xs"
title="Pin"
aria-label="Pin"
title={i18n.t("btn-pin")}
aria-label={i18n.t("btn-pin")}
onclick={() => onPin?.(id)}
>
<Pin class={ICON_SM} aria-hidden="true" />
Expand All @@ -58,8 +61,8 @@
<Button
variant="ghost"
size="icon-xs"
title="Edit"
aria-label="Edit"
title={i18n.t("btn-edit")}
aria-label={i18n.t("btn-edit")}
onclick={() => onEdit?.(id)}
>
<PenLine class={ICON_SM} aria-hidden="true" />
Expand Down
6 changes: 4 additions & 2 deletions web/src/lib/components/ui/pagination-bar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import ChevronLeft from "@lucide/svelte/icons/chevron-left";
import ChevronRight from "@lucide/svelte/icons/chevron-right";
import Button from "$lib/components/ui/button.svelte";
import { useI18n } from "$lib/i18n/context.svelte";
import type { PageSlice } from "$lib/pagination";
import { GAP, GAP_TIGHT, ICON_SM } from "$lib/ui/styles";

Expand All @@ -27,6 +28,7 @@
onDelta,
}: Props = $props();

const i18n = useI18n();
const hasPrev = $derived(currentPage > 1);
const hasNext = $derived(currentPage < totalPages);
</script>
Expand All @@ -37,7 +39,7 @@
variant="outline"
size="icon-xs"
disabled={!hasPrev}
aria-label="Previous page"
aria-label={i18n.t("nav-prev-page")}
onclick={() => onDelta?.(-1)}
>
<ChevronLeft class={ICON_SM} />
Expand All @@ -47,7 +49,7 @@
variant="outline"
size="icon-xs"
disabled={!hasNext}
aria-label="Next page"
aria-label={i18n.t("nav-next-page")}
onclick={() => onDelta?.(1)}
>
<ChevronRight class={ICON_SM} />
Expand Down
9 changes: 6 additions & 3 deletions web/src/lib/components/ui/search-input.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import Search from "@lucide/svelte/icons/search";
import X from "@lucide/svelte/icons/x";
import type { HTMLInputAttributes } from "svelte/elements";
import { useI18n } from "$lib/i18n/context.svelte";
import { FIELD_INPUT, HOVER_SURFACE, ICON, ICON_BTN_DENSE } from "$lib/ui/styles";
import { cn } from "$lib/utils";

Expand All @@ -27,6 +28,8 @@
onkeydown,
}: Props = $props();

const i18n = useI18n();

function handleSearch(event: MouseEvent & { currentTarget: HTMLButtonElement }) {
onSearch?.();
const form = event.currentTarget.closest("form");
Expand Down Expand Up @@ -55,7 +58,7 @@
ICON_BTN_DENSE,
HOVER_SURFACE
)}
aria-label="Clear"
aria-label={i18n.t("btn-clear")}
onclick={() => onClear?.()}
>
<X class={ICON} aria-hidden="true" />
Expand All @@ -68,8 +71,8 @@
ICON_BTN_DENSE,
HOVER_SURFACE
)}
aria-label="Search"
title="Search"
aria-label={i18n.t("label-search")}
title={i18n.t("label-search")}
onclick={handleSearch}
>
<Search class={ICON} aria-hidden="true" />
Expand Down
3 changes: 2 additions & 1 deletion web/src/lib/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { getLanguage } from "./i18n";
export function formatDate(isoString: string): string {
const date = new Date(isoString);
const lang = getLanguage();
const locale = lang === "zh" ? "zh-CN" : lang === "ar" ? "ar-SA" : "en-US";
const locale =
lang === "zh" ? "zh-CN" : lang === "ar" ? "ar-SA" : lang === "de" ? "de-DE" : "en-US";
return date.toLocaleString(locale, {
year: "numeric",
month: "short",
Expand Down
204 changes: 204 additions & 0 deletions web/src/lib/i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,186 @@ export const translations = {
"migration-mismatch-details":
"Model mismatch: Config uses {configDimensions}D ({configModel}), but {shardInfo}.",
"auth-warning-text": "Cross-origin editing disabled. Set webServerAuthPassword to enable.",
"btn-pin": "Pin",
"btn-unpin": "Unpin",
"btn-clear": "Clear",
"label-search": "Search",
"nav-prev-page": "Previous page",
"nav-next-page": "Next page",
},
de: {
title: "OpenCode Memory Explorer",
brand: "opencode-mem",
"tab-project": "Projekt-Memories",
"tab-profile": "Benutzerprofil",
"nav-menu": "Menü",
"nav-close": "Menü schließen",
"nav-collapse": "Seitenleiste einklappen",
"nav-expand": "Seitenleiste ausklappen",
"nav-language": "Sprache",
"nav-theme": "Design",
"theme-dark": "Dunkel",
"theme-light": "Hell",
"label-tag": "Tag",
"label-type": "Typ",
"label-tags": "Tags",
"label-content": "Inhalt",
"btn-cleanup": "Aufräumen",
"btn-deduplicate": "Duplikate entfernen",
"btn-filter": "Filter",
"btn-filter-clear": "Auswahl aufheben",
"btn-delete-selected": "Auswahl löschen",
"btn-select-all": "Seite auswählen",
"btn-deselect-all": "Auswahl aufheben",
"btn-add-memory": "Memory hinzufügen",
"section-project": "Projekt-Memories ({count})",
"section-profile": "Benutzerprofil",
"section-add": "Neues Memory hinzufügen",
"opt-all-tags": "Alle Tags",
"opt-select-tag": "Tag auswählen",
"opt-other": "sonstiges (other)",
"opt-feature": "Feature (feature)",
"opt-bug-fix": "Bugfix (bug-fix)",
"opt-refactor": "Refactoring (refactor)",
"opt-architecture": "Architektur (architecture)",
"opt-rule": "Regel (rule)",
"opt-documentation": "Dokumentation (documentation)",
"opt-discussion": "Diskussion (discussion)",
"opt-analysis": "Analyse (analysis)",
"opt-configuration": "Konfiguration (configuration)",
"modal-edit-title": "Memory bearbeiten",
"modal-migration-title": "Memory-Tag-Migration",
"modal-changelog-title": "Profil-Versionsverlauf",
"btn-cancel": "Abbrechen",
"btn-save": "Änderungen speichern",
"btn-start-migration": "Migration starten",
"loading-init": "Wird initialisiert…",
"loading-profile": "Profil wird geladen…",
"loading-changelog": "Changelog wird geladen…",
"migration-mismatch": "Abweichende Modelldimensionen erkannt!",
"migration-understand":
"Ich verstehe, dass dieser Vorgang nicht rückgängig gemacht werden kann und alle gespeicherten Memories betrifft",
"btn-fresh-start": "Neustart (Alles löschen)",
"btn-reembed": "Neu einbetten (Daten behalten)",
"migration-note":
"Bitte den Browser nicht schließen. Deine Memories werden mit technischen Tags neu vektorisiert, um die Suchgenauigkeit zu verbessern.",
"placeholder-filter": "Filtern…",
"placeholder-search": "Memories suchen…",
"placeholder-tags": "react, hooks, auth (kommagetrennt)",
"placeholder-new-tag": "Neuer Tag…",
"btn-add-tag": "Tag hinzufügen",
"placeholder-content": "Memory-Inhalt eingeben…",
"toast-add-success": "Memory erfolgreich hinzugefügt",
"toast-add-error": "Inhalt und Tag sind erforderlich",
"toast-add-failed": "Memory konnte nicht hinzugefügt werden",
"toast-delete-success": "Memory erfolgreich gelöscht",
"toast-delete-failed": "Memory konnte nicht gelöscht werden",
"toast-update-success": "Memory erfolgreich aktualisiert",
"toast-update-failed": "Memory konnte nicht aktualisiert werden",
"toast-cleanup-success": "Aufräumen erfolgreich abgeschlossen",
"toast-cleanup-failed": "Aufräumen fehlgeschlagen",
"toast-dedup-success": "Duplikatentfernung erfolgreich abgeschlossen",
"toast-dedup-failed": "Duplikatentfernung fehlgeschlagen",
"toast-bulk-delete-success": "Ausgewählte Memories erfolgreich gelöscht",
"toast-bulk-delete-failed": "Ausgewählte Memories konnten nicht gelöscht werden",
"toast-migration-success": "Migration erfolgreich abgeschlossen",
"toast-migration-failed": "Migration fehlgeschlagen",
"toast-migration-tag-failures":
"Migration fehlgeschlagen: {count} Fehler bei der Tag-Generierung",
"toast-fresh-start-success": "Neustart erfolgreich abgeschlossen",
"toast-fresh-start-failed": "Neustart fehlgeschlagen",
"confirm-delete": "Dieses Memory löschen?",
"confirm-delete-title": "Löschen bestätigen",
"confirm-delete-pair": "Dieses Memory UND den verknüpften Prompt löschen?",
"confirm-delete-prompt": "Diesen Prompt UND das verknüpfte Memory löschen?",
"confirm-bulk-delete": "{count} ausgewählte Memories löschen?",
"confirm-bulk-delete-title": "Auswahl löschen",
"confirm-cleanup":
"Damit werden alle Memories entfernt, die nicht mehr relevant sind. Fortfahren?",
"confirm-cleanup-title": "Memories aufräumen",
"confirm-dedup":
"Damit werden doppelte oder sehr ähnliche Memories zusammengeführt. Fortfahren?",
"confirm-dedup-title": "Duplikate entfernen",
"confirm-migration-title": "Migration bestätigen",
"confirm-migration": "{strategy} ausführen?\n\nDieser Vorgang ist nicht rückgängig zu machen.",
"text-selected": "{count} ausgewählt",
"text-page": "Seite {current} von {total}",
"text-total": "Gesamt: {count}",
"empty-memories": "Keine Memories gefunden",
"empty-changelog": "Kein Changelog verfügbar",
"status-cleanup": "Aufräumen läuft…",
"status-dedup": "Duplikatentfernung läuft…",
"status-migration-init": "Migration wird initialisiert…",
"status-migration-progress": "Migration… {current}/{total}",
"profile-version": "Version",
"profile-prompts": "Prompts",
"profile-updated": "Zuletzt aktualisiert",
"profile-meta": "{count} Prompts · {date}",
"profile-preferences": "Präferenzen",
"profile-type-pref": "Präf",
"profile-type-pat": "Muster",
"profile-type-wf": "Ablauf",
"profile-patterns": "Muster",
"profile-workflows": "Workflows",
"label-evidence-tooltip": "KI-Analyse traf {count}-mal zu",
"badge-prompt": "Benutzer-Prompt",
"badge-memory": "Memory",
"badge-pinned": "Angepinnt",
"section-all": "Alle",
"badge-linked": "Verknüpft",
"date-created": "Erstellt:",
"date-updated": "Aktualisiert:",
"empty-preferences": "Noch keine Präferenzen gelernt",
"empty-patterns": "Noch keine Muster erkannt",
"empty-workflows": "Noch keine Workflows identifiziert",
"btn-delete-pair": "Paar löschen",
"btn-edit": "Bearbeiten",
"btn-delete": "Löschen",
"label-category": "Kategorie",
"label-description": "Beschreibung",
"label-steps": "Schritte",
"btn-add-step": "+ Schritt hinzufügen",
"text-generated-above": "Generiertes Memory oben",
"text-from-below": "Aus Prompt unten",
"btn-refresh": "Aktualisieren",
"btn-ai-cleanup": "KI-Aufräumen",
"label-ai-cleanup-title": "KI-Profil-Aufräumen",
"label-ai-cleanup-loading": "KI analysiert das Profil…",
"label-ai-cleanup-merged": "Zusammengeführt",
"label-ai-cleanup-removed": "Entfernt",
"label-ai-cleanup-kept": "Verbleibend",
"label-ai-cleanup-none": "Keine",
"label-ai-cleanup-select-all": "Alle auswählen",
"label-ai-cleanup-deselect-all": "Auswahl aufheben",
"label-ai-cleanup-apply": "Änderungen übernehmen",
"label-ai-cleanup-merge-check": "Zusammenführen",
"label-ai-cleanup-remove-check": "Entfernen",
"label-ai-cleanup-changes-selected": "{selected}/{total} Änderungen ausgewählt",
"label-ai-cleanup-merged-header": "Zusammengeführt ({count} Gruppen)",
"label-ai-cleanup-removed-header": "Entfernt ({count} Einträge)",
"btn-apply": "Übernehmen",
"toast-ai-cleanup-success": "Profil-Aufräumen abgeschlossen",
"toast-ai-cleanup-failed": "KI-Aufräumen fehlgeschlagen",
"toast-cleanup-apply-failed": "Aufräumen konnte nicht übernommen werden",
"label-ai-cleanup-select": "Einträge für die Aufräum-Analyse auswählen",
"label-ai-cleanup-select-low": "Niedrige Häufigkeit (≤3)",
"label-ai-cleanup-select-same-cat": "Paare derselben Kategorie",
"label-ai-cleanup-analyze": "Auswahl analysieren",
"label-ai-cleanup-selected": "{count} ausgewählt",
"migration-found-tags": "{count} Memories benötigen technische Tags.",
"migration-stopped": "Migration gestoppt: Maximale Anzahl Versuche erreicht",
"migration-shards-mismatch": "{count} Shard(s) haben unterschiedliche Dimensionen",
"migration-dimension-mismatch": "Dimensionsabweichung erkannt",
"migration-mismatch-details":
"Modellabweichung: Konfiguration nutzt {configDimensions}D ({configModel}), aber {shardInfo}.",
"auth-warning-text":
"Cross-Origin-Bearbeitung deaktiviert. Setze webServerAuthPassword zum Aktivieren.",
"btn-pin": "Anheften",
"btn-unpin": "Loslösen",
"btn-clear": "Leeren",
"label-search": "Suchen",
"nav-prev-page": "Vorherige Seite",
"nav-next-page": "Nächste Seite",
},
zh: {
title: "OpenCode Memory Explorer",
Expand Down Expand Up @@ -324,6 +504,12 @@ export const translations = {
"migration-mismatch-details":
"模型不匹配:配置使用 {configDimensions}D ({configModel}),但{shardInfo}。",
"auth-warning-text": "跨源编辑已禁用. 设置 webServerAuthPassword 即可启用.",
"btn-pin": "置顶",
"btn-unpin": "取消置顶",
"btn-clear": "清除",
"label-search": "搜索",
"nav-prev-page": "上一页",
"nav-next-page": "下一页",
},
ar: {
title: "مستكشف ذاكرة OpenCode",
Expand Down Expand Up @@ -527,6 +713,12 @@ export const translations = {
"عدم تطابق النموذج: يستخدم الإعداد {configDimensions}D ({configModel}) بينما {shardInfo}.",

"auth-warning-text": "التحرير عبر المصادر معطّل. قم بتعيين webServerAuthPassword لتفعيله.",
"btn-pin": "تثبيت",
"btn-unpin": "إلغاء التثبيت",
"btn-clear": "مسح",
"label-search": "بحث",
"nav-prev-page": "الصفحة السابقة",
"nav-next-page": "الصفحة التالية",
},
tr: {
title: "OpenCode Hafıza Gezgini",
Expand Down Expand Up @@ -693,6 +885,12 @@ export const translations = {
"Model uyumsuzluğu: Yapılandırma {configDimensions}D ({configModel}) kullanıyor, ancak {shardInfo}.",
"auth-warning-text":
"Çapraz kaynak düzenleme devre dışı. Etkinleştirmek için webServerAuthPassword ayarlayın.",
"btn-pin": "Sabitle",
"btn-unpin": "Sabitlemeyi kaldır",
"btn-clear": "Temizle",
"label-search": "Ara",
"nav-prev-page": "Önceki sayfa",
"nav-next-page": "Sonraki sayfa",
},
nl: {
title: "OpenCode Memory Explorer",
Expand Down Expand Up @@ -861,6 +1059,12 @@ export const translations = {
"Modelmismatch: configuratie gebruikt {configDimensions}D ({configModel}), maar {shardInfo}.",
"auth-warning-text":
"Bewerken vanuit andere bronnen is uitgeschakeld. Stel webServerAuthPassword in om dit in te schakelen.",
"btn-pin": "Vastzetten",
"btn-unpin": "Losmaken",
"btn-clear": "Wissen",
"label-search": "Zoeken",
"nav-prev-page": "Vorige pagina",
"nav-next-page": "Volgende pagina",
},
} as const;
export type Lang = keyof typeof translations;
Expand Down
Loading