Bricks + Polylang:WordPress 多语言建站方案与 ACF 实践

October, 28th 2026 17 min read • Markdown •
Bricks + Polylang:WordPress 多语言建站方案与 ACF 实践

用 Bricks 做多语言 WordPress,页面、模板和自定义字段应该怎么配合?Polylang 免费版够不够用?如果网站还用了 ACF,修改英文内容时,会不会把中文或西班牙语一起改掉?

我用一个虚构的徒步旅行网站做了三语言实验,包含普通页面、文章、Trip 自定义内容类型和 Terrain 分类法。先把多语言流程跑通,再把样式、语言切换和分类导航整理到 Bricks 原生能力里。

在这次基础字段和简单内容模型的范围内,Bricks + Polylang Free + ACF Free 可以完成三语言建站,各语言内容和字段也能独立维护。 实际需要花心思的地方,是语言对象怎么关联、模板怎么选择,以及哪些字段应该独立翻译、哪些事实需要同步。

下面按建站思路介绍这套方案。

本文基于 2026 年 10 月 25 日的实验及后续原生化改造记录,测试域名与私有路径已省略。使用版本为 WordPress 7.1.3、Bricks 2.4.3、Bricks Abilities 2.0.0、Polylang Free 3.9.1、ACF Free 6.8.10。Bricks 本身需要许可证;这里的“免费版”指 Polylang 和 ACF。

先分清三个工具的职责

这套组合可以按内容、语言和展示来理解:

工具负责什么
Polylang给文章、页面、自定义内容和词条分配语言,关联同一内容的不同语言版本
ACF定义自定义内容类型、分类法和字段,并保存各内容对象的字段值
Bricks用页面、模板、动态数据和 Query Loop,把当前语言的内容渲染出来

例如,一条路线有英文、中文和西班牙语三个版本,它们分别是独立的 WordPress 内容对象,由 Polylang 建立翻译关联。Bricks 读取当前对象的标题、正文和 ACF 字段,生成对应语言的详情页。

这里有一个容易混淆的点:Polylang 管的是语言内容的组织方式,译文质量仍取决于翻译和审核。 换成 Polylang,并不会让原本不准确的机器翻译自动变好。如果需求只是修正现有译文,TranslatePress 也支持人工修订;如果需要各语言独立编辑长文、产品资料或结构化字段,Polylang 的模型更值得考虑。

页面、内容和模板都要有语言归属

演示站采用 EN、ZH、ES 三种语言,英文为默认语言,不显示 /en/;中文和西班牙语分别使用 /zh/、/es/。

内容模型包含四类普通页面、两篇文章、三条 Trip 路线和两个 Terrain 词条。每项都有三个语言版本,共 27 个页面、文章及 Trip 内容对象,以及 6 个 Terrain 词条。品牌、路线和价格都是虚构示例。

先关联内容,再检查模板

建站时,先在 Polylang 中把 Trip 和 Terrain 纳入可翻译范围,再为 post、term 分配语言并建立翻译关联。普通文章也要分配对应语言的博客分类,不能只把正文翻译了,却继续挂在另一种语言的分类下。

Bricks 模板同样需要处理语言。本例有七组模板:Header、Footer、Trip 详情、Trip 列表、Terrain 列表、普通文章详情和 404。每组维护三个关联版本,共 21 个模板。

它们使用正常的 Bricks 模板条件,再由 Polylang 的语言关联参与匹配。没有额外用 language taxonomy 条件强行选择某个语言模板。

这也带来明确的维护成本:同一版式有三份模板,修改共享结构后,要检查其他语言版本是否需要同步。不能假设建立翻译关联之后,Bricks 的全部结构和文案就会一直自动保持一致。

URL 可以翻译到什么程度

独立内容可以使用各自的 slug。例如,同一条路线的路径可以是:

语言路径示例
English/trips/atlantic-field-notes/
简体中文/zh/trips/daxiyang-haian-xingji/
Español/es/trips/apuntes-del-atlantico/

这里的内容 slug 已经本地化,但 CPT base /trips/ 和 taxonomy base /terrain/ 在三种语言中共用。单篇内容的 slug,与 Polylang Pro 的 URL base 翻译、相同 slug 等能力,需要分开理解。

ACF:共用字段定义,各语言保存自己的值

ACF 的字段定义不需要复制三套。这次共用一个 Trip 内容类型、一个 Terrain 分类法和一个五字段组:

字段含义本例的处理方式
trip_duration行程天数初始化时给各语言显式写入相同数值
trip_price价格初始化时给各语言显式写入相同数值
trip_meeting集合地点说明各语言独立文本
trip_highlight路线亮点各语言独立文本
trip_difficulty难度描述各语言独立文本

字段定义共用,不等于字段值必须同步。 各语言的 Trip 是独立 post,因此可以保存自己的 ACF 值。

为了保住译文,这次关闭了 Polylang 全局 custom-fields 同步。实际修改英文价格或描述时,中文和西班牙语的值没有跟着变化;从后台修改西班牙语字段,也没有改写其他语言,测试后再还原。

这样做解决了语言隔离,却留下另一项业务责任:价格、天数等共享事实更新时,需要有明确的定向同步规则。不能为了同步价格,就开启所有 custom fields 的全局同步,把已经翻译好的文本也覆盖掉。

Bricks 动态模板直接读取当前语言对象的字段。ACF 免费版可以满足本例的基础字段需求,但这次没有验证 Relationship、Post Object 的跨语言对象映射,也没有测试 Repeater、Flexible Content、Options Pages 等复杂场景,不能把五个基础字段的结果推广到所有 ACF 组合。

展示层优先使用 Bricks 原生能力

首版为了快速验证,写了一个辅助插件来提供共用 CSS、语言切换和分类导航。继续检查 Bricks 的能力后,这些功能大部分可以直接由原生系统承担,最终前台已经停用该插件。

样式放回设计系统

基础字体、颜色和表单默认值放到 Theme Style;品牌色、内容宽度放到 global variables;组件布局、响应式和子选择器放到 global classes。

本例最后使用了 1 个全站 Theme Style、7 个自定义变量和 37 个 global classes。迁移时保留原元素 ID,重新回读了内容、语言关联、字段值和模板条件,确认样式调整没有改写内容模型。

这样整理后,样式能在 Bricks 的原生编辑界面里继续维护。没有把整份外部 CSS 塞进某个 class 的自定义 CSS 输入框,再把它算作原生化。

语言切换要指向同一内容的译文

Bricks 2.4.3 已有原生 polylang-language-switcher 元素,可以用于 Header 和 Footer,本例共使用六处。无需再为相同功能维护一个 shortcode 封装。

检查切换器时,重点是目标关联:从西班牙语某条 Trip 切到中文,应该进入这条 Trip 的中文版本,而不是只跳到中文首页。链接显示 EN、ZH、ES,并不足以证明关联正确。

分类导航用原生 Query Loop

Terrain 导航改为 term Query Loop,用 {term_name} 和 {term_url} 输出词条名称和链接。详情页与列表页则读取当前语言的内容与查询结果。

这里踩过一个小坑:把循环直接放在 text-link 上,保存时被拒,因为该元素不注册 hasLoop。改成由 Block 承担循环项,再放原生链接与 Text 子节点即可。

另外,Bricks Heading 在本次实验中没有执行直接填入的 shortcode。分类标题与描述改用原生动态数据,并分别设置 CPT 和 Terrain archive 模板,比继续补渲染逻辑更清楚。

表单保持原生,但按实际链路验证

三种语言继续使用 Bricks Form,只保存演示咨询,不发送邮件。启用保存功能时,通过 bricks/set-global-settings 的真实状态转换,让 Bricks 自己初始化 submission table;单独写一个保存开关,不等于存储表已经准备好。

验证也需要走完整路径:空表单是否被拒绝、成功提示是否出现、记录是否真正落库、归属是否为对应语言的页面和表单。本例的三条测试提交在核对后按精确 ID 清理。

如果通过 AI 或脚本搭建,接口边界也要分清

这次布局自动化使用已注册的 bricks/* AI abilities,创建页面和模板、写入原生元素、设置条件并渲染回读,沿用 Bricks 保存管线,没有直接写元素 postmeta。

语言分配与翻译关联则调用 Polylang 的 PHP API,例如 pll_set_post_language()、pll_save_post_translations() 及对应 term API;ACF 定义和字段值使用其 import/update API。

这三类操作各有自己的接口,不能都归为“Bricks 原生多语言管理”。本次主要通过 SSH/WP-CLI 调用 WordPress Abilities API,后续也验证了 WordPress Abilities REST 的 class 读写,但没有验收 MCP Adapter 的客户端传输链路,也没有启用 bricks/execute-php。

ACF 的 REST 读取与 Polylang 的语言管理扩展也要区分:能通过 REST 读出 acf 数据,不代表免费版同时获得了 Polylang Pro 的 lang、translations REST 能力。

如果要重复执行初始化,还要留意 ACF import 的 key 不等于 upsert。本例重跑时保留既有 definition 和 field ID,避免后台出现重复定义。Polylang 设置写入后,也要用下一次独立请求回读,确认设置真正持久化。

怎么确认这套方案真的跑通

只看到三种语言的页面还不够。更有价值的检查是:语言切换是否到同一内容、模板是否选对、字段有没有串语、修改源文会不会覆盖译文,以及表单是否真的保存。

这次最终改造后的主要验证结果如下:

检查内容实验结果
语言关系、字段值和修改隔离175 项检查通过
原生化前后的内容保留48 个内容/模板对象、6 个 Terrain 词条的事实保持一致;33 个 Bricks 文档完成原生化检查
匿名 HTTP702 项检查通过,覆盖 36 条正常路由、本地化 404、模板、字段、SEO 目标、REST 和资源
浏览器阅读与导航7 个代表页面在 1440px、375px 下抽样检查,共 14 个视口;另验证四步真实导航
三语言表单21 项浏览器、配置及持久化检查通过

这些数字对应原始实验记录,不是每次阅读本文时都在运行的检查。浏览器只抽样了代表页面,没有把全部路由逐一做双视口、跨浏览器测试;表单也没有覆盖邮件投递、付款或真实预订。

SEO 检查核对了 canonical 和 hreflang 的实际目标 URL。模板检查同时核对了请求实际使用的 Header、内容和 Footer 对象,避免把“页面出现了中文”当作模板正确的充分条件。

调样式时也遇到过几个具体问题:class 子选择器中的 & a, & .brand 展开异常,拆成独立 selector 后解决;表单的 fieldMargin 实际生成 .form-group padding,会被 class 中的 padding 归零抵消;Theme Style 和 classes 可以输出到 inline style,不能仅凭没有外链 CSS 文件判断保存失败。

还有一个未解决的版本问题:通过 WP-CLI 创建或更新 Theme Style 时,出现过 borderStyle/styles/buttonSizes 枚举警告。原生编辑 UI 和前台检查通过,但警告根因仍待确认,不能当作已经修复。

用到真实网站前,还要补哪些验证

这个实验能支持的结论是:在上述版本、基础字段和完整三语言内容的条件下,这条建站路线可行。要扩展到业务站点,还需要继续处理几件事。

首先是缺译文和规模。本例每条内容都有三语言版本,没有系统覆盖缺模板、下架、缺字段时的行为;三条 Trip 也不足以证明分页、搜索、复杂过滤和大数据量下的表现。媒体使用共用 attachment,没有验证独立媒体 metadata 翻译。

其次是生产链路。最终方案不再强制全站 no-cache,但生产缓存、登录态页面和性能仍需单独验证。SEO 插件 metadata、SMTP、支付、备份完整恢复以及完整无障碍检查,也没有被这次功能实验替代。

最后是旧站迁移。这次没有迁移原有 TranslatePress 站点。若要迁移,建议先在独立 staging 中选一个真实页面、一组 ACF/term 数据和原有 URL,确认译文、模板、菜单、字段与 SEO 信息能正确映射,再检查路径变化和必要的 301,并验证回滚。

对需要独立维护各语言内容的网站,Bricks 负责展示、Polylang 负责语言关联、ACF 负责结构化字段,是一条可以继续采用和验证的路线。实际维护的重点,会落在模板结构同步、共享事实更新和译文审核上。

参考资料