Skip to content

feat(i18n): add pt-BR support and extract hardcoded strings#142

Open
R-Hart80 wants to merge 2 commits into
CatholicOS:devfrom
R-Hart80:feat/i18n-pt-br-translations
Open

feat(i18n): add pt-BR support and extract hardcoded strings#142
R-Hart80 wants to merge 2 commits into
CatholicOS:devfrom
R-Hart80:feat/i18n-pt-br-translations

Conversation

@R-Hart80
Copy link
Copy Markdown
Contributor

@R-Hart80 R-Hart80 commented Apr 13, 2026

Summary

  • Add pt (Brazilian Portuguese) locale to SUPPORTED_LOCALES in lib/i18n/request.ts
  • Expand messages/en.json with new keys: nav, projects, connection, language, auth
  • Add messages/pt.json with full pt-BR translations for all namespaces
  • Migrate header.tsx, user-menu.tsx, ConnectionStatus.tsx and app/page.tsx to use useTranslations(), replacing all hardcoded UI strings
  • Add LanguageSwitcher component (uses useLocale() for SSR-safe locale detection) in the header

Closes #102

Test plan

  • Run npm run dev and confirm the app loads in English by default
  • Switch language to Português via the globe icon in the header
  • Confirm navigation labels, page titles, filters and buttons update to Portuguese
  • Refresh the page and confirm the selected language persists (cookie-based)
  • Run npm run build and confirm TypeScript passes with no errors

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added multi-language support with English and Portuguese locales
    • Introduced language switcher in the header for easy locale switching
  • Localization

    • Migrated all UI text across components to use translations
    • Added Portuguese language translations for all user-facing strings
    • Enabled dynamic language selection with persistent locale preferences

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

This 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.

Changes

Multi-language i18n Implementation

Layer / File(s) Summary
i18n Infrastructure and Translation Resources
lib/i18n/request.ts, messages/en.json, messages/pt.json
SUPPORTED_LOCALES expanded to ["en", "pt"] to enable Portuguese locale detection and routing. English message file updated with new common, auth, nav, language, connection, and projects namespaces. Complete Portuguese translation file added covering all UI strings and message templates.
Language Switcher Component
components/ui/LanguageSwitcher.tsx
New client-side component displaying locale dropdown (with globe icon), reading current locale via useLocale(), updating NEXT_LOCALE cookie on selection (with path=/, max-age=31536000, SameSite=Lax), and triggering router.refresh() to apply new locale.
Header and Navigation Translation
components/layout/header.tsx
Nav links moved from hardcoded constant to component-scoped array with labels derived from useTranslations("nav"). LanguageSwitcher inserted into header's right-side action area.
Authentication UI Translation
components/auth/user-menu.tsx
UserMenu now sources sign-in button label, avatar alt fallback, "Settings" link, and "Sign out" button from useTranslations("auth") instead of hardcoded strings.
Connection Status UI Translation
components/ui/ConnectionStatus.tsx
StateConfig entries refactored to store labelKey identifiers (including "notAvailable" for disabled state). Tooltip and label rendering now derive translated text from useTranslations("connection") with templated purpose and endpoint fields.
Projects Page Translation
app/page.tsx
Page header, filter tabs ("mine/public/private/all"), search placeholder, auth-conditional subtitle, sign-in prompts, error/empty-state messaging, retry/load-more labels, and search result count/query strings all replaced with translation keys via useTranslations scoped to projects, common, and auth. Load-more error handler adds fallback to translated "failedToLoad" message.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • CatholicOS/ontokit-web#45: Refactors multiple UI components to use next-intl translation APIs and locale switching, directly aligned with next-intl v3→v4 upgrade.
  • CatholicOS/ontokit-web#47: Modifies app/page.tsx projects-list UI; this PR wires the resulting UI text, count, and error labels to next-intl translations.

Suggested reviewers

  • damienriehl

Poem

🐰 Hoppy translations bound through every page,
English and Português now take the stage!
A language switcher hops from en to pt,
No hardcoded strings—just i18n set free! 🌍

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(i18n): add pt-BR support and extract hardcoded strings' accurately summarizes the main change: internationalization expansion with Portuguese and string extraction.
Linked Issues check ✅ Passed The PR fulfills all coded requirements from #102: hardcoded strings extracted from components and replaced with useTranslations(), English messages expanded with clear namespaces, Portuguese translation file added, LanguageSwitcher component implemented with locale detection, and next-intl routing configured.
Out of Scope Changes check ✅ Passed All changes are scope-appropriate: i18n setup, message file expansions, component translations, and LanguageSwitcher component directly support #102 objectives with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@JohnRDOrazio JohnRDOrazio changed the base branch from main to dev April 13, 2026 19:54
@JohnRDOrazio JohnRDOrazio added the enhancement New feature or request label Apr 13, 2026
@JohnRDOrazio JohnRDOrazio added this to the v0.5.0 milestone Apr 28, 2026
R-Hart80 and others added 2 commits May 18, 2026 08:45
- 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>
@R-Hart80 R-Hart80 force-pushed the feat/i18n-pt-br-translations branch from 37e3120 to e6041a0 Compare May 18, 2026 11:52
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Translate 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 win

Use 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 of cn().

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

📥 Commits

Reviewing files that changed from the base of the PR and between a130901 and e6041a0.

📒 Files selected for processing (8)
  • app/page.tsx
  • components/auth/user-menu.tsx
  • components/layout/header.tsx
  • components/ui/ConnectionStatus.tsx
  • components/ui/LanguageSwitcher.tsx
  • lib/i18n/request.ts
  • messages/en.json
  • messages/pt.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

i18n: extract hardcoded strings and add translations

2 participants