summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src')
-rw-r--r--packages/app/src/components/dialog-release-notes.tsx10
-rw-r--r--packages/app/src/components/dialog-select-file.tsx2
-rw-r--r--packages/app/src/components/session/session-header.tsx2
-rw-r--r--packages/app/src/components/settings-providers.tsx8
-rw-r--r--packages/app/src/context/global-sync.tsx10
-rw-r--r--packages/app/src/context/global-sync/bootstrap.ts17
-rw-r--r--packages/app/src/i18n/ar.ts14
-rw-r--r--packages/app/src/i18n/br.ts14
-rw-r--r--packages/app/src/i18n/bs.ts14
-rw-r--r--packages/app/src/i18n/da.ts14
-rw-r--r--packages/app/src/i18n/de.ts14
-rw-r--r--packages/app/src/i18n/en.ts16
-rw-r--r--packages/app/src/i18n/es.ts14
-rw-r--r--packages/app/src/i18n/fr.ts14
-rw-r--r--packages/app/src/i18n/ja.ts14
-rw-r--r--packages/app/src/i18n/ko.ts14
-rw-r--r--packages/app/src/i18n/no.ts14
-rw-r--r--packages/app/src/i18n/pl.ts14
-rw-r--r--packages/app/src/i18n/ru.ts14
-rw-r--r--packages/app/src/i18n/th.ts14
-rw-r--r--packages/app/src/i18n/tr.ts14
-rw-r--r--packages/app/src/i18n/zh.ts14
-rw-r--r--packages/app/src/i18n/zht.ts14
-rw-r--r--packages/app/src/utils/server-errors.ts27
-rw-r--r--packages/app/src/utils/time.ts18
25 files changed, 311 insertions, 23 deletions
diff --git a/packages/app/src/components/dialog-release-notes.tsx b/packages/app/src/components/dialog-release-notes.tsx
index 2040009a8..d0a35b71b 100644
--- a/packages/app/src/components/dialog-release-notes.tsx
+++ b/packages/app/src/components/dialog-release-notes.tsx
@@ -2,6 +2,7 @@ import { createSignal } from "solid-js"
import { Dialog } from "@opencode-ai/ui/dialog"
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
+import { useLanguage } from "@/context/language"
import { useSettings } from "@/context/settings"
export type Highlight = {
@@ -16,6 +17,7 @@ export type Highlight = {
export function DialogReleaseNotes(props: { highlights: Highlight[] }) {
const dialog = useDialog()
+ const language = useLanguage()
const settings = useSettings()
const [index, setIndex] = createSignal(0)
@@ -83,16 +85,16 @@ export function DialogReleaseNotes(props: { highlights: Highlight[] }) {
<div class="flex flex-col items-start gap-3">
{isLast() ? (
<Button variant="primary" size="large" onClick={handleClose}>
- Get started
+ {language.t("dialog.releaseNotes.action.getStarted")}
</Button>
) : (
<Button variant="secondary" size="large" onClick={handleNext}>
- Next
+ {language.t("dialog.releaseNotes.action.next")}
</Button>
)}
<Button variant="ghost" size="small" onClick={handleDisable}>
- Don't show these in the future
+ {language.t("dialog.releaseNotes.action.hideFuture")}
</Button>
</div>
@@ -128,7 +130,7 @@ export function DialogReleaseNotes(props: { highlights: Highlight[] }) {
{feature()!.media!.type === "image" ? (
<img
src={feature()!.media!.src}
- alt={feature()!.media!.alt ?? feature()?.title ?? "Release preview"}
+ alt={feature()!.media!.alt ?? feature()?.title ?? language.t("dialog.releaseNotes.media.alt")}
class="w-full h-full object-cover"
/>
) : (
diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx
index 29a3666c0..b530aff53 100644
--- a/packages/app/src/components/dialog-select-file.tsx
+++ b/packages/app/src/components/dialog-select-file.tsx
@@ -449,7 +449,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
</div>
<Show when={item.updated}>
<span class="text-12-regular text-text-weak whitespace-nowrap ml-2">
- {getRelativeTime(new Date(item.updated!).toISOString())}
+ {getRelativeTime(new Date(item.updated!).toISOString(), language.t)}
</span>
</Show>
</div>
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx
index f3209c354..27b1b9cc0 100644
--- a/packages/app/src/components/session/session-header.tsx
+++ b/packages/app/src/components/session/session-header.tsx
@@ -430,7 +430,7 @@ export function SessionHeader() {
<Spinner class="size-3.5 text-icon-base" />
</Show>
</div>
- <span class="text-12-regular text-text-strong">Open</span>
+ <span class="text-12-regular text-text-strong">{language.t("common.open")}</span>
</Button>
<div class="self-stretch w-px bg-border-weak-base" />
<DropdownMenu
diff --git a/packages/app/src/components/settings-providers.tsx b/packages/app/src/components/settings-providers.tsx
index 55a25ca0c..6fac9feca 100644
--- a/packages/app/src/components/settings-providers.tsx
+++ b/packages/app/src/components/settings-providers.tsx
@@ -162,7 +162,7 @@ export const SettingsProviders: Component = () => {
when={canDisconnect(item)}
fallback={
<span class="text-14-regular text-text-base opacity-0 group-hover:opacity-100 transition-opacity duration-200 pr-3 cursor-default">
- Connected from your environment variables
+ {language.t("settings.providers.connected.environmentDescription")}
</span>
}
>
@@ -229,10 +229,12 @@ export const SettingsProviders: Component = () => {
<div class="flex flex-col min-w-0">
<div class="flex flex-wrap items-center gap-x-3 gap-y-1">
<ProviderIcon id={icon("synthetic")} class="size-5 shrink-0 icon-strong-base" />
- <span class="text-14-medium text-text-strong">Custom provider</span>
+ <span class="text-14-medium text-text-strong">{language.t("provider.custom.title")}</span>
<Tag>{language.t("settings.providers.tag.custom")}</Tag>
</div>
- <span class="text-12-regular text-text-weak pl-8">Add an OpenAI-compatible provider by base URL.</span>
+ <span class="text-12-regular text-text-weak pl-8">
+ {language.t("settings.providers.custom.description")}
+ </span>
</div>
<Button
size="large"
diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx
index f87c3fb39..112bc9240 100644
--- a/packages/app/src/context/global-sync.tsx
+++ b/packages/app/src/context/global-sync.tsx
@@ -204,7 +204,10 @@ function createGlobalSync() {
showToast({
variant: "error",
title: language.t("toast.session.listFailed.title", { project }),
- description: formatServerError(err),
+ description: formatServerError(err, {
+ unknown: language.t("error.chain.unknown"),
+ invalidConfiguration: language.t("error.server.invalidConfiguration"),
+ }),
})
})
@@ -234,6 +237,8 @@ function createGlobalSync() {
setStore: child[1],
vcsCache: cache,
loadSessions,
+ unknownError: language.t("error.chain.unknown"),
+ invalidConfigurationError: language.t("error.server.invalidConfiguration"),
})
})()
@@ -308,6 +313,9 @@ function createGlobalSync() {
url: globalSDK.url,
}),
requestFailedTitle: language.t("common.requestFailed"),
+ unknownError: language.t("error.chain.unknown"),
+ invalidConfigurationError: language.t("error.server.invalidConfiguration"),
+ formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
setGlobalStore,
})
}
diff --git a/packages/app/src/context/global-sync/bootstrap.ts b/packages/app/src/context/global-sync/bootstrap.ts
index b26106561..bc84eb169 100644
--- a/packages/app/src/context/global-sync/bootstrap.ts
+++ b/packages/app/src/context/global-sync/bootstrap.ts
@@ -36,6 +36,9 @@ export async function bootstrapGlobal(input: {
connectErrorTitle: string
connectErrorDescription: string
requestFailedTitle: string
+ unknownError: string
+ invalidConfigurationError: string
+ formatMoreCount: (count: number) => string
setGlobalStore: SetStoreFunction<GlobalStore>
}) {
const health = await input.globalSDK.global
@@ -88,8 +91,11 @@ export async function bootstrapGlobal(input: {
const results = await Promise.allSettled(tasks)
const errors = results.filter((r): r is PromiseRejectedResult => r.status === "rejected").map((r) => r.reason)
if (errors.length) {
- const message = errors[0] instanceof Error ? errors[0].message : String(errors[0])
- const more = errors.length > 1 ? ` (+${errors.length - 1} more)` : ""
+ const message = formatServerError(errors[0], {
+ unknown: input.unknownError,
+ invalidConfiguration: input.invalidConfigurationError,
+ })
+ const more = errors.length > 1 ? input.formatMoreCount(errors.length - 1) : ""
showToast({
variant: "error",
title: input.requestFailedTitle,
@@ -116,6 +122,8 @@ export async function bootstrapDirectory(input: {
setStore: SetStoreFunction<State>
vcsCache: VcsCache
loadSessions: (directory: string) => Promise<void> | void
+ unknownError: string
+ invalidConfigurationError: string
}) {
if (input.store.status !== "complete") input.setStore("status", "loading")
@@ -137,7 +145,10 @@ export async function bootstrapDirectory(input: {
showToast({
variant: "error",
title: `Failed to reload ${project}`,
- description: formatServerError(err),
+ description: formatServerError(err, {
+ unknown: input.unknownError,
+ invalidConfiguration: input.invalidConfigurationError,
+ }),
})
input.setStore("status", "partial")
return
diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts
index 0046a8bc4..16f2fbf49 100644
--- a/packages/app/src/i18n/ar.ts
+++ b/packages/app/src/i18n/ar.ts
@@ -734,4 +734,18 @@ export const dict = {
"workspace.reset.archived.one": "ستتم أرشفة جلسة واحدة.",
"workspace.reset.archived.many": "ستتم أرشفة {{count}} جلسات.",
"workspace.reset.note": "سيؤدي هذا إلى إعادة تعيين مساحة العمل لتتطابق مع الفرع الافتراضي.",
+ "common.open": "فتح",
+ "dialog.releaseNotes.action.getStarted": "البدء",
+ "dialog.releaseNotes.action.next": "التالي",
+ "dialog.releaseNotes.action.hideFuture": "عدم إظهار هذا في المستقبل",
+ "dialog.releaseNotes.media.alt": "معاينة الإصدار",
+ "toast.project.reloadFailed.title": "فشل في إعادة تحميل {{project}}",
+ "error.server.invalidConfiguration": "تكوين غير صالح",
+ "common.moreCountSuffix": " (+{{count}} إضافي)",
+ "common.time.justNow": "الآن",
+ "common.time.minutesAgo.short": "قبل {{count}} د",
+ "common.time.hoursAgo.short": "قبل {{count}} س",
+ "common.time.daysAgo.short": "قبل {{count}} ي",
+ "settings.providers.connected.environmentDescription": "متصل من متغيرات البيئة الخاصة بك",
+ "settings.providers.custom.description": "أضف مزود متوافق مع OpenAI بواسطة عنوان URL الأساسي.",
}
diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts
index 0d41ba7fc..26cf433e0 100644
--- a/packages/app/src/i18n/br.ts
+++ b/packages/app/src/i18n/br.ts
@@ -742,4 +742,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sessão será arquivada.",
"workspace.reset.archived.many": "{{count}} sessões serão arquivadas.",
"workspace.reset.note": "Isso redefinirá o espaço de trabalho para corresponder ao branch padrão.",
+ "common.open": "Abrir",
+ "dialog.releaseNotes.action.getStarted": "Começar",
+ "dialog.releaseNotes.action.next": "Próximo",
+ "dialog.releaseNotes.action.hideFuture": "Não mostrar isso no futuro",
+ "dialog.releaseNotes.media.alt": "Prévia do lançamento",
+ "toast.project.reloadFailed.title": "Falha ao recarregar {{project}}",
+ "error.server.invalidConfiguration": "Configuração inválida",
+ "common.moreCountSuffix": " (+{{count}} mais)",
+ "common.time.justNow": "Agora mesmo",
+ "common.time.minutesAgo.short": "{{count}}m atrás",
+ "common.time.hoursAgo.short": "{{count}}h atrás",
+ "common.time.daysAgo.short": "{{count}}d atrás",
+ "settings.providers.connected.environmentDescription": "Conectado a partir de suas variáveis de ambiente",
+ "settings.providers.custom.description": "Adicionar um provedor compatível com a OpenAI através do URL base.",
}
diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts
index a34d857b9..6c8198bd7 100644
--- a/packages/app/src/i18n/bs.ts
+++ b/packages/app/src/i18n/bs.ts
@@ -819,4 +819,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesija će biti arhivirana.",
"workspace.reset.archived.many": "Biće arhivirano {{count}} sesija.",
"workspace.reset.note": "Ovo će resetovati radni prostor da odgovara podrazumijevanoj grani.",
+ "common.open": "Otvori",
+ "dialog.releaseNotes.action.getStarted": "Započni",
+ "dialog.releaseNotes.action.next": "Sljedeće",
+ "dialog.releaseNotes.action.hideFuture": "Ne prikazuj ovo u budućnosti",
+ "dialog.releaseNotes.media.alt": "Pregled izdanja",
+ "toast.project.reloadFailed.title": "Nije uspjelo ponovno učitavanje {{project}}",
+ "error.server.invalidConfiguration": "Nevažeća konfiguracija",
+ "common.moreCountSuffix": " (+{{count}} više)",
+ "common.time.justNow": "Upravo sada",
+ "common.time.minutesAgo.short": "prije {{count}} min",
+ "common.time.hoursAgo.short": "prije {{count}} h",
+ "common.time.daysAgo.short": "prije {{count}} d",
+ "settings.providers.connected.environmentDescription": "Povezano sa vašim varijablama okruženja",
+ "settings.providers.custom.description": "Dodajte provajdera kompatibilnog s OpenAI putem osnovnog URL-a.",
}
diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts
index 3df23bd43..11da68176 100644
--- a/packages/app/src/i18n/da.ts
+++ b/packages/app/src/i18n/da.ts
@@ -813,4 +813,18 @@ export const dict = {
"workspace.reset.archived.one": "1 session vil blive arkiveret.",
"workspace.reset.archived.many": "{{count}} sessioner vil blive arkiveret.",
"workspace.reset.note": "Dette vil nulstille arbejdsområdet til at matche hovedgrenen.",
+ "common.open": "Åbn",
+ "dialog.releaseNotes.action.getStarted": "Kom i gang",
+ "dialog.releaseNotes.action.next": "Næste",
+ "dialog.releaseNotes.action.hideFuture": "Vis ikke disse i fremtiden",
+ "dialog.releaseNotes.media.alt": "Forhåndsvisning af udgivelse",
+ "toast.project.reloadFailed.title": "Kunne ikke genindlæse {{project}}",
+ "error.server.invalidConfiguration": "Ugyldig konfiguration",
+ "common.moreCountSuffix": " (+{{count}} mere)",
+ "common.time.justNow": "Lige nu",
+ "common.time.minutesAgo.short": "{{count}}m siden",
+ "common.time.hoursAgo.short": "{{count}}t siden",
+ "common.time.daysAgo.short": "{{count}}d siden",
+ "settings.providers.connected.environmentDescription": "Tilsluttet fra dine miljøvariabler",
+ "settings.providers.custom.description": "Tilføj en OpenAI-kompatibel udbyder via basis-URL.",
}
diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts
index ce48a1953..51b9ec353 100644
--- a/packages/app/src/i18n/de.ts
+++ b/packages/app/src/i18n/de.ts
@@ -751,4 +751,18 @@ export const dict = {
"workspace.reset.archived.one": "1 Sitzung wird archiviert.",
"workspace.reset.archived.many": "{{count}} Sitzungen werden archiviert.",
"workspace.reset.note": "Dadurch wird der Arbeitsbereich auf den Standard-Branch zurückgesetzt.",
+ "common.open": "Öffnen",
+ "dialog.releaseNotes.action.getStarted": "Loslegen",
+ "dialog.releaseNotes.action.next": "Weiter",
+ "dialog.releaseNotes.action.hideFuture": "In Zukunft nicht mehr anzeigen",
+ "dialog.releaseNotes.media.alt": "Vorschau auf die Version",
+ "toast.project.reloadFailed.title": "Fehler beim Neuladen von {{project}}",
+ "error.server.invalidConfiguration": "Ungültige Konfiguration",
+ "common.moreCountSuffix": " (+{{count}} weitere)",
+ "common.time.justNow": "Gerade eben",
+ "common.time.minutesAgo.short": "vor {{count}} Min",
+ "common.time.hoursAgo.short": "vor {{count}} Std",
+ "common.time.daysAgo.short": "vor {{count}} Tg",
+ "settings.providers.connected.environmentDescription": "Verbunden aus Ihren Umgebungsvariablen",
+ "settings.providers.custom.description": "Fügen Sie einen OpenAI-kompatiblen Anbieter per Basis-URL hinzu.",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts
index 4b97bfb89..7e95fd739 100644
--- a/packages/app/src/i18n/en.ts
+++ b/packages/app/src/i18n/en.ts
@@ -218,6 +218,7 @@ export const dict = {
"common.loading": "Loading",
"common.loading.ellipsis": "...",
"common.cancel": "Cancel",
+ "common.open": "Open",
"common.connect": "Connect",
"common.disconnect": "Disconnect",
"common.submit": "Submit",
@@ -347,6 +348,11 @@ export const dict = {
"dialog.project.edit.worktree.startup.description": "Runs after creating a new workspace (worktree).",
"dialog.project.edit.worktree.startup.placeholder": "e.g. bun install",
+ "dialog.releaseNotes.action.getStarted": "Get started",
+ "dialog.releaseNotes.action.next": "Next",
+ "dialog.releaseNotes.action.hideFuture": "Don't show these in the future",
+ "dialog.releaseNotes.media.alt": "Release preview",
+
"context.breakdown.title": "Context Breakdown",
"context.breakdown.note": 'Approximate breakdown of input tokens. "Other" includes tool definitions and overhead.',
"context.breakdown.system": "System",
@@ -436,6 +442,7 @@ export const dict = {
"toast.session.unshare.failed.description": "An error occurred while unsharing the session",
"toast.session.listFailed.title": "Failed to load sessions for {{project}}",
+ "toast.project.reloadFailed.title": "Failed to reload {{project}}",
"toast.update.title": "Update available",
"toast.update.description": "A new version of OpenCode ({{version}}) is now available to install.",
@@ -460,6 +467,7 @@ export const dict = {
"directory.error.invalidUrl": "Invalid directory in URL.",
"error.chain.unknown": "Unknown error",
+ "error.server.invalidConfiguration": "Invalid configuration",
"error.chain.causedBy": "Caused by:",
"error.chain.apiError": "API error",
"error.chain.status": "Status: {{status}}",
@@ -570,6 +578,7 @@ export const dict = {
"common.closeTab": "Close tab",
"common.dismiss": "Dismiss",
+ "common.moreCountSuffix": " (+{{count}} more)",
"common.requestFailed": "Request failed",
"common.moreOptions": "More options",
"common.learnMore": "Learn more",
@@ -582,6 +591,11 @@ export const dict = {
"common.loadMore": "Load more",
"common.key.esc": "ESC",
+ "common.time.justNow": "Just now",
+ "common.time.minutesAgo.short": "{{count}}m ago",
+ "common.time.hoursAgo.short": "{{count}}h ago",
+ "common.time.daysAgo.short": "{{count}}d ago",
+
"sidebar.menu.toggle": "Toggle menu",
"sidebar.nav.projectsAndSessions": "Projects and sessions",
"sidebar.settings": "Settings",
@@ -742,7 +756,9 @@ export const dict = {
"settings.providers.description": "Provider settings will be configurable here.",
"settings.providers.section.connected": "Connected providers",
"settings.providers.connected.empty": "No connected providers",
+ "settings.providers.connected.environmentDescription": "Connected from your environment variables",
"settings.providers.section.popular": "Popular providers",
+ "settings.providers.custom.description": "Add an OpenAI-compatible provider by base URL.",
"settings.providers.tag.environment": "Environment",
"settings.providers.tag.config": "Config",
"settings.providers.tag.custom": "Custom",
diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts
index de490cbe9..2665a8085 100644
--- a/packages/app/src/i18n/es.ts
+++ b/packages/app/src/i18n/es.ts
@@ -825,4 +825,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesión será archivada.",
"workspace.reset.archived.many": "{{count}} sesiones serán archivadas.",
"workspace.reset.note": "Esto restablecerá el espacio de trabajo para coincidir con la rama predeterminada.",
+ "common.open": "Abrir",
+ "dialog.releaseNotes.action.getStarted": "Comenzar",
+ "dialog.releaseNotes.action.next": "Siguiente",
+ "dialog.releaseNotes.action.hideFuture": "No mostrar esto en el futuro",
+ "dialog.releaseNotes.media.alt": "Vista previa de la versión",
+ "toast.project.reloadFailed.title": "Error al recargar {{project}}",
+ "error.server.invalidConfiguration": "Configuración inválida",
+ "common.moreCountSuffix": " (+{{count}} más)",
+ "common.time.justNow": "Justo ahora",
+ "common.time.minutesAgo.short": "hace {{count}} min",
+ "common.time.hoursAgo.short": "hace {{count}} h",
+ "common.time.daysAgo.short": "hace {{count}} d",
+ "settings.providers.connected.environmentDescription": "Conectado desde tus variables de entorno",
+ "settings.providers.custom.description": "Añade un proveedor compatible con OpenAI por su URL base.",
}
diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts
index 5e197b4fb..1e67db193 100644
--- a/packages/app/src/i18n/fr.ts
+++ b/packages/app/src/i18n/fr.ts
@@ -749,4 +749,18 @@ export const dict = {
"workspace.reset.archived.one": "1 session sera archivée.",
"workspace.reset.archived.many": "{{count}} sessions seront archivées.",
"workspace.reset.note": "Cela réinitialisera l'espace de travail pour correspondre à la branche par défaut.",
+ "common.open": "Ouvrir",
+ "dialog.releaseNotes.action.getStarted": "Commencer",
+ "dialog.releaseNotes.action.next": "Suivant",
+ "dialog.releaseNotes.action.hideFuture": "Ne plus afficher à l'avenir",
+ "dialog.releaseNotes.media.alt": "Aperçu de la version",
+ "toast.project.reloadFailed.title": "Échec du rechargement de {{project}}",
+ "error.server.invalidConfiguration": "Configuration invalide",
+ "common.moreCountSuffix": " (+{{count}} de plus)",
+ "common.time.justNow": "À l'instant",
+ "common.time.minutesAgo.short": "il y a {{count}}m",
+ "common.time.hoursAgo.short": "il y a {{count}}h",
+ "common.time.daysAgo.short": "il y a {{count}}j",
+ "settings.providers.connected.environmentDescription": "Connecté à partir de vos variables d'environnement",
+ "settings.providers.custom.description": "Ajouter un fournisseur compatible avec OpenAI via l'URL de base.",
}
diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts
index 30f27c197..ecd38d332 100644
--- a/packages/app/src/i18n/ja.ts
+++ b/packages/app/src/i18n/ja.ts
@@ -738,4 +738,18 @@ export const dict = {
"workspace.reset.archived.one": "1つのセッションがアーカイブされます。",
"workspace.reset.archived.many": "{{count}}個のセッションがアーカイブされます。",
"workspace.reset.note": "これにより、ワークスペースはデフォルトブランチと一致するようにリセットされます。",
+ "common.open": "開く",
+ "dialog.releaseNotes.action.getStarted": "始める",
+ "dialog.releaseNotes.action.next": "次へ",
+ "dialog.releaseNotes.action.hideFuture": "今後表示しない",
+ "dialog.releaseNotes.media.alt": "リリースのプレビュー",
+ "toast.project.reloadFailed.title": "{{project}} の再読み込みに失敗しました",
+ "error.server.invalidConfiguration": "無効な設定",
+ "common.moreCountSuffix": " (他 {{count}} 件)",
+ "common.time.justNow": "たった今",
+ "common.time.minutesAgo.short": "{{count}} 分前",
+ "common.time.hoursAgo.short": "{{count}} 時間前",
+ "common.time.daysAgo.short": "{{count}} 日前",
+ "settings.providers.connected.environmentDescription": "環境変数から接続されました",
+ "settings.providers.custom.description": "ベース URL を指定して OpenAI 互換のプロバイダーを追加します。",
}
diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts
index da6cde9ea..8f54b8abd 100644
--- a/packages/app/src/i18n/ko.ts
+++ b/packages/app/src/i18n/ko.ts
@@ -738,4 +738,18 @@ export const dict = {
"workspace.reset.archived.one": "1개의 세션이 보관됩니다.",
"workspace.reset.archived.many": "{{count}}개의 세션이 보관됩니다.",
"workspace.reset.note": "이 작업은 작업 공간을 기본 브랜치와 일치하도록 재설정합니다.",
+ "common.open": "열기",
+ "dialog.releaseNotes.action.getStarted": "시작하기",
+ "dialog.releaseNotes.action.next": "다음",
+ "dialog.releaseNotes.action.hideFuture": "다시 보지 않기",
+ "dialog.releaseNotes.media.alt": "릴리스 미리보기",
+ "toast.project.reloadFailed.title": "{{project}} 다시 불러오기 실패",
+ "error.server.invalidConfiguration": "잘못된 구성",
+ "common.moreCountSuffix": " (외 {{count}}개)",
+ "common.time.justNow": "방금 전",
+ "common.time.minutesAgo.short": "{{count}}분 전",
+ "common.time.hoursAgo.short": "{{count}}시간 전",
+ "common.time.daysAgo.short": "{{count}}일 전",
+ "settings.providers.connected.environmentDescription": "환경 변수에서 연결됨",
+ "settings.providers.custom.description": "기본 URL로 OpenAI 호환 공급자를 추가합니다.",
}
diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts
index bc04695d3..0c94046eb 100644
--- a/packages/app/src/i18n/no.ts
+++ b/packages/app/src/i18n/no.ts
@@ -821,4 +821,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesjon vil bli arkivert.",
"workspace.reset.archived.many": "{{count}} sesjoner vil bli arkivert.",
"workspace.reset.note": "Dette vil tilbakestille arbeidsområdet til å samsvare med standardgrenen.",
+ "common.open": "Åpne",
+ "dialog.releaseNotes.action.getStarted": "Kom i gang",
+ "dialog.releaseNotes.action.next": "Neste",
+ "dialog.releaseNotes.action.hideFuture": "Ikke vis disse igjen",
+ "dialog.releaseNotes.media.alt": "Forhåndsvisning av utgivelse",
+ "toast.project.reloadFailed.title": "Kunne ikke laste inn {{project}} på nytt",
+ "error.server.invalidConfiguration": "Ugyldig konfigurasjon",
+ "common.moreCountSuffix": " (+{{count}} mer)",
+ "common.time.justNow": "Akkurat nå",
+ "common.time.minutesAgo.short": "{{count}} m siden",
+ "common.time.hoursAgo.short": "{{count}} t siden",
+ "common.time.daysAgo.short": "{{count}} d siden",
+ "settings.providers.connected.environmentDescription": "Koblet til fra miljøvariablene dine",
+ "settings.providers.custom.description": "Legg til en OpenAI-kompatibel leverandør via basis-URL.",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts
index 0be46f095..59c0513be 100644
--- a/packages/app/src/i18n/pl.ts
+++ b/packages/app/src/i18n/pl.ts
@@ -740,4 +740,18 @@ export const dict = {
"workspace.reset.archived.one": "1 sesja zostanie zarchiwizowana.",
"workspace.reset.archived.many": "{{count}} sesji zostanie zarchiwizowanych.",
"workspace.reset.note": "To zresetuje przestrzeń roboczą, aby odpowiadała domyślnej gałęzi.",
+ "common.open": "Otwórz",
+ "dialog.releaseNotes.action.getStarted": "Rozpocznij",
+ "dialog.releaseNotes.action.next": "Dalej",
+ "dialog.releaseNotes.action.hideFuture": "Nie pokazuj tego w przyszłości",
+ "dialog.releaseNotes.media.alt": "Podgląd wydania",
+ "toast.project.reloadFailed.title": "Nie udało się ponownie wczytać {{project}}",
+ "error.server.invalidConfiguration": "Nieprawidłowa konfiguracja",
+ "common.moreCountSuffix": " (jeszcze {{count}})",
+ "common.time.justNow": "Przed chwilą",
+ "common.time.minutesAgo.short": "{{count}} min temu",
+ "common.time.hoursAgo.short": "{{count}} godz. temu",
+ "common.time.daysAgo.short": "{{count}} dni temu",
+ "settings.providers.connected.environmentDescription": "Połączono ze zmiennymi środowiskowymi",
+ "settings.providers.custom.description": "Dodaj dostawcę zgodnego z OpenAI poprzez podstawowy URL.",
}
diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts
index cbb916a59..2071eaae7 100644
--- a/packages/app/src/i18n/ru.ts
+++ b/packages/app/src/i18n/ru.ts
@@ -821,4 +821,18 @@ export const dict = {
"workspace.reset.archived.one": "1 сессия будет архивирована.",
"workspace.reset.archived.many": "{{count}} сессий будет архивировано.",
"workspace.reset.note": "Рабочее пространство будет сброшено в соответствие с веткой по умолчанию.",
+ "common.open": "Открыть",
+ "dialog.releaseNotes.action.getStarted": "Начать",
+ "dialog.releaseNotes.action.next": "Далее",
+ "dialog.releaseNotes.action.hideFuture": "Больше не показывать",
+ "dialog.releaseNotes.media.alt": "Превью релиза",
+ "toast.project.reloadFailed.title": "Не удалось перезагрузить {{project}}",
+ "error.server.invalidConfiguration": "Недопустимая конфигурация",
+ "common.moreCountSuffix": " (ещё {{count}})",
+ "common.time.justNow": "Только что",
+ "common.time.minutesAgo.short": "{{count}} мин назад",
+ "common.time.hoursAgo.short": "{{count}} ч назад",
+ "common.time.daysAgo.short": "{{count}} д назад",
+ "settings.providers.connected.environmentDescription": "Подключено из ваших переменных окружения",
+ "settings.providers.custom.description": "Добавить провайдера, совместимого с OpenAI, по базовому URL.",
}
diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts
index c6a33dc67..987155553 100644
--- a/packages/app/src/i18n/th.ts
+++ b/packages/app/src/i18n/th.ts
@@ -811,4 +811,18 @@ export const dict = {
"workspace.reset.archived.one": "1 เซสชันจะถูกจัดเก็บ",
"workspace.reset.archived.many": "{{count}} เซสชันจะถูกจัดเก็บ",
"workspace.reset.note": "สิ่งนี้จะรีเซ็ตพื้นที่ทำงานให้ตรงกับสาขาเริ่มต้น",
+ "common.open": "เปิด",
+ "dialog.releaseNotes.action.getStarted": "เริ่มต้น",
+ "dialog.releaseNotes.action.next": "ถัดไป",
+ "dialog.releaseNotes.action.hideFuture": "ไม่ต้องแสดงสิ่งนี้อีกในอนาคต",
+ "dialog.releaseNotes.media.alt": "ตัวอย่างรุ่น",
+ "toast.project.reloadFailed.title": "ไม่สามารถโหลด {{project}} ใหม่ได้",
+ "error.server.invalidConfiguration": "การกำหนดค่าไม่ถูกต้อง",
+ "common.moreCountSuffix": " (เพิ่มอีก {{count}})",
+ "common.time.justNow": "เมื่อสักครู่นี้",
+ "common.time.minutesAgo.short": "{{count}} นาทีที่แล้ว",
+ "common.time.hoursAgo.short": "{{count}} ชม. ที่แล้ว",
+ "common.time.daysAgo.short": "{{count}} วันที่แล้ว",
+ "settings.providers.connected.environmentDescription": "เชื่อมต่อจากตัวแปรสภาพแวดล้อมของคุณ",
+ "settings.providers.custom.description": "เพิ่มผู้ให้บริการที่รองรับ OpenAI ด้วย URL หลัก",
}
diff --git a/packages/app/src/i18n/tr.ts b/packages/app/src/i18n/tr.ts
index 4a38e61ec..701ee0919 100644
--- a/packages/app/src/i18n/tr.ts
+++ b/packages/app/src/i18n/tr.ts
@@ -832,4 +832,18 @@ export const dict = {
"workspace.reset.archived.one": "1 oturum arşivlenecek.",
"workspace.reset.archived.many": "{{count}} oturum arşivlenecek.",
"workspace.reset.note": "Bu işlem çalışma alanını varsayılan dalla eşleşecek şekilde sıfırlayacak.",
+ "common.open": "Aç",
+ "dialog.releaseNotes.action.getStarted": "Başla",
+ "dialog.releaseNotes.action.next": "İleri",
+ "dialog.releaseNotes.action.hideFuture": "Bunu gelecekte bir daha gösterme",
+ "dialog.releaseNotes.media.alt": "Sürüm önizlemesi",
+ "toast.project.reloadFailed.title": "{{project}} yeniden yüklenemedi",
+ "error.server.invalidConfiguration": "Geçersiz yapılandırma",
+ "common.moreCountSuffix": " (+{{count}} daha)",
+ "common.time.justNow": "Şimdi",
+ "common.time.minutesAgo.short": "{{count}}dk önce",
+ "common.time.hoursAgo.short": "{{count}}sa önce",
+ "common.time.daysAgo.short": "{{count}}g önce",
+ "settings.providers.connected.environmentDescription": "Ortam değişkenlerinizden bağlandı",
+ "settings.providers.custom.description": "Temel URL üzerinden OpenAI uyumlu bir sağlayıcı ekleyin.",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts
index 0b5739083..e72d4c0e3 100644
--- a/packages/app/src/i18n/zh.ts
+++ b/packages/app/src/i18n/zh.ts
@@ -809,4 +809,18 @@ export const dict = {
"workspace.reset.archived.one": "将归档 1 个会话。",
"workspace.reset.archived.many": "将归档 {{count}} 个会话。",
"workspace.reset.note": "这将把工作区重置为与默认分支一致。",
+ "common.open": "打开",
+ "dialog.releaseNotes.action.getStarted": "开始",
+ "dialog.releaseNotes.action.next": "下一步",
+ "dialog.releaseNotes.action.hideFuture": "不再显示",
+ "dialog.releaseNotes.media.alt": "发布预览",
+ "toast.project.reloadFailed.title": "无法重新加载 {{project}}",
+ "error.server.invalidConfiguration": "配置无效",
+ "common.moreCountSuffix": " (还有 {{count}} 个)",
+ "common.time.justNow": "刚刚",
+ "common.time.minutesAgo.short": "{{count}}分钟前",
+ "common.time.hoursAgo.short": "{{count}}小时前",
+ "common.time.daysAgo.short": "{{count}}天前",
+ "settings.providers.connected.environmentDescription": "已通过环境变量连接",
+ "settings.providers.custom.description": "通过基础 URL 添加与 OpenAI 兼容的提供商。",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts
index 4e4509e20..70421dfe1 100644
--- a/packages/app/src/i18n/zht.ts
+++ b/packages/app/src/i18n/zht.ts
@@ -804,4 +804,18 @@ export const dict = {
"workspace.reset.archived.one": "將封存 1 個工作階段。",
"workspace.reset.archived.many": "將封存 {{count}} 個工作階段。",
"workspace.reset.note": "這將把工作區重設為與預設分支一致。",
+ "common.open": "打開",
+ "dialog.releaseNotes.action.getStarted": "開始",
+ "dialog.releaseNotes.action.next": "下一步",
+ "dialog.releaseNotes.action.hideFuture": "不再顯示",
+ "dialog.releaseNotes.media.alt": "發佈預覽",
+ "toast.project.reloadFailed.title": "無法重新載入 {{project}}",
+ "error.server.invalidConfiguration": "無效的設定",
+ "common.moreCountSuffix": " (還有 {{count}} 個)",
+ "common.time.justNow": "剛剛",
+ "common.time.minutesAgo.short": "{{count}}分鐘前",
+ "common.time.hoursAgo.short": "{{count}}小時前",
+ "common.time.daysAgo.short": "{{count}}天前",
+ "settings.providers.connected.environmentDescription": "已從環境變數連線",
+ "settings.providers.custom.description": "透過基本 URL 新增與 OpenAI 相容的提供者。",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/app/src/utils/server-errors.ts b/packages/app/src/utils/server-errors.ts
index 4b9727e61..85ebca132 100644
--- a/packages/app/src/utils/server-errors.ts
+++ b/packages/app/src/utils/server-errors.ts
@@ -7,11 +7,28 @@ export type ConfigInvalidError = {
}
}
-export function formatServerError(error: unknown) {
- if (isConfigInvalidErrorLike(error)) return parseReabaleConfigInvalidError(error)
+type Label = {
+ unknown: string
+ invalidConfiguration: string
+}
+
+const fallback: Label = {
+ unknown: "Unknown error",
+ invalidConfiguration: "Invalid configuration",
+}
+
+function resolveLabel(labels: Partial<Label> | undefined): Label {
+ return {
+ unknown: labels?.unknown ?? fallback.unknown,
+ invalidConfiguration: labels?.invalidConfiguration ?? fallback.invalidConfiguration,
+ }
+}
+
+export function formatServerError(error: unknown, labels?: Partial<Label>) {
+ if (isConfigInvalidErrorLike(error)) return parseReabaleConfigInvalidError(error, labels)
if (error instanceof Error && error.message) return error.message
if (typeof error === "string" && error) return error
- return "Unknown error"
+ return resolveLabel(labels).unknown
}
function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError {
@@ -20,8 +37,8 @@ function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError {
return o.name === "ConfigInvalidError" && typeof o.data === "object" && o.data !== null
}
-export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError) {
- const head = "Invalid configuration"
+export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError, labels?: Partial<Label>) {
+ const head = resolveLabel(labels).invalidConfiguration
const file = errorInput.data.path && errorInput.data.path !== "config" ? errorInput.data.path : ""
const detail = errorInput.data.message?.trim() ?? ""
const issues = (errorInput.data.issues ?? []).map((issue) => {
diff --git a/packages/app/src/utils/time.ts b/packages/app/src/utils/time.ts
index ac709d86d..d183e1080 100644
--- a/packages/app/src/utils/time.ts
+++ b/packages/app/src/utils/time.ts
@@ -1,4 +1,12 @@
-export function getRelativeTime(dateString: string): string {
+type TimeKey =
+ | "common.time.justNow"
+ | "common.time.minutesAgo.short"
+ | "common.time.hoursAgo.short"
+ | "common.time.daysAgo.short"
+
+type Translate = (key: TimeKey, params?: Record<string, string | number>) => string
+
+export function getRelativeTime(dateString: string, t: Translate): string {
const date = new Date(dateString)
const now = new Date()
const diffMs = now.getTime() - date.getTime()
@@ -7,8 +15,8 @@ export function getRelativeTime(dateString: string): string {
const diffHours = Math.floor(diffMinutes / 60)
const diffDays = Math.floor(diffHours / 24)
- if (diffSeconds < 60) return "Just now"
- if (diffMinutes < 60) return `${diffMinutes}m ago`
- if (diffHours < 24) return `${diffHours}h ago`
- return `${diffDays}d ago`
+ if (diffSeconds < 60) return t("common.time.justNow")
+ if (diffMinutes < 60) return t("common.time.minutesAgo.short", { count: diffMinutes })
+ if (diffHours < 24) return t("common.time.hoursAgo.short", { count: diffHours })
+ return t("common.time.daysAgo.short", { count: diffDays })
}