(SP:3) feat(i18n): add UA and PL translations for shop/admin pages#225
Conversation
Add comprehensive i18n support for shop and admin sections in 3 languages (en, uk, pl). Translation coverage: - Shop pages: main page, products, cart, checkout, orders - Admin pages: dashboard, products management, orders management - Navigation: header, mobile menu, category links - Product components: cards, filters, sort, badges (NEW/SALE) - Category names: Apparel, Lifestyle, Collectibles - All UI buttons, labels, and actions Key changes: - Added ~250+ translation keys to messages/en.json, messages/uk.json, messages/pl.json - Updated 20+ components to use useTranslations() and getTranslations() - Implemented color translation in cart and product detail pages - Translated hero message - Added badge translations
✅ Deploy Preview for develop-devlovers ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughAdds comprehensive internationalization across the shop frontend: imports next-intl, replaces hard-coded UI strings with translation lookups, adds/updates locale JSONs, and converts several components to async or updated signatures to fetch server-side translations. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
frontend/app/[locale]/shop/products/[slug]/page.tsx (1)
58-61: Badge in unavailable block is not translated.The badge in the unavailable product view (line 60) renders
{p.badge}directly, while the available product view at line 119 uses the translated version{tProduct(\badges.${product.badge}`)}`. This creates an inconsistent user experience across locales.Proposed fix
{p.badge && p.badge !== 'NONE' && ( <span className="absolute left-4 top-4 z-10 rounded bg-foreground px-2 py-1 text-xs font-semibold uppercase text-background"> - {p.badge} + {tProduct(`badges.${p.badge}`)} </span> )}frontend/app/[locale]/shop/admin/page.tsx (1)
27-35: Translate the "Admin sections" aria-label.This label is still hard-coded in English; consider using the page namespace (e.g.,
t('sectionsLabel')) and add the key to messages.💡 Suggested tweak
- <section className="mt-6" aria-label="Admin sections"> + <section className="mt-6" aria-label={t('sectionsLabel')}>frontend/app/[locale]/shop/orders/[id]/page.tsx (1)
321-343: Visible prefixes still hard-coded in English.
TheQty,Unit, andLineprefixes should be localized to avoid mixed-language UI.💡 Suggested fix
- <dd className="text-sm">Qty: {it.quantity}</dd> + <dd className="text-sm"> + {t('quantity')}: {it.quantity} + </dd> ... - <dd className="text-sm opacity-80"> - Unit:{' '} - {safeFormatMoneyMajor(it.unitPrice, currency, locale)} - </dd> + <dd className="text-sm opacity-80"> + {t('unitPrice')}: {safeFormatMoneyMajor(it.unitPrice, currency, locale)} + </dd> ... - <dd className="text-sm font-medium"> - Line:{' '} - {safeFormatMoneyMajor(it.lineTotal, currency, locale)} - </dd> + <dd className="text-sm font-medium"> + {t('lineTotal')}: {safeFormatMoneyMajor(it.lineTotal, currency, locale)} + </dd>
🤖 Fix all issues with AI agents
In `@frontend/app/`[locale]/shop/checkout/error/page.tsx:
- Around line 101-105: The translation key errors.orderNotFoundDescription used
in the JSX (t('errors.orderNotFoundDescription')) is missing; either add that
key to your locale JSONs with the appropriate copy or change the call to use the
existing key notFoundOrder.message instead—update the JSX reference accordingly
so it matches an existing translation key.
- Around line 57-61: The page references non-existent or mis-namespaced
translation keys; update the component (page.tsx) to use the correct existing
keys and add the missing keys to all locale files (en.json, uk.json, pl.json).
Specifically: replace errors.unableToLoadOrder with errors.unableToLoad and
replace actions.retryPayment with error.retryPayment in the component; add the
four missing description keys errors.missingOrderIdDescription,
errors.orderNotFoundDescription, error.paymentFailedDescription,
error.paymentUnclearDescription to each locale and add the three label keys
error.orderLabel, error.totalLabel, error.statusLabel to each locale (using the
corresponding existing values for missing keys where appropriate). Ensure
namespaces match (errors.* and error.*) and that the component uses the exact
keys added to the locale files.
In `@frontend/app/`[locale]/shop/checkout/success/page.tsx:
- Around line 167-172: Replace the confusing translation key t('error.order')
used in the success page heading with a semantically appropriate key (e.g.,
t('success.orderLabel')): update the call in the checkout success page (the h1
that uses order.id.slice(0, 8)) to use the new key, add the corresponding
key/value to your locale files for all supported locales, and run your i18n
extraction/validation (or update any translation tests) so the new key is
present and the old error.order key is not wrongly reused here.
In `@frontend/app/`[locale]/shop/orders/[id]/page.tsx:
- Around line 285-288: The code renders a raw boolean (order.stockRestored ?
'true' : 'false') which leaks English; update the UI to output a localized
string by mapping the boolean to translation keys via the existing i18n helper t
— e.g. replace the ternary with a call that returns t('yes') when
order.stockRestored is truthy and t('no') when falsy (or add a small helper like
mapBooleanToTranslation used in this component), ensuring you use the same
translation namespace as t('stockRestored').
In `@frontend/app/`[locale]/shop/orders/page.tsx:
- Around line 111-117: The redirect for unauthenticated users in the route using
params and getCurrentUser currently drops locale and uses the wrong query key;
change the redirect call in the page component so it builds a locale-aware path
(include the resolved locale variable) and pass the return URL using the
returnTo query parameter (e.g., returnTo=<encoded path>) instead of next; update
the analogous redirect in the order detail page as well to use the same
locale-aware path and returnTo parameter so post-login navigation returns to the
correct localized page.
In `@frontend/components/header/AppMobileMenu.tsx`:
- Around line 61-72: The shop category links (shopLinks) are never marked active
because pathname ignores the ?category=... query; update the component to read
the current category using Next.js useSearchParams() and adjust the active-check
in the shop variant rendering logic (where variant === 'shop' determines active
state) to return active when pathname matches '/shop/products' AND the search
param 'category' equals the link's category value (extract category from each
shopLinks.href or store it alongside href/label), falling back to active when no
category is set for the base products page; ensure you reference
useSearchParams(), shopLinks, links and the variant-based active state to
implement the comparison.
In `@frontend/messages/uk.json`:
- Around line 454-469: The checkout.errors block is missing two translation keys
used by the error page: add "missingOrderIdDescription" and
"orderNotFoundDescription" under the existing "checkout.errors" object in
frontend/messages/uk.json; populate them with appropriate Ukrainian text that
complements "missingOrderId" and "orderNotFound" (e.g., short sentences
explaining next steps or guidance) so components referencing
checkout.errors.missingOrderIdDescription and
checkout.errors.orderNotFoundDescription can read localized strings.
🧹 Nitpick comments (7)
frontend/components/shop/product-sort.tsx (1)
31-39: Consider deriving translation keys to reduce maintenance burden.The
keyMapduplicates sort values that likely exist inSORT_OPTIONS. If new sort options are added to the config, this mapping must be manually updated—otherwise options silently fall back to 'featured'.♻️ Suggested refactor: derive keys programmatically
const getOptionLabel = (value: string) => { - const keyMap: Record<string, string> = { - 'featured': 'featured', - 'price-asc': 'priceAsc', - 'price-desc': 'priceDesc', - 'newest': 'newest', - }; - return tOptions(keyMap[value] || 'featured'); + // Convert 'price-asc' → 'priceAsc', 'featured' → 'featured', etc. + const key = value.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); + return tOptions(key); };Alternatively, store translation keys directly in
SORT_OPTIONSconfig to keep them co-located with the values.frontend/components/shop/header/nav-links.tsx (1)
35-44: Consider using a more appropriate translation namespace.The component uses
shop.admin.navigationforshopNavandhometranslations, but this is the public shop navigation (not admin). While functional, this could cause confusion during maintenance. Consider either:
- Moving these keys to a shared/common namespace like
shop.navigation- Or renaming the namespace to clarify it's shared
frontend/app/[locale]/shop/checkout/payment/[orderId]/page.tsx (1)
196-223: Consider reusingbuildStatusMessagefor consistency.The paid status block at line 201 directly accesses
t('payment.statusMessages.alreadyPaid')whilebuildStatusMessagealready handles the'paid'case. This creates slight duplication. Consider usingbuildStatusMessage(order.paymentStatus)here for consistency, or extract the status message keys to a shared constant.frontend/components/shop/category-tile.tsx (1)
11-13: Parallelize translation loading to reduce latency.These namespaces can be fetched concurrently.
♻️ Proposed refactor
- const t = await getTranslations('shop.products'); - const tCategories = await getTranslations('shop.catalog.categories'); - const tCategoryTile = await getTranslations('shop.categoryTile'); + const [t, tCategories, tCategoryTile] = await Promise.all([ + getTranslations('shop.products'), + getTranslations('shop.catalog.categories'), + getTranslations('shop.categoryTile'), + ]);frontend/components/shop/product-filters.tsx (1)
64-65: Consider simplifying the nested ternary for category keys.
It’s readable now but will get unwieldy as more special slugs appear.♻️ Optional refactor
+ const categoryKeyMap: Record<string, string> = { + 'new-arrivals': 'newArrivals', + 'best-sellers': 'bestSellers', + }; ... - {tCategories(cat.slug === 'new-arrivals' ? 'newArrivals' : cat.slug === 'best-sellers' ? 'bestSellers' : cat.slug)} + {tCategories(categoryKeyMap[cat.slug] ?? cat.slug)}frontend/components/shop/add-to-cart-button.tsx (1)
28-35: Normalize color slugs for better translation coverage.
Lowercasing alone can miss multi-word or symbol colors (e.g., “Light Blue”).♻️ Optional refactor
- const translateColor = (color: string): string => { - const colorSlug = color.toLowerCase(); + const translateColor = (color: string): string => { + const colorSlug = color + .toLowerCase() + .trim() + .replace(/\s+/g, '-') + .replace(/[^a-z0-9-]/g, ''); try { return tColors(colorSlug); } catch { return color; } };frontend/app/[locale]/shop/products/page.tsx (1)
89-92: Consider localizing the SR-only “Product results” heading.
Keeps screen-reader text consistent across locales.♻️ Suggested tweak
- <h2 id="results-heading" className="sr-only"> - Product results - </h2> + <h2 id="results-heading" className="sr-only"> + {t('resultsHeading')} + </h2>Remember to add
resultsHeadingin the locale message files.
| {t('errors.missingOrderId')} | ||
| </h1> | ||
| <p className="mt-2 text-sm text-muted-foreground"> | ||
| We couldn’t identify your order. | ||
| {t('errors.missingOrderIdDescription')} | ||
| </p> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, search for the translation key across all JSON files
echo "=== Searching for 'missingOrderIdDescription' in JSON files ==="
rg -n "missingOrderIdDescription" --glob "*.json"
echo ""
# Also search for similar keys
echo "=== Searching for 'missingOrderId' (without Description) ==="
rg -n "missingOrderId" --glob "*.json"
echo ""
# List translation files to understand structure
echo "=== Translation files available ==="
fd -e json . | grep -E "(translations|locales|i18n|messages)" | head -20Repository: DevLoversTeam/devlovers.net
Length of output: 571
🏁 Script executed:
# Examine the errors section in translation files
echo "=== Errors section in en.json ==="
rg -A 20 '"errors":\s*{' frontend/messages/en.json | head -40
echo ""
# Check the page.tsx file to see what keys are being used
echo "=== Translation keys in page.tsx ==="
rg "t\(['\"]" frontend/app/\[locale\]/shop/checkout/error/page.tsx | head -20Repository: DevLoversTeam/devlovers.net
Length of output: 1249
🏁 Script executed:
# Let's examine the structure more carefully
echo "=== All 'error' and 'errors' related keys in en.json ==="
rg "\"error" frontend/messages/en.json | head -30
echo ""
# Check if there are any "Description" keys pattern
echo "=== All keys with 'Description' in en.json ==="
rg "Description" frontend/messages/en.jsonRepository: DevLoversTeam/devlovers.net
Length of output: 916
🏁 Script executed:
# Get the full errors and error sections with better formatting
echo "=== Full shop.checkout errors section from en.json ==="
jq '.shop.checkout.errors' frontend/messages/en.json
echo ""
echo "=== Full shop.checkout error section from en.json ==="
jq '.shop.checkout.error' frontend/messages/en.jsonRepository: DevLoversTeam/devlovers.net
Length of output: 979
🏁 Script executed:
# Verify all the translation keys being used in page.tsx against what exists
echo "=== Checking all translation key usages in page.tsx ==="
cat frontend/app/\[locale\]/shop/checkout/error/page.tsx | rg "t\(['\"]" -o | sort | uniq
echo ""
echo "=== Confirming which keys are missing from translations ==="
# The missing keys identified
jq '.shop.checkout | keys' frontend/messages/en.jsonRepository: DevLoversTeam/devlovers.net
Length of output: 293
🏁 Script executed:
# Get the exact content to see all translation calls clearly
echo "=== All t() calls in page.tsx ==="
cat frontend/app/\[locale\]/shop/checkout/error/page.tsx | grep -oP "t\('[^']+'" | sort | uniq
echo ""
echo "=== Compare with available keys in shop.checkout sections ==="
echo "Available errors keys:"
jq '.shop.checkout.errors | keys' frontend/messages/en.json
echo ""
echo "Available error keys:"
jq '.shop.checkout.error | keys' frontend/messages/en.json
echo ""
echo "Available actions keys:"
jq '.shop.checkout.actions | keys' frontend/messages/en.jsonRepository: DevLoversTeam/devlovers.net
Length of output: 1084
Multiple missing and incorrectly referenced translation keys in error page.
The code references several translation keys that don't exist or use incorrect namespaces:
Missing description keys:
errors.missingOrderIdDescription(onlymissingOrderIdexists)errors.orderNotFoundDescription(onlyorderNotFoundexists)error.paymentFailedDescription(onlypaymentFailedexists)error.paymentUnclearDescription(onlypaymentUnclearexists)
Missing label keys:
error.orderLabel(onlyorderexists)error.totalLabel(onlytotalexists)error.statusLabel(onlystatusexists)
Incorrect key references:
- Line 63:
errors.unableToLoadOrdershould beerrors.unableToLoad actions.retryPaymentshould beerror.retryPayment(retryPayment is defined under error, not actions)
Add the missing keys to all translation files (en.json, uk.json, pl.json) and fix the namespace/key references in the code.
🤖 Prompt for AI Agents
In `@frontend/app/`[locale]/shop/checkout/error/page.tsx around lines 57 - 61, The
page references non-existent or mis-namespaced translation keys; update the
component (page.tsx) to use the correct existing keys and add the missing keys
to all locale files (en.json, uk.json, pl.json). Specifically: replace
errors.unableToLoadOrder with errors.unableToLoad and replace
actions.retryPayment with error.retryPayment in the component; add the four
missing description keys errors.missingOrderIdDescription,
errors.orderNotFoundDescription, error.paymentFailedDescription,
error.paymentUnclearDescription to each locale and add the three label keys
error.orderLabel, error.totalLabel, error.statusLabel to each locale (using the
corresponding existing values for missing keys where appropriate). Ensure
namespaces match (errors.* and error.*) and that the component uses the exact
keys added to the locale files.
| {t('errors.orderNotFound')} | ||
| </h1> | ||
| <p className="mt-2 text-sm text-muted-foreground"> | ||
| We couldn’t find this order. | ||
| {t('errors.orderNotFoundDescription')} | ||
| </p> |
There was a problem hiding this comment.
Missing translation key: errors.orderNotFoundDescription.
Similarly, errors.orderNotFoundDescription (line 104) doesn't exist in the translation files. Only errors.orderNotFound is defined.
Consider using notFoundOrder.message (line 468 in uk.json) or adding the missing key.
🤖 Prompt for AI Agents
In `@frontend/app/`[locale]/shop/checkout/error/page.tsx around lines 101 - 105,
The translation key errors.orderNotFoundDescription used in the JSX
(t('errors.orderNotFoundDescription')) is missing; either add that key to your
locale JSONs with the appropriate copy or change the call to use the existing
key notFoundOrder.message instead—update the JSX reference accordingly so it
matches an existing translation key.
- Replace confusing error.order with success.orderLabel in checkout success page heading - Localize boolean stockRestored display (yes/no instead of true/false) in order details - Fix active state detection for shop category links in mobile menu using search params - Add missing translation keys (orderLabel, yes, no) to all locales (en, uk, pl)
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/app/[locale]/shop/orders/[id]/page.tsx (1)
328-343: Localize visible item labels (“Qty/Unit/Line”).
The visible prefixes remain hard‑coded English even though the SR-only labels are translated.✅ Suggested fix
- <dd className="text-sm">Qty: {it.quantity}</dd> + <dd className="text-sm">{t('quantity')}: {it.quantity}</dd> ... - <dd className="text-sm opacity-80"> - Unit:{' '} - {safeFormatMoneyMajor(it.unitPrice, currency, locale)} - </dd> + <dd className="text-sm opacity-80"> + {t('unitPrice')}: {safeFormatMoneyMajor(it.unitPrice, currency, locale)} + </dd> ... - <dd className="text-sm font-medium"> - Line:{' '} - {safeFormatMoneyMajor(it.lineTotal, currency, locale)} - </dd> + <dd className="text-sm font-medium"> + {t('lineTotal')}: {safeFormatMoneyMajor(it.lineTotal, currency, locale)} + </dd>
🧹 Nitpick comments (1)
frontend/app/[locale]/shop/checkout/success/page.tsx (1)
164-183: Localize the payment status value, not just its label.
Right now the status value shows raw enum text (e.g.,paid,pending), which will stay English in non‑EN locales. Consider mapping it to localized labels with a fallback.♻️ Suggested mapping (adjust keys to match your status enum)
const totalMinor = order.totalAmountMinor; const itemsCount = order.items.reduce((sum, item) => sum + item.quantity, 0); + const paymentStatusLabel = + { + paid: t('success.paymentStatus.paid'), + pending: t('success.paymentStatus.pending'), + processing: t('success.paymentStatus.processing'), + }[order.paymentStatus] ?? order.paymentStatus; ... - <dd className="font-semibold capitalize text-foreground"> - {order.paymentStatus} - </dd> + <dd className="font-semibold capitalize text-foreground"> + {paymentStatusLabel} + </dd>Also applies to: 193-210, 224-230
Add comprehensive i18n support for shop and admin sections in 3 languages (en, uk,
pl).
Translation coverage:
Key changes:
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.