Builder.io Svelte SDK 实战为 SvelteKit 应用构建多语言本地化页面【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder本文以仓库中examples/svelte/localized-sveltekit示例为主线讲解如何在 SvelteKit 中集成 Builder.io 官方 Svelte SDK实现基于 Builder 可视化编辑器维护内容、按语言前缀/en、/fr、/de分发页面、并渲染本地化自定义组件的完整闭环。读完本文你将掌握 Builder.io 后台配置、SvelteKit 多语言路由与 301 重定向、服务端内容抓取fetchOneEntry以及Content组件渲染本地化内容的标准做法。一、示例概览它解决了什么问题localized-sveltekit是 Builder.io 官方仓库中演示Svelte SDK × SvelteKit × 本地化三件事如何协作的最小可运行项目。它的核心诉求是一个 Builder.io 空间space内配置了en、fr、de三种语言站点通过 URL 前缀如http://localhost:3000/fr/...区分语言页面内容由 Builder 可视化编辑器维护SvelteKit 负责抓取并按语言渲染自定义组件Counter中的部分输入字段声明为localized实现同一组件在不同语言下展示不同文案。整个示例的目录结构如下详见 examples/svelte/localized-sveltekitsrc/ ├── apiKey.js # Builder 公钥配置 ├── app.html # SvelteKit 应用外壳HTML 模板 ├── hooks.server.js # 服务端钩子语言解析 301 重定向 html lang 属性 ├── utils.js # 语言解析工具函数 ├── lib/ │ └── Counter.svelte # 演示用的自定义组件含本地化输入 └── routes/ └── [lang]/ └── [...catchall]/ ├── page.server.js # 服务端加载按语言抓取 Builder 内容 └── page.svelte # 页面组件渲染 Builder Content 与自定义组件其中路由采用 SvelteKit 的动态段[lang]加 catchall[...catchall]的嵌套写法lang即语言前缀catchall承接任意子路径——这套结构正是多语言 Builder 页面的骨架。二、Builder.io 后台设置在运行代码之前需要先在 Builder.io 平台完成以下准备对应原文档 README.md 的 Builder.io Setup 一节登录 builder.io 账号进入账号页面复制你的 API KeyPublic API Key粘贴到 src/apiKey.js 中导出// TODO: enter your public API key export const BUILDER_PUBLIC_API_KEY f1a790f8c3204b3b8c5c1795aeac4660; // ggignore注意原 README 写作BUILDER_API_KEY而当前示例源码中实际导出名是BUILDER_PUBLIC_API_KEY请以源码为准。文件末尾的ggignore注释表示该示例密钥会被 gitignore 忽略实际部署时务必替换为你自己的密钥打开 Builder.io 针对名为page的模型model的 Visual Editor可视化编辑器在 Builder 预览区右上角的 URL 输入框中填入http://localhost:3000在 Layers图层面板中拖入一个组件它就会实时出现在编辑器中——这就是 Builder 可视化开发的入口。后台还有一个关键前提在 Builder 空间中配置好与代码一致的语言列表。示例代码中supportedLocales [en, fr, de]见 src/utils.js注释明确写着Match this with the locales defined in your builder space即代码里声明的语言集合必须与 Builder 空间里配置的语言一一对应否则本地化内容无法正确抓取。三、安装与本地开发示例使用 Vite SvelteKit 作为构建工具链。先安装依赖再启动开发服务器命令与原文档 Build Setup 一节一致# 安装依赖 $ npm install # 以热重载方式在 localhost:3000 启动 $ npm run dev # 或启动服务器并在新浏览器标签页中打开应用 $ npm run dev -- --open从 package.json 可以看到脚本定义dev对应vite devbuild对应vite buildpreview对应vite preview。核心依赖是builder.io/sdk-svelte: ^1.0.27即仓库 packages/sdks/output/svelte 下的 Svelte SDK 产物其余为 SvelteKit 开发依赖。生产构建与预览# 生成生产版本 npm run build # 预览生产构建结果 npm run preview如果要部署到目标环境可能需要按 SvelteKit 的适配器adapter机制为对应平台安装适配器。当前 svelte.config.js 使用的是自动适配器sveltejs/adapter-auto。关于 vite.config.js 的一个关键细节src 同级目录的 vite.config.js 中有一处与 SDK 强相关的配置optimizeDeps: { /** * isolated-vm is an SDK dependency that must be excluded from the * pre-bundling optimization because it cannot (and shouldnt) be bundled at all. */ exclude: [isolated-vm] }isolated-vm是 Svelte SDK 的底层依赖用于隔离执行环境它无法也不应被 Vite 预打包因此必须通过optimizeDeps.exclude排除。这是让本示例正常跑起来的关键配置其他 SvelteKit 项目中引入该 SDK 时同样需要保留此项否则可能触发预打包相关的构建错误。四、多语言路由架构[lang][...catchall]本地化的实现基础是 SvelteKit 的动态路由。目录src/routes/[lang]/[...catchall]/同时使用了两类动态段[lang]匹配语言前缀如/en、/fr、/de[...catchall]rest 参数匹配语言前缀之后的任意剩余路径例如/en/blog/post-1中的blog/post-1。这样任何以语言前缀开头的 URL 都会命中同一个页面处理逻辑由服务端代码负责按语言抓取内容和决定是否渲染 404。原 README 指出用户也可以把多个语言映射到同一页面见下一节page.server.js中remove locale from the path to match multiple locales in same page if needed的注释。语言工具函数utils.jssrc/utils.js 集中了语言解析的纯函数是整个本地化逻辑的基础export const getLocaleFromPathname (pathname) ${pathname.match(/[^/]?(?\/|$)/)}.toLowerCase(); export const defaultLocale en; // Match this with the locales defined in your builder space export const supportedLocales [en, fr, de]; export const routeRegex new RegExp(/^\/[^.]*([?#].*)?$/); // checks if a string is a route request (i.e not an asset request like /image.png) https://regexr.com/73ccb export const isRoute (pathname) routeRegex.test(pathname);四个导出的职责分别是导出作用说明getLocaleFromPathname从路径中提取第一个段作为语言例如/en/about→en/de→dedefaultLocale默认语言当前为en在用户语言无法匹配时兜底supportedLocales支持的语言白名单当前为[en, fr, de]须与 Builder 空间配置一致isRoute判断是否为页面路由请求用正则^\/[^.]*([?#].*)?$排除图片等静态资源请求如/image.png避免把资源请求误当页面处理五、服务端钩子语言判定与 301 重定向本地化场景下用户访问无语言前缀的 URL 时应如何处理是一个必须解决的问题。示例通过 SvelteKit 的 handle 钩子在 src/hooks.server.js 中解决其流程为import { getLocaleFromPathname, defaultLocale, supportedLocales, isRoute } from ./utils; /** type {import(sveltejs/kit).Handle} */ export const handle async ({ event, resolve }) { const { url, request } event; const { pathname } url; // If this request is a route request if (isRoute(pathname)) { // Try to get locale from pathname. let locale supportedLocales.find( (l) ${l}.toLowerCase() getLocaleFromPathname(pathname) ); // If route locale is not supported if (!locale) { // Get user preferred locale locale ${${request.headers.get(accept-language)}.match( /[a-zA-Z]?(?-|_|,|;)/ )}.toLowerCase(); // Set default locale if user preferred locale does not match if (!supportedLocales.includes(locale)) locale defaultLocale; // 301 redirect return new Response(undefined, { headers: { location: /${locale}${pathname}${event.url.search} }, status: 301 }); } // Add html lang attribute return resolve(event, { transformPageChunk: ({ html }) html.replace(/html.*/, html lang${locale}) }); } return resolve(event); };整个钩子分三步从路径解析语言用supportedLocales.find(...)判断当前路径前缀是否属于已支持语言不支持则回退 301 重定向若前缀不在白名单中则读取请求头accept-language正则/[a-zA-Z]?(?-|_|,|;)/提取主语言标记作为用户偏好若仍不匹配支持列表回退到defaultLocaleen最终以301状态码返回location: /${locale}${pathname}${search}把用户永久重定向到带语言前缀的 URL。这一步对 SEO 友好——搜索引擎会把无前缀 URL 视为旧地址并跟随跳转注入lang属性通过transformPageChunk把html标签改写为html lang${locale}让浏览器与搜索引擎明确感知页面语言这是本地化页面的标准做法之一。六、服务端加载用fetchOneEntry按语言抓取内容页面数据在 src/routes/[lang]/[...catchall]/page.server.js 中通过 SvelteKit 的load函数在服务端完成import { fetchOneEntry, getBuilderSearchParams } from builder.io/sdk-svelte; import { BUILDER_PUBLIC_API_KEY } from ../../../apiKey; import { getLocaleFromPathname } from ../../../utils; /** type {import(./$types).PageServerLoad} */ export async function load(event) { const locale getLocaleFromPathname(event.url.pathname); // remove locale from the path to match multiple locales in same page if needed const urlPath event.url.pathname.replace(/${locale}, ) || /; // fetch your Builder content const content await fetchOneEntry({ model: page, apiKey: BUILDER_PUBLIC_API_KEY, locale, options: getBuilderSearchParams(event.url.searchParams), userAttributes: { urlPath, locale } }); return { content, locale, ...(!content { status: 404 }) }; }要点拆解fetchOneEntry是 SDK 提供的抓取单条内容API参数model: page对应在 Builder 中配置的模型名apiKey传入公钥locale传入当前语言locale剥离event.url.pathname.replace(/${locale}, ) || /把语言前缀从路径中移除得到不带语言前缀的urlPath。注释说明了意图——如果多个语言共用同一页面可以据此让不同语言的 URL 映射到同一条 Builder 内容getBuilderSearchParams(event.url.searchParams)把当前 URL 的查询参数透传给 Builder用于预览模式等场景userAttributes传入urlPath与locale是 Builder 内容定位targeting与实验分流的依据404 兜底...(!content { status: 404 })—— 当抓不到内容时load 返回的status: 404会让 SvelteKit 渲染出 404 响应。七、页面渲染Content组件、预览态与自定义组件注册服务端返回的content会流入 src/routes/[lang]/[...catchall]/page.svelte。该文件同时示范了三个 Builder SDK 的核心能力渲染内容、预览模式、注册自定义组件。1. 注册自定义组件import Counter from ../../../lib/Counter.svelte; // Create an array of your custom components and their properties const CUSTOM_COMPONENTS [ { component: Counter, name: Counter, inputs: [ { name: name, type: text, defaultValue: hello }, { name: count, type: number, defaultValue: 0 }, { name: localizeExample, type: text, localized: true } ] } ];每个自定义组件声明包含component组件实现、name在 Builder 编辑器中显示的名称和inputs属性 schema。其中localizeExample的localized: true是本地化示例的关键标记为localized的输入字段Builder 会按语言分别存储其值从而让同一组件在不同语言页面展示不同文案。defaultValue则提供了组件属性的默认值。2. 渲染 Builder 内容script import { isPreviewing, Content } from builder.io/sdk-svelte; import { BUILDER_PUBLIC_API_KEY } from ../../../apiKey; // this data comes from the function in page.server.js, which runs on the server only export let data; // we want to show unpublished content when in preview mode. const canShowContent data.content || isPreviewing(); const locale data.locale; /script main {#if canShowContent} divpage Title: {data.content?.data?.title || Unpublished}/div Content locale{locale} modelpage content{data.content} apiKey{BUILDER_PUBLIC_API_KEY} customComponents{CUSTOM_COMPONENTS} / {:else} Content Not Found {/if} /mainContent组件接收model、content、apiKey、locale与customComponents五个关键 props负责把服务端抓取到的内容树渲染为真实 DOMisPreviewing()SDK 提供的预览态判断。canShowContent data.content || isPreviewing()的含义是——正式发布的内容直接渲染若没有内容但正处于 Builder 预览模式也允许展示以便在编辑器中预览尚未发布的内容。注意data注释明确说明它来自page.server.js的load只在服务端运行空态处理既无内容又非预览时页面显示Content Not Found。八、本地化自定义组件Counter 示例src/lib/Counter.svelte 是一个带弹性动画的计数器组件接收三个 propsname、count与localizeExamplescript import { spring } from svelte/motion; export let name; export let count 0; export let localizeExample; const displayed_count spring(); $: displayed_count.set(count); $: offset modulo($displayed_count, 1); function modulo(n, m) { // handle negative numbers return ((n % m) m) % m; } /script div classcounter {name} - {localizeExample} ... /div值得注意的实现细节使用svelte/motion的spring让数字滚动过渡并借助modulo函数处理负数取模保证滚动方向正确渲染时同时展示{name} - {localizeExample}直观验证非本地化字段name各语言相同与本地化字段localizeExample各语言不同的差异——这正是本示例演示本地化输入localized: true价值的地方该组件在page.svelte中通过CUSTOM_COMPONENTS注册后便可以在 Builder 的可视化编辑器中像内置组件一样被拖拽、配置。九、SDK 状态与特性支持原文档指出Svelte SDK 各特性的实现状态可查阅 packages/sdks/README.md 中的特性实现表格Feature Implementation。仓库中 SDK 的构建与输出目录位于 packages/sdks/output/svelte本示例直接以 npm 包builder.io/sdk-svelte形式依赖它。若你需要了解该 SDK 支持哪些功能如 A/B 测试、个性化、数据绑定等以及当前实现进度以该表格为准。十、完整接入清单实操小结把以上内容收敛为接入 Builder 本地化 SvelteKit 站点的步骤清单在 Builder 空间配置好语言列表与代码中supportedLocales一致并创建名为page的模型复制公钥填入 src/apiKey.js建立src/routes/[lang]/[...catchall]/路由在 src/utils.js 中声明supportedLocales与defaultLocale在 src/hooks.server.js 中实现语言判定与 301 重定向并通过transformPageChunk注入html lang在page.server.js中用fetchOneEntry按locale、urlPath抓取内容并在抓取失败时返回 404在page.svelte中用Content渲染内容用isPreviewing()支持预览未发布内容并通过CUSTOM_COMPONENTS注册自定义组件将需要按语言区分的组件输入字段标记为localized: true若 SDK 引入导致 Vite 预打包报错参照 vite.config.js 在optimizeDeps.exclude中排除isolated-vm。至此你便拥有了一个可视化编辑 多语言分发 服务端渲染完整可用的 Builder.io × SvelteKit 本地化站点骨架。【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考