Skip to content

feat: add next-intl internationalization with 4 locales#76

Closed
gzhang33 wants to merge 6 commits into
Anyesh:mainfrom
gzhang33:feature/i18n-work
Closed

feat: add next-intl internationalization with 4 locales#76
gzhang33 wants to merge 6 commits into
Anyesh:mainfrom
gzhang33:feature/i18n-work

Conversation

@gzhang33
Copy link
Copy Markdown

Summary

  • Integrate next-intl for full i18n support across the frontend
  • Support 4 locales: English, Chinese (zh), French (fr), Italian (it)
  • Extract all hardcoded strings to translation files (967 keys)
  • Add locale switcher component with dropdown menu
  • Migrate all pages, components, dialogs, and cards to use useTranslations()
  • Replace hardcoded toast messages, title attributes, placeholders, and inline text
  • Add use-translated-constants hook for dynamic locale-aware constants
  • Add formatWornAgo i18n helper and location error message translations

Test Plan

  • npx tsc --noEmit — zero TypeScript errors
  • npx vitest run — all 65 tests pass
  • npm run build — production build succeeds
  • Translation key parity: all 967 keys match across en/zh/fr/it
  • All 4 locale JSON files validate as valid JSON

🤖 Generated with Claude Code

apple and others added 6 commits May 12, 2026 18:23
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Integrate next-intl for full i18n support across the frontend:

- Add next-intl package and configure middleware + request config
- Create locale switcher component with dropdown menu
- Extract all hardcoded strings to translation files (967 keys)
- Support English, Chinese, French, and Italian locales
- Migrate all pages, components, dialogs, and cards to use useTranslations()
- Replace hardcoded toast messages, titles, placeholders, and inline text
- Add use-translated-constants hook for dynamic locale-aware constants
- Add formatWornAgo i18n helper and location error message translations
- Update tests with next-intl mock setup
- All 65 tests pass, TypeScript compiles clean, production build succeeds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 13, 2026 00:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces end-to-end internationalization in the Next.js frontend using next-intl (supporting en/zh/fr/it) and updates the backend weather API to support resolving saved, free-form location names via a geocoding service (with configurable User-Agent/contact metadata).

Changes:

  • Frontend: integrate next-intl (plugin + request config), add locale switching via NEXT_LOCALE cookie, and migrate UI strings to translation keys.
  • Frontend: add i18n-aware helpers/hooks (e.g., formatWornAgo, translated constants for types/colors/occasions) and expand test coverage for i18n/location helpers.
  • Backend: add Nominatim geocoding support for weather endpoints + config/test coverage for geocoding User-Agent behavior.

Reviewed changes

Copilot reviewed 71 out of 72 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
frontend/tests/utils.test.ts Adds unit tests for formatWornAgo (and keeps existing cn tests).
frontend/tests/setup.ts Mocks next-intl / next-intl/server for frontend tests.
frontend/tests/location.test.ts Adds tests for new frontend location helper module.
frontend/package.json Adds next-intl dependency.
frontend/next.config.js Wraps Next config with next-intl plugin.
frontend/lib/utils.ts Updates formatWornAgo to be translation-function driven.
frontend/lib/location.ts Introduces reusable helpers for network/reverse-geocoded locations + i18n error mapping.
frontend/lib/hooks/use-translated-constants.ts Adds hooks returning translated clothing types/colors/occasions.
frontend/i18n/request.ts Adds next-intl request config (locale resolution + message loading).
frontend/Dockerfile Copies messages/ into the runtime image.
frontend/components/ui/dropdown-menu.tsx Adds dropdown menu UI wrapper (used by locale switcher).
frontend/components/studio/details-panel.tsx Migrates strings/toasts/warnings to translations in studio details panel.
frontend/components/studio/canvas-panel.tsx Migrates canvas empty state + aria-labels to translations.
frontend/components/sidebar.tsx Translates sidebar navigation labels + brand alt/name.
frontend/components/shared/occasion-chips.tsx Uses translated occasions instead of hardcoded constants.
frontend/components/shared/lineage-card.tsx Translates lineage card text with/without date.
frontend/components/shared/item-picker.tsx Translates search/loading/empty states + count labels.
frontend/components/shared/clone-to-lookbook-dialog.tsx Translates dialog text and toast messages.
frontend/components/pairing-card.tsx Translates pairing card labels and toast messages.
frontend/components/pagination.tsx Translates aria-labels and “Page X of Y”.
frontend/components/outfits/outfit-card.tsx Translates source badge labels + fallback title/meta labels.
frontend/components/outfit-preview-dialog.tsx Translates dialog UI strings, titles, tooltips, and toasts.
frontend/components/outfit-history-card.tsx Translates history card UI strings and toasts.
frontend/components/outfit-calendar.tsx Translates legend/source badges.
frontend/components/offline-indicator.tsx Translates offline indicator message.
frontend/components/mobile-sidebar.tsx Translates mobile sidebar nav/labels and brand strings.
frontend/components/mobile-nav.tsx Translates bottom mobile nav labels.
frontend/components/locale-switcher.tsx Adds locale switcher that sets NEXT_LOCALE cookie and reloads.
frontend/components/image-lightbox.tsx Translates lightbox action label.
frontend/components/header.tsx Adds locale switcher to header + translates aria-labels/fallbacks.
frontend/components/generate-pairings-dialog.tsx Translates dialog strings and success messaging for generated pairings.
frontend/components/feedback-dialog.tsx Translates multi-step feedback dialog UI/toasts.
frontend/components/family-ratings.tsx Translates family rating form/display strings and toasts.
frontend/components/color-eyedropper.tsx Translates eyedropper dialog strings; uses translated color names.
frontend/components/bulk-action-toolbar.tsx Translates bulk selection labels and delete confirmation copy.
frontend/components/add-item-dialog.tsx Translates add/bulk upload dialog; uses translated type/color constants.
frontend/app/page.tsx Migrates landing page to server-side getTranslations.
frontend/app/not-found.tsx Migrates 404 page to server-side getTranslations.
frontend/app/login/page.tsx Migrates login UI + error messages to translations.
frontend/app/layout.tsx Wraps app with NextIntlClientProvider, sets <html lang> dynamically.
frontend/app/invite/page.tsx Migrates invite page text + error mapping to translations.
frontend/app/error.tsx Migrates global error page text/buttons to translations.
frontend/app/dashboard/wardrobe/page.tsx Migrates wardrobe page strings (filters, labels, toasts) to translations.
frontend/app/dashboard/suggest/page.tsx Migrates suggest page strings; refactors greeting/weather hints to key-based translation.
frontend/app/dashboard/pairings/page.tsx Migrates pairings page strings + empty states to translations.
frontend/app/dashboard/page.tsx Migrates dashboard widgets/labels/toasts to translations.
frontend/app/dashboard/outfits/page.tsx Migrates outfits list/calendar page strings to translation keys.
frontend/app/dashboard/outfits/new/page.tsx Migrates studio editor/new outfit flow strings and toasts to translations.
frontend/app/dashboard/outfits/[id]/page.tsx Migrates outfit detail page strings and confirm/toasts to translations.
frontend/app/dashboard/notifications/page.tsx Migrates notifications UI; translates days/occasions/channels/schedules.
frontend/app/dashboard/learning/page.tsx Migrates learning page strings to translations.
frontend/app/dashboard/layout.tsx Migrates dashboard layout loading text to translations.
frontend/app/dashboard/history/page.tsx Migrates history page labels/filters/empty states to translations.
frontend/app/dashboard/family/page.tsx Migrates family management UI (including rich text) to translations.
frontend/app/dashboard/family/feed/page.tsx Migrates family feed strings/badges/actions to translations.
frontend/app/dashboard/analytics/page.tsx Migrates analytics page strings to translations.
backend/tests/test_weather_service.py Adds test coverage for geocoding JSON decode failure behavior.
backend/tests/test_weather_api.py Adds API tests for geocoding fallback and failure handling.
backend/tests/test_config.py Adds tests for geocoding User-Agent construction logic.
backend/app/services/weather_service.py Adds geocode_location_name and GeocodingServiceError.
backend/app/main.py Uses settings.app_version for FastAPI version metadata.
backend/app/config.py Adds app_version, geocoding configuration, and user-agent builder helper.
backend/app/api/weather.py Adds geocoding fallback for saved location names + consistent 503 response detail.
.env.example Documents geocoding env vars and frontend network location override.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

{item.last_worn_at ? (
<p className={`text-xs mt-1 ${getWornAgoColorClass(item.last_worn_at, userTimezone)}`}>
{formatWornAgo(item.last_worn_at, userTimezone)}
{formatWornAgo(item.last_worn_at, userTimezone, tShared.raw)}
Comment on lines 136 to 138
<p className="font-medium text-lg">
{generatedPairings.length} outfit{generatedPairings.length !== 1 ? 's' : ''} created!
{generatedPairings.length} outfit{generatedPairings.length !== 1 ? 's' : ''} {t('success').replace(/\d+ outfit\(s\) /, '')}
</p>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : null}
Leave
{t('leaveFamily').split(' ')[0]}
Comment on lines +509 to 512
<DialogTitle>{t('schedule.addSchedule')}</DialogTitle>
<DialogDescription>
Set up when you want to receive outfit recommendations.
{t('schedule.description').split('.')[0]}.
</DialogDescription>
return;
}
toast.error(getErrorMessage(error, 'Failed to save outfit'));
toast.error(getErrorMessage(error, t('new.outfitUpdated')));
Comment thread frontend/tests/setup.ts
Comment on lines +25 to +32
vi.mock('next-intl', () => ({
useTranslations: () => (key: string) => key,
}))

vi.mock('next-intl/server', () => ({
getTranslations: async () => (key: string) => key,
getMessages: async () => ({}),
}))
Comment on lines +1 to +3
import { describe, it, expect, vi } from 'vitest'
import { cn, formatWornAgo, getDaysSinceDateInTimezone } from '@/lib/utils'

Comment on lines +28 to 29
function computeWarnings(items: StudioItem[], t: any): string[] {
const warnings: string[] = [];
@gzhang33
Copy link
Copy Markdown
Author

Closing in favor of two focused PRs:

@gzhang33 gzhang33 closed this May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants