Case Study - Reusable E-Commerce Engine – TanosMotorsport
A production-grade, reusable e-commerce engine built on Next.js 15 and Strapi 5 — with ISR product pages, multi-currency support, and Horizon Translate integration for full multilingual storefronts.
- Client
- TanosMotorsport
- Year
- Service
- E-Commerce · Headless CMS · Multilingual Infrastructure

Overview
TanosMotorsport is an online store specialising in van and sports car modifications — a niche, technical catalogue where product pages need rich specs, multi-currency pricing, and a genuinely multilingual experience for a European audience.
We didn't just build a store. We built the engine that powers it — and designed every layer to be extracted, reused, and deployed again under a different brand without rewriting core logic.
The reusable engine architecture
The project is structured as a pnpm workspace monorepo:
/app— Next.js 15 (App Router) frontend, fully typed with TypeScript/backend— Strapi V5 headless CMS with custom admin plugins/packages/email— transactional email templates shared across deployments/packages/globalcomponents— UI component library reusable across storefronts
The backend exposes a clean REST API with no frontend-specific coupling. Swapping the frontend or running two storefronts against the same backend is a configuration change, not a rewrite.

ISR: fresh products without full redeploys
Product pages live at [locale]/product/[id]. At build time, generateStaticParams pre-renders the top products across all three locales — giving sub-100ms response times for the most visited pages from the first request.
// ISR: revalidate every 2 minutes — fresh pricing, no full redeploy
export const revalidate = 120
export const dynamicParams = true
export async function generateStaticParams() {
const products = await getProductsServer({ page: 1, limit: STATIC_GENERATION_LIMIT })
const locales = ['pl', 'en', 'de']
return locales.flatMap(locale =>
products.map(product => ({ locale, id: product.id.toString() }))
)
}
Long-tail products that weren't pre-rendered fall through to on-demand rendering via dynamicParams = true — rendered once, then cached. The 2-minute revalidate window means a price update in Strapi is live on all product pages within 120 seconds, with zero server downtime and zero cache-clear scripts.
Horizon Translate: static + dynamic multilingual pages
Integrating multilingual content in a product catalogue is harder than it looks. Product titles, descriptions, and spec labels arrive in Polish from the CMS and need accurate, consistent translations in English and German — for SEO-indexed static pages, not just client-side rendering.
We integrated our own Horizon Translate pipeline as the translation layer. The system uses a two-tier cache:
L1 — in-memory: Hot translations served in microseconds during a running server session.
L2 — disk cache: Persistent across restarts and deployments. The cache key is a SHA-256 content hash of the source strings — so when product content changes in Strapi, the hash changes, the cache misses, and a background re-translation fires automatically. No manual invalidation.
// SHA-256 of sorted source strings → automatic invalidation on content change
export function contentHash(strings: Record<string, string>): string {
const sorted = Object.keys(strings).sort()
.reduce<Record<string, string>>((acc, k) => { acc[k] = strings[k]; return acc }, {})
return crypto.createHash('sha256').update(JSON.stringify(sorted)).digest('hex').slice(0, 20)
}
Writes are atomic (.tmp + rename) to prevent corrupt reads under concurrent requests. TTL is 7 days — but because the hash encodes the content, a 7-day-old cache entry is still valid if the product description hasn't changed.

A /api/translate-warm endpoint allows pre-populating the disk cache on deployment — ensuring cold-start product pages never hit the translation API live during the first request.
Multi-currency and checkout
The storefront handles PLN, EUR, and GBP with live exchange rates fetched via a background API route. Prices are stored in PLN in Strapi and converted client-side using a CurrencyContext — avoiding CMS data duplication while keeping pricing accurate for international buyers.
Authentication uses Clerk, with a full checkout flow and order management accessible from the user dashboard.
Result
A complete, production-deployed e-commerce platform for a niche automotive brand — and a reusable engine that can be licensed, forked, or adapted for any product-driven business. The combination of ISR, content-hash-driven translation caching, and a clean monorepo package structure means the next deployment starts from a working platform, not a blank canvas.