feat(i18n): wire services page and sub-components to translations
Some checks failed
Build & Push / build-and-push (push) Failing after 2m38s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 15:01:33 -04:00
parent 1705b618c3
commit 00f78f53d7
4 changed files with 46 additions and 158 deletions

View File

@@ -36,136 +36,6 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
}; };
} }
// ─── Service data ──────────────────────────────────────────────────────────────
export const SERVICE_PILLARS = [
{
id: 'design-development',
numeral: '01',
title: 'Web Design & Development',
description:
'Your website shouldn\'t look like everyone else\'s — and it shouldn\'t be built like everyone else\'s either. We design and build custom websites and web applications from a blank canvas, crafting every layout, every interaction, and every page with intention. The result is fast, search-engine-friendly, and built to grow with your business. Whether you need a marketing site that converts, a web application your team relies on, or an e-commerce platform that scales — we build it from scratch, and we build it to last.',
background: 'bg-surface' as const,
features: [
{
icon: 'Palette',
title: 'Custom Design',
description:
'Every layout, component, and interaction is designed for your brand. No themes, no templates, no shortcuts.',
},
{
icon: 'Globe',
title: 'Web Applications',
description:
'Modern, responsive applications built with the latest technologies — fast, reliable, and ready to scale.',
},
{
icon: 'ShoppingCart',
title: 'E-Commerce',
description:
'Custom storefronts, checkout flows, and multi-currency platforms built for serious online retail.',
},
{
icon: 'Zap',
title: 'Performance & SEO',
description:
'Fast load times, clean code, and search engine optimization built into the foundation — not bolted on after.',
},
],
},
{
id: 'custom-systems',
numeral: '02',
title: 'Software & Platforms',
description:
'Off-the-shelf software makes assumptions about how your business works. We don\'t. When spreadsheets and generic tools stop cutting it, we build the exact system your team needs — designed around your workflow, not someone else\'s. From CRMs tailored to your sales process, to management platforms that replace three different subscriptions, to integrations that connect your existing tools — everything we build is yours, fully documented, and built to last.',
background: 'bg-surface-low' as const,
features: [
{
icon: 'Database',
title: 'CRM & Management Tools',
description:
'Relationship and pipeline management built around how your team actually works — not how a generic platform thinks you should.',
},
{
icon: 'Code2',
title: 'Custom Software',
description:
'From booking platforms to internal tools to full SaaS products — purpose-built for your business.',
},
{
icon: 'GitBranch',
title: 'Integrations & APIs',
description:
'We connect your existing tools and build the bridges between systems so everything works together.',
},
{
icon: 'Wrench',
title: 'Dashboards & Automation',
description:
'Admin panels, reporting tools, and workflow automation that give your team an unfair advantage.',
},
],
},
{
id: 'infrastructure',
numeral: '03',
title: 'Hosting & Infrastructure',
description:
'Your website and software need a home — and we think you should own it. We set up and manage dedicated servers, email, cloud storage, and all the infrastructure your business runs on. No shared hosting, no mysterious third-party dependencies. You know where your data lives, who has access, and that someone is watching the dashboard around the clock.',
background: 'bg-surface' as const,
features: [
{
icon: 'Server',
title: 'Dedicated Hosting',
description:
'Private servers managed for your business — no shared hosting, no noisy neighbors, no surprises.',
},
{
icon: 'Shield',
title: 'Your Data, Your Control',
description:
'You own your data and know exactly where it lives. Full access, full transparency, no lock-in.',
},
{
icon: 'Lock',
title: 'Security & Protection',
description:
'Serious security, proactive monitoring, and protection built into your infrastructure from day one.',
},
{
icon: 'Settings',
title: 'Monitoring & Support',
description:
'Proactive monitoring, regular updates, and ongoing support so you never have to worry about uptime.',
},
],
},
] as const;
// ─── AI Layer data ─────────────────────────────────────────────────────────────
export const AI_CAPABILITIES = [
{
id: 'ai-teammate',
title: 'AI Teammate',
description:
'An AI assistant built into your workflow — automates repetitive tasks, surfaces the info your team needs, and connects your tools.',
},
{
id: 'customer-facing-ai',
title: 'Customer-Facing AI',
description:
'Smart features for your customers — intelligent search, personalized recommendations, and conversational interfaces that work around the clock.',
},
{
id: 'data-intelligence',
title: 'Data Intelligence',
description:
'AI that helps you understand your data — automated reports, trend spotting, and insights you can actually act on.',
},
] as const;
// ─── Page ────────────────────────────────────────────────────────────────────── // ─── Page ──────────────────────────────────────────────────────────────────────
type Props = { type Props = {
@@ -176,19 +46,37 @@ export default async function ServicesPage({ params }: Props) {
const { locale } = await params; const { locale } = await params;
setRequestLocale(locale); setRequestLocale(locale);
const t = await getTranslations({ locale, namespace: 'servicesPage' })
const backgrounds = ['bg-surface', 'bg-surface-low', 'bg-surface'] as const
const pillars = [0, 1, 2].map((i) => ({
id: t(`pillars.${i}.id`),
numeral: t(`pillars.${i}.numeral`),
title: t(`pillars.${i}.title`),
description: t(`pillars.${i}.description`),
background: backgrounds[i],
features: [0, 1, 2, 3].map((j) => ({
icon: t(`pillars.${i}.features.${j}.icon`),
title: t(`pillars.${i}.features.${j}.title`),
description: t(`pillars.${i}.features.${j}.description`),
})),
}))
const aiCapabilities = [0, 1, 2].map((i) => ({
id: t(`ai.capabilities.${i}.id`),
title: t(`ai.capabilities.${i}.title`),
description: t(`ai.capabilities.${i}.description`),
}))
return ( return (
<main> <main>
<ServicesHero /> <ServicesHero />
{SERVICE_PILLARS.map((pillar, index) => ( {pillars.map((pillar, index) => (
<ServicePillar <ServicePillar key={pillar.id} pillar={pillar} index={index} />
key={pillar.id}
pillar={pillar}
index={index}
/>
))} ))}
<AILayer capabilities={AI_CAPABILITIES} /> <AILayer capabilities={aiCapabilities} />
<ServicesCTA /> <ServicesCTA />
</main> </main>

View File

@@ -1,6 +1,7 @@
'use client'; 'use client';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useTranslations } from 'next-intl';
import { Users, MessageCircle, BarChart3 } from 'lucide-react'; import { Users, MessageCircle, BarChart3 } from 'lucide-react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { import {
@@ -115,6 +116,7 @@ function AICapabilityCard({ capability }: { capability: AiCapability }) {
// ─── Component ───────────────────────────────────────────────────────────────── // ─── Component ─────────────────────────────────────────────────────────────────
export default function AILayer({ capabilities }: AILayerProps) { export default function AILayer({ capabilities }: AILayerProps) {
const t = useTranslations('servicesPage.ai')
return ( return (
<section <section
id="ai-automation" id="ai-automation"
@@ -137,7 +139,7 @@ export default function AILayer({ capabilities }: AILayerProps) {
variants={revealVariants} variants={revealVariants}
className="label-md text-primary mb-4" className="label-md text-primary mb-4"
> >
Intelligent Layer {t('eyebrow')}
</motion.span> </motion.span>
{/* Heading */} {/* Heading */}
@@ -146,7 +148,7 @@ export default function AILayer({ capabilities }: AILayerProps) {
variants={revealVariants} variants={revealVariants}
className="font-serif font-semibold tracking-headline text-white text-4xl md:text-5xl max-w-2xl leading-[1.1]" className="font-serif font-semibold tracking-headline text-white text-4xl md:text-5xl max-w-2xl leading-[1.1]"
> >
AI Built Into Everything {t('title')}
</motion.h2> </motion.h2>
{/* Vertical line */} {/* Vertical line */}
@@ -163,7 +165,7 @@ export default function AILayer({ capabilities }: AILayerProps) {
className="font-serif italic text-xl leading-relaxed max-w-xl" className="font-serif italic text-xl leading-relaxed max-w-xl"
style={{ color: 'rgba(255,255,255,0.75)' }} style={{ color: 'rgba(255,255,255,0.75)' }}
> >
Your platform, made smarter. {t('subtitle')}
</motion.p> </motion.p>
{/* Context paragraph */} {/* Context paragraph */}
@@ -172,10 +174,7 @@ export default function AILayer({ capabilities }: AILayerProps) {
className="mt-5 text-[0.9375rem] leading-relaxed max-w-2xl" className="mt-5 text-[0.9375rem] leading-relaxed max-w-2xl"
style={{ color: 'rgba(255,255,255,0.5)' }} style={{ color: 'rgba(255,255,255,0.5)' }}
> >
We integrate AI directly into the websites and software we build {t('description')}
for you. Not as a buzzword or an add-on as practical features
that save your team time and give your customers a better
experience.
</motion.p> </motion.p>
</motion.div> </motion.div>
@@ -201,7 +200,7 @@ export default function AILayer({ capabilities }: AILayerProps) {
className="mt-10 text-center text-xs uppercase tracking-widest" className="mt-10 text-center text-xs uppercase tracking-widest"
style={{ color: 'rgba(255,255,255,0.25)' }} style={{ color: 'rgba(255,255,255,0.25)' }}
> >
Every AI feature is tailored to your business your data stays on your servers {t('bottomNote')}
</motion.p> </motion.p>
</div> </div>

View File

@@ -1,6 +1,7 @@
'use client'; 'use client';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useTranslations } from 'next-intl';
import { ArrowRight } from 'lucide-react'; import { ArrowRight } from 'lucide-react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { import {
@@ -27,6 +28,7 @@ const decorLineVariants = {
// ─── Component ───────────────────────────────────────────────────────────────── // ─── Component ─────────────────────────────────────────────────────────────────
export default function ServicesCTA() { export default function ServicesCTA() {
const t = useTranslations('servicesPage.cta')
return ( return (
<section <section
className="bg-surface-low py-24" className="bg-surface-low py-24"
@@ -90,22 +92,21 @@ export default function ServicesCTA() {
{/* Eyebrow */} {/* Eyebrow */}
<ScrollReveal variant="fadeUp"> <ScrollReveal variant="fadeUp">
<span className="label-md text-primary"> <span className="label-md text-primary">
Let&apos;s Talk {t('eyebrow')}
</span> </span>
</ScrollReveal> </ScrollReveal>
{/* Heading */} {/* Heading */}
<ScrollReveal variant="fadeUp" delay={0.08}> <ScrollReveal variant="fadeUp" delay={0.08}>
<h2 className="font-serif font-semibold tracking-headline text-on-surface text-4xl md:text-5xl max-w-2xl leading-[1.1]"> <h2 className="font-serif font-semibold tracking-headline text-on-surface text-4xl md:text-5xl max-w-2xl leading-[1.1]">
Ready to get started? {t('title')}
</h2> </h2>
</ScrollReveal> </ScrollReveal>
{/* Subtitle */} {/* Subtitle */}
<ScrollReveal variant="fadeUp" delay={0.16}> <ScrollReveal variant="fadeUp" delay={0.16}>
<p className="text-lg text-outline leading-relaxed max-w-xl"> <p className="text-lg text-outline leading-relaxed max-w-xl">
Walk through a few questions and we&apos;ll put together a project {t('subtitle')}
brief tailored to you no commitment required, just clarity.
</p> </p>
</ScrollReveal> </ScrollReveal>
@@ -118,14 +119,14 @@ export default function ServicesCTA() {
size="lg" size="lg"
arrow arrow
> >
Start Your Project {t('primary')}
</Button> </Button>
<Button <Button
href="mailto:hello@letsbe.biz" href="mailto:hello@letsbe.biz"
variant="secondary" variant="secondary"
size="lg" size="lg"
> >
hello@letsbe.biz {t('email')}
</Button> </Button>
</div> </div>
</ScrollReveal> </ScrollReveal>
@@ -133,7 +134,7 @@ export default function ServicesCTA() {
{/* Reassurance */} {/* Reassurance */}
<ScrollReveal variant="fadeIn" delay={0.3}> <ScrollReveal variant="fadeIn" delay={0.3}>
<p className="text-sm text-outline/60 mt-1"> <p className="text-sm text-outline/60 mt-1">
No commitment required just a conversation about what&apos;s possible. {t('reassurance')}
</p> </p>
</ScrollReveal> </ScrollReveal>

View File

@@ -1,6 +1,7 @@
'use client'; 'use client';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useTranslations } from 'next-intl';
import { staggerContainer, revealVariants, viewportOnce } from '@/lib/animations'; import { staggerContainer, revealVariants, viewportOnce } from '@/lib/animations';
// ─── Animation variants ──────────────────────────────────────────────────────── // ─── Animation variants ────────────────────────────────────────────────────────
@@ -45,6 +46,7 @@ const ruleVariants = {
// ─── Component ───────────────────────────────────────────────────────────────── // ─── Component ─────────────────────────────────────────────────────────────────
export default function ServicesHero() { export default function ServicesHero() {
const t = useTranslations('servicesPage.hero')
return ( return (
<section <section
className="bg-surface pt-32 pb-20" className="bg-surface pt-32 pb-20"
@@ -62,7 +64,7 @@ export default function ServicesHero() {
variants={eyebrowVariants} variants={eyebrowVariants}
className="label-md text-primary mb-5" className="label-md text-primary mb-5"
> >
Our Services {t('eyebrow')}
</motion.span> </motion.span>
{/* Headline */} {/* Headline */}
@@ -70,8 +72,8 @@ export default function ServicesHero() {
variants={headlineVariants} variants={headlineVariants}
className="font-serif font-semibold tracking-headline text-on-surface text-5xl md:text-6xl lg:text-7xl max-w-4xl leading-[1.05]" className="font-serif font-semibold tracking-headline text-on-surface text-5xl md:text-6xl lg:text-7xl max-w-4xl leading-[1.05]"
> >
Everything your business{' '} {t('title')}{' '}
<span className="text-gradient">needs online.</span> <span className="text-gradient">{t('titleAccent')}</span>
</motion.h1> </motion.h1>
{/* Subtitle */} {/* Subtitle */}
@@ -79,9 +81,7 @@ export default function ServicesHero() {
variants={subtitleVariants} variants={subtitleVariants}
className="mt-6 text-lg text-outline leading-relaxed max-w-2xl" className="mt-6 text-lg text-outline leading-relaxed max-w-2xl"
> >
We design custom websites, build purpose-built software, and manage {t('subtitle')}
the infrastructure behind it all one team, one standard of
quality, nothing outsourced.
</motion.p> </motion.p>
{/* Decorative rule */} {/* Decorative rule */}