
后端微服務(wù)云原生【免費下載鏈接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 項目地址https://gitcode.com/gh_mirrors/mi/midway點擊查看免費下載本文基于 Midway 開源倉庫中的midwayjs/i18n組件源碼位于 packages/i18n系統(tǒng)講解如何在 koa / express / egg / faas 應(yīng)用中接入國際化能力從組件安裝、配置項解析、消息格式化語法到請求級語言識別query / header / cookie / Accept-Language、Cookie 回寫、fallback 兜底策略與底層源碼實現(xiàn)。讀完本文你將能夠在 Midway 應(yīng)用中用一套localeTable配置完成多語言文案管理并理解其請求上下文語言解析的完整調(diào)用鏈。一、組件概覽與安裝midwayjs/i18n是 Midway 官方提供的國際化組件核心能力包括基于localeTable的多語言文案注冊與分組管理MidwayI18nService.translate()消息翻譯接口支持對象與數(shù)組兩種占位符格式化Web 場景下自動從請求的 query、header、cookie 乃至Accept-Language中解析當前語言支持將解析結(jié)果寫入 Cookie 以便后續(xù)請求繼續(xù)使用支持 fallback 規(guī)則匹配如en_*→en_US與默認語言兜底。安裝方式$ npm i midwayjs/i18n --save該組件本身只依賴picomatch用于 fallback 通配符匹配見 package.json其余核心能力基于midwayjs/core提供要求 Node.js 版本不低于 20engines.node 20。二、接入組件在應(yīng)用的配置類中將 i18n 組件加入imports即可。以 koa 應(yīng)用為例與 egg / express / faas 的接入方式相同import * as koa from midwayjs/koa; import * as i18n from midwayjs/i18n; Configuration({ imports: [ koa, i18n, ], }) export class MainConfiguration {}組件在onReady階段會通過MidwayApplicationManager獲取所有類型為koa、egg、faas、express的應(yīng)用實例并為每個應(yīng)用注冊I18nMiddleware見 configuration.ts。也就是說只要你的應(yīng)用中存在這四類 Web 框架之一國際化中間件就會被自動掛載無需手動useMiddleware。三、核心服務(wù)MidwayI18nService 與 translate 方法組件暴露了兩個服務(wù)類均在 i18nService.ts 中定義MidwayI18nServiceSingleton單例服務(wù)負責語言表存儲、fallback 規(guī)則編譯與翻譯核心邏輯MidwayI18nService請求上下文服務(wù)注入ctx自動從當前請求上下文讀取已解析的語言再委托給 Singleton 執(zhí)行翻譯。在控制器中使用translateController(/) export class UserController { Inject() i18nService: MidwayI18nService; Get(/) async index(Query(username) username: string) { return this.i18nService.translate(HELLO_MESSAGE, { args: { username, }, }); } }translate(message, options)的完整簽名由 interface.ts 定義export interface TranslateOptions { locale?: string; // 指定語言不傳時使用請求上下文解析出的語言 group?: string; // 文案分組默認 default args?: any; // 占位符參數(shù)可以是對象或數(shù)組 }MidwayI18nService.translate的關(guān)鍵行為是當未顯式傳入locale時會優(yōu)先讀取上下文屬性I18N_ATTR_KEY即i18n:locale中由中間件預(yù)先解析好的請求語言public translate(message: string, options: TranslateOptions {}) { if (!options.locale) { options.locale this.ctx.getAttr this.ctx.getAttr(I18N_ATTR_KEY); } return this.i18nServiceSingleton.translate(message, options); }這條“中間件解析 → 存入上下文 → 服務(wù)讀取”的鏈路是理解整個組件 Web 端行為的關(guān)鍵。四、完整配置說明組件的默認配置位于 config.default.ts完整結(jié)構(gòu)如下import { I18nOptions } from ../interface; import { FORMAT } from midwayjs/core; export const i18n: I18nOptions { defaultLocale: en_US, localeTable: { en_US: {}, }, fallbacks: { // en_*: en_US, // pt: pt-BR, }, writeCookie: true, resolver: { queryField: locale, cookieField: { fieldName: locale, cookieDomain: , cookieMaxAge: FORMAT.MS.ONE_YEAR, }, }, localsField: i18n, missingKeyHandler: message message, };各配置項含義如下配置項類型默認值說明defaultLocalestringen_US默認語言。注意內(nèi)部會統(tǒng)一轉(zhuǎn)換為小寫連字符格式如en_US→en-us見下文locale 規(guī)范化localeTableRecordstring, Recordstring, any{ en_US: {} }語言文案表key 為 localevalue 為分組結(jié)構(gòu)fallbacksRecordstring, any{}fallback 規(guī)則支持通配符如en_*: en_US、pt: pt-BRwriteCookiebooleantrue是否將解析出的語言寫入 CookieresolverRequestResolver \| false見下請求語言解析配置設(shè)為false可關(guān)閉自動解析resolver.queryFieldstringlocale從 URL query 參數(shù)讀取語言的字段名resolver.cookieField.fieldNamestringlocaleCookie 中語言字段名resolver.cookieField.cookieDomainstringCookie 域名默認空即當前域名下有效resolver.cookieField.cookieMaxAgenumberFORMAT.MS.ONE_YEARCookie 有效期默認一年localsFieldstringi18n注入視圖locals/res.locals的輔助函數(shù)名missingKeyHandler(message, options?) stringmessage message文案缺失時的兜底處理函數(shù)默認原樣返回 key在應(yīng)用配置文件中覆蓋即可// config/config.default.ts export const i18n { defaultLocale: en_US, localeTable: { en_US: { default: { // hello: hello }, user: { HELLO_MESSAGE: Hello {username}, }, }, zh_CN: { default: { // hello: 你好 }, user: { HELLO_MESSAGE: 你好 {username}, }, }, }, fallbacks: { // en_*: en_US, // pt: pt-BR, }, writeCookie: true, resolver: { queryField: locale, headerField: locale, cookieField: { fieldName: locale, cookieDomain: , cookieMaxAge: FORMAT.MS.ONE_YEAR, }, }, };4.1 locale 規(guī)范化_與大小寫的統(tǒng)一組件在內(nèi)部通過formatLocaleutils.ts將所有 locale 統(tǒng)一為小寫 連字符形式export function formatLocale(locale: string) { if (!locale) return locale; // support zh_CN, en_US zh-cn, en-us return locale.replace(_, -).toLowerCase(); }因此在配置localeTable時寫zh_CN、en_US均可翻譯與匹配時組件會先規(guī)范化再查找測試中g(shù)etDefaultLocale()的返回結(jié)果即為en-us見 index.test.ts。4.2 localeTable 的分組結(jié)構(gòu)localeTable中的每個語言其 value 支持兩種層級平鋪字符串直接以 key 存放到默認分組default分組對象groupName: { key: text }翻譯時通過options.group指定分組。從addLocale的實現(xiàn)i18nService.ts可以看到這一規(guī)則字符串類型的 value 被放入default分組對象類型的 value 按分組名展開存儲。這也解釋了為什么默認配置中en_US: { default: { hello: hello } }與en_US: { hello: hello }在效果上是等價的——getES6Object會把僅含default鍵的對象拍平。public addLocale(locale: string, localeTextMapping: Recordstring, any) { locale formatLocale(locale); const currentLangMap getMap(this.localeTextMap, locale); for (const key in localeTextMapping) { if (typeof localeTextMapping[key] string) { getMap(currentLangMap, default).set(key, localeTextMapping[key]); } else { for (const newKey in localeTextMapping[key]) { getMap(currentLangMap, key).set(newKey, localeTextMapping[key][newKey]); } } } this.localeJSONMap.set(locale, localeTextMapping); }五、消息格式化對象占位符與數(shù)組占位符translate的args既支持對象也支持數(shù)組對應(yīng)兩種格式化實現(xiàn)utils.ts。5.1 對象占位符消息中使用{name}形式的命名占位符args傳對象this.i18nService.translate(HELLO_MESSAGE {username}, { args: { username, }, });底層formatWithObject使用正則/\{(.?)\}/g匹配從對象中取對應(yīng)屬性替換如果某占位符在對象中找不到值會保留原文占位符export function formatWithObject(text, values) { return text.replace(Object_INDEX_RE, (original, matched) { const value values[matched]; if (value) { return value; } return original; // 未匹配則保留原樣 }); }5.2 數(shù)組占位符消息中使用{0}、{1}形式的位置占位符args傳數(shù)組this.i18nService.translate(HELLO_MESSAGE {0} {1}, { args: [harry, chen], });底層formatWithArray使用正則/\{(\d)\}/g匹配按下標取值越界時保留原占位符。測試用例驗證了這一邊界行為index.test.tsexpect(formatWithArray(hello world, {0} {1}, [harry])).toEqual(hello world, harry {1}); expect(formatWithObject(hello world, {name1} {name2}, { name1: harry })) .toEqual(hello world, harry {name2});5.3 結(jié)合分組的完整示例以下配置同時演示了對象占位符、數(shù)組占位符與分組使用對應(yīng)測試 i18n service multi-leveli18n: { defaultLocale: en_US, localeTable: { en_US: { user: { HELLO_MESSAGE: Hello {username}, USER_ADDED_PRODUCT: {0} added {1} to cart, }, }, }, }// 對象占位符 group i18nService.translate(HELLO_MESSAGE, { args: { username: world }, group: user, }); // Hello world // 數(shù)組占位符 group i18nService.translate(USER_ADDED_PRODUCT, { group: user, args: [a, b], }); // a added b to cart六、Web 場景下的語言解析鏈路在 koa / egg / express / faas 中當前語言會按照query → cookie → Accept-Language的優(yōu)先級自動解析源碼見 middleware.ts。若resolver配置為false則整個自動解析過程被跳過。6.1 解析順序query 參數(shù)讀取ctx.query[resolver.queryField]默認字段名localeCookie讀取ctx.cookies.get(resolver.cookieField.fieldName)koa 路徑或req.cookies[...]express 路徑Accept-Language 請求頭通過ctx.acceptsLanguages()/req.acceptsLanguages()獲取語言列表逐個用formatLocale規(guī)范化后調(diào)用i18nService.hasAvailableLocale(lang)判斷是否為已注冊語言命中即采用當列表第一個為*時先剔除該通配項再遍歷。若以上都未命中則回退到默認語言。6.2 將解析結(jié)果寫入上下文與 Cookie解析完成后中間件調(diào)用saveRequestLocale(locale)將語言寫入上下文屬性i18n:locale供MidwayI18nService.translate讀取隨后在響應(yīng)階段koa 為next()之后express 通過注冊的I18nFilter根據(jù)writeCookie配置把語言寫入 Cookieconst cookieOptions { httpOnly: false, // 保證瀏覽器 JavaScript 可讀取便于前端與后端共享語言狀態(tài) maxAge: this.resolverConfig.cookieField.cookieMaxAge, signed: false, domain: this.resolverConfig.cookieField.cookieDomain, overwrite: true, }; ctx.cookies.set(resolver.cookieField.fieldName, saveLocale, cookieOptions);由此用戶首次通過?localezh_CN訪問后語言會被持久化到 Cookie默認有效期一年后續(xù)請求無需再帶參數(shù)也能保持中文環(huán)境。測試用例 should test with request by cookieindex.test.ts完整驗證了這一行為第一次請求后set-cookie中包含zh-cn第二次攜帶該 Cookie 請求依然返回中文文案。6.3 視圖模板輔助函數(shù)中間件還會向視圖注入翻譯輔助函數(shù)koa 為ctx.locals[i18nConfig.localsField]express 為res.locals[i18nConfig.localsField]默認字段名i18n在模板中即可直接調(diào)用// 模板中如 nunjucks / ejs {{ i18n(HELLO_MESSAGE, { username: world }) }}該輔助函數(shù)等價于i18nService.translate(message, { args: data })。七、fallback 兜底與缺失 Key 處理7.1 fallback 規(guī)則匹配fallbacks配置支持 glob 通配符組件在初始化時用picomatch將規(guī)則編譯為匹配器i18nService.tsfor (const rule in this.i18nConfig.fallbacks) { this.fallbackMatch.push({ pattern: pm(formatLocale(rule)), locale: formatLocale(this.i18nConfig.fallbacks[rule]), }); }translate的完整回退順序為目標 locale 的直接文案命中 fallback 規(guī)則后規(guī)則指向 locale 的文案默認語言defaultLocale的文案仍未找到時調(diào)用missingKeyHandler其默認實現(xiàn)為原樣返回 key。測試用例 should test fallbacksindex.test.ts驗證了通配符規(guī)則配置fallbacks: { en_*: zh_CN }時請求en_AU會翻譯成中文文案而未配置規(guī)則的fr_FR則回退到默認英文。7.2 缺失 Key 處理// 默認行為返回 key 本身 i18nService.translate(nonexistent_key); // nonexistent_key // 自定義行為可結(jié)合 locale 與 group 返回可讀提示 i18n: { missingKeyHandler: (message, options) { return [Missing: ${message}${options?.locale ? (${options.locale}) : }]; }, } i18nService.translate(nonexistent_key, { locale: zh_CN }); // [Missing: nonexistent_key (zh_CN)]從源碼看missingKeyHandler僅在“所有兜底路徑均未命中”時被調(diào)用i18nService.ts 第 112-115 行因此它不會干擾正常的 fallback 鏈路。八、語言可用性相關(guān) API除了translateMidwayI18nService還提供了一系列語言管理方法部分為 4.0.0 版本新增方法說明addLocale(locale, mapping)運行時動態(tài)注冊語言文案可用于按需加載語言包getLocaleMapping(locale, group default)獲取某語言指定分組的文案 MapgetLocaleList(group default)獲取包含指定分組的全部 locale 列表getOriginLocaleJSON(locale, group default)獲取某語言的原始配置 JSON未拍平前的結(jié)構(gòu)getAvailableLocale(locale, group)根據(jù)當前已注冊語言與 fallback 規(guī)則計算某 locale 實際可用的語言帶緩存hasAvailableLocale(locale)判斷某 locale 是否可用含 fallback 命中g(shù)etDefaultLocale()獲取規(guī)范化后的默認語言saveRequestLocale(locale?)將當前請求的語言寫入上下文屬性其中g(shù)etAvailableLocale的實現(xiàn)值得注意它會先檢查目標 locale 是否存在于localeTextMap且包含對應(yīng)分組再檢查 fallback 規(guī)則最后回退到默認語言并將結(jié)果緩存到localeMatchCache鍵為locale _ group避免重復計算。測試中連續(xù)兩次調(diào)用返回相同結(jié)果即驗證了緩存行為。九、動態(tài)注冊與按需加載語言addLocale允許在應(yīng)用運行期間動態(tài)補充文案適用于“語言包異步加載”場景。測試 i18n service 演示了其用法const i18nService await app.getApplicationContext().getAsync(MidwayI18nService); i18nService.addLocale(zh_TW, { hello: 你好美麗的世界, }); i18nService.translate(hello, { locale: zh_TW }); // 你好美麗的世界也可以在配置中直接require獨立語言文件將不同語言的文案拆分為模塊管理。測試夾具 base-app-i18n 展示了這種組織方式import { Configuration } from midwayjs/core; Configuration({ imports: [require(../../../../src)], importConfigs: [ { default: { i18n: { localeTable: { en_US: { base: require(./i18n/en) }, zh_CN: { base: require(./i18n/zh-cn) }, }, }, }, }, ], }) export class AutoConfiguration {}其中en.ts/zh-cn.ts各導出一個文案對象掛載到base分組下翻譯時通過group: base訪問對應(yīng)測試 should test create app。十、支持的語言與 Locale 對照表組件對語言標識采用語言_地區(qū)的命名規(guī)范內(nèi)部統(tǒng)一轉(zhuǎn)換為語言-地區(qū)小寫形式。以下是常見語言與 locale 文本對照語言locale 文本語言locale 文本阿拉伯語ar_EG亞美尼亞語hy_AM保加利亞語bg_BG加泰羅尼亞語ca_ES捷克語cs_CZ丹麥語da_DK德語de_DE希臘語el_GR英語英式en_GB英語美式en_US西班牙語es_ES愛沙尼亞語et_EE波斯語fa_IR芬蘭語fi_FI法語比利時fr_BE法語fr_FR希伯來語he_IL印地語hi_IN克羅地亞語hr_HR匈牙利語hu_HU冰島語is_IS印度尼西亞語id_ID意大利語it_IT日語ja_JP格魯吉亞語ka_GE卡納達語kn_IN韓語/朝鮮語ko_KR庫爾德語ku_IQ拉脫維亞語lv_LV馬來語ms_MY蒙古語mn_MN挪威語nb_NO尼泊爾語ne_NP荷蘭語比利時nl_BE荷蘭語nl_NL波蘭語pl_PL葡萄牙語巴西pt_BR葡萄牙語pt_PT斯洛伐克語sk_SK塞爾維亞語sr_RS斯洛文尼亞語sl_SI瑞典語sv_SE泰米爾語ta_IN泰語th_TH土耳其語tr_TR羅馬尼亞語ro_RO俄語ru_RU烏克蘭語uk_UA越南語vi_VN簡體中文zh_CN繁體中文zh_TW這些 locale 名稱既可以作為localeTable的鍵、translate的locale參數(shù)也可以作為 URL query、Cookie 中傳遞的語言值如?localezh_CN。十一、快速驗證測試用例中的端到端行為組件在 test/index.test.ts 中提供了覆蓋上述全部行為的測試可作為使用參考。幾個有代表性的場景// 1. query 參數(shù)指定語言 const result await createHttpRequest(app).get(/).query({ locale: zh_CN, username: 世界, }); expect(result.text).toEqual(你好 世界); // 2. Accept-Language 請求頭指定語言 const result await createHttpRequest(app).get(/) .set(Accept-Language, zh-CN,zh;q0.5) .query({ username: 世界 }); expect(result.text).toEqual(你好 世界); // 3. Cookie 保持語言 const result await createHttpRequest(app).get(/) .set(Accept-Language, zh-CN,zh;q0.5) .query({ username: 世界 }); expect(result.headers[set-cookie][0]).toMatch(/zh-cn/); // 后續(xù)請求攜帶 Cookie 仍返回中文 const result1 await createHttpRequest(app).get(/) .set({ cookie: result.headers[set-cookie][0] }) .query({ username: 世界 }); expect(result1.text).toEqual(你好 世界);測試夾具還針對不同 Web 框架與解析方式提供了獨立示例目錄test/fixtures例如base-app-koa-query-locale、base-app-express-cookie-locale、base-app-koa-ctx-locals等分別對應(yīng) query、cookie、header、視圖 locals、關(guān)閉 resolver 等不同配置形態(tài)可直接對照閱讀其 configuration.ts 與控制器實現(xiàn)。十二、小結(jié)midwayjs/i18n通過“單例服務(wù) 請求上下文服務(wù)”的雙層設(shè)計將翻譯邏輯與請求語言解析解耦MidwayI18nServiceSingleton負責文案存儲、fallback 匹配與格式化MidwayI18nService負責注入請求上下文并讀取中間件解析好的語言。接入時只需三件事——安裝組件、加入imports、在config.default.ts中維護localeTable語言解析、Cookie 持久化、視圖輔助函數(shù)與兜底策略均由組件自動完成。對于需要動態(tài)加載語言包或深度定制缺失文案的場景addLocale與missingKeyHandler提供了靈活的擴展入口。贊分享后端微服務(wù)云原生【免費下載鏈接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 項目地址https://gitcode.com/gh_mirrors/mi/midway點擊查看免費下載相關(guān)推薦Yii 2 國際化I18N實戰(zhàn)指南Locale 配置、消息翻譯與多語言格式化Yii 2 國際化I18N實戰(zhàn)指南Locale 配置、消息翻譯與多語言格式化 本文以 Yii 2 框架的國際化Internationalisation,后端Web框架Yii 2 國際化I18N完全指南消息翻譯、ICU 消息格式化與多語言應(yīng)用實戰(zhàn)Yii 2 國際化I18N完全指南消息翻譯、ICU 消息格式化與多語言應(yīng)用實戰(zhàn) 國際化Internationalization簡稱 I18N是指在不后端Web框架VitePress 多語言i18n國際化配置完全指南VitePress 多語言i18n國際化配置完全指南 本篇指南圍繞 VitePress 內(nèi)置的 i18n國際化能力展開講解如何通過目錄結(jié)構(gòu)與 loca前端文檔上一篇鳴潮游戲自動化工具ok-ww實踐指南下一篇從3B到70B模型怎么選OpenBuddy全系列模型尺寸對比與應(yīng)用場景推薦創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考