From fa3f5faa58f6548807670b16b593361e9541b5ac Mon Sep 17 00:00:00 2001 From: neodk2004 Date: Fri, 21 Aug 2026 22:33:03 +0200 Subject: [PATCH] fix(i18n): translate milestone type labels MILESTONE_META in src/lib/milestone-meta.ts held hardcoded English labels ("Birthday", "Vet Visit", ...) consumed directly by MilestoneCard and MilestoneForm -- bypassing next-intl entirely, so no de.json entry could ever have translated them (there was no key to translate, not a missing one). Adds a MilestoneTypes namespace to both message catalogs and switches the two user-facing consumers to useTranslations("MilestoneTypes") for the label, keeping MILESTONE_META only for the icon lookup. The admin posts page (p/[secret]/posts) still reads MILESTONE_META.label directly and is left as-is -- the admin panel is English-only by existing project convention, not part of the localized surface. --- messages/de.json | 18 ++++++++++++++++++ messages/en.json | 18 ++++++++++++++++++ src/components/feed/MilestoneCard.tsx | 9 ++++++--- src/components/post-creation/MilestoneForm.tsx | 7 ++++--- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/messages/de.json b/messages/de.json index 1848b8f..186c70c 100644 --- a/messages/de.json +++ b/messages/de.json @@ -488,6 +488,24 @@ "invalidType": "Lade ein JPG-, PNG- oder WebP-Bild hoch.", "uploadFailed": "Upload fehlgeschlagen — bitte versuch es erneut." }, + "MilestoneTypes": { + "BIRTHDAY": "Geburtstag", + "ADOPTION_DAY": "Adoptionstag", + "FIRST_OUTING": "Erster Ausflug", + "VET_VISIT": "Tierarztbesuch", + "LITTER": "Neuer Wurf", + "FOLLOWERS_500": "500 Follower", + "FOLLOWERS_1K": "1.000 Follower", + "FOLLOWERS_2_5K": "2.500 Follower", + "FOLLOWERS_5K": "5.000 Follower", + "FOLLOWERS_10K": "10.000 Follower", + "FOLLOWERS_25K": "25.000 Follower", + "FOLLOWERS_50K": "50.000 Follower", + "FOLLOWERS_100K": "100.000 Follower", + "FOLLOWERS_250K": "250.000 Follower", + "FOLLOWERS_500K": "500.000 Follower", + "FOLLOWERS_1M": "1.000.000 Follower" + }, "VideoUploadForm": { "selectVideo": "Video auswählen", "formatHint": "MP4, MOV oder WebM · bis 500 MB · max. 3 Minuten", diff --git a/messages/en.json b/messages/en.json index c6ab4fb..1efaa38 100644 --- a/messages/en.json +++ b/messages/en.json @@ -488,6 +488,24 @@ "invalidType": "Upload a JPG, PNG, or WebP image.", "uploadFailed": "Upload failed — please try again." }, + "MilestoneTypes": { + "BIRTHDAY": "Birthday", + "ADOPTION_DAY": "Adoption Day", + "FIRST_OUTING": "First Outing", + "VET_VISIT": "Vet Visit", + "LITTER": "New Litter", + "FOLLOWERS_500": "500 Followers", + "FOLLOWERS_1K": "1,000 Followers", + "FOLLOWERS_2_5K": "2,500 Followers", + "FOLLOWERS_5K": "5,000 Followers", + "FOLLOWERS_10K": "10,000 Followers", + "FOLLOWERS_25K": "25,000 Followers", + "FOLLOWERS_50K": "50,000 Followers", + "FOLLOWERS_100K": "100,000 Followers", + "FOLLOWERS_250K": "250,000 Followers", + "FOLLOWERS_500K": "500,000 Followers", + "FOLLOWERS_1M": "1,000,000 Followers" + }, "VideoUploadForm": { "selectVideo": "Select a video", "formatHint": "MP4, MOV or WebM · up to 500 MB · max 3 minutes", diff --git a/src/components/feed/MilestoneCard.tsx b/src/components/feed/MilestoneCard.tsx index 1f50b71..f1ccc55 100644 --- a/src/components/feed/MilestoneCard.tsx +++ b/src/components/feed/MilestoneCard.tsx @@ -1,6 +1,7 @@ "use client"; import type { MilestoneType } from "@prisma/client"; +import { useTranslations } from "next-intl"; import { MILESTONE_META } from "@/lib/milestone-meta"; import { getAvatarUrl } from "@/lib/avatar-url"; import { formatPetName } from "@/lib/pet-display-name"; @@ -40,12 +41,12 @@ function formatDate(date: Date | string): string { * Background: bg-orange-50 border border-orange-200 per UI-SPEC accent reservation #8. */ export function MilestoneCard({ post }: MilestoneCardProps) { + const t = useTranslations("MilestoneTypes"); const { pet, milestone, images, caption } = post; if (!milestone) return null; - const meta = MILESTONE_META[milestone.milestoneType]; - const Icon = meta.icon; + const Icon = MILESTONE_META[milestone.milestoneType].icon; const avatarUrl = getAvatarUrl(pet.avatarKey); const photo = images[0]; @@ -70,7 +71,9 @@ export function MilestoneCard({ post }: MilestoneCardProps) { {/* Milestone type badge */}
{/* Optional photo */} diff --git a/src/components/post-creation/MilestoneForm.tsx b/src/components/post-creation/MilestoneForm.tsx index d31880c..0e07a00 100644 --- a/src/components/post-creation/MilestoneForm.tsx +++ b/src/components/post-creation/MilestoneForm.tsx @@ -40,6 +40,7 @@ type UploadState = "idle" | "uploading" | "ready"; */ export function MilestoneForm({ onClose }: MilestoneFormProps) { const t = useTranslations("MilestoneForm"); + const tType = useTranslations("MilestoneTypes"); const trpc = useTRPC(); const router = useRouter(); const queryClient = useQueryClient(); @@ -166,10 +167,10 @@ export function MilestoneForm({ onClose }: MilestoneFormProps) { - {selectedMeta ? ( + {selectedType && selectedMeta ? ( ) : ( {t("selectPlaceholder")} @@ -191,7 +192,7 @@ export function MilestoneForm({ onClose }: MilestoneFormProps) { }} >