(
{c.name}
),
},
{
key: "rank",
- label: "Rank",
+ label: t("comparisonTable.rank"),
render: (_c, index) =>
,
},
{
key: "weight",
- label: "Weight",
+ label: t("comparisonTable.weight"),
render: (c) => {
const isBest = c.id === bestWeightId;
const delta = weightDeltas[c.id];
@@ -176,7 +177,7 @@ export function ComparisonTable({
},
{
key: "price",
- label: "Price",
+ label: t("comparisonTable.price"),
render: (c) => {
const isBest = c.id === bestPriceId;
const delta = priceDeltas[c.id];
@@ -202,14 +203,14 @@ export function ComparisonTable({
},
{
key: "status",
- label: "Status",
+ label: t("comparisonTable.status"),
render: (c) => (
{STATUS_LABELS[c.status]}
),
},
{
key: "link",
- label: "Link",
+ label: t("comparisonTable.link"),
render: (c) =>
c.productUrl ? (
) : (
—
@@ -225,7 +226,7 @@ export function ComparisonTable({
},
{
key: "notes",
- label: "Notes",
+ label: t("comparisonTable.notes"),
render: (c) =>
c.notes ? (
{c.notes}
@@ -235,7 +236,7 @@ export function ComparisonTable({
},
{
key: "pros",
- label: "Pros",
+ label: t("comparisonTable.pros"),
render: (c) => {
if (!c.pros) return
—;
const items = c.pros.split("\n").filter((s) => s.trim() !== "");
@@ -254,7 +255,7 @@ export function ComparisonTable({
},
{
key: "cons",
- label: "Cons",
+ label: t("comparisonTable.cons"),
render: (c) => {
if (!c.cons) return
—;
const items = c.cons.split("\n").filter((s) => s.trim() !== "");
@@ -342,7 +343,7 @@ export function ComparisonTable({
<>
|
- Weight Impact
+ {t("comparisonTable.weightImpact")}
|
{candidates.map((candidate) => {
const isWinner = candidate.id === resolvedCandidateId;
@@ -362,7 +363,7 @@ export function ComparisonTable({
|
- Price Impact
+ {t("comparisonTable.priceImpact")}
|
{candidates.map((candidate) => {
const isWinner = candidate.id === resolvedCandidateId;
diff --git a/src/client/components/CreateThreadModal.tsx b/src/client/components/CreateThreadModal.tsx
index 8c1c058..d53172a 100644
--- a/src/client/components/CreateThreadModal.tsx
+++ b/src/client/components/CreateThreadModal.tsx
@@ -1,9 +1,11 @@
import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
import { useCategories } from "../hooks/useCategories";
import { useCreateThread } from "../hooks/useThreads";
import { useUIStore } from "../stores/uiStore";
export function CreateThreadModal() {
+ const { t } = useTranslation(["threads", "common"]);
const isOpen = useUIStore((s) => s.createThreadModalOpen);
const closeModal = useUIStore((s) => s.closeCreateThreadModal);
@@ -38,11 +40,11 @@ export function CreateThreadModal() {
e.preventDefault();
const trimmed = name.trim();
if (!trimmed) {
- setError("Thread name is required");
+ setError(t("create.nameRequired"));
return;
}
if (categoryId === null) {
- setError("Please select a category");
+ setError(t("create.selectCategory"));
return;
}
setError(null);
@@ -55,7 +57,7 @@ export function CreateThreadModal() {
},
onError: (err) => {
setError(
- err instanceof Error ? err.message : "Failed to create thread",
+ err instanceof Error ? err.message : t("create.createFailed"),
);
},
},
@@ -77,7 +79,7 @@ export function CreateThreadModal() {
onClick={(e) => e.stopPropagation()}
onKeyDown={() => {}}
>
- New Thread
+ {t("create.title")}
diff --git a/src/client/components/StatusBadge.tsx b/src/client/components/StatusBadge.tsx
index bb0b60c..e887730 100644
--- a/src/client/components/StatusBadge.tsx
+++ b/src/client/components/StatusBadge.tsx
@@ -1,13 +1,14 @@
import { useEffect, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
import { LucideIcon } from "../lib/iconData";
-const STATUS_CONFIG = {
- researching: { icon: "search", label: "Researching" },
- ordered: { icon: "truck", label: "Ordered" },
- arrived: { icon: "check", label: "Arrived" },
+const STATUS_ICONS = {
+ researching: "search",
+ ordered: "truck",
+ arrived: "check",
} as const;
-type CandidateStatus = keyof typeof STATUS_CONFIG;
+type CandidateStatus = keyof typeof STATUS_ICONS;
interface StatusBadgeProps {
status: CandidateStatus;
@@ -15,10 +16,15 @@ interface StatusBadgeProps {
}
export function StatusBadge({ status, onStatusChange }: StatusBadgeProps) {
+ const { t } = useTranslation("threads");
const [isOpen, setIsOpen] = useState(false);
const containerRef = useRef(null);
- const config = STATUS_CONFIG[status];
+ const STATUS_LABELS: Record = {
+ researching: t("statusBadge.researching"),
+ ordered: t("statusBadge.ordered"),
+ arrived: t("statusBadge.arrived"),
+ };
useEffect(() => {
if (!isOpen) return;
@@ -56,14 +62,13 @@ export function StatusBadge({ status, onStatusChange }: StatusBadgeProps) {
}}
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-600 hover:bg-gray-200 transition-colors cursor-pointer"
>
-
- {config.label}
+
+ {STATUS_LABELS[status]}
{isOpen && (
- {(Object.keys(STATUS_CONFIG) as CandidateStatus[]).map((key) => {
- const option = STATUS_CONFIG[key];
+ {(Object.keys(STATUS_ICONS) as CandidateStatus[]).map((key) => {
const isActive = key === status;
return (