Tổng hợp ví dụ của chính next-intl triển khai với pages, app (ok)

Nghiên cứu dự án mẫu trong next-intl

— 1. example-pages-router

messages\en.json

{
  "Index": {
    "title": "Home",
    "description": "This is the home page."
  },
  "LocaleSwitcher": {
    "switchLocale": "Switch to {locale, select, vi {Vietnam} en {English} other {Unknown}}"
  },
  "PageLayout": {
    "pageTitle": "Next Intl En"
  }
}

messages\vi.json

{
  "Index": {
    "title": "Trang chủ",
    "description": "Đây là trang chủ"
  },
  "LocaleSwitcher": {
    "switchLocale": "Chuyển sang {locale, select, vi {Vietnam} en {English} other {Unknown}}"
  },
  "PageLayout": {
    "pageTitle": "Next Intl Vi"
  }
}

next.config.ts

import type { NextConfig } from "next";
const nextConfig: NextConfig = {
  i18n: {
    locales: ['en', 'vi'],
    defaultLocale: 'en'
  },
  reactStrictMode: true
};
export default nextConfig;

src\pages\index.tsx

import {GetStaticPropsContext} from 'next';
import {useTranslations} from 'next-intl';
import LocaleSwitcher from '@/pages/components/LocaleSwitcher';
import PageLayout from '@/pages/components/PageLayout';
export default function Home() {
  const t = useTranslations('Index');
  return (
    <PageLayout title={t('title')}>
      <p>{t('description')}</p>
      <LocaleSwitcher />
    </PageLayout>
  );
}
export async function getStaticProps({locale}: GetStaticPropsContext) {
  return {
    props: {
      messages: (await import(`../../messages/${locale}.json`)).default
    }
  };
}

src\pages\_app.tsx

import "@/styles/globals.css";
import type { AppProps } from "next/app";
import {useRouter} from 'next/router';
import {NextIntlClientProvider} from 'next-intl';
export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();
  return (
    <NextIntlClientProvider
      locale={router.locale}
      messages={pageProps.messages}
      timeZone="Europe/Vienna"
    >
      <Component {...pageProps} />;
    </NextIntlClientProvider>
  )
}

src\pages\components\LocaleSwitcher.tsx

import Link from 'next/link';
import { useRouter } from 'next/router';
import { useTranslations } from 'next-intl';
export default function LocaleSwitcher() {
  const t = useTranslations('LocaleSwitcher');
  const { locale, locales, route } = useRouter();
  const otherLocale = locales?.find((cur) => cur !== locale) as string;
  return (
    <Link href={route} locale={otherLocale}>
      {t('switchLocale', { locale: otherLocale })}
    </Link>
  )
}

src\pages\components\PageLayout.tsx

import Head from 'next/head';
import { useTranslations } from 'next-intl';
import { ReactNode } from 'react';
type Props = {
  children?: ReactNode;
  title: string;
};
export default function PageLayout({ children, title }: Props) {
  const t = useTranslations('PageLayout')
  return (
    <>
      <Head>
        <title>{[title, t('pageTitle')].join(' - ')}</title>
      </Head>
      <div className="container m-auto">
        <h1>{title}</h1>
        {children}
      </div>
    </>
  )
}

pages\index.tsx

import messages from '../messages/en.json';
export default function Home() {
  console.log(typeof messages);
  return (
    <div>
      Home
    </div>
  );
}
Kết quả: console.log(typeof messages); object

— 2. example-pages-router-advanced

messages\en.json

{
  "Index": {
    "title": "Home",
    "description": "<p>Only the minimum of <code>{locale}</code> messages are loaded to render this page.</p><p>These namespaces are available:</p>"
  },
  "LocaleSwitcher": {
    "switchLocale": "Switch to {locale, select, vi {Vietnam} en {English} other {Unknown}}"
  },
  "PageLayout": {
    "pageTitle": "Next Intl En"
  },
  "About": {
    "title": "About",
    "lastUpdated": "This example was updated {lastUpdatedRelative} ({lastUpdated, date, short})."
  },
  "Navigation": {
    "index": "Home",
    "about": "About",
    "switchLocale": "Switch to {locale, select, de {German} en {English} other {Unknown}}"
  },
  "NotFound": {
    "title": "Sorry, this page could not be found."
  },
  "StrictTypes": {
    "nested": {
      "hello": "Hello",
      "another": {
        "level": "Level"
      }
    }
  }
}

messages\vi.json

{
  "Index": {
    "title": "Trang chủ",
    "description": "<p>Chỉ có tối thiểu <code>{locale}</code> tin nhắn được tải để hiển thị trang này.</p><p>Các không gian tên sau đây khả dụng:</p>"
  },
  "LocaleSwitcher": {
    "switchLocale": "Chuyển sang {locale, select, vi {Vietnam} en {English} other {Unknown}}"
  },
  "About": {
    "title": "About",
    "lastUpdated": "Ví dụ này đã được cập nhật {lastUpdatedRelative} ({lastUpdated, date, short})."
  },
  "PageLayout": {
    "pageTitle": "Next Intl Vi"
  },
  "Navigation": {
    "index": "Trang chủ",
    "about": "Chúng tôi",
    "switchLocale": "Chuyển đến {locale, select, vi {Việt Nam} en {English} other {Unknown}}"
  },
  "NotFound": {
    "title": "Xin lỗi trang không được tìm thấy"
  },
  "StrictTypes": {
    "nested": {
      "hello": "Xin chào",
      "another": {
        "level": "Cấp độ"
      }
    }
  }
}

src\pages\components\Code.tsx

import {ReactNode} from 'react';
type Props = {
  children: ReactNode;
};
export default function Code({children}: Props) {
  return (
    <code style={{background: 'red', padding: 4, borderRadius: 4}}>
      {children}
    </code>
  );
}

src\pages\components\LocaleSwitcher.tsx

import Link from 'next/link';
import { useRouter } from 'next/router';
import { useTranslations } from 'next-intl';
export default function LocaleSwitcher() {
  const t = useTranslations('LocaleSwitcher');
  const { locale, locales, route } = useRouter();
  const otherLocale = locales?.find((cur) => cur !== locale) as string;
  return (
    <Link href={route} locale={otherLocale}>
      {t('switchLocale', { locale: otherLocale })}
    </Link>
  )
}

src\pages\components\Navigation.tsx

import Link from 'next/link';
import {useRouter} from 'next/router';
import {useTranslations} from 'next-intl';
export default function Navigation() {
  const t = useTranslations('Navigation');
  const {locale, locales, route} = useRouter();
  const otherLocale = locales?.find((cur) => cur !== locale) as string;
  return (
    <div style={{display: 'flex', justifyContent: 'space-between'}}>
      <div style={{display: 'flex', gap: 10}}>
        <Link href="/">{t('index')}</Link>
        <Link href="/about">{t('about')}</Link>
      </div>
      <Link href={route} locale={otherLocale}>
        {t('switchLocale', {locale: otherLocale})}
      </Link>
    </div>
  );
}
Navigation.messages = ['Navigation'];

src\pages\components\PageLayout.tsx

import Head from 'next/head';
import { useTranslations } from 'next-intl';
import { ReactNode } from 'react';
import Navigation from './Navigation';
type Props = {
  children?: ReactNode;
  title: string;
};
export default function PageLayout({ children, title }: Props) {
  const t = useTranslations('PageLayout')
  return (
    <>
      <Head>
        <title>{[title, t('pageTitle')].join(' - ')}</title>
      </Head>
      <div className="container m-auto">
        <Navigation />
        <h1>{title}</h1>
        {children}
      </div>
    </>
  )
}
PageLayout.messages = ['PageLayout', ...Navigation.messages];

src\pages\_app.tsx

import "@/styles/globals.css";
import type { AppProps } from "next/app";
import {useRouter} from 'next/router';
import {NextIntlClientProvider} from 'next-intl';
export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();
  return (
    <NextIntlClientProvider
      // To achieve consistent date, time and number formatting
      // across the app, you can define a set of global formats.
      formats={{
        dateTime: {
          short: {
            day: 'numeric',
            month: 'short',
            year: 'numeric'
          }
        }
      }}
      locale={router.locale}
      // Messages can be received from individual pages or configured
      // globally in this module (`App.getInitialProps`). Note that in
      // the latter case the messages are available as a top-level prop
      // and not nested within `pageProps`.
      messages={pageProps.messages}
      // Providing an explicit value for `now` ensures consistent formatting of
      // relative values regardless of the server or client environment.
      now={new Date(pageProps.now)}
      // Also an explicit time zone is helpful to ensure dates render the
      // same way on the client as on the server, which might be located
      // in a different time zone.
      timeZone="Europe/Vienna"
    >
      <Component {...pageProps} />
    </NextIntlClientProvider>
  )
}

src\pages\index.tsx

import {GetStaticPropsContext} from 'next';
import {useTranslations} from 'next-intl';
import PageLayout from '@/pages/components/PageLayout';
import Code from '@/pages/components/Code';
import { useRouter } from 'next/router';
export default function Home() {
  const t = useTranslations('Index');
  const {locale} = useRouter();
  return (
    <PageLayout title={t('title')}>
      <div>
        {t.rich('description', {
          locale: locale!,
          p: (children) => <p>{children}</p>,
          code: (children) => <Code>{children}</Code>
        })}
      </div>
      <ul>
        {Home.messages.map((componentName) => (
          <li key={componentName} style={{marginBottom: 5}}>
            <Code>{componentName}</Code>
          </li>
        ))}
      </ul>
    </PageLayout>
  );
}
export async function getStaticProps({locale}: GetStaticPropsContext) {
  return {
    props: {
      messages: (await import(`../../messages/${locale}.json`)).default
    }
  };
}
Home.messages = ['Index', ...PageLayout.messages];

src\pages\about.tsx

import {GetServerSidePropsContext} from 'next';
import {useFormatter, useTranslations} from 'next-intl';
import PageLayout from '@/pages/components/PageLayout';
export default function About() {
  const t = useTranslations('About');
  const format = useFormatter();
  const lastUpdated = new Date('2021-12-23T10:04:45.567Z');
  return (
    <PageLayout title={t('title')}>
      <p>
        {t('lastUpdated', {
          lastUpdated,
          lastUpdatedRelative: format.relativeTime(lastUpdated)
        })}
      </p>
    </PageLayout>
  );
}
About.messages = ['About', ...PageLayout.messages];
export async function getServerSideProps({locale}: GetServerSidePropsContext) {
  return {
    props: {
      messages: (await import(`../../messages/${locale}.json`)).default,
      // Note that when `now` is passed to the app, you need to make sure the
      // value is updated from time to time, so relative times are updated. See
      // https://next-intl.dev/docs/usage/configuration#global-now-value
      now: new Date().getTime()
    }
  };
}

Last updated

Was this helpful?