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
1 change: 0 additions & 1 deletion docs/architecture/localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,4 @@ Developers write English only, inline in the `formatMessage` call; `pnpm transla

- `enforce-placeholders` checks values only when they are passed as an object literal; a call that passes a variable, as `model-ready.tsx` does, is not checked.
- Backend prose without a code stays English in every language: model install messages, fit verdicts, `not_runnable_reason`, a Studio format's `unavailable_reason`, and the disk-space `detail` that `lib/api.ts` wraps in a translated sentence.
- Studio's fallback "Needs {models}" joins translated noun phrases with `formatList`, so German case agreement is not guaranteed. It shows only when a format lacks the backend's `unavailable_reason`.
- A chat failure caught before the stream starts shows the `unknown` kind's text, not the error's own detail.
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,51 @@ describe("studio panel", () => {
expect(await screen.findByText("Needs an image model")).toBeTruthy()
})

it("uses complete messages for both model fallback shapes", async () => {
const user = userEvent.setup()

render(
<TooltipProvider>
<StudioPanel
workspaceId={1}
documents={[]}
selectedDocumentIds={[]}
onSelectionChange={vi.fn()}
onToggleAll={vi.fn()}
formats={[
{
key: "summary",
label: "Summary",
requires_model_types: ["text_gen"],
available: false,
unavailable_reason: null,
},
{
key: "image",
label: "Image",
requires_model_types: ["image_gen", "text_gen"],
available: false,
unavailable_reason: null,
},
]}
isCreating={false}
error={null}
onGenerate={async () => false}
/>
</TooltipProvider>
)

const image = screen.getByRole("button", { name: "Image" })
await user.hover(image)
expect(
await screen.findByText("Needs a chat model and an image model.")
).toBeTruthy()
await user.unhover(image)
const summary = screen.getByRole("button", { name: "Summary" })
await user.hover(summary)
expect(await screen.findByText("Needs a chat model.")).toBeTruthy()
})

it("shows an explanation tooltip on an available artifact", async () => {
vi.stubGlobal(
"fetch",
Expand Down
53 changes: 10 additions & 43 deletions surfsense_local/frontend/src/features/studio/studio-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip"
import type { WorkspaceDocument } from "@/features/sources/api"
import { cn } from "@/lib/utils"
import type { ModelType } from "@/features/models/model-type"
import { intl } from "@/i18n/intl"
import { cn } from "@/lib/utils"

import type { StudioFormat, StudioJobCreate } from "./api"
import { PodcastBriefForm } from "./podcast-brief-form"
Expand Down Expand Up @@ -105,49 +104,17 @@ function catalogFormats(formats: StudioFormat[]) {
return catalog.map((entry) => loaded.get(entry.key) ?? entry)
}

// Only for the catalog painted before the API answers; the backend's own
// reason replaces it.
const NEEDED_MODEL: Record<ModelType, () => string> = {
text_gen: () =>
intl.formatMessage({
id: "studio_format_needs_chat_model_label",
defaultMessage: "a chat model",
}),
image_gen: () =>
intl.formatMessage({
id: "studio_format_needs_image_model_label",
defaultMessage: "an image model",
}),
image_edit: () =>
intl.formatMessage({
id: "studio_format_needs_image_edit_model_label",
defaultMessage: "an image editing model",
}),
video_gen: () =>
intl.formatMessage({
id: "studio_format_needs_video_model_label",
defaultMessage: "a video model",
}),
audio_gen: () =>
intl.formatMessage({
id: "studio_format_needs_audio_model_label",
defaultMessage: "an audio model",
}),
}

function unavailableReason(entry: StudioFormat) {
if (entry.unavailable_reason != null) return entry.unavailable_reason
const models = intl.formatList(
entry.requires_model_types.map((type) => NEEDED_MODEL[type]()),
{ type: "conjunction" }
)
return intl.formatMessage(
{
id: "studio_format_unavailable_tooltip",
defaultMessage: "Needs {models}",
},
{ models }
)
return entry.requires_model_types.includes("image_gen")
? intl.formatMessage({
id: "studio_format_unavailable_chat_image_tooltip",
defaultMessage: "Needs a chat model and an image model.",
})
: intl.formatMessage({
id: "studio_format_unavailable_chat_tooltip",
defaultMessage: "Needs a chat model.",
})
}

function formatHint(entry: StudioFormat) {
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "Infografik mit KI aus deinen Quellen erstellen",
"studio_format_mindmap_label": "Mindmap",
"studio_format_mindmap_tooltip": "Mindmap mit KI aus deinen Quellen erstellen",
"studio_format_needs_audio_model_label": "ein Audiomodell",
"studio_format_needs_chat_model_label": "ein Chatmodell",
"studio_format_needs_image_edit_model_label": "ein Bildbearbeitungsmodell",
"studio_format_needs_image_model_label": "ein Bildmodell",
"studio_format_needs_video_model_label": "ein Videomodell",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "PDF mit KI aus deinen Quellen erstellen",
"studio_format_podcast_label": "Podcast",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "Interaktives Quiz mit KI aus deinen Quellen erstellen",
"studio_format_summary_label": "Zusammenfassung",
"studio_format_summary_tooltip": "Zusammenfassung mit KI aus deinen Quellen erstellen",
"studio_format_unavailable_tooltip": "Benötigt {models}",
"studio_format_unavailable_chat_image_tooltip": "Ein Chatmodell und ein Bildmodell sind erforderlich.",
"studio_format_unavailable_chat_tooltip": "Ein Chatmodell ist erforderlich.",
"studio_format_xlsx_label": "Tabelle",
"studio_format_xlsx_tooltip": "Tabelle mit KI aus deinen Quellen erstellen",
"studio_html_viewer_error_body": "Diese Seite kann hier nicht angezeigt werden. Lade sie herunter, um sie zu öffnen.",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "Generate an AI infographic based on your sources",
"studio_format_mindmap_label": "Mind map",
"studio_format_mindmap_tooltip": "Generate an AI mind map based on your sources",
"studio_format_needs_audio_model_label": "an audio model",
"studio_format_needs_chat_model_label": "a chat model",
"studio_format_needs_image_edit_model_label": "an image editing model",
"studio_format_needs_image_model_label": "an image model",
"studio_format_needs_video_model_label": "a video model",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "Generate an AI PDF based on your sources",
"studio_format_podcast_label": "Podcast",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "Generate an AI interactive quiz based on your sources",
"studio_format_summary_label": "Summary",
"studio_format_summary_tooltip": "Generate an AI summary based on your sources",
"studio_format_unavailable_tooltip": "Needs {models}",
"studio_format_unavailable_chat_image_tooltip": "Needs a chat model and an image model.",
"studio_format_unavailable_chat_tooltip": "Needs a chat model.",
"studio_format_xlsx_label": "Spreadsheet",
"studio_format_xlsx_tooltip": "Generate an AI spreadsheet based on your sources",
"studio_html_viewer_error_body": "This page can’t be previewed here. Download it to open it.",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "Genera una infografía con IA a partir de tus fuentes",
"studio_format_mindmap_label": "Mapa mental",
"studio_format_mindmap_tooltip": "Genera un mapa mental con IA a partir de tus fuentes",
"studio_format_needs_audio_model_label": "un modelo de audio",
"studio_format_needs_chat_model_label": "un modelo de chat",
"studio_format_needs_image_edit_model_label": "un modelo de edición de imágenes",
"studio_format_needs_image_model_label": "un modelo de imagen",
"studio_format_needs_video_model_label": "un modelo de video",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "Genera un PDF con IA a partir de tus fuentes",
"studio_format_podcast_label": "Podcast",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "Genera un cuestionario interactivo con IA a partir de tus fuentes",
"studio_format_summary_label": "Resumen",
"studio_format_summary_tooltip": "Genera un resumen con IA a partir de tus fuentes",
"studio_format_unavailable_tooltip": "Necesita {models}",
"studio_format_unavailable_chat_image_tooltip": "Se necesitan un modelo de chat y uno de imagen.",
"studio_format_unavailable_chat_tooltip": "Se necesita un modelo de chat.",
"studio_format_xlsx_label": "Hoja de cálculo",
"studio_format_xlsx_tooltip": "Genera una hoja de cálculo con IA a partir de tus fuentes",
"studio_html_viewer_error_body": "Esta página no se puede previsualizar aquí. Descárgala para abrirla.",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "Générer une infographie par IA à partir de vos sources",
"studio_format_mindmap_label": "Carte mentale",
"studio_format_mindmap_tooltip": "Générer une carte mentale par IA à partir de vos sources",
"studio_format_needs_audio_model_label": "un modèle audio",
"studio_format_needs_chat_model_label": "un modèle de chat",
"studio_format_needs_image_edit_model_label": "un modèle de modification d’images",
"studio_format_needs_image_model_label": "un modèle d’image",
"studio_format_needs_video_model_label": "un modèle vidéo",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "Générer un PDF par IA à partir de vos sources",
"studio_format_podcast_label": "Podcast",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "Générer un quiz interactif par IA à partir de vos sources",
"studio_format_summary_label": "Résumé",
"studio_format_summary_tooltip": "Générer un résumé par IA à partir de vos sources",
"studio_format_unavailable_tooltip": "Nécessite {models}",
"studio_format_unavailable_chat_image_tooltip": "Un modèle de chat et un modèle d’image sont requis.",
"studio_format_unavailable_chat_tooltip": "Un modèle de chat est requis.",
"studio_format_xlsx_label": "Feuille de calcul",
"studio_format_xlsx_tooltip": "Générer une feuille de calcul par IA à partir de vos sources",
"studio_html_viewer_error_body": "Cette page ne peut pas être prévisualisée ici. Téléchargez-la pour l’ouvrir.",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/hi.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "अपने स्रोतों से AI इन्फ़ोग्राफ़िक जनरेट करें",
"studio_format_mindmap_label": "माइंड मैप",
"studio_format_mindmap_tooltip": "अपने स्रोतों से AI माइंड मैप जनरेट करें",
"studio_format_needs_audio_model_label": "ऑडियो मॉडल",
"studio_format_needs_chat_model_label": "चैट मॉडल",
"studio_format_needs_image_edit_model_label": "इमेज एडिटिंग मॉडल",
"studio_format_needs_image_model_label": "इमेज मॉडल",
"studio_format_needs_video_model_label": "वीडियो मॉडल",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "अपने स्रोतों से AI PDF जनरेट करें",
"studio_format_podcast_label": "पॉडकास्ट",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "अपने स्रोतों से AI इंटरैक्टिव क्विज़ जनरेट करें",
"studio_format_summary_label": "सारांश",
"studio_format_summary_tooltip": "अपने स्रोतों से AI सारांश जनरेट करें",
"studio_format_unavailable_tooltip": "{models} चाहिए",
"studio_format_unavailable_chat_image_tooltip": "एक चैट मॉडल और एक इमेज मॉडल चाहिए।",
"studio_format_unavailable_chat_tooltip": "एक चैट मॉडल चाहिए।",
"studio_format_xlsx_label": "स्प्रेडशीट",
"studio_format_xlsx_tooltip": "अपने स्रोतों से AI स्प्रेडशीट जनरेट करें",
"studio_html_viewer_error_body": "इस पेज का प्रीव्यू यहां नहीं दिखाया जा सकता। इसे खोलने के लिए डाउनलोड करें।",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "ソースをもとにAIでインフォグラフィックを生成",
"studio_format_mindmap_label": "マインドマップ",
"studio_format_mindmap_tooltip": "ソースをもとにAIでマインドマップを生成",
"studio_format_needs_audio_model_label": "音声モデル",
"studio_format_needs_chat_model_label": "チャットモデル",
"studio_format_needs_image_edit_model_label": "画像編集モデル",
"studio_format_needs_image_model_label": "画像モデル",
"studio_format_needs_video_model_label": "動画モデル",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "ソースをもとにAIでPDFを生成",
"studio_format_podcast_label": "ポッドキャスト",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "ソースをもとにAIでインタラクティブなクイズを生成",
"studio_format_summary_label": "要約",
"studio_format_summary_tooltip": "ソースをもとにAIで要約を生成",
"studio_format_unavailable_tooltip": "{models}が必要です",
"studio_format_unavailable_chat_image_tooltip": "チャットモデルと画像モデルが必要です。",
"studio_format_unavailable_chat_tooltip": "チャットモデルが必要です。",
"studio_format_xlsx_label": "スプレッドシート",
"studio_format_xlsx_tooltip": "ソースをもとにAIでスプレッドシートを生成",
"studio_html_viewer_error_body": "このページはここではプレビューできません。ダウンロードして開いてください。",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "소스를 바탕으로 AI 인포그래픽 생성",
"studio_format_mindmap_label": "마인드맵",
"studio_format_mindmap_tooltip": "소스를 바탕으로 AI 마인드맵 생성",
"studio_format_needs_audio_model_label": "음성 모델",
"studio_format_needs_chat_model_label": "채팅 모델",
"studio_format_needs_image_edit_model_label": "이미지 편집 모델",
"studio_format_needs_image_model_label": "이미지 모델",
"studio_format_needs_video_model_label": "동영상 모델",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "소스를 바탕으로 AI PDF 생성",
"studio_format_podcast_label": "팟캐스트",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "소스를 바탕으로 AI 인터랙티브 퀴즈 생성",
"studio_format_summary_label": "요약",
"studio_format_summary_tooltip": "소스를 바탕으로 AI 요약 생성",
"studio_format_unavailable_tooltip": "{models} 필요",
"studio_format_unavailable_chat_image_tooltip": "채팅 모델과 이미지 모델이 필요합니다.",
"studio_format_unavailable_chat_tooltip": "채팅 모델이 필요합니다.",
"studio_format_xlsx_label": "스프레드시트",
"studio_format_xlsx_tooltip": "소스를 바탕으로 AI 스프레드시트 생성",
"studio_html_viewer_error_body": "이 페이지는 여기에서 미리 볼 수 없습니다. 다운로드해서 여세요.",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "Gerar um infográfico com IA a partir das suas fontes",
"studio_format_mindmap_label": "Mapa mental",
"studio_format_mindmap_tooltip": "Gerar um mapa mental com IA a partir das suas fontes",
"studio_format_needs_audio_model_label": "um modelo de áudio",
"studio_format_needs_chat_model_label": "um modelo de chat",
"studio_format_needs_image_edit_model_label": "um modelo de edição de imagem",
"studio_format_needs_image_model_label": "um modelo de imagem",
"studio_format_needs_video_model_label": "um modelo de vídeo",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "Gerar um PDF com IA a partir das suas fontes",
"studio_format_podcast_label": "Podcast",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "Gerar um quiz interativo com IA a partir das suas fontes",
"studio_format_summary_label": "Resumo",
"studio_format_summary_tooltip": "Gerar um resumo com IA a partir das suas fontes",
"studio_format_unavailable_tooltip": "Requer {models}",
"studio_format_unavailable_chat_image_tooltip": "São necessários um modelo de chat e um modelo de imagem.",
"studio_format_unavailable_chat_tooltip": "É necessário um modelo de chat.",
"studio_format_xlsx_label": "Planilha",
"studio_format_xlsx_tooltip": "Gerar uma planilha com IA a partir das suas fontes",
"studio_html_viewer_error_body": "Não é possível visualizar esta página aqui. Baixe-a para abrir.",
Expand Down
8 changes: 2 additions & 6 deletions surfsense_local/frontend/translations/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,6 @@
"studio_format_infographic_tooltip": "Создать с помощью ИИ инфографику на основе ваших источников",
"studio_format_mindmap_label": "Интеллект-карта",
"studio_format_mindmap_tooltip": "Создать с помощью ИИ интеллект-карту на основе ваших источников",
"studio_format_needs_audio_model_label": "модель для аудио",
"studio_format_needs_chat_model_label": "модель для чата",
"studio_format_needs_image_edit_model_label": "модель для редактирования изображений",
"studio_format_needs_image_model_label": "модель для изображений",
"studio_format_needs_video_model_label": "модель для видео",
"studio_format_pdf_label": "PDF",
"studio_format_pdf_tooltip": "Создать с помощью ИИ PDF на основе ваших источников",
"studio_format_podcast_label": "Подкаст",
Expand All @@ -673,7 +668,8 @@
"studio_format_quiz_tooltip": "Создать с помощью ИИ интерактивный тест на основе ваших источников",
"studio_format_summary_label": "Резюме",
"studio_format_summary_tooltip": "Создать с помощью ИИ резюме на основе ваших источников",
"studio_format_unavailable_tooltip": "Требуется {models}",
"studio_format_unavailable_chat_image_tooltip": "Требуются модель для чата и модель для изображений.",
"studio_format_unavailable_chat_tooltip": "Требуется модель для чата.",
"studio_format_xlsx_label": "Таблица",
"studio_format_xlsx_tooltip": "Создать с помощью ИИ таблицу на основе ваших источников",
"studio_html_viewer_error_body": "Эту страницу нельзя просмотреть здесь. Скачайте её, чтобы открыть.",
Expand Down
Loading
Loading