feat: implement localization support and add translations for de/es/fr#159
Conversation
- Added localization support using Lingui for BodyPartChart, ExerciseProgressionChart, ExerciseCompactCard, InsightsStrip, TimeRangeSelector, WorkoutCalendarModal, WorkoutHistorySection, and HelpData. - Translated static strings to use message descriptors for better internationalization. - Enhanced user experience by providing localized messages for workout summaries, insights, and help sections. - Improved accessibility and usability for non-English speaking users.
There was a problem hiding this comment.
Sorry @isotronic, your pull request is larger than the review limit of 150000 diff characters
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
💤 Files with no reviewable changes (3)
📝 WalkthroughWalkthroughThis PR adds Lingui i18n across the app: provider and utils initialisation, lingui.config updates, populated locale message catalogs, DB-backed translation maps, and replaces hard-coded user-facing strings with ChangesLingui i18n Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
|
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/(app)/(workout)/index.tsx (1)
817-817:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalise the fallback quick-workout name.
When the input is empty, the default name is still hard-coded in English (
"Quick Workout"), so this path is not translated.Suggested fix
- const name = saveWorkoutName.trim() || "Quick Workout"; + const name = saveWorkoutName.trim() || t`Quick Workout`;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`(app)/(workout)/index.tsx at line 817, Replace the hard-coded English fallback at the assignment to const name (currently using saveWorkoutName.trim() || "Quick Workout") with a localized fallback by calling the project's translation helper (e.g., useTranslations or t) so the fallback string comes from your locale keys (for example: saveWorkoutName.trim() || t('workout.quickName')); also add the corresponding key ("workout.quickName") to the locale files. Ensure you import the translation hook/function at the top of the file and use it where const name is computed.app/(app)/(tabs)/(stats)/history-details.tsx (1)
203-214:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse Lingui plural forms for count labels
app/(app)/(tabs)/(stats)/history-details.tsxhardcodesmins/sets/Repsalongside interpolated counts (e.g.,Math.round(workout.duration / 60)} mins,workout.total_sets_completed} sets,{set.reps} Reps), which will be grammatically incorrect for singular vs plural in many locales. Switch these to Lingui pluralisation usingplural(value, { one: ..., other: ... })from@lingui/core/macro(same approach already used inapp/(app)/(workout)/workout-summary.tsx).Update: lines 203-214, 269-270, 277-279, 283-287.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`(app)/(tabs)/(stats)/history-details.tsx around lines 203 - 214, Replace the hardcoded count labels in history-details.tsx with Lingui plural forms: import plural from '`@lingui/core/macro`' and use plural(...) for duration, sets, and reps instead of raw strings. Specifically, replace Trans>{Math.round(workout.duration / 60)} mins</Trans> with a plural(Math.round(workout.duration / 60), { one: "1 min", other: "# mins" }) wrapped in ThemedText/Trans as used elsewhere; replace ThemedText showing workout.total_sets_completed with plural(workout.total_sets_completed, { one: "1 set", other: "# sets" }); and replace occurrences of {set.reps} Reps with plural(set.reps, { one: "1 rep", other: "# reps" }). Keep the existing ThemedText/Trans components and styles (e.g., summaryText, summaryItem) and mirror the plural usage pattern from workout-summary.tsx.
🧹 Nitpick comments (2)
components/WorkoutCard.tsx (1)
259-260: ⚡ Quick winUse Lingui pluralisation for set count.
"${item.sets.length} Sets"produces incorrect singular output (1 Sets) and limits translator control.💡 Suggested fix
-import { t } from "`@lingui/core/macro`"; +import { t, plural } from "`@lingui/core/macro`"; ... - ? t`${item.sets.length} Sets` + ? t`${item.sets.length} ${plural(item.sets.length, { + one: "Set", + other: "Sets", + })}` : t`No Sets Available`}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/WorkoutCard.tsx` around lines 259 - 260, Replace the hard-coded string interpolation that renders set counts ("${item.sets.length} Sets") with Lingui pluralization so translators control singular/plural and formatting; update the render in WorkoutCard (the expression using item.sets.length) to use Lingui's plural/ICU plural syntax (e.g., the t`{count, plural, one {...} other {...}}` or plural(count, {...}) helper) and pass item.sets.length as the count so "1 Set" vs "2 Sets" is produced correctly.components/charts/ExerciseProgressionChart.tsx (1)
377-383: ⚡ Quick winAvoid fragment-based localisation for the
additionalInfosentence.Building this line from multiple
tfragments (assistance,x ... reps,for ...s) restricts translators from reordering grammar naturally in de/es/fr.💡 Suggested direction
-<ThemedText style={styles.additionalInfo}> - {latestWeight !== undefined && - `${latestWeight.toFixed(1)}${weightUnitLabel} `} - {exercise.tracking_type === "assisted" && t`assistance `} - {latestSet.reps !== undefined && t`x ${latestSet.reps} reps `} - {latestSet.time !== undefined && t`for ${latestSet.time}s `}( - {new Date(latestSet.date_completed).toLocaleDateString()}) -</ThemedText> +<ThemedText style={styles.additionalInfo}> + <Trans> + {latestWeight !== undefined ? `${latestWeight.toFixed(1)}${weightUnitLabel}` : ""} + {exercise.tracking_type === "assisted" ? " assistance" : ""} + {latestSet.reps !== undefined ? ` x ${latestSet.reps} reps` : ""} + {latestSet.time !== undefined ? ` for ${latestSet.time}s` : ""} ( + {new Date(latestSet.date_completed).toLocaleDateString()}) + </Trans> +</ThemedText>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/charts/ExerciseProgressionChart.tsx` around lines 377 - 383, The additionalInfo text uses multiple inline t fragments which prevents translators from reordering words; replace the concatenated fragments inside ThemedText (styles.additionalInfo) with a single localized string that accepts interpolation/placeholders (e.g. use i18n.t or the single t template) and pass values for weight, weightUnitLabel, assistance flag, reps, time and date (format date before interpolation and conditionally include parts so you pass empty strings for missing values), referencing the existing symbols latestWeight, weightUnitLabel, exercise.tracking_type, latestSet.reps, latestSet.time and latestSet.date_completed and constructing one translated sentence instead of multiple t calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/`(app)/(tabs)/(plans)/overview.tsx:
- Around line 255-256: The button label is split into two <Trans> elements which
prevents proper translation; update the JSX that checks plan?.app_plan_id so
each branch returns a single <Trans> containing the full phrase (e.g., if
plan?.app_plan_id then one <Trans> with "Customise Plan", else one <Trans> with
"Edit Plan") so translators can control word order/spacing; locate the
conditional using plan?.app_plan_id in the component and replace the two <Trans>
nodes with a single combined <Trans> per branch.
In `@app/`(app)/(tabs)/index.tsx:
- Around line 159-167: The error message currently hard-codes the subject
strings ("active plan", "settings", "completed workouts") inside the <Trans>
block; replace those literals with localized strings (e.g., use the i18n
translation function or <Trans> keys) so the branch uses
t('error.subject.activePlan') / t('error.subject.settings') /
t('error.subject.completedWorkouts') (or equivalent <Trans> keys) and keep the
rest of the message including {error?.message} unchanged; update the translation
resource keys accordingly so Trans/t(...) returns translated subjects when
rendering.
- Around line 348-351: Replace the inline conditional string inside the <Trans>
block so translators control word order: stop injecting " back" directly and
instead pass a translation key or interpolated variable that represents the
"back" variant, using the existing Trans component with a conditional i18n key
or a boolean variable (referencing the <Trans> usage, the activePlan boolean and
userName variable) so the translated string can render either a "welcome" or
"welcome_back" variant (or include an interpolation like welcome_with_return)
and still interpolate userName; update the Trans call to select the correct i18n
key or provide a named interpolation/variable rather than concatenating " back".
In `@app/`(app)/(workout)/workout-summary.tsx:
- Around line 367-369: The text " {completed} of {goal} workouts this week" can
be grammatically wrong for singular goals; update the JSX in workout-summary.tsx
(where variables completed and goal are rendered inside the <Trans> component)
to choose "workout" vs "workouts" based on the numeric goal (e.g., use a
conditional on goal === 1 to select the singular form) so the string reads "1 of
1 workout this week" when goal is 1 while keeping "workouts" for other counts;
ensure this logic lives inside the same render where completed and goal are used
so translations still wrap correctly.
In `@app/login.tsx`:
- Around line 113-116: The accessibilityLabel on the Button does not match the
visible text ("Google sign in"); update the accessibilityLabel prop on the
Button component to use the same localized string used in the button (e.g., the
Trans string or a matching i18n key) so screen readers announce "Google sign in"
consistently—locate the Button in app/login.tsx and replace
accessibilityLabel={t`Login`} with the equivalent localized "Google sign in"
string.
In `@components/PlanScheduleEditor.tsx`:
- Around line 81-83: Replace the hard-coded English pluralization for schedule
days and auto-suggest counts by using Lingui's Plural macro: import Plural from
'`@lingui/react/macro`', then replace uses that build strings with scheduledDays
(and the auto-suggest count) plus manual "s" with <Plural value={scheduledDays}
one="# day" other="# days"/> (and similarly for the auto-suggest count) inside
ThemedText or wherever the count is rendered; ensure you use "#" in the
one/other messages and remove the manual ternary plural logic (e.g., references
to scheduledDays !== 1 ? "s" : "") so all locale rules come from Plural.
In `@components/stats/ExerciseCompactCard.tsx`:
- Around line 24-26: Replace the manual English-only branching in
ExerciseCompactCard with Lingui's plural() so translations can apply correct
plural rules: update the formatDaysAgo function (the block that returns "X days
ago" / "1 week ago" / "X weeks ago") to call plural(days, { one: "1 day ago",
other: "# days ago" } or use week-based forms after dividing by 7) and similarly
replace the manual "reps" branch that returns "1 rep" vs "N reps" to use
plural(reps, { one: "1 rep", other: "# reps" }); ensure you import and use
Lingui's plural helper (and keep existing t usage for fixed strings).
In `@components/WeeklyScheduleDisplay.tsx`:
- Around line 36-51: The memoized scheduleMap currently embeds a translated
fallback (workout?.name || t`Workout`) without depending on locale, causing
stale translations; change the useMemo for scheduleMap to only map day_of_week
-> workout?.name (or undefined) and remove the t`` call from inside useMemo,
then apply the fallback t`Workout` at render-time where you read scheduleMap
values (so translation is evaluated with the current locale). Also replace the
hardcoded English pluralization (scheduledCount !== 1 ? "s" : "") with Lingui
pluralization (use the Lingui plural API or i18n._(plural(...)) / the _ helper)
when building the week summary string so plural forms are locale-safe. Ensure
you update references to scheduleMap, useMemo, workouts, scheduleEntries, and
any place using workout?.name || t`Workout` and the scheduledCount plural logic.
- Around line 78-80: The weekly summary currently hardcodes English
pluralization in WeeklyScheduleDisplay (the JSX that renders {scheduledCount}
<Trans>day{scheduledCount !== 1 ? "s" : ""}/week</Trans>); replace that with
Lingui's plural helper by importing plural from '`@lingui/macro`' and using
plural(scheduledCount, { one: 'day/week', other: 'days/week' }) (or similar
locale-aware message) inside the Trans/JSX so plural forms are generated per
locale; update the import and the JSX that references scheduledCount (e.g., in
the WeeklyScheduleDisplay component) accordingly.
In `@components/WeeklySummaryCard.tsx`:
- Around line 161-173: The value strings in formatAchievement are partially
hard-coded (e.g., "reps", the time "s" suffix and pctStr) causing mixed-language
output; update formatAchievement to localize the entire value by wrapping the
assembled strings with Lingui t (and use plural/select helpers for units like
"rep"/"reps" and "second"/"seconds") instead of concatenating raw English
tokens, and ensure pctStr is produced via a localized formatter (or included in
the t template) so references like a.exerciseName, a.thisWeekBest, a.delta,
pctStr and unit are interpolated into localized messages rather than appended as
English suffixes.
In `@components/WorkoutHistoryCard.tsx`:
- Line 58: Replace the hardcoded template string in WorkoutHistoryCard.tsx that
renders the sets chip (currently Text using {t`${setsCount} sets`}) with
Lingui's plural handling: import plural from '`@lingui/macro`' (or the project's
Lingui helper) if missing, and use plural(setsCount, { one: "# set", other: "#
sets" }) as the chip text (still inside the Text styled with styles.chipText) so
"1 set" vs "2 sets" render correctly.
In `@locales/en/messages.js`:
- Line 1: The en locale export (module.exports={messages:JSON.parse(...)}) has a
different set of message IDs than other locales causing key mismatches;
reconcile by ensuring all locale message catalogs share the exact same message
ID set: compare the parsed messages object from the en file with the messages
objects in the other locale files, add any missing keys (copying the English
string as a placeholder) or remove/flag extra keys in the other locales, and
re-run your i18n validation; locate the JSON passed to JSON.parse in
module.exports (the top-level messages object) as the target to align with other
locale files and consider scripting the diff/merge so this stays consistent.
---
Outside diff comments:
In `@app/`(app)/(tabs)/(stats)/history-details.tsx:
- Around line 203-214: Replace the hardcoded count labels in history-details.tsx
with Lingui plural forms: import plural from '`@lingui/core/macro`' and use
plural(...) for duration, sets, and reps instead of raw strings. Specifically,
replace Trans>{Math.round(workout.duration / 60)} mins</Trans> with a
plural(Math.round(workout.duration / 60), { one: "1 min", other: "# mins" })
wrapped in ThemedText/Trans as used elsewhere; replace ThemedText showing
workout.total_sets_completed with plural(workout.total_sets_completed, { one: "1
set", other: "# sets" }); and replace occurrences of {set.reps} Reps with
plural(set.reps, { one: "1 rep", other: "# reps" }). Keep the existing
ThemedText/Trans components and styles (e.g., summaryText, summaryItem) and
mirror the plural usage pattern from workout-summary.tsx.
In `@app/`(app)/(workout)/index.tsx:
- Line 817: Replace the hard-coded English fallback at the assignment to const
name (currently using saveWorkoutName.trim() || "Quick Workout") with a
localized fallback by calling the project's translation helper (e.g.,
useTranslations or t) so the fallback string comes from your locale keys (for
example: saveWorkoutName.trim() || t('workout.quickName')); also add the
corresponding key ("workout.quickName") to the locale files. Ensure you import
the translation hook/function at the top of the file and use it where const name
is computed.
---
Nitpick comments:
In `@components/charts/ExerciseProgressionChart.tsx`:
- Around line 377-383: The additionalInfo text uses multiple inline t fragments
which prevents translators from reordering words; replace the concatenated
fragments inside ThemedText (styles.additionalInfo) with a single localized
string that accepts interpolation/placeholders (e.g. use i18n.t or the single t
template) and pass values for weight, weightUnitLabel, assistance flag, reps,
time and date (format date before interpolation and conditionally include parts
so you pass empty strings for missing values), referencing the existing symbols
latestWeight, weightUnitLabel, exercise.tracking_type, latestSet.reps,
latestSet.time and latestSet.date_completed and constructing one translated
sentence instead of multiple t calls.
In `@components/WorkoutCard.tsx`:
- Around line 259-260: Replace the hard-coded string interpolation that renders
set counts ("${item.sets.length} Sets") with Lingui pluralization so translators
control singular/plural and formatting; update the render in WorkoutCard (the
expression using item.sets.length) to use Lingui's plural/ICU plural syntax
(e.g., the t`{count, plural, one {...} other {...}}` or plural(count, {...})
helper) and pass item.sets.length as the count so "1 Set" vs "2 Sets" is
produced correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 422f1611-8741-4595-b46d-f8c0f832d2f5
📒 Files selected for processing (77)
app/(app)/(create-plan)/_layout.tsxapp/(app)/(create-plan)/create-workout.tsxapp/(app)/(create-plan)/create.tsxapp/(app)/(create-plan)/exercises.tsxapp/(app)/(create-plan)/image-search.tsxapp/(app)/(create-plan)/sets-overview.tsxapp/(app)/(tabs)/(plans)/_layout.tsxapp/(app)/(tabs)/(plans)/index.tsxapp/(app)/(tabs)/(plans)/overview.tsxapp/(app)/(tabs)/(plans)/standalone-workout.tsxapp/(app)/(tabs)/(stats)/_layout.tsxapp/(app)/(tabs)/(stats)/edit-history.tsxapp/(app)/(tabs)/(stats)/exercise-detail.tsxapp/(app)/(tabs)/(stats)/exercises.tsxapp/(app)/(tabs)/(stats)/history-details.tsxapp/(app)/(tabs)/(stats)/index.tsxapp/(app)/(tabs)/_layout.tsxapp/(app)/(tabs)/index.tsxapp/(app)/(workout)/_layout.tsxapp/(app)/(workout)/exercises.tsxapp/(app)/(workout)/index.tsxapp/(app)/(workout)/workout-session.tsxapp/(app)/(workout)/workout-summary.tsxapp/(app)/+not-found.tsxapp/(app)/_layout.tsxapp/(app)/custom-exercise.tsxapp/(app)/exercise-info.tsxapp/(app)/help.tsxapp/(app)/settings.tsxapp/_layout.tsxapp/login.tsxcomponents/AppMenu.tsxcomponents/EditSetModal.tsxcomponents/ExerciseItem.tsxcomponents/ExerciseList.tsxcomponents/ExerciseTimerModal.tsxcomponents/FilterRow.tsxcomponents/Notes.tsxcomponents/Onboarding.tsxcomponents/PlanList.tsxcomponents/PlanScheduleEditor.tsxcomponents/RestDayCard.tsxcomponents/SessionSetInfo.tsxcomponents/SettingsModal.tsxcomponents/StandaloneWorkoutListItem.tsxcomponents/TrainingPlanCard.tsxcomponents/UpdateModal.tsxcomponents/WeeklyScheduleDisplay.tsxcomponents/WeeklySummaryCard.tsxcomponents/WhatsNewModal.tsxcomponents/WorkoutCard.tsxcomponents/WorkoutDetailsScreen.tsxcomponents/WorkoutDoneCard.tsxcomponents/WorkoutHistoryCard.tsxcomponents/charts/BodyPartChart.tsxcomponents/charts/ExerciseProgressionChart.tsxcomponents/stats/ExerciseCompactCard.tsxcomponents/stats/InsightsStrip.tsxcomponents/stats/TimeRangeSelector.tsxcomponents/stats/WorkoutCalendarModal.tsxcomponents/stats/WorkoutHistorySection.tsxconstants/HelpData.tsconstants/WhatsNew.tsconstants/dbTranslations.tshooks/useDeleteCompletedWorkoutMutation.tshooks/useImageManagement.tshooks/useStatsInsights.tslingui.config.tslocales/de/messages.jslocales/de/messages.polocales/en/messages.jslocales/en/messages.polocales/es/messages.jslocales/es/messages.polocales/fr/messages.jslocales/fr/messages.poutils/i18n.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/PlanScheduleEditor.tsx (1)
206-206:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse Lingui pluralisation for the "Auto-suggest" button label.
When
weeklyGoalequals 1, the current implementation renders "Auto-suggest (1 days)" with incorrect grammar. Applyplural()so the label reads correctly for all counts.🔧 Suggested fix
> - <Trans>Auto-suggest ({weeklyGoal} days)</Trans> + {plural(weeklyGoal, { + one: "Auto-suggest (# day)", + other: "Auto-suggest (# days)", + })} </Button>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/PlanScheduleEditor.tsx` at line 206, The "Auto-suggest ({weeklyGoal} days)" label uses raw interpolation and shows incorrect grammar for 1; replace it with Lingui's pluralization API by calling plural(weeklyGoal, { one: "Auto-suggest (1 day)", other: "Auto-suggest (# days)" }) (or equivalent plural macro usage) and render that instead of the current <Trans> string so the label correctly shows "day" vs "days" in PlanScheduleEditor (reference weeklyGoal and the current Trans usage).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@components/PlanScheduleEditor.tsx`:
- Line 206: The "Auto-suggest ({weeklyGoal} days)" label uses raw interpolation
and shows incorrect grammar for 1; replace it with Lingui's pluralization API by
calling plural(weeklyGoal, { one: "Auto-suggest (1 day)", other: "Auto-suggest
(# days)" }) (or equivalent plural macro usage) and render that instead of the
current <Trans> string so the label correctly shows "day" vs "days" in
PlanScheduleEditor (reference weeklyGoal and the current Trans usage).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 32413d75-b17f-4b66-a450-cea778825f4e
📒 Files selected for processing (20)
app/(app)/(tabs)/(plans)/overview.tsxapp/(app)/(tabs)/(stats)/history-details.tsxapp/(app)/(tabs)/index.tsxapp/(app)/(workout)/index.tsxapp/(app)/(workout)/workout-summary.tsxapp/login.tsxcomponents/PlanScheduleEditor.tsxcomponents/WeeklyScheduleDisplay.tsxcomponents/WeeklySummaryCard.tsxcomponents/WorkoutCard.tsxcomponents/WorkoutHistoryCard.tsxcomponents/stats/ExerciseCompactCard.tsxlocales/de/messages.jslocales/de/messages.polocales/en/messages.jslocales/en/messages.polocales/es/messages.jslocales/es/messages.polocales/fr/messages.jslocales/fr/messages.po
💤 Files with no reviewable changes (1)
- locales/fr/messages.js
✅ Files skipped from review due to trivial changes (1)
- locales/es/messages.js
Summary by CodeRabbit