| 开发者 | 301st |
|---|---|
| 更新时间 | 2026年9月7日 01:43 |
| 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), 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, 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.8.0. Six fixes that already shipped across the family — Composer 0.6–0.8, npm (@spintax/core 0.4–0.6), PyPI, Object Pascal and .NET — every one locked by the shared cross-engine corpus.#set %a% = %b% %b% over #set %b% = %a% %a% doubles the text on every expansion pass, and there is no cycle, so the circular-reference guard never fired. Plural-form expansion now stops at 64 KB and reports the count as unknowable instead of dying. Every engine in the family had this.%variable% text; past that a reference is left as a literal %name% — exactly what an undefined name already does. The budget is per render and shared by #included templates, so a nested include cannot reset it.#def %tail% = few|many with {plural 2: one|%tail%} under ru rendered correctly yet was reported as the wrong number of forms. The validator now substitutes definitions first — and only where the count is provably fixed; a value carrying brackets suppresses the verdict instead of guessing. Templates that were wrongly flagged are now valid; the one new error is a #set whose value smuggles extra |-separated forms into a block, which always rendered as fullwidth braces anyway.{plural …} block with a non-default form count validated without a locale now carries a warning instead of passing silently. The template editor always passes the template's own locale, so editors keep getting the definite verdict as before.Spintax.Core on NuGet) joins the family list, Spintax Studio's engine version is current, and a new FAQ covers the n8n node and the MCP server.spintax/core 0.5.2. The standalone engines (Composer, npm, PyPI, Object Pascal) had moved ahead of the plugin; every change below ships identically across the family and is locked by the shared cross-engine corpus.#set no longer publishes an empty render. A template whose definitions reference each other in a cycle used to render as an empty string; it now stops expanding at the depth budget and emits the partially-expanded text with the unresolved reference left visible — what every other engine in the family already does. The validator still reports the cycle as an error.#include recognition follows the family grammar exactly. Variable names are ASCII, as documented — #set %имя% = … was silently accepted (and expanded) by this engine alone while being an error to every other; the editor now reports it and the line renders as text. Likewise an #include separated by a non-breaking space is plain text rather than an include, a CRLF line ending no longer leaks a carriage return into a directive's value, and a stray control character before a #set no longer flags a valid template as malformed.CHANGELOG.md in the plugin's GitHub repository: https://github.com/investblog/spintax/blob/main/CHANGELOG.md