以前の投稿Gatsbyで多言語のウェブサイトを作成し、kodou.meを作成しました。 鼓童には日本語版と英語版があります。 投稿が少し長かったので、私が使用したユーティリティのいくつかと、サイトのナビゲーションメニューを作成する方法については話しませんでした。

クイックサマリー

前回の投稿では、日本語と英語でサイトを構築しました。 サイトのデフォルト言語は英語です。 これは、2つのURLタイプがあることを意味します。

  • 日本語ページ:kodou.me/ja/team
  • 英語のページkodou.me/team

さまざまなページバージョンがCosmicJSで記述されています。 Gatsbyに/config/languagesで使用する言語を認識させます。 gatsby-node.jsでは、CosmicJSからのデータを入力したテンプレートを使用してページを作成します。

これは、CosmicJSによって返されるteam-members配列の簡略版です。

teamMembers = [
  {
    title: 'CEO',
    fullName: 'Jack Misteli',
    content: 'The CEO of the Company',
    locale: 'en'
  },
  {
    title: 'CEO',
    fullName: 'ジャック・ミステリ',
    content: '会社のCEO',
    locale: 'ja'
  }
]

teamMembersを受け取った後、2つのオブジェクトjaTeamMembersenTeamMembersを作成します。 templates/teamjaTeamMembersを入力して/ja/teamを作成し、enTeamMembersを入力して/teamを作成します。

サイトを言語対応にする

優れたWeb市民であり、私たちが作成するサイトにアクセスできるようにすることが重要です。 したがって、最初に行う必要があるのは、サイトのメタデータに言語を追加することです。 また、よりターゲットを絞った検索結果を得るのに役立つ場合があります。

モジュール:gatsby-config.js
module.export = {
  siteMetadata: {
    title: `Kodou`,
    description: `Kodou site description`,
    author: `Jack Misteli `,
    languages
  },
  //....

Gatsbyアプリケーションでは、ページのコンテキストで現在の言語をテンプレートに渡します。

モジュール:pageGenerator.js
// langs contains the languages of our blog and default langKey is the default language of the site
// To be fully programmatic we could calculate langs
// here langs = ['en', 'ja'] and defaultLangKey = 'en'
const { langs, defaultLangKey } = require('../config/languages')
const path = require(`path`)
const { localizeUrl, createLanguagesObject } = require('../utils/localization')

module.exports = async (options, createPage, graphql) => {
  const {query, pageName} = options
  let templateName = options.templateName ? options.templateName : pageName
  const result = await graphql(query)
  if (result.errors)
      console.error(result.errors)

  const cosmicJSData = createLanguagesObject(langs)

  Object.values(result.data)[0].edges.forEach(({ node }) => {
  cosmicJSData[node.locale].push(node)
  })

  // we create a new page for each language
  langs.forEach(lang => {
    createPage({
      // the localizeUrl function creates a url which takes into consideration what the default language is
      path: localizeUrl(lang, defaultLangKey, '/team'),
      component: path.resolve(`src/templates/team.js`),
      context: {
      profiles: profiles[lang],
      // Here we pass the current language to the page
      lang
    }
  })
  })
}

これで、テンプレートでlangにアクセスできます

const { lang } = props.pageContext;

国際化APIの使用

Intl API は、文字列の比較、数値の書式設定、および日付と時刻の書式設定に使用されます。 ここでは取り上げないクールな機能がたくさんあります。 ここでは、日付を適切な形式で表示するために使用します。

react-intlパッケージをLayoutファイルに追加します。

モジュール:layout.js
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import "../styles/main.scss"
import Header from "./header"
import { IntlProvider, FormattedDate } from "react-intl"

const Layout = ({ children, location, lang }) => {

  // We populated the siteMetaData in `gatsby-config.js` and are extracting it here for some extra language context
  // The best practice here would be to  directly get that data from `config` but I want to show different ways to do it
  const data = useStaticQuery(graphql`
    query SiteInfoQuery {
      site {
        siteMetadata {
          title
          languages {
            defaultLang
            langs
          }
        }
      }
}
  `)
  // langs is an array of all the supported languages
  // defaultLang is the default site language
  // title is the website's title
  const {langs, defaultLang, title} = data.site.siteMetadata

  return (
    // We use IntlProvider to set the default language of our page
    <IntlProvider
      locale={lang}
      defaultLocale={defaultLang}
    >
      <Header 
        location={location}
        defaultLang={defaultLang}
        languages={langs}
        siteTitle={title} />
        <main className="section">
          <div className="container"> 
            {children}
          </div>
        </main>
        <footer>
          <div className="footer">
            <div className="content has-text-centered">
            {/* FormattedDate will format our date according to the language we set in IntlProvider locale prop */}
              © <FormattedDate value={new Date()}
               year="numeric"
                month="long"
                day="numeric"
                weekday="long" />, Built by 
              <a href="https://jmisteli.com"> Jack Misteli</a>
            </div>
          </div>
        </footer>
    </IntlProvider>
  )
}

export default Layout

ページが英語で生成される場合、<FormattedDate>Monday, December 9, 2019を返します。 ページが日本語で生成された場合、<FormattedDate>2019年12月9日月曜日を返します。

メニューの作成

Layoutには、Headerコンポーネントがあることがわかります。 現在の言語プロップを除くすべての言語情報をヘッダーに渡します。 ページの現在の言語への別の方法を紹介したいので、合格しません。

import { Link } from "gatsby"
import PropTypes from "prop-types"
import React from "react"
import { getCurrentLangKey, getLangs, getUrlForLang } from 'ptz-i18n'
import langmap from 'langmap'
import { localizeUrl, buildMenu } from '../../utils/localization'

const Header = ({ languages, location, defaultLang}) => {

  const url = location.pathname
  const currentLangKey = getCurrentLangKey(languages, defaultLang, url)

  // Create a home link by adding a slash before the language and if it
  const homeLink = localizeUrl(currentLangKey, defaultLang, '/')

  // Get langs return language menu information

  // langsMenu will allow us to build a dropdown with all the available language options
  const langsMenu = buildMenu(languages, defaultLang, currentLangKey, url)
  // On the `/team` page this will return the following array
  //  [{selected: true, link: "/team/", langKey: "en"},
  //  {selected: false, link: "/ja/team/", langKey: "ja"}]

  // All the navigation menu item titles
  const allLanguageTitles = {
    'en':['Concept', 'Work', 'Team', 'News', 'Contact'],
    'ja': ['コンセプト', '仕事', 'チーム', 'ニュース', '連絡先']
  }

  // Selecting the current language and default to english titles
  const currentLanguageTitles = allLanguageTitles[currentLangKey] || allLanguageTitles['en']

  // allNavigationLinks contains all the pages name, with urls in every supported language
  const allNavigationLinks = currentLanguageTitles.map((page, i) => ({
    name: page,
    url: `${homeLink.replace(defaultLang, '')}${allLanguageTitles.en[i].toLowerCase()}`
  }))
  // On the English page it will return 
  // [{name: "Concept", url: "/concept"}, {name: "Work", url: "/work"}, {name: "Team", url: "/team"}...]
  // [{name: "コンセプト", url: "/ja/concept"}, {name: "仕事", url: "/ja/work"}, {name: "チーム", url: "/ja/team"} ...]

  return (
    <nav>
      <Link to={homeLink} className="navbar-item">
        HOME
      </Link>
      {allLinks.map((link, i) => (
      <Link key={i} to={link.url} className="navbar-item">
        {link.name.toUpperCase()}
      </Link>
      ))}

      <div className="navbar-language-menu">
        <div className="current-language">
        // langmap is an object with the language keys as object keys and english, original versions of the language
        {langmap[langKey]['englishName']}
        </div>
        <div className="all-languages-dropdown">
          {langsMenu.map((lang)=>(
            !lang.selected && 
            <Link key={lang.langKey} to={lang.link} className="navbar-item">
            {langmap[lang.langKey]['englishName']}
            </Link>
          ))}
        </div>
      </div>
    </nav>
)}

export default Header

これで、ユーザーの現在の言語に応じてリンクを調整するさまざまな言語のナビゲーションメニューが表示されます。 私が作成したユーティリティ関数を確認したい場合は、GitHubリポジトリで入手できます。