i18n для статических сайтов с помощью Strapi

Внедрив интернационализацию (часто сокращенно i18n) на своем сайте, вы сможете получить конкурентное преимущество, первым выйдя на неохваченные рынки. Интернационализация – это практика разработки продукта с намерением представить его международной аудитории. Независимо от того, ведете ли вы новостной сайт, блог или электронный курс, клиенты с гораздо большей вероятностью будут взаимодействовать с контентом на своем родном языке.

В онлайновом мире вы быстро обнаружите, что существует значительное неравенство в плане предоставляемого контента. Например, англоязычный контент представлен в более чем 60% Интернета, но англоязычные пользователи составляют лишь 25% от общего числа пользователей. С другой стороны, 19% пользователей Интернета – китайцы, но они имеют доступ только к 1,3% контента на родном языке. Это дает бизнесу возможность сократить разрыв между спросом и предложениями.

Благодаря Strapi и его плагину интернационализации разработчики теперь могут быстро и легко создавать контент на разных языках и предоставлять локализованный контент своим пользователям. В этом руководстве вы узнаете, как настроить функции i18n в проектах Strapi и Next.js, используя возможности, предлагаемые Strapi и Next.js.

Что такое интернационализация?

Прежде чем приступить к изучению данного руководства, важно понять разницу между интернационализацией и локализацией (L10n). Эти два термина часто используются как взаимозаменяемые, но они совершенно разные.

Интернационализация – это процесс разработки программного обеспечения таким образом, чтобы его можно было локализовать для определенной аудитории, которая может отличаться по культуре, языку или региону. Например, это может означать удаление всех жестко закодированных строк в вашем приложении и помещение их в файл JSON.

Локализация, с другой стороны, происходит после интернационализации вашего программного обеспечения и заключается в добавлении поддержки (контента, изображений и т.д.) для конкретной аудитории; например, взять те жестко закодированные строки в файле JSON и перевести их для франкоговорящей аудитории.

При интернационализации разработчики часто используют термин локаль, который обозначает аудиторию для определенного региона. Например, вы можете иметь локаль en, которая будет представлять всех англоговорящих пользователей. Однако, как вы можете себе представить, носители британского и американского английского языка иногда используют разные термины (например, soccer vs. football). В результате для этих двух аудиторий были придуманы две разные локали: en-US и en-GB.

Хорошая новость заключается в том, что благодаря интернационализации и такому инструменту, как Strapi, сайты, подобные сайту FIFA, могут предлагать локализованный контент. На одном сайте британские болельщики могут увидеть термин football, а американцы – soccer. Даже в пределах одной страны преподаватели в Швейцарии, стране, известной своим многоязычием, могут интернационализировать свои онлайн-курсы, чтобы предложить поддержку многочисленной аудитории, или канадские кулинарные блоггеры могут использовать один сайт с поддержкой как англоязычных, так и франкоязычных канадцев.

Реализация интернационализации в Strapi

В этом учебнике вы создадите сайт, на котором будут представлены рецепты. Пользователи смогут менять язык сайта, а локализованное содержимое будет извлекаться из внутреннего проекта Strapi.

Настройка проекта Strapi

Для начала откройте новое окно терминала и выполните эту команду, чтобы создать новый проект:

    yarn create strapi-app my-project --quickstart
    # or
    npx create-strapi-app my-project --quickstart
Войти в полноэкранный режим Выйти из полноэкранного режима

При использовании флага --quickstart откроется новая вкладка браузера, в которой вам будет предложено создать новый проект. Заполните форму своей информацией и нажмите “Let’s start”:

И теперь у вас есть новый блестящий проект Strapi!

Настройка проекта Next.js в качестве фронтенда

Создание проекта Next.js очень простое. В новой вкладке терминала выполните команду create и выберите имя вашего проекта:

    npx create-next-app@latest
    # or
    yarn create next-app
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем запустите yarn dev или npm start, чтобы запустить ваш сервер.

После этого бэкенд Strapi будет работать на порту 1337, а фронтенд Next.js – на порту 3000.

Реализация интернационализации в Strapi

Теперь вам нужно вернуться в проект Strapi и убедиться, что интернационализация включена. Перейдите в раздел General > Plugins и убедитесь, что интернационализация появилась в списке установленных плагинов:

Интернационализация автоматически включается в приложениях Strapi версии 3.6.0. Если вы используете более старую версию и у вас нет плагина интернационализации, вы можете установить его, посетив Marketplace и выполнив эту команду терминала:

    yarn strapi install i18n
    # or
    npm run strapi install i18n
Войти в полноэкранный режим Выйти из полноэкранного режима

Если вам нужны более подробные сведения, не стесняйтесь обращаться к документации Strapi.

После установки плагина настало время установить новую локаль. Перейдите в раздел Global Settings > Internationalization и нажмите на “Add new locale”. В рамках данного руководства вы можете создать новую локаль для рынка Французской Канады, выбрав Французский (Канада) (fr-CA):

Примечание: Вы не можете создать пользовательскую локаль, но текущий список (включенный в выпадающее меню) содержит исчерпывающий каталог языков и стран для выбора.

Если вы хотите выбрать локаль по умолчанию, отличную от английской, вы можете нажать на “Дополнительные настройки и установить флажок, чтобы установить новую локаль по умолчанию:

Теперь пришло время создать ваш контент. Для вашего проекта вам понадобится тип коллекции Recipe. Перейдите в раздел Plugins > Content-Type Builder.

Вы можете быть незнакомы с понятием типа коллекции. Они похожи на модели в базе данных. Они позволяют разработчикам определять набор полей и управлять несколькими записями одного типа. Например, блог имеет тип коллекции Post. При извлечении этих записей вы получите содержимое в виде списка записей с одним и тем же полем (название, дата создания и т.д.).

Нажмите кнопку Создать новый тип коллекции и введите “Рецепт” в поле Отображаемое имя:

Нажмите Продолжить. Затем вам будет предложено создать поле. На этот раз нажмите на Текст и введите “Заголовок” в поле ввода с надписью Имя:

Выберите Готово. Вы перейдете к новому типу коллекции с только что созданным полем заголовка. Затем нажмите на Добавить другое поле и выберите Насыщенный текст. На этот раз в качестве названия введите “инструкции”. В этом поле будут содержаться все измерения и шаги для вашего рецепта.

Вы можете добавить столько полей, сколько захотите. Для этого руководства вам также нужно создать медиа-поле с одним медиа-типом под названием “header_image”, чтобы сохранить изображение для ваших рецептов. Когда все готово, нажмите кнопку Сохранить.

Ваш только что сохраненный тип коллекции рецептов будет выглядеть следующим образом:

Прежде чем создавать новое содержимое, необходимо включить локализацию для этой коллекции. В Content-Type Builder > Recipe нажмите на Edit. Во всплывающем окне выберите Дополнительные параметры и установите флажок Включить локализацию для этого типа содержимого:

Когда тип коллекции сохранен и локализация включена, можно приступать к созданию контента. Перейдите в Менеджер содержимого и нажмите на Создать новую запись. Выберите необходимую локаль и введите содержимое:

Нажмите Сохранить, чтобы зафиксировать новую запись.

На данном этапе вы также можете нажать на выпадающий список Locales и создать содержимое для своей локали. После выбора второй локали вы перейдете на новую страницу для ввода переведенного содержимого.

Если вы хотите импортировать некоторые элементы, например, изображение_заголовка, или даже числовые поля, например, цены, вы можете нажать на Заполнить из другой локали, что приведет к импорту содержимого другой локали:

Обратите внимание: это перезапишет то, что у вас есть сейчас.

После того, как вы создали содержимое, опубликуйте его, зайдя в свою запись и выбрав “Опубликовать”. После завершения работы у вас должно быть несколько опубликованных записей с содержимым, доступным в нескольких локалях:

Важно: Публикация одной локали не приводит к автоматической публикации всех остальных в этой записи. Вам нужно нажать кнопку Опубликовать для каждой отдельной локали.

Реализация интернационализации в Next.js

Теперь, когда вы настроили интернационализацию для своего проекта Strapi, пришло время сделать то же самое в приложении Next.js. В этой части руководства вы будете использовать библиотеку next-i18next. Эта библиотека построена на i18next, популярном фреймворке интернационализации с поддержкой многих фреймворков, таких как React, Next.js, PHP и .NET.

Для начала установите библиотеку next-i18next:

    npm install next-i18next
    # or
    yarn add next-i18next
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Затем создайте файл next-i18next.config.js в корневом проекте и добавьте свои локали:

    module.exports = {
      i18n: {
        defaultLocale: 'en',
        locales: ['en', 'fr-CA'],
      },
    };
Войти в полноэкранный режим Выйти из полноэкранного режима

В вашем файле next.config.js импортируйте ваш новый конфигурационный файл i18next и добавьте его в NextConfig:

    /** @type {import('next').NextConfig} */
    const { i18n } = require('./next-i18next.config');

    const nextConfig = {
      i18n,
      reactStrictMode: true,
    }

    module.exports = nextConfig
Вход в полноэкранный режим Выйти из полноэкранного режима

Затем в _app.js, используйте appWithTranslation из next-i18next. Этот компонент высшего порядка (HOC) обернет i18nextProvider вокруг вашего приложения. Благодаря контекстному API ваш экземпляр i18next будет передан вашим компонентам. С помощью этого экземпляра вы можете получить перевод и изменить локаль:

    import '../styles/globals.css'

    import { appWithTranslation } from 'next-i18next';

    function MyApp({ Component, pageProps }) {
      return <Component {...pageProps} />
    }

    //Wrap appWithTranslation around your app
    export default appWithTranslation(MyApp);
Войти в полноэкранный режим Выйти из полноэкранного режима

Когда все это настроено, вы можете начать добавлять переводы для своего приложения. next-i18next ожидает определенную структуру файлов для ваших переводов. Они должны быть разделены на собственные папки locales в папке public, как показано ниже:

    .
    └── public
        └── locales
            ├── en
            |   └── common.json
            └── fr-CA
                └── common.json
Вход в полноэкранный режим Выход из полноэкранного режима

Внутри нужно добавить несколько простых переводов, которые вы попробуете захватить позже:

    // locales/en/common.json
    {
      "en": "English",
      "fr-CA": "French Canadian",
      "change_locale": "Change Language",
      "current_locale": "Current locale"
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

А вот для французской локали:

    //locales/fr-CA/common.json
    {
      "en": "Anglais",
      "fr-CA": "Français (Canadien)",
      "change_locale": "Changement de language",
      "current_locale": "Language actuelle"
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Когда все готово, вам нужно взять эти переводы и попытаться изменить локаль в вашем приложении. Для этого вам понадобится функция serverSideTranslations на страницах Next.js. Она работает с getStaticProps и getServerProps и передает ваши переводы и конфигурации в качестве реквизитов.

Перейдите к index.js. Внутри вы будете использовать serverSideTranslations для передачи ваших переводов и useTranslation для получения правильного текста. Текущая локаль может быть получена из маршрутизатора Next.js:

    import Link from 'next/link'
    import { useRouter } from 'next/router'

    import { useTranslation } from 'next-i18next'
    import { serverSideTranslations } from 'next-i18next/serverSideTranslations'

    export default function Home() {
      //Get the current locale from router.locale
      const router = useRouter()
      const { t } = useTranslation('common')
      return (
        <div style={{margin: '20px'}}>
          <div>{t('current_locale')}: {t(router.locale)}</div>
          <div>
            <Link
              href='/'
              locale={router.locale === 'en' ? 'fr-CA' : 'en'}>
              <button>
                {t('change_locale')}
              </button>
            </Link>
          </div>
        </div>

      )
    }

    export const getServerSideProps = async ({ locale }) => ({
      props: {
        ...await serverSideTranslations(locale, ['common']),
      },
    })
Войти в полноэкранный режим Выйти из полноэкранного режима

После перезагрузки сервера вы должны увидеть следующее:

После внедрения логики интернационализации пришло время получить содержимое из бэкенда Strapi.

С главной страницы вы можете получить рецепты, сделав вызов API на ваш бэкенд и передав результат в качестве реквизита в getServerSideProps. Strapi REST API также поставляется с удобным параметром locale для указания локали, в которой должно быть получено содержимое. В своем index.js добавьте вызов API для получения рецептов:

    export const getServerSideProps = async ({ locale }) => {
      const res = await fetch(`http://localhost:1337/api/recipes?locale=${locale}`)
      const data = await res.json()
      console.log(data)
      return {
        props: {
          data,
          ...await serverSideTranslations(locale, ['common']),
        },
      }
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь, если вы откроете журнал сервера Next.js, вы увидите следующее сообщение об ошибке:

Чтобы исправить эту ошибку разрешения, вернитесь в свой проект Strapi и перейдите в Settings > Roles (в разделе Users & Permissions Plugin) > Public. Затем разверните разрешения для вашего типа содержимого Recipe. Для данного руководства просто установите флажок в поле Выбрать все и нажмите кнопку Сохранить:

Это позволит неаутентифицированным пользователям делать API-запросы к записям в коллекции рецептов. Если вы перезагрузите свою страницу, вы наконец-то увидите, что данные поступают:

Теперь вы можете получить ваши рецепты, передать их в качестве реквизитов на вашу страницу и отобразить список.

Ваш список пользовательских полей будет доступен в разделе attributes для каждой записи. Вы можете получить заголовок, написав recipe.attributes.title:

    import Link from 'next/link'
    import { useRouter } from 'next/router'

    import { useTranslation } from 'next-i18next'
    import { serverSideTranslations } from 'next-i18next/serverSideTranslations'

    export default function Home({recipes}) {
      const router = useRouter()
      const { t } = useTranslation('common')

      return (
        <div style={{margin: '20px'}}>
          <h2>{t('recipes')}</h2>
          {
            recipes.length > 0 && recipes.map((recipe) => {
              return(
                <div key={`recipe-${recipe.id}`}>
                  {recipe.attributes.title}
                </div>
              )
            })
          }
          <div style={{marginTop: '20px'}}>
            <Link
              href='/'
              locale={router.locale === 'en' ? 'fr-CA' : 'en'}>
                {t('change_locale')}
            </Link>
          </div>
        </div>
      )
    }

    export const getServerSideProps = async ({ locale }) => {
      const res = await fetch(`http://localhost:1337/api/recipes?locale=${locale}`)
      //Get your list of recipes
      const { data } = await res.json()

      return {
        props: {
          recipes: data,
          ...await serverSideTranslations(locale, ['common']),
        },
      }
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Добавьте новые переводы для recipes в ваш common.json в обеих локалях:

    //In /public/locales/en/common.json
    {
    ...
      "recipes": "Recipes"
    }

    //In /public/locales/fr-CA/common.json
    {
      ...
      "recipes": "Recettes"
    }
Войти в полноэкранный режим Выйдите из полноэкранного режима

Результат будет выглядеть следующим образом:

Создание подробной страницы для вашего рецепта

Если вы хотите, вы можете пойти дальше и создать страницу для отображения вашего рецепта. Для этого измените список рецептов так, чтобы в нем появилась ссылка на страницу подробного описания рецепта:

    <Link href={`/${recipe.id}`} locale={router.locale}>{recipe.attributes.title}</Link>
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем в папке pages создайте файл [recipeId].js для отображения вашего рецепта с инструкциями.

Примечание: Если именование файла вас смущает, это потому, что [recipeId] – это динамический маршрут. Другими словами, он будет рассчитываться как /:recipeId. Если вам нужна дополнительная информация, ознакомьтесь с документацией Next.js по динамическим маршрутам.

Внутри вашего файла вы можете взять params в getServerSideProps и получить recipeId. Это позволит вам получить нужное содержимое из Strapi.

Примечание: В Strapi записи с разными локалями будут иметь разные ID. Это означает, что для одного и того же рецепта английская версия может иметь ID 2, а французская локаль – ID 3. Вот почему вам не нужно включать локаль в вызов API, так как он и так схватит конкретную локаль.

Однако вам нужно добавить populate=*, чтобы получить изображение header_image, поскольку это отношение, а отношения не заполняются по умолчанию:

    export const getServerSideProps = async ({ locale, params }) => {
      const { recipeId } = params;
      const res = await fetch(`http://localhost:1337/api/recipes/${recipeId}?populate=*`)
      const { data } = await res.json()

      return {
        props: {
          recipe: data,
          ...await serverSideTranslations(locale, ['common']),
        },
      }
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы отобразить ваши инструкции, вам понадобится дополнительная библиотека, поскольку поле с насыщенным текстом Strapi использует Markdown. Самый простой способ отобразить Markdown – установить библиотеку react-markdown:

    npm install react-markdown
    # or
    yarn add react-markdown
Войти в полноэкранный режим Выйдите из полноэкранного режима

Затем в вашем [recipeId].js вы можете получить рецепт из props и отобразить заголовок, изображение заголовка и инструкции:

    import Link from 'next/link'
    import { useRouter } from 'next/router'

    import { useTranslation } from 'next-i18next'
    import { serverSideTranslations } from 'next-i18next/serverSideTranslations'

    import ReactMarkdown from 'react-markdown'

    export default function RecipeDetail({recipe}) {
      const router = useRouter()
      const { t } = useTranslation('common')
      const {title, instructions, header_image } = recipe.attributes

      return (
        <div style={{margin: '20px'}}>
          {
            recipe && (
              <div>
                <img src={`http://localhost:1337${header_image.data.attributes.url}`} width={200} height={200}/>
                <h2>{title}</h2>
                <ReactMarkdown>{instructions}</ReactMarkdown>
              </div>
            )
          }
          <div style={{marginTop: '20px'}}>
            <Link
               href='/'
               locale={router.locale}>
                 {t('go_back')}
            </Link>
          </div>
        </div>
      )
    }

    export const getServerSideProps = async ({ locale, params }) => {
      const { recipeId } = params;
      const res = await fetch(`http://localhost:1337/api/recipes/${recipeId}?populate=*`)
      const { data } = await res.json()

      return {
        props: {
          recipe: data,
          ...await serverSideTranslations(locale, ['common']),
        },
      }
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Не забудьте добавить дополнительные переводы в ваш common.json для обеих локалей:

    //In /public/locales/en/common.json
    {
    ...
      "go_back": "Go back"
    }

    //In /public/locales/fr-CA/common.json
    {
      ...
      "go_back": "Retour"
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Вот окончательный результат вашей страницы Detail:

Если вы хотите увидеть весь проект сразу, вы можете клонировать его из этого репозитория GitHub.

Заключение

В этой статье вы узнали, как создать проект Strapi, установить новые локали и создать локализованный контент для каждой из них. Вы также узнали, как реализовать интернационализацию в приложении Next.js, что позволило вам предложить пользователям возможность сменить локаль и получить правильный контент из бэкенда.

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

Оцените статью
Procodings.ru
Добавить комментарий