🐕

Payloadのコンテンツの多言語対応

に公開

はじめに

Payloadでは、コンテンツの多言語対応のことをLocalizationと呼びます。この記事では、Localizationの設定方法と、多言語コンテンツをフロント画面に表示する方法について紹介します。

ちなみにPayloadでは、コンテンツの多言語対応をLocalizationと呼ぶのに対し、管理画面の多言語対応のことをI18n(Internationalization)と呼びます。管理画面の多言語については、下記の記事で解説していますのでそちらをご参照ください。

https://zenn.dev/ksk1kd/articles/b69038ec67f799

前提

  • フロントエンドの実装はNext.js App Routerで行います
  • フロントエンドの実装ではTailwind CSSを使用しています
  • 管理画面のサポート言語に日本語と英語を設定しています

Localizationの設定

プロジェクトで使用可能な言語の設定

Payloadでは設定をすべてコードで行う仕様となっており、payload.config.tsにて言語設定を行います。下記のコードは、英語と日本語を使用可能な言語に設定する場合の例です。JavaScriptオブジェクトのlocalizationキー配下に設定を記述します。

src/payload.config.ts
export default buildConfig({
  // ...
  localization: {
    locales: [
      {
        label: {
          en: 'English',
          ja: '英語',
        },
        code: 'en',
      },
      {
        label: {
          en: 'Japanese',
          ja: '日本語',
        },
        code: 'ja',
      },
    ],
    defaultLocale: 'en',
  },
})

フィールドごとのLocalization有効化設定

次に、フィールドごとのLocalization有効化設定を行います。前述のプロジェクトで使用可能な言語設定を行うだけでは、コンテンツの多言語対応は実現できません。コンテンツの持つ各フィールドに対して、言語ごとに異なるデータを保持するのか、それともすべての言語で同一のデータを保持するのかを設定してはじめてコンテンツの多言語対応を実現できます。

下記のコードは、お知らせコレクションの各フィールドに多言語設定を記述した例です。タイトルと本文については言語ごとに異なるデータを保持したいため、localized: trueを指定しています。一方、公開日についてはすべての言語で同一のデータを保持したいため、localized: trueを指定していません。

src/collections/News/index.ts
import type { CollectionConfig } from 'payload'

export const News: CollectionConfig = {
  slug: 'news',
  admin: {
    defaultColumns: ['title', 'publishedAt', 'updatedAt'],
    useAsTitle: 'title',
  },
  labels: {
    singular: {
      en: 'News',
      ja: 'お知らせ',
    },
    plural: {
      en: 'News',
      ja: 'お知らせ',
    },
  },
  fields: [
    {
      name: 'title',
      label: {
        en: 'Title',
        ja: 'タイトル',
      },
      type: 'text',
      required: true,
      localized: true,
    },
    {
      name: 'body',
      label: {
        en: 'Body',
        ja: '本文',
      },
      type: 'richText',
      localized: true,
    },
    {
      name: 'publishedAt',
      label: {
        en: 'Published At',
        ja: '公開日',
      },
      type: 'date',
      required: true,
      admin: {
        date: {
          pickerAppearance: 'dayOnly',
        },
        position: 'sidebar',
      },
    },
  ],
}

コンテンツ編集画面

以上の設定を行うと、コンテンツ編集画面の右上に言語選択のプルダウンが表示されるようになり、プルダウンで選択した言語に対応するフィールドデータを編集できるようになります。

言語選択プルダウンがあるコンテンツ編集画面

多言語コンテンツのフロント画面の実装

最後に、多言語コンテンツをフロント画面に表示するための実装を行います。ページのパスの先頭に/ja/~といった形で言語コードを指定すると、その言語に対応したお知らせが表示される機能を実装します。

src/app/(frontend)/[locale]/news/[id]/page.tsxのファイルを作成し、下記のコードを記述すると実現できます。ポイントは、[locale]のパラメータを取得して、payload.find()のオプションにlocaleを指定しているところです。これにより、パスの先頭で指定した言語コードに対応したコンテンツが表示される仕組みとなっています。

このコードはパフォーマンスを考慮していませんので、その点はご注意ください。

src/app/(frontend)/[locale]/news/[id]/page.tsx
import configPromise from '@payload-config'
import { RichText } from '@payloadcms/richtext-lexical/react'
import { getPayload } from 'payload'
import { notFound } from 'next/navigation'
import type { Config } from '@/payload-types'

type Args = {
  params: Promise<{
    id: number
    locale: Config['locale']
  }>
}

export default async function Page({ params: paramsPromise }: Args) {
  const { id, locale } = await paramsPromise

  const payload = await getPayload({ config: configPromise })

  const result = await payload.find({
    collection: 'news',
    limit: 1,
    where: {
      id: {
        equals: id,
      },
    },
    locale,
  })

  const news = result.docs?.[0]

  if (!news) notFound()

  return (
    <article className="mx-40 py-20">
      <time className="block mb-5">{new Date(news.publishedAt).toLocaleDateString(locale)}</time>
      <h1 className="mb-12 text-4xl">{news.title}</h1>
      {news.body && <RichText data={news.body} />}
    </article>
  )
}

以上の実装により、下記のように各言語に対応したデータを表示できます。

/en/news/[id]
英語のお知らせ画面のサンプル
英語のお知らせ

/ja/news/[id]
日本語のお知らせ画面のサンプル
日本語のお知らせ

※デザインはご自身の環境に依存します

まとめ

この記事では、Localizationの設定方法と、多言語コンテンツをフロント画面に表示する方法について紹介しました。フィールドごとにLocalization有効化設定しなければならないことがポイントですので、設定し忘れないようにご注意いただければと思います。

参考

https://payloadcms.com/docs/configuration/localization

Discussion