From de691525fbafb5ffeec78412ff6fe9aa82d070ea Mon Sep 17 00:00:00 2001 From: Andrei Date: Fri, 3 Oct 2025 11:01:19 +0000 Subject: [PATCH] feat: Add backend support for measurement unit preferences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated backend to support measurement unit preferences (Metric/Imperial) in user profiles. The preferences are stored in the existing JSONB preferences column, which already exists in the database. **Backend Changes:** - Updated User entity to include measurementUnit in preferences type - Created UpdateProfileDto with proper validation for preferences - Updated auth controller to use UpdateProfileDto for PATCH /api/v1/auth/profile - Added IsIn validator for measurementUnit ('metric' | 'imperial') **Documentation:** - Updated LOCALIZATION_IMPLEMENTATION_PLAN.md with completion status - Marked Phases 1, 2, 3, 7, 8 as completed - Marked Phase 4 (backend preferences) as in progress - Added detailed completion markers for each subsection **Technical Details:** - No migration needed - using existing preferences JSONB column - Preferences object now includes: notifications, emailUpdates, darkMode, measurementUnit - Endpoint: PATCH /api/v1/auth/profile accepts optional preferences object - Validation ensures measurementUnit is either 'metric' or 'imperial' **Next Steps:** - Frontend integration to persist language/measurement preferences to backend - Apply localization throughout remaining pages and components - Create onboarding flow with language/measurement selection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/LOCALIZATION_IMPLEMENTATION_PLAN.md | 185 ++++++++++-------- .../src/database/entities/user.entity.ts | 1 + .../src/modules/auth/auth.controller.ts | 3 +- .../modules/auth/dto/update-profile.dto.ts | 33 ++++ 4 files changed, 135 insertions(+), 87 deletions(-) create mode 100644 maternal-app/maternal-app-backend/src/modules/auth/dto/update-profile.dto.ts diff --git a/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md b/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md index 59b13bc..7a6fa33 100644 --- a/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md +++ b/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md @@ -18,25 +18,37 @@ Implement comprehensive internationalization (i18n) support for the Maternal App ## Current Status -### ✅ Already Completed +### ✅ Already Completed (Backend) - Backend multilanguage support for AI responses - AI safety responses in 5 languages - MultiLanguageService with language detection +### ✅ Completed (Frontend - Phase 1) +- ✅ i18next framework installed and configured +- ✅ I18nProvider integrated into app layout +- ✅ Translation files structure created (35 files: 5 languages × 7 namespaces) +- ✅ Custom hooks created (useTranslation, useLocale, useFormatting) +- ✅ Measurement unit conversion utilities implemented +- ✅ Language selector component (Settings page) +- ✅ Measurement unit selector component (Settings page) +- ✅ Settings page integration with language/measurement preferences + +### ⏳ In Progress +- Backend user schema update for measurement preferences + ### ❌ To Be Implemented -- Frontend i18next framework -- Translation files for all UI strings -- Language selector in onboarding -- Language selector in settings -- Date/time localization -- Measurement unit preferences (Metric/Imperial) +- Language selector in onboarding flow +- Apply translations to all pages and components +- Date/time localization throughout app - Number formatting per locale +- Tracking forms with unit conversions +- Professional translations (currently using placeholder translations) --- -## Phase 1: Framework Setup +## Phase 1: Framework Setup ✅ COMPLETED -### 1.1 Install Dependencies - latest versions only! +### 1.1 Install Dependencies - latest versions only! ✅ **Files**: `package.json` ```bash @@ -49,31 +61,31 @@ npm install date-fns # Already installed, confirm locales - `react-i18next` - React bindings - `i18next-browser-languagedetector` - Auto-detect user language -### 1.2 Create i18n Configuration -**File**: `lib/i18n/config.ts` (NEW) +### 1.2 Create i18n Configuration ✅ +**File**: `lib/i18n/config.ts` (CREATED) -- Initialize i18next -- Configure language detector -- Set up fallback language (en-US) -- Configure interpolation -- Load translation resources +- ✅ Initialize i18next +- ✅ Configure language detector +- ✅ Set up fallback language (en-US) +- ✅ Configure interpolation +- ✅ Load translation resources -### 1.3 Create i18n Provider -**File**: `components/providers/I18nProvider.tsx` (NEW) +### 1.3 Create i18n Provider ✅ +**File**: `components/providers/I18nProvider.tsx` (CREATED) -- Wrap app with I18nextProvider -- Initialize i18n on mount -- Handle language loading states +- ✅ Wrap app with I18nextProvider +- ✅ Initialize i18n on mount +- ✅ Handle language loading states -### 1.4 Update Root Layout -**File**: `app/layout.tsx` (MODIFY) +### 1.4 Update Root Layout ✅ +**File**: `app/layout.tsx` (MODIFIED) -- Add I18nProvider to provider stack -- Set html lang attribute dynamically +- ✅ Add I18nProvider to provider stack +- ⏳ Set html lang attribute dynamically (TODO) --- -## Phase 2: Translation Files Structure +## Phase 2: Translation Files Structure ✅ COMPLETED ### 2.1 Directory Structure ``` @@ -141,33 +153,34 @@ locales/ --- -## Phase 3: Custom Hooks +## Phase 3: Custom Hooks ✅ COMPLETED -### 3.1 useTranslation Hook -**File**: `hooks/useTranslation.ts` (NEW) +### 3.1 useTranslation Hook ✅ +**File**: `hooks/useTranslation.ts` (CREATED) -- Re-export from react-i18next with type safety -- Custom hook for easier usage +- ✅ Re-export from react-i18next with type safety +- ✅ Custom hook for easier usage -### 3.2 useLocale Hook -**File**: `hooks/useLocale.ts` (NEW) +### 3.2 useLocale Hook ✅ +**File**: `hooks/useLocale.ts` (CREATED) -- Get current locale -- Change locale -- Get available locales -- Get locale display name +- ✅ Get current locale +- ✅ Change locale +- ✅ Get available locales +- ✅ Get locale display name +- ✅ Measurement system management -### 3.3 useFormatting Hook -**File**: `hooks/useFormatting.ts` (NEW) +### 3.3 useFormatting Hook ✅ +**File**: `hooks/useFormatting.ts` (CREATED) -- Format dates based on locale -- Format numbers based on locale -- Format currency based on locale -- Format units based on preference +- ✅ Format dates based on locale +- ✅ Format numbers based on locale +- ✅ Format currency based on locale +- ✅ Format units based on preference --- -## Phase 4: Measurement Unit Preference +## Phase 4: Measurement Unit Preference ⏳ IN PROGRESS ### 4.1 Backend Schema Update **File**: `src/database/migrations/V0XX_add_measurement_preference.sql` (NEW) @@ -200,10 +213,10 @@ Add optional `measurementUnit` field. --- -## Phase 5: Frontend User Preferences +## Phase 5: Frontend User Preferences ✅ PARTIALLY COMPLETED -### 5.1 Redux State -**File**: `store/slices/userSlice.ts` (MODIFY) +### 5.1 Redux State ⏳ +**File**: `store/slices/userSlice.ts` (TODO) Add to user state: ```typescript @@ -214,28 +227,28 @@ interface UserState { } ``` -### 5.2 Language Selector Component -**File**: `components/settings/LanguageSelector.tsx` (NEW) +### 5.2 Language Selector Component ✅ +**File**: `components/settings/LanguageSelector.tsx` (CREATED) Features: -- Dropdown with 5 language options -- Flag icons for each language -- Updates user preference on change -- Persists to backend -- Updates i18n instance +- ✅ Dropdown with 5 language options +- ✅ Shows language names in native script +- ✅ Updates user preference on change +- ⏳ Persists to backend (pending backend schema) +- ✅ Updates i18n instance -### 5.3 Measurement Unit Selector Component -**File**: `components/settings/MeasurementUnitSelector.tsx` (NEW) +### 5.3 Measurement Unit Selector Component ✅ +**File**: `components/settings/MeasurementUnitSelector.tsx` (CREATED) Features: -- Toggle or dropdown (Metric/Imperial) -- Updates user preference on change -- Persists to backend -- Shows example conversions +- ✅ Dropdown (Metric/Imperial) +- ✅ Updates user preference on change +- ⏳ Persists to backend (pending backend schema) +- ✅ Shows unit examples --- -## Phase 6: Onboarding Flow Integration +## Phase 6: Onboarding Flow Integration ❌ TODO ### 6.1 Update Onboarding Page **File**: `app/(auth)/onboarding/page.tsx` (MODIFY) @@ -266,47 +279,47 @@ Include language and measurementUnit in profile update call. --- -## Phase 7: Settings Page Integration +## Phase 7: Settings Page Integration ✅ COMPLETED -### 7.1 Update Settings Page -**File**: `app/settings/page.tsx` (MODIFY) +### 7.1 Update Settings Page ✅ +**File**: `app/settings/page.tsx` (MODIFIED) Add new sections: -- **Language & Region** section - - Language selector - - Measurement unit selector +- ✅ **Preferences** section + - ✅ Language selector + - ✅ Measurement unit selector -### 7.2 Settings UI Components +### 7.2 Settings UI Components ✅ **Files**: -- `components/settings/LanguageSettings.tsx` (NEW) -- `components/settings/MeasurementSettings.tsx` (NEW) +- ✅ `components/settings/LanguageSelector.tsx` (CREATED) +- ✅ `components/settings/MeasurementUnitSelector.tsx` (CREATED) --- -## Phase 8: Unit Conversion Utilities +## Phase 8: Unit Conversion Utilities ✅ COMPLETED -### 8.1 Conversion Utility -**File**: `lib/units/conversions.ts` (NEW) +### 8.1 Conversion Utility ✅ +**File**: `lib/utils/unitConversion.ts` (CREATED) Functions: -- `mlToOz(ml: number): number` -- `ozToMl(oz: number): number` -- `kgToLb(kg: number): number` -- `lbToKg(lb: number): number` -- `cmToIn(cm: number): number` -- `inToCm(in: number): number` +- ✅ `convertWeight` / `convertWeightToKg` +- ✅ `convertHeight` / `convertHeightToCm` +- ✅ `convertTemperature` / `convertTemperatureToCelsius` +- ✅ `convertVolume` / `convertVolumeToMl` +- ✅ `getUnitSymbol` +- ✅ `getConversionFactor` -### 8.2 Format Unit Hook -**File**: `hooks/useUnitFormat.ts` (NEW) +### 8.2 Format Unit Hook ✅ +**File**: `hooks/useFormatting.ts` (CREATED) -- Get user's measurement preference -- Format value with correct unit -- Convert between units -- Display with locale-specific formatting +- ✅ Get user's measurement preference +- ✅ Format value with correct unit +- ✅ Convert between units +- ✅ Display with locale-specific formatting --- -## Phase 9: Apply Localization Throughout App +## Phase 9: Apply Localization Throughout App ❌ TODO ### 9.1 Update All Pages Replace hardcoded strings with translation keys: diff --git a/maternal-app/maternal-app-backend/src/database/entities/user.entity.ts b/maternal-app/maternal-app-backend/src/database/entities/user.entity.ts index f7e390a..f50ced3 100644 --- a/maternal-app/maternal-app-backend/src/database/entities/user.entity.ts +++ b/maternal-app/maternal-app-backend/src/database/entities/user.entity.ts @@ -91,6 +91,7 @@ export class User { notifications?: boolean; emailUpdates?: boolean; darkMode?: boolean; + measurementUnit?: 'metric' | 'imperial'; }; @CreateDateColumn({ name: 'created_at' }) diff --git a/maternal-app/maternal-app-backend/src/modules/auth/auth.controller.ts b/maternal-app/maternal-app-backend/src/modules/auth/auth.controller.ts index 0939187..3e1f995 100644 --- a/maternal-app/maternal-app-backend/src/modules/auth/auth.controller.ts +++ b/maternal-app/maternal-app-backend/src/modules/auth/auth.controller.ts @@ -24,6 +24,7 @@ import { RegisterDto } from './dto/register.dto'; import { LoginDto } from './dto/login.dto'; import { RefreshTokenDto } from './dto/refresh-token.dto'; import { LogoutDto } from './dto/logout.dto'; +import { UpdateProfileDto } from './dto/update-profile.dto'; import { RequestPasswordResetDto, ResetPasswordDto, @@ -88,7 +89,7 @@ export class AuthController { @HttpCode(HttpStatus.OK) async updateProfile( @CurrentUser() user: any, - @Body() updateData: { name?: string }, + @Body() updateData: UpdateProfileDto, ) { return await this.authService.updateProfile(user.userId, updateData); } diff --git a/maternal-app/maternal-app-backend/src/modules/auth/dto/update-profile.dto.ts b/maternal-app/maternal-app-backend/src/modules/auth/dto/update-profile.dto.ts new file mode 100644 index 0000000..cbefd5e --- /dev/null +++ b/maternal-app/maternal-app-backend/src/modules/auth/dto/update-profile.dto.ts @@ -0,0 +1,33 @@ +import { IsString, IsOptional, IsObject, ValidateNested, IsIn } from 'class-validator'; +import { Type } from 'class-transformer'; + +export class UserPreferencesDto { + @IsOptional() + notifications?: boolean; + + @IsOptional() + emailUpdates?: boolean; + + @IsOptional() + darkMode?: boolean; + + @IsOptional() + @IsIn(['metric', 'imperial']) + measurementUnit?: 'metric' | 'imperial'; +} + +export class UpdateProfileDto { + @IsOptional() + @IsString() + name?: string; + + @IsOptional() + @IsString() + locale?: string; + + @IsOptional() + @IsObject() + @ValidateNested() + @Type(() => UserPreferencesDto) + preferences?: UserPreferencesDto; +}