Skip to content

[2.x] feat(gambits): localization alias support — English keywords always work regardless of locale#4570

Open
imorland wants to merge 7 commits into2.xfrom
im/gambit-localization-alias
Open

[2.x] feat(gambits): localization alias support — English keywords always work regardless of locale#4570
imorland wants to merge 7 commits into2.xfrom
im/gambit-localization-alias

Conversation

@imorland
Copy link
Copy Markdown
Member

@imorland imorland commented Apr 14, 2026

Summary

Resolves #4566.

  • Adds canonicalKey() and canonicalPattern() to BooleanGambit and KeyValueGambit. Both default to key(), so extensions that don't translate their keywords require no changes.
  • GambitManager.match() now tries the canonical English pattern as a second-pass alias when the translated pattern doesn't match — English keywords (is:hidden, author:behz, etc.) are always accepted regardless of the active locale.
  • Fixes SubscriptionFilter to accept only stable canonical values (follow/ignore) instead of raw locale strings leaking from the frontend. SubscriptionGambit.toFilter() now maps matched surface keywords to canonical values before sending them to the API.
  • Unrecognised subscription filter values now return an empty result set (WHERE 0 = 1) rather than crashing with a TypeError or returning all discussions.
  • Adds a Flarum\Subscriptions\Extend\Subscription extender so third-party extensions can register custom subscription types with their accepted filter aliases:
(new Flarum\Subscriptions\Extend\Subscription())
    ->addSubscriptionType('lurk', ['lurk', 'lurking', 'lurked'])

Extension authors

If your gambit's key() returns a translated string via app.translator.trans(), add a canonicalKey() override returning the hardcoded English keyword. That's the only change needed. See the companion docs PR flarum/docs#524 for full guidance.

Test plan

  • PHP: SubscriptionFilterTest — covers canonical values, surface aliases, negation, unrecognised values, and the Subscription extender (custom type registration + unregistered alias still returns empty)
  • JS: IGambit.test.ts — regression tests for all built-in gambit classes + TDD tests for canonicalKey() / canonicalPattern()
  • JS: GambitManager.test.ts — regression tests + localization alias describe block (translated vs canonical produce identical output, negation, unmatched keywords not consumed)

…ork regardless of locale

Adds `canonicalKey()` and `canonicalPattern()` to `BooleanGambit` and `KeyValueGambit`.
`GambitManager.match()` now tries the canonical English pattern as an alias when the
translated pattern doesn't match, so English keywords like `is:hidden` or `author:behz`
are always accepted even when the site locale has translated them.

Fixes `SubscriptionFilter` accepting raw locale strings from the frontend; it now
only accepts the stable canonical values `follow`/`ignore`. `SubscriptionGambit.toFilter()`
maps matched keywords to canonical values before sending them to the API.

Closes #4566
@imorland imorland requested a review from a team as a code owner April 14, 2026 16:33
@imorland imorland changed the title feat(gambits): localization alias support — English keywords always work regardless of locale [2.x] feat(gambits): localization alias support — English keywords always work regardless of locale Apr 14, 2026
@imorland imorland added this to the 2.0.0-rc.1 milestone Apr 14, 2026
imorland and others added 4 commits April 14, 2026 17:40
…on types

Adds a Flarum\Subscriptions\Extend\Subscription extender so third-party
extensions can register custom subscription types (e.g. 'lurk') with their
accepted filter aliases:

    (new Extend\Subscription())
        ->addSubscriptionType('lurk', ['lurk', 'lurking', 'lurked'])

SubscriptionFilter now resolves values through a container-bound registry
('flarum-subscriptions.subscription_types') rather than a hardcoded match
expression. The built-in 'follow' and 'ignore' types are seeded by the
subscriptions extension's own extend.php via the same extender.
…tures

- Add canonicalPattern() to the IGambit object literal in GambitsAutocomplete.tsx
  so it satisfies the updated interface (TS2345)
- Add __esModule: true to the nanoid Jest mock so Jest's CJS/ESM interop resolves
  the named export correctly
- Move lurk discussion/post/discussion_user fixtures into setUp() so the
  subscription_extender_registers_custom_type test can find them
@imorland imorland modified the milestones: 2.0.0-rc.1, 2.1 Apr 14, 2026
Jest runs with --experimental-vm-modules which does real ESM linking, so CJS
mock files cannot satisfy named ESM imports. Replace emptyModule.js and
nanoid.js with proper .mjs equivalents using ESM export syntax.

Fix SubscriptionFilterTest: seed the 'lurk-user' fixture under user 3 (not
user 2) to avoid polluting the negation tests' result sets; switch negation
assertions from assertEqualsCanonicalizing to assertContains/assertNotContains
so they remain correct regardless of how many discussions exist.

Replace the lurk custom-type test (which required seeding an invalid enum value)
with a test that registers an additional alias for the existing 'follow' type,
which is a valid use of the extender and exercises the same code path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2.x] Hardcoded gambit keywords prevent localization

2 participants