{"id":185,"date":"2026-06-25T10:14:36","date_gmt":"2026-06-25T02:14:36","guid":{"rendered":"https:\/\/yykcj.com\/?p=185"},"modified":"2026-06-25T10:14:36","modified_gmt":"2026-06-25T02:14:36","slug":"%e5%89%8d%e7%ab%af%e5%9b%bd%e9%99%85%e5%8c%96-i18n-%e6%9c%80%e4%bd%b3%e5%ae%9e%e8%b7%b5%ef%bc%9a%e4%bb%8e%e9%9b%b6%e6%9e%84%e5%bb%ba%e5%8f%af%e6%89%a9%e5%b1%95%e7%9a%84%e5%a4%9a%e8%af%ad%e8%a8%80","status":"publish","type":"post","link":"https:\/\/yykcj.com\/?p=185","title":{"rendered":"\u524d\u7aef\u56fd\u9645\u5316 (i18n) \u6700\u4f73\u5b9e\u8df5\uff1a\u4ece\u96f6\u6784\u5efa\u53ef\u6269\u5c55\u7684\u591a\u8bed\u8a00\u5e94\u7528"},"content":{"rendered":"<h2>\u524d\u8a00<\/h2>\n<p>\u968f\u7740\u4e1a\u52a1\u5168\u7403\u5316\uff0c\u524d\u7aef\u5e94\u7528\u9700\u8981\u652f\u6301\u591a\u8bed\u8a00\u3002\u56fd\u9645\u5316\uff08i18n\uff09\u770b\u4f3c\u7b80\u5355\uff0c\u4f46\u82e5\u7f3a\u4e4f\u5408\u7406\u8bbe\u8ba1\uff0c\u540e\u7eed\u7ef4\u62a4\u4f1a\u53d8\u5f97\u75db\u82e6\uff1a\u7ffb\u8bd1\u6587\u4ef6\u81a8\u80c0\u3001\u952e\u540d\u6df7\u4e71\u3001\u52a8\u6001\u5185\u5bb9\u96be\u4ee5\u5904\u7406\u3001\u6027\u80fd\u4e0b\u964d\u7b49\u3002\u672c\u6587\u5c06\u5206\u4eab\u4e00\u5957\u7ecf\u8fc7\u5927\u578b\u9879\u76ee\u9a8c\u8bc1\u7684 i18n \u6700\u4f73\u5b9e\u8df5\uff0c\u6db5\u76d6\u65b9\u6848\u9009\u578b\u3001\u9879\u76ee\u7ed3\u6784\u3001\u52a8\u6001\u52a0\u8f7d\u3001\u7f16\u8bd1\u65f6\u63d0\u53d6\u4ee5\u53ca\u5e38\u89c1\u9677\u9631\uff0c\u5e76\u63d0\u4f9b\u5b8c\u6574\u53ef\u8fd0\u884c\u7684 React + i18next \u793a\u4f8b\u3002<\/p>\n<h2>1. \u65b9\u6848\u9009\u578b<\/h2>\n<p>\u524d\u7aef i18n \u5e93\u4f17\u591a\uff0c\u5982 i18next\u3001react-intl\u3001vue-i18n \u7b49\u3002\u63a8\u8350 <strong>i18next<\/strong>\uff0c\u539f\u56e0\u5982\u4e0b\uff1a<\/p>\n<ul>\n<li>\u6846\u67b6\u65e0\u5173\uff0c\u53ef\u914d\u5408 React\/Vue\/Angular\/\u539f\u751f JS<\/li>\n<li>\u652f\u6301\u5d4c\u5957\u952e\u3001\u590d\u6570\u3001\u4e0a\u4e0b\u6587\u3001\u63d2\u503c<\/li>\n<li>\u652f\u6301\u5ef6\u8fdf\u52a0\u8f7d\uff08\u6309\u9700\u52a0\u8f7d\u7ffb\u8bd1\u6587\u4ef6\uff09<\/li>\n<li>\u63d2\u4ef6\u751f\u6001\u4e30\u5bcc\uff08\u5982\u81ea\u52a8\u68c0\u6d4b\u8bed\u8a00\u3001ICU \u6d88\u606f\u683c\u5f0f\uff09<\/li>\n<\/ul>\n<h2>2. \u9879\u76ee\u7ed3\u6784\u6700\u4f73\u5b9e\u8df5<\/h2>\n<p>\u5c06\u7ffb\u8bd1\u6587\u4ef6\u6309\u529f\u80fd\u6a21\u5757\u62c6\u5206\uff0c\u907f\u514d\u5355\u4e00\u5de8\u5927\u6587\u4ef6\u3002\u63a8\u8350\u7ed3\u6784\uff1a<\/p>\n<pre><code>\nsrc\/\n  locales\/\n    en\/\n      common.json      # \u901a\u7528\u6587\u6848\n      user.json         # \u7528\u6237\u6a21\u5757\n      payment.json      # \u652f\u4ed8\u6a21\u5757\n    zh-CN\/\n      common.json\n      user.json\n      payment.json\n    i18n.ts             # i18n \u521d\u59cb\u5316\u914d\u7f6e\n<\/code><\/pre>\n<p>\u6bcf\u4e2a\u6a21\u5757\u7684\u7ffb\u8bd1\u6587\u4ef6\u4fdd\u6301\u6241\u5e73\u952e\uff08\u907f\u514d\u5d4c\u5957\u8fc7\u6df1\uff09\uff0c\u4e14\u952e\u540d\u4f7f\u7528\u70b9\u53f7\u5206\u9694\u7684\u547d\u540d\u7a7a\u95f4\uff0c\u5982 <code>common.save<\/code>\u3002<\/p>\n<h2>3. \u521d\u59cb\u5316\u914d\u7f6e<\/h2>\n<p>\u4ee5 TypeScript + React \u4e3a\u4f8b\uff0c\u521b\u5efa <code>i18n.ts<\/code>\uff1a<\/p>\n<pre><code class=\"language-typescript\">\nimport i18n from &#x27;i18next&#x27;;\nimport { initReactI18next } from &#x27;react-i18next&#x27;;\nimport Backend from &#x27;i18next-http-backend&#x27;;\nimport LanguageDetector from &#x27;i18next-browser-languagedetector&#x27;;\n\ni18n\n  .use(Backend) \/\/ \u5ef6\u8fdf\u52a0\u8f7d\u7ffb\u8bd1\u6587\u4ef6\n  .use(LanguageDetector) \/\/ \u81ea\u52a8\u68c0\u6d4b\u7528\u6237\u8bed\u8a00\n  .use(initReactI18next)\n  .init({\n    fallbackLng: &#x27;en&#x27;,\n    debug: process.env.NODE_ENV === &#x27;development&#x27;,\n    interpolation: {\n      escapeValue: false, \/\/ React \u5df2\u81ea\u52a8\u8f6c\u4e49\n    },\n    backend: {\n      loadPath: &#x27;\/locales\/{{lng}}\/{{ns}}.json&#x27;, \/\/ \u7ffb\u8bd1\u6587\u4ef6\u8def\u5f84\n    },\n    ns: [&#x27;common&#x27;, &#x27;user&#x27;, &#x27;payment&#x27;], \/\/ \u9ed8\u8ba4\u547d\u540d\u7a7a\u95f4\n    defaultNS: &#x27;common&#x27;,\n    react: {\n      useSuspense: false, \/\/ \u907f\u514d Suspense \u5f00\u9500\n    },\n  });\n\nexport default i18n;\n<\/code><\/pre>\n<p><strong>\u6ce8\u610f<\/strong>\uff1a<code>loadPath<\/code> \u4e2d\u7684 <code>{{lng}}<\/code> \u548c <code>{{ns}}<\/code> \u4f1a\u88ab\u52a8\u6001\u66ff\u6362\u4e3a\u8bed\u8a00\u548c\u547d\u540d\u7a7a\u95f4\u3002\u786e\u4fdd\u7ffb\u8bd1\u6587\u4ef6\u653e\u5728 <code>public\/locales<\/code> \u4e0b\uff0c\u6216\u901a\u8fc7 webpack \u63d2\u4ef6\u590d\u5236\u3002<\/p>\n<h2>4. \u5728\u7ec4\u4ef6\u4e2d\u4f7f\u7528<\/h2>\n<p>\u4f7f\u7528 <code>useTranslation<\/code> hook\uff1a<\/p>\n<pre><code class=\"language-tsx\">\nimport { useTranslation } from &#x27;react-i18next&#x27;;\n\nfunction UserGreeting({ name }: { name: string }) {\n  const { t } = useTranslation(&#x27;user&#x27;); \/\/ \u6307\u5b9a\u547d\u540d\u7a7a\u95f4\n\n  return &lt;h1&gt;{t(&#x27;greeting&#x27;, { name })}&lt;\/h1&gt;;\n}\n<\/code><\/pre>\n<p>\u7ffb\u8bd1\u6587\u4ef6 <code>user.json<\/code>\uff1a<\/p>\n<pre><code class=\"language-json\">\n{\n  &quot;greeting&quot;: &quot;Hello, {{name}}!&quot;\n}\n<\/code><\/pre>\n<p><strong>\u6700\u4f73\u5b9e\u8df5<\/strong>\uff1a\u907f\u514d\u5728\u7ec4\u4ef6\u4e2d\u76f4\u63a5\u4f7f\u7528 <code>t('key')<\/code> \u5b57\u7b26\u4e32\uff0c\u6539\u7528\u5e38\u91cf\u6216\u51fd\u6570\uff0c\u4fbf\u4e8e\u91cd\u6784\u548c\u7c7b\u578b\u5b89\u5168\u3002<\/p>\n<h2>5. \u7f16\u8bd1\u65f6\u63d0\u53d6\u7ffb\u8bd1\u952e\uff08TypeScript \u7c7b\u578b\u5b89\u5168\uff09<\/h2>\n<p>\u4e3a\u4e86\u5728\u7f16\u8bd1\u65f6\u68c0\u67e5\u952e\u662f\u5426\u5b58\u5728\uff0c\u53ef\u4ee5\u751f\u6210\u7c7b\u578b\u5b9a\u4e49\u3002\u4f7f\u7528 <code>i18next-resources-for-ts<\/code> \u6216\u624b\u52a8\u5b9a\u4e49\uff1a<\/p>\n<pre><code class=\"language-typescript\">\n\/\/ resources.d.ts\nimport common from &#x27;.\/locales\/en\/common.json&#x27;;\nimport user from &#x27;.\/locales\/en\/user.json&#x27;;\n\nconst resources = {\n  common,\n  user,\n} as const;\n\nexport type TranslationKeys = {\n  [K in keyof typeof resources]: keyof typeof resources[K];\n};\n<\/code><\/pre>\n<p>\u7136\u540e\u5c01\u88c5\u7c7b\u578b\u5b89\u5168\u7684 <code>t<\/code> \u51fd\u6570\uff1a<\/p>\n<pre><code class=\"language-typescript\">\nimport { TFunction } from &#x27;i18next&#x27;;\nimport { TranslationKeys } from &#x27;.\/resources&#x27;;\n\nexport function safeT(\n  t: TFunction,\n  key: TranslationKeys[&#x27;common&#x27;] | TranslationKeys[&#x27;user&#x27;],\n  options?: Record&lt;string, unknown&gt;\n) {\n  return t(key, options);\n}\n<\/code><\/pre>\n<p><strong>\u6ce8\u610f<\/strong>\uff1a\u6b64\u65b9\u6cd5\u9700\u8981\u624b\u52a8\u7ef4\u62a4\u7c7b\u578b\uff0c\u4f46\u80fd\u6709\u6548\u9632\u6b62\u952e\u540d\u62fc\u5199\u9519\u8bef\u3002<\/p>\n<h2>6. \u52a8\u6001\u52a0\u8f7d\u4e0e\u61d2\u52a0\u8f7d<\/h2>\n<p>\u5bf9\u4e8e\u5927\u578b\u5e94\u7528\uff0c\u4e0d\u5e94\u4e00\u6b21\u6027\u52a0\u8f7d\u6240\u6709\u7ffb\u8bd1\u6587\u4ef6\u3002i18next \u7684 <code>Backend<\/code> \u63d2\u4ef6\u652f\u6301\u6309\u9700\u52a0\u8f7d\uff1a<\/p>\n<pre><code class=\"language-typescript\">\n\/\/ \u5728\u8def\u7531\u5207\u6362\u65f6\u52a8\u6001\u52a0\u8f7d\u547d\u540d\u7a7a\u95f4\nimport i18n from &#x27;.\/i18n&#x27;;\n\nasync function loadPaymentTranslations() {\n  await i18n.loadNamespaces(&#x27;payment&#x27;);\n}\n<\/code><\/pre>\n<p>\u4e5f\u53ef\u4ee5\u7ed3\u5408 React.lazy \u548c Suspense\uff1a<\/p>\n<pre><code class=\"language-tsx\">\nconst PaymentPage = React.lazy(() =&gt; import(&#x27;.\/PaymentPage&#x27;));\n\nfunction App() {\n  return (\n    &lt;Suspense fallback={&lt;Loading \/&gt;}&gt;\n      &lt;PaymentPage \/&gt;\n    &lt;\/Suspense&gt;\n  );\n}\n<\/code><\/pre>\n<p>\u4f46\u9700\u6ce8\u610f\uff0ci18next \u7684 <code>useSuspense<\/code> \u9ed8\u8ba4\u542f\u7528\uff0c\u53ef\u80fd\u5bfc\u81f4\u975e\u9884\u671f\u7684\u52a0\u8f7d\u884c\u4e3a\u3002\u5efa\u8bae\u8bbe\u7f6e\u4e3a <code>false<\/code>\uff0c\u624b\u52a8\u5904\u7406\u52a0\u8f7d\u72b6\u6001\u3002<\/p>\n<h2>7. \u5904\u7406\u52a8\u6001\u5185\u5bb9\u4e0e\u590d\u6570<\/h2>\n<p>\u7ffb\u8bd1\u4e2d\u5e38\u5305\u542b\u53d8\u91cf\u3001\u590d\u6570\u3001\u4e0a\u4e0b\u6587\u3002i18next \u539f\u751f\u652f\u6301\uff1a<\/p>\n<pre><code class=\"language-json\">\n{\n  &quot;item&quot;: &quot;{{count}} item&quot;,\n  &quot;item_plural&quot;: &quot;{{count}} items&quot;\n}\n<\/code><\/pre>\n<pre><code class=\"language-tsx\">\nt(&#x27;item&#x27;, { count: 1 }); \/\/ &quot;1 item&quot;\nt(&#x27;item&#x27;, { count: 5 }); \/\/ &quot;5 items&quot;\n<\/code><\/pre>\n<p>\u5bf9\u4e8e\u590d\u6742\u590d\u6570\u89c4\u5219\uff08\u5982\u963f\u62c9\u4f2f\u8bed\uff09\uff0c\u53ef\u4f7f\u7528 <code>i18next-plurals<\/code> \u63d2\u4ef6\u3002<\/p>\n<p><strong>\u6700\u4f73\u5b9e\u8df5<\/strong>\uff1a\u5c06\u53d8\u91cf\u540d\u7528\u5927\u62ec\u53f7\u5305\u88f9\uff0c\u907f\u514d\u4e0e HTML \u6807\u7b7e\u51b2\u7a81\u3002<\/p>\n<h2>8. \u5e38\u89c1\u9677\u9631\u4e0e\u89e3\u51b3\u65b9\u6848<\/h2>\n<h3>\u9677\u96311\uff1a\u7ffb\u8bd1\u952e\u540d\u51b2\u7a81<\/h3>\n<p>\u4f7f\u7528\u547d\u540d\u7a7a\u95f4\u907f\u514d\u51b2\u7a81\uff0c\u952e\u540d\u52a0\u524d\u7f00\uff0c\u5982 <code>common.save<\/code> \u548c <code>user.save<\/code>\u3002<\/p>\n<h3>\u9677\u96312\uff1aHTML \u6807\u7b7e\u7ffb\u8bd1<\/h3>\n<p>\u4e0d\u8981\u5c06\u6807\u7b7e\u5d4c\u5165\u7ffb\u8bd1\u5b57\u7b26\u4e32\uff0c\u4f7f\u7528\u7ec4\u4ef6\u7ec4\u5408\uff1a<\/p>\n<pre><code class=\"language-tsx\">\n\/\/ \u9519\u8bef\uff1a&lt;p&gt;{t(&#x27;welcome&#x27;, { link: &#x27;&lt;a&gt;here&lt;\/a&gt;&#x27; })}&lt;\/p&gt;\n\/\/ \u6b63\u786e\uff1a\nconst Trans = require(&#x27;react-i18next&#x27;).Trans;\n&lt;Trans i18nKey=&quot;welcome&quot;&gt;\n  Welcome, &lt;a href=&quot;\/profile&quot;&gt;here&lt;\/a&gt;.\n&lt;\/Trans&gt;\n<\/code><\/pre>\n<h3>\u9677\u96313\uff1a\u6027\u80fd\u95ee\u9898<\/h3>\n<p>\u907f\u514d\u5728\u6e32\u67d3\u5faa\u73af\u4e2d\u8c03\u7528 <code>t<\/code> \u51fd\u6570\uff0c\u4f7f\u7528 <code>useTranslation<\/code> \u7684 <code>t<\/code> \u662f\u7a33\u5b9a\u7684\uff0c\u4f46\u4ecd\u9700\u6ce8\u610f\uff1a<\/p>\n<ul>\n<li>\u4f7f\u7528 <code>React.memo<\/code> \u5305\u88f9\u7eaf\u5c55\u793a\u7ec4\u4ef6<\/li>\n<li>\u5c06\u7ffb\u8bd1\u7ed3\u679c\u7f13\u5b58\uff08\u5982 <code>useMemo<\/code>\uff09<\/li>\n<\/ul>\n<h3>\u9677\u96314\uff1a\u8bed\u8a00\u68c0\u6d4b\u4e0d\u51c6\u786e<\/h3>\n<p><code>LanguageDetector<\/code> \u53ef\u80fd\u8bef\u5224\uff0c\u5efa\u8bae\u63d0\u4f9b\u8bed\u8a00\u5207\u6362 UI \u5e76\u4fdd\u5b58\u7528\u6237\u9009\u62e9\u5230 localStorage\u3002<\/p>\n<h2>9. \u603b\u7ed3<\/h2>\n<p>\u672c\u6587\u4ece\u524d\u7aef\u56fd\u9645\u5316\u7684\u65b9\u6848\u9009\u578b\u3001\u9879\u76ee\u7ed3\u6784\u3001\u521d\u59cb\u5316\u914d\u7f6e\u3001\u7c7b\u578b\u5b89\u5168\u3001\u52a8\u6001\u52a0\u8f7d\u5230\u5e38\u89c1\u9677\u9631\uff0c\u63d0\u4f9b\u4e86\u4e00\u5957\u5b8c\u6574\u7684\u6700\u4f73\u5b9e\u8df5\u3002\u6838\u5fc3\u8981\u70b9\uff1a<\/p>\n<ul>\n<li>\u4f7f\u7528 i18next \u914d\u5408\u6309\u9700\u52a0\u8f7d<\/li>\n<li>\u6309\u529f\u80fd\u6a21\u5757\u62c6\u5206\u7ffb\u8bd1\u6587\u4ef6<\/li>\n<li>\u5229\u7528 TypeScript \u5b9e\u73b0\u7c7b\u578b\u5b89\u5168\u7684\u7ffb\u8bd1\u952e<\/li>\n<li>\u907f\u514d\u5c06 HTML \u5d4c\u5165\u7ffb\u8bd1\u5b57\u7b26\u4e32<\/li>\n<li>\u5408\u7406\u5904\u7406\u590d\u6570\u3001\u53d8\u91cf\u4e0e\u4e0a\u4e0b\u6587<\/li>\n<\/ul>\n<p><strong>\u5ef6\u4f38\u9605\u8bfb<\/strong>\uff1a<\/p>\n<ul>\n<li>i18next \u5b98\u65b9\u6587\u6863\uff1ahttps:\/\/www.i18next.com\/<\/li>\n<li>react-i18next \u6307\u5357\uff1ahttps:\/\/react.i18next.com\/<\/li>\n<li>ICU \u6d88\u606f\u683c\u5f0f\uff1ahttps:\/\/formatjs.io\/docs\/core-concepts\/icu-syntax\/<\/li>\n<\/ul>\n<p>\u5e0c\u671b\u672c\u6587\u80fd\u5e2e\u52a9\u4f60\u6784\u5efa\u4e00\u4e2a\u5065\u58ee\u3001\u53ef\u7ef4\u62a4\u7684\u591a\u8bed\u8a00\u524d\u7aef\u5e94\u7528\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u6df1\u5165\u63a2\u8ba8\u524d\u7aef\u56fd\u9645\u5316\uff08i18n\uff09\u7684\u6700\u4f73\u5b9e\u8df5\uff0c\u6db5\u76d6\u65b9\u6848\u9009\u578b\u3001\u9879\u76ee\u7ed3\u6784\u3001\u52a8\u6001\u52a0\u8f7d\u4e0e\u7f16\u8bd1\u65f6\u63d0\u53d6\u7b49\u5173\u952e\u73af\u8282\uff0c\u5e76\u63d0\u4f9b\u5b8c\u6574\u53ef\u8fd0\u884c\u7684\u4ee3\u7801\u793a\u4f8b\uff0c\u52a9\u4f60\u6784\u5efa\u9ad8\u6548\u3001\u53ef\u7ef4\u62a4\u7684\u591a\u8bed\u8a00\u524d\u7aef\u5e94\u7528\u3002<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,4],"tags":[77,79,11,40,78,45],"class_list":["post-185","post","type-post","status-publish","format-standard","hentry","category-3","category-4","tag-i18n","tag-i18next","tag-react","tag-typescript","tag-78","tag-45"],"_links":{"self":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts\/185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=185"}],"version-history":[{"count":0,"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts\/185\/revisions"}],"wp:attachment":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}