Custom i18n system with typed translation dictionaries (~570 keys), LanguageProvider context, and useTranslation hook. All 31 components and pages wired with t() calls. Chatbot backend passes language hint to Claude for Ukrainian responses. Language preference persists via localStorage. SEO meta tags and html lang attribute update dynamically. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
Tasks: Multi-Language Support (EN/UK)
Input: Design documents from /specs/001-multi-language/
Prerequisites: plan.md, spec.md, research.md, data-model.md, quickstart.md
Tests: Not requested — no test tasks included.
Organization: Tasks are grouped by user story to enable independent implementation and testing of each story.
Format: [ID] [P?] [Story] Description
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
Phase 1: Setup (Shared Infrastructure)
Purpose: Create the i18n module and translation files
- T001 Create
Translationsinterface with all translation keys insrc/i18n/types.ts - T002 Create English translations object (source of truth) in
src/i18n/en.ts— extract all ~200 hardcoded strings from every component into dot-separated keys (e.g.,header.nav.home,hero.title,contact.form.fullName). Mustsatisfies Translations. - T003 Create Ukrainian translations object in
src/i18n/uk.ts— translate all keys fromen.tsinto Ukrainian. Mustsatisfies Translations. - T004 Create
LanguageProvidercontext anduseTranslationhook insrc/i18n/LanguageContext.tsx— state initialized fromlocalStorage.getItem('aimpress_lang') || 'en',setLangupdates state + localStorage +document.documentElement.lang,t(key)resolves against current language dict with English fallback. - T005 Create barrel export in
src/i18n/index.ts
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Wire LanguageProvider into the app and connect the Header toggle
CRITICAL: No user story work can begin until this phase is complete
- T006 Wrap
<App>with<LanguageProvider>inside the existing<HelmetProvider>insrc/main.tsx - T007 Wire the existing Header language toggle to the i18n context in
src/components/Header.tsx— remove localcurrentLangstate, importuseTranslation, mapsetLang('en')/setLang('uk')to toggle clicks, derive display label ("Eng"/"Ukr") from contextlangvalue
Checkpoint: Language toggle is functional — clicking Eng/Ukr updates lang in context and localStorage. No visible text changes yet.
Phase 3: User Story 1 — Browse Site in Ukrainian (Priority: P1) MVP
Goal: All static homepage text displays in the selected language when the toggle is clicked.
Independent Test: Click the language toggle in the header → verify all visible text on every homepage section changes to Ukrainian without page reload. Click back to English → verify all text reverts.
Implementation for User Story 1
- T008 [US1] Replace hardcoded nav item names with
t()calls insrc/components/Header.tsx— movenavItemsarray inside component body to use hook, translate login form strings - T009 [P] [US1] Replace hardcoded title and CTA button text with
t()calls insrc/components/Hero.tsx - T010 [P] [US1] Replace flip card titles/subtitles/back text, "Built for" section, and static card titles/descriptions with
t()calls insrc/components/Benefits.tsx— move data arrays inside component body - T011 [P] [US1] Replace questions array and CTA button text with
t()calls insrc/components/Banner1.tsx - T012 [P] [US1] Replace section title and result descriptions with
t()calls insrc/components/RealResults.tsx - T013 [P] [US1] Replace section title, step titles, durations, descriptions, and details with
t()calls insrc/components/Timeline.tsx— move timeline steps array inside component body - T014 [P] [US1] Replace questions and CTA button text with
t()calls insrc/components/Banner2.tsx - T015 [P] [US1] Replace section title, metric labels/values, alternative names, and footer text with
t()calls insrc/components/ComparisonTable.tsx— move comparison data inside component body - T016 [P] [US1] Replace "Recent Updates" title, "View All Posts" link text with
t()calls, and update date locale from hardcoded'en-US'tolang === 'uk' ? 'uk-UA' : 'en-GB'insrc/components/BlogSection.tsx - T017 [P] [US1] Replace section title and UI text with
t()calls insrc/components/ResourcesSection.tsx - T018 [P] [US1] Replace heading and subtitle with
t()calls insrc/components/ContactSection.tsx - T019 [P] [US1] Replace copyright text and link labels with
t()calls insrc/components/Footer.tsx - T020 [P] [US1] Replace banner text and button labels with
t()calls insrc/components/CookieConsent.tsx
Checkpoint: All homepage static text switches between English and Ukrainian via the Header toggle. MVP deliverable.
Phase 4: User Story 2 — Submit Forms in Preferred Language (Priority: P2)
Goal: Contact form, chatbot lead form — all labels, placeholders, validation messages, and success/error messages display in the selected language.
Independent Test: Switch to Ukrainian → open contact form → verify all labels/placeholders are in Ukrainian → submit with invalid data → verify validation messages in Ukrainian → submit successfully → verify success message in Ukrainian. Repeat for chatbot lead form.
Implementation for User Story 2
- T021 [P] [US2] Replace form title, labels ("Full Name", "Job Title / Role", "Work Email", etc.), placeholders, submit button text ("Submit a request" / "Sending..."), success message, and validation error messages with
t()calls insrc/components/ContactForm.tsx - T022 [P] [US2] Replace chatbot lead form labels ("Your name *", "Email *", "Company (optional)"), placeholders, consent text ("I agree to the processing..."), and button text with
t()calls insrc/components/ChatLeadForm.tsx - T023 [P] [US2] Replace quote form labels and placeholders with
t()calls insrc/components/QuoteForm.tsx
Checkpoint: All forms display labels, placeholders, validation, and success messages in the selected language.
Phase 5: User Story 3 — Language Preference Persistence (Priority: P3)
Goal: Selected language persists across browser sessions via localStorage.
Independent Test: Select Ukrainian → close tab → reopen site → verify it loads in Ukrainian. Clear browser data → reopen → verify English default.
Implementation for User Story 3
- T024 [US3] Verify and ensure
LanguageContext.tsxreads fromlocalStorageon initialization insrc/i18n/LanguageContext.tsx— confirmuseStateinitializer readslocalStorage.getItem('aimpress_lang'), confirmsetLangwrites to localStorage, confirmdocument.documentElement.langis set on mount - T025 [US3] Verify the Header toggle correctly reflects the persisted language on page load in
src/components/Header.tsx— ensure the toggle display ("Eng"/"Ukr") matches the stored preference, not a hardcoded default
Checkpoint: Language preference persists across page refreshes and browser sessions.
Phase 6: User Story 4 — Chatbot Conversations in Preferred Language (Priority: P4)
Goal: Chatbot UI displays in the selected language and AI responds in the matching language.
Independent Test: Switch to Ukrainian → open chatbot → verify greeting, status text ("Online"), and input placeholder are in Ukrainian → send a message → verify AI responds in Ukrainian.
Implementation for User Story 4
- T026 [P] [US4] Replace greeting text with
t()call insrc/components/ChatBubble.tsx - T027 [P] [US4] Replace "Online" status text, welcome message with
t()calls insrc/components/ChatWindow.tsx - T028 [P] [US4] Replace "Type a message..." placeholder with
t()call insrc/components/ChatInput.tsx - T029 [US4] Pass
langfromuseTranslation()context to theuseChathook insrc/components/ChatWidget.tsx— pass as parameter touseChat - T030 [US4] Add
languagefield to the request body sent to/api/chatinsrc/hooks/useChat.ts— acceptlangparameter, includelanguage: langin fetch body - T031 [US4] Add
language: str = "en"field toChatRequestmodel inchatbot-api/models.py - T032 [US4] Prepend language hint to messages when
req.language == "uk"inchatbot-api/main.py— add synthetic user/assistant message pair before the conversation to guide Claude to respond in Ukrainian
Checkpoint: Chatbot UI is fully translated and AI responds in the selected language.
Phase 7: Polish & Cross-Cutting Concerns
Purpose: SEO, subpage translations, and final verification
- T033 [P] [US1] Replace all hardcoded text (hero, story, differentiators, values, founder bio, industries, CTA) with
t()calls insrc/pages/AboutPage.tsx— move data arrays (differentiators, values, industries) inside component body - T034 [P] [US1] Replace service titles, descriptions, includes list, and CTA with
t()calls insrc/pages/ServicesPage.tsx - T035 [P] [US1] Replace pricing labels, tier names, feature lists, and CTA with
t()calls insrc/pages/PricingPage.tsx - T036 [P] [US1] Replace page title and UI chrome with
t()calls insrc/pages/BlogPage.tsx— update date locale - T037 [P] [US1] Replace "Back to blog" link text with
t()call and update date formatting locale insrc/pages/BlogPostPage.tsx - T038 Add
<html lang={lang}>via Helmet and pass translated SEO title/description to each page's<SEO>component — update SEO component to accept lang, addseo.*translation keys for all pages - T039 Visual review of all pages in Ukrainian — check for text overflow, broken layouts, or missing translations. Fix any CSS issues caused by longer Ukrainian text (buttons, nav items, cards).
- T040 Run quickstart.md validation — verify the developer workflow (adding a new key, using
t()in a component, date formatting) works as documented
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies — can start immediately
- Foundational (Phase 2): Depends on Phase 1 completion — BLOCKS all user stories
- User Stories (Phases 3-6): All depend on Phase 2 completion
- US1 (Phase 3): No dependencies on other stories
- US2 (Phase 4): No dependencies on other stories (forms are separate components)
- US3 (Phase 5): Largely verification — persistence is built into Phase 2's LanguageContext
- US4 (Phase 6): No dependencies on other stories (chatbot is separate component tree)
- Polish (Phase 7): Depends on Phase 2 completion (can run in parallel with user stories, but best after US1)
User Story Dependencies
- US1 (P1): After Phase 2 — no cross-story dependencies
- US2 (P2): After Phase 2 — no cross-story dependencies
- US3 (P3): After Phase 2 — verification only (persistence built into LanguageContext)
- US4 (P4): After Phase 2 — no cross-story dependencies, but includes backend changes
Parallel Opportunities
- T009–T020 (US1 homepage components): ALL parallelizable — different files, no dependencies
- T021–T023 (US2 forms): ALL parallelizable — different files
- T026–T028 (US4 chat UI): ALL parallelizable — different files
- T033–T037 (Polish subpages): ALL parallelizable — different files
- US1, US2, US4 can all proceed in parallel after Phase 2
Parallel Example: User Story 1
# After Phase 2 is complete, launch all homepage component translations in parallel:
Task: "T009 — Replace text in Hero.tsx"
Task: "T010 — Replace text in Benefits.tsx"
Task: "T011 — Replace text in Banner1.tsx"
Task: "T012 — Replace text in RealResults.tsx"
Task: "T013 — Replace text in Timeline.tsx"
Task: "T014 — Replace text in Banner2.tsx"
Task: "T015 — Replace text in ComparisonTable.tsx"
Task: "T016 — Replace text in BlogSection.tsx"
Task: "T017 — Replace text in ResourcesSection.tsx"
Task: "T018 — Replace text in ContactSection.tsx"
Task: "T019 — Replace text in Footer.tsx"
Task: "T020 — Replace text in CookieConsent.tsx"
Implementation Strategy
MVP First (User Story 1 Only)
- Complete Phase 1: Create i18n module + translation files (T001–T005)
- Complete Phase 2: Wire provider + Header toggle (T006–T007)
- Complete Phase 3: Translate all homepage components (T008–T020)
- STOP and VALIDATE: Toggle language, verify all homepage text switches
- Deploy/demo if ready
Incremental Delivery
- Phase 1 + 2 → Foundation ready
- Add US1 (homepage) → Test → Deploy (MVP!)
- Add US2 (forms) → Test → Deploy
- Add US3 (persistence verification) → Test → Deploy
- Add US4 (chatbot) → Test → Deploy
- Polish (subpages + SEO) → Final deploy
Notes
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story for traceability
- Translation files (T002, T003) are the most labor-intensive tasks — ~200 keys each
- Ukrainian translations should be reviewed by a native speaker before final deploy
- Privacy Policy and Terms of Use pages remain English-only (legal review needed for translation)
- Analytics event names stay in English regardless of UI language