fix(imports): show a readable file format in the upload hint

The upload hint interpolated the raw `accept` string, so Trakt rendered
as "Upload a .json,.zip export…". Derive a label from `accept` with a new
locale-aware `formatList` helper ("JSON or ZIP"), and wrap the source
card descriptions in `msg` so they are translatable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Opus 5.5 committed 2026-10-04 15:46:53 -04:00
1 parent 0c9ef44ea4
commit a2724abd89
15 files changed
+182 -7

No files matched your search

@@ -1,3 +1,5 @@
import type { MessageDescriptor } from "@lingui/core";
import { msg } from "@lingui/core/macro";
import { Trans, useLingui } from "@lingui/react/macro";
import { IconCloudUpload, IconLink } from "@tabler/icons-react";
import { useMutation, useQuery } from "@tanstack/react-query";
@@ -22,6 +24,7 @@ import { Spinner } from "@/components/ui/spinner";
import { getErrorMessage } from "@/lib/error-messages";
import { client, orpc } from "@/lib/orpc/client";
import type { NormalizedImport } from "@sofa/api/schemas";
import { formatList } from "@sofa/i18n/format";
// ─── Source Configs ──────────────────────────────────────────
@@ -30,12 +33,21 @@ type ImportSource = "trakt" | "simkl" | "letterboxd";
interface SourceConfig {
source: ImportSource;
label: string;
description: string;
description: MessageDescriptor;
accept: string;
icon: React.ReactNode;
supportsOAuth: boolean;
}
/** Turns an `accept` string like ".json,.zip" into a readable label like "JSON or ZIP". */
function formatAcceptLabel(accept: string): string {
const formats = accept
.split(",")
.map((ext) => ext.trim().replace(/^\./, "").toUpperCase())
.filter(Boolean);
return formatList(formats, { type: "disjunction" });
}
// ─── Provider Logos ──────────────────────────────────────────
function TraktLogo({ className }: { className?: string }) {
@@ -99,7 +111,7 @@ const SOURCES: SourceConfig[] = [
{
source: "trakt",
label: "Trakt",
description: "Connect your Trakt account or upload your Trakt export (ZIP or JSON)",
description: msg`Connect your Trakt account or upload your Trakt export (ZIP or JSON)`,
accept: ".json,.zip",
icon: <TraktLogo className="text-primary size-4" />,
supportsOAuth: true,
@@ -107,7 +119,7 @@ const SOURCES: SourceConfig[] = [
{
source: "simkl",
label: "Simkl",
description: "Connect your Simkl account or upload a JSON export",
description: msg`Connect your Simkl account or upload a JSON export`,
accept: ".json",
icon: <SimklLogo className="text-primary size-4" />,
supportsOAuth: true,
@@ -115,7 +127,7 @@ const SOURCES: SourceConfig[] = [
{
source: "letterboxd",
label: "Letterboxd",
description: "Upload the ZIP export from your Letterboxd account settings",
description: msg`Upload the ZIP export from your Letterboxd account settings`,
accept: ".zip",
icon: <LetterboxdLogo className="text-primary size-4" />,
supportsOAuth: false,
@@ -179,7 +191,7 @@ export function ImportsSection() {
// ─── Source Card ─────────────────────────────────────────────
function ImportSourceCard({ config }: { config: SourceConfig }) {
const { t } = useLingui();
const { i18n, t } = useLingui();
const { data: systemStatus } = useQuery(orpc.system.status.queryOptions());
const publicApiUrl = systemStatus?.publicApiUrl ?? "https://public-api.sofa.watch";
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -453,7 +465,7 @@ function ImportSourceCard({ config }: { config: SourceConfig }) {
</div>
<div>
<CardTitle>{config.label}</CardTitle>
<CardDescription>{config.description}</CardDescription>
<CardDescription>{i18n._(config.description)}</CardDescription>
</div>
</div>
<input
@@ -548,7 +560,7 @@ function ChooseStep({
}) {
const { t } = useLingui();
const sourceLabel = config.label;
const acceptFormat = config.accept;
const acceptFormat = formatAcceptLabel(config.accept);
return (
<>
<DialogHeader>
+4
View File
@@ -63,3 +63,7 @@ export function formatBytes(bytes: number): string {
const value = bytes / k ** i;
return `${new Intl.NumberFormat(i18n.locale, { maximumFractionDigits: 1 }).format(value)} ${sizes[i]}`;
}
export function formatList(items: string[], options?: Intl.ListFormatOptions): string {
return new Intl.ListFormat(i18n.locale, options).format(items);
}
+12
View File
@@ -750,6 +750,14 @@ msgstr "الاتصال بـ TMDB"
msgid "Connect with {sourceLabel}"
msgstr "الاتصال عبر {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "رفع صورة"
msgid "Upload profile picture"
msgstr "رفع صورة الملف الشخصي"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "تم نسخ الرابط إلى الحافظة"
+12
View File
@@ -750,6 +750,14 @@ msgstr "Mit TMDB verbinden"
msgid "Connect with {sourceLabel}"
msgstr "Mit {sourceLabel} verbinden"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "Bild hochladen"
msgid "Upload profile picture"
msgstr "Profilbild hochladen"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL in die Zwischenablage kopiert"
+12
View File
@@ -750,6 +750,14 @@ msgstr ""
msgid "Connect with {sourceLabel}"
msgstr "Connect with {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr "Connect your Simkl account or upload a JSON export"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr ""
msgid "Upload profile picture"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr "Upload the ZIP export from your Letterboxd account settings"
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr ""
+12
View File
@@ -750,6 +750,14 @@ msgstr "Conectar con TMDB"
msgid "Connect with {sourceLabel}"
msgstr "Conectar con {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "Subir foto"
msgid "Upload profile picture"
msgstr "Subir foto de perfil"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL copiada al portapapeles"
+12
View File
@@ -750,6 +750,14 @@ msgstr "Se connecter à TMDB"
msgid "Connect with {sourceLabel}"
msgstr "Se connecter avec {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "Télécharger une photo"
msgid "Upload profile picture"
msgstr "Télécharger une photo de profil"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL copiée dans le presse-papiers"
+12
View File
@@ -750,6 +750,14 @@ msgstr "התחבר אל TMDB"
msgid "Connect with {sourceLabel}"
msgstr "התחבר עם {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "העלה תמונה"
msgid "Upload profile picture"
msgstr "העלה תמונת פרופיל"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "כתובת ה-URL הועתקה ללוח"
+12
View File
@@ -750,6 +750,14 @@ msgstr "Connetti a TMDB"
msgid "Connect with {sourceLabel}"
msgstr "Connetti con {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "Carica immagine"
msgid "Upload profile picture"
msgstr "Carica immagine del profilo"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL copiato negli appunti"
+12
View File
@@ -750,6 +750,14 @@ msgstr "TMDBに接続"
msgid "Connect with {sourceLabel}"
msgstr "{sourceLabel}で接続"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "画像をアップロード"
msgid "Upload profile picture"
msgstr "プロフィール画像をアップロード"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URLをクリップボードにコピーしました"
+12
View File
@@ -750,6 +750,14 @@ msgstr "TMDB에 연결"
msgid "Connect with {sourceLabel}"
msgstr "{sourceLabel}으로 연결"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "사진 업로드"
msgid "Upload profile picture"
msgstr "프로필 사진 업로드"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL이 클립보드에 복사되었습니다"
+12
View File
@@ -750,6 +750,14 @@ msgstr "Verbinden met TMDB"
msgid "Connect with {sourceLabel}"
msgstr "Verbinden met {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "Afbeelding uploaden"
msgid "Upload profile picture"
msgstr "Profielfoto uploaden"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL gekopieerd naar klembord"
+12
View File
@@ -750,6 +750,14 @@ msgstr "Conectar ao TMDB"
msgid "Connect with {sourceLabel}"
msgstr "Conectar com {sourceLabel}"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "Enviar foto"
msgid "Upload profile picture"
msgstr "Enviar foto de perfil"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL copiada para a área de transferência"
+12
View File
@@ -750,6 +750,14 @@ msgstr "连接到 TMDB"
msgid "Connect with {sourceLabel}"
msgstr "通过 {sourceLabel} 连接"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Simkl account or upload a JSON export"
msgstr ""
#: apps/web/src/components/settings/imports-section.tsx
msgid "Connect your Trakt account or upload your Trakt export (ZIP or JSON)"
msgstr ""
#: apps/native/src/app/(auth)/server-url.tsx
#: apps/native/src/app/(tabs)/(settings)/index.tsx
#: apps/web/src/components/settings/system-health-section.tsx
@@ -3429,6 +3437,10 @@ msgstr "上传图片"
msgid "Upload profile picture"
msgstr "上传头像"
#: apps/web/src/components/settings/imports-section.tsx
msgid "Upload the ZIP export from your Letterboxd account settings"
msgstr ""
#: apps/native/src/components/settings/integration-card.tsx
msgid "URL copied to clipboard"
msgstr "URL 已复制到剪贴板"
+15
View File
@@ -7,6 +7,7 @@ vi.mock("../src/index", () => ({
import {
formatBytes,
formatDate,
formatList,
formatNumber,
formatRelativeTime,
formatShortDate,
@@ -146,3 +147,17 @@ describe("formatBytes", () => {
expect(formatBytes(1536 * 1024)).toBe("1.5 MB");
});
});
describe("formatList", () => {
test("joins items as a conjunction by default", () => {
expect(formatList(["A", "B", "C"])).toBe("A, B, and C");
});
test("supports disjunctions", () => {
expect(formatList(["ZIP", "JSON"], { type: "disjunction" })).toBe("ZIP or JSON");
});
test("returns a single item unchanged", () => {
expect(formatList(["ZIP"], { type: "disjunction" })).toBe("ZIP");
});
});