diff --git a/package.json b/package.json index 45e6de4a..8693b2ca 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,9 @@ "i18next": "^25.2.1", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", + "i18next-icu": "^2.4.1", "input-otp": "^1.2.3", + "intl-messageformat": "^10.7.18", "lucide-react": "^0.544.0", "nanoid": "^5.0.6", "next": "15.4.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d33219b..ac53fcd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,9 +84,15 @@ importers: i18next-http-backend: specifier: ^3.0.2 version: 3.0.2 + i18next-icu: + specifier: ^2.4.1 + version: 2.4.1(intl-messageformat@10.7.18) input-otp: specifier: ^1.2.3 version: 1.4.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + intl-messageformat: + specifier: ^10.7.18 + version: 10.7.18 lucide-react: specifier: ^0.544.0 version: 0.544.0(react@19.1.1) @@ -821,6 +827,21 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@formatjs/ecma402-abstract@2.3.6': + resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==} + + '@formatjs/fast-memoize@2.2.7': + resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} + + '@formatjs/icu-messageformat-parser@2.11.4': + resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==} + + '@formatjs/icu-skeleton-parser@1.8.16': + resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==} + + '@formatjs/intl-localematcher@0.6.2': + resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + '@heroicons/react@2.2.0': resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} peerDependencies: @@ -3376,6 +3397,11 @@ packages: i18next-http-backend@3.0.2: resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==} + i18next-icu@2.4.1: + resolution: {integrity: sha512-fh01aSjGlnsR377J7mu/CM3wcdZTpvNZejapPfHI+YnVyiWwvGFT2gZOgecm9B19ttyAJ3ijmHG6r/2jiQqXCA==} + peerDependencies: + intl-messageformat: ^10.3.3 + i18next@25.2.1: resolution: {integrity: sha512-+UoXK5wh+VlE1Zy5p6MjcvctHXAhRwQKCxiJD8noKZzIXmnAX8gdHX5fLPA3MEVxEN4vbZkQFy8N0LyD9tUqPw==} peerDependencies: @@ -3413,6 +3439,9 @@ packages: react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + intl-messageformat@10.7.18: + resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -5759,6 +5788,32 @@ snapshots: '@floating-ui/utils@0.2.9': {} + '@formatjs/ecma402-abstract@2.3.6': + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/intl-localematcher': 0.6.2 + decimal.js: 10.5.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.7': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.11.4': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/icu-skeleton-parser': 1.8.16 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.16': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.2': + dependencies: + tslib: 2.8.1 + '@heroicons/react@2.2.0(react@19.1.1)': dependencies: react: 19.1.1 @@ -8503,6 +8558,10 @@ snapshots: transitivePeerDependencies: - encoding + i18next-icu@2.4.1(intl-messageformat@10.7.18): + dependencies: + intl-messageformat: 10.7.18 + i18next@25.2.1(typescript@5.7.2): dependencies: '@babel/runtime': 7.27.6 @@ -8534,6 +8593,13 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) + intl-messageformat@10.7.18: + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.4 + tslib: 2.8.1 + is-arrayish@0.2.1: {} is-arrayish@0.3.2: diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 76eeee63..ced3f99d 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -134,7 +134,7 @@ "request_error": "Error requesting notification", "saving_expense": "Error while saving expense", "setting_update_failed": "Failed to update setting", - "signin_error": "An error occurred while signing in: ", + "signin_error": "An error occurred while signing in: {{error}}", "signup_disabled": "Signup of new accounts is disabled on this instance", "something_went_wrong": "Something went wrong", "subscribe_error": "Cannot subscribe to notification", @@ -361,7 +361,6 @@ "lent": "lent", "owe": "owes", "paid": "paid", - "pay": "pays", "received": "received" }, "you": { @@ -372,6 +371,27 @@ "paid": "paid", "pay": "pay", "received": "received" + }, + "statements": { + "you_lent": "You lent", + "you_owe": "You owe", + "you_lent_amount": "You lent {{currency}} {{amount}}", + "you_owe_amount": "You owe {{currency}} {{amount}}", + "you_paid_amount": "You paid {{currency}} {{amount}}", + "you_received_amount": "You received {{currency}} {{amount}}", + "you_pay_user": "You pay {{user}}", + "user_pays_you": "{{user}} pays you", + "friend_owes_you": "{{friend}} owes {{youDative}}", + "you_owe_friend": "You owe {{friend}}", + "paid_for_beneficiary": "paid for {{beneficiary}}", + "user_paid_amount": "{{user}} paid {{currency}} {{amount}}", + "user_received_amount": "{{user}} received {{currency}} {{amount}}", + "user_paid_amount_to_user": "{{payer}} paid {{currency}} {{amount}} to {{receiver}}", + "user_received_amount_from_user": "{{payer}} received {{currency}} {{amount}} from {{receiver}}", + "you_paid_amount_to_user": "You paid {{currency}} {{amount}} to {{receiver}}", + "you_received_amount_from_user": "You received {{currency}} {{amount}} from {{payer}}", + "for_users": "for {{payer}} and {{receiver}}", + "user_pays_user": "{{payer}} pays {{receiver}}" } }, "expense_details": "Expense details", @@ -386,6 +406,7 @@ "settlement": "Settlement", "share_text": "Check out SplitPro. It's an open source free alternative for Splitwise", "today": "Today", - "in_group": "In group" + "in_group": "In group", + "remaining_balances": "+{{count}} {{count, plural, one {balance} other {balances}}}..." } } diff --git a/public/locales/it/common.json b/public/locales/it/common.json index 2ac6ac31..5b68451f 100644 --- a/public/locales/it/common.json +++ b/public/locales/it/common.json @@ -120,7 +120,7 @@ "request_error": "Errore durante la richiesta delle notifiche", "saving_expense": "Errore durante il salvataggio della spesa", "setting_update_failed": "Errore nell'aggiornamento dell'impostazione", - "signin_error": "Si è verificato un errore durante l'accesso: ", + "signin_error": "Si è verificato un errore durante l'accesso: {{error}}", "signup_disabled": "La registrazione di nuovi account è disabilitata in questa istanza", "something_went_wrong": "Qualcosa è andato storto", "subscribe_error": "Impossibile attivare le notifiche", @@ -308,7 +308,6 @@ "lent": "ha prestato", "owe": "deve", "paid": "ha pagato", - "pay": "paga", "received": "ha ricevuto" }, "you": { @@ -319,6 +318,27 @@ "paid": "hai pagato", "pay": "paga", "received": "hai ricevuto" + }, + "statements": { + "you_lent": "Hai prestato", + "you_owe": "Devi", + "you_lent_amount": "Hai prestato {{currency}} {{amount}}", + "you_owe_amount": "Devi {{currency}} {{amount}}", + "you_paid_amount": "Hai pagato {{currency}} {{amount}}", + "you_received_amount": "Hai ricevuto {{currency}} {{amount}}", + "you_pay_user": "Paghi {{user}}", + "user_pays_you": "{{user}} ti paga", + "friend_owes_you": "{{friend}} ti deve", + "you_owe_friend": "Devi a {{friend}}", + "paid_for_beneficiary": "pagato per {{beneficiary}}", + "user_paid_amount": "{{user}} ha pagato {{currency}} {{amount}}", + "user_received_amount": "{{user}} ha ricevuto {{currency}} {{amount}}", + "user_paid_amount_to_user": "{{payer}} ha pagato {{currency}} {{amount}} a {{receiver}}", + "user_received_amount_from_user": "{{payer}} ha ricevuto {{currency}} {{amount}} da {{receiver}}", + "you_paid_amount_to_user": "Hai pagato {{currency}} {{amount}} a {{receiver}}", + "you_received_amount_from_user": "Hai ricevuto {{currency}} {{amount}} da {{payer}}", + "for_users": "per {{payer}} e {{receiver}}", + "user_pays_user": "{{payer}} paga {{receiver}}" } }, "expense_details": "Dettagli spesa", @@ -332,6 +352,7 @@ "settled_up": "Regolato", "settlement": "Regolamento", "share_text": "Dai un'occhiata a SplitPro. È un'alternativa gratuita e open source a Splitwise", - "today": "Oggi" + "today": "Oggi", + "remaining_balances": "+{{count}} {{count, plural, one {saldo} other {saldi}}}..." } } diff --git a/src/components/Expense/BalanceEntry.tsx b/src/components/Expense/BalanceEntry.tsx index 380b9f92..da5ae93a 100644 --- a/src/components/Expense/BalanceEntry.tsx +++ b/src/components/Expense/BalanceEntry.tsx @@ -36,7 +36,7 @@ export const BalanceEntry: React.FC<{ isPositive ? 'text-emerald-500' : 'text-orange-600', )} > - {t('actors.you')} {t(`ui.expense.you.${isPositive ? 'lent' : 'owe'}`)} + {t(`ui.expense.statements.${isPositive ? 'you_lent' : 'you_owe'}`)}
{toUIString(amount)} diff --git a/src/components/Expense/ExpenseList.tsx b/src/components/Expense/ExpenseList.tsx index 9a44401a..abe3a554 100644 --- a/src/components/Expense/ExpenseList.tsx +++ b/src/components/Expense/ExpenseList.tsx @@ -4,9 +4,9 @@ import Image from 'next/image'; import Link from 'next/link'; import React from 'react'; import { - CURRENCY_CONVERSION_ICON, CategoryIcon, - SETTLEUP_ICON, + CURRENCY_CONVERSION_ICON as CurrencyConversionIcon, + SETTLEUP_ICON as SettleUpIcon, } from '~/components/ui/categoryIcons'; import type { ExpenseRouter } from '~/server/api/routers/expense'; import { useTranslationWithUtils } from '~/hooks/useTranslationWithUtils'; @@ -70,6 +70,10 @@ const Expense: ExpenseComponent = ({ e, userId }) => { const yourExpenseAmount = youPaid ? (theirExpense?.amount ?? yourExpense?.amount ?? 0n) : -(yourExpense?.amount ?? 0n); + const isYouPayer = e.paidBy === userId; + const amountStatementKey = `ui.expense.statements.${isYouPayer ? 'you' : 'user'}_${ + e.amount < 0n ? 'received_amount' : 'paid_amount' + }`; const { toUIString } = getCurrencyHelpersCached(e.currency); @@ -83,8 +87,11 @@ const Expense: ExpenseComponent = ({ e, userId }) => {

{e.name}

- {displayName(e.paidByUser, userId)}{' '} - {t(`ui.expense.user.${e.amount < 0n ? 'received' : 'paid'}`)} {toUIString(e.amount)} + {t(amountStatementKey, { + user: displayName(e.paidByUser, userId), + currency: e.currency, + amount: toUIString(e.amount), + })}

@@ -94,7 +101,7 @@ const Expense: ExpenseComponent = ({ e, userId }) => {
- {t('actors.you')} {t(`ui.expense.you.${youPaid ? 'lent' : 'owe'}`)} + {t(`ui.expense.statements.${youPaid ? 'you_lent' : 'you_owe'}`)}
{toUIString(yourExpenseAmount)} @@ -117,18 +124,29 @@ const Settlement: ExpenseComponent = ({ e, userId }) => { const receiverId = e.expenseParticipants.find((p) => p.userId !== e.paidBy)?.userId; const userDetails = api.user.getUserDetails.useQuery({ userId: receiverId! }); - + const isYouPayer = e.paidBy === userId; + const isYouReceiver = receiverId === userId; + const settlementStatementKey = `ui.expense.statements.${ + isYouPayer ? 'you' : 'user' + }_${e.amount < 0n ? 'received_amount_from_user' : 'paid_amount_to_user'}`; + const receiverLabel = isYouReceiver + ? t('actors.you_dativus').toLowerCase() + : displayName(userDetails.data, userId); + const payerLabel = displayName(e.paidByUser, userId); return (
{toUIDate(e.expenseDate)}
- +

- {displayName(e.paidByUser, userId)}{' '} - {t(`ui.expense.user.${e.amount < 0n ? 'received' : 'paid'}`)} {toUIString(e.amount)}{' '} - {t('ui.expense.to')} {displayName(userDetails.data, userId)} + {t(settlementStatementKey, { + payer: payerLabel, + receiver: receiverLabel, + currency: e.currency, + amount: toUIString(e.amount), + })}

@@ -146,7 +164,7 @@ const CurrencyConversion: ExpenseComponent = ({ e, userId }) => {
{toUIDate(e.expenseDate)}
- +

{getCurrencyHelpersCached(e.currency).toUIString(e.amount)} ➡️{' '} @@ -156,8 +174,10 @@ const CurrencyConversion: ExpenseComponent = ({ e, userId }) => { }

- {t('ui.expense.for')} {displayName(e.paidByUser, userId)} {t('ui.and')}{' '} - {displayName(userDetails.data, userId)} + {t('ui.expense.statements.for_users', { + payer: displayName(e.paidByUser, userId), + receiver: displayName(userDetails.data, userId), + })}

diff --git a/src/components/Friend/FriendBalance.tsx b/src/components/Friend/FriendBalance.tsx index 781805cf..e3e1eb79 100644 --- a/src/components/Friend/FriendBalance.tsx +++ b/src/components/Friend/FriendBalance.tsx @@ -19,7 +19,7 @@ export const FriendBalance: React.FC<{ user: User; balance: Balance }> = ({ user
- {t('actors.you')} {isPositive ? t('ui.expense.you.lent') : t('ui.expense.you.owe')} + {t(`ui.expense.statements.${isPositive ? 'you_lent' : 'you_owe'}`)}
{toUIString(balance.amount)} diff --git a/src/components/Friend/GroupSettleup.tsx b/src/components/Friend/GroupSettleup.tsx index 49ba8e5f..73ce2f39 100644 --- a/src/components/Friend/GroupSettleup.tsx +++ b/src/components/Friend/GroupSettleup.tsx @@ -40,8 +40,19 @@ export const GroupSettleUp: React.FC<{ const addExpenseMutation = api.expense.addOrEditExpense.useMutation(); const utils = api.useUtils(); - const sender = 0 > _amount ? user : friend; - const receiver = 0 > _amount ? friend : user; + const sender = 0 > amount ? user : friend; + const receiver = 0 > amount ? friend : user; + const payerRole = sender.id === user.id ? 'you' : 'other'; + const receiverRole = receiver.id === user.id ? 'you' : 'other'; + const paymentText = + payerRole === 'you' + ? t('ui.expense.statements.you_pay_user', { user: displayName(receiver) }) + : receiverRole === 'you' + ? t('ui.expense.statements.user_pays_you', { user: displayName(sender) }) + : t('ui.expense.statements.user_pays_user', { + payer: displayName(sender), + receiver: displayName(receiver), + }); const saveExpense = React.useCallback(() => { if (!amount) { @@ -98,11 +109,7 @@ export const GroupSettleUp: React.FC<{
-

- {displayName(sender, data?.user.id)}{' '} - {t(`ui.expense.${sender.id === data?.user.id ? 'you' : 'user'}.pay`)}{' '} - {displayName(receiver, data?.user.id)} -

+

{paymentText}

{isCurrentUserPaying - ? `${t('actors.you')} ${t('ui.expense.you.pay')} ${displayName(friend)}` - : `${displayName(friend)} ${t('ui.expense.user.pay')} ${t('actors.you')}`} + ? t('ui.expense.statements.you_pay_user', { user: displayName(friend) }) + : t('ui.expense.statements.user_pays_you', { user: displayName(friend) })}

= ({ const youLent = Object.entries(cumulatedBalances).filter(([_, amount]) => 0 < amount); const youOwe = Object.entries(cumulatedBalances).filter(([_, amount]) => 0 > amount); + const youDative = t('actors.you_dativus').toLowerCase(); return (
{0 < youLent.length ? (
- {t('actors.you')} {t('ui.expense.you.lent')} + {t('ui.expense.statements.you_lent')} {youLent.map(([currency, amount], index, arr) => (
@@ -68,7 +69,7 @@ const GroupMyBalance: React.FC = ({ {0 < youOwe.length ? (
- {t('actors.you')} {t('ui.expense.you.owe')} + {t('ui.expense.statements.you_owe')} {youOwe.map(([currency, amount], index, arr) => (
@@ -93,8 +94,13 @@ const GroupMyBalance: React.FC = ({ {Object.entries(balances).map(([currency, amount]) => (
{0 < amount - ? `${friend?.name} ${t('ui.expense.user.owe')} ${t('actors.you_dativus').toLowerCase()}` - : `${t('actors.you')} ${t('ui.expense.you.owe')} ${friend?.name}`}{' '} + ? t('ui.expense.statements.friend_owes_you', { + friend: friend?.name ?? '', + youDative, + }) + : t('ui.expense.statements.you_owe_friend', { + friend: friend?.name ?? '', + })}{' '} {getCurrencyHelpersCached(currency).toUIString(amount)}
))} @@ -102,10 +108,11 @@ const GroupMyBalance: React.FC = ({ ); })} - {2 < Object.keys(friendBalances).length ? ( + {Object.keys(friendBalances).length > 2 ? (
- +{Object.keys(friendBalances).length - 2}{' '} - {Object.keys(friendBalances).length === 3 ? t('ui.balance') : t('ui.balances')}... + {t('ui.remaining_balances', { + count: Object.keys(friendBalances).length - 2, + })}
) : null}
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 5b75a724..f866fe3c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -19,6 +19,9 @@ import { useAddExpenseStore } from '~/store/addStore'; import { useAppStore } from '~/store/appStore'; import { type NextPageWithUser } from '~/types'; import { api } from '~/utils/api'; +import { registerICU } from '~/utils/i18n/registerICU'; + +registerICU(); const poppins = Poppins({ weight: ['200', '300', '400', '500', '600', '700'], subsets: ['latin'] }); const toastOptions = { duration: 1500 }; diff --git a/src/pages/auth/signin.tsx b/src/pages/auth/signin.tsx index dd8ff1ea..6c5eb5c9 100644 --- a/src/pages/auth/signin.tsx +++ b/src/pages/auth/signin.tsx @@ -102,7 +102,7 @@ const Home: NextPage<{ } else if ('SessionRequired' === error) { return; } else { - toast.error(t('errors.signin_error') + error); + toast.error(t('errors.signin_error', { error })); console.error('Error during sign-in:', error); } } diff --git a/src/pages/balances.tsx b/src/pages/balances.tsx index f9dd06d1..087deabd 100644 --- a/src/pages/balances.tsx +++ b/src/pages/balances.tsx @@ -57,9 +57,7 @@ const BalancePage: NextPageWithUser = () => {
-

- {t('actors.you')} {t('ui.expense.you.owe')} -

+

{t('ui.expense.statements.you_owe')}

@@ -78,9 +76,7 @@ const BalancePage: NextPageWithUser = () => {
-

- {t('actors.you')} {t('ui.expense.you.lent')} -

+

{t('ui.expense.statements.you_lent')}

diff --git a/src/pages/balances/[friendId].tsx b/src/pages/balances/[friendId].tsx index ebbf0ba0..925d01f6 100644 --- a/src/pages/balances/[friendId].tsx +++ b/src/pages/balances/[friendId].tsx @@ -81,7 +81,7 @@ const FriendPage: NextPageWithUser = ({ user }) => {
{0 < (youOwe?.length ?? 0) && ( <> - {t('actors.you')} {t('ui.expense.you.owe')}{' '} + {t('ui.expense.statements.you_owe')}{' '} {youOwe?.map((bal, index) => ( @@ -97,7 +97,7 @@ const FriendPage: NextPageWithUser = ({ user }) => {
{0 < (youLent?.length ?? 0) && ( <> - {t('actors.you')} {t('ui.expense.you.lent')}{' '} + {t('ui.expense.statements.you_lent')}{' '} {youLent?.map((bal, index) => ( diff --git a/src/utils/i18n/registerICU.ts b/src/utils/i18n/registerICU.ts new file mode 100644 index 00000000..67432e5b --- /dev/null +++ b/src/utils/i18n/registerICU.ts @@ -0,0 +1,13 @@ +import i18next from 'i18next'; +import ICU from 'i18next-icu'; + +let isRegistered = false; + +export const registerICU = (): void => { + if (isRegistered) { + return; + } + + i18next.use(new ICU()); + isRegistered = true; +}; diff --git a/src/utils/i18n/server.ts b/src/utils/i18n/server.ts index d34b7b74..d6ebd0d6 100644 --- a/src/utils/i18n/server.ts +++ b/src/utils/i18n/server.ts @@ -1,6 +1,9 @@ import { type SSRConfig } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import i18nConfig from '@/next-i18next.config.js'; +import { registerICU } from './registerICU'; + +registerICU(); export const customServerSideTranslations = async ( locale: string | undefined, diff --git a/src/utils/strings.ts b/src/utils/strings.ts index 3e60daf1..73d34063 100644 --- a/src/utils/strings.ts +++ b/src/utils/strings.ts @@ -60,7 +60,7 @@ export function generateSplitDescription( if (selectedParticipants.length === 1) { const beneficiary = selectedParticipants[0]; const beneficiaryName = displayName(t, beneficiary as User, currentUser.id) || 'someone'; - return `${t('ui.expense.user.paid')} ${t('ui.expense.for')} ${beneficiaryName}`; + return t('ui.expense.statements.paid_for_beneficiary', { beneficiary: beneficiaryName }); } // Case 2: Splitting with multiple people