From 7e17796b8f4cad4c50021b18736c343b7b7ce671 Mon Sep 17 00:00:00 2001 From: Jorge Moya Date: Tue, 14 Jan 2025 15:47:59 -0600 Subject: [PATCH] feat(core): add locale switcher to Header (#1907) * feat(core): add locale switcher to Header * fix: add translation for error * chore: add context comment --- .../header/_actions/switch-locale.ts | 40 +++++++++++++++++++ core/components/header/index.tsx | 13 +++++- core/messages/en.json | 5 +-- 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 core/components/header/_actions/switch-locale.ts diff --git a/core/components/header/_actions/switch-locale.ts b/core/components/header/_actions/switch-locale.ts new file mode 100644 index 0000000000..3146478ddb --- /dev/null +++ b/core/components/header/_actions/switch-locale.ts @@ -0,0 +1,40 @@ +'use server'; + +import { SubmissionResult } from '@conform-to/react'; +import { parseWithZod } from '@conform-to/zod'; +import { revalidatePath } from 'next/cache'; +import { getTranslations } from 'next-intl/server'; + +import { localeSchema } from '@/vibes/soul/primitives/navigation/schema'; +import { defaultLocale, redirect } from '~/i18n/routing'; + +export const switchLocale = async (_prevState: SubmissionResult | null, payload: FormData) => { + const t = await getTranslations('Components.Header.Locale'); + + const submission = parseWithZod(payload, { schema: localeSchema }); + + if (submission.status !== 'success') { + return submission.reply({ formErrors: [t('invalidLocale')] }); + } + + await Promise.resolve(); + + revalidatePath('/'); + + // Since `redirect` doesn't prepend the local to the redirect url + // when navigating the a default locale link, we need to prepend + // it ourselves to ensure the redirect happens. + if (submission.value.id === defaultLocale) { + redirect({ + href: `/${submission.value.id}`, + locale: submission.value.id, + }); + } else { + redirect({ + href: `/`, + locale: submission.value.id, + }); + } + + return submission.reply({ resetForm: true }); +}; diff --git a/core/components/header/index.tsx b/core/components/header/index.tsx index 07f36ce4d6..dd0d065e49 100644 --- a/core/components/header/index.tsx +++ b/core/components/header/index.tsx @@ -1,5 +1,5 @@ import { cookies } from 'next/headers'; -import { getTranslations } from 'next-intl/server'; +import { getLocale, getTranslations } from 'next-intl/server'; import PLazy from 'p-lazy'; import { cache } from 'react'; @@ -11,8 +11,10 @@ import { graphql, readFragment } from '~/client/graphql'; import { revalidate } from '~/client/revalidate-target'; import { TAGS } from '~/client/tags'; import { logoTransformer } from '~/data-transformers/logo-transformer'; +import { routing } from '~/i18n/routing'; import { search } from './_actions/search'; +import { switchLocale } from './_actions/switch-locale'; import { HeaderFragment } from './fragment'; const GetCartCountQuery = graphql(` @@ -95,6 +97,12 @@ const getCartCount = async () => { export const Header = async () => { const t = await getTranslations('Components.Header'); + const locale = await getLocale(); + + const locales = routing.locales.map((enabledLocales) => ({ + id: enabledLocales, + label: enabledLocales.toLocaleUpperCase(), + })); return ( { openSearchPopupLabel: t('Search.openSearchPopup'), logoLabel: t('home'), cartCount: PLazy.from(getCartCount), + activeLocaleId: locale, + locales, + localeAction: switchLocale, }} /> ); diff --git a/core/messages/en.json b/core/messages/en.json index e23cacb1fe..2f23ae03dc 100644 --- a/core/messages/en.json +++ b/core/messages/en.json @@ -507,9 +507,8 @@ "login": "Login", "logout": "Log out" }, - "LocaleSwitcher": { - "chooseCountryAndLanguage": "Choose your country and language", - "goToSite": "Go to site" + "Locale": { + "invalidLocale": "Invalid locale" }, "MiniCart": { "cart": "Cart",