Skip to content

Commit

Permalink
feat: i18n router
Browse files Browse the repository at this point in the history
  • Loading branch information
etiennecl committed Mar 11, 2024
1 parent 44a736c commit f98658b
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 16 deletions.
6 changes: 5 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"title": "Home of the Clinia Health Grade Search Sandbox",
"home": {
"questions": {
"title": "Try asking..."
}
},
"searchbox": {
"placeholder": "Search for words and phrases, or ask a question...",
"groups": {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@clinia-ui/react": "^0.1.8",
"@clinia/search-sdk-core": "^0.1.0",
"@clinia/search-sdk-react": "^0.1.0",
"@uidotdev/usehooks": "^2.4.1",
"clsx": "^2.1.0",
"lucide-react": "^0.354.0",
"next": "14.1.0",
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/app/[locale]/i18n-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getMessages } from '@/i18n';
import type { PropsWithChildren } from 'react';
import { NextIntlClientProvider } from 'next-intl';
import type { useTimeZone } from 'next-intl';
Expand All @@ -12,7 +13,7 @@ export default async function I18nProvider({
locale,
timeZone,
}: I18nProviderProps) {
const messages = (await import(`../../../messages/${locale}.json`)).default;
const messages = await getMessages(locale);

return (
<NextIntlClientProvider
Expand Down
12 changes: 10 additions & 2 deletions src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { Searchbox } from '@/components/searchbox';
import { useTranslations } from 'next-intl';
import { Badge, Button } from '@clinia-ui/react';

export default function Home() {
const t = useTranslations();
return (
<div className="flex justify-center">
<div className="w-[570px]">
<div className="grid justify-items-center">
<div className="w-[570px] py-80">
<Searchbox />
</div>
<div className="flex flex-col gap-4">
<h1 className="pb-4 text-center text-base font-medium text-foreground">
{t('home.questions.title')}
</h1>
<Button>How long to recover from ACL tear?</Button>
<Button>How reliable are COVID tests?</Button>
</div>
</div>
);
}
9 changes: 9 additions & 0 deletions src/app/[locale]/search/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Searchbox } from '@/components/searchbox';

export default function Search() {
return (
<div>
<Searchbox />
</div>
);
}
24 changes: 20 additions & 4 deletions src/components/searchbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
'use client';

import { useI18nRouter } from '@/lib/use-i18n-router';
import { useClickAway } from '@uidotdev/usehooks';
import { Search, Sparkles } from 'lucide-react';
import { twMerge } from 'tailwind-merge';
import { useMemo, useState } from 'react';
import { useTranslations } from 'next-intl';
import {
Expand All @@ -12,10 +15,13 @@ import {
CommandList,
} from '@clinia-ui/react';

export const Searchbox = () => {
type SearchBoxProps = React.HTMLAttributes<HTMLDivElement>;

export const Searchbox = ({ className, ...props }: SearchBoxProps) => {
const t = useTranslations();
const [value, setValue] = useState('');
const [open, setOpen] = useState(false);
const router = useI18nRouter();

const groups = useMemo(
() => [
Expand All @@ -37,14 +43,24 @@ export const Searchbox = () => {
[t]
);

const ref = useClickAway<HTMLDivElement>(() => setOpen(false));

const handleSearch = (v: string) => {
router.push(`/search?q=${v}`);
};

return (
<Command className="border" loop>
<Command className={twMerge('border', className)} loop ref={ref} {...props}>
<CommandInput
placeholder={t('searchbox.placeholder')}
value={value}
onFocus={() => setOpen(true)}
onBlur={() => setOpen(false)}
onValueChange={(v) => setValue(v)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSearch(value);
}
}}
/>

<CommandList>
Expand All @@ -64,7 +80,7 @@ export const Searchbox = () => {
<CommandItem
key={item}
className="gap-2"
onSelect={(v) => console.log(v)}
onSelect={(v) => handleSearch(v)}
>
{group.icon}
{item}
Expand Down
30 changes: 22 additions & 8 deletions src/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
import { getRequestConfig } from 'next-intl/server';
import { notFound } from 'next/navigation';

// Can be imported from a shared config
const locales = ['en', 'de'];
export default getRequestConfig(async ({locale}) => {

export default getRequestConfig(async ({ locale }) => {
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale as any)) notFound();

return {
messages: (await import(`../messages/${locale}.json`)).default
messages: (await import(`../messages/${locale}.json`)).default,
};
});
});

const messages = {
en: (): Record<string, any> =>
import('../messages/en.json').then((module) => module.default),
fr: (): Record<string, any> =>
import('../messages/fr.json').then((module) => module.default),
};

type Locales = keyof typeof messages;

export const getMessages = async (locale: string) => {
const key = locale as Locales;
return messages[key]();
};
15 changes: 15 additions & 0 deletions src/lib/use-i18n-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useParams, useRouter } from 'next/navigation';

export const useI18nRouter = (): ReturnType<typeof useRouter> => {
const { locale } = useParams();
const router = useRouter();

return {
push: (url: string) => router.push(`/${locale}${url}`),
forward: () => router.forward(),
back: () => router.back(),
replace: (url: string) => router.replace(`/${locale}${url}`),
refresh: () => router.refresh(),
prefetch: (url: string) => router.prefetch(`/${locale}${url}`),
};
};

0 comments on commit f98658b

Please sign in to comment.