- Added src/lib/i18n/metadata.ts with helper functions
- Updated [locale]/layout.tsx to use DEFAULT_LOCALE constant
- routing.ts already uses centralized SUPPORTED_LOCALES
Note: For full antifragility, a larger refactor would centralize
all hardcoded locale comparisons for metadata text fallbacks.
Currently adding a new language requires:
1. SUPPORTED_LOCALES in locales.ts
2. LOCALE_CONFIG entry
3. Translation keys in all message files
- Added getPathWithoutLocale() and buildLocalePath() helpers to locales.ts
- Updated Header to use centralized helpers instead of hardcoded regex
- Updated middleware to use SUPPORTED_LOCALES in matcher config
- Updated LocaleProvider to use isValidLocale() instead of hardcoded array
To add a new language now, only update:
1. SUPPORTED_LOCALES in locales.ts
2. LOCALE_CONFIG entry with label, flag, saleorLocale
3. Add translation keys to all message files
All routing now uses centralized constants - no more hardcoded locale lists.
- Default locale was "SR" instead of "sr"
- Comparisons used "EN" instead of "en" for shortDescription and benefits
- These hardcoded English strings were being skipped due to wrong comparison
- homepage page.tsx was passing productLocale (SR/EN) instead of locale (sr/en) to ProductCard
- ProductShowcase default locale was "SR" instead of "sr"
- ProductBenefits default locale was "SR" instead of "sr"
These caused URLs like /en/SR/products/... when clicking products
Created src/lib/i18n/locales.ts as single source of truth for:
- SUPPORTED_LOCALES array
- LOCALE_COOKIE name
- DEFAULT_LOCALE
- LOCALE_CONFIG (labels, flags, Saleor locale mapping)
- Helper functions (isValidLocale, getSaleorLocale, getLocaleFromPath)
Updated all files to use centralized constants:
- middleware.ts
- Header.tsx
- ProductCard.tsx
- sitemap.ts
- root layout and locale layout
- routing.ts
Benefits:
- Adding new locale only requires updating ONE file (locales.ts)
- No more hardcoded locale lists scattered across codebase
- Cookie name defined in one place
- Type-safe locale validation
- Added hreflang alternates to root layout for all locales (sr, en, de, fr)
- Added hreflang alternates to [locale] layout for all locales
- Updated sitemap to include all locale variants for every page
- Google will now properly index all language versions
The navLinks were using localePath which was derived from locale
but the switchLocale was using pathname directly. This caused
mismatch when switching languages.
Changes:
- Root page.tsx now detects browser language (Accept-Language header)
and cookie preference to redirect to correct locale (/sr or /en)
- Middleware handles old Serbian URLs (/products, /about, etc.) with
301 redirects to /sr/* while respecting locale cookie
- Header component now includes language switcher dropdown with
flag icons (Serbian and English)
- Language selection sets NEXT_LOCALE cookie and persists preference
Behavior:
- User visits / → redirects to /sr or /en based on cookie/browser
- User selects English from dropdown → cookie set, redirects to /en/*
- User visits /products with en cookie → /en/products (301)
- User visits /products with sr/no cookie → /sr/products (301)
- Root / now uses next-intl locale detection to redirect based on
Accept-Language header (English browser → /en, Serbian → /sr, etc.)
- Old Serbian URLs (/products, /about, etc.) still redirect to /sr/* with 301
- English URLs (/en/*) remain unchanged
Redirect old Serbian URLs (without /sr/ prefix) to new /sr/ URLs:
- / → /sr (301)
- /products → /sr/products (301)
- /about → /sr/about (301)
- /contact → /sr/contact (301)
- /checkout → /sr/checkout (301)
English URLs (/en/*) remain unchanged. This preserves SEO value
as Google treats 301 as permanent redirect passing ~90-99% PageRank.
WARNING: This change breaks existing SEO URLs for Serbian locale.
Changes:
- Migrated from separate locale folders (src/app/en/, src/app/de/, etc.)
to [locale] dynamic segments (src/app/[locale]/)
- Serbian is now at /sr/ instead of / (root)
- English at /en/, German at /de/, French at /fr/
- All components updated to generate locale-aware links
- Root / now redirects to /sr (307 temporary redirect)
SEO Impact:
- Previously indexed Serbian URLs (/, /products, /about, /contact)
will now return 404 or redirect to /sr/* URLs
- This is a breaking change for SEO - Serbian pages should ideally
remain at root (/) with only non-default locales getting prefix
- Consider implementing 301 redirects from old URLs to maintain
search engine rankings
Technical Notes:
- next-intl v4 with [locale] structure requires ALL locales to
have the prefix (cannot have default locale at root)
- Alternative approach would be separate folder structure per locale
- Add comprehensive Serbian translation file (sr.json) with all UI strings
- Add comprehensive English translation file (en.json) with all UI strings
- Update Serbian root pages (/, /products, /products/[slug], /about, /contact) to use getTranslations()
- Update English pages (/en/*) to use getTranslations()
- Replace all hardcoded strings with translation keys
- Add middleware.ts for locale detection (URL path, cookie, Accept-Language)
- Update routing.ts to include en, de, fr locales
- Update layout.tsx with NextIntlClientProvider and dynamic lang attribute
- Create EN/DE/FR homepages, product listings, product details, about, and contact pages
- Serbian remains at root URL (/products, /about, /contact)
- English at /en/*, German at /de/*, French at /fr/*
- Fix checkoutLinesDelete mutation: use 'id' param and 'linesIds' instead of 'lineIds'
- Fix viewport metadata warning: move to separate viewport export in layout.tsx
- Add sizes prop to checkout Image with fill
- Fix CartDrawer init checkout useEffect to prevent re-render loops
- Various product detail improvements