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

в 14:25, , рубрики: Axios, мультиязычность

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

  • Сама страница, текст, которой приходит с бэка по 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, то используем это значение. Таким образом, при перезагрузке страницы, у нас остаётся актуальное значение языка.

Автор: paspean

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js