Skip to content

Fix/cancel counterparty locale - #874

Merged
grunch merged 5 commits into
lnp2pBot:mainfrom
Matobi98:fix/cancel-counterparty-locale
Jul 10, 2026
Merged

Fix/cancel counterparty locale#874
grunch merged 5 commits into
lnp2pBot:mainfrom
Matobi98:fix/cancel-counterparty-locale

Conversation

@Matobi98

@Matobi98 Matobi98 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What

Fixes cancellation (and admin-cancel) messages being delivered in the wrong language — the caller's, the buyer's, or the admin's — instead of each recipient's own configured language.

Closes #873.

Root cause

Two independent layers:

  1. getUserI18nContext never resolved the user's real language. It read user.language, but the User model stores the language in lang (only Community has language). The field was always undefined, so every per-user context silently fell back to the default locale — affecting every counterparty-facing message, not just cancellations.

  2. Call sites passing the wrong context to a recipient. cancelAddInvoice built a single context from the buyer and reused it for the seller, and successCancelOrderByAdminMessage sent notices to both parties using the admin's ctx.i18n.

Changes

  • util/index.tsgetUserI18nContext now reads user.lang (with an 'en' fallback) and builds the context from the resolved language.
  • bot/commands.tscancelAddInvoice now resolves i18nCtxSeller / i18nCtxBuyer separately and passes the correct one to each messages.* call (mirroring the already-correct cancelShowHoldInvoice).
  • bot/messages.tssuccessCancelOrderByAdminMessage takes an explicit i18n parameter instead of using ctx.i18n.
  • bot/start.ts — the admin /cancelorder handler resolves the buyer's and seller's contexts and passes each to its own message.

Affected entry points (all verified)

All cancellation paths funnel through cancelAddInvoice, cancelShowHoldInvoice, and the cooperative cancelOrder:

  • /cancel (command and inline cancel buttons)
  • /cancelall
  • /cancelorder (admin / solver)
  • automatic order-expiration jobs

Tests

  • tests/bot/cancelLocale.spec.ts (new) — exercises every buyer/seller × taker/creator combination of the cancel flow and asserts, generically, that each message's i18n context matches its recipient's language. Verified the guard actually catches the bug by temporarily reintroducing it.
  • tests/util/index.spec.ts — adds unit tests locking getUserI18nContext to the lang field (and the 'en' fallback), covering the root cause directly.

All new tests pass (155 passing); lint and tsc are clean. The 28 pre-existing failures on main (DB/LND-dependent) are unchanged.

Steps to reproduce (before this fix)

  1. Seller sets bot language to Farsi (or any non-default language) and creates a sell order.
  2. A buyer with a different language takes the order (status WAITING_BUYER_INVOICE).
  3. Seller runs /cancelall (or /cancel).
  4. The cancellation confirmation is delivered in the wrong language instead of the recipient's configured language.

Summary by CodeRabbit

  • Bug Fixes

    • Cancellation notifications now consistently use each recipient’s preferred language for buyer, seller, and admin messages.
    • Cooperative and order-based cancel flows were updated so localized admin and counterparty messages reflect the correct locale per recipient.
  • Tests

    • Added dedicated test coverage to lock in language-correct cancellation messaging across roles and order states.
    • Extended utility tests to verify getUserI18nContext builds locale from user.lang and properly falls back to English.
  • Style

    • Reformatted the Persian locale text for cooperative-cancellation messaging without changing keys/content.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR fixes locale mismatches in cancellation notifications by deriving per-recipient i18n contexts in cancellation flows, updating the admin cancel message helper to accept an explicit i18n argument, simplifying locale selection, and adding regression tests.

Changes

Recipient-specific i18n for cancellation messages

Layer / File(s) Summary
Simplify getUserI18nContext locale resolution
util/index.ts, tests/util/index.spec.ts
getUserI18nContext now derives locale from user.lang with an 'en' fallback, and tests verify the locale and fallback behavior.
Per-recipient i18n in cancelAddInvoice
bot/commands.ts
cancelAddInvoice now sends the seller message with the seller context and derives a buyer-specific i18n context for the counterparty message.
Admin cancellation message accepts explicit i18n context
bot/messages.ts, bot/start.ts
successCancelOrderByAdminMessage now uses a passed-in i18n context, and cancelorder builds separate seller and buyer contexts before notifying each recipient.
Regression tests for recipient-language correctness
tests/bot/cancelLocale.spec.ts
A new test suite stubs cancellation flows and asserts that recipient-targeted messages use the matching language across the covered cancel paths.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • lnp2pBot/bot#763: Both PRs adjust cancellation flows to use recipient-specific i18n contexts in bot/commands.ts.
  • lnp2pBot/bot#821: Both PRs update cancelAddInvoice to pass the correct buyer/seller locale context into cancellation-related messages.
  • lnp2pBot/bot#665: Both PRs touch the same cancellation handlers in bot/commands.ts.

Suggested reviewers: grunch, mostronatorcoder, ermeme

Poem

I hop through locale fields so neat,
With buyer, seller, each in seat.
A message lands in language true,
No mixed-up words for me or you.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The fa.yaml whitespace-only reformat appears unrelated to the language-routing fix and adds no functional change. Remove or split the locale-file formatting changes unless they are required for a functional update.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR fixes the lang lookup, passes per-recipient i18n at cancellation/admin call sites, and adds regression tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: fixing cancellation message locale handling for counterparties.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

🧹 Nitpick comments (1)
util/index.ts (1)

388-395: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider caching the I18n instance to avoid repeated disk reads.

getUserI18nContext constructs new I18n({ directory: 'locales' }) on every invocation. With this PR's per-recipient pattern, a single cancel flow can call it 3–4 times, each reading the same locale files. A module-level singleton keyed by locale (or a single shared I18n with createContext per language) would eliminate redundant I/O.

♻️ Suggested refactor: reuse a single I18n instance
+// Module-level singleton — loaded once, reused across all calls.
+const sharedI18n = new I18n({
+  defaultLanguageOnMissing: true,
+  directory: 'locales',
+});
+
 const getUserI18nContext = async (user: UserDocument) => {
   const language = user.lang || 'en';
-  const i18n = new I18n({
-    locale: language,
-    defaultLanguageOnMissing: true,
-    directory: 'locales',
-  });
-
-  return i18n.createContext(language);
+  return sharedI18n.createContext(language);
 };
🤖 Prompt for 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.

In `@util/index.ts` around lines 388 - 395, `getUserI18nContext` in util/index.ts
is recreating `I18n` on every call, causing repeated locale-file reads. Refactor
it to reuse a cached `I18n` instance, ideally as a module-level singleton shared
across calls, and keep using `createContext(language)` for the requested locale.
If locale-specific state is needed, key the cache by language but avoid
reconstructing `new I18n({ directory: 'locales' })` repeatedly.
🤖 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.

Nitpick comments:
In `@util/index.ts`:
- Around line 388-395: `getUserI18nContext` in util/index.ts is recreating
`I18n` on every call, causing repeated locale-file reads. Refactor it to reuse a
cached `I18n` instance, ideally as a module-level singleton shared across calls,
and keep using `createContext(language)` for the requested locale. If
locale-specific state is needed, key the cache by language but avoid
reconstructing `new I18n({ directory: 'locales' })` repeatedly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 97a68042-e4c6-4ca7-b616-670425594e71

📥 Commits

Reviewing files that changed from the base of the PR and between 11e1dfe and 8b5ed66.

📒 Files selected for processing (6)
  • bot/commands.ts
  • bot/messages.ts
  • bot/start.ts
  • tests/bot/cancelLocale.spec.ts
  • tests/util/index.spec.ts
  • util/index.ts

@Matobi98
Matobi98 marked this pull request as draft July 8, 2026 19:14
@Matobi98
Matobi98 marked this pull request as ready for review July 8, 2026 19:38

@ermeme ermeme 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.

Reviewed current head. No blocking issues found.

@Luquitasjeffrey Luquitasjeffrey left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Cancelation message before the changes of this PR with the code of main

Image

Cancelation message with the changes of this PR:

Image

It happens when an admin cancels an order using the /cancelorder command.

tACK

Comment thread util/index.ts
Comment on lines +388 to 396
const language = user.lang || 'en';
const i18n = new I18n({
locale: language,
defaultLanguageOnMissing: true,
directory: 'locales',
});

return i18n.createContext(user.lang || language || 'en');
return i18n.createContext(language);
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a nice fix. user.language didn't existed so always falled back to user.lang or english.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@Luquitasjeffrey huh? there was no user.language string here

@grunch
grunch merged commit e28a12c into lnp2pBot:main Jul 10, 2026
5 checks passed
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.

Cancel flow (and admin cancel) send messages in the wrong recipient's language

4 participants