Многоязычное меню сайта на Gatsby.js

В предыдущем материале по работе с Gatsby мы поговорили о создании многоязычного сайта и в качестве примера рассмотрели сайт Kodou, у которого есть японская и английская версии. Материал получился довольно длинным, поэтому мы не стали останавливаться на некоторых использованных утилитах и на том, как создать меню навигации по сайту.

Читайте также: Многоязычный сайт на Gatsby и Cosmic JS

Краткое резюме

Итак, в предыдущем мануале мы создали сайт на японском и английском языках. По умолчанию язык сайта – английский. Это означает, что на сайте есть два типа URL:

  • Страницы на японском: kodou.me/ja/team
  • Страницы на английском: kodou.me/team

В Cosmic JS написаны разные версии страниц. Gatsby узнает, какие языки используются на сайте, из файла /config/languages. В файле gatsby-node.js мы создаем страницы. Это делается с помощью шаблонов, которые мы заполняем данными из Cosmic JS.

Вот упрощенная версия того, как может выглядеть массив team-members, возвращаемый Cosmic JS.

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

Получив объект teamMembers, мы создаем два объекта – jaTeamMembers и enTeamMembers – и заполняем templates/team: jaTeamMembers создаст /ja/team, а enTeamMembers создаст /team.

Настройка языковых версий сайта

Сегодня очень важно разрабатывать доступные сайты. Итак, первое, что нам нужно сделать, это добавить наши языки в метаданные сайта. Это также поможет вам получить более точные результаты поиска. Откройте gatsby-config.js:

module.export = {
  siteMetadata: {
    title: `Kodou`,
    description: `Kodou site description`,
    author: `Jack Misteli `,
    languages
  },
  //....

Также в приложении Gatsby нужно передать шаблонам текущий язык в контексте страницы. Откройте pageGenerator.js:

// langs содержит языки сайта, а defaultLangKey является языком сайта по умолчанию
// langs можно было бы вычислить программным образом
// здесь langs = ['en', 'ja'] и 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)
  })

  // создаем новую страницу для каждого языка
  langs.forEach(lang => {
    createPage({
      // функция localizeUrl создает URL-адрес, который учитывает язык по умолчанию
      path: localizeUrl(lang, defaultLangKey, '/team'),
      component: path.resolve(`src/templates/team.js`),
      context: {
      profiles: profiles[lang],
      // передаем текущий язык странице
      lang
    }
  })
  })
}

Теперь можно получить доступ к lang в шаблоне:

const { lang } = props.pageContext;

Использование Intl API

Intl API используется для сравнения строк, а также для форматирования чисел, даты и времени. В нем много интересных функций, но, к сожалению, многие из них выходят за рамки нашего материала. Здесь мы просто будем использовать Intl API для отображения дат в соответствующем формате.

Добавляем пакет response-intl в файл Layout:

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 }) => {

  // Мы заполнили siteMetaData в файле gatsby-config.js и извлекаем их здесь для дополнительного языкового контекста
  // Здесь было бы лучше получить эти данные из config напрямую, но мы хотим показать разные способы работы
  const data = useStaticQuery(graphql`
    query SiteInfoQuery {
      site {
        siteMetadata {
          title
          languages {
            defaultLang
            langs
          }
        }
      }
}
  `)
  // langs – массив поддерживаемых языков
  // defaultLang – язык сайта по умолчанию
  // title – заголовок сайта
  const {langs, defaultLang, title} = data.site.siteMetadata

  return (
    // IntlProvider определяет язык страницы по умолчанию
    <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 отформатирует нашу дату в соответствии с языком, который мы установили в свойстве IntlProvider */}
              © <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日月曜日.

Создание меню

Возможно, вы обратили внимание на компонент Header в Layout. Мы передаем в него всю информацию о языке, кроме свойства текущего языка, – это нужно, чтобы мы могли показать вам другой вариант.

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)

  // добавьте слеш перед языком, чтобы создать ссылку home
  const homeLink = localizeUrl(currentLangKey, defaultLang, '/')

  // langs вернуть информацию о меню языка

  // langsMenu создаст раскрывающийся список со всеми доступными языковыми параметрами
  const langsMenu = buildMenu(languages, defaultLang, currentLangKey, url)
  // на странице /team это вернет следующий массив
  //  [{selected: true, link: "/team/", langKey: "en"},
  //  {selected: false, link: "/ja/team/", langKey: "ja"}]

  // все заголовки пунктов меню навигации
  const allLanguageTitles = {
    'en':['Concept', 'Work', 'Team', 'News', 'Contact'],
    'ja': ['コンセプト', '仕事', 'チーム', 'ニュース', '連絡先']
  }

  // Устанавливает английский в качестве текущего и языка по умолчанию
  const currentLanguageTitles = allLanguageTitles[currentLangKey] || allLanguageTitles['en']

  // allNavigationLinks содержит имена всех страниц с URL-адресами на всех поддерживаемых языках
  const allNavigationLinks = currentLanguageTitles.map((page, i) => ({
    name: page,
    url: `${homeLink.replace(defaultLang, '')}${allLanguageTitles.en[i].toLowerCase()}`
  }))
  // на англоязычной странице это вернет:
  // [{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 – это объект, который содержит языковые ключи        {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.

Tags: , ,

Добавить комментарий