fix: translate dietary tags, recipe tags form, edit title, nutrition bar (v0.27.2)
These were hardcoded English strings ignoring app locale: DietaryTagPicker, the recipe tag-input label/placeholder/help/aria-label, the Edit recipe page heading, and WeeklyNutritionBar's labels/messages. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type NutritionTotals = {
|
||||
calories: number;
|
||||
@@ -45,6 +46,7 @@ type BarItem = {
|
||||
};
|
||||
|
||||
export function WeeklyNutritionBar({ weekStart }: WeeklyNutritionBarProps) {
|
||||
const t = useTranslations("mealPlan.nutritionBar");
|
||||
const [data, setData] = useState<NutritionResponse | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -60,7 +62,7 @@ export function WeeklyNutritionBar({ weekStart }: WeeklyNutritionBarProps) {
|
||||
|
||||
const bars: BarItem[] = [
|
||||
{
|
||||
label: "Calories",
|
||||
label: t("calories"),
|
||||
value: dailyAverage.calories,
|
||||
goal: goals.caloriesKcal,
|
||||
unit: "kcal",
|
||||
@@ -68,7 +70,7 @@ export function WeeklyNutritionBar({ weekStart }: WeeklyNutritionBarProps) {
|
||||
colorClass: "bg-orange-500",
|
||||
},
|
||||
{
|
||||
label: "Protein",
|
||||
label: t("protein"),
|
||||
value: dailyAverage.protein,
|
||||
goal: goals.proteinG,
|
||||
unit: "g",
|
||||
@@ -76,7 +78,7 @@ export function WeeklyNutritionBar({ weekStart }: WeeklyNutritionBarProps) {
|
||||
colorClass: "bg-blue-500",
|
||||
},
|
||||
{
|
||||
label: "Carbs",
|
||||
label: t("carbs"),
|
||||
value: dailyAverage.carbs,
|
||||
goal: goals.carbsG,
|
||||
unit: "g",
|
||||
@@ -84,7 +86,7 @@ export function WeeklyNutritionBar({ weekStart }: WeeklyNutritionBarProps) {
|
||||
colorClass: "bg-green-500",
|
||||
},
|
||||
{
|
||||
label: "Fat",
|
||||
label: t("fat"),
|
||||
value: dailyAverage.fat,
|
||||
goal: goals.fatG,
|
||||
unit: "g",
|
||||
@@ -95,7 +97,7 @@ export function WeeklyNutritionBar({ weekStart }: WeeklyNutritionBarProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<p className="text-xs text-muted-foreground">Daily average vs. your goals</p>
|
||||
<p className="text-xs text-muted-foreground">{t("dailyAverageVsGoals")}</p>
|
||||
{bars.map((bar) => {
|
||||
if (bar.goal == null) return null;
|
||||
const width = Math.min(bar.percentage, 100);
|
||||
@@ -118,7 +120,7 @@ export function WeeklyNutritionBar({ weekStart }: WeeklyNutritionBarProps) {
|
||||
})}
|
||||
{unknownCount > 0 && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{unknownCount} planned {unknownCount === 1 ? "meal" : "meals"} missing nutrition data — estimate it from the recipe page for a more accurate picture.
|
||||
{t(unknownCount === 1 ? "missingDataSingular" : "missingDataPlural", { count: unknownCount })}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type DietaryTags = {
|
||||
vegan?: boolean;
|
||||
vegetarian?: boolean;
|
||||
@@ -10,15 +12,7 @@ type DietaryTags = {
|
||||
kosher?: boolean;
|
||||
};
|
||||
|
||||
const TAG_LABELS: Record<keyof DietaryTags, string> = {
|
||||
vegan: "Vegan",
|
||||
vegetarian: "Vegetarian",
|
||||
glutenFree: "Gluten-free",
|
||||
dairyFree: "Dairy-free",
|
||||
nutFree: "Nut-free",
|
||||
halal: "Halal",
|
||||
kosher: "Kosher",
|
||||
};
|
||||
const TAG_KEYS: (keyof DietaryTags)[] = ["vegan", "vegetarian", "glutenFree", "dairyFree", "nutFree", "halal", "kosher"];
|
||||
|
||||
export function DietaryTagPicker({
|
||||
value,
|
||||
@@ -27,9 +21,11 @@ export function DietaryTagPicker({
|
||||
value: DietaryTags;
|
||||
onChange: (tags: DietaryTags) => void;
|
||||
}) {
|
||||
const t = useTranslations("recipe.dietary");
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{(Object.entries(TAG_LABELS) as [keyof DietaryTags, string][]).map(([key, label]) => {
|
||||
{TAG_KEYS.map((key) => {
|
||||
const label = t(key);
|
||||
const active = !!value[key];
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -431,7 +431,7 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
|
||||
|
||||
{/* Tags */}
|
||||
<div className="space-y-2">
|
||||
<Label>Tags</Label>
|
||||
<Label>{t("tags")}</Label>
|
||||
<div
|
||||
className="flex flex-wrap gap-1.5 min-h-[36px] rounded-lg border border-input bg-transparent px-2.5 py-1.5 cursor-text focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50"
|
||||
onClick={() => tagInputRef.current?.focus()}
|
||||
@@ -447,7 +447,7 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
|
||||
type="button"
|
||||
onClick={(e) => { e.stopPropagation(); removeTag(tag); }}
|
||||
className="hover:text-foreground transition-colors"
|
||||
aria-label={`Remove tag ${tag}`}
|
||||
aria-label={t("removeTagAriaLabel", { tag })}
|
||||
>
|
||||
<X className="h-2.5 w-2.5" />
|
||||
</button>
|
||||
@@ -460,12 +460,12 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
|
||||
onChange={(e) => setTagInput(e.target.value)}
|
||||
onKeyDown={handleTagKeyDown}
|
||||
onBlur={() => { if (tagInput.trim()) addTag(tagInput); }}
|
||||
placeholder={tags.length === 0 ? "Add tags… (press Enter)" : ""}
|
||||
placeholder={tags.length === 0 ? t("tagsPlaceholder") : ""}
|
||||
className="flex-1 min-w-[120px] bg-transparent text-sm outline-none placeholder:text-muted-foreground"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">Press Enter to add · Backspace to remove last · max 20 tags</p>
|
||||
<p className="text-xs text-muted-foreground">{t("tagsHelp")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user