@intlayer/docs 9.1.3 → 9.2.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/rag_powered_documentation_assistant.md +7 -0
- package/blog/de/rag_powered_documentation_assistant.md +6 -0
- package/blog/en-GB/rag_powered_documentation_assistant.md +6 -0
- package/blog/es/rag_powered_documentation_assistant.md +7 -0
- package/blog/fr/i18n_meaning.md +7 -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/next-i18next_vs_next-intl_vs_intlayer.md +119 -42
- package/blog/hi/rag_powered_documentation_assistant.md +7 -0
- package/blog/id/rag_powered_documentation_assistant.md +6 -0
- package/blog/it/rag_powered_documentation_assistant.md +6 -0
- package/blog/ja/rag_powered_documentation_assistant.md +7 -0
- package/blog/ko/list_i18n_technologies/frameworks/react.md +7 -7
- package/blog/ko/rag_powered_documentation_assistant.md +6 -0
- package/blog/pl/rag_powered_documentation_assistant.md +6 -0
- package/blog/pt/rag_powered_documentation_assistant.md +6 -0
- 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/rag_powered_documentation_assistant.md +6 -0
- package/blog/vi/rag_powered_documentation_assistant.md +6 -0
- 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/generated/blog.entry.cjs +27 -27
- package/dist/cjs/generated/blog.entry.cjs.map +1 -1
- package/dist/cjs/generated/docs.entry.cjs +234 -214
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/cjs/generated/frequentQuestions.entry.cjs +17 -17
- package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
- package/dist/cjs/generated/legal.entry.cjs +2 -2
- package/dist/cjs/generated/legal.entry.cjs.map +1 -1
- package/dist/esm/generated/blog.entry.mjs +27 -27
- package/dist/esm/generated/blog.entry.mjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +234 -214
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/esm/generated/frequentQuestions.entry.mjs +17 -17
- package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
- package/dist/esm/generated/legal.entry.mjs +2 -2
- package/dist/esm/generated/legal.entry.mjs.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/docs/ar/analytics.md +222 -0
- package/docs/ar/autoFill.md +28 -9
- 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/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/configuration.md +25 -8
- package/docs/bn/intlayer_with_angular_21.md +9 -9
- 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/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/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/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-GB/analytics.md +222 -0
- package/docs/en-GB/autoFill.md +26 -7
- 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/configuration.md +25 -8
- package/docs/nl/intlayer_with_angular_21.md +9 -9
- package/docs/pl/analytics.md +220 -0
- 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/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/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/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/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/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/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/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/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/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/configuration.md +25 -8
- package/docs/ur/intlayer_with_angular_21.md +9 -9
- package/docs/vi/analytics.md +222 -0
- 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/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/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/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/intlayer_with_angular_21.md +7 -7
- package/package.json +6 -6
- package/src/generated/blog.entry.ts +27 -27
- package/src/generated/docs.entry.ts +234 -214
- package/src/generated/frequentQuestions.entry.ts +17 -17
- package/src/generated/legal.entry.ts +2 -2
|
@@ -0,0 +1,1078 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-08-06
|
|
3
|
+
updatedAt: 2026-08-06
|
|
4
|
+
title: "Solid Start i18n - Guía completa para traducir tu aplicación"
|
|
5
|
+
description: "Sin más i18next. La guía 2026 para crear una aplicación SolidStart multilingüe (i18n). Enrutamiento de locale renderizado en servidor, hreflang, sitemap y traducción asistida por IA."
|
|
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: "Historial inicial"
|
|
25
|
+
author: aymericzip
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Traduce tu sitio web SolidStart usando Intlayer | Internacionalización (i18n)
|
|
29
|
+
|
|
30
|
+
<Tabs defaultTab="video">
|
|
31
|
+
<Tab label="Vídeo" value="video">
|
|
32
|
+
|
|
33
|
+
<iframe title="¿La mejor solución i18n para Vite y Solid? Descubre 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="Código" 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="Demo CodeSandbox - Cómo internacionalizar tu aplicación usando 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
|
+
## Tabla de contenidos
|
|
50
|
+
|
|
51
|
+
<TOC/>
|
|
52
|
+
|
|
53
|
+
Esta guía cubre una aplicación SolidStart **renderizada en el servidor**: la detección de la locale ocurre en la solicitud, las páginas se renderizan en el servidor en el idioma correcto, y las señales `<html lang>`, `hreflang` y sitemap que los motores de búsqueda necesitan se emiten del lado del servidor.
|
|
54
|
+
|
|
55
|
+
## ¿Por qué Intlayer en lugar de alternativas?
|
|
56
|
+
|
|
57
|
+
En comparación con las soluciones principales como `@solid-primitives/i18n` o `i18next`, Intlayer es una solución que viene con optimizaciones integradas como:
|
|
58
|
+
|
|
59
|
+
<AccordionGroup>
|
|
60
|
+
|
|
61
|
+
<Accordion header="Cobertura completa de Solid">
|
|
62
|
+
|
|
63
|
+
Intlayer está optimizado para funcionar perfectamente con Solid ofreciendo **alcance de contenido a nivel de componente**, **traducciones reactivas**, y todas las funciones necesarias para escalar la internacionalización (i18n).
|
|
64
|
+
|
|
65
|
+
</Accordion>
|
|
66
|
+
|
|
67
|
+
<Accordion header="Tamaño del bundle">
|
|
68
|
+
|
|
69
|
+
En lugar de cargar archivos JSON masivos en tus páginas, carga solo el contenido necesario. Intlayer ayuda a **reducir los tamaños de tu bundle y de tus páginas hasta en un 50%**.
|
|
70
|
+
|
|
71
|
+
</Accordion>
|
|
72
|
+
|
|
73
|
+
<Accordion header="Mantenibilidad">
|
|
74
|
+
|
|
75
|
+
Limitar el alcance del contenido de tu aplicación **facilita el mantenimiento** para aplicaciones a gran escala. Puedes duplicar o eliminar una sola carpeta de funciones sin la carga mental de revisar toda tu base de código de contenido. Además, Intlayer está **completamente tipado** para garantizar la precisión de tu contenido.
|
|
76
|
+
|
|
77
|
+
</Accordion>
|
|
78
|
+
|
|
79
|
+
<Accordion header="Agente de IA">
|
|
80
|
+
|
|
81
|
+
Colocar el contenido en la misma ubicación **reduce el contexto necesario** para los grandes modelos de lenguaje (LLM). Intlayer también viene con una suite de herramientas, como un **CLI** para probar traducciones faltantes, **[LSP](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/lsp.md)**, **[MCP](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/mcp_server.md)** y **[habilidades de agente](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/agent_skills.md)**, para hacer la experiencia del desarrollador (DX) aún más fluida para los agentes de IA.
|
|
82
|
+
|
|
83
|
+
</Accordion>
|
|
84
|
+
|
|
85
|
+
<Accordion header="Automatización">
|
|
86
|
+
|
|
87
|
+
Usa la automatización para traducir en tu flujo de CI/CD utilizando el LLM de tu elección al costo de tu proveedor de IA. Intlayer también ofrece un **compilador** para automatizar la extracción de contenido, así como una [plataforma web](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/intlayer_CMS.md) para ayudar a **traducir en segundo plano**.
|
|
88
|
+
|
|
89
|
+
</Accordion>
|
|
90
|
+
|
|
91
|
+
<Accordion header="Rendimiento">
|
|
92
|
+
|
|
93
|
+
Conectar archivos JSON masivos a los componentes puede generar problemas de rendimiento y reactividad. Intlayer optimiza la carga de tu contenido en el momento de la compilación.
|
|
94
|
+
|
|
95
|
+
</Accordion>
|
|
96
|
+
|
|
97
|
+
<Accordion header="Escalar con no desarrolladores">
|
|
98
|
+
|
|
99
|
+
Más que una simple solución i18n, Intlayer proporciona un **[editor visual](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/intlayer_visual_editor.md) autoalojado** y un **[CMS completo](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/intlayer_CMS.md)** para ayudarte a gestionar tu contenido multilingüe en **tiempo real**, haciendo que la colaboración con traductores, redactores y otros miembros del equipo sea fluida. El contenido se puede almacenar de forma local y/o remota.
|
|
100
|
+
|
|
101
|
+
</Accordion>
|
|
102
|
+
</AccordionGroup>
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Guía paso a paso para configurar Intlayer en una aplicación SolidStart
|
|
107
|
+
|
|
108
|
+
<Steps>
|
|
109
|
+
|
|
110
|
+
<Step number={1} title="Instalar dependencias">
|
|
111
|
+
|
|
112
|
+
Instala los paquetes necesarios usando 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
|
+
> la marca `--interactive` es opcional. Usa `intlayer-cli init` si eres un agente de IA.
|
|
131
|
+
|
|
132
|
+
> Este comando detectará tu entorno e instalará los paquetes requeridos. Por ejemplo:
|
|
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
|
+
El paquete principal que proporciona herramientas de internacionalización para la gestión de configuración, traducción, [declaración de contenido](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/dictionary/content_file.md), transpilación y [comandos CLI](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/cli/index.md).
|
|
153
|
+
|
|
154
|
+
- **solid-intlayer**
|
|
155
|
+
|
|
156
|
+
El paquete que integra Intlayer con la aplicación Solid. Proporciona proveedores de contexto y hooks para la internacionalización de Solid.
|
|
157
|
+
|
|
158
|
+
- **vite-intlayer**
|
|
159
|
+
|
|
160
|
+
Incluye el plugin de Vite para integrar Intlayer con el [empaquetador Vite](https://vite.dev/guide/why.html#why-bundle-for-production), así como el controlador de enrutamiento de locale que detecta la locale preferida del usuario, gestiona las cookies y maneja la redirección de URL.
|
|
161
|
+
|
|
162
|
+
> `vite-intlayer` es una preocupación del lado del servidor aquí, no solo en tiempo de compilación: suministra el controlador de solicitudes que ejecuta el servidor Nitro de SolidStart. Mantenerlo en `dependencies` es la opción segura predeterminada; solo puedes moverlo a `devDependencies` si despliegas el directorio `.output` compilado, en el que Nitro incluye el controlador en línea.
|
|
163
|
+
|
|
164
|
+
</Step>
|
|
165
|
+
|
|
166
|
+
<Step number={2} title="Configuración de tu proyecto">
|
|
167
|
+
|
|
168
|
+
Crea un archivo de configuración para configurar los idiomas de tu aplicación:
|
|
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
|
+
// Tus otras locales
|
|
180
|
+
],
|
|
181
|
+
defaultLocale: Locales.ENGLISH,
|
|
182
|
+
},
|
|
183
|
+
routing: {
|
|
184
|
+
mode: "prefix-no-default",
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export default config;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Con `prefix-no-default`, la locale predeterminada se sirve desde URL sin prefijo:
|
|
192
|
+
|
|
193
|
+
```plaintext
|
|
194
|
+
/ /about → Inglés (locale predeterminada)
|
|
195
|
+
/fr /fr/about → Francés
|
|
196
|
+
/es /es/about → Español
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
> A través de este archivo de configuración, puedes configurar URL localizadas, redirección de middleware, nombres de cookies, la ubicación y extensión de tus declaraciones de contenido, desactivar los registros de Intlayer en la consola y más. Para obtener una lista completa de parámetros disponibles, consulta la [documentación de configuración](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/configuration.md).
|
|
200
|
+
|
|
201
|
+
</Step>
|
|
202
|
+
|
|
203
|
+
<Step number={3} title="Integrar Intlayer en tu configuración de Vite">
|
|
204
|
+
|
|
205
|
+
Agrega el plugin Intlayer a tu configuración:
|
|
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
|
+
> El plugin de Vite `intlayer()` compila tus archivos de declaración de contenido, los observa en modo de desarrollo y define las variables de entorno de Intlayer dentro de la aplicación. También proporciona alias que optimizan el rendimiento.
|
|
219
|
+
|
|
220
|
+
### El enrutamiento de locale viene con el plugin
|
|
221
|
+
|
|
222
|
+
SolidStart se ejecuta en [Nitro](https://nitro.build), e `intlayer()` registra su controlador de enrutamiento de locale directamente en la canalización del servidor de Nitro (a través de la opción `routing.enableProxy`, `true` por defecto). Nada más que conectar: en un servidor compilado, cada solicitud se inspecciona antes de llegar al enrutador, y
|
|
223
|
+
|
|
224
|
+
- la locale se lee del prefijo de la URL, luego de la cookie `INTLAYER_LOCALE`, luego del encabezado `Accept-Language`;
|
|
225
|
+
- una URL sin prefijo se redirige a su contraparte localizada cuando la locale resuelta no es la predeterminada (`/` → `/fr`);
|
|
226
|
+
- una URL con un prefijo redundante se redirige a su forma canónica (`/en/about` → `/about`);
|
|
227
|
+
- la cookie de locale se vuelve a escribir en la respuesta.
|
|
228
|
+
|
|
229
|
+
</Step>
|
|
230
|
+
|
|
231
|
+
<Step number={4} title="Declarar tu contenido">
|
|
232
|
+
|
|
233
|
+
Crea y gestiona tus declaraciones de contenido para almacenar traducciones:
|
|
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
|
+
> ⚠️ **Detalle específico de SolidStart**: cada archivo `.ts` / `.tsx` bajo `src/routes` se convierte en una ruta, y un archivo `.content.ts` tiene una exportación predeterminada, por lo que se detectaría como una página. Mantén las declaraciones de contenido de tus **páginas** fuera del directorio de rutas (`src/contents/` funciona bien). El contenido de los **componentes** puede permanecer co-ubicado, ya que `src/components` no es escaneado por el enrutador del sistema de archivos.
|
|
298
|
+
|
|
299
|
+
> Tus declaraciones de contenido se pueden definir en cualquier lugar de tu aplicación siempre que estén incluidas en el directorio `contentDir` (por defecto, `./src`), y coincidan con la extensión del archivo de declaración de contenido (por defecto, `.content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}`).
|
|
300
|
+
>
|
|
301
|
+
> Para obtener más detalles, consulta la [documentación de declaración de contenido](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/dictionary/content_file.md).
|
|
302
|
+
|
|
303
|
+
</Step>
|
|
304
|
+
|
|
305
|
+
<Step number={5} title="Agregar enrutamiento localizado">
|
|
306
|
+
|
|
307
|
+
El objetivo de este paso es darle a cada idioma su propia URL, que es lo que los motores de búsqueda indexan.
|
|
308
|
+
|
|
309
|
+
Mueve tus páginas bajo un **segmento dinámico opcional**. En el enrutador del sistema de archivos de SolidStart, `[[locale]]` se compila con el patrón de ruta `:locale?`:
|
|
310
|
+
|
|
311
|
+
```plaintext
|
|
312
|
+
src/routes/
|
|
313
|
+
[[locale]].tsx ← layout que valida el segmento
|
|
314
|
+
[[locale]]/
|
|
315
|
+
index.tsx → / y /fr y /es
|
|
316
|
+
about.tsx → /about y /fr/about y /es/about
|
|
317
|
+
[...404].tsx → catch-all para cualquier otra cosa
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
La única función del archivo de layout es restringir el segmento a una locale configurada:
|
|
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` expande `:locale?` en dos patrones (uno con el segmento y otro sin él) y los prueba por especificidad descendente. `matchFilters` es lo que hace la diferencia entre una configuración que funciona y una confusa:
|
|
338
|
+
|
|
339
|
+
| URL | Sin `matchFilters` | Con `matchFilters` |
|
|
340
|
+
| ----------- | ----------------------------------------------------------- | -------------------------------------- |
|
|
341
|
+
| `/fr/about` | Página de acerca de en francés | Página de acerca de en francés |
|
|
342
|
+
| `/about` | Página de acerca de (gana el segmento estático) | Página de acerca de |
|
|
343
|
+
| `/unknown` | **Página de inicio**, silenciosamente, con `locale=unknown` | Sin coincidencia → pasa al 404 general |
|
|
344
|
+
|
|
345
|
+
> Prefiere `[locale]` (requerido) sobre `[[locale]]` si usas el modo de enrutamiento `'prefix-all'`, y elimina el segmento por completo para `'no-prefix'` o `'search-params'`.
|
|
346
|
+
|
|
347
|
+
</Step>
|
|
348
|
+
|
|
349
|
+
<Step number={6} title="Proporcionar la locale a tu aplicación">
|
|
350
|
+
|
|
351
|
+
La URL es la única fuente de verdad para la locale: el middleware ya ha redirigido la solicitud a su ruta localizada, por lo que leer la ruta en el layout raíz mantiene el renderizado del servidor y la hidratación del cliente de acuerdo, y hace que cada navegación del lado del cliente actualice la locale de forma gratuita.
|
|
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
|
+
// El servidor renderiza <html> en entry-server.tsx; las navegaciones del lado del cliente
|
|
369
|
+
// entre locales deben actualizar los atributos ellas mismas.
|
|
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` reacciona a su prop `locale`, por lo que pasar la llamada al accesador `locale()` dentro de JSX es suficiente: Solid lo compila en un getter y todo el árbol se vuelve a renderizar en el nuevo idioma cuando la URL cambia.
|
|
397
|
+
|
|
398
|
+
</Step>
|
|
399
|
+
|
|
400
|
+
<Step number={7} title="Establecer los atributos HTML lang y dir en el servidor">
|
|
401
|
+
|
|
402
|
+
El elemento `<html>` se renderiza mediante `entry-server.tsx`, fuera del `Router`. Lee la locale desde la URL de la solicitud en su lugar:
|
|
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
|
+
Los rastreadores ahora reciben el idioma correcto desde el primer byte:
|
|
439
|
+
|
|
440
|
+
```html
|
|
441
|
+
<html dir="ltr" lang="fr"></html>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
</Step>
|
|
445
|
+
|
|
446
|
+
<Step number={8} title="Utilizar Intlayer en tus páginas">
|
|
447
|
+
|
|
448
|
+
Accede a tus diccionarios de contenido en toda tu aplicación:
|
|
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
|
+
> En Solid, `useIntlayer` devuelve contenido reactivo (ej. `content`). Puedes acceder directamente a sus propiedades.
|
|
471
|
+
|
|
472
|
+
> Si deseas usar tu contenido en un atributo `string`, como `alt`, `title`, `href`, `aria-label`, etc., puedes usar el valor de la función, como:
|
|
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
|
+
> Para obtener más información sobre el hook `useIntlayer`, consulta la [documentación](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/packages/solid-intlayer/useIntlayer.md).
|
|
481
|
+
|
|
482
|
+
Los nodos de contenido no se limitan a traducciones simples. Un contador pluralizado, por ejemplo:
|
|
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()` selecciona la categoría a través de `Intl.PluralRules` para la locale activa, por lo que los idiomas con más de dos formas plurales funcionan sin código adicional.
|
|
525
|
+
|
|
526
|
+
</Step>
|
|
527
|
+
|
|
528
|
+
<Step number={9} title="Crear un componente Link localizado">
|
|
529
|
+
|
|
530
|
+
Crea un componente `Link` personalizado que agregue automáticamente un prefijo a las URL internas con el idioma actual:
|
|
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
|
+
Escribir `href="/about"` una vez ahora produce `/about`, `/fr/about` o `/es/about` según la locale activa; sin prefijos manuales en ninguna parte de tus páginas.
|
|
570
|
+
|
|
571
|
+
</Step>
|
|
572
|
+
|
|
573
|
+
<Step number={10} title="Crear un componente conmutador de locale">
|
|
574
|
+
|
|
575
|
+
Renderiza el conmutador como **anclas reales** en lugar de un `<select>`: cada idioma de la página actual se convierte en un enlace rastreable que se puede abrir en una nueva pestaña, algo que un control basado solo en JavaScript no puede ofrecer.
|
|
576
|
+
|
|
577
|
+
`getPathWithoutLocale` elimina el segmento de locale de la ruta actual, y `getLocalizedUrl` la me vuelve a construir para la locale de destino, de modo que los enlaces sigan tu modo de enrutamiento sin codificar nada de forma rígida. La navegación es lo que cambia la locale renderizada (la ruta `[[locale]]` la deriva de la URL) mientras que `setLocale` persiste la elección en la cookie `INTLAYER_LOCALE` para que una visita posterior a una URL sin locale se resuelva en el mismo idioma.
|
|
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
|
+
// Ruta canónica (sin locale) de la página que se muestra actualmente
|
|
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
|
+
// Solo coincidencia exacta, para que el enlace de la locale predeterminada no se marque
|
|
613
|
+
// activo en cada página
|
|
614
|
+
end
|
|
615
|
+
href={getLocalizedUrl(pathWithoutLocale(), localeItem)}
|
|
616
|
+
hreflang={localeItem}
|
|
617
|
+
lang={localeItem}
|
|
618
|
+
onClick={() => setLocale(localeItem)}
|
|
619
|
+
// Asegura que el botón "atrás" del navegador vuelva a la página anterior
|
|
620
|
+
replace
|
|
621
|
+
>
|
|
622
|
+
{/* Idioma en su propia locale - ej. Français */}
|
|
623
|
+
{getLocaleName(localeItem)}
|
|
624
|
+
</A>
|
|
625
|
+
)}
|
|
626
|
+
</For>
|
|
627
|
+
</div>
|
|
628
|
+
</div>
|
|
629
|
+
);
|
|
630
|
+
};
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
> En Solid, `locale` de `useLocale` es un **accesador de señal**. Usa `locale()` (con paréntesis) para leer su valor actual de forma reactiva.
|
|
634
|
+
>
|
|
635
|
+
> `getLocaleName(localeItem)` renderiza cada idioma en su propio idioma: `English / Français / Español`. Pasa un segundo argumento para traducir los nombres al idioma que se muestra actualmente: `getLocaleName(localeItem, locale())` devuelve `English / French / Spanish` en inglés, `anglais / français / espagnol` en francés.
|
|
636
|
+
>
|
|
637
|
+
> `<A>` ya establece `aria-current="page"` en el enlace que coincide con la URL actual, por lo que no hay nada que agregar para eso. `replace` es leído del atributo renderizado por el enrutador: intercambia la entrada del historial en lugar de insertar una, de modo que el botón "atrás" del navegador vuelve a la página visitada antes del cambio en lugar de a la misma página en el idioma anterior.
|
|
638
|
+
>
|
|
639
|
+
> `dir` y `hreflang` en cada enlace mantienen los nombres de los idiomas de derecha a izquierda orientados correctamente e informan a las tecnologías de asistencia y a los rastreadores a qué idioma apunta cada enlace.
|
|
640
|
+
>
|
|
641
|
+
> Para obtener más información sobre el hook `useLocale`, consulta la [documentación](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/packages/solid-intlayer/useLocale.md).
|
|
642
|
+
|
|
643
|
+
</Step>
|
|
644
|
+
|
|
645
|
+
<Step number={11} title="Emitir enlaces canónicos y hreflang" isOptional={true}>
|
|
646
|
+
|
|
647
|
+
Las anotaciones `hreflang` indican a los motores de búsqueda que `/about`, `/fr/about` y `/es/about` son la misma página en diferentes idiomas. `getMultilingualUrls` las deriva de la ruta canónica (sin locale), siguiendo tu modo de enrutamiento, de modo que nada está codificado de forma rígida:
|
|
648
|
+
|
|
649
|
+
```tsx fileName="src/components/AlternateLinks.tsx" codeFormat="typescript"
|
|
650
|
+
import {
|
|
651
|
+
defaultLocale,
|
|
652
|
+
getMultilingualUrls,
|
|
653
|
+
getPathWithoutLocale,
|
|
654
|
+
} from "intlayer";
|
|
655
|
+
import { type Component, For } from "solid-js";
|
|
656
|
+
|
|
657
|
+
export type AlternateLinksProps = {
|
|
658
|
+
/** URL absoluta de la página que se está renderizando. */
|
|
659
|
+
url: string;
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
export const AlternateLinks: Component<AlternateLinksProps> = (props) => {
|
|
663
|
+
const multilingualUrls = () => {
|
|
664
|
+
const { origin, pathname } = new URL(props.url);
|
|
665
|
+
|
|
666
|
+
return Object.entries(
|
|
667
|
+
getMultilingualUrls(`${origin}${getPathWithoutLocale(pathname)}`)
|
|
668
|
+
);
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
const canonicalUrl = () =>
|
|
672
|
+
new URL(props.url).origin + new URL(props.url).pathname;
|
|
673
|
+
|
|
674
|
+
return (
|
|
675
|
+
<>
|
|
676
|
+
<link href={canonicalUrl()} rel="canonical" />
|
|
677
|
+
<For each={multilingualUrls()}>
|
|
678
|
+
{([locale, localizedUrl]) => (
|
|
679
|
+
<link href={localizedUrl} hreflang={locale} rel="alternate" />
|
|
680
|
+
)}
|
|
681
|
+
</For>
|
|
682
|
+
<link
|
|
683
|
+
href={
|
|
684
|
+
multilingualUrls().find(([locale]) => locale === defaultLocale)?.[1]
|
|
685
|
+
}
|
|
686
|
+
hreflang="x-default"
|
|
687
|
+
rel="alternate"
|
|
688
|
+
/>
|
|
689
|
+
</>
|
|
690
|
+
);
|
|
691
|
+
};
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Renderízalo en el encabezado del documento, donde la URL de la solicitud esté disponible:
|
|
695
|
+
|
|
696
|
+
```tsx fileName="src/entry-server.tsx" codeFormat="typescript"
|
|
697
|
+
import { AlternateLinks } from "~/components/AlternateLinks";
|
|
698
|
+
|
|
699
|
+
// … dentro de <head>, junto a las otras etiquetas meta:
|
|
700
|
+
<AlternateLinks url={url} />;
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
`GET /fr/about` luego sirve:
|
|
704
|
+
|
|
705
|
+
```html
|
|
706
|
+
<link href="https://example.com/fr/about" rel="canonical" />
|
|
707
|
+
<link href="https://example.com/about" hreflang="en" rel="alternate" />
|
|
708
|
+
<link href="https://example.com/fr/about" hreflang="fr" rel="alternate" />
|
|
709
|
+
<link href="https://example.com/es/about" hreflang="es" rel="alternate" />
|
|
710
|
+
<link href="https://example.com/about" hreflang="x-default" rel="alternate" />
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
> **Nota sobre `@solidjs/meta`**: al momento de escribir este artículo, `<Title>` y `<Meta>` de `@solidjs/meta` se aplican en el cliente después de la hidratación pero **no** se emiten en el `<head>` renderizado en el servidor en SolidStart v2. Hasta que eso se solucione en el proyecto original, renderiza las etiquetas que los rastreadores deben ver sin JavaScript (`canonical`, `hreflang` y, si es necesario, `title` / `description`) directamente en `entry-server.tsx`, como se muestra arriba.
|
|
714
|
+
|
|
715
|
+
</Step>
|
|
716
|
+
|
|
717
|
+
<Step number={12} title="Gestionar páginas no encontradas" isOptional={true}>
|
|
718
|
+
|
|
719
|
+
Una ruta comodín (splat) en la raíz de `src/routes` captura cada ruta que el segmento de locale no coincidió, incluidos los prefijos de locale no válidos rechazados por `matchFilters`. Debido a que la locale todavía proviene de la URL a través del layout raíz, la página 404 se muestra en el idioma del visitante:
|
|
720
|
+
|
|
721
|
+
```tsx fileName="src/routes/[...404].tsx" codeFormat="typescript"
|
|
722
|
+
import { Title } from "@solidjs/meta";
|
|
723
|
+
import { HttpStatusCode } from "@solidjs/start";
|
|
724
|
+
import { useIntlayer } from "solid-intlayer";
|
|
725
|
+
import { LocalizedLink } from "~/components/LocalizedLink";
|
|
726
|
+
|
|
727
|
+
export default function NotFound() {
|
|
728
|
+
const content = useIntlayer("not-found-page");
|
|
729
|
+
|
|
730
|
+
return (
|
|
731
|
+
<main>
|
|
732
|
+
<Title>{content.metaTitle.value}</Title>
|
|
733
|
+
<HttpStatusCode code={404} />
|
|
734
|
+
<h1>{content.title}</h1>
|
|
735
|
+
<LocalizedLink href="/">{content.backHome}</LocalizedLink>
|
|
736
|
+
</main>
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
| Solicitud | Resultado |
|
|
742
|
+
| ----------------- | ----------------------------------------- |
|
|
743
|
+
| `/xx` | `404` — `xx` no es una locale configurada |
|
|
744
|
+
| `/nonexistent` | `404` en la locale predeterminada |
|
|
745
|
+
| `/fr/nonexistent` | `404` en francés (`Page introuvable`) |
|
|
746
|
+
|
|
747
|
+
</Step>
|
|
748
|
+
|
|
749
|
+
<Step number={13} title="Generar un sitemap multilingüe" isOptional={true}>
|
|
750
|
+
|
|
751
|
+
El generador de sitemaps de Intlayer expande cada ruta en una entrada por locale y conecta las alternativas `xhtml:link` entre ellas, por lo que la ruta solo tiene que listar las rutas canónicas y sin locale.
|
|
752
|
+
|
|
753
|
+
> A diferencia de los generadores básicos que solo emiten URL planas, Intlayer conecta enlaces bidireccionales entre cada variante localizada de cada página, lo que ayuda a los motores de búsqueda a relacionar las URL localizadas y servir la adecuada a la audiencia correcta.
|
|
754
|
+
|
|
755
|
+
SolidStart convierte un archivo que exporta un método HTTP en una ruta de API y elimina la extensión `.ts` de la ruta, por lo que `src/routes/sitemap.xml.ts` se sirve en `/sitemap.xml`:
|
|
756
|
+
|
|
757
|
+
```typescript fileName="src/routes/sitemap.xml.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
758
|
+
import type { APIEvent } from "@solidjs/start/server";
|
|
759
|
+
import { generateSitemap } from "intlayer";
|
|
760
|
+
|
|
761
|
+
const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000";
|
|
762
|
+
|
|
763
|
+
export const GET = (_event: APIEvent) => {
|
|
764
|
+
const sitemap = generateSitemap(
|
|
765
|
+
[
|
|
766
|
+
{ path: "/", changefreq: "daily", priority: 1.0 },
|
|
767
|
+
{ path: "/about", changefreq: "monthly", priority: 0.8 },
|
|
768
|
+
],
|
|
769
|
+
{ siteUrl: SITE_URL }
|
|
770
|
+
);
|
|
771
|
+
|
|
772
|
+
return new Response(sitemap, {
|
|
773
|
+
headers: { "Content-Type": "application/xml" },
|
|
774
|
+
});
|
|
775
|
+
};
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
```xml fileName="salida de GET /sitemap.xml"
|
|
779
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
780
|
+
<urlset
|
|
781
|
+
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
782
|
+
xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
783
|
+
>
|
|
784
|
+
<url>
|
|
785
|
+
<loc>https://example.com/about</loc>
|
|
786
|
+
<changefreq>monthly</changefreq>
|
|
787
|
+
<priority>0.8</priority>
|
|
788
|
+
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/about"/>
|
|
789
|
+
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/about"/>
|
|
790
|
+
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/about"/>
|
|
791
|
+
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about"/>
|
|
792
|
+
</url>
|
|
793
|
+
</urlset>
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
> Las rutas de la API no admiten parámetros opcionales, por lo que debes mantener este archivo en la raíz de `src/routes`, fuera del segmento `[[locale]]`. El sitemap ya contiene cada locale.
|
|
797
|
+
|
|
798
|
+
Puedes crear un `robots.txt` de la misma manera con `getMultilingualUrls`, de modo que las entradas `Disallow` cubran cada variante localizada de una ruta sensible:
|
|
799
|
+
|
|
800
|
+
```typescript fileName="src/routes/robots.txt.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
801
|
+
import { getMultilingualUrls } from "intlayer";
|
|
802
|
+
|
|
803
|
+
const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000";
|
|
804
|
+
|
|
805
|
+
const disallowedPaths = ["/admin", "/private"].flatMap((path) =>
|
|
806
|
+
Object.values(getMultilingualUrls(path))
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
export const GET = () =>
|
|
810
|
+
new Response(
|
|
811
|
+
[
|
|
812
|
+
"User-agent: *",
|
|
813
|
+
"Allow: /",
|
|
814
|
+
...disallowedPaths.map((path) => `Disallow: ${path}`),
|
|
815
|
+
"",
|
|
816
|
+
`Sitemap: ${SITE_URL}/sitemap.xml`,
|
|
817
|
+
].join("\n"),
|
|
818
|
+
{ headers: { "Content-Type": "text/plain" } }
|
|
819
|
+
);
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
</Step>
|
|
823
|
+
|
|
824
|
+
<Step number={14} title="Recuperar la locale en tus funciones de servidor" isOptional={true}>
|
|
825
|
+
|
|
826
|
+
Es posible que desees acceder a la locale actual desde dentro de una función de servidor o una ruta de API.
|
|
827
|
+
|
|
828
|
+
En una configuración basada en prefijos como esta, **la URL es la autoridad**: `getLocaleFromPath` lee el prefijo de la URL de la solicitud. `getLocale` es la alternativa para solicitudes que no llevan prefijo de locale: inspecciona la cookie `INTLAYER_LOCALE`, luego el encabezado `x-intlayer-locale`, y luego negocia `Accept-Language`.
|
|
829
|
+
|
|
830
|
+
```tsx fileName="src/routes/[[locale]]/index.tsx" codeFormat="typescript"
|
|
831
|
+
import { createAsync } from "@solidjs/router";
|
|
832
|
+
import { getCookie, getIntlayer, getLocale, getLocaleFromPath } from "intlayer";
|
|
833
|
+
import { getRequestEvent } from "solid-js/web";
|
|
834
|
+
|
|
835
|
+
const loadLocalizedData = async () => {
|
|
836
|
+
"use server";
|
|
837
|
+
|
|
838
|
+
const request = getRequestEvent()?.request;
|
|
839
|
+
|
|
840
|
+
const locale =
|
|
841
|
+
getLocaleFromPath(request?.url) ??
|
|
842
|
+
(await getLocale({
|
|
843
|
+
// Obtener la cookie de la solicitud (por defecto: 'INTLAYER_LOCALE')
|
|
844
|
+
getCookie: (name) =>
|
|
845
|
+
getCookie(name, request?.headers.get("cookie") ?? ""),
|
|
846
|
+
// Obtener el encabezado de la solicitud (por defecto: 'x-intlayer-locale'),
|
|
847
|
+
// recurriendo a la negociación Accept-Language
|
|
848
|
+
getHeader: (name) => request?.headers.get(name) ?? undefined,
|
|
849
|
+
}));
|
|
850
|
+
|
|
851
|
+
// Recuperar algún contenido fuera de un componente usando getIntlayer()
|
|
852
|
+
const content = getIntlayer("home-page", locale);
|
|
853
|
+
|
|
854
|
+
return { locale, title: String(content.title) };
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
export default function Page() {
|
|
858
|
+
const data = createAsync(() => loadLocalizedData());
|
|
859
|
+
|
|
860
|
+
return <p>{data()?.title}</p>;
|
|
861
|
+
}
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
> No te fíes únicamente de `getLocale` aquí: la cookie de locale solo se escribe una vez que un visitante cambia activamente de idioma, por lo que una primera visita a `/fr/...` se resolvería en la locale predeterminada.
|
|
865
|
+
|
|
866
|
+
</Step>
|
|
867
|
+
|
|
868
|
+
<Step number={15} title="Extraer el contenido de tus componentes" isOptional={true}>
|
|
869
|
+
|
|
870
|
+
Si tienes una base de código existente, transformar miles de archivos puede llevar mucho tiempo.
|
|
871
|
+
|
|
872
|
+
Para facilitar este proceso, Intlayer propone un [compilador](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/compiler.md) / [extractor](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/cli/extract.md) para transformar tus componentes y extraer el contenido.
|
|
873
|
+
|
|
874
|
+
Para configurarlo, puedes agregar una sección `compiler` en tu archivo `intlayer.config.ts`:
|
|
875
|
+
|
|
876
|
+
```typescript fileName="intlayer.config.ts" codeFormat={["typescript", "esm", "commonjs"]}
|
|
877
|
+
import { type IntlayerConfig } from "intlayer";
|
|
878
|
+
|
|
879
|
+
const config: IntlayerConfig = {
|
|
880
|
+
// ... Resto de tu configuración
|
|
881
|
+
compiler: {
|
|
882
|
+
/**
|
|
883
|
+
* Indica si el compilador debe estar habilitado.
|
|
884
|
+
*/
|
|
885
|
+
enabled: true,
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Define la ruta de los archivos de salida
|
|
889
|
+
*/
|
|
890
|
+
output: ({ fileName, extension }) => `./${fileName}${extension}`,
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Indica si los componentes deben guardarse después de ser transformados.
|
|
894
|
+
*
|
|
895
|
+
* - Si es `true`, el compilador reescribirá el archivo del componente en el disco. Por lo tanto, la transformación será permanente y el compilador omitirá la transformación en el siguiente proceso. De esta manera, el compilador puede transformar la aplicación y luego ser eliminado.
|
|
896
|
+
*
|
|
897
|
+
* - Si es `false`, el compilador inyectará la llamada a la función `useIntlayer()` en el código solo en la salida de compilación y mantendrá intacta la base de código base. La transformación se realizará solo en memoria.
|
|
898
|
+
*/
|
|
899
|
+
saveComponents: false,
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Prefijo de clave del diccionario
|
|
903
|
+
*/
|
|
904
|
+
dictionaryKeyPrefix: "",
|
|
905
|
+
},
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
export default config;
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
<Tabs>
|
|
912
|
+
<Tab value="Comando de extracción">
|
|
913
|
+
|
|
914
|
+
Ejecuta el extractor para transformar tus componentes y extraer el contenido
|
|
915
|
+
|
|
916
|
+
```bash packageManager="npm"
|
|
917
|
+
npx intlayer extract
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
```bash packageManager="pnpm"
|
|
921
|
+
pnpm intlayer extract
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
```bash packageManager="yarn"
|
|
925
|
+
yarn intlayer extract
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
```bash packageManager="bun"
|
|
929
|
+
bun x intlayer extract
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
> Mueve los archivos de contenido generados de tus páginas fuera de `src/routes` después, por la razón explicada en el paso 5.
|
|
933
|
+
|
|
934
|
+
</Tab>
|
|
935
|
+
<Tab value="Compilador Babel">
|
|
936
|
+
|
|
937
|
+
> Desde la v9, `intlayerCompiler` está incluido en el plugin `intlayer`. Por lo tanto, no necesitas agregarlo manualmente.
|
|
938
|
+
|
|
939
|
+
Actualiza tu `vite.config.ts` para incluir el plugin `intlayerCompiler`:
|
|
940
|
+
|
|
941
|
+
```ts fileName="vite.config.ts"
|
|
942
|
+
import { solidStart } from "@solidjs/start/config";
|
|
943
|
+
import { nitro } from "nitro/vite";
|
|
944
|
+
import { defineConfig } from "vite";
|
|
945
|
+
import { intlayer, intlayerCompiler } from "vite-intlayer";
|
|
946
|
+
|
|
947
|
+
export default defineConfig({
|
|
948
|
+
plugins: [
|
|
949
|
+
solidStart({ middleware: "src/middleware.ts" }),
|
|
950
|
+
nitro(),
|
|
951
|
+
intlayer(),
|
|
952
|
+
intlayerCompiler(), // Agrega el plugin del compilador
|
|
953
|
+
],
|
|
954
|
+
});
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
```bash packageManager="npm"
|
|
958
|
+
npm run build # O npm run dev
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
```bash packageManager="pnpm"
|
|
962
|
+
pnpm run build # O pnpm run dev
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
```bash packageManager="yarn"
|
|
966
|
+
yarn build # O yarn dev
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
```bash packageManager="bun"
|
|
970
|
+
bun run build # O bun run dev
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
</Tab>
|
|
974
|
+
</Tabs>
|
|
975
|
+
|
|
976
|
+
</Step>
|
|
977
|
+
|
|
978
|
+
<Step number={16} title="Configurar TypeScript">
|
|
979
|
+
|
|
980
|
+
Intlayer utiliza el aumento de módulos para aprovechar los beneficios de TypeScript y fortalecer tu base de código.
|
|
981
|
+
|
|
982
|
+
Asegúrate de que tu configuración de TypeScript incluya los tipos generados automáticamente:
|
|
983
|
+
|
|
984
|
+
```json5 fileName="tsconfig.json"
|
|
985
|
+
{
|
|
986
|
+
compilerOptions: {
|
|
987
|
+
// ... tus configuraciones existentes
|
|
988
|
+
},
|
|
989
|
+
include: [
|
|
990
|
+
"src",
|
|
991
|
+
"*.ts",
|
|
992
|
+
".intlayer/**/*.ts", // Incluir los tipos generados automáticamente
|
|
993
|
+
],
|
|
994
|
+
}
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
Las claves de diccionario y las rutas de contenido ahora se verifican en tiempo de compilación:
|
|
998
|
+
|
|
999
|
+
```tsx
|
|
1000
|
+
useIntlayer("home-page"); // ✅
|
|
1001
|
+
useIntlayer("hom-page"); // ❌ Argument of type '"hom-page"' is not assignable to parameter of type 'keyof __DictionaryRegistry'
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
</Step>
|
|
1005
|
+
|
|
1006
|
+
</Steps>
|
|
1007
|
+
|
|
1008
|
+
---
|
|
1009
|
+
|
|
1010
|
+
## Verificando tu configuración
|
|
1011
|
+
|
|
1012
|
+
Compila e inicia el servidor, luego verifica que estas solicitudes se comporten según lo esperado:
|
|
1013
|
+
|
|
1014
|
+
```bash
|
|
1015
|
+
npm run build
|
|
1016
|
+
node .output/server/index.mjs
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
| Solicitud | Respuesta esperada |
|
|
1020
|
+
| --------------------------------------- | --------------------------------------- |
|
|
1021
|
+
| `GET /` | `200` — Inglés |
|
|
1022
|
+
| `GET /` con `Accept-Language: fr` | `302` → `/fr` |
|
|
1023
|
+
| `GET /` con cookie `INTLAYER_LOCALE=es` | `302` → `/es` |
|
|
1024
|
+
| `GET /fr` | `200` — Francés, `<html lang="fr">` |
|
|
1025
|
+
| `GET /fr/about` | `200` — Página de acerca de en francés |
|
|
1026
|
+
| `GET /en/about` | `302` → `/about` (redirección canónica) |
|
|
1027
|
+
| `GET /xx` | `404` |
|
|
1028
|
+
| `GET /fr/nonexistent` | `404` en francés |
|
|
1029
|
+
| `GET /sitemap.xml` | `200` — Sitemap XML multilingüe |
|
|
1030
|
+
|
|
1031
|
+
Las filas que renderizan una página se comportan de forma idéntica en `vite dev`. Las tres filas de redirección solo se aplican a un servidor compilado a menos que registres el controlador como un middleware tú mismo; consulta el paso 3.
|
|
1032
|
+
|
|
1033
|
+
> Ejecuta el servidor dev en Node (`vite dev`) en lugar de Bun (`bun --bun vite dev`): el SSR de SolidStart falla actualmente bajo el tiempo de ejecución de Bun con `Expected a Response object, but received 'NodeResponse'`. Esto no está relacionado con Intlayer (se reproduce en la plantilla básica) y solo afecta al servidor dev, no a `vite build`.
|
|
1034
|
+
|
|
1035
|
+
---
|
|
1036
|
+
|
|
1037
|
+
## Configuración de Git
|
|
1038
|
+
|
|
1039
|
+
Se recomienda ignorar los archivos generados por Intlayer. Esto te permite evitar subirlos a tu repositorio de Git.
|
|
1040
|
+
|
|
1041
|
+
Para hacer esto, puedes agregar las siguientes instrucciones a tu archivo `.gitignore`:
|
|
1042
|
+
|
|
1043
|
+
```plaintext fileName=".gitignore"
|
|
1044
|
+
# Ignorar los archivos generados por Intlayer
|
|
1045
|
+
.intlayer
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
|
|
1050
|
+
## Extensión VS Code
|
|
1051
|
+
|
|
1052
|
+
Para mejorar tu experiencia de desarrollo con Intlayer, puedes instalar la **extensión oficial de Intlayer para VS Code**.
|
|
1053
|
+
|
|
1054
|
+
[Instalar desde VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1055
|
+
|
|
1056
|
+
Esta extensión proporciona:
|
|
1057
|
+
|
|
1058
|
+
- **Autocompletado** para claves de traducción.
|
|
1059
|
+
- **Detección de errores en tiempo real** para traducciones faltantes.
|
|
1060
|
+
- **Vistas previas integradas** del contenido traducido.
|
|
1061
|
+
- **Acciones rápidas** para crear y actualizar traducciones fácilmente.
|
|
1062
|
+
|
|
1063
|
+
---
|
|
1064
|
+
|
|
1065
|
+
## Ir más lejos
|
|
1066
|
+
|
|
1067
|
+
Para ir más lejos, puedes implementar el [editor visual](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/intlayer_visual_editor.md) o externalizar tu contenido usando el [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/intlayer_CMS.md).
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
1071
|
+
## Referencias de documentación
|
|
1072
|
+
|
|
1073
|
+
- [Documentación de Intlayer](https://intlayer.org)
|
|
1074
|
+
- [Documentación de SolidStart](https://start.solidjs.com)
|
|
1075
|
+
- [Hook useIntlayer](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/packages/solid-intlayer/useIntlayer.md)
|
|
1076
|
+
- [Hook useLocale](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/packages/solid-intlayer/useLocale.md)
|
|
1077
|
+
- [Declaración de contenido](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/dictionary/content_file.md)
|
|
1078
|
+
- [Configuración](https://github.com/aymericzip/intlayer/blob/main/docs/docs/es/configuration.md)
|