feat: implement proper i18n translations for Serbian and English

- 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
This commit is contained in:
Unchained
2026-03-23 18:28:00 +02:00
parent f72f32fe60
commit bcc51ce282
11 changed files with 700 additions and 577 deletions

View File

@@ -1,4 +1,5 @@
import { getProducts } from "@/lib/saleor";
import { getTranslations } from "next-intl/server";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import HeroVideo from "@/components/home/HeroVideo";
@@ -10,120 +11,108 @@ import BeforeAfterGallery from "@/components/home/BeforeAfterGallery";
import ProblemSection from "@/components/home/ProblemSection";
import HowItWorks from "@/components/home/HowItWorks";
export const metadata = {
title: "ManoonOils - Premium Natural Oils for Hair & Skin",
description:
"Discover our premium collection of natural oils for hair and skin care. Handmade with love using only the finest ingredients.",
};
export async function generateMetadata() {
const t = await getTranslations("Home");
return {
title: "ManoonOils - Premium prirodna ulja za negu kose i kože",
description: "Otkrijte našu premium kolekciju prirodnih ulja za negu kose i kože.",
};
}
export default async function Homepage() {
const t = await getTranslations("Home");
const tBenefits = await getTranslations("Benefits");
let products: any[] = [];
try {
products = await getProducts("SR");
} catch (e) {
// Fallback for build time when API is unavailable
console.log('Failed to fetch products during build');
console.log("Failed to fetch products during build");
}
const featuredProducts = products?.slice(0, 4) || [];
const hasProducts = featuredProducts.length > 0;
return (
<>
<Header />
<main className="min-h-screen bg-white">
{/* Hero Section with Video Background */}
<HeroVideo />
{/* As Seen In */}
<AsSeenIn />
{/* Testimonials Section */}
<ProductReviews />
{/* Trust Badges */}
<TrustBadges />
{/* Problem Section - Create empathy */}
<ProblemSection />
{/* Before/After Gallery */}
<BeforeAfterGallery />
{/* Main Content */}
<div id="main-content" className="scroll-mt-[72px] lg:scroll-mt-[72px]">
{/* Products Grid Section */}
{hasProducts && (
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-white">
<div className="max-w-7xl mx-auto">
{/* Section Header */}
<div className="text-center mb-16">
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Our Collection
{t("collection")}
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-4">
Premium Natural Oils
{t("premiumOils")}
</h2>
<p className="text-[#666666] max-w-xl mx-auto">
Cold-pressed, pure, and natural oils for your daily beauty routine
{t("oilsDescription")}
</p>
</div>
{/* Products Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
{featuredProducts.map((product, index) => (
<ProductCard key={product.id} product={product} index={index} locale="SR" />
))}
</div>
{/* View All Link */}
<div className="text-center mt-12">
<a
href="/products"
className="inline-block text-sm uppercase tracking-[0.1em] border-b border-black pb-1 hover:text-[#666666] hover:border-[#666666] transition-colors"
>
View All Products
{t("viewAll")}
</a>
</div>
</div>
</section>
)}
{/* How It Works */}
<HowItWorks />
{/* Brand Story Section */}
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-[#f8f9fa]">
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20 items-center">
<div>
<span className="text-xs uppercase tracking-[0.2em] text-[#666666] mb-3 block">
Our Story
{t("ourStory")}
</span>
<h2 className="text-3xl md:text-4xl font-medium mb-6">
Handmade with Love
{t("handmadeWithLove")}
</h2>
<p className="text-[#666666] mb-6 leading-relaxed">
Every bottle of ManoonOils is crafted with care using traditional
methods passed down through generations. We source only the finest
organic ingredients to bring you oils that nourish both hair and skin.
{t("storyText1")}
</p>
<p className="text-[#666666] mb-8 leading-relaxed">
Our commitment to purity means no additives, no preservatives -
just nature&apos;s goodness in its most potent form.
{t("storyText2")}
</p>
<a
href="/about"
className="inline-block text-sm uppercase tracking-[0.1em] border-b border-black pb-1 hover:text-[#666666] hover:border-[#666666] transition-colors"
>
Learn More
{t("learnMore")}
</a>
</div>
<div className="relative aspect-[4/3] bg-[#e8f0f5] rounded-lg overflow-hidden">
<img
src="https://images.unsplash.com/photo-1608571423902-eed4a5ad8108?q=80&w=800&auto=format&fit=crop"
alt="Natural oils production"
alt="Proizvodnja prirodnih ulja"
className="w-full h-full object-cover"
/>
</div>
@@ -131,15 +120,14 @@ export default async function Homepage() {
</div>
</section>
{/* Benefits Section */}
<section className="py-24 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-white to-[#faf9f7]">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-16">
<span className="text-xs uppercase tracking-[0.3em] text-[#c9a962] mb-4 block font-medium">
Why Choose Us
{t("whyChooseUs")}
</span>
<h2 className="text-3xl md:text-4xl lg:text-5xl font-medium text-[#1a1a1a]">
The Manoon Difference
{t("manoonDifference")}
</h2>
<div className="w-24 h-1 bg-gradient-to-r from-[#c9a962] to-[#FFD700] mx-auto mt-6 rounded-full" />
</div>
@@ -147,8 +135,8 @@ export default async function Homepage() {
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
{[
{
title: "100% Natural",
description: "Pure, cold-pressed oils with no additives or preservatives. Just nature's goodness.",
title: tBenefits("natural"),
description: tBenefits("naturalDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="#7eb89e"/>
@@ -157,8 +145,8 @@ export default async function Homepage() {
),
},
{
title: "Handcrafted",
description: "Each batch is carefully prepared by hand to ensure the highest quality.",
title: tBenefits("handcrafted"),
description: tBenefits("handcraftedDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path stroke="#c9a962" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" d="M15.182 15.182a4.5 4.5 0 01-6.364 0M21 12a9 9 0 11-18 0 9 9 0 0118 0zM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75zm-.375 0h.008v.015h-.008V9.75zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75zm-.375 0h.008v.015h-.008V9.75z"/>
@@ -166,8 +154,8 @@ export default async function Homepage() {
),
},
{
title: "Sustainable",
description: "Ethically sourced ingredients and eco-friendly packaging for a better planet.",
title: tBenefits("sustainable"),
description: tBenefits("sustainableDesc"),
icon: (
<svg className="w-10 h-10" viewBox="0 0 24 24" fill="none">
<path stroke="#e8967a" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" d="M12.75 3.03v.568c0 .334.148.65.405.864l1.068.89c.442.369.535 1.01.216 1.49l-.51.766a2.25 2.25 0 01-1.161.886l-.143.048a1.107 1.107 0 00-.57 1.664c.369.555.169 1.307-.427 1.605L9 13.125l.423 1.059a.956.956 0 11-1.652.928l-.714-.093a1.125 1.125 0 00-1.906.172L4.5 15.75l-.612.153M12.75 3.031l.002-.004m0 0a8.955 8.955 0 00-4.943.834 8.974 8.974 0 004.943.834m4.943-.834a8.955 8.955 0 00-4.943-.834c2.687 0 5.18.948 7.161 2.664a8.974 8.974 0 014.943-.834z"/>
@@ -175,8 +163,8 @@ export default async function Homepage() {
),
},
].map((benefit, index) => (
<div
key={index}
<div
key={index}
className="relative text-center p-8 bg-white rounded-3xl shadow-lg border border-[#f0ede8] hover:shadow-2xl hover:border-[#c9a962]/30 transition-all duration-500 group"
>
<div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-[#faf9f7] to-[#f5f0e8] flex items-center justify-center shadow-md border border-[#e8e4dc] group-hover:border-[#c9a962]/50 transition-colors duration-300">
@@ -190,31 +178,29 @@ export default async function Homepage() {
</div>
</section>
{/* Newsletter Section */}
<section className="py-28 lg:py-32 px-4 sm:px-6 lg:px-8 bg-[#1a1a1a] text-white">
<div className="max-w-7xl mx-auto">
<div className="max-w-2xl mx-auto text-center">
<span className="text-xs uppercase tracking-[0.2em] text-white/60 mb-3 block">
Stay Connected
{t("stayConnected")}
</span>
<h2 className="text-3xl md:text-4xl lg:text-5xl font-medium mb-6">
Join Our Community
{t("joinCommunity")}
</h2>
<p className="text-white/70 mb-10 mx-auto text-lg">
Subscribe to receive exclusive offers, beauty tips, and be the first to know about new products.
{t("newsletterText")}
</p>
{/* Newsletter Form - Centered */}
<form className="flex flex-col sm:flex-row items-stretch justify-center max-w-md mx-auto gap-0">
<input
type="email"
placeholder="Enter your email"
placeholder={t("emailPlaceholder")}
className="flex-1 min-w-0 px-5 h-14 bg-white/10 border border-white/20 border-b-0 sm:border-b border-r-0 sm:border-r border-white/20 text-white placeholder:text-white/50 focus:border-white focus:outline-none transition-colors text-base text-center sm:text-left rounded-t sm:rounded-l sm:rounded-tr-none"
/>
<button
type="submit"
className="px-8 h-14 bg-white text-black text-sm uppercase tracking-[0.1em] font-medium hover:bg-white/90 transition-colors whitespace-nowrap flex-shrink-0 rounded-b sm:rounded-r sm:rounded-bl-none"
>
Subscribe
{t("subscribe")}
</button>
</form>
</div>