Skip to Content
Multi-Language Plan

Multi-Language Support – Implementation Plan

Status: Planning
Target Language: French (fr-CA / fr-FR)
Audience: Engineering team

This document outlines the plan for adding multi-language support to QuoteStore, starting with French translation.


1. Goals

  1. Language Dropdown – Add a visible language switcher in the storefront header that lets buyers toggle between English and French.
  2. Automatic Translation – Translate all static UI strings (buttons, labels, navigation, messages) into French.
  3. SEO-Friendly – Ensure translated pages are indexable and have proper hreflang tags.
  4. Minimal Friction – Buyers should be able to switch languages instantly without page reload.
  5. Future-Proof – Architecture should support adding more languages later (Spanish, etc.).

2. Scope

In Scope (Phase 1)

AreaWhat gets translated
Storefront UINavigation, buttons, form labels, validation messages, empty states, tooltips
Checkout flowCart, shipping, payment, order confirmation
Account pagesProfile, addresses, team management
Emails (stretch)Order confirmations, status updates

Out of Scope (Phase 1)

AreaReason
Product names & descriptionsDynamic content from AutoQuotes; would require dealer-side translation workflow
Dealer portalLower priority; dealers are primarily English-speaking
Admin portalInternal use only
User-generated contentComments, notes, etc.

3. Solution Options

Option A: Weglot (SaaS)

How it works: Third-party service that intercepts and translates content on-the-fly.

ProsCons
Zero code changes to existing componentsMonthly cost (~$15–$200/mo depending on word count)
Visual editor for translation refinementsExternal dependency
SEO-friendly URLs out of the boxPrivacy/data considerations
Works immediatelyLess control over translation quality

Integration:

// layout.tsx or providers.tsx import { WeglotProvider } from '@weglot/react'; export default function RootLayout({ children }) { return ( <WeglotProvider apiKey={process.env.NEXT_PUBLIC_WEGLOT_API_KEY}> {children} </WeglotProvider> ); }

Estimated effort: 1–2 hours setup + ongoing subscription cost.


How it works: First-party i18n library for Next.js App Router. We maintain JSON translation files.

ProsCons
Free & open sourceRequires creating translation files
Full control over translationsMore upfront work
Type-safe with TypeScriptNeed to wrap text in t() calls
Excellent App Router / RSC support—
SEO-friendly with proper routing—

File structure:

apps/storefront/ ├── messages/ │ ├── en.json │ └── fr.json ├── i18n/ │ ├── request.ts │ └── routing.ts └── middleware.ts (locale detection)

Example usage:

// messages/en.json { "nav": { "catalog": "Browse Catalog", "cart": "Cart", "orders": "Orders", "account": "Account" }, "cart": { "empty": "Your cart is empty", "checkout": "Proceed to Checkout", "total": "Total" } } // messages/fr.json { "nav": { "catalog": "Parcourir le catalogue", "cart": "Panier", "orders": "Commandes", "account": "Compte" }, "cart": { "empty": "Votre panier est vide", "checkout": "Passer à la caisse", "total": "Total" } }
// Component usage import { useTranslations } from 'next-intl'; export function CartPage() { const t = useTranslations('cart'); return ( <div> <h1>{t('checkout')}</h1> <p>{t('empty')}</p> </div> ); }

Estimated effort: 2–3 days for full storefront coverage.


Option C: Google Translate Widget (Quick & Free)

How it works: Embed Google’s translate widget; client-side machine translation.

ProsCons
FreeUnprofessional appearance
2-minute setupNot SEO-friendly (no indexable translated pages)
Supports 100+ languagesTranslation quality varies
—Google may deprecate at any time
—Flash of untranslated content

Not recommended for a B2B platform where professionalism matters.


4. Recommendation

Use next-intl (Option B) for the following reasons:

  1. Cost: Free, no ongoing subscription.
  2. Quality: We control translations; can have a native French speaker review.
  3. Performance: No external API calls; translations are bundled.
  4. SEO: Proper /en/... and /fr/... URL structure with hreflang tags.
  5. Future-proof: Easy to add Spanish, etc. later.
  6. Type-safety: Catches missing translation keys at build time.

For the initial French translations, we can:

  • Use ChatGPT/Claude to generate the fr.json file from en.json
  • Have a native speaker review and refine
  • Iterate based on buyer feedback

5. Implementation Plan

Phase 1: Foundation (Day 1)

  • Install next-intl package
  • Create messages/en.json with all static strings
  • Configure i18n routing in middleware.ts
  • Add locale prefix to App Router (/[locale]/...)
  • Create LocaleSwitcher component for header

Phase 2: String Extraction (Day 1–2)

  • Audit all storefront pages for hardcoded strings
  • Replace hardcoded strings with t() calls
  • Group translations logically: nav, cart, checkout, orders, account, common

Phase 3: French Translations (Day 2)

  • Generate fr.json using AI translation
  • Review with native French speaker (if available)
  • Test all pages in French locale

Phase 4: Polish (Day 3)

  • Add language switcher dropdown to header (flag + language name)
  • Persist language preference in cookie/localStorage
  • Add hreflang meta tags for SEO
  • Test locale detection from browser Accept-Language
  • Update email templates (if in scope)

Phase 5: QA & Launch

  • Full storefront walkthrough in both languages
  • Test checkout flow end-to-end in French
  • Verify no missing translation keys (TypeScript will catch most)
  • Deploy to staging → production

6. UI/UX Design

Language Switcher Location

Place in the header, right side, near the account/cart icons:

┌─────────────────────────────────────────────────────────────┐ │ 🏠 QuoteStore Catalog Orders Reorder Lists │ 🌐 EN ▾ │ 👤 │ 🛒 │ └─────────────────────────────────────────────────────────────┘ ├── English └── Français
<DropdownMenu> <DropdownMenuTrigger> <Globe className="h-4 w-4" /> <span>{currentLocale.toUpperCase()}</span> <ChevronDown className="h-3 w-3" /> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem>🇺🇸 English</DropdownMenuItem> <DropdownMenuItem>🇫🇷 Français</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>

7. Technical Details

URL Structure

EnglishFrench
/catalog/fr/catalog
/cart/fr/cart
/orders/123/fr/orders/123

Default locale (English) can optionally omit the prefix for cleaner URLs.

Middleware

// middleware.ts import createMiddleware from 'next-intl/middleware'; import { routing } from './i18n/routing'; export default createMiddleware(routing); export const config = { matcher: ['/', '/(fr|en)/:path*'] };

Server Components

next-intl supports React Server Components:

// Server Component import { getTranslations } from 'next-intl/server'; export default async function CatalogPage() { const t = await getTranslations('catalog'); return <h1>{t('title')}</h1>; }

Client Components

'use client'; import { useTranslations } from 'next-intl'; export function AddToCartButton() { const t = useTranslations('cart'); return <Button>{t('addToCart')}</Button>; }

8. Translation File Structure

// messages/en.json { "common": { "loading": "Loading...", "error": "Something went wrong", "save": "Save", "cancel": "Cancel", "delete": "Delete", "edit": "Edit", "search": "Search", "noResults": "No results found" }, "nav": { "catalog": "Catalog", "cart": "Cart", "orders": "Orders", "reorderLists": "Reorder Lists", "account": "Account", "signOut": "Sign Out" }, "catalog": { "title": "Browse Products", "filters": "Filters", "sortBy": "Sort by", "addToCart": "Add to Cart", "viewDetails": "View Details" }, "cart": { "title": "Shopping Cart", "empty": "Your cart is empty", "subtotal": "Subtotal", "checkout": "Proceed to Checkout", "continueShopping": "Continue Shopping", "remove": "Remove", "quantity": "Quantity" }, "checkout": { "title": "Checkout", "shipping": "Shipping Address", "review": "Review Order", "placeOrder": "Place Order", "orderPlaced": "Order Placed Successfully!" }, "orders": { "title": "Your Orders", "orderNumber": "Order #", "status": "Status", "total": "Total", "viewDetails": "View Details", "noOrders": "You haven't placed any orders yet" }, "account": { "title": "Account Settings", "profile": "Profile", "addresses": "Addresses", "team": "Team Members" } }

9. Risks & Mitigations

RiskMitigation
Translation quality issuesHave native speaker review; iterate based on feedback
Missing translations in edge casesTypeScript strict mode catches at build time
Performance impactTranslations are static; no runtime overhead
Dynamic content (product names)Out of scope for Phase 1; document as future work
Pluralization edge casesnext-intl has built-in ICU message format support

10. Future Considerations

  1. More languages: Spanish (es), German (de) based on customer demand
  2. Product translation: Workflow for dealers to provide translated product names/descriptions
  3. RTL support: Arabic, Hebrew if needed (requires additional CSS work)
  4. Translation management UI: Admin interface to edit translations without code deploy
  5. AI-assisted translation: Integration with DeepL API for automatic draft translations

11. References

Last updated on