2022年12月20日 星期二

[React]用 react-i18next 實現多國語言

此篇主要說明如何讓網站實現多國語言,範例程式碼可參考這裡


主要核心元件為react-i18next,透過設定的方式來實現多國語言的切換,想要知道更多的內容可以參考官網


那麼就教學開始!

首先需要先安裝i18Nextreact-i18Nexti18Next-http-backendi18Next-borwser-languagedetector

指令碼↓↓↓↓↓↓↓

npm install react-i18next i18next i18next-http-backend i18next-browser-languagedetector --save


再來看一下最後完工的檔案結構,紅色是要新增的部分

src
├─locales
│  ├─en
│  │  └─translation.json
│  └─zh
│    └─translation.json
│ 
├─app.tsx 
├─index.tsx (要import等等設定好的i18n) 
├─package.json
└─i18n.ts

先新增語言檔,分成en和zh(中文都只用繁體中文,所以不用在加-Tw)


---------------locales/en/translation.json------------------
{
  "SwitchLan": "Switch Language",
  "Login": {
    "SignIn": "Sign in",
    "Account": "Account",
    "Password": "Password",
    "Remember": "Remember me",
    "ForgetPassword": "Forgot password?"
  }
}

---------------locales/zh/translation.json------------------
{
  "SwitchLan": "切換語言",
  "Login": {
    "SignIn": "登入",
    "Account": "帳號",
    "Password": "密碼",
    "Remember": "記住我",
    "ForgetPassword": "忘記密碼?"
  }
}


再來新增i18n.ts(i18n設定檔)

--------------------------i18n.ts------------------------------
import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import translationEN from './locales/en/translation.json';
import translationZH from './locales/zh-tw/translation.json';

const resources = {
    en: {
        translation:translationEN,
    },
    zh: {
        translation:translationZH
    }
};

i18n
    // load translation using http -> see /public/locales
    // learn more: https://github.com/i18next/i18next-http-backend
    .use(Backend)
    // detect user language
    // learn more: https://github.com/i18next/i18next-browser-languageDetector
    .use(LanguageDetector)
    // pass the i18n instance to react-i18next.
    .use(initReactI18next)
    // init i18next
    // for all options read: https://www.i18next.com/overview/configuration-options
    .init({
        resources,
        fallbackLng: 'en',
        lng:"zh",
        debug: true,
        interpolation: {
            escapeValue: false, // not needed for react as it escapes by default
        },
    });

export default i18n;
----------------------------------------------------------------

打開index.tsx,把i18n設定檔import進去(紅字為修改部分)


--------------------------index.tsx------------------------------
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './i18n';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

...
-------------------------------------------------------------------

這樣設定就完成了,再來就拿Login頁面來實作,我們使用的是useTranslation(Hook)方式

簡單來說就是使用const{t}=useTranslation()後在用t('Key')去抓取對應的文字
p.s. 如果要多層結構以.隔開,例如t('login.key')

下方紅字為登入頁異動部分(有...代表被省略,請勿直接複製貼上)


--------------------------Login.tsx------------------------------

...
import UserContext from '../contexts/UserContext';
import { useTranslation } from 'react-i18next';
...

export default function Login() {
    //登入訊息
    const [LoginMsg, setLoginMsg] = useState<string>('');
    //登入資訊Context
    const userContext:any = useContext(UserContext)
    //登入確認
    const handleSubmit = ...
    //i18n
    const { t,i18n } = useTranslation();

 return (
        <ThemeProvider theme={theme}>
            <Grid container component="main" sx={{ height: '100vh' }}>
                <CssBaseline />
                <Grid
                    item
                    xs={false}
                    sm={4}
                    md={7}
                    sx={{
                        backgroundImage: 'url(https://source.unsplash.com/random)',
                        backgroundRepeat: 'no-repeat',
                        backgroundColor: (t) =>
                            t.palette.mode === 'light' ? t.palette.grey[50] : t.palette.grey[900],
                        backgroundSize: 'cover',
                        backgroundPosition: 'center',
                    }}
                />
                <Grid item xs={12} sm={8} md={5} component={Paper} elevation={6} square>
                    <Box
                        sx={{
                            my: 8,
                            mx: 4,
                            display: 'flex',
                            flexDirection: 'column',
                            alignItems: 'center',
                        }}
                    >
                        <Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
                            <LockOutlinedIcon />
                        </Avatar>
                        <Typography component="h1" variant="h5">
                            {t('Login.SignIn')}
                        </Typography>
                        <Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 1 }}>
                            <TextField
                                error={LoginMsg !== ''}
                                margin="normal"
                                required
                                fullWidth
                                id="account"
                                label={t('Login.Account')}
                                name="account"
                                autoFocus
                            />
                            <TextField
                                error={LoginMsg !== ''}
                                margin="normal"
                                required
                                fullWidth
                                name="password"
                                label={t('Login.Password')}
                                type="password"
                                id="password"
                                autoComplete="current-password"
                                helperText={LoginMsg !== '' ? LoginMsg:""}
                            />
                            <FormControlLabel
                                control={<Checkbox  value="remember" color="primary" />}
                                label={t('Login.Remember')}
                                name="remember"
                            />
                            <Button
                                type="submit"
                                fullWidth
                                variant="contained"
                                sx={{ mt: 3, mb: 2 }}
                            >
                                {t('Login.SignIn')}
                            </Button>
                            <Grid container>
                                <Grid item xs>
                                    <Link href="#" variant="body2">
                                        {t('Login.ForgetPassword')}
                                    </Link>
                                </Grid>
                                <Grid item>
                                    <Link href="#" variant="body2" onClick={() => { i18n.changeLanguage(i18n.language=="en"?"zh":"en") }}>
                                        {t('SwitchLan')}
                                    </Link>
                                </Grid>
                            </Grid>
                            <Copyright sx={{ mt: 5 }} />
                        </Box>
                    </Box>
                </Grid>
            </Grid>
        </ThemeProvider>
    );


--------------------------------------------------------------------


這樣登入頁就有多國語系了,這次在按鈕的右下有加入一個連結來實現切換功能。

因預設zh,所以會先顯示中文


點選"切換語言",就會變成英文頁面。


這樣就完成了多國語言的實作,只要在需要的頁面使用useTransaltion()就可以使用

而且i18next還有許多優點以及功能,例如自動偵測瀏覽器語言(需載入i18next-browser-languagedetector)、下次進入時會抓取上次的語言設定等等,未來如果有更多實作會在持續更新。






















沒有留言:

張貼留言

【.Net Core】 EF Core + Web API 實作

 EF Core是Entity Framework在.Net Core使用的版本,功能幾乎相同,但具有輕巧、高擴充性以及高效能等優點,建議各位學習。 通常在.Net Core如果要用ORM的方式會有兩種選擇分別是EF Core以及Dapper。 從其他網路文章可以看到這兩種在最新...