Skip to content

Feature/add next intl for localization #456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ NEXT_PUBLIC_STRIPE_KEY=

# Your Next.js revalidation secret. See – https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#on-demand-revalidation
REVALIDATE_SECRET=supersecret

# displays missing translation keys for debugging
NEXT_PUBLIC_TRANSLATION_HELPERS=false
9 changes: 8 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
const checkEnvVariables = require("./check-env-variables")
const nextIntl = require("next-intl/plugin")


checkEnvVariables()

/**
* @type {import('next').NextConfig}
*/
const nextConfig = {

const withNextIntl = nextIntl("./src/lib/i18n/request-config.js")

const configOpts = {
reactStrictMode: true,
logging: {
fetches: {
Expand Down Expand Up @@ -40,4 +45,6 @@ const nextConfig = {
},
}

const nextConfig = withNextIntl(configOpts)

module.exports = nextConfig
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@stripe/stripe-js": "^1.29.0",
"lodash": "^4.17.21",
"next": "15.0.3",
"next-intl": "^3.26.3",
"pg": "^8.11.3",
"qs": "^6.12.1",
"react": "19.0.0-rc-66855b96-20241106",
Expand Down
43 changes: 0 additions & 43 deletions src/app/[countryCode]/(checkout)/layout.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import CheckoutForm from "@modules/checkout/templates/checkout-form"
import CheckoutSummary from "@modules/checkout/templates/checkout-summary"
import { Metadata } from "next"
import { notFound } from "next/navigation"
import { Suspense } from "react"


export const metadata: Metadata = {
title: "Checkout",
Expand All @@ -20,11 +22,13 @@ export default async function Checkout() {
const customer = await retrieveCustomer()

return (
<div className="grid grid-cols-1 small:grid-cols-[1fr_416px] content-container gap-x-40 py-12">
<PaymentWrapper cart={cart}>
<CheckoutForm cart={cart} customer={customer} />
</PaymentWrapper>
<CheckoutSummary cart={cart} />
</div>
<Suspense fallback={<></>}>
<div className="grid grid-cols-1 small:grid-cols-[1fr_416px] content-container gap-x-40 py-12">
<PaymentWrapper cart={cart}>
<CheckoutForm cart={cart} customer={customer} />
</PaymentWrapper>
<CheckoutSummary cart={cart} />
</div>
</Suspense>
)
}
50 changes: 50 additions & 0 deletions src/app/[locale]/[countryCode]/(checkout)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import k from "@lib/i18n/translations/keys"
import { getTranslations } from "next-intl/server"
import LocalizedClientLink from "@modules/common/components/localized-client-link"
import ChevronDown from "@modules/common/icons/chevron-down"
import MedusaCTA from "@modules/layout/components/medusa-cta"
import { Suspense } from "react"

export default async function CheckoutLayout({
children,
}: {
children: React.ReactNode
params: { locale: string }
}) {
const t = await getTranslations()
return (
<Suspense fallback={<></>}>
<div className="w-full bg-white relative small:min-h-screen">
<div className="h-16 bg-white border-b ">
<nav className="flex h-full items-center content-container justify-between">
<LocalizedClientLink
href="/cart"
className="text-small-semi text-ui-fg-base flex items-center gap-x-2 uppercase flex-1 basis-0"
data-testid="back-to-cart-link"
>
<ChevronDown className="rotate-90" size={16} />
<span className="mt-px hidden small:block txt-compact-plus text-ui-fg-subtle hover:text-ui-fg-base ">
{t(k.BACK_TO_SHOPPING_CART)}
</span>
<span className="mt-px block small:hidden txt-compact-plus text-ui-fg-subtle hover:text-ui-fg-base">
{t(k.BACK)}
</span>
</LocalizedClientLink>
<LocalizedClientLink
href="/"
className="txt-compact-xlarge-plus text-ui-fg-subtle hover:text-ui-fg-base uppercase"
data-testid="store-link"
>
{t(k.MEDUSA_STORE)}
</LocalizedClientLink>
<div className="flex-1 basis-0" />
</nav>
</div>
<div className="relative" data-testid="checkout-container">{children}</div>
<div className="py-4 w-full flex items-center justify-center">
<MedusaCTA />
</div>
</div>
</Suspense>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import k from "@lib/i18n/translations/keys"
import { useSafeTranslations } from "@lib/i18n/use-safe-translations"
import InteractiveLink from "@modules/common/components/interactive-link"
import { Metadata } from "next"

Expand All @@ -7,13 +9,14 @@ export const metadata: Metadata = {
}

export default async function NotFound() {
const t = useSafeTranslations()
return (
<div className="flex flex-col gap-4 items-center justify-center min-h-[calc(100vh-64px)]">
<h1 className="text-2xl-semi text-ui-fg-base">Page not found</h1>
<h1 className="text-2xl-semi text-ui-fg-base">{t(k.PAGE_NOT_FOUND)}</h1>
<p className="text-small-regular text-ui-fg-base">
The page you tried to access does not exist.
{t(k.THE_PAGE_YOU_TRIED_TO_ACCESS_D)}
</p>
<InteractiveLink href="/">Go to frontpage</InteractiveLink>
<InteractiveLink href="/">{t(k.GO_TO_FRONTPAGE)}</InteractiveLink>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import k from "@lib/i18n/translations/keys"
import { getTranslations } from "next-intl/server"

import { Metadata } from "next"
import { notFound } from "next/navigation"

Expand All @@ -18,6 +21,7 @@ export default async function Addresses(props: {
const { countryCode } = params
const customer = await retrieveCustomer()
const region = await getRegion(countryCode)
const t = await getTranslations()

if (!customer || !region) {
notFound()
Expand All @@ -26,10 +30,9 @@ export default async function Addresses(props: {
return (
<div className="w-full" data-testid="addresses-page-wrapper">
<div className="mb-8 flex flex-col gap-y-4">
<h1 className="text-2xl-semi">Shipping Addresses</h1>
<h1 className="text-2xl-semi">{t(k.SHIPPING_ADDRESSES)}</h1>
<p className="text-base-regular">
View and update your shipping addresses, you can add as many as you
like. Saving your addresses will make them available during checkout.
{t(k.VIEW_AND_UPDATE_YOUR_SHIPPING)}
</p>
</div>
<AddressBook customer={customer} region={region} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import k from "@lib/i18n/translations/keys"
import { useSafeTranslations } from "@lib/i18n/use-safe-translations"

import { retrieveOrder } from "@lib/data/orders"
import OrderDetailsTemplate from "@modules/order/templates/order-details-template"
import { Metadata } from "next"
Expand All @@ -8,16 +11,19 @@ type Props = {
}

export async function generateMetadata(props: Props): Promise<Metadata> {

const t = useSafeTranslations()

const params = await props.params
const order = await retrieveOrder(params.id).catch(() => null)

if (!order) {
notFound()
}

return {
title: `Order #${order.display_id}`,
description: `View your order`,
title: `${t(k.ORDER)} #${order.display_id}`,
description: `${t(k.VIEW_YOUR_ORDER)}`,
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import k from "@lib/i18n/translations/keys"
import { useSafeTranslations } from "@lib/i18n/use-safe-translations"
import { getTranslations } from "next-intl/server"
import { Metadata } from "next"

import OrderOverview from "@modules/account/components/order-overview"
Expand All @@ -13,6 +16,7 @@ export const metadata: Metadata = {

export default async function Orders() {
const orders = await listOrders()
const t = await getTranslations()

if (!orders) {
notFound()
Expand All @@ -21,10 +25,9 @@ export default async function Orders() {
return (
<div className="w-full" data-testid="orders-page-wrapper">
<div className="mb-8 flex flex-col gap-y-4">
<h1 className="text-2xl-semi">Orders</h1>
<h1 className="text-2xl-semi">{t(k.ORDERS)}</h1>
<p className="text-base-regular">
View your previous orders and their status. You can also create
returns or exchanges for your orders if needed.
{t(k.VIEW_YOUR_PREVIOUS_ORDERS_AND)}
</p>
</div>
<div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import k from "@lib/i18n/translations/keys"
import { getTranslations } from "next-intl/server"

import { Metadata } from "next"

import ProfilePhone from "@modules/account//components/profile-phone"
Expand All @@ -18,6 +21,7 @@ export const metadata: Metadata = {
export default async function Profile() {
const customer = await retrieveCustomer()
const regions = await listRegions()
const t = await getTranslations()

if (!customer || !regions) {
notFound()
Expand All @@ -26,11 +30,9 @@ export default async function Profile() {
return (
<div className="w-full" data-testid="profile-page-wrapper">
<div className="mb-8 flex flex-col gap-y-4">
<h1 className="text-2xl-semi">Profile</h1>
<h1 className="text-2xl-semi">{t(k.PROFILE)}</h1>
<p className="text-base-regular">
View and update your profile information, including your name, email,
and phone number. You can also update your billing address, or change
your password.
{t(k.VIEW_AND_UPDATE_YOUR_PROFILE_I)}
</p>
</div>
<div className="flex flex-col gap-y-8 w-full">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import k from "@lib/i18n/translations/keys"
import { useSafeTranslations } from "@lib/i18n/use-safe-translations"

import { Metadata } from "next"

import InteractiveLink from "@modules/common/components/interactive-link"

const t = useSafeTranslations()

export const metadata: Metadata = {
title: "404",
description: "Something went wrong",
Expand All @@ -10,12 +15,11 @@ export const metadata: Metadata = {
export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-64px)]">
<h1 className="text-2xl-semi text-ui-fg-base">Page not found</h1>
<h1 className="text-2xl-semi text-ui-fg-base">{t(k.PAGE_NOT_FOUND)}</h1>
<p className="text-small-regular text-ui-fg-base">
The cart you tried to access does not exist. Clear your cookies and try
again.
{t(k.THE_CART_YOU_TRIED_TO_ACCESS_D)}
</p>
<InteractiveLink href="/">Go to frontpage</InteractiveLink>
<InteractiveLink href="/">{t(k.GO_TO_FRONTPAGE)}</InteractiveLink>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { listRegions } from "@lib/data/regions"
import { StoreRegion } from "@medusajs/types"
import CategoryTemplate from "@modules/categories/templates"
import { SortOptions } from "@modules/store/components/refinement-list/sort-products"
import { setRequestLocale } from "next-intl/server"

type Props = {
params: Promise<{ category: string[]; countryCode: string }>
params: Promise<{ category: string[]; countryCode: string; locale: string }>
searchParams: Promise<{
sortBy?: SortOptions
page?: string
Expand Down Expand Up @@ -66,6 +67,7 @@ export async function generateMetadata(props: Props): Promise<Metadata> {
export default async function CategoryPage(props: Props) {
const searchParams = await props.searchParams
const params = await props.params
setRequestLocale(params.locale)
const { sortBy, page } = searchParams

const productCategory = await getCategoryByHandle(params.category)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { listRegions } from "@lib/data/regions"
import { StoreCollection, StoreRegion } from "@medusajs/types"
import CollectionTemplate from "@modules/collections/templates"
import { SortOptions } from "@modules/store/components/refinement-list/sort-products"
import { setRequestLocale } from "next-intl/server"

type Props = {
params: Promise<{ handle: string; countryCode: string }>
params: Promise<{ handle: string; countryCode: string; locale: string }>
searchParams: Promise<{
page?: string
sortBy?: SortOptions
Expand Down Expand Up @@ -70,6 +71,7 @@ export default async function CollectionPage(props: Props) {
const searchParams = await props.searchParams
const params = await props.params
const { sortBy, page } = searchParams
setRequestLocale(params.locale)

const collection = await getCollectionByHandle(params.handle).then(
(collection: StoreCollection) => collection
Expand Down
Loading