Improve translation fallback and scale UI
build-push / docker (push) Successful in 4m12s

This commit is contained in:
AI Assistant
2026-03-13 12:11:38 +09:00
parent 6852e07607
commit 4a51998cbd
2 changed files with 70 additions and 30 deletions
+50 -10
View File
@@ -8,6 +8,7 @@ import (
"io" "io"
"mime" "mime"
"net/http" "net/http"
neturl "net/url"
"strings" "strings"
"time" "time"
) )
@@ -201,15 +202,17 @@ func (g *GeminiService) TranslateQuery(query string) string {
} }
rawText, err := g.generateText(body) rawText, err := g.generateText(body)
if err != nil { if err == nil {
return strings.TrimSpace(query) translated := sanitizePlainEnglishLine(rawText)
if translated != "" && !strings.EqualFold(translated, strings.TrimSpace(query)) {
return translated
}
} }
translated := sanitizePlainEnglishLine(rawText) if translated, err := g.translateViaGoogle(query); err == nil && translated != "" {
if translated == "" { return translated
return strings.TrimSpace(query)
} }
return translated return strings.TrimSpace(query)
} }
func (g *GeminiService) generateText(body map[string]any) (string, error) { func (g *GeminiService) generateText(body map[string]any) (string, error) {
@@ -464,10 +467,6 @@ func fallbackQueryExpansion(originalQuery, englishQuery string) []string {
base + " 4k footage", base + " 4k footage",
base + " cinematic b-roll", base + " cinematic b-roll",
} }
if strings.TrimSpace(originalQuery) != "" && !strings.EqualFold(strings.TrimSpace(originalQuery), strings.TrimSpace(englishQuery)) {
candidates = append(candidates, strings.TrimSpace(originalQuery))
}
seen := map[string]bool{} seen := map[string]bool{}
queries := make([]string, 0, len(candidates)) queries := make([]string, 0, len(candidates))
for _, item := range candidates { for _, item := range candidates {
@@ -519,3 +518,44 @@ func looksMostlyASCII(text string) bool {
} }
return ascii >= len(runes)*8/10 return ascii >= len(runes)*8/10
} }
func (g *GeminiService) translateViaGoogle(query string) (string, error) {
endpoint := "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=en&dt=t&q=" + neturl.QueryEscape(query)
resp, err := g.Client.Get(endpoint)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return "", fmt.Errorf("google translate fallback returned status %d", resp.StatusCode)
}
var payload []any
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
return "", err
}
if len(payload) == 0 {
return "", fmt.Errorf("google translate fallback returned no payload")
}
top, ok := payload[0].([]any)
if !ok {
return "", fmt.Errorf("google translate fallback returned unexpected payload")
}
var builder strings.Builder
for _, part := range top {
segment, ok := part.([]any)
if !ok || len(segment) == 0 {
continue
}
if text, ok := segment[0].(string); ok {
builder.WriteString(text)
}
}
translated := strings.TrimSpace(builder.String())
if translated == "" {
return "", fmt.Errorf("google translate fallback returned empty translation")
}
return translated, nil
}
+20 -20
View File
@@ -8,12 +8,12 @@
<link rel="stylesheet" href="/style.css" /> <link rel="stylesheet" href="/style.css" />
</head> </head>
<body class="min-h-full bg-zinc-950 text-zinc-100 selection:bg-white selection:text-black"> <body class="min-h-full bg-zinc-950 text-zinc-100 selection:bg-white selection:text-black">
<main class="mx-auto flex min-h-screen max-w-7xl flex-col gap-6 px-4 py-6 lg:px-8"> <main class="mx-auto flex min-h-screen max-w-[1560px] flex-col gap-8 px-5 py-8 lg:px-10">
<header class="rounded-3xl border border-white/10 bg-white/5 p-6 backdrop-blur"> <header class="rounded-3xl border border-white/10 bg-white/5 p-8 backdrop-blur">
<div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between"> <div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div> <div>
<p class="text-xs uppercase tracking-[0.4em] text-zinc-500">AI Media Asset Ingest Hub</p> <p class="text-xs uppercase tracking-[0.4em] text-zinc-500">AI Media Asset Ingest Hub</p>
<h1 class="mt-3 text-3xl font-semibold tracking-tight text-white">SAVE THE NURSE AI Search</h1> <h1 class="mt-3 text-4xl font-semibold tracking-tight text-white">SAVE THE NURSE AI Search</h1>
</div> </div>
<div class="w-full max-w-md"> <div class="w-full max-w-md">
<div class="mb-2 flex items-center justify-between text-xs uppercase tracking-[0.3em] text-zinc-500"> <div class="mb-2 flex items-center justify-between text-xs uppercase tracking-[0.3em] text-zinc-500">
@@ -27,41 +27,41 @@
</div> </div>
</header> </header>
<section class="grid gap-6 lg:grid-cols-[1.2fr_0.9fr]"> <section class="grid gap-8 lg:grid-cols-[1.3fr_0.95fr]">
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-5"> <article class="rounded-3xl border border-white/10 bg-white/[0.03] p-7">
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
<div> <div>
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone A</p> <p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone A</p>
<h2 class="text-xl font-semibold text-white">AI Smart Discovery</h2> <h2 class="text-2xl font-semibold text-white">AI Smart Discovery</h2>
</div> </div>
</div> </div>
<form id="searchForm" class="flex flex-col gap-3 md:flex-row"> <form id="searchForm" class="flex flex-col gap-3 md:flex-row">
<input id="searchQuery" type="text" placeholder="한글 검색어를 입력하세요" class="flex-1 rounded-2xl border border-white/10 bg-black/40 px-4 py-3 text-sm text-white outline-none ring-0 placeholder:text-zinc-500" /> <input id="searchQuery" type="text" placeholder="한글 검색어를 입력하세요" class="flex-1 rounded-2xl border border-white/10 bg-black/40 px-5 py-4 text-base text-white outline-none ring-0 placeholder:text-zinc-500" />
<button class="rounded-2xl border border-white bg-white px-5 py-3 text-sm font-medium text-black transition hover:bg-zinc-200">AI Search</button> <button class="rounded-2xl border border-white bg-white px-7 py-4 text-base font-medium text-black transition hover:bg-zinc-200">AI Search</button>
</form> </form>
<div id="searchWarning" class="mt-3 hidden rounded-2xl border border-amber-500/30 bg-amber-500/10 px-4 py-3 text-sm text-amber-200"></div> <div id="searchWarning" class="mt-3 hidden rounded-2xl border border-amber-500/30 bg-amber-500/10 px-4 py-3 text-sm text-amber-200"></div>
<div id="queryVariants" class="mt-3 hidden flex-wrap gap-2"></div> <div id="queryVariants" class="mt-3 hidden flex-wrap gap-2"></div>
<div id="searchResults" class="mt-5 grid gap-4 sm:grid-cols-2 xl:grid-cols-3"></div> <div id="searchResults" class="mt-6 grid gap-5 sm:grid-cols-2 xl:grid-cols-3"></div>
</article> </article>
<div class="grid gap-6"> <div class="grid gap-8">
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-5"> <article class="rounded-3xl border border-white/10 bg-white/[0.03] p-7">
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone B</p> <p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone B</p>
<h2 class="text-xl font-semibold text-white">Smart Ingest Dropzone</h2> <h2 class="text-2xl font-semibold text-white">Smart Ingest Dropzone</h2>
<label id="dropzone" class="mt-4 flex min-h-64 cursor-pointer flex-col items-center justify-center rounded-3xl border border-dashed border-white/20 bg-black/30 p-6 text-center transition hover:border-white/50 hover:bg-white/[0.05]"> <label id="dropzone" class="mt-4 flex min-h-64 cursor-pointer flex-col items-center justify-center rounded-3xl border border-dashed border-white/20 bg-black/30 p-6 text-center transition hover:border-white/50 hover:bg-white/[0.05]">
<input id="fileInput" type="file" class="hidden" /> <input id="fileInput" type="file" class="hidden" />
<span class="text-lg font-medium text-white">Drop file here</span> <span class="text-2xl font-medium text-white">Drop file here</span>
<span class="mt-2 text-sm text-zinc-400">or click to upload into /app/downloads</span> <span class="mt-2 text-base text-zinc-400">or click to upload into /app/downloads</span>
</label> </label>
<p id="uploadResult" class="mt-3 text-sm text-zinc-400"></p> <p id="uploadResult" class="mt-3 text-sm text-zinc-400"></p>
</article> </article>
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-5"> <article class="rounded-3xl border border-white/10 bg-white/[0.03] p-7">
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone C</p> <p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone C</p>
<h2 class="text-xl font-semibold text-white">Direct Downloader & Crop</h2> <h2 class="text-2xl font-semibold text-white">Direct Downloader & Crop</h2>
<form id="downloadForm" class="mt-4 space-y-3"> <form id="downloadForm" class="mt-4 space-y-3">
<input id="downloadUrl" type="url" placeholder="https://..." class="w-full rounded-2xl border border-white/10 bg-black/40 px-4 py-3 text-sm text-white placeholder:text-zinc-500" /> <input id="downloadUrl" type="url" placeholder="https://..." class="w-full rounded-2xl border border-white/10 bg-black/40 px-5 py-4 text-base text-white placeholder:text-zinc-500" />
<button class="w-full rounded-2xl border border-white px-5 py-3 text-sm font-medium text-white transition hover:bg-white hover:text-black">Preview & Queue</button> <button class="w-full rounded-2xl border border-white px-6 py-4 text-base font-medium text-white transition hover:bg-white hover:text-black">Preview & Queue</button>
</form> </form>
<p id="downloadResult" class="mt-3 text-sm text-zinc-400"></p> <p id="downloadResult" class="mt-3 text-sm text-zinc-400"></p>
</article> </article>
@@ -131,8 +131,8 @@
<div class="absolute left-3 top-3 rounded-full border border-white/20 bg-black/60 px-3 py-1 text-[11px] uppercase tracking-[0.25em] text-white">AI Recommended</div> <div class="absolute left-3 top-3 rounded-full border border-white/20 bg-black/60 px-3 py-1 text-[11px] uppercase tracking-[0.25em] text-white">AI Recommended</div>
<div class="source-badge absolute bottom-3 left-3 rounded-full bg-white px-3 py-1 text-[11px] font-medium uppercase tracking-[0.2em] text-black"></div> <div class="source-badge absolute bottom-3 left-3 rounded-full bg-white px-3 py-1 text-[11px] font-medium uppercase tracking-[0.2em] text-black"></div>
</div> </div>
<div class="space-y-2 p-4"> <div class="space-y-2 p-5">
<h3 class="line-clamp-2 text-sm font-medium text-white"></h3> <h3 class="line-clamp-2 text-base font-medium text-white"></h3>
<p class="line-clamp-3 text-sm text-zinc-400"></p> <p class="line-clamp-3 text-sm text-zinc-400"></p>
</div> </div>
</a> </a>