Skip to content

Add macros for the remaining ICU argument types - #82

Merged
k0d13 merged 2 commits into
mainfrom
kodie/icu-argument-type-macros
Aug 4, 2026
Merged

Add macros for the remaining ICU argument types#82
k0d13 merged 2 commits into
mainfrom
kodie/icu-argument-type-macros

Conversation

@k0d13

@k0d13 k0d13 commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Closes #74.

Adds the missing ICU argument types as macros: say.number, say.date, say.time (and <Say.Number>, <Say.Date>, <Say.Time>), plus plural offset.

Approach

These are fragments, not whole messages, and the existing pipeline already handles that for free: parseCallExpression returns a CompositeMessage, parseExpression recurses into nested say macros, convertMessageToIcu flattens composite children, and generateChildExpressions recurses. So say`You have ${say.number(n)} items` needed no new nesting machinery, only new call-kind recognition.

ArgumentMessage gains an optional format: { type, style? } rather than getting a new FormatMessage sibling. assignSequenceIdentifiers, collectAssignedIdentifiers, and generateChildExpressions all already branch on instanceof ArgumentMessage, so all three needed zero changes.

Styles stay ICU strings rather than Intl option objects: the catalogue is the source of truth, and only the ICU spelling round-trips back out of it.

Two findings from probing the runtime formatter

currency is excluded, and that isn't a style preference. {n, number, currency} doesn't error, it silently formats as ⁨{$n}⁩ with a stderr warning, because MF1 has nowhere to write the currency code. Shipping it would have been worse than omitting it. Literal patterns (#,##0.00) do work, so those are accepted. Documented with the Intl.NumberFormat workaround.

=0 in select was backwards. The issue lists numeric select keys as "emitted bare, not as =n". Both halves were off: getBranchCase did prefix =, and validateBranchIdentifier then rejected it, so say.select(tier, { 0: 'Free' }) was a hard build error. Probing settled it, =0 is a parse error under select while a bare 0 matches both 0 and '0'. getBranchCase now takes the kind, and the select-rejection branch is gone.

Also here

A pre-existing bug, fixed because I was editing the same files: the React example's fr/pl/ja catalogues still carried Assigned to <0>{1}</0> from before the say-tag rename. Ids are content hashes, so it no longer resolved and the example threw in every non-English locale. Happy to split it out if you'd rather.

Docs

Moved the new section after Select (it had orphaned Ordinals and Select from the branching group), and took a pass over the whole page. Net ~57 lines shorter despite the additions: merged the duplicated CLDR-categories explanation in Plurals, dropped the Context example repeated verbatim in Best practices, and collapsed the per-component JSX listing. Two genuine gaps filled, Ordinals presented numeric keys and CLDR categories as equivalent (1: '#st' leaves 21st as "21th"), and {' '} inside a <Say> extracts as a placeholder, which I hit writing the example.

Not included

Skeletons stay blocked on the formatter rejecting ::. spellout, RBNF ordinal, and choice are now documented as explicit "no"s.

Verification

  • 502 tests pass (78 new); pnpm check, oxlint, and all 10 package builds clean.
  • Ran saykit extract on both examples, the new ICU round-trips out of source.
  • Verified all 136 translations across both examples hash-match en and format cleanly through the real mf1ToMessage.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added locale-aware number, date, and time formatting for tagged templates and React components.
    • Added plural offsets and improved support for formatted message arguments and numeric select keys.
    • Expanded React and vanilla examples with progress, dates, times, queues, and watcher details.
  • Documentation
    • Documented formatting styles, plural offsets, JSX usage, and translation guidance.
  • Bug Fixes
    • Improved ICU output for select branches and validated unsupported formatting styles.

@changeset-bot

changeset-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 72e2648

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@saykit/config Minor
@saykit/transform-js Minor
@saykit/transform-jsx Minor
saykit Minor
@saykit/react Minor
@saykit/format-json Minor
@saykit/format-po Minor
babel-plugin-saykit Minor
unplugin-saykit Minor
@saykit/carbon Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
saykit Ready Ready Preview Aug 4, 2026 2:22am

@github-actions github-actions Bot added examples Updates or additions to example apps tests Modifications, additions, or fixes related to testing package: core Related to the core saykit package package: react Related to @saykit/react package: config Related to @saykit/config and the CLI website Updates to the documentation website package: transform-js Related to @saykit/transform-js package: transform-jsx Related to @saykit/transform-jsx labels Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@k0d13, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dd4e1dbc-d0d4-4381-80fe-9ce61e98d8a8

📥 Commits

Reviewing files that changed from the base of the PR and between 400a3f2 and 72e2648.

📒 Files selected for processing (23)
  • examples/react/src/board.ts
  • examples/react/src/components/board.tsx
  • examples/react/src/locales/en.po
  • examples/react/src/locales/fr.po
  • examples/react/src/locales/ja.po
  • examples/react/src/locales/pl.po
  • examples/vanilla/src/library.ts
  • examples/vanilla/src/locales/en.po
  • examples/vanilla/src/locales/fr.po
  • examples/vanilla/src/locales/ja.po
  • examples/vanilla/src/locales/pl.po
  • examples/vanilla/src/main.ts
  • packages/config/src/features/messages/convert.test.ts
  • packages/config/src/features/messages/convert.ts
  • packages/config/src/features/messages/format.test.ts
  • packages/config/src/features/messages/format.ts
  • packages/config/src/features/messages/identifier.test.ts
  • packages/config/src/features/messages/identifier.ts
  • packages/config/src/features/messages/types.ts
  • packages/integration-react/src/runtime/index.ts
  • packages/integration/src/runtime.test.ts
  • packages/integration/src/runtime.ts
  • website/content/core-concepts/messages.mdx

Walkthrough

The PR adds ICU number, date, and time macros, plural offsets, numeric select keys, formatter validation, JavaScript and JSX parser support, runtime declarations, examples, tests, documentation, and release changesets.

Changes

ICU formatting support

Layer / File(s) Summary
Message model and ICU conversion
packages/config/src/features/messages/*
Message types now support argument formats and choice offsets. Conversion emits formatted ICU arguments, plural offsets, and bare numeric select keys.
JavaScript and JSX parser support
packages/transform-js/src/*, packages/transform-jsx/src/*
Both parsers handle number, date, and time arguments, plural offsets, descriptor propagation, and formatter validation.
Runtime formatting APIs
packages/integration/src/*, packages/integration-react/src/runtime/index.ts
Runtime types and classes expose number, date, and time macros. React declarations expose matching components. Tests cover plugin errors and formatted output.
Example integration flows
examples/react/src/*, examples/vanilla/src/*
The examples render formatted completion, watcher, reservation, queue, date, time, and percentage messages with translated catalogues.
Documentation and release metadata
website/content/*, .changeset/*
Documentation describes formatting fragments, plural offsets, select keys, supported styles, and JSX behaviour. Changesets record the package releases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

  • k0d13/saykit#12 — Related message formatting and transformer parser changes.
  • k0d13/saykit#68 — Related parser, runtime, JSX, and identifier handling changes.
  • k0d13/saykit#80 — Related numeric select branch-key handling.

Poem

A rabbit counts the branches bright,
With dates and numbers set just right.
“Time” hops in, and “select” stays clear,
Plural offsets guide each ear.
New messages bloom across the site—
I thump my paws in pure delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarises the main change: adding macros for the remaining ICU argument types.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kodie/icu-argument-type-macros

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/react/src/board.ts`:
- Line 71: Update the `sprintEndsAt` initialization to preserve 17:00 local time
across daylight-saving transitions: create the date, set its hours to 17:00,
then advance it by three calendar days with `setDate` instead of adding
milliseconds.

In `@examples/react/src/components/board.tsx`:
- Around line 80-85: Update the formatter extraction comments in
examples/react/src/components/board.tsx at lines 80-85 to accurately document
the complete, sprintEndsAt date, and sprintEndsAt time placeholders. Also update
the comments in examples/vanilla/src/main.ts at lines 106-109 to identify the
queue message as say.plural and describe say.number separately; make no other
code changes.
- Around line 100-107: Update the plural branches in
examples/react/src/components/board.tsx (lines 100-107) and
examples/vanilla/src/main.ts (lines 111-119) to match total-count selection by
changing the exact zero branches to exact one branches, while preserving
offset-based “other” wording. Update every matching affected PO entry’s msgid
and msgstr from =0 to =1.

In `@packages/config/src/features/messages/format.test.ts`:
- Around line 55-59: Update validateArgumentStyle to reject the currency style
for number arguments, preventing unsupported formatter output. Change the test
around validateArgumentStyle to call it with number instead of date while
preserving the expected Invalid date style assertion only if the
implementation’s existing error message requires it.

In `@packages/config/src/features/messages/identifier.ts`:
- Around line 55-57: Update the validation flow around getBranchCase and
BRANCH_PATTERN to reject exact-value keys matching =\d+ when kind is 'select',
while preserving existing numeric exact-value handling for other branch types.
Add a regression test asserting validateBranchIdentifier('select', '=0') is
rejected.

In `@packages/config/src/features/messages/types.ts`:
- Around line 30-32: Restrict ArgumentFormat.type to the supported ArgumentType
union instead of a general string. At the ICU conversion boundary, validate type
and style combinations and reject offset values unless the argument kind permits
them, preventing offsets on select. Add direct-construction tests for the
message classes covering unsupported types/styles and invalid
ChoiceMessage.offset usage.

In `@packages/integration/src/types.ts`:
- Around line 28-31: Update the selector parameter types in Say.select and
Say.Select to accept string | number | Named<string | number>, matching
SelectOptions and allowing bare numeric ICU cases; add type coverage confirming
numeric selectors compile for both macro APIs.

In `@website/content/core-concepts/messages.mdx`:
- Around line 219-222: Update the `select` documentation to state that selector
values are compared as exact strings, so hyphenated values such as “sold-out”
must be normalized before calling `say.select`, or replaced with ICU-compatible
source discriminants; otherwise they fall through to `other`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f0bbfe92-c1ab-46fc-b1cc-262523e89b17

📥 Commits

Reviewing files that changed from the base of the PR and between deeebf0 and 400a3f2.

📒 Files selected for processing (32)
  • .changeset/brave-pumas-invent.md
  • .changeset/olive-moons-shake.md
  • examples/react/src/board.ts
  • examples/react/src/components/board.tsx
  • examples/react/src/locales/en.po
  • examples/react/src/locales/fr.po
  • examples/react/src/locales/ja.po
  • examples/react/src/locales/pl.po
  • examples/vanilla/src/library.ts
  • examples/vanilla/src/locales/en.po
  • examples/vanilla/src/locales/fr.po
  • examples/vanilla/src/locales/ja.po
  • examples/vanilla/src/locales/pl.po
  • examples/vanilla/src/main.ts
  • packages/config/src/features/messages/convert.test.ts
  • packages/config/src/features/messages/convert.ts
  • packages/config/src/features/messages/format.test.ts
  • packages/config/src/features/messages/format.ts
  • packages/config/src/features/messages/identifier.test.ts
  • packages/config/src/features/messages/identifier.ts
  • packages/config/src/features/messages/index.ts
  • packages/config/src/features/messages/types.ts
  • packages/integration-react/src/runtime/index.ts
  • packages/integration/src/runtime.test.ts
  • packages/integration/src/runtime.ts
  • packages/integration/src/types.ts
  • packages/transform-js/src/parser.test.ts
  • packages/transform-js/src/parser.ts
  • packages/transform-jsx/src/parser.test.ts
  • packages/transform-jsx/src/parser.ts
  • website/content/core-concepts/messages.mdx
  • website/content/integrations/react.mdx

Comment thread examples/react/src/board.ts Outdated
Comment thread examples/react/src/components/board.tsx Outdated
Comment thread examples/react/src/components/board.tsx
Comment thread packages/config/src/features/messages/format.test.ts Outdated
Comment thread packages/config/src/features/messages/identifier.ts
Comment thread packages/config/src/features/messages/types.ts
Comment thread packages/integration/src/types.ts
Comment thread website/content/core-concepts/messages.mdx Outdated
@k0d13

k0d13 commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@greptile review
@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@k0d13 I will review the changes.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds number, date, and time formatting fragments plus plural offsets across the configuration model, JavaScript and JSX transforms, runtime typings, examples, tests, and documentation.

  • Adds ICU argument format metadata and serialization.
  • Recognizes say.number, say.date, say.time, and their React equivalents.
  • Supports literal plural and ordinal offsets.
  • Corrects numeric select branch serialization and refreshes example catalogues.

Confidence Score: 5/5

The PR appears safe to merge with no concrete blocking or independently actionable non-blocking issues identified.

The formatted-argument pipeline preserves ICU formats while sharing raw descriptor values correctly, literal offsets are consistently parsed across JavaScript and JSX, and numeric select keys are serialized according to the runtime formatter’s contract.

Reviews (1): Last reviewed commit: "Address CodeRabbit review on ICU argumen..." | Re-trigger Greptile

@k0d13 k0d13 added the preview Publish a preview build and link it from the pull request label Aug 4, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@saykit/config

npm i https://pkg.pr.new/@saykit/config@82

@saykit/format-json

npm i https://pkg.pr.new/@saykit/format-json@82

@saykit/format-po

npm i https://pkg.pr.new/@saykit/format-po@82

saykit

npm i https://pkg.pr.new/saykit@82

@saykit/carbon

npm i https://pkg.pr.new/@saykit/carbon@82

@saykit/react

npm i https://pkg.pr.new/@saykit/react@82

babel-plugin-saykit

npm i https://pkg.pr.new/babel-plugin-saykit@82

unplugin-saykit

npm i https://pkg.pr.new/unplugin-saykit@82

@saykit/transform-js

npm i https://pkg.pr.new/@saykit/transform-js@82

@saykit/transform-jsx

npm i https://pkg.pr.new/@saykit/transform-jsx@82

commit: 72e2648

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

The preview build of 72e2648 is published. Open it in the playground to run this pull request against your own code, straight from the browser.

@github-actions github-actions Bot removed the preview Publish a preview build and link it from the pull request label Aug 4, 2026
@k0d13
k0d13 merged commit ff5f4c7 into main Aug 4, 2026
56 checks passed
@k0d13
k0d13 deleted the kodie/icu-argument-type-macros branch August 4, 2026 02:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

examples Updates or additions to example apps package: config Related to @saykit/config and the CLI package: core Related to the core saykit package package: react Related to @saykit/react package: transform-js Related to @saykit/transform-js package: transform-jsx Related to @saykit/transform-jsx tests Modifications, additions, or fixes related to testing website Updates to the documentation website

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add macros for the remaining ICU argument types (say.number, say.date, ...)

1 participant