@spintax/core 0.3.4 → 0.4.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/CHANGELOG.md CHANGED
@@ -3,6 +3,33 @@
3
3
  All notable changes to `@spintax/core` are documented here. This project adheres to
4
4
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## 0.4.0 — 2026-08-17
7
+
8
+ ### Added
9
+
10
+ - **`plural.locale-missing` — a warning where `validate` used to be silent** (issue #65, reported
11
+ from a pipeline rendering ~1000 articles per campaign).
12
+
13
+ `validate` files no arity *verdict* without a locale, and that stays: the template may well be
14
+ correct for the locale the host will render with, so failing it here would fail a good template
15
+ for a fact the caller never claimed. But `render` has no such luxury — it resolves against the
16
+ 2-form default — so a `{plural …}` block with any other form count reached finished text as the
17
+ fullwidth-brace fallback `{plural …}`, with nothing said upfront. Downstream checks that scan
18
+ for `{`/`}` do not see those braces.
19
+
20
+ The warning fires only where the risk is: **no locale normalized, and the form count is not the
21
+ render default**. A 2-form block stays silent, because the default resolves it. `valid` is
22
+ unchanged in every case (`valid ⇔ no error-severity diagnostic`), and supplying any locale
23
+ replaces the warning with the real verdict — nothing for `ru`, `plural.arity` for `en`.
24
+
25
+ Carries `data: { got, defaultArity }`. The default arity is derived from the same table `render`
26
+ uses rather than written as a literal, so the two cannot come to disagree.
27
+
28
+ Mirrored into `spintax/core`, the WordPress plugin and `spintax-core` (Python) in the same
29
+ change, and pinned by a corpus fixture. Note the gate's shape: the shared corpus asserts
30
+ diagnostics as a SUBSET, so it can pin that the warning IS emitted but not that it is absent on
31
+ a 2-form block — that negative control lives in each engine's own suite.
32
+
6
33
  ## 0.3.4 — 2026-08-08
7
34
 
8
35
  Docs-only release — no engine changes; every byte of `dist/` behavior is 0.3.3's.
package/dist/index.cjs CHANGED
@@ -399,6 +399,7 @@ function pluralArity(baseLang) {
399
399
  return 2;
400
400
  }
401
401
  }
402
+ var DEFAULT_PLURAL_ARITY = pluralArity("");
402
403
  function pluralFor(baseLang, n, forms) {
403
404
  const abs = Math.abs(n);
404
405
  const mod10 = abs % 10;
@@ -581,8 +582,8 @@ function checkPlurals(text, idx, locale, out) {
581
582
  out.push(err("plural.nested-brackets", "{plural ...}: forms must not contain nested spintax brackets. Extract via #def first \u2014 a #set is substituted verbatim and would put the brackets straight back.", at));
582
583
  continue;
583
584
  }
585
+ const count = block.formsRaw.split("|").length;
584
586
  if (arity > 0) {
585
- const count = block.formsRaw.split("|").length;
586
587
  if (count !== arity) {
587
588
  out.push(err(
588
589
  "plural.arity",
@@ -590,6 +591,12 @@ function checkPlurals(text, idx, locale, out) {
590
591
  { ...at, data: { expected: arity, got: count } }
591
592
  ));
592
593
  }
594
+ } else if (count !== DEFAULT_PLURAL_ARITY) {
595
+ out.push(warn(
596
+ "plural.locale-missing",
597
+ `{plural ...}: ${count} forms, but no locale was supplied. render defaults to ${DEFAULT_PLURAL_ARITY} forms and leaves this block unresolved \u2014 pass the locale you will render with.`,
598
+ { ...at, data: { got: count, defaultArity: DEFAULT_PLURAL_ARITY } }
599
+ ));
593
600
  }
594
601
  }
595
602
  }