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.
This commit is contained in:
1 parent
c1951b29b9
commit
fa3f5faa58
4 files changed
+46
-6
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 */}
|
||||
<div className="flex items-center gap-3 px-4 pb-3">
|
||||
<Icon className="h-8 w-8 text-orange-500 shrink-0" aria-hidden="true" />
|
||||
<span className="text-base font-semibold text-orange-500">{meta.label}</span>
|
||||
<span className="text-base font-semibold text-orange-500">
|
||||
{t(milestone.milestoneType)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Optional photo */}
|
||||
|
||||
@@ -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) {
|
||||
</label>
|
||||
<Popover open={pickerOpen} onOpenChange={setPickerOpen}>
|
||||
<PopoverTrigger className="flex items-center justify-between w-full min-h-[44px] px-3 py-2 rounded-md border border-input bg-background text-sm hover:bg-accent hover:text-accent-foreground transition-colors">
|
||||
{selectedMeta ? (
|
||||
{selectedType && selectedMeta ? (
|
||||
<span className="flex items-center gap-2 text-foreground">
|
||||
<selectedMeta.icon className="h-4 w-4 text-orange-500 shrink-0" aria-hidden="true" />
|
||||
{selectedMeta.label}
|
||||
{tType(selectedType)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground">{t("selectPlaceholder")}</span>
|
||||
@@ -191,7 +192,7 @@ export function MilestoneForm({ onClose }: MilestoneFormProps) {
|
||||
}}
|
||||
>
|
||||
<Icon className="h-5 w-5 text-orange-500 shrink-0" aria-hidden="true" />
|
||||
{meta.label}
|
||||
{tType(type)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in new issue
Block a user