Это часто нужно создать сайт что будет поддерживать несколько языков, для примера я собираюсь создать мультиязычный сайт который будет поддерживать 3 языка пока что. Потом я добавлю еще несколько.
И так, я хочуу создать с тремя ua, ru и en. И в этой статье я объясну вам как это реализовать.
Как react-router работает с мультиязычными сайтами
Короткое описание и понимание что мы делаем. Наш сайт будет иметь ссылки:
site.com — it will redirect to site.com/[locale]/
site.com — это будет редиреткить на site.com/[locale]/
site.com/en/ — English версия
site.com/ru/ — Russian версия
site.com/ua/ — Ukrainian версия
site.com должен редиректить пользователей на en или ru или ua
он редиректит на ru если locale в window.localStorage.getItem(‘locale’). Если это не инициализировано он будет инициализировано в ‘en‘ window.localStorage.setItem(‘locale’, ‘en’)
все остальные урлы такие как site.com/de/, site.com/sdf/, site.com/adfsdf… etc. будут редиректить на 404 page.
когда урлы начинаются с site.com/ru/ etc. и locale установлена в en то это будет локаль из урл и изменение сохраненной локали в url локаль.
И так начинаем!
Мы должны установить react-router-dom
1 |
npm i react-router-dom |
и потом импортировать компоненты
1 2 3 4 5 6 |
import { BrowserRouter as Router, Route, Link, Redirect, Switch, withRouter } from 'react-router-dom' |
Теперь создаем 4 компонента
Home, About, Contacts и NoFound для 404 error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
const Home = (props) => { return <div> <LangButtons {...props} /> <h1>Home</h1> </div> } const About = (props) => { return <div> <LangButtons {...props} /> <h1>About</h1> </div> } const Contacts = (props) => { return <div> <LangButtons {...props} /> <h1>Contacts</h1> </div> } const NoFound = (props) => { return <div> <LangButtons {...props} /> <h1>404</h1> </div> } |
<LangButtons> это компонент что рендерит три кнопки en, ru, ua, которые будут изменять язык.
потом мы создаем <LangRouter> где помещаем 3 роута для каждого языка с регулянрыми выражениями /en/* /ru/* и /ua/*.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
class LangRouter extends React.Component { constructor(props) { super(props) let locale = window.localStorage.getItem('locale') if (locale === null) { window.localStorage.setItem('locale', 'en') locale = 'ru' } this.state = { locale: locale } } setLocale = (newLocale) => { console.log('newLocale = ', newLocale); window.localStorage.setItem('locale', newLocale) this.setState({ locale: newLocale }) } render() { const { locale } = this.state return ( <Switch> <Route path="/" exact render={propRouter => { return <Redirect to={locale + "/"} /> }} /> <Route path="/en/*" render={ propsRouter => <App {...propsRouter} locale={locale} setLocale={this.setLocale} /> }/> <Route path="/ru/*" render={ propsRouter => <App {...propsRouter} locale={locale} setLocale={this.setLocale} /> } /> <Route path="/ua/*" render={ propsRouter => <App {...propsRouter} locale={locale} setLocale={this.setLocale} /> } /> <Route path="*" render={ propsRouter => <NoFound {...propsRouter} locale={locale} setLocale={this.setLocale} /> } /> </Switch> ) } } |
Это корневой компонет в котором сохранена locale нашего приложения для всех child компонент. В конструкторе мы проверяем window.localStorage на ‘local’ и если это установлено то мы инициализируем this.state.locale с этим значением и передаем вниз компонентам. Функция setLocal используется передавать вниз как это называется two way binding. Эта функция будет передваваться кнопкам которые будут изменять this.state.locale.
в Route мы используем render потому что мы должны передать setLocal и this.state.locale к children, и так мы не можем использовать component.
Теперь, давайте создадим App
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
const App = (props) => { let { locale } = props let urlLocale = props.location.pathname.substring(1, 3) if ( locale !== urlLocale ) { props.setLocale(urlLocale) locale = urlLocale } return ( <div> <Link to={"/" + locale + "/"}>Home</Link> |{' '} <Link to={"/" + locale + "/about"}>About</Link> |{' '} <Link to={"/" + locale + "/contacts"}>Contacts</Link> <Switch> <Route path={"/" + locale + "/"} exact render={propRouter => <Home {...propRouter} {...props} /> } /> <Route path={"/" + locale + "/about"} render={propRouter => <About {...propRouter} {...props} /> } /> <Route path={"/" + locale + "/contacts"} render={propRouter => <Contacts {...propRouter} {...props} /> } /> <Route path="*" render={propRouter => <NoFound {...propRouter} {...props} /> } /> </Switch> </div> ) } |
Он принимает props и проверяет locale из url если они разные, урл значение будет установлено locale из родительского компонента вызовом function props.setLocale(urlLocale)
Потом мы видим несколько <Link> елементов и <Route> где динамически генерируются пути. Снова мы используем render потому что мы должны передавать данные к children.
Последний елемент LangButtons.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class LangButtons extends React.Component { setLocale = (newLocale) => { const { locale } = this.props let url = '/' + newLocale + this.props.location.pathname.substring(3) this.props.history.push(url) this.props.setLocale(newLocale) } render() { return ( <div> <button onClick={() => this.setLocale('ru')} >ru</button> | <button onClick={() => this.setLocale('en')} >en</button> | <button onClick={() => this.setLocale('ua')} >ua</button> </div> ) } } |
Каждая кнопка имеет свой собственные обработчик событий которые вызывает props function setLocale и изменяет locale
Ниже весь код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
import React from 'react' import ReactDOM from 'react-dom' import { BrowserRouter as Router, Route, Link, Redirect, Switch, withRouter } from 'react-router-dom' class LangButtons extends React.Component { setLocale = (newLocale) => { const { locale } = this.props let url = '/' + newLocale + this.props.location.pathname.substring(3) this.props.history.push(url) this.props.setLocale(newLocale) } render() { return ( <div> <button onClick={() => this.setLocale('ru')} >ru</button> | <button onClick={() => this.setLocale('en')} >en</button> | <button onClick={() => this.setLocale('ua')} >ua</button> </div> ) } } const Home = (props) => { return <div> <LangButtons {...props} /> <h1>Home</h1> </div> } const About = (props) => { return <div> <LangButtons {...props} /> <h1>About</h1> </div> } const Contacts = (props) => { return <div> <LangButtons {...props} /> <h1>Contacts</h1> </div> } const NoFound = (props) => { return <div> <LangButtons {...props} /> <h1>404</h1> </div> } const App = (props) => { let { locale } = props let urlLocale = props.location.pathname.substring(1, 3) if ( locale !== urlLocale ) { props.setLocale(urlLocale) locale = urlLocale } return ( <div> <Link to={"/" + locale + "/"}>Home</Link> |{' '} <Link to={"/" + locale + "/about"}>About</Link> |{' '} <Link to={"/" + locale + "/contacts"}>Contacts</Link> <Switch> <Route path={"/" + locale + "/"} exact render={propRouter => <Home {...propRouter} {...props} /> } /> <Route path={"/" + locale + "/about"} render={propRouter => <About {...propRouter} {...props} /> } /> <Route path={"/" + locale + "/contacts"} render={propRouter => <Contacts {...propRouter} {...props} /> } /> <Route path="*" render={propRouter => <NoFound {...propRouter} {...props} /> } /> </Switch> </div> ) } class LangRouter extends React.Component { constructor(props) { super(props) let locale = window.localStorage.getItem('locale') if (locale === null) { window.localStorage.setItem('locale', 'en') locale = 'ru' } this.state = { locale: locale } } setLocale = (newLocale) => { console.log('newLocale = ', newLocale); window.localStorage.setItem('locale', newLocale) this.setState({ locale: newLocale }) } render() { const { locale } = this.state return ( <Switch> <Route path="/" exact render={propRouter => { return <Redirect to={locale + "/"} /> }} /> <Route path="/en/*" render={ propsRouter => <App {...propsRouter} locale={locale} setLocale={this.setLocale} /> }/> <Route path="/ru/*" render={ propsRouter => <App {...propsRouter} locale={locale} setLocale={this.setLocale} /> } /> <Route path="/ua/*" render={ propsRouter => <App {...propsRouter} locale={locale} setLocale={this.setLocale} /> } /> <Route path="*" render={ propsRouter => <NoFound {...propsRouter} locale={locale} setLocale={this.setLocale} /> } /> </Switch> ) } } ReactDOM.render( <Router> <LangRouter /> </Router>, document.getElementById('root') ) |
Мы получили следующий результат.