Designing a Scalable Multilingual Component for Modern Frontend Apps
This article explains how NetEase Qiyu built a high‑cohesion multilingual component using React context and hooks, transformed tree‑structured language data for component libraries, and created a VSCode plugin to automate i18n key replacement, enabling efficient multi‑language support for their customer‑service console.
Introduction
With increasing overseas opportunities, many companies are expanding globally, which raises new requirements for internationalization support in services and capabilities.
Industry Market Background
Policy, pandemic, international situation, and exchange rates have created many outbound opportunities. In 2021, China’s cross‑border e‑commerce import‑export scale reached 1.98 trillion CNY (exports 1.44 trillion CNY, up 24.5%). Chinese games earned $18.013 billion overseas, a 16.59% YoY increase, accounting for 23.4% of the global market and 40.2% of revenue share.
Customer Development Needs
As Chinese enterprises expand overseas, fine‑grained operation management concepts permeate, requiring globally distributed multilingual customer‑service teams to deliver localized experiences and unified management of different language support.
Business Background
NetEase Qiyu’s customer‑service console originally used a temporary internationalization solution supporting Chinese, English, and Traditional Chinese. With accelerated overseas expansion, the solution showed three major drawbacks: inability to meet growing multilingual demands, scattered and hard‑to‑maintain translation fields, and high development cost for each additional language. A redesign targeting scalability, maintainability, and universality was therefore needed.
Problems to Solve
Multilingual support in the component library.
Handling internationalization within business components.
Interaction between business logic and the multilingual platform.
Automating replacement issues caused by multiple languages.
Existing Solutions in the Industry
vue‑i18n – comprehensive and mature for Vue stacks.
react‑intl
react‑i18next + i18next
react‑intl‑universal
These solutions do not satisfy our needs for dynamic language‑pack delivery, basic component adaptation, and strong extensibility, prompting the development of a new multilingual component.
Implementation of the Multilingual Component
The component leverages React context and provides a useI18n hook for business usage.
const useI18n = (props?: { i18n: Record<any, any> }): I18nData => {</code><code> const { i18n: i18nFromProps } = props || {};</code><code> const { lang, langJson } = React.useContext(I18nContext) || {};</code><code> const i18n = i18nFromProps || langJson;</code><code> const getValue = () => (key: string, defaultValue?: string, ...args) => {</code><code> let res = defaultValue || key;</code><code> if (typeof i18n[key] !== 'undefined') { res = i18n[key]; }</code><code> if (args && args.length) { return replaceSlot(res, ...args); }</code><code> return res;</code><code> };</code><code> if (!i18n || !Object.keys(i18n).length) {</code><code> return { t: getValue(), langJson: {}, ready: false, lang };</code><code> }</code><code> return { lang, langJson: i18n, t: getValue() };</code><code>};The hook can accept an optional language configuration; if provided, it takes precedence, otherwise it falls back to the context or default Chinese.
Because the multilingual platform only supports flat key structures, tree‑structured component libraries (e.g., ppfish) require key transformation.
export function formatPpfish(langJson) {</code><code> const res = {};</code><code> const splitText = '&';</code><code> if (!langJson || !Object.keys(langJson).length) { return res; }</code><code> for (const key of Object.keys(langJson)) {</code><code> const splitKey = key.replace(/^ppfish&/i, '');</code><code> if (!splitKey.includes(splitText)) { res[splitKey] = langJson[key]; continue; }</code><code> const keyArr = splitKey.split(splitText);</code><code> setAttr(res, keyArr, langJson[key]);</code><code> }</code><code> return res;</code><code>}; export function setAttr(obj, arr, value) {</code><code> if (!Array.isArray(arr) || !arr.length) { return obj; }</code><code> let data = obj;</code><code> for (let i = 0, len = arr.length; i < len; i++) {</code><code> if (i === len - 1) { data[arr[i]] = value; }</code><code> else { const defaultData = arr[i] === 'MONTH_ARRAY' ? [] : {}; data = data[arr[i]] = data[arr[i]] || defaultData; }</code><code> }</code><code>};To flatten a tree‑structured language object for the platform, the following utility is used:
export function expandTreeKey(tree, tag = '&') {</code><code> const res = {};</code><code> if (!tree || !Object.keys(tree).length) { return res; }</code><code> const expandFun = (obj, hisKey) => {</code><code> for (const key of Object.keys(obj)) {</code><code> if (typeof obj[key] === 'object') { expandFun(obj[key], hisKey ? `${hisKey}${tag}${key}` : key); }</code><code> else { res[hisKey ? `${hisKey}${tag}${key}` : key] = obj[key]; }</code><code> }</code><code> };</code><code> expandFun(tree, '');</code><code> const ppfishRes = {};</code><code> for (const key of Object.keys(res)) { if (key) { ppfishRes[`${PREFIX}${key}`] = res[key]; } }</code><code> return ppfishRes;</code><code>};Practical Usage in the Product
Initialize language configuration at the page entry and provide it via I18nProvider.
render(</code><code> <I18nProvider defaultLangJson={defaultLangJson} i18nKey={['web-sesssion']} onload={setLocalJson}> </code><code> <ErrorBoundary> </code><code> <SessionHistory /> </code><code> </ErrorBoundary> </code><code> </I18nProvider>,</code><code> document.getElementById('react-content')</code><code>);Use the useI18n hook in functional components.
import { useI18n } from '@ysf/i18n';</code><code>const Comp = () => {</code><code> const { langJson, t } = useI18n();</code><code> return <div><p>{t('TEST_KEY', '测试的')}</p></div>;</code><code>};Use the withI18n HOC for class components.
import React from 'react';</code><code>import { withI18n } from '@ysf/i18n';</code><code>class MyComponent extends React.Component {</code><code> render() { return <p>{t('TEST_KEY', '测试的')}</p>; }</code><code>}</code><code>export default withI18n()(MyComponent);Utility functions can receive the language JSON directly.
const timestamp2fixedDate = (langJson?: langOption): string => {</code><code> return `${langJson.TODAY || '今天'}`;</code><code>};VSCode Plugin (ysf‑vsc‑plugin)
The plugin automates the tedious process of converting Chinese strings to i18n keys.
Add a right‑click menu command for quick key‑value replacement.
Generate keys automatically from Chinese content with configurable rules.
Support both JSX and plain JavaScript usage.
Provide batch replacement without selection.
"commands": [</code><code> { "command": "ysf-language-replace.replace", "title": "多语言KeyValue快捷替换" }</code><code>],</code><code>"menus": { "editor/context": [ { "command": "ysf-language-replace.replace", "group": "ysf" } ] } const langReplaceDisposable = vscode.commands.registerCommand(</code><code> "ysf-language-replace.replace", async () => {</code><code> const editor = vscode.window.activeTextEditor;</code><code> if (!editor) { return false; }</code><code> const { selection } = editor;</code><code> let obj: any = {};</code><code> try {</code><code> obj = JSON.parse(text);</code><code> const result = `t('${obj.key}', '${obj.value}')`;</code><code> editor && editor.edit(builder => { builder.replace(selection, result); });</code><code> } catch (error) { vscode.window.showWarningMessage('字符串匹配异常'); }</code><code> }</code><code>);</code><code>context.subscriptions.push(langReplaceDisposable);Configuration is done via ysf.config.json, specifying language file paths and the languageReplaceRelativePath array.
{</code><code> "languageReplaceRelativePath": ["test/i18n/zh-CN.js", "test/i18n/zh-TW.js"]</code><code>}Conclusion
By integrating the high‑cohesion multilingual component with the language platform and the VSCode automation plugin, the Qiyu customer‑service console now supports multiple languages efficiently, and development productivity has been significantly improved.
References
vue‑i18n: https://github.com/kazupon/vue-i18n react‑intl: https://github.com/yahoo/react-intl react‑i18next: https://github.com/yahoo/react-intl i18next: https://react.i18next.com/ react‑intl‑universal: https://github.com/alibaba/react-intl-universal React Context: https://reactjs.org/docs/context.html ppfish components: https://github.com/NSFI/ppfish-components
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
NetEase Smart Enterprise Tech+
Get cutting-edge insights from NetEase's CTO, access the most valuable tech knowledge, and learn NetEase's latest best practices. NetEase Smart Enterprise Tech+ helps you grow from a thinker into a tech expert.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
