Skip to content

test(ui): guard the interpolated placeholders that the language packs require - #171

Merged
argszero merged 1 commit into
mainfrom
test/i18n-placeholder-gate
Sep 11, 2026
Merged

argszero merged 1 commit into
mainfrom
test/i18n-placeholder-gate

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

The four key-resolution tests added in #170 check that every key exists, but nothing checked that a call site actually passes the variables the text needs. ui/js/i18n.js leaves the placeholder verbatim when a variable is missing:

s = s.replace(/\{(\w+)\}/g, (m, k) => vars[k] !== undefined ? String(vars[k]) : m);

so T("cnt.calls") renders as {n} 次 / {n} instead of 1,234 次 / 1,234. That defect fails no existing assertion — the key exists, both packs have it, and every count is unchanged — so today only a human eye can catch it.

This adds one more assertion to the same module (src/i18n_pack.rs), plus its negative control:

  • every T("key", { … }) call site in ui/js/app.js supplies all placeholders that either pack's text for that key uses (487 call sites, 0 violations today);
  • no value bound by a static data-i18n* attribute contains a placeholder — those are written by applyStatic() via innerHTML and have no argument channel at all, so such text would be wrong permanently (305 bound keys, 0 today).

The call-site scanner reuses the module's existing T("literal") recognition rule instead of reimplementing it: an earlier draft with a "smarter" rule found 517 call sites instead of 520, i.e. it counted a different set than every_t_literal_resolves. The tests now share one primitive, and the call-site count is asserted against the existing T_LITERAL_COUNT control.

Related Issue

None. This is a test-only change (CONTRIBUTING sanctions the test commit type and requires #[cfg(test)] tests for new functionality), and the repository has no open issue.

Changes

  • 功能/修复说明: no functional change — tests only. No production code, no new dependency, no change to any language pack, ui/ file or behaviour. Adds 2 tests to the existing #[cfg(test)] mod i18n_pack (still compiled only in test builds).
  • 涉及配置/数据结构的改动已同步示例文件: N/A — no config or data-structure change.

Tests

  • cargo test 全部通过 — 155 passed (was 153)
  • cargo fmt --check 通过
  • 新增/更新了单元测试(如适用) — 2 added, both with explicit positive controls

The new tests are not merely green on today's tree — they are proven able to fail. Injecting (a) a dropped { n: … } argument at a real call site, (b) a renamed variable ({ count: … }), and (c) a placeholder on a statically bound key each turned the new assertion red with a message naming the offending key. The UI files were restored byte-for-byte afterwards (md5 verified against git show HEAD:…).

Checklist

  • 分支命名符合约定(feat/ /fix/ /docs/ ...) — deviation: used test/i18n-placeholder-gate. CONTRIBUTING lists feat/ fix/ docs/ refactor/ but does not list test/, while test is an allowed commit type and nothing but tests changed; the same deviation was taken and disclosed in test(ui): guard the language-pack invariants that were previously unchecked #170.
  • Commit message 使用 Conventional Commits 格式 — test(ui): …
  • 单一职责,改动最小化 — one assertion class, one file, +495/−18 lines in src/i18n_pack.rs

… require

The four key-resolution tests added in #170 check that every key exists, but nothing
checked that a call site actually passes the variables the text needs. ui/js/i18n.js
leaves the placeholder verbatim when a variable is missing, so T("cnt.calls") renders
as {n} 次 / {n} instead of 1,234 次 / 1,234 - a defect that fails no existing assertion.

Adds one assertion class to src/i18n_pack.rs plus its negative control:
- every T("key", { ... }) call site in ui/js/app.js supplies all placeholders that
  either pack uses for that key (487 sites, 0 violations today)
- no value bound by a static data-i18n* attribute contains a placeholder - those are
  written by applyStatic() with innerHTML and have no argument channel (305 keys, 0 today)

The call-site scanner reuses the module's existing T("literal") rule rather than
reimplementing it (a "smarter" draft counted 517 sites instead of 520, i.e. a
different set than every_t_literal_resolves).

Tests only: no production code, no new dependency, no language-pack or ui/ change.
@argszero
argszero merged commit 0de4bde into main Sep 11, 2026
1 check passed
@argszero
argszero deleted the test/i18n-placeholder-gate branch September 11, 2026 23:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant