| 开发者 | 301st |
|---|---|
| 更新时间 | 2026年10月6日 15:15 |
| PHP版本: | 8.0 及以上 |
| WordPress版本: | 7.1 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
{a|b|c} — randomly pick one option, with nesting support[<config>a|b|c] — pick N elements, shuffle, join with custom separators%var% — global, local (#set re-picks at every use, #def picks once per render), and shortcode-level scopes{?VAR?then|else} — render a branch based on whether a variable is set (also {?!VAR?then} inverted){plural <count>: form1|form2|form3} — pick grammatically correct noun form by count. RU/UK/BE and SR/HR/BS 3-form (one|few|many), Arabic 6-form (zero|one|two|few|many|other), EN-style 2-form (one|many). Other languages fall back to the 2-form rule, so pl, cs, sk, sl and bg are bucketed by a rule that is not theirs rather than rejected. First spintax engine with first-class plurals.#include or [spintax][spintax] / spintax_render() automatically expose the current product as %product_name%, %product_sku%, %product_categories%, %product_attribute_<slug>%, and more. Volatile pricing is intentionally out of scope. WooCommerce is optional — the variables simply appear when a product context is present.wp spintax bindings list|apply|test|export|import#def variables — and documented in full at spintax.net. Six independent engines — five installable libraries (PHP, JavaScript/TypeScript, Python, .NET, Object Pascal) plus this plugin — are held to one shared test corpus, and a free native Windows editor — Spintax Studio — authors it with live preview and validation.
spintax folder to /wp-content/plugins/[spintax slug="my-template"] in posts/pages or spintax_render('my-template') in theme fileswp spintax bindings apply --binding=<id> --all. Action Scheduler turns those into one-click chunked async jobs that don't block the request. If you already use WooCommerce or another plugin that bundles Action Scheduler, you're already set; the Bindings page only shows the install notice when AS isn't loaded.Go to Spintax > Add New in the WordPress admin. Enter a title and your spintax markup in the editor.
{a|b|c} — randomly picks one option[a|b|c] — permutation: picks N elements, shuffles, joins with space[<minsize=2;maxsize=3;sep=", ";lastsep=" and "> a|b|c|d] — configured permutation%variable% — variable reference#set %var% = value — local variable, a macro: re-picked at every use#def %var% = value — local variable, picked once per render and held at every use{?VAR?then|else} — conditional: render a branch by truthiness of %VAR% (also {?!VAR?then} inverted){plural %Count%: form1|form2|form3} — plural agreement: picks the correct grammatical form by count (RU/UK/BE and SR/HR/BS 3-form, Arabic 6-form, EN-style 2-form)/#comment#/ — block comment (stripped from output)#include "slug" — embed another template{plural N: form1|form2|form3} in depth (EN/RU){?VAR?then|else} value-driven branching (EN/RU)spintax/core package this plugin's engine is kept in step withYes — Spintax Studio, a free native Windows editor built for exactly this syntax: two-pane live preview rendered by a real engine, inline validation with per-error documentation, variable panels, variant counting and export, and built-in help in 14 languages. Install it from the Microsoft Store; an overview of all the editor tooling (including VS Code and Sublime Text extensions) lives at https://spintax.net/spintax-editor/
Studio embeds spintax-win v0.8.1 — the Object Pascal engine from the same corpus-locked family as this plugin's — so a template that validates and previews there behaves the same way when this plugin renders it.
The plugin uses the WordPress Object Cache API. With a persistent backend (Redis, Memcached), cached output persists across requests. Without one, templates are re-rendered on each page load.
Yes: [spintax slug="greeting" name="Alice" city="Moscow"] makes %name% and %city% available inside the template.
Yes, since 2.2. On a single-product page the plugin auto-detects the current product and exposes it to [spintax] and spintax_render() as %product_*% variables — for example %product_name%, %product_slug%, %product_sku%, %product_type%, %product_stock_status%, %product_categories%, %product_tags%, %product_short_description%, and one %product_attribute_<slug>% per product attribute. So a template embedded as [spintax slug="product-seo-block"] on a product renders that product's data, and the same template on two products gets two separate cached variants.
Pricing (%product_price% and friends) is intentionally not exposed: it is volatile commerce data, not generated copy, and folding it into templates would churn the render cache on every price change.
To target a specific product regardless of the current page, pass [spintax slug="…" product_id="123"]; any explicit variable you pass always overrides the auto-detected one. WooCommerce is optional: with it inactive, or on non-product pages, behavior is unchanged.
Since 2.4 the plugin can also write generated copy into a product — see the next question. Product loops and cards are still deferred.
Yes, since 2.4. Create a binding with the target kind WooCommerce product field, on the Product post type, and pick Description or Short description. Every matching product then gets its own rendered copy — seeded when the field is empty, or regenerated on save if you ask for that — through the same machinery as ACF and post-meta bindings: cron schedules, Bulk Apply, WP-CLI, and the Logs page.
Turn on Expose WooCommerce product data in the binding's Variables tab and the template can use that product's own facts — %product_name%, %product_sku%, %product_type%, %product_categories%, %product_tags%, %product_attribute_<slug>% — so each product gets copy that is actually about it, not just a differently-worded version of the same sentence.
Three deliberate limits:
A binding pairs a Spintax template (or a per-post inline source) with one target field on one post type — for example "Posts → ACF: hero_subtitle". Configure it once under Spintax → Bindings and the plugin populates the field on every matching post on save, on a cron schedule, or on demand via Bulk Apply. Manual edits are preserved by default (hash-tracked); flags control whether the binding auto-seeds empty fields, regenerates on every save, or clears the field when the template renders to empty.
Yes. Bindings support both ACF (text / textarea / wysiwyg, top-level fields) and plain post-meta keys. ACF Free and Pro are both supported; nested fields (repeater / flexible_content rows) are not supported in 2.0 — that lands in a later release. The form-side field picker auto-fills the stable ACF field key so writes work on the first save without ACF's reference-meta handshake.
It's a recommended optional dependency for binding-heavy sites. The plugin works without it: admins can run a walk via the synchronous Run now button on each binding card, or wp spintax bindings apply --binding=<id> --all from the CLI. What Action Scheduler adds is chunked async execution, so:
Both walk every matching post for a binding and produce the same writes. They differ in how the walk runs:
Spintax → Logs in the admin sidebar. Both paths log a completion entry per walk (e.g. Bulk Apply run_synchronously completed for binding <id> — wrote=N skipped=M cleared=K.), plus warnings for partial failures. The Logs page supports level filtering, substring search, and pagination; entries are kept in a ring buffer sized by Settings → Spintax → Max log entries.
Five subcommands under wp spintax bindings:
wp spintax bindings list [--format=table|json|csv] — list all bindings on the site.wp spintax bindings apply --binding=<id> [--all|--post=<id>] — run a binding against every matching post (--all) or a single post (--post=<id>). This is the synchronous fallback path for Bulk Apply.wp spintax bindings test --binding=<id> --post=<id> — dry-run a binding against one post and report what would be written (target value, rendered preview, skip reason). Same logic as the admin Test panel; use this instead of apply when you want a preview.wp spintax bindings export {--binding=<id>|--all} [> bindings.json] — emit one binding or the full store as JSON to stdout, deduped by (post_type, target.key).wp spintax bindings import --file=bindings.json [--overwrite] [--dry-run] — import bindings from JSON. --overwrite updates matches on the same target triple; without it, duplicates are skipped. Use --dry-run to preview the plan without writing.A binding template sees four layered variable sources (later layers override earlier ones):
#set block in Settings → Spintax. Site-wide.#set / #def lines in the binding's Variables tab. Applies to that binding only.%post_id%, %post_title%, %post_url%, %post_slug%, %post_date%, %post_modified%, %author_id%, %author_name%.%acf_<field_name>%. Siblings are always fresh on save: the binding runs after ACF persists.{a|b|c}, [a|b], {?VAR?then|else}, {plural %N%: …}, #include "slug", /#comment#/).
Two trigger paths, both configurable per binding under "Triggers":
Each binding signs its last-rendered value and re-checks the target before every write. With Preserve manual edits enabled (default):
Bindings are a pre-generation system, not a render-on-read layer. The rendered string is stored in the target field; consumers (themes, blocks, REST readers) get that stored value directly. Editing the source template doesn't propagate to existing posts until a trigger writes a fresh value to each one. When you edit a template that has bindings pointing at it, the plugin:
wp spintax bindings apply --binding=<id> --all from the CLI). The Stale badge only clears when the entire walk completes with zero failures — partial-failure walks keep the badge so you notice the divergence and retry.
200 bindings per site. The store is a single autoloaded option (~500 bytes per binding), and the cap keeps autoload memory bounded. If you genuinely need more, please open an issue with your use case.
The form rejects a handful of unsafe targets at save time:
_wp_, _edit_, _oembed_, etc.)._spintax_* slots used to store source, signatures, and cache versions.wp_posts columns like post_title, post_content, post_excerpt. These are not post-meta and writing to them via the meta API would silently create shadow rows.No — bindings are per-site. Each subsite manages its own. Use wp --url=site2 spintax bindings import --file=site1-bindings.json to copy bindings between subsites via the WP-CLI export/import round-trip.
Not in 2.0; bindings are admin-only. The wp spintax bindings WP-CLI surface covers staging→production sync scenarios. REST API exposure is tracked for a later release.
nested-spintax-for-acf. Is there a migration path?Yes. After activating Spintax 2.0, a dismissible admin banner points to Tools → Spintax Migration. The wizard scans for predecessor data, shows a per-row preview, and creates bindings deduped by (post type, target field). Per-post sources and variables are copied non-destructively — the old plugin's data stays in place until you delete it.
Yes — the engine is published as a family of standalone open-source libraries, so a template you author here renders identically elsewhere:
composer require spintax/core — https://packagist.org/packages/spintax/corenpm i @spintax/core — https://www.npmjs.com/package/@spintax/corepip install spintax-core — https://pypi.org/project/spintax-core/dotnet add package Spintax.Core — https://www.nuget.org/packages/Spintax.Core — netstandard2.0 and net472, so .NET Framework 4.7.2+ and every .NET sincespintax-win v0.8.1 — https://github.com/investblog/spintax-win — the engine inside Spintax StudioYes — the same engine also ships as automation and agent tooling, held to the same corpus as this plugin:
n8n-nodes-spintax renders, validates and checks templates inside a workflow — one unique message per row of a sheet, for example. Guide: https://spintax.net/spintax-for-n8n/@spintax/mcp lets Claude, Cursor or any MCP-capable agent validate and render a template before it is published — hosted at https://spintax.net/mcp, or locally via npx @spintax/mcp. Guide: https://spintax.net/spintax-mcp/@spintax/authoring-prompt is the maintained prompt for writing templates in this syntax; the approach is written up at https://spintax.net/ai-spintax-templates/spintax/core 0.12.0. The same changes, locked by the shared cross-engine corpus.ar takes the six CLDR forms in order zero|one|two|few|many|other (few = 3–10, many = 11–99 of the last two digits, other = 100–102 and the like). It used to get the English two-form rule, which is ungrammatical for most counts. A two-form {plural} block under an Arabic locale is now reported as the wrong number of forms and renders as fullwidth braces until it has six.[<lastsep="和">A|B] was padded with spaces on both sides — right for English and Korean, wrong for scripts written without spaces between words. A separator made only of Han, Hiragana or Katakana now joins bare: A和B, AおよびB. Thai, Lao, Khmer and Myanmar separators join bare too. Rendered text changes for these shapes.ar locale (the site's, or a template's own), a separator that is exactly و or ف — under he, ו — keeps the space before it and none after when the next item starts with a letter of that script: الكازينو والبث, not الكازينو و البث. Before a Latin brand or a digit both spaces stay; Persian and Urdu, which write و as a word of its own, are untouched. Rendered text changes for these shapes.#def %7% = … and #set %1% = … printed the reference literally, or repeated a fragment, because PHP turned the name into an array index and merging the variable maps renumbered it. The same held for a digits-only shortcode attribute or spintax_render() argument on a WooCommerce product page.word.Word for a domain — so kept compact.Game categories and конец.Начало stayed glued. A domain's last label must now be all lower case or all upper case. example.com, ASP.NET, info@Example.COM and 例子.中国 are untouched; the accepted cost is that Yandex.Money renders as Yandex. Money and info@example.Com is no longer treated as an address. Rendered text changes for these shapes."Is it audited? ",, «Как дела? »,, (really? ) and title="really? " lose that space; a quote that opens the next phrase keeps its own. Rendered text changes for these shapes.a.a.a.…a.Game made the address and domain protection restart from every label: 2,000 one-letter labels took 48 ms where they had taken 0.7. Now 0.1–0.9 ms. Ordinary text costs 2–4% more.#def definitions. Three inner loops were charging the whole variable map for every definition in it. Measured on PHP 8.4, same machine: 3,200 chained definitions 8.7 s → 0.010 s, 12,800 independent ones 14.5 s → 0.021 s, and validating one cycle of 16,000 names 6.1 s → 0.163 s. Nothing a template renders changes — byte-identical across 3,000 generated definition-graph documents, with six deliberate mutations proving the check can see breakage first.