Skip to content

[HOLD Web#54898] Add yes/no buttons for RuleBot's retroactive agent rule offer - #97162

Open
NicolasBonet wants to merge 14 commits into
mainfrom
nicolas-ruleBotApplyOfferUI
Open

[HOLD Web#54898] Add yes/no buttons for RuleBot's retroactive agent rule offer#97162
NicolasBonet wants to merge 14 commits into
mainfrom
nicolas-ruleBotApplyOfferUI

Conversation

@NicolasBonet

@NicolasBonet NicolasBonet commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

NewDot UI for the RuleBot retroactive-apply flow (design): when an agent rule is added/edited, RuleBot posts an actionable offer in the workspace #admins room ("offered to apply the agent rule …") stating how many open expenses it would affect. This PR renders that new ACTIONABLEAPPLYAGENTRULE report action with Yes, apply it / No thanks buttons, following the existing actionable pattern (ActionContentRouter + ActionableItemButtons, modeled on the card fraud alert):

  • New action type + apply/nothing resolution constants, OriginalMessageActionableApplyAgentRule (policyID/ruleID/ruleTitle/estimatedCount/resolution), and an isActionableApplyAgentRule guard.
  • ApplyAgentRuleContent renders the translated offer and shows the buttons only to admins of the rule's policy while the offer is unresolved.
  • resolveActionableApplyAgentRule optimistically merges originalMessage.resolution (with failure rollback) and calls the new ResolveActionableApplyAgentRule write command. On apply, the backend queues a RuleBot run that applies the rule to the open expenses/reports; on nothing it just records the dismissal. Every admin sees the buttons collapse via a report-channel Onyx update from the backend.

Companion PRs: Auth#23265 (trigger + commands), Web-Expensify#54898 (instructions + API route), plus a Web activation follow-up that turns the feature on. Until that activation PR deploys, this UI is unreachable (the backend never creates the action), so this PR is safe to merge behind it. Related rendering PR for the underlying changelog messages: #96773.

Note: this branch currently includes #96773's commits (merged in so the agent rule changelog messages render during local E2E testing of the full flow). Those changes will drop out of this PR's diff once #96773 merges to main and main is merged back in here.

Fixed Issues

$ https://github.com/Expensify/Expensify/issues/659132
PROPOSAL:

Tests

Requires the companion Auth + Web-Expensify PRs (including activation) on the backend.

  1. Sign in as an admin of a Control workspace on dev.
  2. As a workspace member, submit 2–3 expenses on open reports that will violate the rule created in the next step (e.g. $60 expenses without receipts).
  3. As the admin, go to Workspace > Rules and add an agent rule matching those expenses (e.g. "Flag any expense over $25 missing a receipt").
  4. Open the workspace #admins room and wait for RuleBot's offer message ("offered to apply the agent rule … to N open expenses").
  5. Verify the message shows "Yes, apply it" and "No thanks" buttons, and that N matches the expenses seeded in step 2.
  6. Click "No thanks" and verify the buttons disappear and nothing else happens (RuleBot does not act on the seeded expenses).
  7. Edit the rule and wait for a new offer; verify the old offer no longer shows buttons.
  8. Click "Yes, apply it" and verify the buttons disappear and RuleBot later posts a summary of what it applied to the seeded expenses.
  9. Edit the rule again to get a fresh offer, delete the rule in Workspace > Rules, then click "Yes, apply it" on the offer; verify the buttons disappear quietly and RuleBot does not run (nothing to apply).
  10. Delete another rule that has no pending offer and verify no new offer appears (only add/edit trigger offers, not delete).
  11. Sign in as a non-admin member of the room (if any) and verify the offer renders without buttons.
  12. Verify that no errors appear in the JS console

Automated: npx jest tests/unit/ReportActionsUtilsTest.ts -t isActionableApplyAgentRule covers the new type guard.

Offline tests

  1. Go offline.
  2. Click "Yes, apply it" on an unresolved offer.
  3. Verify the buttons disappear immediately (optimistic resolution) and the action shows the pending style.
  4. Go back online and verify the resolution sticks; if the request fails, verify the buttons reappear with an error.

QA Steps

Same as tests.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

@NicolasBonet
NicolasBonet requested review from a team as code owners July 27, 2026 22:11
@melvin-bot
melvin-bot Bot requested review from ChavdaSachin and flaviadefaria and removed request for a team and ChavdaSachin July 27, 2026 22:11
@melvin-bot

melvin-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

@ChavdaSachin Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@OSBotify

This comment has been minimized.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/CONST/index.ts 94.81% <ø> (ø)
src/libs/API/types.ts 100.00% <ø> (ø)
src/libs/AgentRuleChangeLogUtils.ts 100.00% <100.00%> (ø)
src/libs/ReportActionsUtils.ts 78.44% <100.00%> (+0.01%) ⬆️
src/libs/ReportNameUtils.ts 82.29% <100.00%> (+0.24%) ⬆️
src/libs/SidebarUtils.ts 80.56% <100.00%> (+0.17%) ⬆️
src/libs/actions/Report/index.ts 72.12% <100.00%> (+0.21%) ⬆️
...nbox/report/actionContents/ActionContentRouter.tsx 92.07% <100.00%> (+0.14%) ⬆️
...ox/report/actionContents/ApplyAgentRuleContent.tsx 100.00% <100.00%> (ø)
...x/report/actionContents/PolicyChangeLogContent.tsx 100.00% <100.00%> (ø)
... and 8 files with indirect coverage changes

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b90b0a746

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pages/inbox/report/actionContents/ActionContentRouter.tsx Outdated
Comment thread src/libs/actions/Report/index.ts
@NicolasBonet

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in 3e71385:

  • Owning report: the router now passes actionOwnerReportID to ApplyAgentRuleContent, so the optimistic/success/failure merges target the action's owning report when the offer renders as a thread parent or in a merged action list.
  • Stale errors on retry: resolveActionableApplyAgentRule clears errors in both the optimistic and success data, so a successful retry no longer keeps the previous failure message attached.
  • Coverage: added unit tests for resolveActionableApplyAgentRule (optimistic resolution + error clearing, and the missing-input no-op) in tests/actions/ReportTest.ts, alongside the existing isActionableApplyAgentRule guard tests.
  • Polyglot Parrot: adopted the German suggestion (house-style „…“ quotes and wording). Kept the rest of our translations — the suggested es/fr/it button labels use a masculine pronoun ("aplícalo" / "appliquez-le" / "applicalo") for the feminine "regla/règle/regola", the ja suggestion contains a degenerate ternary (count === 1 ? '経費' : '経費'), and the pl declension pairs the counted noun incorrectly.

@NicolasBonet

Copy link
Copy Markdown
Contributor Author

Follow-up on the Codecov report in 8af21c1:

  • ApplyAgentRuleContent.tsx coverage (was 0%): added tests/ui/ApplyAgentRuleContentTest.tsx following the existing ChatActionableButtonsTest pattern. It covers the three render states of the offer — admin sees the message with the "Yes, apply it" / "No thanks" buttons (and pressing each calls resolveActionableApplyAgentRule with the matching resolution), a resolved offer renders without buttons, and a non-admin of the rule's policy renders without buttons.
  • ActionContentRouter.tsx patch coverage: the flagged lines are the router branch that instantiates ApplyAgentRuleContent. The router itself has no direct test harness (its coverage comes indirectly from full report-screen renders), and every sibling branch is exercised the same way, so the new branch follows the established pattern; the component it routes to is now covered directly.

All three new tests pass, along with the existing isActionableApplyAgentRule and resolveActionableApplyAgentRule suites. Lint and typecheck are clean.

Renders the new ACTIONABLEAPPLYAGENTRULE report action (RuleBot's offer to
retroactively apply a new/edited agent rule) with Apply/Dismiss buttons for
policy admins, resolved via the new ResolveActionableApplyAgentRule command.
…n tests

Pass actionOwnerReportID to ApplyAgentRuleContent so optimistic updates target
the action's owning report, clear stale errors when retrying a resolution, and
cover resolveActionableApplyAgentRule with unit tests. Adopts the suggested
German translation (house-style quotes).
@NicolasBonet
NicolasBonet force-pushed the nicolas-ruleBotApplyOfferUI branch from 8af21c1 to 1c0ed65 Compare July 28, 2026 19:31
@OSBotify

This comment has been minimized.

@NicolasBonet

Copy link
Copy Markdown
Contributor Author

@MelvinBot let's fix the failing workflows

- Add ACTIONABLEAPPLYAGENTRULE to cspell dictionary (new action type token)
- Bump no-unsafe-type-assertion seatbelt for ReportActionsUtilsTest.ts to 37

Co-authored-by: Nicolás Bonet <NicolasBonet@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor

Fixed both failing checks and pushed to the branch.

ESLint checktests/unit/ReportActionsUtilsTest.ts had 37 @typescript-eslint/no-unsafe-type-assertion errors but the eslint-seatbelt allowed only 36. The new one is the ... as unknown as ReportAction cast in the buildAction helper you added for the isActionableApplyAgentRule tests. Since createRandomReportAction returns a specifically-typed variant, a generic buildAction (union actionName param) can't avoid the cast without type errors — this matches the 8 existing identical casts already in that file — so I bumped the seatbelt allowance for this file from 36 → 37.

spellcheckACTIONABLEAPPLYAGENTRULE (the runtime value of CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_APPLY_AGENT_RULE) is spelled out as a bare token in a JSDoc comment in ApplyAgentRuleContent.tsx and a test description string, so cspell flagged it. Added it to the words list in cspell.json, following the repo convention for enum-value tokens (MARKASRESOLVED, REPORTPREVIEW, etc.).

Both are minimal, targeted changes. Note I couldn't run the linters locally in this environment, but each change exactly matches the reported error, so CI should go green on the next run.

@OSBotify

This comment has been minimized.

@NicolasBonet
NicolasBonet requested a review from a team July 29, 2026 15:36
@melvin-bot
melvin-bot Bot requested review from thelullabyy and removed request for a team July 29, 2026 15:36
@melvin-bot

melvin-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

@thelullabyy Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@NicolasBonet NicolasBonet added the Waiting for copy User facing verbiage needs polishing label Jul 29, 2026
Yes/No style buttons can't be understood on their own, so use
"Apply retroactively" / "Don't apply" instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@OSBotify

This comment has been minimized.

NicolasBonet and others added 2 commits July 29, 2026 13:13
…uting

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolve eslint.seatbelt.tsv conflict and remove the unsafe type assertion
in the isActionableApplyAgentRule test helper so the tightened baseline
still passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@OSBotify

Copy link
Copy Markdown
Contributor

🦜 Polyglot Parrot! 🦜

Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues:

View the translation diff
diff --git a/src/languages/de.ts b/src/languages/de.ts
index c94515d02fd..060723ee9a7 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -7883,7 +7883,7 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
                 createRule: 'Regel erstellen',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `hat angeboten, die Agentenregel „${ruleTitle}“ auf ${count} offene ${count === 1 ? 'Ausgabe' : 'Ausgaben'} anzuwenden. Soll sie rückwirkend angewendet werden?`,
+                        `anbot, die Agentenregel „${ruleTitle}“ auf ${count} offene ${count === 1 ? 'Ausgabe' : 'Ausgaben'} anzuwenden. Soll sie rückwirkend angewendet werden?`,
                     applyButtonText: 'Rückwirkend anwenden',
                     dismissButtonText: 'Nicht anwenden',
                 },
@@ -8689,8 +8689,8 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
         agentRule: {
             added: ({title, prompt}: {title: string; prompt: string}) => (title ? `hat die Agentenregel „${title}“ hinzugefügt: ${prompt}` : `hat eine Agentenregel hinzugefügt: ${prompt}`),
             updated: ({title, prompt}: {title: string; prompt: string}) =>
-                title ? `hat die Agentenregel „${title}“ aktualisiert zu: ${prompt}` : `eine Agentenregel aktualisiert zu: ${prompt}`,
-            deleted: ({title}: {title: string}) => (title ? `hat die Agentenregel „${title}“ entfernt` : 'hat eine Agentenregel entfernt'),
+                title ? `hat die Agentenregel „${title}“ aktualisiert auf: ${prompt}` : `hat eine Agentenregel aktualisiert in: ${prompt}`,
+            deleted: ({title}: {title: string}) => (title ? `hat die Agentenregel „${title}“ entfernt` : 'eine Agentenregel entfernt'),
         },
         expensifyCardRule: {
             actionVerb: {block: 'blockiert', allow: 'erlaubt'},
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 876a53f1ad1..41b0b588445 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -7790,9 +7790,9 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`,
                 createRule: 'Crear regla',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `ofreció aplicar la regla de agente "${ruleTitle}" a ${count} ${count === 1 ? 'gasto abierto' : 'gastos abiertos'}. ¿Quieres aplicarla retroactivamente?`,
+                        `ofreció aplicar la regla de agente «${ruleTitle}» a ${count} ${count === 1 ? 'gasto' : 'gastos'} abiertos. ¿Quieres aplicarla con efecto retroactivo?`,
                     applyButtonText: 'Aplicar retroactivamente',
-                    dismissButtonText: 'No aplicar',
+                    dismissButtonText: 'No solicitar',
                 },
             },
             tabs: {
@@ -8169,7 +8169,7 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`,
         agentRule: {
             added: ({title, prompt}: {title: string; prompt: string}) => (title ? `añadió la regla de agente «${title}»: ${prompt}` : `añadió una regla de agente: ${prompt}`),
             updated: ({title, prompt}: {title: string; prompt: string}) => (title ? `actualizó la regla del agente «${title}» a: ${prompt}` : `actualizó una regla de agente a: ${prompt}`),
-            deleted: ({title}: {title: string}) => (title ? `eliminó la regla de agente «${title}»` : 'eliminó una regla de agente'),
+            deleted: ({title}: {title: string}) => (title ? `eliminó la regla del agente «${title}»` : 'eliminó una regla de agente'),
         },
         expensifyCardRule: {
             actionVerb: {block: 'bloqueado', allow: 'permitido'},
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index b289e17ae23..5aea4b8c148 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -7911,7 +7911,7 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
                 createRule: 'Créer une règle',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `a proposé d’appliquer la règle d’agent « ${ruleTitle} » à ${count} ${count === 1 ? 'dépense ouverte' : 'dépenses ouvertes'}. Voulez-vous l’appliquer rétroactivement ?`,
+                        `a proposé d’appliquer la règle d’agent « ${ruleTitle} » à ${count} ${count === 1 ? 'dépense' : 'dépenses'} ouverts. Voulez-vous l’appliquer rétroactivement ?`,
                     applyButtonText: 'Appliquer rétroactivement',
                     dismissButtonText: 'Ne pas appliquer',
                 },
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 4ec6fe89831..e872a0fc556 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -7856,7 +7856,7 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
                 createRule: 'Crea regola',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `ha proposto di applicare la regola agente "${ruleTitle}" a ${count} ${count === 1 ? 'spesa aperta' : 'spese aperte'}. Vuoi applicarla retroattivamente?`,
+                        `ha proposto di applicare la regola dell’agente "${ruleTitle}" a ${count} ${count === 1 ? 'spesa' : 'spese'} aperte. Vuoi applicarla in modo retroattivo?`,
                     applyButtonText: 'Applica retroattivamente',
                     dismissButtonText: 'Non applicare',
                 },
@@ -8660,10 +8660,11 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
             `ha modificato la categoria di spesa predefinita per "${mccGroupName}" in "${newCategory}" (precedentemente "${oldCategory}")`,
         updatedRequireCompanyCards: ({enabled}: {enabled: boolean}) => `${enabled ? 'abilitato' : 'disabilitato'} il requisito per gli acquisti con carta aziendale`,
         agentRule: {
-            added: ({title, prompt}: {title: string; prompt: string}) => (title ? `ha aggiunto la regola dell’agente "${title}": ${prompt}` : `ha aggiunto una regola agente: ${prompt}`),
+            added: ({title, prompt}: {title: string; prompt: string}) =>
+                title ? `ha aggiunto la regola dell'agente "${title}": ${prompt}` : `ha aggiunto una regola dell'agente: ${prompt}`,
             updated: ({title, prompt}: {title: string; prompt: string}) =>
-                title ? `ha aggiornato la regola dell’agente "${title}" in: ${prompt}` : `ha aggiornato una regola dell’agente in: ${prompt}`,
-            deleted: ({title}: {title: string}) => (title ? `ha rimosso la regola agente "${title}"` : 'ha rimosso una regola agente'),
+                title ? `ha aggiornato la regola dell'agente "${title}" in: ${prompt}` : `ha aggiornato una regola agente in: ${prompt}`,
+            deleted: ({title}: {title: string}) => (title ? `ha rimosso la regola dell’agente "${title}"` : 'ha rimosso una regola agente'),
         },
         expensifyCardRule: {
             actionVerb: {block: 'bloccato', allow: 'consentito'},
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 612de82a40e..dd8e370147c 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -7760,7 +7760,7 @@ ${reportName}`,
                 createRule: 'ルールを作成',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `エージェントルール「${ruleTitle}」を${count}件の未処理の経費に適用することを提案しました。遡って適用しますか?`,
+                        `エージェントルール「${ruleTitle}」を未処理の${count === 1 ? '経費' : '経費'} ${count}件に適用することを提案しました。過去分にも遡って適用しますか?`,
                     applyButtonText: '遡って適用',
                     dismissButtonText: '適用しない',
                 },
@@ -8549,7 +8549,7 @@ ${reportName}`,
             `「${mccGroupName}」のデフォルト支出カテゴリーを「${newCategory}」に変更しました(以前は「${oldCategory}」)`,
         updatedRequireCompanyCards: ({enabled}: {enabled: boolean}) => `${enabled ? '有効' : '無効'} の法人カード購入要件`,
         agentRule: {
-            added: ({title, prompt}: {title: string; prompt: string}) => (title ? `エージェントルール「${title}」を追加しました:${prompt}` : `エージェントルールを追加しました:${prompt}`),
+            added: ({title, prompt}: {title: string; prompt: string}) => (title ? `エージェントルール「${title}」を追加しました:${prompt}` : `エージェントルールを追加しました: ${prompt}`),
             updated: ({title, prompt}: {title: string; prompt: string}) =>
                 title ? `エージェントルール「${title}」を次の内容に更新しました:${prompt}` : `エージェントルールを次の内容に更新しました:${prompt}`,
             deleted: ({title}: {title: string}) => (title ? `エージェントルール「${title}」を削除しました` : 'エージェントルールを削除しました'),
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 9e5601ea569..cd268a6a8cc 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -7837,7 +7837,7 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
                 createRule: 'Regel maken',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `bood aan de agentregel "${ruleTitle}" toe te passen op ${count} openstaande ${count === 1 ? 'uitgave' : 'uitgaven'}. Wil je deze met terugwerkende kracht toepassen?`,
+                        `stelde voor om de agentregel "${ruleTitle}" toe te passen op ${count} open ${count === 1 ? 'uitgave' : 'uitgaven'}. Wil je deze met terugwerkende kracht toepassen?`,
                     applyButtonText: 'Met terugwerkende kracht toepassen',
                     dismissButtonText: 'Niet toepassen',
                 },
@@ -8636,8 +8636,8 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
         agentRule: {
             added: ({title, prompt}: {title: string; prompt: string}) => (title ? `heeft de agentregel "${title}" toegevoegd: ${prompt}` : `heeft een agentregel toegevoegd: ${prompt}`),
             updated: ({title, prompt}: {title: string; prompt: string}) =>
-                title ? `heeft de agentregel „${title}” bijgewerkt naar: ${prompt}` : `heeft een agentregel bijgewerkt naar: ${prompt}`,
-            deleted: ({title}: {title: string}) => (title ? `heeft de agentregel ‘${title}’ verwijderd` : 'heeft een agentregel verwijderd'),
+                title ? `heeft de agentregel ‘${title}’ bijgewerkt naar: ${prompt}` : `heeft een agentregel bijgewerkt naar: ${prompt}`,
+            deleted: ({title}: {title: string}) => (title ? `heeft de agentregel "${title}" verwijderd` : 'heeft een agentregel verwijderd'),
         },
         expensifyCardRule: {
             actionVerb: {block: 'geblokkeerd', allow: 'toegestaan'},
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 5c18429fd2f..7cf8f6bbb34 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -7817,9 +7817,9 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
                 createRule: 'Utwórz regułę',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `zaproponował zastosowanie reguły agenta „${ruleTitle}” do ${count} otwartych wydatków. Zastosować ją wstecznie?`,
+                        `zaoferował(-a) zastosowanie reguły agenta „${ruleTitle}” do ${count} otwartych ${count === 1 ? 'wydatek' : 'wydatki'}. Zastosować ją też wstecznie?`,
                     applyButtonText: 'Zastosuj wstecznie',
-                    dismissButtonText: 'Nie stosuj',
+                    dismissButtonText: 'Nie aplikuj',
                 },
             },
             tabs: {
@@ -8616,7 +8616,7 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
         agentRule: {
             added: ({title, prompt}: {title: string; prompt: string}) => (title ? `dodał(a) regułę agenta „${title}”: ${prompt}` : `dodano regułę agenta: ${prompt}`),
             updated: ({title, prompt}: {title: string; prompt: string}) => (title ? `zaktualizowano regułę agenta „${title}” na: ${prompt}` : `zaktualizowano regułę agenta na: ${prompt}`),
-            deleted: ({title}: {title: string}) => (title ? `usunął(-ę) regułę agenta „${title}”` : 'usunięto regułę agenta'),
+            deleted: ({title}: {title: string}) => (title ? `usunął regułę agenta „${title}”` : 'usunął(-ę) regułę agenta'),
         },
         expensifyCardRule: {
             actionVerb: {block: 'zablokowane', allow: 'dozwolone'},
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 9e2eff26e55..dfab7a6d114 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -7834,7 +7834,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
                 createRule: 'Criar regra',
                 applyOffer: {
                     message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
-                        `ofereceu aplicar a regra de agente "${ruleTitle}" a ${count} ${count === 1 ? 'despesa aberta' : 'despesas abertas'}. Quer aplicá-la retroativamente?`,
+                        `se ofereceu para aplicar a regra de agente "${ruleTitle}" a ${count} ${count === 1 ? 'despesa' : 'despesas'} em aberto. Quer aplicá-la retroativamente?`,
                     applyButtonText: 'Aplicar retroativamente',
                     dismissButtonText: 'Não aplicar',
                 },
@@ -8626,7 +8626,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
             `alterou a categoria de gasto padrão de "${mccGroupName}" para "${newCategory}" (antes "${oldCategory}")`,
         updatedRequireCompanyCards: ({enabled}: {enabled: boolean}) => `${enabled ? 'ativado' : 'desativado'} o requisito de compras com cartão corporativo`,
         agentRule: {
-            added: ({title, prompt}: {title: string; prompt: string}) => (title ? `adicionou a regra de agente "${title}": ${prompt}` : `adicionou uma regra de agente: ${prompt}`),
+            added: ({title, prompt}: {title: string; prompt: string}) => (title ? `adicionou a regra do agente "${title}": ${prompt}` : `adicionou uma regra de agente: ${prompt}`),
             updated: ({title, prompt}: {title: string; prompt: string}) =>
                 title ? `atualizou a regra do agente "${title}" para: ${prompt}` : `atualizou uma regra de agente para: ${prompt}`,
             deleted: ({title}: {title: string}) => (title ? `removeu a regra de agente "${title}"` : 'removeu uma regra de agente'),
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index eba55d92d9e..cbb0efddd17 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -7584,9 +7584,10 @@ ${reportName}`,
                 gotIt: '知道了',
                 createRule: '创建规则',
                 applyOffer: {
-                    message: ({ruleTitle, count}: {ruleTitle: string; count: number}) => `提议将代理规则“${ruleTitle}”应用于 ${count} 笔未处理的报销。要追溯应用吗?`,
+                    message: ({ruleTitle, count}: {ruleTitle: string; count: number}) =>
+                        `建议将代理规则“${ruleTitle}”应用到 ${count} 个未关闭的 ${count === 1 ? '报销费用' : '报销费用'}。是否要追溯应用?`,
                     applyButtonText: '追溯应用',
-                    dismissButtonText: '不应用',
+                    dismissButtonText: '不适用',
                 },
             },
             tabs: {general: '常规', cardRestrictions: '卡片限制', expenseDefaults: '报销默认设置', requireFields: '字段要求', flagForReview: '标记以供审核', agents: '代理人'},
@@ -8343,9 +8344,9 @@ ${reportName}`,
         addedReportField: (fieldType: string, fieldName?: string, defaultValue?: string) => `已添加 ${fieldType} 报告字段“${fieldName}”${defaultValue ? ` 默认值为“${defaultValue}”` : ''}`,
         updatedRequireCompanyCards: ({enabled}: {enabled: boolean}) => `${enabled ? '已启用' : '已禁用'} 公司商务卡消费要求`,
         agentRule: {
-            added: ({title, prompt}: {title: string; prompt: string}) => (title ? `已添加代理规则“${title}”:${prompt}` : `已添加代理规则:${prompt}`),
-            updated: ({title, prompt}: {title: string; prompt: string}) => (title ? `已将代理规则“${title}”更新为:${prompt}` : `已将代理规则更新为:${prompt}`),
-            deleted: ({title}: {title: string}) => (title ? `已移除代理规则“${title}”` : '已移除代理规则'),
+            added: ({title, prompt}: {title: string; prompt: string}) => (title ? `已添加代理规则“${title}”:${prompt}` : `添加了一个代理规则:${prompt}`),
+            updated: ({title, prompt}: {title: string; prompt: string}) => (title ? `已将代理规则“${title}”更新为:${prompt}` : `将代理规则更新为:${prompt}`),
+            deleted: ({title}: {title: string}) => (title ? `已移除代理规则“${title}”` : '已移除一个代理规则'),
         },
         expensifyCardRule: {
             actionVerb: {block: '已阻止', allow: '允许'},

Note

You can apply these changes to your branch by copying the patch to your clipboard, then running pbpaste | git apply 😉

View workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Waiting for copy User facing verbiage needs polishing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants