feat(i18n): add pt-BR support and extract hardcoded strings#142
feat(i18n): add pt-BR support and extract hardcoded strings#142R-Hart80 wants to merge 2 commits into
Conversation
📝 WalkthroughWalkthroughThis PR completes the i18n implementation by adding Portuguese locale support, creating a language switcher component, and systematically replacing hardcoded UI text across five components and the main projects page with translation key references. The supporting translation resources are expanded with complete English and Portuguese message files organized by functional namespace. ChangesMulti-language i18n Implementation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Add `pt` locale to SUPPORTED_LOCALES in lib/i18n/request.ts - Expand messages/en.json with nav, projects, connection, language and auth keys - Add messages/pt.json with full Brazilian Portuguese translations - Migrate header.tsx, user-menu.tsx, ConnectionStatus.tsx and app/page.tsx to use useTranslations() replacing all hardcoded UI strings - Add LanguageSwitcher component using useLocale() for SSR-safe locale detection Closes CatholicOS#102 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Keep "Label", "Range", "Merge" in English — standard terms in the BR dev community - nav.info: "Info" instead of "Informações" to match the page name - git.diff: "Ver Mudanças" (more natural than "Ver Alterações") - projects.checkBackLater: rewrite to more natural Brazilian Portuguese Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
37e3120 to
e6041a0
Compare
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)
app/page.tsx (1)
242-245:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTranslate the remaining filtered-count suffix text.
Line 244 still hardcodes English (
of), which leaves mixed-language UI in pt-BR. Please move this suffix into translations.Suggested change
- <span className="text-slate-400 dark:text-slate-500"> - {` (of ${unfilteredTotal})`} - </span> + <span className="text-slate-400 dark:text-slate-500"> + {t("ofTotal", { total: unfilteredTotal })} + </span>// messages/en.json { "projects": { "ofTotal": "(of {total})" } }// messages/pt.json { "projects": { "ofTotal": "(de {total})" } }🤖 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/page.tsx` around lines 242 - 245, The hardcoded English suffix " (of ${unfilteredTotal})" should be moved into translations and rendered via the i18n helper instead of literal text; replace the inline fragment inside the isFiltered conditional with a translated string using the key projects.ofTotal and pass unfilteredTotal as the interpolation value (e.g., t('projects.ofTotal', { total: unfilteredTotal })) so both en/pt messages files can supply "(of {total})" and "(de {total})" respectively.
🧹 Nitpick comments (1)
components/ui/LanguageSwitcher.tsx (1)
23-29: ⚡ Quick winUse
cn()for Tailwind className composition in this component.This file matches the
components/**/*.{ts,tsx}rule, but class names are passed as raw strings instead ofcn().Proposed change
+import { cn } from "`@/lib/utils`"; ... - <div className="flex items-center gap-1.5" title={t("label")}> + <div className={cn("flex items-center gap-1.5")} title={t("label")}> ... - <select + <select value={currentLocale} onChange={(e) => handleChange(e.target.value as Locale)} - className="bg-transparent text-sm text-slate-600 dark:text-slate-400 cursor-pointer focus:outline-hidden" + className={cn( + "bg-transparent text-sm text-slate-600 dark:text-slate-400 cursor-pointer focus:outline-hidden" + )} aria-label={t("label")} >As per coding guidelines,
components/**/*.{ts,tsx}: Use cn() utility function from lib/utils.ts for Tailwind CSS class merging.🤖 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/ui/LanguageSwitcher.tsx` around lines 23 - 29, The component uses raw string className values; import and use the cn() utility from lib/utils to compose/merge Tailwind classes for the elements with className (e.g., the wrapping div, Globe component className prop if present, and the select element's className) and replace the literal strings with cn("...") calls (ensure you add the import for cn at top of LanguageSwitcher.tsx and keep the same class lists when wrapping them in cn()).
🤖 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 `@app/page.tsx`:
- Around line 242-245: The hardcoded English suffix " (of ${unfilteredTotal})"
should be moved into translations and rendered via the i18n helper instead of
literal text; replace the inline fragment inside the isFiltered conditional with
a translated string using the key projects.ofTotal and pass unfilteredTotal as
the interpolation value (e.g., t('projects.ofTotal', { total: unfilteredTotal
})) so both en/pt messages files can supply "(of {total})" and "(de {total})"
respectively.
---
Nitpick comments:
In `@components/ui/LanguageSwitcher.tsx`:
- Around line 23-29: The component uses raw string className values; import and
use the cn() utility from lib/utils to compose/merge Tailwind classes for the
elements with className (e.g., the wrapping div, Globe component className prop
if present, and the select element's className) and replace the literal strings
with cn("...") calls (ensure you add the import for cn at top of
LanguageSwitcher.tsx and keep the same class lists when wrapping them in cn()).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: aab7e5c0-2ac9-4dec-a9b1-2342a004b205
📒 Files selected for processing (8)
app/page.tsxcomponents/auth/user-menu.tsxcomponents/layout/header.tsxcomponents/ui/ConnectionStatus.tsxcomponents/ui/LanguageSwitcher.tsxlib/i18n/request.tsmessages/en.jsonmessages/pt.json
Summary
pt(Brazilian Portuguese) locale toSUPPORTED_LOCALESinlib/i18n/request.tsmessages/en.jsonwith new keys:nav,projects,connection,language,authmessages/pt.jsonwith full pt-BR translations for all namespacesheader.tsx,user-menu.tsx,ConnectionStatus.tsxandapp/page.tsxto useuseTranslations(), replacing all hardcoded UI stringsLanguageSwitchercomponent (usesuseLocale()for SSR-safe locale detection) in the headerCloses #102
Test plan
npm run devand confirm the app loads in English by defaultnpm run buildand confirm TypeScript passes with no errors🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Localization