@intlayer/docs 9.1.3 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/index.md +1 -1
- package/blog/ar/rag_powered_documentation_assistant.md +7 -0
- package/blog/de/index.md +1 -1
- package/blog/de/rag_powered_documentation_assistant.md +6 -0
- package/blog/en/index.md +1 -1
- package/blog/en-GB/index.md +1 -1
- package/blog/en-GB/rag_powered_documentation_assistant.md +6 -0
- package/blog/es/index.md +1 -1
- package/blog/es/rag_powered_documentation_assistant.md +7 -0
- package/blog/fr/i18n_meaning.md +7 -1
- package/blog/fr/index.md +1 -1
- package/blog/fr/list_i18n_technologies/frameworks/angular.md +5 -1
- package/blog/fr/rag_powered_documentation_assistant.md +6 -0
- package/blog/hi/index.md +1 -1
- package/blog/hi/next-i18next_vs_next-intl_vs_intlayer.md +119 -42
- package/blog/hi/rag_powered_documentation_assistant.md +7 -0
- package/blog/id/index.md +1 -1
- package/blog/id/rag_powered_documentation_assistant.md +6 -0
- package/blog/it/index.md +1 -1
- package/blog/it/rag_powered_documentation_assistant.md +6 -0
- package/blog/ja/index.md +1 -1
- package/blog/ja/rag_powered_documentation_assistant.md +7 -0
- package/blog/ko/index.md +1 -1
- package/blog/ko/list_i18n_technologies/frameworks/react.md +7 -7
- package/blog/ko/rag_powered_documentation_assistant.md +6 -0
- package/blog/pl/index.md +1 -1
- package/blog/pl/rag_powered_documentation_assistant.md +6 -0
- package/blog/pt/index.md +1 -1
- package/blog/pt/rag_powered_documentation_assistant.md +6 -0
- package/blog/ru/index.md +1 -1
- package/blog/ru/rag_powered_documentation_assistant.md +7 -0
- package/blog/tr/intlayer_with_react-intl.md +3 -1
- package/blog/tr/next-i18next_vs_next-intl_vs_intlayer.md +175 -42
- package/blog/tr/rag_powered_documentation_assistant.md +6 -0
- package/blog/uk/index.md +1 -1
- package/blog/uk/rag_powered_documentation_assistant.md +6 -0
- package/blog/vi/index.md +1 -1
- package/blog/vi/rag_powered_documentation_assistant.md +6 -0
- package/blog/zh/index.md +1 -1
- package/blog/zh/intlayer_with_i18next.md +87 -28
- package/blog/zh/intlayer_with_next-i18next.md +86 -29
- package/blog/zh/intlayer_with_next-intl.md +88 -28
- package/blog/zh/intlayer_with_react-i18next.md +87 -28
- package/blog/zh/intlayer_with_react-intl.md +88 -27
- package/blog/zh/intlayer_with_vue-i18n.md +117 -8
- package/blog/zh/rag_powered_documentation_assistant.md +6 -0
- package/dist/cjs/common.cjs +24 -1
- package/dist/cjs/common.cjs.map +1 -1
- package/dist/cjs/generated/blog.entry.cjs +58 -31
- package/dist/cjs/generated/blog.entry.cjs.map +1 -1
- package/dist/cjs/generated/docs.entry.cjs +265 -218
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/cjs/generated/frequentQuestions.entry.cjs +48 -21
- package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
- package/dist/cjs/generated/legal.entry.cjs +33 -6
- package/dist/cjs/generated/legal.entry.cjs.map +1 -1
- package/dist/esm/common.mjs +24 -1
- package/dist/esm/common.mjs.map +1 -1
- package/dist/esm/generated/blog.entry.mjs +58 -31
- package/dist/esm/generated/blog.entry.mjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +265 -218
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/esm/generated/frequentQuestions.entry.mjs +48 -21
- package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
- package/dist/esm/generated/legal.entry.mjs +33 -6
- package/dist/esm/generated/legal.entry.mjs.map +1 -1
- package/dist/types/common.d.ts.map +1 -1
- package/dist/types/generated/blog.entry.d.ts.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +1 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
- package/dist/types/generated/legal.entry.d.ts.map +1 -1
- package/docs/ar/analytics.md +222 -0
- package/docs/ar/autoFill.md +28 -9
- package/docs/ar/bundle_optimization.md +58 -25
- package/docs/ar/configuration.md +25 -8
- package/docs/ar/dictionary/file.md +3 -1
- package/docs/ar/dictionary/nesting.md +47 -30
- package/docs/ar/formatters.md +148 -193
- package/docs/ar/intlayer_CMS.md +152 -0
- package/docs/ar/intlayer_with_angular_21.md +9 -9
- package/docs/ar/intlayer_with_nextjs_14.md +1 -28
- package/docs/ar/intlayer_with_nextjs_15.md +1 -33
- package/docs/ar/intlayer_with_react_router_v7_fs_routes.md +40 -83
- package/docs/ar/intlayer_with_solid_start.md +1078 -0
- package/docs/ar/intlayer_with_vite+vue.md +1 -3
- package/docs/ar/live-sync.md +4 -0
- package/docs/ar/packages/intlayer/getLocalizedUrl.md +26 -24
- package/docs/ar/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/ar/packages/react-intlayer/useDictionary.md +4 -21
- package/docs/ar/packages/react-intlayer/useIntlayer.md +2 -2
- package/docs/ar/packages/vite-intlayer/exports.md +15 -0
- package/docs/ar/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/ar/packages/vite-intlayer/intlayerPrune.md +70 -3
- package/docs/ar/releases/v9.md +0 -37
- package/docs/ar/self_hosting.md +46 -49
- package/docs/bn/bundle_optimization.md +58 -25
- package/docs/bn/configuration.md +25 -8
- package/docs/bn/intlayer_with_angular_21.md +9 -9
- package/docs/cs/bundle_optimization.md +58 -25
- package/docs/cs/configuration.md +25 -8
- package/docs/cs/intlayer_with_angular_21.md +9 -9
- package/docs/de/analytics.md +222 -0
- package/docs/de/autoFill.md +28 -9
- package/docs/de/bundle_optimization.md +58 -25
- package/docs/de/configuration.md +25 -8
- package/docs/de/dictionary/file.md +3 -1
- package/docs/de/dictionary/nesting.md +47 -31
- package/docs/de/formatters.md +208 -334
- package/docs/de/intlayer_CMS.md +66 -15
- package/docs/de/intlayer_with_angular_21.md +9 -9
- package/docs/de/intlayer_with_nestjs.md +11 -7
- package/docs/de/intlayer_with_nextjs_14.md +1 -28
- package/docs/de/intlayer_with_nextjs_15.md +1 -33
- package/docs/de/intlayer_with_solid_start.md +1078 -0
- package/docs/de/intlayer_with_vite+vue.md +1 -3
- package/docs/de/live-sync.md +4 -0
- package/docs/de/packages/intlayer/getLocalizedUrl.md +33 -25
- package/docs/de/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/de/packages/react-intlayer/useDictionary.md +2 -5
- package/docs/de/packages/vite-intlayer/exports.md +15 -0
- package/docs/de/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/de/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/de/releases/v9.md +0 -15
- package/docs/de/self_hosting.md +46 -50
- package/docs/en/bundle_optimization.md +51 -23
- package/docs/en/cli/doc-review.md +13 -2
- package/docs/en/configuration.md +24 -15
- package/docs/en/intlayer_with_angular_21.md +9 -9
- package/docs/en/intlayer_with_solid_start.md +1091 -0
- package/docs/en/intlayer_with_vite+solid.md +12 -0
- package/docs/en/live-sync.md +4 -0
- package/docs/en-GB/analytics.md +222 -0
- package/docs/en-GB/autoFill.md +26 -7
- package/docs/en-GB/bundle_optimization.md +58 -25
- package/docs/en-GB/configuration.md +25 -8
- package/docs/en-GB/dictionary/file.md +2 -0
- package/docs/en-GB/dictionary/nesting.md +64 -205
- package/docs/en-GB/formatters.md +146 -181
- package/docs/en-GB/intlayer_CMS.md +152 -0
- package/docs/en-GB/intlayer_with_angular_21.md +9 -9
- package/docs/en-GB/intlayer_with_nestjs.md +6 -2
- package/docs/en-GB/intlayer_with_nextjs_14.md +0 -27
- package/docs/en-GB/intlayer_with_nextjs_15.md +1 -33
- package/docs/en-GB/intlayer_with_react_router_v7_fs_routes.md +40 -82
- package/docs/en-GB/intlayer_with_solid_start.md +1078 -0
- package/docs/en-GB/intlayer_with_vite+vue.md +1 -3
- package/docs/en-GB/live-sync.md +4 -0
- package/docs/en-GB/packages/intlayer/getLocalizedUrl.md +30 -22
- package/docs/en-GB/packages/intlayer/getMultilingualUrls.md +16 -48
- package/docs/en-GB/packages/vite-intlayer/exports.md +15 -0
- package/docs/en-GB/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/en-GB/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/en-GB/self_hosting.md +60 -49
- package/docs/es/analytics.md +222 -0
- package/docs/es/autoFill.md +28 -9
- package/docs/es/bundle_optimization.md +58 -25
- package/docs/es/configuration.md +24 -7
- package/docs/es/dictionary/file.md +3 -1
- package/docs/es/dictionary/nesting.md +47 -30
- package/docs/es/formatters.md +114 -168
- package/docs/es/intlayer_CMS.md +35 -10
- package/docs/es/intlayer_with_angular_21.md +9 -9
- package/docs/es/intlayer_with_nextjs_14.md +1 -28
- package/docs/es/intlayer_with_nextjs_15.md +1 -33
- package/docs/es/intlayer_with_solid_start.md +1078 -0
- package/docs/es/live-sync.md +4 -0
- package/docs/es/packages/intlayer/getLocalizedUrl.md +31 -23
- package/docs/es/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/es/packages/vite-intlayer/exports.md +15 -0
- package/docs/es/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/es/packages/vite-intlayer/intlayerPrune.md +69 -2
- package/docs/es/releases/v9.md +0 -15
- package/docs/es/self_hosting.md +46 -49
- package/docs/fr/analytics.md +222 -0
- package/docs/fr/autoFill.md +27 -8
- package/docs/fr/bundle_optimization.md +58 -25
- package/docs/fr/configuration.md +25 -8
- package/docs/fr/dictionary/file.md +2 -0
- package/docs/fr/dictionary/nesting.md +37 -184
- package/docs/fr/formatters.md +95 -236
- package/docs/fr/intlayer_CMS.md +66 -15
- package/docs/fr/intlayer_with_angular_21.md +9 -9
- package/docs/fr/intlayer_with_nextjs_14.md +1 -28
- package/docs/fr/intlayer_with_nextjs_15.md +1 -33
- package/docs/fr/intlayer_with_solid_start.md +1078 -0
- package/docs/fr/live-sync.md +4 -0
- package/docs/fr/packages/intlayer/getLocalizedUrl.md +37 -27
- package/docs/fr/packages/intlayer/getMultilingualUrls.md +18 -20
- package/docs/fr/packages/vite-intlayer/exports.md +15 -0
- package/docs/fr/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/fr/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/fr/releases/v9.md +0 -15
- package/docs/fr/self_hosting.md +46 -49
- package/docs/hi/analytics.md +222 -0
- package/docs/hi/autoFill.md +28 -9
- package/docs/hi/bundle_optimization.md +58 -25
- package/docs/hi/configuration.md +25 -8
- package/docs/hi/dictionary/file.md +3 -1
- package/docs/hi/dictionary/nesting.md +47 -30
- package/docs/hi/formatters.md +233 -334
- package/docs/hi/how_works_intlayer.md +3 -1
- package/docs/hi/intlayer_CMS.md +152 -0
- package/docs/hi/intlayer_with_angular_21.md +9 -9
- package/docs/hi/intlayer_with_nestjs.md +11 -7
- package/docs/hi/intlayer_with_nextjs_14.md +1 -28
- package/docs/hi/intlayer_with_nextjs_15.md +1 -33
- package/docs/hi/intlayer_with_react_router_v7_fs_routes.md +40 -83
- package/docs/hi/intlayer_with_solid_start.md +1077 -0
- package/docs/hi/intlayer_with_vite+preact.md +56 -49
- package/docs/hi/intlayer_with_vite+react.md +48 -41
- package/docs/hi/intlayer_with_vite+solid.md +32 -41
- package/docs/hi/intlayer_with_vite+vue.md +49 -44
- package/docs/hi/live-sync.md +4 -0
- package/docs/hi/packages/intlayer/getLocalizedUrl.md +26 -24
- package/docs/hi/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/hi/packages/next-intlayer/t.md +33 -1
- package/docs/hi/packages/react-intlayer/useDictionary.md +4 -21
- package/docs/hi/packages/vite-intlayer/exports.md +15 -0
- package/docs/hi/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/hi/packages/vite-intlayer/intlayerPrune.md +78 -5
- package/docs/hi/plugins/sync-json.md +9 -9
- package/docs/hi/releases/v9.md +0 -15
- package/docs/hi/self_hosting.md +38 -51
- package/docs/id/analytics.md +223 -0
- package/docs/id/bundle_optimization.md +58 -25
- package/docs/id/configuration.md +25 -8
- package/docs/id/dictionary/nesting.md +37 -184
- package/docs/id/formatters.md +208 -305
- package/docs/id/intlayer_CMS.md +164 -0
- package/docs/id/intlayer_with_angular_21.md +9 -9
- package/docs/id/intlayer_with_nextjs_14.md +1 -26
- package/docs/id/intlayer_with_react_router_v7_fs_routes.md +70 -63
- package/docs/id/intlayer_with_solid_start.md +1078 -0
- package/docs/id/live-sync.md +4 -0
- package/docs/id/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/id/packages/react-intlayer/useIntlayer.md +2 -2
- package/docs/id/packages/vite-intlayer/exports.md +15 -0
- package/docs/id/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/id/packages/vite-intlayer/intlayerPrune.md +78 -5
- package/docs/id/releases/v9.md +12 -0
- package/docs/id/self_hosting.md +61 -50
- package/docs/it/analytics.md +222 -0
- package/docs/it/autoFill.md +27 -8
- package/docs/it/bundle_optimization.md +58 -25
- package/docs/it/configuration.md +25 -8
- package/docs/it/dictionary/file.md +3 -1
- package/docs/it/dictionary/nesting.md +47 -30
- package/docs/it/formatters.md +114 -168
- package/docs/it/intlayer_CMS.md +66 -15
- package/docs/it/intlayer_with_angular_21.md +9 -9
- package/docs/it/intlayer_with_nestjs.md +11 -7
- package/docs/it/intlayer_with_nextjs_14.md +1 -28
- package/docs/it/intlayer_with_nextjs_15.md +1 -33
- package/docs/it/intlayer_with_solid_start.md +1078 -0
- package/docs/it/intlayer_with_vite+vue.md +1 -3
- package/docs/it/live-sync.md +4 -0
- package/docs/it/packages/intlayer/getLocalizedUrl.md +34 -26
- package/docs/it/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/it/packages/react-intlayer/useDictionary.md +4 -21
- package/docs/it/packages/vite-intlayer/exports.md +15 -0
- package/docs/it/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/it/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/it/releases/v9.md +0 -15
- package/docs/it/self_hosting.md +60 -49
- package/docs/ja/analytics.md +222 -0
- package/docs/ja/autoFill.md +28 -9
- package/docs/ja/bundle_optimization.md +58 -25
- package/docs/ja/configuration.md +25 -8
- package/docs/ja/dictionary/content_file.md +15 -7
- package/docs/ja/dictionary/file.md +3 -1
- package/docs/ja/dictionary/nesting.md +47 -30
- package/docs/ja/formatters.md +133 -187
- package/docs/ja/intlayer_visual_editor.md +3 -1
- package/docs/ja/intlayer_with_angular_21.md +6 -6
- package/docs/ja/intlayer_with_nestjs.md +2 -12
- package/docs/ja/intlayer_with_nextjs_14.md +1 -28
- package/docs/ja/intlayer_with_nextjs_15.md +1 -33
- package/docs/ja/intlayer_with_react_router_v7.md +456 -20
- package/docs/ja/intlayer_with_solid_start.md +1078 -0
- package/docs/ja/intlayer_with_vite+preact.md +73 -41
- package/docs/ja/intlayer_with_vite+vue.md +1 -3
- package/docs/ja/live-sync.md +4 -0
- package/docs/ja/mcp_server.md +3 -9
- package/docs/ja/packages/intlayer/getLocalizedUrl.md +26 -24
- package/docs/ja/packages/intlayer/getMultilingualUrls.md +3 -17
- package/docs/ja/packages/vite-intlayer/exports.md +15 -0
- package/docs/ja/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/ja/packages/vite-intlayer/intlayerPrune.md +79 -6
- package/docs/ja/releases/v9.md +12 -0
- package/docs/ja/self_hosting.md +46 -50
- package/docs/ko/analytics.md +222 -0
- package/docs/ko/autoFill.md +28 -9
- package/docs/ko/bundle_optimization.md +58 -25
- package/docs/ko/configuration.md +25 -8
- package/docs/ko/dictionary/file.md +3 -1
- package/docs/ko/dictionary/nesting.md +47 -30
- package/docs/ko/formatters.md +133 -187
- package/docs/ko/intlayer_with_angular_21.md +9 -9
- package/docs/ko/intlayer_with_lynx+react.md +133 -39
- package/docs/ko/intlayer_with_nextjs_14.md +1 -28
- package/docs/ko/intlayer_with_nextjs_15.md +1 -33
- package/docs/ko/intlayer_with_react_router_v7_fs_routes.md +40 -188
- package/docs/ko/intlayer_with_solid_start.md +1078 -0
- package/docs/ko/intlayer_with_vite+preact.md +57 -23
- package/docs/ko/intlayer_with_vite+vue.md +1 -3
- package/docs/ko/live-sync.md +4 -0
- package/docs/ko/packages/intlayer/getLocalizedUrl.md +26 -24
- package/docs/ko/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/ko/packages/vite-intlayer/exports.md +15 -0
- package/docs/ko/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/ko/packages/vite-intlayer/intlayerPrune.md +79 -6
- package/docs/ko/releases/v9.md +12 -0
- package/docs/ko/self_hosting.md +60 -49
- package/docs/nl/bundle_optimization.md +58 -25
- package/docs/nl/configuration.md +25 -8
- package/docs/nl/intlayer_with_angular_21.md +9 -9
- package/docs/pl/analytics.md +220 -0
- package/docs/pl/bundle_optimization.md +58 -25
- package/docs/pl/configuration.md +25 -8
- package/docs/pl/dictionary/nesting.md +47 -30
- package/docs/pl/formatters.md +208 -305
- package/docs/pl/intlayer_CMS.md +152 -0
- package/docs/pl/intlayer_with_angular_21.md +9 -9
- package/docs/pl/intlayer_with_nextjs_14.md +1 -26
- package/docs/pl/intlayer_with_solid_start.md +1078 -0
- package/docs/pl/live-sync.md +4 -0
- package/docs/pl/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/pl/packages/react-intlayer/useDictionary.md +2 -19
- package/docs/pl/packages/vite-intlayer/exports.md +15 -0
- package/docs/pl/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/pl/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/pl/releases/v9.md +0 -15
- package/docs/pl/self_hosting.md +60 -49
- package/docs/pt/analytics.md +222 -0
- package/docs/pt/bundle_optimization.md +58 -24
- package/docs/pt/configuration.md +25 -8
- package/docs/pt/dictionary/file.md +3 -1
- package/docs/pt/dictionary/nesting.md +47 -30
- package/docs/pt/formatters.md +112 -168
- package/docs/pt/intlayer_CMS.md +66 -15
- package/docs/pt/intlayer_with_angular_21.md +9 -9
- package/docs/pt/intlayer_with_astro_lit.md +26 -1
- package/docs/pt/intlayer_with_nextjs_14.md +1 -28
- package/docs/pt/intlayer_with_nextjs_15.md +1 -33
- package/docs/pt/intlayer_with_solid_start.md +1078 -0
- package/docs/pt/intlayer_with_vite+vue.md +1 -3
- package/docs/pt/live-sync.md +4 -0
- package/docs/pt/packages/intlayer/getLocalizedUrl.md +34 -26
- package/docs/pt/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/pt/packages/vite-intlayer/exports.md +15 -0
- package/docs/pt/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/pt/packages/vite-intlayer/intlayerPrune.md +78 -6
- package/docs/pt/releases/v9.md +0 -15
- package/docs/pt/self_hosting.md +60 -49
- package/docs/ru/analytics.md +222 -0
- package/docs/ru/autoFill.md +27 -8
- package/docs/ru/bundle_optimization.md +58 -25
- package/docs/ru/configuration.md +25 -8
- package/docs/ru/dictionary/file.md +3 -1
- package/docs/ru/dictionary/nesting.md +64 -205
- package/docs/ru/formatters.md +108 -128
- package/docs/ru/intlayer_with_angular_21.md +9 -9
- package/docs/ru/intlayer_with_nextjs_14.md +1 -28
- package/docs/ru/intlayer_with_nextjs_15.md +1 -33
- package/docs/ru/intlayer_with_solid_start.md +1078 -0
- package/docs/ru/intlayer_with_vite+vue.md +1 -3
- package/docs/ru/live-sync.md +4 -0
- package/docs/ru/packages/intlayer/getLocalizedUrl.md +34 -26
- package/docs/ru/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/ru/packages/react-intlayer/useDictionary.md +4 -21
- package/docs/ru/packages/vite-intlayer/exports.md +15 -0
- package/docs/ru/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/ru/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/ru/releases/v9.md +0 -15
- package/docs/ru/self_hosting.md +60 -49
- package/docs/tr/analytics.md +222 -0
- package/docs/tr/autoFill.md +28 -9
- package/docs/tr/bundle_optimization.md +58 -25
- package/docs/tr/configuration.md +25 -8
- package/docs/tr/dictionary/file.md +2 -0
- package/docs/tr/dictionary/nesting.md +47 -30
- package/docs/tr/formatters.md +144 -190
- package/docs/tr/intlayer_CMS.md +152 -0
- package/docs/tr/intlayer_with_angular_21.md +9 -9
- package/docs/tr/intlayer_with_nestjs.md +2 -12
- package/docs/tr/intlayer_with_nextjs_15.md +1 -33
- package/docs/tr/intlayer_with_solid_start.md +1078 -0
- package/docs/tr/intlayer_with_vite+vue.md +1 -3
- package/docs/tr/live-sync.md +4 -0
- package/docs/tr/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/tr/packages/vite-intlayer/exports.md +15 -0
- package/docs/tr/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/tr/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/tr/self_hosting.md +47 -50
- package/docs/uk/analytics.md +222 -0
- package/docs/uk/bundle_optimization.md +58 -25
- package/docs/uk/configuration.md +25 -8
- package/docs/uk/dictionary/nesting.md +47 -30
- package/docs/uk/formatters.md +128 -185
- package/docs/uk/intlayer_CMS.md +152 -0
- package/docs/uk/intlayer_with_angular_21.md +9 -9
- package/docs/uk/intlayer_with_nextjs_14.md +1 -26
- package/docs/uk/intlayer_with_solid_start.md +1078 -0
- package/docs/uk/live-sync.md +4 -0
- package/docs/uk/packages/react-intlayer/useDictionary.md +2 -19
- package/docs/uk/packages/vite-intlayer/exports.md +15 -0
- package/docs/uk/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/uk/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/uk/releases/v9.md +0 -15
- package/docs/uk/self_hosting.md +60 -50
- package/docs/ur/bundle_optimization.md +58 -25
- package/docs/ur/configuration.md +25 -8
- package/docs/ur/intlayer_with_angular_21.md +9 -9
- package/docs/vi/analytics.md +222 -0
- package/docs/vi/bundle_optimization.md +58 -25
- package/docs/vi/configuration.md +24 -8
- package/docs/vi/dictionary/nesting.md +37 -184
- package/docs/vi/formatters.md +208 -309
- package/docs/vi/intlayer_CMS.md +164 -0
- package/docs/vi/intlayer_with_angular_21.md +9 -9
- package/docs/vi/intlayer_with_nextjs_14.md +0 -25
- package/docs/vi/intlayer_with_react_router_v7_fs_routes.md +70 -63
- package/docs/vi/intlayer_with_solid_start.md +1078 -0
- package/docs/vi/live-sync.md +4 -0
- package/docs/vi/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/vi/packages/react-intlayer/useIntlayer.md +2 -2
- package/docs/vi/packages/vite-intlayer/exports.md +15 -0
- package/docs/vi/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/vi/packages/vite-intlayer/intlayerPrune.md +77 -4
- package/docs/vi/plugins/sync-json.md +6 -6
- package/docs/vi/releases/v9.md +12 -0
- package/docs/vi/self_hosting.md +61 -50
- package/docs/zh/CI_CD.md +4 -0
- package/docs/zh/analytics.md +222 -0
- package/docs/zh/autoFill.md +28 -9
- package/docs/zh/bundle_optimization.md +58 -25
- package/docs/zh/configuration.md +24 -8
- package/docs/zh/dictionary/file.md +2 -0
- package/docs/zh/dictionary/nesting.md +47 -30
- package/docs/zh/formatters.md +140 -188
- package/docs/zh/intlayer_CMS.md +164 -0
- package/docs/zh/intlayer_with_analog.md +193 -3
- package/docs/zh/intlayer_with_angular_19.md +272 -3
- package/docs/zh/intlayer_with_angular_21.md +7 -7
- package/docs/zh/intlayer_with_create_react_app.md +348 -26
- package/docs/zh/intlayer_with_lynx+react.md +263 -4
- package/docs/zh/intlayer_with_nextjs_14.md +1 -28
- package/docs/zh/intlayer_with_nextjs_15.md +1 -33
- package/docs/zh/intlayer_with_nextjs_no_locale_path.md +558 -1
- package/docs/zh/intlayer_with_nuxt.md +182 -7
- package/docs/zh/intlayer_with_react_router_v7.md +183 -25
- package/docs/zh/intlayer_with_react_router_v7_fs_routes.md +306 -42
- package/docs/zh/intlayer_with_solid_start.md +1077 -0
- package/docs/zh/intlayer_with_storybook.md +260 -3
- package/docs/zh/intlayer_with_svelte_kit.md +428 -48
- package/docs/zh/intlayer_with_tanstack+solid.md +713 -13
- package/docs/zh/intlayer_with_tanstack.md +731 -20
- package/docs/zh/intlayer_with_vanilla.md +314 -4
- package/docs/zh/intlayer_with_vite+lit.md +465 -4
- package/docs/zh/intlayer_with_vite+preact.md +422 -116
- package/docs/zh/intlayer_with_vite+react.md +324 -45
- package/docs/zh/intlayer_with_vite+react_compiler.md +228 -13
- package/docs/zh/intlayer_with_vite+solid.md +462 -32
- package/docs/zh/intlayer_with_vite+vanilla.md +497 -4
- package/docs/zh/live-sync.md +4 -0
- package/docs/zh/mcp_server.md +3 -9
- package/docs/zh/packages/intlayer/getLocalizedUrl.md +26 -24
- package/docs/zh/packages/intlayer/getMultilingualUrls.md +4 -18
- package/docs/zh/packages/vite-intlayer/exports.md +29 -0
- package/docs/zh/packages/vite-intlayer/intlayer.md +121 -6
- package/docs/zh/packages/vite-intlayer/intlayerPrune.md +79 -6
- package/docs/zh/releases/v9.md +12 -0
- package/docs/zh/self_hosting.md +60 -50
- package/docs/zh-TW/bundle_optimization.md +58 -25
- package/docs/zh-TW/intlayer_with_angular_21.md +7 -7
- package/package.json +7 -7
- package/src/common.ts +39 -2
- package/src/generated/blog.entry.ts +66 -34
- package/src/generated/docs.entry.ts +273 -221
- package/src/generated/frequentQuestions.entry.ts +56 -24
- package/src/generated/legal.entry.ts +41 -9
|
@@ -0,0 +1,1077 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-08-06
|
|
3
|
+
updatedAt: 2026-08-06
|
|
4
|
+
title: "Solid Start i18n - 翻译你的应用的完整指南"
|
|
5
|
+
description: "告别 i18next。2026 年构建多语言 (i18n) SolidStart 应用的指南。包含服务端渲染的语言路由、hreflang、sitemap 和 AI 辅助翻译。"
|
|
6
|
+
keywords:
|
|
7
|
+
- Internationalization
|
|
8
|
+
- Documentation
|
|
9
|
+
- Intlayer
|
|
10
|
+
- SolidStart
|
|
11
|
+
- Solid
|
|
12
|
+
- i18n
|
|
13
|
+
- TypeScript
|
|
14
|
+
- Locale Routing
|
|
15
|
+
- Sitemap
|
|
16
|
+
slugs:
|
|
17
|
+
- doc
|
|
18
|
+
- environment
|
|
19
|
+
- solid-start
|
|
20
|
+
applicationTemplate: https://github.com/aymericzip/intlayer-solid-start-template
|
|
21
|
+
history:
|
|
22
|
+
- version: 9.1.3
|
|
23
|
+
date: 2025-08-06
|
|
24
|
+
changes: "初始历史"
|
|
25
|
+
author: aymericzip
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# 使用 Intlayer 翻译你的 SolidStart 网站 | 国际化 (i18n)
|
|
29
|
+
|
|
30
|
+
<Tabs defaultTab="video">
|
|
31
|
+
<Tab label="视频" value="video">
|
|
32
|
+
|
|
33
|
+
<iframe title="Vite 和 Solid 的最佳 i18n 解决方案?探索 Intlayer" class="m-auto aspect-16/9 w-full overflow-hidden rounded-lg border-0" allow="autoplay; gyroscope;" loading="lazy" width="1080" height="auto" src="https://www.youtube.com/embed/dS9L7uJeak4?si=VaKmrYMmXjo3xpk2"/>
|
|
34
|
+
|
|
35
|
+
</Tab>
|
|
36
|
+
<Tab label="代码" value="code">
|
|
37
|
+
|
|
38
|
+
<iframe
|
|
39
|
+
src="https://ide.intlayer.org/aymericzip/intlayer-solid-start-template?file=intlayer.config.ts"
|
|
40
|
+
className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
|
|
41
|
+
title="演示 CodeSandbox - 如何使用 Intlayer 实现应用程序国际化"
|
|
42
|
+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
43
|
+
loading="lazy"
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
</Tab>
|
|
47
|
+
</Tabs>
|
|
48
|
+
|
|
49
|
+
## 目录
|
|
50
|
+
|
|
51
|
+
<TOC/>
|
|
52
|
+
|
|
53
|
+
本指南涵盖了一个**服务端渲染**的 SolidStart 应用程序:语言检测在请求时发生,页面在服务端以正确的语言渲染,并且搜索引擎所需的 `<html lang>`、`hreflang` 和 sitemap 信号都是在服务端生成的。
|
|
54
|
+
|
|
55
|
+
## 为什么选择 Intlayer 而不是其他替代方案?
|
|
56
|
+
|
|
57
|
+
与 `@solid-primitives/i18n` 或 `i18next` 等主流解决方案相比,Intlayer 是一个带有集成优化的解决方案,例如:
|
|
58
|
+
|
|
59
|
+
<AccordionGroup>
|
|
60
|
+
|
|
61
|
+
<Accordion header="全面支持 Solid">
|
|
62
|
+
|
|
63
|
+
Intlayer 经过优化,可与 Solid 完美配合,提供**组件级内容划分**、**响应式翻译**以及扩展国际化 (i18n) 所需的所有功能。
|
|
64
|
+
|
|
65
|
+
</Accordion>
|
|
66
|
+
|
|
67
|
+
<Accordion header="打包体积">
|
|
68
|
+
|
|
69
|
+
无需将庞大的 JSON 文件加载到页面中,只需加载必要的内容。Intlayer 有助于**将打包文件和页面体积减少高达 50%**。
|
|
70
|
+
|
|
71
|
+
</Accordion>
|
|
72
|
+
|
|
73
|
+
<Accordion header="可维护性">
|
|
74
|
+
|
|
75
|
+
对应用程序的内容进行局部作用域划分**有助于大型应用程序的维护**。你可以复制或删除单个功能文件夹,而无需心理负担去审查整个内容代码库。此外,Intlayer 是**完全类型化**的,以确保内容的准确性。
|
|
76
|
+
|
|
77
|
+
</Accordion>
|
|
78
|
+
|
|
79
|
+
<Accordion header="AI 代理">
|
|
80
|
+
|
|
81
|
+
将内容协同定位**减少了大语言模型 (LLM) 所需的上下文**。Intlayer 还附带了一套工具,例如用于测试缺失翻译的 **CLI**、**[LSP](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/lsp.md)**、**[MCP](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/mcp_server.md)** 和 **[agent skills](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/agent_skills.md)**,使 AI 代理的开发人员体验 (DX) 更加顺畅。
|
|
82
|
+
|
|
83
|
+
</Accordion>
|
|
84
|
+
|
|
85
|
+
<Accordion header="自动化">
|
|
86
|
+
|
|
87
|
+
在 CI/CD 流水线中使用你选择的 LLM 按照 AI 提供商的成本自动进行翻译。Intlayer 还提供了一个**编译器**来自动提取内容,以及一个 [Web 平台](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_CMS.md) 来帮助**在后台进行翻译**。
|
|
88
|
+
|
|
89
|
+
</Accordion>
|
|
90
|
+
|
|
91
|
+
<Accordion header="性能">
|
|
92
|
+
|
|
93
|
+
将庞大的 JSON 文件连接到组件可能会导致性能和响应性问题。Intlayer 在构建时优化了内容加载。
|
|
94
|
+
|
|
95
|
+
</Accordion>
|
|
96
|
+
|
|
97
|
+
<Accordion header="与非开发人员协同扩展">
|
|
98
|
+
|
|
99
|
+
Intlayer 不仅仅是一个 i18n 解决方案,还提供了一个**自托管的[可视化编辑器](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_visual_editor.md)** 和一个 **[完整 CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_CMS.md)**,帮助你**实时**管理多语言内容,使与翻译人员、文案人员和其他团队成员的协作更加无缝。内容可以存储在本地和/或远程。
|
|
100
|
+
|
|
101
|
+
</Accordion>
|
|
102
|
+
</AccordionGroup>
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 在 SolidStart 应用程序中设置 Intlayer 的分步指南
|
|
107
|
+
|
|
108
|
+
<Steps>
|
|
109
|
+
|
|
110
|
+
<Step number={1} title="安装依赖项">
|
|
111
|
+
|
|
112
|
+
使用 npm 安装必要的软件包:
|
|
113
|
+
|
|
114
|
+
```bash packageManager="npm"
|
|
115
|
+
npx intlayer init --interactive
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```bash packageManager="pnpm"
|
|
119
|
+
pnpm dlx intlayer@canary init --interactive
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```bash packageManager="yarn"
|
|
123
|
+
yarn dlx intlayer@canary init --interactive
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```bash packageManager="bun"
|
|
127
|
+
bunx intlayer@canary init --interactive
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> `--interactive` 标志是可选的。如果你是 AI 代理,请使用 `intlayer-cli init`。
|
|
131
|
+
|
|
132
|
+
> 此命令将检测你的环境并安装所需的软件包。例如:
|
|
133
|
+
|
|
134
|
+
```bash packageManager="npm"
|
|
135
|
+
npm install intlayer solid-intlayer vite-intlayer
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```bash packageManager="pnpm"
|
|
139
|
+
pnpm add intlayer solid-intlayer vite-intlayer
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```bash packageManager="yarn"
|
|
143
|
+
yarn add intlayer solid-intlayer vite-intlayer
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```bash packageManager="bun"
|
|
147
|
+
bun add intlayer solid-intlayer vite-intlayer
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- **intlayer**
|
|
151
|
+
|
|
152
|
+
核心包,提供用于配置管理、翻译、[内容声明](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/content_file.md)、转译和 [CLI 命令](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/cli/index.md) 的国际化工具。
|
|
153
|
+
|
|
154
|
+
- **solid-intlayer**
|
|
155
|
+
|
|
156
|
+
将 Intlayer 与 Solid 应用程序集成的软件包。它为 Solid 国际化提供上下文提供程序 (context providers) 和钩子 (hooks)。
|
|
157
|
+
|
|
158
|
+
- **vite-intlayer**
|
|
159
|
+
|
|
160
|
+
包含用于将 Intlayer 与 [Vite 打包器](https://vite.dev/guide/why.html#why-bundle-for-production) 集成的 Vite 插件,以及检测用户偏好语言、管理 cookie 和处理 URL 重定向的语言路由句柄。
|
|
161
|
+
|
|
162
|
+
> 这里 `vite-intlayer` 是一个服务端关注点,不仅是构建时的关注点:它提供了 SolidStart 的 Nitro 服务器运行的请求句柄。将其保留在 `dependencies` 中是安全的默认设置 —— 仅当你要部署包含 Nitro 内联句柄的构建后的 `.output` 目录时,才可以将其移动到 `devDependencies`。
|
|
163
|
+
|
|
164
|
+
</Step>
|
|
165
|
+
|
|
166
|
+
<Step number={2} title="配置你的项目">
|
|
167
|
+
|
|
168
|
+
创建一个配置文件来配置应用程序的语言:
|
|
169
|
+
|
|
170
|
+
```typescript fileName="intlayer.config.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
171
|
+
import { type IntlayerConfig, Locales } from "intlayer";
|
|
172
|
+
|
|
173
|
+
const config: IntlayerConfig = {
|
|
174
|
+
internationalization: {
|
|
175
|
+
locales: [
|
|
176
|
+
Locales.ENGLISH,
|
|
177
|
+
Locales.FRENCH,
|
|
178
|
+
Locales.SPANISH,
|
|
179
|
+
// 你的其他语言
|
|
180
|
+
],
|
|
181
|
+
defaultLocale: Locales.ENGLISH,
|
|
182
|
+
},
|
|
183
|
+
routing: {
|
|
184
|
+
mode: "prefix-no-default",
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export default config;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
使用 `prefix-no-default`,默认语言从无前缀的 URL 提供:
|
|
192
|
+
|
|
193
|
+
```plaintext
|
|
194
|
+
/ /about → 英语 (默认语言)
|
|
195
|
+
/fr /fr/about → 法语
|
|
196
|
+
/es /es/about → 西班牙语
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
> 通过此配置文件,你可以设置本地化 URL、中间件重定向、cookie 名称、内容声明的位置和扩展名、禁用控制台中的 Intlayer 日志等。有关可用参数的完整列表,请参阅[配置文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/configuration.md)。
|
|
200
|
+
|
|
201
|
+
</Step>
|
|
202
|
+
|
|
203
|
+
<Step number={3} title="在 Vite 配置中集成 Intlayer">
|
|
204
|
+
|
|
205
|
+
将 Intlayer 插件添加到你的配置中:
|
|
206
|
+
|
|
207
|
+
```typescript fileName="vite.config.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
208
|
+
import { solidStart } from "@solidjs/start/config";
|
|
209
|
+
import { nitro } from "nitro/vite";
|
|
210
|
+
import { defineConfig } from "vite";
|
|
211
|
+
import { intlayer } from "vite-intlayer";
|
|
212
|
+
|
|
213
|
+
export default defineConfig({
|
|
214
|
+
plugins: [solidStart(), nitro(), intlayer()],
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
> `intlayer()` Vite 插件构建你的内容声明文件,在开发模式下监视它们,并在应用程序内部定义 Intlayer 环境变量。它还提供可优化性能的别名。
|
|
219
|
+
|
|
220
|
+
### 语言路由随插件一起提供
|
|
221
|
+
|
|
222
|
+
SolidStart 运行在 [Nitro](https://nitro.build) 上,并且 `intlayer()` 将其语言路由句柄直接注册到 Nitro 的服务器管道中(通过 `routing.enableProxy` 选项,默认为 `true`)。无需配置其他内容:在构建好的服务器上,每个请求在到达路由器之前都会经过检查,并且
|
|
223
|
+
|
|
224
|
+
- 语言从 URL 前缀读取,其次是 `INTLAYER_LOCALE` cookie,然后是 `Accept-Language` 请求头;
|
|
225
|
+
- 当解析出的语言不是默认语言时,无前缀的 URL 会重定向到对应的本地化页面(`/` → `/fr`);
|
|
226
|
+
- 冗余前缀的 URL 会重定向回其规范形式(`/en/about` → `/about`);
|
|
227
|
+
- 语言 cookie 会在响应中写回。
|
|
228
|
+
|
|
229
|
+
</Step>
|
|
230
|
+
|
|
231
|
+
<Step number={4} title="声明你的内容">
|
|
232
|
+
|
|
233
|
+
创建并管理你的内容声明以存储翻译:
|
|
234
|
+
|
|
235
|
+
```tsx fileName="src/contents/home.content.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
236
|
+
import { type Dictionary, t } from "intlayer";
|
|
237
|
+
|
|
238
|
+
const homeContent = {
|
|
239
|
+
key: "home-page",
|
|
240
|
+
content: {
|
|
241
|
+
title: t({
|
|
242
|
+
en: "Hello world!",
|
|
243
|
+
fr: "Bonjour le monde !",
|
|
244
|
+
es: "¡Hola mundo!",
|
|
245
|
+
}),
|
|
246
|
+
metaTitle: "SolidStart + Intlayer",
|
|
247
|
+
metaDescription: t({
|
|
248
|
+
en: "A SolidStart application internationalized with Intlayer.",
|
|
249
|
+
fr: "Une application SolidStart internationalisée avec Intlayer.",
|
|
250
|
+
es: "Una aplicación SolidStart internacionalizada con Intlayer.",
|
|
251
|
+
}),
|
|
252
|
+
documentation: t({
|
|
253
|
+
en: "Visit start.solidjs.com to learn how to build SolidStart apps.",
|
|
254
|
+
fr: "Visitez start.solidjs.com pour apprendre à créer des applications SolidStart.",
|
|
255
|
+
es: "Visita start.solidjs.com para aprender a crear aplicaciones SolidStart.",
|
|
256
|
+
}),
|
|
257
|
+
},
|
|
258
|
+
} satisfies Dictionary;
|
|
259
|
+
|
|
260
|
+
export default homeContent;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
```json fileName="src/contents/home.content.json" contentDeclarationFormat="json"
|
|
264
|
+
{
|
|
265
|
+
"$schema": "https://intlayer.org/schema.json",
|
|
266
|
+
"key": "home-page",
|
|
267
|
+
"content": {
|
|
268
|
+
"title": {
|
|
269
|
+
"nodeType": "translation",
|
|
270
|
+
"translation": {
|
|
271
|
+
"en": "Hello world!",
|
|
272
|
+
"fr": "Bonjour le monde !",
|
|
273
|
+
"es": "¡Hola mundo!"
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"metaTitle": "SolidStart + Intlayer",
|
|
277
|
+
"metaDescription": {
|
|
278
|
+
"nodeType": "translation",
|
|
279
|
+
"translation": {
|
|
280
|
+
"en": "A SolidStart application internationalized with Intlayer.",
|
|
281
|
+
"fr": "Une application SolidStart internationalisée avec Intlayer.",
|
|
282
|
+
"es": "Una aplicación SolidStart internacionalizada con Intlayer."
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"documentation": {
|
|
286
|
+
"nodeType": "translation",
|
|
287
|
+
"translation": {
|
|
288
|
+
"en": "Visit start.solidjs.com to learn how to build SolidStart apps.",
|
|
289
|
+
"fr": "Visitez start.solidjs.com pour apprendre à créer des applications SolidStart.",
|
|
290
|
+
"es": "Visita start.solidjs.com para aprender a crear aplicaciones SolidStart."
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
> ⚠️ **SolidStart 特别注意点**:`src/routes` 下的每个 `.ts` / `.tsx` 文件都会成为一个路由,而 `.content.ts` 文件具有默认导出,因此它会被误识别为一个页面。请将**页面**的内容声明保留在 routes 目录之外(`src/contents/` 效果很好)。**组件**的内容可以保持协同定位,因为文件系统路由器不会扫描 `src/components`。
|
|
298
|
+
|
|
299
|
+
> 只要你的内容声明包含在 `contentDir` 目录(默认为 `./src`)中,并匹配内容声明文件扩展名(默认为 `.content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}`),就可以在应用程序的任何位置定义它们。
|
|
300
|
+
>
|
|
301
|
+
> 有关更多详细信息,请参阅[内容声明文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/content_file.md)。
|
|
302
|
+
|
|
303
|
+
</Step>
|
|
304
|
+
|
|
305
|
+
<Step number={5} title="添加本地化路由">
|
|
306
|
+
|
|
307
|
+
本步骤的目标是赋予每种语言自己的 URL,这也是搜索引擎进行索引的内容。
|
|
308
|
+
|
|
309
|
+
将你的页面移动到**可选动态段**下。在 SolidStart 的文件系统路由器中,`[[locale]]` 编译为 `:locale?` 路径模式:
|
|
310
|
+
|
|
311
|
+
```plaintext
|
|
312
|
+
src/routes/
|
|
313
|
+
[[locale]].tsx ← 验证动态段的 layout
|
|
314
|
+
[[locale]]/
|
|
315
|
+
index.tsx → / 以及 /fr 以及 /es
|
|
316
|
+
about.tsx → /about 以及 /fr/about 以及 /es/about
|
|
317
|
+
[...404].tsx → 捕获其他任何内容的 catch-all
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
布局文件的唯一工作是将该动态段约束为已配置的语言:
|
|
321
|
+
|
|
322
|
+
```tsx fileName="src/routes/[[locale]].tsx" codeFormat="typescript"
|
|
323
|
+
import type { RouteSectionProps } from "@solidjs/router";
|
|
324
|
+
import { locales } from "intlayer";
|
|
325
|
+
|
|
326
|
+
export const route = {
|
|
327
|
+
matchFilters: {
|
|
328
|
+
locale: locales,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export default function LocaleLayout(props: RouteSectionProps) {
|
|
333
|
+
return <>{props.children}</>;
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
`@solidjs/router` 将 `:locale?` 扩展为两种模式 —— 一种带有段,一种不带段 —— 并按特异性递减进行匹配。`matchFilters` 是区分正常设置与令人困惑的设置的关键所在:
|
|
338
|
+
|
|
339
|
+
| URL | 没有 `matchFilters` | 带有 `matchFilters` |
|
|
340
|
+
| ----------- | --------------------------------------- | ----------------------------- |
|
|
341
|
+
| `/fr/about` | 法语关于页面 | 法语关于页面 |
|
|
342
|
+
| `/about` | 关于页面 (静态段胜出) | 关于页面 |
|
|
343
|
+
| `/unknown` | **主页**,静默处理,且 `locale=unknown` | 不匹配 → 回退到 catch-all 404 |
|
|
344
|
+
|
|
345
|
+
> 如果你使用 `'prefix-all'` 路由模式,请首选 `[locale]`(必需),如果是 `'no-prefix'` 或 `'search-params'`,则完全放弃该段。
|
|
346
|
+
|
|
347
|
+
</Step>
|
|
348
|
+
|
|
349
|
+
<Step number={6} title="为你的应用程序提供语言 locale">
|
|
350
|
+
|
|
351
|
+
URL 是语言 locale 的唯一真理来源:中间件已经将请求重定向到其本地化路径,因此在根布局中读取路径可使服务端渲染与客户端水化(hydration)保持一致,并使每次客户端导航都自动更新语言 locale。
|
|
352
|
+
|
|
353
|
+
```tsx fileName="src/app.tsx" codeFormat="typescript"
|
|
354
|
+
import { MetaProvider } from "@solidjs/meta";
|
|
355
|
+
import { Router, useLocation } from "@solidjs/router";
|
|
356
|
+
import { FileRoutes } from "@solidjs/start/router";
|
|
357
|
+
import { defaultLocale, getHTMLTextDir, getLocaleFromPath } from "intlayer";
|
|
358
|
+
import { IntlayerProvider } from "solid-intlayer";
|
|
359
|
+
import { createEffect, type ParentProps, Suspense } from "solid-js";
|
|
360
|
+
import { isServer } from "solid-js/web";
|
|
361
|
+
import { Nav } from "~/components/Nav";
|
|
362
|
+
import "./app.css";
|
|
363
|
+
|
|
364
|
+
const RootLayout = (props: ParentProps) => {
|
|
365
|
+
const location = useLocation();
|
|
366
|
+
const locale = () => getLocaleFromPath(location.pathname) ?? defaultLocale;
|
|
367
|
+
|
|
368
|
+
// 服务端在 entry-server.tsx 中渲染 <html>;
|
|
369
|
+
// 语言之间的客户端导航必须自行更新这些属性。
|
|
370
|
+
createEffect(() => {
|
|
371
|
+
if (isServer) return;
|
|
372
|
+
|
|
373
|
+
document.documentElement.lang = locale();
|
|
374
|
+
document.documentElement.dir = getHTMLTextDir(locale());
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<MetaProvider>
|
|
379
|
+
<IntlayerProvider locale={locale()}>
|
|
380
|
+
<Nav />
|
|
381
|
+
<Suspense>{props.children}</Suspense>
|
|
382
|
+
</IntlayerProvider>
|
|
383
|
+
</MetaProvider>
|
|
384
|
+
);
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
export default function App() {
|
|
388
|
+
return (
|
|
389
|
+
<Router root={RootLayout}>
|
|
390
|
+
<FileRoutes />
|
|
391
|
+
</Router>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
> `IntlayerProvider` 会对其 `locale` prop 作出响应,因此在 JSX 中传递访问器调用 `locale()` 就足够了 —— Solid 会将其编译为一个 getter,当 URL 改变时整个树都会以新语言重新渲染。
|
|
397
|
+
|
|
398
|
+
</Step>
|
|
399
|
+
|
|
400
|
+
<Step number={7} title="在服务端设置 HTML 的 lang 和 dir 属性">
|
|
401
|
+
|
|
402
|
+
`<html>` 元素由 `entry-server.tsx` 在 `Router` 之外渲染。改为从请求 URL 读取语言 locale:
|
|
403
|
+
|
|
404
|
+
```tsx fileName="src/entry-server.tsx" codeFormat="typescript"
|
|
405
|
+
// @refresh reload
|
|
406
|
+
import { createHandler, StartServer } from "@solidjs/start/server";
|
|
407
|
+
import { defaultLocale, getHTMLTextDir, getLocaleFromPath } from "intlayer";
|
|
408
|
+
import { getRequestEvent } from "solid-js/web";
|
|
409
|
+
|
|
410
|
+
export default createHandler(() => (
|
|
411
|
+
<StartServer
|
|
412
|
+
document={({ assets, children, scripts }) => {
|
|
413
|
+
const url = getRequestEvent()?.request.url ?? "/";
|
|
414
|
+
const locale = getLocaleFromPath(url) ?? defaultLocale;
|
|
415
|
+
|
|
416
|
+
return (
|
|
417
|
+
<html dir={getHTMLTextDir(locale)} lang={locale}>
|
|
418
|
+
<head>
|
|
419
|
+
<meta charset="utf-8" />
|
|
420
|
+
<meta
|
|
421
|
+
name="viewport"
|
|
422
|
+
content="width=device-width, initial-scale=1"
|
|
423
|
+
/>
|
|
424
|
+
<link rel="icon" href="/favicon.ico" />
|
|
425
|
+
{assets}
|
|
426
|
+
</head>
|
|
427
|
+
<body>
|
|
428
|
+
<div id="app">{children}</div>
|
|
429
|
+
{scripts}
|
|
430
|
+
</body>
|
|
431
|
+
</html>
|
|
432
|
+
);
|
|
433
|
+
}}
|
|
434
|
+
/>
|
|
435
|
+
));
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
网络爬虫现在可以在首个字节接收到正确的语言:
|
|
439
|
+
|
|
440
|
+
```html
|
|
441
|
+
<html dir="ltr" lang="fr"></html>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
</Step>
|
|
445
|
+
|
|
446
|
+
<Step number={8} title="在页面中使用 Intlayer">
|
|
447
|
+
|
|
448
|
+
在整个应用程序中访问你的内容字典:
|
|
449
|
+
|
|
450
|
+
```tsx fileName="src/routes/[[locale]]/index.tsx" codeFormat="typescript"
|
|
451
|
+
import { Meta, Title } from "@solidjs/meta";
|
|
452
|
+
import { useIntlayer } from "solid-intlayer";
|
|
453
|
+
import Counter from "~/components/Counter";
|
|
454
|
+
|
|
455
|
+
export default function Home() {
|
|
456
|
+
const content = useIntlayer("home-page");
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<main>
|
|
460
|
+
<Title>{content.metaTitle.value}</Title>
|
|
461
|
+
<Meta content={content.metaDescription.value} name="description" />
|
|
462
|
+
<h1>{content.title}</h1>
|
|
463
|
+
<Counter />
|
|
464
|
+
<p>{content.documentation}</p>
|
|
465
|
+
</main>
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
> 在 Solid 中,`useIntlayer` 返回响应式内容(例如 `content`)。你可以直接访问其属性。
|
|
471
|
+
|
|
472
|
+
> 如果你想在 `string` 属性中使用内容,例如 `alt`、`title`、`href`、`aria-label` 等,可以使用该函数的值,如下所示:
|
|
473
|
+
>
|
|
474
|
+
> ```html
|
|
475
|
+
> <img src="{content.image.src.value}" alt="{content.image.value}" />
|
|
476
|
+
> <img src="{content.image.src.toString()}" alt="{content.image.toString()}" />
|
|
477
|
+
> <img src="{String(content.image.src)}" alt="{String(content.image)}" />
|
|
478
|
+
> ```
|
|
479
|
+
|
|
480
|
+
> 要了解有关 `useIntlayer` 钩子的更多信息,请参阅[文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/solid-intlayer/useIntlayer.md)。
|
|
481
|
+
|
|
482
|
+
内容节点不仅限于纯文本翻译。例如复数形式的计数器:
|
|
483
|
+
|
|
484
|
+
```typescript fileName="src/components/Counter.content.ts" codeFormat="typescript"
|
|
485
|
+
import { type Dictionary, plural, t } from "intlayer";
|
|
486
|
+
|
|
487
|
+
const counterContent = {
|
|
488
|
+
key: "counter",
|
|
489
|
+
content: {
|
|
490
|
+
clicks: plural({
|
|
491
|
+
one: t({
|
|
492
|
+
en: "{{count}} click",
|
|
493
|
+
fr: "{{count}} clic",
|
|
494
|
+
es: "{{count}} clic",
|
|
495
|
+
}),
|
|
496
|
+
other: t({
|
|
497
|
+
en: "{{count}} clicks",
|
|
498
|
+
fr: "{{count}} clics",
|
|
499
|
+
es: "{{count}} clics",
|
|
500
|
+
}),
|
|
501
|
+
}),
|
|
502
|
+
},
|
|
503
|
+
} satisfies Dictionary;
|
|
504
|
+
|
|
505
|
+
export default counterContent;
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
```tsx fileName="src/components/Counter.tsx" codeFormat="typescript"
|
|
509
|
+
import { useIntlayer } from "solid-intlayer";
|
|
510
|
+
import { createSignal } from "solid-js";
|
|
511
|
+
|
|
512
|
+
export default function Counter() {
|
|
513
|
+
const [count, setCount] = createSignal(0);
|
|
514
|
+
const content = useIntlayer("counter");
|
|
515
|
+
|
|
516
|
+
return (
|
|
517
|
+
<button onClick={() => setCount(count() + 1)} type="button">
|
|
518
|
+
{content.clicks(count())}
|
|
519
|
+
</button>
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
`plural()` 通过针对当前语言的 `Intl.PluralRules` 选择类别,因此拥有两种以上复数形式的语言无需任何额外代码即可工作。
|
|
525
|
+
|
|
526
|
+
</Step>
|
|
527
|
+
|
|
528
|
+
<Step number={9} title="创建本地化链接组件">
|
|
529
|
+
|
|
530
|
+
创建自定义 `Link` 组件,它会自动向内部 URL 添加当前语言的前缀:
|
|
531
|
+
|
|
532
|
+
```tsx fileName="src/components/LocalizedLink.tsx" codeFormat="typescript"
|
|
533
|
+
import { A, type AnchorProps } from "@solidjs/router";
|
|
534
|
+
import { getLocalizedUrl } from "intlayer";
|
|
535
|
+
import { useLocale } from "solid-intlayer";
|
|
536
|
+
import type { ParentComponent } from "solid-js";
|
|
537
|
+
|
|
538
|
+
export const LocalizedLink: ParentComponent<AnchorProps> = (props) => {
|
|
539
|
+
const { locale } = useLocale();
|
|
540
|
+
|
|
541
|
+
const isExternal = () => /^[a-z][a-z0-9+.-]*:/i.test(props.href);
|
|
542
|
+
|
|
543
|
+
const localizedHref = () =>
|
|
544
|
+
isExternal() ? props.href : getLocalizedUrl(props.href, locale());
|
|
545
|
+
|
|
546
|
+
return <A {...props} href={localizedHref()} />;
|
|
547
|
+
};
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
```tsx fileName="src/components/Nav.tsx" codeFormat="typescript"
|
|
551
|
+
import { useIntlayer } from "solid-intlayer";
|
|
552
|
+
import type { Component } from "solid-js";
|
|
553
|
+
import { LocaleSwitcher } from "./LocaleSwitcher";
|
|
554
|
+
import { LocalizedLink } from "./LocalizedLink";
|
|
555
|
+
|
|
556
|
+
export const Nav: Component = () => {
|
|
557
|
+
const content = useIntlayer("nav");
|
|
558
|
+
|
|
559
|
+
return (
|
|
560
|
+
<nav>
|
|
561
|
+
<LocalizedLink href="/">{content.home}</LocalizedLink>
|
|
562
|
+
<LocalizedLink href="/about">{content.about}</LocalizedLink>
|
|
563
|
+
<LocaleSwitcher />
|
|
564
|
+
</nav>
|
|
565
|
+
);
|
|
566
|
+
};
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
现在只需编写一次 `href="/about"`,即可根据活动语言生成 `/about`、`/fr/about` 或 `/es/about` —— 页面中的任何位置都无需手动添加前缀。
|
|
570
|
+
|
|
571
|
+
</Step>
|
|
572
|
+
|
|
573
|
+
<Step number={10} title="创建语言切换器组件">
|
|
574
|
+
|
|
575
|
+
将切换器渲染为**真实的 <a> 锚点**而非 `<select>`:当前页面的每种语言都会变为可爬取的链接,并且可以在新标签页中打开,这是仅依靠 JavaScript 的控件无法提供的。
|
|
576
|
+
|
|
577
|
+
`getPathWithoutLocale` 会从当前路径中剥离语言段,而 `getLocalizedUrl` 会为目标语言重新构建它,因此这些链接会遵循你的路由模式,无需硬编码任何内容。导航是改变渲染语言的原因 —— `[[locale]]` 路由从 URL 中推导语言 —— 而 `setLocale` 会将选择保存在 `INTLAYER_LOCALE` cookie 中,以便以后访问无语言前缀的 URL 时能解析为相同的语言。
|
|
578
|
+
|
|
579
|
+
```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat={["typescript", "esm"]}
|
|
580
|
+
import { A, useLocation } from "@solidjs/router";
|
|
581
|
+
import {
|
|
582
|
+
getHTMLTextDir,
|
|
583
|
+
getLocaleName,
|
|
584
|
+
getLocalizedUrl,
|
|
585
|
+
getPathWithoutLocale,
|
|
586
|
+
} from "intlayer";
|
|
587
|
+
import { useIntlayer, useLocale } from "solid-intlayer";
|
|
588
|
+
import { type Component, For } from "solid-js";
|
|
589
|
+
|
|
590
|
+
export const LocaleSwitcher: Component = () => {
|
|
591
|
+
const content = useIntlayer("locale-switcher");
|
|
592
|
+
const location = useLocation();
|
|
593
|
+
const { locale, setLocale, availableLocales } = useLocale();
|
|
594
|
+
|
|
595
|
+
// 当前显示页面的规范(无语言段)路径
|
|
596
|
+
const pathWithoutLocale = () => getPathWithoutLocale(location.pathname);
|
|
597
|
+
|
|
598
|
+
return (
|
|
599
|
+
<div>
|
|
600
|
+
<button
|
|
601
|
+
aria-label={content.label.value}
|
|
602
|
+
popoverTarget="localePopover"
|
|
603
|
+
type="button"
|
|
604
|
+
>
|
|
605
|
+
{getLocaleName(locale())}
|
|
606
|
+
</button>
|
|
607
|
+
<div id="localePopover" popover="auto">
|
|
608
|
+
<For each={availableLocales}>
|
|
609
|
+
{(localeItem) => (
|
|
610
|
+
<A
|
|
611
|
+
dir={getHTMLTextDir(localeItem)}
|
|
612
|
+
// 仅精确定位,使默认语言链接不会在每个页面上都被标记为 active
|
|
613
|
+
end
|
|
614
|
+
href={getLocalizedUrl(pathWithoutLocale(), localeItem)}
|
|
615
|
+
hreflang={localeItem}
|
|
616
|
+
lang={localeItem}
|
|
617
|
+
onClick={() => setLocale(localeItem)}
|
|
618
|
+
// 确保浏览器的“后退”按钮返回到上一页
|
|
619
|
+
replace
|
|
620
|
+
>
|
|
621
|
+
{/* 各自语言下的语言名称 - 例如 Français */}
|
|
622
|
+
{getLocaleName(localeItem)}
|
|
623
|
+
</A>
|
|
624
|
+
)}
|
|
625
|
+
</For>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
);
|
|
629
|
+
};
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
> 在 Solid 中,来自 `useLocale` 的 `locale` 是一个 **signal 访问器**。使用带有括号的 `locale()` 响应式地读取其当前值。
|
|
633
|
+
>
|
|
634
|
+
> `getLocaleName(localeItem)` 会以各自的语言渲染每种语言名称 —— `English / Français / Español`。传递第二个参数可以将其翻译为当前显示语言:例如 `getLocaleName(localeItem, locale())` 在英语中为 `English / French / Spanish`,在法语中为 `anglais / français / espagnol`。
|
|
635
|
+
>
|
|
636
|
+
> `<A>` 已经在匹配当前 URL 的链接上设置了 `aria-current="page"`,因此无需额外添加处理。`replace` 由路由器从渲染的属性中读取:它会替换历史记录条目而不是推入新条目,因此浏览器的“后退”按钮会返回切换前访问的页面,而不是返回前一种语言的同一页面。
|
|
637
|
+
>
|
|
638
|
+
> 每个链接上的 `dir` 和 `hreflang` 属性可使从右到左的语言名称保持正确的方向,并告知辅助技术和网络爬虫每个链接指向哪种语言。
|
|
639
|
+
>
|
|
640
|
+
> 要了解有关 `useLocale` 钩子的更多信息,请参阅[文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/solid-intlayer/useLocale.md)。
|
|
641
|
+
|
|
642
|
+
</Step>
|
|
643
|
+
|
|
644
|
+
<Step number={11} title="生成规范 canonical 和 hreflang 链接" isOptional={true}>
|
|
645
|
+
|
|
646
|
+
`hreflang` 注释告知搜索引擎 `/about`、`/fr/about` 和 `/es/about` 是不同语言下的同一个页面。`getMultilingualUrls` 根据你的路由模式从规范(无语言段)路径中导出它们,因此无需硬编码任何内容:
|
|
647
|
+
|
|
648
|
+
```tsx fileName="src/components/AlternateLinks.tsx" codeFormat="typescript"
|
|
649
|
+
import {
|
|
650
|
+
defaultLocale,
|
|
651
|
+
getMultilingualUrls,
|
|
652
|
+
getPathWithoutLocale,
|
|
653
|
+
} from "intlayer";
|
|
654
|
+
import { type Component, For } from "solid-js";
|
|
655
|
+
|
|
656
|
+
export type AlternateLinksProps = {
|
|
657
|
+
/** 正在渲染的页面的绝对 URL。 */
|
|
658
|
+
url: string;
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
export const AlternateLinks: Component<AlternateLinksProps> = (props) => {
|
|
662
|
+
const multilingualUrls = () => {
|
|
663
|
+
const { origin, pathname } = new URL(props.url);
|
|
664
|
+
|
|
665
|
+
return Object.entries(
|
|
666
|
+
getMultilingualUrls(`${origin}${getPathWithoutLocale(pathname)}`)
|
|
667
|
+
);
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
const canonicalUrl = () =>
|
|
671
|
+
new URL(props.url).origin + new URL(props.url).pathname;
|
|
672
|
+
|
|
673
|
+
return (
|
|
674
|
+
<>
|
|
675
|
+
<link href={canonicalUrl()} rel="canonical" />
|
|
676
|
+
<For each={multilingualUrls()}>
|
|
677
|
+
{([locale, localizedUrl]) => (
|
|
678
|
+
<link href={localizedUrl} hreflang={locale} rel="alternate" />
|
|
679
|
+
)}
|
|
680
|
+
</For>
|
|
681
|
+
<link
|
|
682
|
+
href={
|
|
683
|
+
multilingualUrls().find(([locale]) => locale === defaultLocale)?.[1]
|
|
684
|
+
}
|
|
685
|
+
hreflang="x-default"
|
|
686
|
+
rel="alternate"
|
|
687
|
+
/>
|
|
688
|
+
</>
|
|
689
|
+
);
|
|
690
|
+
};
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
在可获取请求 URL 的文档 head 中渲染它:
|
|
694
|
+
|
|
695
|
+
```tsx fileName="src/entry-server.tsx" codeFormat="typescript"
|
|
696
|
+
import { AlternateLinks } from "~/components/AlternateLinks";
|
|
697
|
+
|
|
698
|
+
// … 在 <head> 内部,在其他 meta 标签旁边:
|
|
699
|
+
<AlternateLinks url={url} />;
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
随后 `GET /fr/about` 将响应:
|
|
703
|
+
|
|
704
|
+
```html
|
|
705
|
+
<link href="https://example.com/fr/about" rel="canonical" />
|
|
706
|
+
<link href="https://example.com/about" hreflang="en" rel="alternate" />
|
|
707
|
+
<link href="https://example.com/fr/about" hreflang="fr" rel="alternate" />
|
|
708
|
+
<link href="https://example.com/es/about" hreflang="es" rel="alternate" />
|
|
709
|
+
<link href="https://example.com/about" hreflang="x-default" rel="alternate" />
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
> **关于 `@solidjs/meta` 的注意事项**:在撰写本文时,`@solidjs/meta` 中的 `<Title>` 和 `<Meta>` 在客户端水化后应用,但**不会**发散到 SolidStart v2 的服务端渲染 `<head>` 中。在 upstream 修复此问题之前,请直接在 `entry-server.tsx` 中渲染爬虫无需 JavaScript 即可看到的标签 —— `canonical`、`hreflang` 以及需要的 `title` / `description`,如上所示。
|
|
713
|
+
|
|
714
|
+
</Step>
|
|
715
|
+
|
|
716
|
+
<Step number={12} title="处理未找到 (404) 页面" isOptional={true}>
|
|
717
|
+
|
|
718
|
+
处于 `src/routes` 根目录的通配符路由(splat route)可以捕获语言段未匹配到的所有路径 —— 包括被 `matchFilters` 拒绝的无效语言前缀。由于语言仍通过根布局来自 URL,因此 404 页面将以访问者的语言显示:
|
|
719
|
+
|
|
720
|
+
```tsx fileName="src/routes/[...404].tsx" codeFormat="typescript"
|
|
721
|
+
import { Title } from "@solidjs/meta";
|
|
722
|
+
import { HttpStatusCode } from "@solidjs/start";
|
|
723
|
+
import { useIntlayer } from "solid-intlayer";
|
|
724
|
+
import { LocalizedLink } from "~/components/LocalizedLink";
|
|
725
|
+
|
|
726
|
+
export default function NotFound() {
|
|
727
|
+
const content = useIntlayer("not-found-page");
|
|
728
|
+
|
|
729
|
+
return (
|
|
730
|
+
<main>
|
|
731
|
+
<Title>{content.metaTitle.value}</Title>
|
|
732
|
+
<HttpStatusCode code={404} />
|
|
733
|
+
<h1>{content.title}</h1>
|
|
734
|
+
<LocalizedLink href="/">{content.backHome}</LocalizedLink>
|
|
735
|
+
</main>
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
| 请求 | 预期响应 |
|
|
741
|
+
| ----------------- | ----------------------------------- |
|
|
742
|
+
| `/xx` | `404` — `xx` 不是已配置的语言 |
|
|
743
|
+
| `/nonexistent` | 默认语言下的 `404` |
|
|
744
|
+
| `/fr/nonexistent` | 法语下的 `404` (`Page introuvable`) |
|
|
745
|
+
|
|
746
|
+
</Step>
|
|
747
|
+
|
|
748
|
+
<Step number={13} title="生成多语言 sitemap 站点地图" isOptional={true}>
|
|
749
|
+
|
|
750
|
+
Intlayer 的 sitemap 生成器将每个路径扩展为每个语言对应一个条目,并在它们之间连接 `xhtml:link` 备用链接,因此路由只需列出规范的、无语言前缀的路径。
|
|
751
|
+
|
|
752
|
+
> 与仅生成平铺 URL 的基础生成器不同,Intlayer 在每个页面的每个本地化变体之间建立双向链接,这有助于搜索引擎关联本地化 URL 并将正确的页面提供给正确的受众。
|
|
753
|
+
|
|
754
|
+
SolidStart 将导出 HTTP 方法的文件转换为 API 路由,并从路径中剥离 `.ts` 扩展名 —— 因此 `src/routes/sitemap.xml.ts` 在 `/sitemap.xml` 处提供服务:
|
|
755
|
+
|
|
756
|
+
```typescript fileName="src/routes/sitemap.xml.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
757
|
+
import type { APIEvent } from "@solidjs/start/server";
|
|
758
|
+
import { generateSitemap } from "intlayer";
|
|
759
|
+
|
|
760
|
+
const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000";
|
|
761
|
+
|
|
762
|
+
export const GET = (_event: APIEvent) => {
|
|
763
|
+
const sitemap = generateSitemap(
|
|
764
|
+
[
|
|
765
|
+
{ path: "/", changefreq: "daily", priority: 1.0 },
|
|
766
|
+
{ path: "/about", changefreq: "monthly", priority: 0.8 },
|
|
767
|
+
],
|
|
768
|
+
{ siteUrl: SITE_URL }
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
return new Response(sitemap, {
|
|
772
|
+
headers: { "Content-Type": "application/xml" },
|
|
773
|
+
});
|
|
774
|
+
};
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
```xml fileName="output of GET /sitemap.xml"
|
|
778
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
779
|
+
<urlset
|
|
780
|
+
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
781
|
+
xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
782
|
+
>
|
|
783
|
+
<url>
|
|
784
|
+
<loc>https://example.com/about</loc>
|
|
785
|
+
<changefreq>monthly</changefreq>
|
|
786
|
+
<priority>0.8</priority>
|
|
787
|
+
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/about"/>
|
|
788
|
+
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/about"/>
|
|
789
|
+
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/about"/>
|
|
790
|
+
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about"/>
|
|
791
|
+
</url>
|
|
792
|
+
</urlset>
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
> API 路由不支持可选参数,因此请将此文件保留在 `src/routes` 的根目录下,置于 `[[locale]]` 段之外。sitemap 已经包含了每种语言。
|
|
796
|
+
|
|
797
|
+
你可以使用 `getMultilingualUrls` 以相同方式构建 `robots.txt`,以便 `Disallow` 条目涵盖敏感路径的每个本地化拼写:
|
|
798
|
+
|
|
799
|
+
```typescript fileName="src/routes/robots.txt.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
800
|
+
import { getMultilingualUrls } from "intlayer";
|
|
801
|
+
|
|
802
|
+
const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000";
|
|
803
|
+
|
|
804
|
+
const disallowedPaths = ["/admin", "/private"].flatMap((path) =>
|
|
805
|
+
Object.values(getMultilingualUrls(path))
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
export const GET = () =>
|
|
809
|
+
new Response(
|
|
810
|
+
[
|
|
811
|
+
"User-agent: *",
|
|
812
|
+
"Allow: /",
|
|
813
|
+
...disallowedPaths.map((path) => `Disallow: ${path}`),
|
|
814
|
+
"",
|
|
815
|
+
`Sitemap: ${SITE_URL}/sitemap.xml`,
|
|
816
|
+
].join("\n"),
|
|
817
|
+
{ headers: { "Content-Type": "text/plain" } }
|
|
818
|
+
);
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
</Step>
|
|
822
|
+
|
|
823
|
+
<Step number={14} title="在服务端函数中检索语言 locale" isOptional={true}>
|
|
824
|
+
|
|
825
|
+
你可能希望在服务端函数或 API 路由内部访问当前语言 locale。
|
|
826
|
+
|
|
827
|
+
在像这样基于前缀的设置中,**URL 具有权威性**:`getLocaleFromPath` 从请求 URL 中读取前缀。`getLocale` 是不带语言前缀的请求的回退机制 —— 它会检查 `INTLAYER_LOCALE` cookie,然后检查 `x-intlayer-locale` 请求头,接着协商 `Accept-Language`。
|
|
828
|
+
|
|
829
|
+
```tsx fileName="src/routes/[[locale]]/index.tsx" codeFormat="typescript"
|
|
830
|
+
import { createAsync } from "@solidjs/router";
|
|
831
|
+
import { getCookie, getIntlayer, getLocale, getLocaleFromPath } from "intlayer";
|
|
832
|
+
import { getRequestEvent } from "solid-js/web";
|
|
833
|
+
|
|
834
|
+
const loadLocalizedData = async () => {
|
|
835
|
+
"use server";
|
|
836
|
+
|
|
837
|
+
const request = getRequestEvent()?.request;
|
|
838
|
+
|
|
839
|
+
const locale =
|
|
840
|
+
getLocaleFromPath(request?.url) ??
|
|
841
|
+
(await getLocale({
|
|
842
|
+
// 从请求中获取 cookie (默认为 'INTLAYER_LOCALE')
|
|
843
|
+
getCookie: (name) =>
|
|
844
|
+
getCookie(name, request?.headers.get("cookie") ?? ""),
|
|
845
|
+
// 从请求中获取 header (默认为 'x-intlayer-locale'),
|
|
846
|
+
// 回退到 Accept-Language 协商
|
|
847
|
+
getHeader: (name) => request?.headers.get(name) ?? undefined,
|
|
848
|
+
}));
|
|
849
|
+
|
|
850
|
+
// 使用 getIntlayer() 在组件外部检索部分内容
|
|
851
|
+
const content = getIntlayer("home-page", locale);
|
|
852
|
+
|
|
853
|
+
return { locale, title: String(content.title) };
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
export default function Page() {
|
|
857
|
+
const data = createAsync(() => loadLocalizedData());
|
|
858
|
+
|
|
859
|
+
return <p>{data()?.title}</p>;
|
|
860
|
+
}
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
> 此处不要仅依赖 `getLocale`:仅当访问者主动切换语言时才会写入语言 cookie,因此首次访问 `/fr/...` 将会被解析为默认语言。
|
|
864
|
+
|
|
865
|
+
</Step>
|
|
866
|
+
|
|
867
|
+
<Step number={15} title="提取组件的内容" isOptional={true}>
|
|
868
|
+
|
|
869
|
+
如果你有一个现有的代码库,转换数千个文件可能会非常耗时。
|
|
870
|
+
|
|
871
|
+
为了简化此过程,Intlayer 提议使用 [编译器 (compiler)](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/compiler.md) / [提取器 (extractor)](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/cli/extract.md) 来转换组件并提取内容。
|
|
872
|
+
|
|
873
|
+
要进行设置,你可以在 `intlayer.config.ts` 文件中添加一个 `compiler` 部分:
|
|
874
|
+
|
|
875
|
+
```typescript fileName="intlayer.config.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
876
|
+
import { type IntlayerConfig } from "intlayer";
|
|
877
|
+
|
|
878
|
+
const config: IntlayerConfig = {
|
|
879
|
+
// ... 其他配置
|
|
880
|
+
compiler: {
|
|
881
|
+
/**
|
|
882
|
+
* 指示是否启用编译器。
|
|
883
|
+
*/
|
|
884
|
+
enabled: true,
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* 定义输出文件路径
|
|
888
|
+
*/
|
|
889
|
+
output: ({ fileName, extension }) => `./${fileName}${extension}`,
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* 指示在转换组件后是否应保存组件。
|
|
893
|
+
*
|
|
894
|
+
* - 如果为 `true`,编译器将在磁盘中重写组件文件。因此转换将是永久性的,编译器在下一次进程中将跳过该转换。通过这种方式,编译器可以转换应用,然后将其移除。
|
|
895
|
+
*
|
|
896
|
+
* - 如果为 `false`,编译器将仅在构建输出的代码中注入 `useIntlayer()` 函数调用,并保持基础代码库完好。转换将仅在内存中完成。
|
|
897
|
+
*/
|
|
898
|
+
saveComponents: false,
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* 字典键前缀
|
|
902
|
+
*/
|
|
903
|
+
dictionaryKeyPrefix: "",
|
|
904
|
+
},
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
export default config;
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
<Tabs>
|
|
911
|
+
<Tab value='提取命令'>
|
|
912
|
+
|
|
913
|
+
运行提取器以转换组件并提取内容
|
|
914
|
+
|
|
915
|
+
```bash packageManager="npm"
|
|
916
|
+
npx intlayer extract
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
```bash packageManager="pnpm"
|
|
920
|
+
pnpm intlayer extract
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
```bash packageManager="yarn"
|
|
924
|
+
yarn intlayer extract
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
```bash packageManager="bun"
|
|
928
|
+
bun x intlayer extract
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
> 之后,将生成的页面内容文件移出 `src/routes`,原因如步骤 5 所述。
|
|
932
|
+
|
|
933
|
+
</Tab>
|
|
934
|
+
<Tab value='Babel 编译器'>
|
|
935
|
+
|
|
936
|
+
> 从 v9 开始,`intlayerCompiler` 已包含在 `intlayer` 插件中。因此你无需手动添加它。
|
|
937
|
+
|
|
938
|
+
更新你的 `vite.config.ts` 以包含 `intlayerCompiler` 插件:
|
|
939
|
+
|
|
940
|
+
```ts fileName="vite.config.ts"
|
|
941
|
+
import { solidStart } from "@solidjs/start/config";
|
|
942
|
+
import { nitro } from "nitro/vite";
|
|
943
|
+
import { defineConfig } from "vite";
|
|
944
|
+
import { intlayer, intlayerCompiler } from "vite-intlayer";
|
|
945
|
+
|
|
946
|
+
export default defineConfig({
|
|
947
|
+
plugins: [
|
|
948
|
+
solidStart({ middleware: "src/middleware.ts" }),
|
|
949
|
+
nitro(),
|
|
950
|
+
intlayer(),
|
|
951
|
+
intlayerCompiler(), // 添加编译器插件
|
|
952
|
+
],
|
|
953
|
+
});
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
```bash packageManager="npm"
|
|
957
|
+
npm run build # 或 npm run dev
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
```bash packageManager="pnpm"
|
|
961
|
+
pnpm run build # 或 pnpm run dev
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
```bash packageManager="yarn"
|
|
965
|
+
yarn build # 或 yarn dev
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
```bash packageManager="bun"
|
|
969
|
+
bun run build # 或 bun run dev
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
</Tab>
|
|
973
|
+
</Tabs>
|
|
974
|
+
|
|
975
|
+
</Step>
|
|
976
|
+
|
|
977
|
+
<Step number={16} title="配置 TypeScript">
|
|
978
|
+
|
|
979
|
+
Intlayer 使用模块增强 (module augmentation) 来获得 TypeScript 的优势并增强你的代码库。
|
|
980
|
+
|
|
981
|
+
确保你的 TypeScript 配置包含自动生成的类型:
|
|
982
|
+
|
|
983
|
+
```json5 fileName="tsconfig.json"
|
|
984
|
+
{
|
|
985
|
+
compilerOptions: {
|
|
986
|
+
// ... 你现有的配置
|
|
987
|
+
},
|
|
988
|
+
include: [
|
|
989
|
+
"src",
|
|
990
|
+
"*.ts",
|
|
991
|
+
".intlayer/**/*.ts", // 包含自动生成的类型
|
|
992
|
+
],
|
|
993
|
+
}
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
字典键和内容路径现在会在编译时进行检查:
|
|
997
|
+
|
|
998
|
+
```tsx
|
|
999
|
+
useIntlayer("home-page"); // ✅
|
|
1000
|
+
useIntlayer("hom-page"); // ❌ Argument of type '"hom-page"' is not assignable to parameter of type 'keyof __DictionaryRegistry'
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
</Step>
|
|
1004
|
+
|
|
1005
|
+
</Steps>
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
## 验证你的设置
|
|
1010
|
+
|
|
1011
|
+
构建并启动服务器,然后检查这些请求是否按预期运行:
|
|
1012
|
+
|
|
1013
|
+
```bash
|
|
1014
|
+
npm run build
|
|
1015
|
+
node .output/server/index.mjs
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
| 请求 | 预期响应 |
|
|
1019
|
+
| ---------------------------------------- | -------------------------------- |
|
|
1020
|
+
| `GET /` | `200` — 英语 |
|
|
1021
|
+
| `GET /` 带有 `Accept-Language: fr` | `302` → `/fr` |
|
|
1022
|
+
| `GET /` 带有 cookie `INTLAYER_LOCALE=es` | `302` → `/es` |
|
|
1023
|
+
| `GET /fr` | `200` — 法语, `<html lang="fr">` |
|
|
1024
|
+
| `GET /fr/about` | `200` — 法语关于页面 |
|
|
1025
|
+
| `GET /en/about` | `302` → `/about` (规范重定向) |
|
|
1026
|
+
| `GET /xx` | `404` |
|
|
1027
|
+
| `GET /fr/nonexistent` | `404` 法语 |
|
|
1028
|
+
| `GET /sitemap.xml` | `200` — 多语言 XML sitemap |
|
|
1029
|
+
|
|
1030
|
+
在 `vite dev` 下渲染页面的行行为相同。除非你自己将句柄注册为中间件,否则三个重定向行仅适用于构建后的服务器 —— 参见步骤 3。
|
|
1031
|
+
|
|
1032
|
+
> 请在 Node (`vite dev`) 上运行开发服务器,而不是在 Bun (`bun --bun vite dev`) 上:SolidStart 的 SSR 目前在 Bun 运行时下会失败并显示 `Expected a Response object, but received 'NodeResponse'`。这与 Intlayer 无关 —— 它在纯模板上也会复现 —— 并且只影响开发服务器,不影响 `vite build`。
|
|
1033
|
+
|
|
1034
|
+
---
|
|
1035
|
+
|
|
1036
|
+
## Git 配置
|
|
1037
|
+
|
|
1038
|
+
建议忽略由 Intlayer 生成的文件。这可以让你避免将它们提交到 Git 仓库。
|
|
1039
|
+
|
|
1040
|
+
为此,你可以将以下指令添加到你的 `.gitignore` 文件中:
|
|
1041
|
+
|
|
1042
|
+
```plaintext fileName=".gitignore"
|
|
1043
|
+
# 忽略 Intlayer 生成的文件
|
|
1044
|
+
.intlayer
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
---
|
|
1048
|
+
|
|
1049
|
+
## VS Code 插件
|
|
1050
|
+
|
|
1051
|
+
为了提升你使用 Intlayer 的开发体验,你可以安装官方的 **Intlayer VS Code 插件**。
|
|
1052
|
+
|
|
1053
|
+
[从 VS Code 插件市场安装](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1054
|
+
|
|
1055
|
+
此插件提供:
|
|
1056
|
+
|
|
1057
|
+
- 翻译键的**自动补全**。
|
|
1058
|
+
- 缺失翻译的**实时错误检测**。
|
|
1059
|
+
- 翻译内容的**行内预览**。
|
|
1060
|
+
- 轻松创建和更新翻译的**快速操作**。
|
|
1061
|
+
|
|
1062
|
+
---
|
|
1063
|
+
|
|
1064
|
+
## 深入了解
|
|
1065
|
+
|
|
1066
|
+
要进一步了解,你可以实现[可视化编辑器](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_visual_editor.md)或使用 [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/intlayer_CMS.md) 外包你的内容。
|
|
1067
|
+
|
|
1068
|
+
---
|
|
1069
|
+
|
|
1070
|
+
## 文档参考
|
|
1071
|
+
|
|
1072
|
+
- [Intlayer 文档](https://intlayer.org)
|
|
1073
|
+
- [SolidStart 文档](https://start.solidjs.com)
|
|
1074
|
+
- [useIntlayer 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/solid-intlayer/useIntlayer.md)
|
|
1075
|
+
- [useLocale 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/solid-intlayer/useLocale.md)
|
|
1076
|
+
- [内容声明](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/content_file.md)
|
|
1077
|
+
- [配置](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/configuration.md)
|