Skip to content

Add Rillet integration multiple account export pages - #95562

Merged
lakchote merged 75 commits into
Expensify:mainfrom
s77rt:Rillet-integration-multiple-account-export
Jul 16, 2026
Merged

lakchote merged 75 commits into
Expensify:mainfrom
s77rt:Rillet-integration-multiple-account-export

Conversation

@s77rt

@s77rt s77rt commented Jul 8, 2026

Copy link
Copy Markdown
Member

Explanation of Change

Added Rillet-integration multiple account export pages

Fixed Issues

$ #94851
PROPOSAL:

Tests

  • Have a workspace with multiple assigned cards from different feeds (e.g. a Expensify Card and a Company Card)
  • Have Rillet integration connected to workspace
  1. Go to accouting
  2. Click on Export
  3. Verify there is a Configure exporting to multiple accounts toggle
  4. Enable the toggle
  5. Verify two more option rows appeared Card program account and Per-card account
  6. Verify you can update the Card program account for each program
  7. Verify you can clear the custom account for each program
  8. Verify you can update the Per-card account for each card
  9. Verify you can clear the custom account for each card
  10. Go to Company Cards > Card
  11. Verify you can update the custom account from there too (the option is named Rillet per-card account export)
Screen.Recording.2026-07-14.at.9.28.36.PM.mov
  • Verify that no errors appear in the JS console

Offline tests

Same as Tests

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 any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • 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 any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • 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

@melvin-bot

melvin-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running:

npx ts-node ./scripts/generateTranslations.ts --help

Typically, you'd want to translate only what you changed by running npx ts-node ./scripts/generateTranslations.ts --compare-ref main

@lakchote
lakchote requested review from lakchote and truph01 July 10, 2026 08:17

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

Two things before this ships:

  1. The per-card override stores the account code but the backend resolves that NVP as an account id, so exports fail for any card with an override. This one's a blocker since it breaks the main path of the feature.
  2. The custom account counts and error badges include closed/deactivated cards while the card list filters them out .

Comment thread src/pages/workspace/companyCards/utils.tsx Outdated
Comment thread src/libs/CardFeedUtils.ts Outdated
@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 48e73a12fcc..8afcd6c5a2c 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -5623,16 +5623,31 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
             exportToMultipleAccounts: 'Export in mehrere Konten konfigurieren',
             cardProgramAccount: {
                 label: 'Kartenprogramm-Konto',
-                description: 'Überschreiben Sie das Arbeitsbereichskonto für diese Kartenprogramme.',
-                descriptionLevel2: 'Überschreiben Sie das Arbeitsbereichskonto für dieses Kartenprogramm.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Alle Programme verwenden das Standardkonto' : `${customAccountsCount} Programme mit benutzerdefinierten Konten`,
+                description: 'Workspace-Konto für diese Kartenprogramme überschreiben.',
+                descriptionLevel2: 'Überschreiben Sie das Workspace-Konto für dieses Kartenprogramm.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Alle Programme verwenden das Standardkonto';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} Programm mit benutzerdefiniertem Konto`;
+                    }
+                    return `${customAccountsCount} Programme mit benutzerdefinierten Konten`;
+                },
             },
             cardAccount: {
-                label: 'Konto pro Karte',
+                label: 'Kontotyp pro Karte',
                 description: 'Programmkonto für einzelne Karten überschreiben.',
-                descriptionLevel2: 'Programm­konto für diese Karten überschreiben.',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? 'Alle Karten verwenden Programmkonten' : `${customAccountsCount} Karten mit benutzerdefinierten Konten`),
+                descriptionLevel2: 'Programmkonto für diese Karten überschreiben.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Alle Karten verwenden Programmkonten';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} Karte mit individuellem Konto`;
+                    }
+                    return `${customAccountsCount} Karten mit benutzerdefinierten Konten`;
+                },
             },
         },
         type: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 2a207a96315..ded23f046d7 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -5457,17 +5457,31 @@ ${amount} para ${merchant} - ${date}`,
             exportToMultipleAccounts: 'Configura la exportación a varias cuentas',
             cardProgramAccount: {
                 label: 'Cuenta del programa de tarjetas',
-                description: 'Sobrescribe la cuenta del espacio de trabajo para estos programas de tarjetas.',
+                description: 'Reemplaza la cuenta del espacio de trabajo para estos programas de tarjetas.',
                 descriptionLevel2: 'Sobrescribe la cuenta del espacio de trabajo para este programa de tarjetas.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Todos los programas usan la cuenta predeterminada' : `${customAccountsCount} programas con cuentas personalizadas`,
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Todos los programas usan la cuenta predeterminada';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} programa con cuenta personalizada`;
+                    }
+                    return `${customAccountsCount} programas con cuentas personalizadas`;
+                },
             },
             cardAccount: {
                 label: 'Cuenta por tarjeta',
                 description: 'Sobrescribe la cuenta del programa para tarjetas individuales.',
                 descriptionLevel2: 'Reemplaza la cuenta del programa para estas tarjetas.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Todas las tarjetas usan cuentas del programa' : `${customAccountsCount} tarjetas con cuentas personalizadas`,
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Todas las tarjetas usan cuentas de programa';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} tarjeta con cuenta personalizada`;
+                    }
+                    return `${customAccountsCount} tarjetas con cuentas personalizadas`;
+                },
             },
         },
         type: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index ddbfba045dd..a777ac091d9 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -5634,20 +5634,34 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
                 label: 'Compte de règlement de facturation de voyage',
                 description: 'Choisissez votre compte de règlement et nous créerons le paiement dans Rillet.',
             },
-            exportToMultipleAccounts: 'Configurer l’exportation vers plusieurs comptes',
+            exportToMultipleAccounts: 'Configurer l’export vers plusieurs comptes',
             cardProgramAccount: {
-                label: 'Compte du programme de carte',
-                description: 'Remplacer le compte de l’espace de travail pour ces programmes de carte.',
-                descriptionLevel2: 'Remplacer le compte de l’espace de travail pour ce programme de carte.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Tous les programmes utilisent le compte par défaut' : `${customAccountsCount} programmes avec comptes personnalisés`,
+                label: 'Compte de programme de carte',
+                description: 'Remplacer le compte d’espace de travail pour ces programmes de cartes.',
+                descriptionLevel2: 'Remplacer le compte d’espace de travail pour ce programme de carte.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Tous les programmes utilisent le compte par défaut';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} programme avec compte personnalisé`;
+                    }
+                    return `${customAccountsCount} programmes avec comptes personnalisés`;
+                },
             },
             cardAccount: {
                 label: 'Compte par carte',
                 description: 'Remplacez le compte de programme pour des cartes individuelles.',
-                descriptionLevel2: 'Remplacer le compte du programme pour ces cartes.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Toutes les cartes utilisent des comptes de programme' : `${customAccountsCount} cartes avec comptes personnalisés`,
+                descriptionLevel2: 'Remplacer le compte de programme pour ces cartes.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Toutes les cartes utilisent des comptes de programme';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} carte avec compte personnalisé`;
+                    }
+                    return `${customAccountsCount} cartes avec comptes personnalisés`;
+                },
             },
         },
         type: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index de1a42fcdf1..8f0853f2bdb 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -5603,19 +5603,34 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
             settlementAccount: {label: 'Conto di regolamento Carta Expensify', description: 'Scegli il tuo conto di regolamento e creeremo il pagamento in Rillet.'},
             syncTravelInvoicingSettlements: 'Sincronizza le liquidazioni di fatturazione viaggi',
             travelInvoicingSettlementAccount: {label: 'Conto di regolamento fatturazione viaggi', description: 'Scegli il tuo conto di regolamento e creeremo il pagamento in Rillet.'},
-            exportToMultipleAccounts: 'Configura l’esportazione verso più conti',
+            exportToMultipleAccounts: 'Configura l’esportazione su più conti',
             cardProgramAccount: {
                 label: 'Conto del programma carta',
-                description: 'Sostituisci il conto della workspace per questi programmi di carte.',
-                descriptionLevel2: 'Sostituisci il conto spazio di lavoro per questo programma di carte.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Tutti i programmi usano il conto predefinito' : `${customAccountsCount} programmi con conti personalizzati`,
+                description: 'Sostituisci il conto dell’area di lavoro per questi programmi di carta.',
+                descriptionLevel2: 'Ignora il conto della workspace per questo programma di carte.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Tutti i programmi usano il conto predefinito';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} programma con conto personalizzato`;
+                    }
+                    return `${customAccountsCount} programmi con conti personalizzati`;
+                },
             },
             cardAccount: {
                 label: 'Conto per carta',
-                description: 'Sostituisci il conto del programma per le singole carte.',
-                descriptionLevel2: 'Sostituisci il conto programma per queste carte.',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? 'Tutte le carte usano conti del programma' : `${customAccountsCount} carte con conti personalizzati`),
+                description: 'Ignora il conto del programma per le singole carte.',
+                descriptionLevel2: 'Ignora il conto del programma per queste carte.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Tutte le carte usano conti di programma';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} carta con conto personalizzato`;
+                    }
+                    return `${customAccountsCount} carte con conti personalizzati`;
+                },
             },
         },
         type: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index aae3a9f7e02..dc294d4c436 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -5543,19 +5543,34 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
             settlementAccount: {label: 'Expensify カードの決済口座', description: '精算用の口座を選択すると、Rillet で支払いを作成します。'},
             syncTravelInvoicingSettlements: '出張請求の精算を同期',
             travelInvoicingSettlementAccount: {label: '出張請求の精算口座', description: '精算用の口座を選択すると、Rillet で支払いを作成します。'},
-            exportToMultipleAccounts: '複数の勘定科目へのエクスポートを設定する',
+            exportToMultipleAccounts: '複数アカウントへのエクスポートを設定',
             cardProgramAccount: {
                 label: 'カードプログラム口座',
                 description: 'これらのカードプログラムのワークスペース口座を上書きします。',
-                descriptionLevel2: 'このカードプログラムのワークスペースアカウントを上書きします。',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'すべてのプログラムがデフォルトアカウントを使用します' : `カスタム勘定科目を使用しているプログラムが ${customAccountsCount} 件あります`,
+                descriptionLevel2: 'このカードプログラムのワークスペース口座を上書きします。',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'すべてのプログラムがデフォルトアカウントを使用します';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `カスタム勘定科目付きプログラム:${customAccountsCount}`;
+                    }
+                    return `${customAccountsCount} 件のカスタムアカウントを含むプログラム`;
+                },
             },
             cardAccount: {
-                label: 'カード単位の口座',
-                description: '個々のカードのプログラム口座を上書きします。',
+                label: 'カード単位のアカウント',
+                description: '個々のカードに対してプログラム口座を上書きします。',
                 descriptionLevel2: 'これらのカードのプログラム口座を上書きします。',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? 'すべてのカードはプログラムアカウントを使用します' : `カスタム口座付きカード ${customAccountsCount} 枚`),
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'すべてのカードはプログラムアカウントを使用します';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `カスタム口座のカードが ${customAccountsCount} 枚`;
+                    }
+                    return `${customAccountsCount} 枚のカスタムアカウント付きカード`;
+                },
             },
         },
         type: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index b81effffd28..88a1f2b5ded 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -5593,19 +5593,34 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
                 label: 'Rekening voor verrekening van reiskostenfacturen',
                 description: 'Kies je vereffeningsrekening en we maken de betaling voor je aan in Rillet.',
             },
-            exportToMultipleAccounts: 'Instellen van exporteren naar meerdere rekeningen',
+            exportToMultipleAccounts: 'Export naar meerdere rekeningen instellen',
             cardProgramAccount: {
                 label: 'Kaartprogramma-account',
-                description: 'Overschrijf de werkruimte-account voor deze kaartprogramma’s.',
-                descriptionLevel2: 'Overschrijf de werkruimte-account voor dit kaartprogramma.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Alle programma’s gebruiken de standaardrekening' : `${customAccountsCount} programma’s met aangepaste accounts`,
+                description: "Overschrijf de werkruimte-account voor deze kaartprogramma's.",
+                descriptionLevel2: 'Overschrijf het werkruimteaccount voor dit kaartprogramma.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Alle programma’s gebruiken de standaardrekening';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} programma met aangepaste rekening`;
+                    }
+                    return `${customAccountsCount} programma's met aangepaste rekeningen`;
+                },
             },
             cardAccount: {
-                label: 'Rekening per kaart',
+                label: 'Account per kaart',
                 description: 'Overschrijf de programmarekening voor individuele kaarten.',
-                descriptionLevel2: 'Overschrijf de programmarekening voor deze kaarten.',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? 'Alle kaarten gebruiken programma-accounts' : `${customAccountsCount} kaarten met aangepaste rekeningen`),
+                descriptionLevel2: 'Stel een andere programmarekening in voor deze kaarten.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Alle kaarten gebruiken programmarekeningen';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} kaart met aangepaste rekening`;
+                    }
+                    return `${customAccountsCount} kaarten met aangepaste rekeningen`;
+                },
             },
         },
         type: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index a8dc386dc11..519c81e012f 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -5580,15 +5580,30 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
             cardProgramAccount: {
                 label: 'Konto programu kartowego',
                 description: 'Zastąp konto przestrzeni roboczej dla tych programów kart.',
-                descriptionLevel2: 'Nadpisz konto workspace dla tego programu kart.',
-                countInfo: (customAccountsCount: number) =>
-                    !customAccountsCount ? 'Wszystkie programy używają domyślnego konta' : `${customAccountsCount} programy z niestandardowymi kontami`,
+                descriptionLevel2: 'Zastąp konto przestrzeni roboczej dla tego programu kart.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Wszystkie programy używają domyślnego konta';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} program z własnym kontem`;
+                    }
+                    return `${customAccountsCount} programy z własnymi kontami`;
+                },
             },
             cardAccount: {
-                label: 'Konto przypisane do karty',
-                description: 'Zastąp konto programu dla pojedynczych kart.',
-                descriptionLevel2: 'Nadpisz konto programu dla tych kart.',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? 'Wszystkie karty używają kont programów' : `${customAccountsCount} karty z kontami niestandardowymi`),
+                label: 'Konto dla każdej karty',
+                description: 'Nadpisz konto programu dla poszczególnych kart.',
+                descriptionLevel2: 'Zastąp konto programu dla tych kart.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Wszystkie karty korzystają z kont programów';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} karta z niestandardowym kontem`;
+                    }
+                    return `${customAccountsCount} karty z niestandardowymi kontami`;
+                },
             },
         },
         type: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 07568e8f49d..e61e7857251 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -5584,18 +5584,34 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
             settlementAccount: {label: 'Conta de liquidação do Cartão Expensify', description: 'Escolha sua conta de liquidação e nós criaremos o pagamento no Rillet.'},
             syncTravelInvoicingSettlements: 'Sincronizar liquidações de faturamento de viagens',
             travelInvoicingSettlementAccount: {label: 'Conta de liquidação de faturamento de viagem', description: 'Escolha sua conta de liquidação e nós criaremos o pagamento no Rillet.'},
-            exportToMultipleAccounts: 'Configurar exportação para múltiplas contas',
+            exportToMultipleAccounts: 'Configurar exportação para várias contas',
             cardProgramAccount: {
                 label: 'Conta do programa de cartão',
-                description: 'Substitua a conta do workspace para estes programas de cartão.',
-                descriptionLevel2: 'Substituir a conta do espaço de trabalho para este programa de cartão.',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? 'Todos os programas usam a conta padrão' : `${customAccountsCount} programas com contas personalizadas`),
+                description: 'Substitua a conta do espaço de trabalho para esses programas de cartão.',
+                descriptionLevel2: 'Substituir a conta do workspace para este programa de cartão.',
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Todos os programas usam a conta padrão';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} programa com conta personalizada`;
+                    }
+                    return `${customAccountsCount} programas com contas personalizadas`;
+                },
             },
             cardAccount: {
                 label: 'Conta por cartão',
                 description: 'Substitua a conta do programa para cartões individuais.',
                 descriptionLevel2: 'Substituir a conta do programa para estes cartões.',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? 'Todos os cartões usam contas de programa' : `${customAccountsCount} cartões com contas personalizadas`),
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return 'Todos os cartões usam contas de programa';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} cartão com conta personalizada`;
+                    }
+                    return `${customAccountsCount} cartões com contas personalizadas`;
+                },
             },
         },
         type: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index c857ab9c486..720e0a8e443 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -5436,15 +5436,31 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
             exportToMultipleAccounts: '配置导出到多个账户',
             cardProgramAccount: {
                 label: '卡计划账户',
-                description: '为这些卡片计划覆盖工作区账户。',
+                description: '为这些银行卡计划覆盖工作区账户。',
                 descriptionLevel2: '为此卡计划覆盖工作区账户。',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? '所有项目使用默认账户' : `${customAccountsCount} 个包含自定义科目的项目`),
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return '所有计划都使用默认账户';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} 个具有自定义账户的计划`;
+                    }
+                    return `${customAccountsCount} 个带有自定义科目的方案`;
+                },
             },
             cardAccount: {
                 label: '按卡计费账户',
-                description: '为单张卡片覆盖所属的项目账户。',
+                description: '为单张卡片覆盖默认项目账户。',
                 descriptionLevel2: '覆盖这些卡片的项目账户。',
-                countInfo: (customAccountsCount: number) => (!customAccountsCount ? '所有卡都使用项目账户' : `${customAccountsCount} 张带有自定义账户的卡`),
+                countInfo: (customAccountsCount: number) => {
+                    if (!customAccountsCount) {
+                        return '所有卡都使用项目账户';
+                    }
+                    if (customAccountsCount === 1) {
+                        return `${customAccountsCount} 张带有自定义账户的卡`;
+                    }
+                    return `${customAccountsCount} 张带有自定义账户的卡`;
+                },
             },
         },
         type: {

Note

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

View workflow run

@s77rt
s77rt requested a review from lakchote July 16, 2026 13:42
@lakchote

Copy link
Copy Markdown
Contributor

@s77rt can you please merge main?

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

LGTM

@lakchote
lakchote merged commit 6c61fe9 into Expensify:main Jul 16, 2026
35 checks passed
@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@github-actions

Copy link
Copy Markdown
Contributor

🚧 lakchote has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/lakchote in version: 9.4.36-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #96413 was identified to be related to this PR.

@lanitochka17

Copy link
Copy Markdown

Deploy Blocker #96414 was identified to be related to this PR.

@lanitochka17

Copy link
Copy Markdown

Deploy Blocker #96415 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #96417 was identified to be related to this PR.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/lakchote in version: 9.4.38-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 cancelled 🔪
🍎 iOS 🍎 cancelled 🔪

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/lakchote in version: 9.4.40-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/yuwenmemon in version: 9.4.40-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

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.

7 participants