說(shuō)瘋狂的開發(fā)者!
今天我將向您展示,將整個(gè)項(xiàng)目翻譯成 react 從未像現(xiàn)在這樣容易。但首先您需要知道為什么這很重要。
當(dāng)人們開始編程時(shí),代碼文本和消息通常使用葡萄牙語(yǔ)(pt-br)。將項(xiàng)目翻譯成其他語(yǔ)言從來(lái)都不是優(yōu)先事項(xiàng),并且被認(rèn)為是復(fù)雜或不必要的。
那么為什么它會(huì)相關(guān)呢?
這取決于你的實(shí)際情況。以下是您應(yīng)該考慮此過(guò)程的一些原因:
公司需要
可能是您工作的公司或您擁有的某些 saas 開始在另一個(gè)國(guó)家/地區(qū)運(yùn)營(yíng)并且有此需求。具有此功能的產(chǎn)品有巨大的差異。
申請(qǐng)國(guó)??際職位空缺
如果您正在申請(qǐng)國(guó)際職位空缺,擁有包含國(guó)際化項(xiàng)目的作品集可以給您帶來(lái)引人注目的亮點(diǎn)。這表明您已準(zhǔn)備好從事全球項(xiàng)目,并且不像大多數(shù)人一樣懶惰。
你永遠(yuǎn)不會(huì)學(xué)太多
國(guó)際化不僅是一種特征,也是一種重要的學(xué)習(xí)經(jīng)歷。這是您的技能和工具庫(kù)中的另一種武器。
過(guò)去是如何做到的?
項(xiàng)目翻譯已經(jīng)是個(gè)老問(wèn)題了。人們?cè)?html 中選擇了該國(guó)家的國(guó)旗,供人們選擇,并在代碼中用 if 填充以了解將顯示哪些文本。
這是非常被忽視的。網(wǎng)站是用單一語(yǔ)言制作的,翻譯是隨意添加的。如果是在后端,交易會(huì)更糟糕。
隨著互聯(lián)網(wǎng)的全球化,對(duì)多語(yǔ)言軟件的需求不斷增長(zhǎng),帶來(lái)了針對(duì)國(guó)際化的特定工具。像 gnu gettext 這樣的解決方案出現(xiàn)在后端,隨后出現(xiàn)了像 i18next 和 react-intl 這樣的前端庫(kù)。然后疑問(wèn)就來(lái)了…
i18next 對(duì)比react-intl:選擇哪一個(gè)?
i18next:這個(gè)出現(xiàn)于 2011 年,它是一個(gè) npm 包,適用于客戶端的 node.js 和 spa。社區(qū)采納了它,并最終于2015年在react-i18next lib中制作了react版本。因此,作為積極和消極的點(diǎn),我們有:
優(yōu)點(diǎn):靈活性、路上時(shí)間(自 2011 年起)、龐大的生態(tài)系統(tǒng)(一次學(xué)習(xí),隨處翻譯)和自動(dòng)回退。
缺點(diǎn):學(xué)習(xí)曲線。有很多文檔需要閱讀,但并不是您需要的所有內(nèi)容都在那里。
react-intl:formatjs 項(xiàng)目的一部分,遵循國(guó)際 javascript api 標(biāo)準(zhǔn),確保與現(xiàn)代瀏覽器的兼容性。
優(yōu)點(diǎn):與 ecmascript 標(biāo)準(zhǔn)保持一致,集成簡(jiǎn)單。
缺點(diǎn):靈活性較差,插件支持較少。
我們將使用哪一個(gè)?
i18下一個(gè)我的朋友們!我總是建議閱讀文檔來(lái)開始,但讓我們看看 doido 的指南!
使用 i18next 國(guó)際化 react 應(yīng)用程序
安裝:
npm install i18next i18next-chained-backend i18next-http-backend i18next-resources-to-backend react-i18next next-i18next
登錄后復(fù)制
配置:創(chuàng)建一個(gè)i18n.js來(lái)配置i18next。
import i18n from 'i18next'; import { initreacti18next } from 'react-i18next'; import backend from 'i18next-http-backend'; import languagedetector from 'i18next-browser-languagedetector'; i18n .use(backend) .use(languagedetector) .use(initreacti18next) .init({ fallbacklng: 'en', interpolation: { escapevalue: false } }); export default i18n;
登錄后復(fù)制
translations:在 locales/en/translation.json 和 locales/pt/translation.json 中創(chuàng)建翻譯文件。
{ "welcome": "welcome to our application!", "login": "login" }
登錄后復(fù)制
翻譯的使用:在 react 中使用 usetranslation 鉤子。
import react from 'react'; import { usetranslation } from 'react-i18next'; function app() { const { t } = usetranslation(); return ( <div> <h1>{t('welcome')}</h1> <button>{t('login')}</button> </div> ); } export default app;
登錄后復(fù)制
語(yǔ)言更改:允許用戶更改語(yǔ)言。
import react from 'react'; import { usetranslation } from 'react-i18next'; function languageswitcher() { const { i18n } = usetranslation(); const changelanguage = (lng) => i18n.changelanguage(lng); return ( <div> <button onclick="{()"> changelanguage('en')}>english</button> <button onclick="{()"> changelanguage('pt')}>português</button> </div> ); } export default languageswitcher;
登錄后復(fù)制
這就是全部嗎?
當(dāng)然不是,我現(xiàn)在向您展示我在 crazystack 項(xiàng)目中做了什么。首先,我在 nextjs 中做了一個(gè)不同的配置,采用了我在項(xiàng)目本身的公共文件夾中定義的靜態(tài) json!看看:
import i18next from "i18next"; import chainedbackend from "i18next-chained-backend"; import httpbackend from "i18next-http-backend"; import resourcestobackend from "i18next-resources-to-backend"; import { initreacti18next } from "react-i18next"; import { defaulttexts } from "./defaulttexts"; i18next .use(chainedbackend) .use(initreacti18next) .init({ lng: "pt-br", fallbacklng: "pt-br", interpolation: { escapevalue: false, }, compatibilityjson: "v3", react: { //wait: true,//usar no react native usesuspense: false, }, backend: { backends: [httpbackend, resourcestobackend(defaulttexts)], backendoptions: [ { loadpath: `${process.env.next_public_url}/{{lng}}/{{ns}}.json`, }, ], }, });
登錄后復(fù)制
然后我創(chuàng)建了一個(gè)上下文 api 來(lái)保存語(yǔ)言并在整個(gè)項(xiàng)目中訪問(wèn)它。從進(jìn)口開始
2. 進(jìn)口
import { usetranslation } from "react-i18next"; import { createcontext, usestate, usecontext } from "react";
登錄后復(fù)制
usetranslation:react-i18next 掛鉤來(lái)訪問(wèn)翻譯功能。在這里,您將在項(xiàng)目中幾乎每個(gè) jsx 組件中使用它。
createcontext、usestate、usecontext:用于創(chuàng)建和使用上下文以及管理狀態(tài)的 react 函數(shù)。
3. 上下文創(chuàng)建
const i18ncontext = createcontext({} as any);
登錄后復(fù)制
創(chuàng)建上下文來(lái)通過(guò) dom 存儲(chǔ)和提供數(shù)據(jù)(例如當(dāng)前語(yǔ)言)。
4. 環(huán)境檢查
export const isbrowser = typeof window !== "undefined";
登錄后復(fù)制
此行檢查代碼是否在瀏覽器中運(yùn)行(而不是在服務(wù)器上),這對(duì)于處理 localstorage 等特定于客戶端的功能至關(guān)重要。
5.i18nprovider組件
export const i18nprovider = ({ children }: any) => { const { i18n } = usetranslation() || {}; const [currentlanguage, setcurrentlanguage] = usestate( formatlanguagefromi18n(i18n?.language) ); const changelanguage = (language) => { setcurrentlanguage(language); i18n?.changelanguage?.(formatlanguagefromselect(language)); localstorage.setitem("language", formatlanguagefromselect(language)); }; return ( <i18ncontext.provider value="{{" changelanguage currentlanguage setcurrentlanguage> {children} </i18ncontext.provider> ); };
登錄后復(fù)制
這個(gè)組件是一個(gè)provider,它包裝了react組件樹并提供了語(yǔ)言的當(dāng)前狀態(tài)以及更改它的函數(shù)。
usetranslation:從react-i18next庫(kù)中檢索i18n對(duì)象,其中包含有關(guān)當(dāng)前語(yǔ)言的信息。
currentlanguage:存儲(chǔ)當(dāng)前語(yǔ)言的state,根據(jù)i18n檢測(cè)到的語(yǔ)言進(jìn)行初始化。
changelanguage:更改語(yǔ)言的函數(shù),這也會(huì)將選擇保存在 localstorage 中,以便在頁(yè)面重新加載之間保持持久性。
6. 掛鉤使用i18n
export const usei18n = () => { if (!isbrowser) { return { currentlanguage: "pt-br", setcurrentlanguage: () => {}, changelanguage: () => {}, }; } return usecontext(i18ncontext); };
登錄后復(fù)制
這個(gè)鉤子可以輕松訪問(wèn)任何組件中的國(guó)際化上下文。
檢查您是否在瀏覽器中(isbrowser)。如果沒(méi)有,則返回默認(rèn)值,以避免服務(wù)器端出錯(cuò)。
如果在瀏覽器中,則消耗并返回 i18ncontext 上下文。
7. 轉(zhuǎn)換圖
const countrytolanguage = { br: "pt-br", us: "en", }; const languagetocountry = { "pt-br": "br", en: "us", };
登錄后復(fù)制
這些對(duì)象將國(guó)家/地區(qū)代碼映射到語(yǔ)言代碼,反之亦然,從而可以輕松地在不同約定之間格式化語(yǔ)言代碼。
8. 格式化函數(shù)
export const formatlanguagefromi18n = (language) => languagetocountry[language]; export const formatlanguagefromselect = (language) => countrytolanguage[language];
登錄后復(fù)制
這些函數(shù)根據(jù)需要格式化語(yǔ)言環(huán)境。 formatlanguagefromi18n 將語(yǔ)言代碼轉(zhuǎn)換為國(guó)家/地區(qū)代碼,而 formatlanguagefromselect 則進(jìn)行反向轉(zhuǎn)換。
完整代碼
"use client"; import { usetranslation } from "react-i18next"; import { createcontext, usestate, usecontext } from "react"; const i18ncontext = createcontext({} as any); export const isbrowser = typeof window !== "undefined"; export const i18nprovider = ({ children }: any) => { const { i18n } = usetranslation() || {}; const [currentlanguage, setcurrentlanguage] = usestate( formatlanguagefromi18n(i18n?.language) ); const changelanguage = (language) => { setcurrentlanguage(language); i18n?.changelanguage?.(formatlanguagefromselect(language)); localstorage.setitem("language", formatlanguagefromselect(language)); }; return ( <i18ncontext.provider value="{{" changelanguage currentlanguage setcurrentlanguage> {children} </i18ncontext.provider> ); }; export const usei18n = () => { if (!isbrowser) { return { currentlanguage: "pt-br", setcurrentlanguage: () => {}, changelanguage: () => {}, }; } return usecontext(i18ncontext); }; const countrytolanguage = { br: "pt-br", us: "en", }; const languagetocountry = { "pt-br": "br", en: "us", }; export const formatlanguagefromi18n = (language) => languagetocountry[language]; export const formatlanguagefromselect = (language) => countrytolanguage[language];
登錄后復(fù)制
然后我改變了導(dǎo)航欄
在代碼中,我使用國(guó)家/地區(qū)下拉菜單選擇語(yǔ)言。看看:
"use client"; //@ts-nocheck import { header, flex, logo, profile, notificationsnav, searchbar } from "@/shared/ui"; import { usebreakpointvalue, icon, iconbutton, usemediaquery } from "@chakra-ui/react"; import { rimenuline } from "react-icons/ri"; import { useauth, usesidebardrawer } from "@/shared/libs"; import { useeffect, usestate } from "react"; import { countrydropdown } from "react-country-region-selector"; import { theme } from "@/application/theme"; import { formatlanguagefromi18n, usei18n } from "@/application/providers/i18nprovider"; import { usetranslation } from "react-i18next"; export const navbar = ({ showlogo = true }) => { const { isauthenticated } = useauth() || {}; const { i18n } = usetranslation(); const { changelanguage, setcurrentlanguage } = usei18n() || {}; const { onopen = () => {}, onclose } = usesidebardrawer() || {}; const isdesktopversion = usebreakpointvalue({ base: false, lg: true }); const [country, setcountry] = usestate(formatlanguagefromi18n(i18n?.language)); useeffect(() => { return () => { onclose?.(); }; }, []); const dropdown = countrydropdown as any; useeffect(() => { const language = localstorage.getitem("language"); if (language) { setcountry(formatlanguagefromi18n(language)); setcurrentlanguage(language); i18n?.changelanguage?.(language); } }, []); return ( <header><flex alignitems='{"center"}' w='{"100%"}'> {isauthenticated && !isdesktopversion && ( <iconbutton aria-label="open sidebar" fontsize="24" icon="{<icon" as="{rimenuline}"></iconbutton>} variant="unstyled" onclick={onopen} mr="1" mt={2} /> )} <logo marginbottom="{0}"></logo> {/* {islargerthan560 && ( <searchbar placeholder="pesquise por nome..." name="search" width="auto"></searchbar> )} */} {isauthenticated && ( <flex align="center" ml="auto"> {/* <notificationsnav></notificationsnav> */} <dropdown value="{country}" onchange="{(val)"> { setcountry(val); changelanguage(val); }} labeltype="short" valuetype="short" showdefaultoption defaultoptionlabel="selecione o idioma" whitelist={["us", "br"]} style={{ backgroundcolor: theme.colors.secondary[400], padding: 10, width: 60, marginright: 15, borderradius: 8, }} /> <profile showprofiledata="{isdesktopversion}"></profile></dropdown></flex> )} </flex></header> ); };
登錄后復(fù)制
導(dǎo)入和初始設(shè)置:
useauth:檢查用戶是否經(jīng)過(guò)身份驗(yàn)證。
usebreakpointvalue:根據(jù)屏幕大小決定是否顯示桌面版本。
usestate:設(shè)置國(guó)家/語(yǔ)言(國(guó)家)的初始狀態(tài),使用formatlanguagefromi18n函數(shù)格式化i18n的當(dāng)前語(yǔ)言
useeffect:第一個(gè)效果是在卸載組件時(shí)清除側(cè)邊欄(onclose)。第二個(gè)效果檢查語(yǔ)言是否保存在 localstorage 中,如果是,則更新國(guó)家/地區(qū)狀態(tài)并更改應(yīng)用程序中的語(yǔ)言。
語(yǔ)言下拉菜單:
下拉列表是使用react-country-region-selector庫(kù)的countrydropdown組件實(shí)現(xiàn)的,該組件被定制為用作語(yǔ)言選擇器。
value={country}:下拉列表中選擇的值由國(guó)家/地區(qū)控制。
onchange={(val) => { … }}:當(dāng)下拉值改變時(shí),更新國(guó)家狀態(tài),并調(diào)用changelanguage函數(shù)更改應(yīng)用程序語(yǔ)言。
whitelist={[“us”, “br”]}:將下拉選項(xiàng)限制為“us”(英語(yǔ))和“br”(葡萄牙語(yǔ))。
style={…}:下拉菜單的自定義內(nèi)聯(lián)樣式,使用主題的顏色和間距。
語(yǔ)言選擇器行為:
下拉列表允許用戶選擇首選語(yǔ)言,并且此選擇會(huì)保留在 localstorage 中。
更改語(yǔ)言時(shí),下拉列表會(huì)反映此更改,并且應(yīng)用程序會(huì)更新為使用新選擇的語(yǔ)言。
要在文章中包含您在圖像中提供的代碼片段,您可以遵循以下格式:
以及如何更改文本?
從一個(gè)組件到另一個(gè)組件,我都遵循相同的過(guò)程。下面的代碼展示了如何根據(jù)本地化密鑰用動(dòng)態(tài)翻譯替換靜態(tài)文本:
import { Divider } from "@chakra-ui/react"; import { IoExitOutline } from "react-icons/io5"; import { useRouter } from "next/navigation"; import { useTranslation } from "react-i18next"; // Importando o hook useTranslation type ProfileProps = { showProfileData?: boolean; }; export const Profile = ({ showProfileData }: ProfileProps) => { const { t } = useTranslation(["PAGES"]); // Obtendo a fun??o t para tradu??o const { user, logout } = useAuth() || {}; const router = useRouter(); const { showUserMenu, setShowUserMenu } = useProfile(); return ( <box> {/* Outras partes do componente */} <flex><ioexitoutline></ioexitoutline><text fontsize="sm"> {t("PAGES:HOME_PAGE.logout", { defaultValue: "Sair" })} // Chave de tradu??o com valor padr?o </text></flex></box> ); };
登錄后復(fù)制
在此示例中,usetranslation 掛鉤用于加載 pages:home_page.logout 翻譯鍵。如果未找到該密鑰,將顯示默認(rèn)文本“退出”。
結(jié)論
這個(gè)想法可以應(yīng)用于任何靜態(tài)文本組件。只需使用 usetranslation 鉤子即可。
國(guó)際化您的應(yīng)用程序可以打開全球市場(chǎng)的大門,突出您的投資組合并提高您的技能。在 i18next 和 react-intl 之間進(jìn)行選擇取決于您項(xiàng)目的具體需求,但對(duì)于那些想要入門的人來(lái)說(shuō),兩者都是很好的選擇。
課程建議
2022 年,我創(chuàng)建了crazystack 訓(xùn)練營(yíng)。在其中,我展示了在線服務(wù)調(diào)度系統(tǒng)的 2 個(gè)完整應(yīng)用程序,應(yīng)用了設(shè)計(jì)模式、簡(jiǎn)潔架構(gòu)、功能切片設(shè)計(jì)、solid、ddd 以及單元、集成和 e2e 測(cè)試等先進(jìn)概念。
在第一個(gè)應(yīng)用程序中,您將學(xué)習(xí)如何在 node.js 生態(tài)系統(tǒng)中構(gòu)建 rest api。將創(chuàng)建涉及復(fù)雜業(yè)務(wù)規(guī)則的用例,例如列出可用時(shí)間、根據(jù)預(yù)訂生成訂單、忠誠(chéng)度系統(tǒng)、傭金、付款、客戶評(píng)論等等。一切都在 typescript 中完成并使用非關(guān)系數(shù)據(jù)庫(kù) mongodb。
在第二個(gè)應(yīng)用程序中,您將學(xué)習(xí)如何在 react.js 生態(tài)系統(tǒng)中構(gòu)建管理面板來(lái)查看圖表和操作記錄。一切都是通過(guò) typescript 和 next.js 框架完成的。此外,還將使用chakra ui可視化組件庫(kù),將原子設(shè)計(jì)概念應(yīng)用于創(chuàng)建的組件。要了解更多信息,請(qǐng)?jiān)L問(wèn)crazystack.com.br。