Перевод состоит из двух частей:
-
Сама страница, текст, которой приходит с бэка по 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/
Добавить комментарий