在前一篇文章中,我們展示了如何將多語言資料從本地 JSON 檔案轉換為 API 形式,並將其部署到 Vercel 進行集中管理。這樣的改進使我們的應用能夠動態從伺服器獲取翻譯資料,提升了多語言管理的靈活性與效能。
今天,我們將繼續這個基礎,深入探討如何在前端 React 應用中整合這個 API,並實現翻譯資料的快取,避免重複請求,提升應用的效能。這將包括利用單例模式來管理 i18n 實例,並進行多語言切換的效能優化。
單例模式 是設計模式中的一種,它的主要特點是保證一個類在應用程序中只有一個實例,並提供一個全局的訪問點。具體來說,單例模式通常應用在需要共享全局狀態或設定的場景,確保狀態的一致性。
在這篇文章中,我們使用了單例模式來管理 i18n 的初始化邏輯。這樣可以確保 i18n 的初始化只會執行一次,即使在應用中的不同部分多次調用,也始終會返回同一個 i18n 實例,避免了重複初始化的問題。這不僅提高了效能,還能保持語言設定和翻譯資料的一致性。
在本實作中,當我們第一次創建 i18n 實例時,系統會檢查是否已經有一個實例存在,如果不存在則創建一個,否則直接返回現有的實例。這樣的設計模式讓我們的應用更加靈活且高效。
接下來,我們來看如何使用 i18next-chained-backend 和 i18next-localstorage-backend 來優化翻譯資料的加載。
i18next-chained-backend
首先,我們需要安裝兩個插件來支援我們的翻譯資料動態加載及快取:
npm install i18next-chained-backend i18next-localstorage-backend
i18next-chained-backend
:這個插件允許我們將多個翻譯資料來源鏈接在一起。例如,我們可以首先嘗試從本地快取讀取資料,若快取不存在或過期,則回退到遠端的 API。i18next-localstorage-backend
:這個插件負責將翻譯資料儲存在 localStorage
中,讓應用下次訪問時可以直接使用快取資料,提升效能。LangButton
我們將語言偏好儲存到 localStorage
中,以便後續使用。在語言切換過程中,可能會面臨網路延遲或 API 請求失敗的情況,因此需要一個可靠的錯誤處理機制來確保即時反饋用戶操作。
//src/components/navBar/Langbutton
import React from 'react'
import i18n from '@/utils/i18n';
import * as styles from './LangButton.module.scss';
import { log } from '@/utils/log';
import loadChineseFont from '@/utils/loadChineseFont';
const LangButton = ({ }) => {
// 切換語言選項的函數
const toggleLanguage = async () => {
const newLang = i18n.language === 'en' ? 'zh' : 'en';
if (newLang === 'zh') {
loadChineseFont();
}
try {
await i18n.changeLanguage(newLang);
localStorage.setItem('language', newLang); // 保存語言偏好到 localStorage
} catch (err) {
log(logLevel.ERROR, `Language switch error: ${err}`);
}
}
log(logLevel.DEBUG, 'LangButton rendered');
return (
<button className={styles.lang_btn}
onClick={toggleLanguage}>
🌐 {i18n.language.toUpperCase()} ▼
</button>
)
}
export default React.memo(LangButton);
說明:
try-catch
來捕捉 i18n.changeLanguage
的潛在錯誤,確保在網路或翻譯載入失敗的情況下,應用不會崩潰。在這一步,我們將確保應用能自動偵測用戶的語言偏好,並通過 localStorage
快取翻譯資料,避免每次重新請求 API。
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-chained-backend';
import LocalStorageBackend from 'i18next-localstorage-backend'; // primary use cache
import HttpApi from 'i18next-http-backend';
import config from '@/utils/config';
import { log } from '@/utils/log';
let instance = null;
const createI18nInstance = () => {
if (!instance) {
console.log("createI18nInstance");
// 支援的語言
const supportedLanguages = ['en', 'zh'];
// 從 localStorage 或瀏覽器語言檢測得到用戶的語言
let defaultLanguage = localStorage.getItem('language') || navigator.language.split('-')[0];
// 檢查是否支援該語言,否則預設為英文
if (!supportedLanguages.includes(defaultLanguage)) {
defaultLanguage = 'en';
}
// 保存語言到 localStorage,以便下次訪問時使用
localStorage.setItem('language', defaultLanguage);
i18n
.use(Backend) // 使用 i18next-http-backend
.use(initReactI18next)
.init({
lng: defaultLanguage, // 預設語言
fallbackLng: "en", // 找不到語言時回退語言
debug: config.featureFlags.enableLogging && config.logLevel === logLevel.DEBUG, // 只有當 logLevel 設置為 DEBUG 時,啟用 debug 模式
interpolation: {
escapeValue: false, // React 已經防止了 XSS 攻擊
},
useSuspense: false,
backend: {
backends: [
LocalStorageBackend, // primary backend
HttpApi // fallback backend
],
backendOptions: [{
/* options for primary backend */
enabled: true, // 啟用快取
prefix: 'i18next_res_', // prefix for stored languages
versions: { en: 'v1.0', zh: 'v1.0' }, // 版本控制快取
expirationTime: 7 * 24 * 60 * 60 * 1000, // 7天後過期
// can be either window.localStorage or window.sessionStorage. Default: window.localStorage
store: typeof window !== 'undefined' ? window.localStorage : null
}, {
/* options for secondary backend */
loadPath: (languages, namespaces) => {
const path = `${process.env.REACT_APP_I18N_API_URL}?lang=${languages[0]}`;
log(logLevel.DEBUG, `Dynamically generated loadPath: ${path}`); // 打印 loadPath
return path;
},
crossDomain: true,
}],
},
});
instance = i18n;
}
return instance;
};
export default createI18nInstance;
說明:
localStorage
或瀏覽器的語言設定自動選擇適合的語言。i18next-localstorage-backend
來儲存翻譯資料,並設定 7 天的過期時間,提升效能並減少不必要的 API 請求。透過結合 navigator.language
和 localStorage
,我們為應用提供了一個智能且靈活的語言切換機制。這種方式能夠自動偵測用戶的語言需求,並讓用戶手動調整語言後能保存其選擇,進一步提升應用的使用體驗。此外,我們使用了快取機制來優化翻譯資料的加載效能,減少不必要的 API 請求。
最後,我們也介紹了 單例模式 如何幫助我們有效管理應用中的全域狀態,確保 i18n
實例的唯一性並提升應用的效能。如果你對更多前端技術或專案實作有興趣,請持續關注我們的後續文章!
✨ 流光館Luma<∕> ✨ 期待與你繼續探索更多技術知識!