Требуется мультиязычность на странице: английский и французский

от автора

Перевод состоит из двух частей:

  • Сама страница, текст, которой приходит с бэка по REST API запросу с указанием нужного языка

  • Перевод вспомогательных компонентов: кнопки, элементы навигации, отдельные заголовки.

Так же нам нужно переключение с английского языка на французский и обратно.

Для удобства работы будем использовать библиотеку Axios (установка npm i axios).

В корне проекта в папке store заводим инстанс, который отвечает за первичную настройку запросов:

apiInstance.ts

import axios from 'axios'; const URL = process.env.NEXT_PUBLIC_API_URL; const instance = axios.create({ baseURL: `${URL}/api/`, }); export default instance;

Для безопасности разработки используем process.env для хранения URL бэкэнда.

В той же папке создаём файлы с GET запросом для отдельных страниц с нужными эндпоинтами.  Для примера назовём один из файлов наших страниц  apiTeam.ts, а компонент отрисовки этой страницы Team. Такие файлы мы будем заводить для каждой страницы, где предусмотрен перевод.

apiTeam.ts

import instance from './apiInstance'; const getTeam = async (lang = 'Eng') => { const res = await instance.get('team', { headers: { Language: lang, }, }); return res; }; export default getTeam;

Далее заводим файл apiTranslate.ts с GET запросом для перевода вспомогательных компонентов, которые мы будем использовать на всех страницах, где есть вспомогательные компоненты с переводом:

apiTranslate.ts

import instance from './apiInstance';  const getTranslateInfo = async (lang: string) => {  const res = await instance.get('translate/', {  headers: {  Language: lang,  },  });  return res;  }; export default getTranslateInfo;

Делаем в компонент Team импорт созданного запроса.

import getTeam from '../../store/apiTeam';

и теперь можем использовать для отрисовки страницы

useEffect(() => { setIsLoading(true); getTeam(lang = 'Eng") .then((res) => setData(res.data)) .catch((err) => console.log(err)) .finally(() => setIsLoading(false)); }, []);

используем стейт переменную const [isLoading, setIsLoading] = useState(true), чтобы страница не была пустой при отрисовке.

{isLoading ? ( <Load /> ) : ( <Team> )}

В getTeam(lang = ‘Eng») нам нужно иметь актуальное состояние языка. Пока по умолчанию у нас ‘Eng’. Для настройки хранилища и выполнения операций по переключению языка будем использовать Redux Toolkit. В уже знакомой нам папке store заводим файл store.ts:

import { configureStore } from '@reduxjs/toolkit'; import reduserLang from './languageSlics'; export const store = () => { return configureStore({ reducer: { reduserLang, }, }); }; export type AppStore = ReturnType<typeof store>; export type RootState = returnType<AppStore['getState']>; export type AppDispatch = AppStore['dispatch'];

в нем мы делаем первичную настройку хранилища. Сами стейт переменные и экшены мы настраиваем в файле languageSlics.ts:

import { createSlice } from '@reduxjs/toolkit'; interface LanguageState { lang: string; } const language = createSlice({ name: 'isLang', initialState: { lang: 'Eng' } as LanguageState, reducers: { setLang(state: LanguageState, payload: { payload: string }) { state.lang = payload.payload; } }, }); export default language.reducer; export const { setLang } = language.actions;

В результате у нас есть стейт переменная lang, которая храниться в redux хранилище, и экшен setLang, который меняет значение стейт переменной lang.

В компонент Team мы можем импортировать import { useSelector } from ‘react-redux’ и использовать конструкцию:

const lang = useSelector( (state: { reduserLang: { lang: any } }) => state.reduserLang.lang );

так мы получаем доступ к актуальному значению языка и можем его использовать на странице для запроса в бэкэнд:

getTeam(lang) .then((res) => setData(res.data)) .catch((err) => console.log(err))

Так же у нас остаётся вопрос перевода вспомогательных компонентов. Действуем аналогично.  В файле languageSlics.ts заводим переменную words и экшен setWords для неё:

import { createSlice } from '@reduxjs/toolkit'; interface LanguageState { lang: string; words: {}[]; } const language = createSlice({ name: 'isLang', initialState: { lang: 'Eng', words: [], } as LanguageState, reducers: { setLang(state: LanguageState, payload: { payload: string }) { state.lang = payload.payload; }, setWords(state: LanguageState, payload: { payload: [] }) { state.words = payload.payload; }, }, }); export default language.reducer; export const { setLang, setWords } = language.actions;

Теперь у нас в хранилище 2 переменных: lang: ‘Eng’, words: [] и экшены для изменения их значения setLang, setWords. Как говорилось выше, в lang будет храниться актуальный язык для запроса к бэкэнду. а мы будем хранить массив объектов со значением текста кнопок, заголовка и прочего. Пример массива:

["nWho": "Who we helped", "nUrgent": "Urgent help", "nNews": "News", "nAbout": "About us", "nContacts": "Contacts", "nReports": "Reports and documentation", "nTeam": "Team", "bDonate": "Donate now", "bLearn": "Learn more", "bShare": "Share", "bCopied": "Copied", "bSee": "See all", "bBack": "Back", "bHome": "Home", "bLoad": "Load more", "hAbout": "About us", "hUrgent": "Urgent help", "hNews": "News", "hPartners": "Partners", "hSubscribe": "Subscribe to our newsletter", "hPlaceholder": "Your email address", "fPolicy": "Privacy Policy", "fSite": "Site development", "fCollected": "Funds collected", "sReport": "Report"]

Переменную words мы можем использовать на странице, предварительно импортировав хук import { useSelector } from ‘react-redux’  и получить доступ к переменной через:

const words = useSelector( (state: { reduserLang: { words: any } }) => state.reduserLang.words );

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

getTranslate(lang) .then((res) => { dispatch(setWords(res.data)); }) .catch((err) => console.log(err))

Не забываем получить доступ к стейт переменной lang:

const lang = useSelector( (state: { reduserLang: { lang: any } }) => state.reduserLang.lang );

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

Остаётся сделать переключение языка. Так как у нас в проекте переключение языка было в header, то и функционал переключения языка тоже поместил в header:

const toggleLang = () => { const langCurrent = lang === 'Eng' ? 'Fr' : 'Eng'; dispatch(setLang(langCurrent)); };

так же там поместил функционал для заполнения переменной words:

useEffect(() => { getTranslate(lang) .then((res) => { dispatch(setWords(res.data)); }) .catch((err) => console.log(err)) }, []);

И не забываем отслеживать переключение языка:

useEffect(() => {  getTranslate(lang)  .then((res) => {  dispatch(setWords(res.data[0]));  })  .catch((err) => console.log(err))  }, [lang]);

Но тут появляется тонкий момент, всё хорошо работает до того, как пользователь не обновит страницу. Переменные lang сбрасывается до начального значения и язык снова становиться по умолчанию — английским. Что бы этого избежать, мы добавим хранение актуального значения lang в sessionStorage. Для этого доработаем в функцию переключения языка

sessionStorage.setItem('lang', langCurrent): const toggleLang = () => { const langCurrent = lang === 'Eng' ? 'Fr' : 'Eng'; sessionStorage.setItem('lang', langCurrent); dispatch(setLang(langCurrent)); };

И перед GET запросом проверяем, если есть значение в  sessionStorage по ключу lang,  если есть, то берём это значение, если нет, то берём значение по умолчанию:

const langCurrent = sessionStorage.getItem('lang') ? sessionStorage.getItem('lang') : lang;

Так же актуализируем состояние языка в стейт переменной lang и sessionStorage:

sessionStorage.getItem('lang') ? dispatch(setLang(sessionStorage.getItem('lang'))) : sessionStorage.setItem('lang', lang);

Соберём запрос:

const langCurrent = sessionStorage.getItem('lang') ? sessionStorage.getItem('lang') : lang; sessionStorage.getItem('lang') ? dispatch(setLang(sessionStorage.getItem('lang'))) : sessionStorage.setItem('lang', lang); getTranslate(langCurrent) .then((res) => { dispatch(setWords(res.data[0])); }) .catch((err) => console.log(err));

Если sessionStorage.getItem(‘lang’) пусто, то мы берём значение по умолчанию из lang и его записываем в sessionStorage.setItem(‘lang’, lang).Если запись в sessionStorage присутствует по ключу lang, то используем это значение. Таким образом, при перезагрузке страницы, у нас остаётся актуальное значение языка.


ссылка на оригинал статьи https://habr.com/ru/articles/838666/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *