😍Đa ngôn ngữ cho dự án React.js, xử lý theo attribute change theo lang (ok)

https://viblo.asia/p/huong-dan-da-ngon-ngu-cho-du-an-reactjs-su-dung-react-i18next-LzD5da8EKjY

Example 1

src\index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import i18n from './translation/i18n';
import { I18nextProvider } from 'react-i18next';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <I18nextProvider i18n={i18n}>
      <App />
    </I18nextProvider>
  </React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

src\App.js

import Header from './components/Header'
import ClassComponent from './components/Class'
import Functional from './components/Functional'
import './App.css';
function App() {
  return (
    <div>
        <div className="App">
            <Header />
        </div>
        <div className="content d-flex w-100">
            <ClassComponent />
            <Functional />
        </div>
    </div>
  );
}
export default App;

src\components\Class\index.js

import React from 'react'
import { withTranslation } from 'react-i18next';
class ClassComponent extends React.Component {
  render() {
    const { t } = this.props
    return (
      <div className="col-md-6">
        <div className="card p-2">
          <div className="card-body">
            <h5 class="card-title">{t('content.class')}</h5>
          </div>
        </div>
      </div>
    )
  }
}
export default withTranslation()(ClassComponent)

src\components\Functional\index.js

import React from 'react'
import { useTranslation } from 'react-i18next';
const Functional = () => {
  const { t } = useTranslation()
  return (
    <div className="col-md-6">
      <div className="card p-2">
        <div className="card-body">
          <h5 class="card-title">{t('content.functional')}</h5>
        </div>
      </div>
    </div>
  )
}
export default Functional

src\components\Header.js

import React from 'react'
import i18n from '../translation/i18n';
import logo from '../logo.svg';
import './style.css';
const Header = () => {
  function changeLanguage(e) {
    i18n.changeLanguage(e.target.value);
  }
  return (
    <header className="App-header">
      <div className="w-100 d-flex justify-content-between">
        <img src={logo} className="App-logo" alt="logo" />
        <div className="d-flex align-items-center">
          <select onChange={changeLanguage}>
            <option value="vi">
              Vietnamese
            </option>
            <option value="en">
              English
            </option>
          </select>
        </div>
      </div>
    </header>
  )
}
export default Header

src\locales\en\translation.json

{
  "content": {
    "functional": "Functional",
    "class": "Class",
    "text": "This is text"
  }
}

src\locales\vi\translation.json

{
  "content": {
    "functional": "Hàm",
    "class": "Lớp",
    "text": "Đây là văn bản"
  }
}

src\translation\i18n.js

import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
import translationEN from '../locales/en/translation';
import translationVI from '../locales/vi/translation';
// the translations
const resources = {
  en: {
    translation: translationEN
  },
  vi: {
    translation: translationVI
  }
};
i18n
  .use(Backend)
  .use(initReactI18next)
  .init({
    resources,
    fallbackLng: 'vi',
    debug: true,
    interpolation: {
      escapeValue: false // not needed for react as it escapes by default
    }
  });
export default i18n;

package.json

{
  "name": "react-i18n-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "i18next": "^23.16.2",
    "i18next-http-backend": "^2.6.2",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-i18next": "^15.1.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Example 2: Xử lý thoe attribute change cái này để áp dụng vào dự án khác

public\index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
    <script type="text/javascript">
      document.documentElement.setAttribute('lang', navigator.language);
    </script>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

src\translation\i18n.js

import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
import translationEN from '../locales/en/translation';
import translationVI from '../locales/vi/translation';
// the translations
const resources = {
  en: {
    translation: translationEN
  },
  vi: {
    translation: translationVI
  }
};
console.log(document.documentElement);
i18n
  .use(Backend)
  .use(initReactI18next)
  .init({
    resources,
    fallbackLng: document.documentElement.lang || "vi",
    debug: true,
    interpolation: {
      escapeValue: false // not needed for react as it escapes by default
    }
  });
export default i18n;

Example 3 Áp dụng cho theme wordpress

wp-content\themes\twentytwentyfour\functions.php

function lionel_wp_dequeue_style() {
   wp_enqueue_script("index-DFLraXyt", get_stylesheet_directory_uri() . "/assets/js/index.js", array(), '1.0.0', true);
}
add_action('wp_enqueue_scripts', 'lionel_wp_dequeue_style', 1000);

Lời mở đầu

II. Nội dung chính

1. Cài đặt:

  • Cài đặt React app:

npx create-react-app react-i18n-app
cd react-i18n-app
  • Cài đặt package react-i18n

yarn add react-i18next i18next i18next-http-backend
npm install react-i18next i18next i18next-http-backend --save
  • Hoặc sử dụng qua CDN:

https://unpkg.com/react-i18next/react-i18next.js
https://unpkg.com/react-i18next/react-i18next.min.js

2. Áp dụng:

Đầu tiên bạn hãy tạo folder locales, chứa phần dịch văn bản theo các ngôn ngữ. Ví dụ mình đa ngôn ngữ cho tiếng việt và tiếng anh thì mình tạo 2 folder là envi.

  • File locales/en/translation.json

{
    "content": {
        "functional": "Functional",
        "class": "Class",
        "text": "This is text"
    }
}
  • File locales/vi/translation.json

{
    "content": {
        "functional": "Hàm",
        "class": "Lớp",
        "text": "Đây là văn bản"
    }
}

Sau đó hãy tạo folder translation chứa file translation/i18n.js. Bạn import những phần ngôn ngữ dịch cho trang và init i18next:

import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';

import translationEN from '../locales/en/translation';
import translationVI from '../locales/vi/translation';

// the translations
const resources = {
    en: {
        translation: translationEN
    },
    vi: {
        translation: translationVI
    }
};

i18n
    .use(Backend)
    .use(initReactI18next)
    .init({
        resources,
        fallbackLng: 'vi',
        debug: true,
        interpolation: {
            escapeValue: false // not needed for react as it escapes by default
        }
    });

export default i18n;

Cuối cùng bạn dùng I18nextProvider để có thể sử dụng prop i18n qua context API:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import 'bootstrap/dist/css/bootstrap.min.css';

import i18n from './translation/i18n';
import { I18nextProvider } from 'react-i18next';

ReactDOM.render(
  <React.StrictMode>
    <I18nextProvider i18n={i18n}>
      <App />
    </I18nextProvider>

  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Vậy là quá trình setup package đã xong. Package react-i18next có hỗ trợ chúng ta khá nhiều cách sử dụng instance phía bên trong component:

  • Sử dụng hook: useTranslation

import React from 'react'
import { useTranslation } from 'react-i18next';

const Functional = () => {
    const { t } = useTranslation()

    return (
        <div className="col-md-6">
            <div className="card p-2">
                <div className="card-body">
                    <h5 class="card-title">{t('content.functional')}</h5>
                </div>
            </div>
        </div>
    )
}

export default Functional
  • Sử dụng HOC: withTranslation

import React from 'react'
import { withTranslation } from 'react-i18next';

class ClassComponent extends React.Component {
    render() {
        const { t } = this.props

        return (
            <div className="col-md-6">
                <div className="card p-2">
                    <div className="card-body">
                        <h5 class="card-title">{t('content.class')}</h5>
                    </div>
                </div>
            </div>
        )
    }
}

export default withTranslation()(ClassComponent)
  • Sử dụng Trans Component

import React from 'react'
import { Trans } from 'react-i18next';

const Functional = () => {
    return (
        <div className="col-md-6">
            <div className="card p-2">
                <div className="card-body">
                    <Trans i18nKey='content.text' />
                </div>
            </div>
        </div>
    )
}

export default Functional
  • Sử dụng Translation (render prop)

import React from 'react'
import { Translation } from 'react-i18next';

const Functional = () => {
    return (
        <div className="col-md-6">
            <div className="card p-2">
                <div className="card-body">
                    <Translation>
                        {
                            (t, { i18n }) => <p>{t('content.text')}</p>
                        }
                    </Translation>
                </div>
            </div>
        </div>
    )
}

export default Functional

3. Demo:

Github: https://github.com/vanquynguyen/react-i18n-app

III. Tạm kết

Chắc hẳn sau bài viết này các bạn đã có thể sử dụng package để phát triển đa ngôn ngữ cho dự án của mình rồi đúng không nào. Rất mong nhận được ý kiến đóng góp từ mọi người.

Last updated

Was this helpful?