diff --git a/client/src/components/Budget/CostsPanel.test.tsx b/client/src/components/Budget/CostsPanel.test.tsx
index 4eaba32ba..7f77d72a1 100644
--- a/client/src/components/Budget/CostsPanel.test.tsx
+++ b/client/src/components/Budget/CostsPanel.test.tsx
@@ -278,6 +278,43 @@ describe('CostsPanel — settlements in the ledger', () => {
expect(within(hotelRow).queryByText(/you lent|you borrowed/)).toBeNull()
})
+ it('lists who an expense is split between, with each part, in the row (#1763)', async () => {
+ seedStore(useAuthStore, { user: buildUser({ id: 1, username: 'alice' }), isAuthenticated: true })
+ seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, default_currency: 'EUR' } })
+ const custom = {
+ ...buildBudgetItem({ trip_id: 1, category: 'food', name: 'Dinner' }),
+ total_price: 90,
+ payers: [{ user_id: 1, amount: 90, username: 'alice' }],
+ members: [{ user_id: 1, username: 'alice', paid: 1, amount: 60 }, { user_id: 2, username: 'bob', paid: 0, amount: 30 }],
+ }
+ const crowd = {
+ ...buildBudgetItem({ trip_id: 1, category: 'activity', name: 'Boat' }),
+ total_price: 80,
+ payers: [{ user_id: 1, amount: 80, username: 'alice' }],
+ members: Array.from({ length: 8 }, (_, i) => ({ user_id: i + 1, username: `p${i + 1}`, paid: 0 })),
+ }
+ server.use(
+ http.get('/api/trips/1/budget', () => HttpResponse.json({ items: [custom, crowd] })),
+ http.get('/api/trips/1/budget/settlement', () => HttpResponse.json({ balances: [], flows: [], settlements: [] })),
+ )
+ render()
+
+ const dinnerRow = (await screen.findByText('Dinner')).closest('.exp-row') as HTMLElement
+ const chips = within(dinnerRow).getAllByTestId('split-chip')
+ expect(chips).toHaveLength(2)
+ expect(chips[0]).toHaveTextContent('60')
+ expect(chips[1]).toHaveTextContent('30')
+ // alice marked her part as paid; bob has not.
+ expect(within(chips[0]).getByLabelText('paid')).toBeInTheDocument()
+ expect(within(chips[1]).queryByLabelText('paid')).toBeNull()
+
+ // Eight people fold into five chips and a "+3".
+ const boatRow = (await screen.findByText('Boat')).closest('.exp-row') as HTMLElement
+ expect(within(boatRow).getAllByTestId('split-chip')).toHaveLength(5)
+ expect(within(boatRow).getByText('+3')).toBeInTheDocument()
+ expect(within(boatRow).getAllByTestId('split-chip')[0]).toHaveTextContent('10')
+ })
+
it('sums only unfinished expenses in the Outstanding amount card', async () => {
// Display in the trip's own currency so FX conversion is an identity — keeps the asserted sum deterministic.
seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, default_currency: 'EUR' } })
@@ -2463,8 +2500,10 @@ describe('CostsPanel: a bill entered in the display currency (#2525)', () => {
await screen.findByText('Aparthotel Silver')
expect(screen.getByText(/\$400\.88 → 342,63\s€$/)).toBeInTheDocument()
- expect(screen.getByText('$395.77')).toBeInTheDocument()
- expect(screen.getByText('$30.00')).toBeInTheDocument()
+ // The hotel's split chips can carry the same figure, so look past them to the payment.
+ const outsideChips = (text: string) => screen.queryAllByText(text).filter(el => !el.closest('[data-testid="split-chip"]'))
+ expect(outsideChips('$395.77')).toHaveLength(1)
+ expect(outsideChips('$30.00')).toHaveLength(1)
expect(screen.queryByText('$34.65')).toBeNull()
})
diff --git a/client/src/components/Budget/CostsPanel.tsx b/client/src/components/Budget/CostsPanel.tsx
index 4cdcdfe34..0c239e139 100644
--- a/client/src/components/Budget/CostsPanel.tsx
+++ b/client/src/components/Budget/CostsPanel.tsx
@@ -37,6 +37,9 @@ import { CountPill, EYEBROW } from '../Planner/bookings/bookingParts'
import CostsToolbar, { type CostsView } from './CostsToolbar'
import CostsTable, { CostsTableSummary } from './CostsTable'
+/** Split chips a row shows before the rest fold into "+N" (#1763). */
+const SPLIT_CHIP_MAX = 6
+
interface CostsPanelProps {
tripId: number
tripMembers?: TripMember[]
@@ -195,19 +198,20 @@ export default function CostsPanel({ tripId, tripMembers = [] }: CostsPanelProps
// total but stays out of settlements until who-paid is filled in. A negative
// total (a refund, #2176) is just as unfinished until its recipient is named.
const isUnfinished = (e: BudgetItem) => baseTotal(e) !== 0 && (e.payers || []).filter(p => p.amount !== 0).length === 0
- const myShareOf = (e: BudgetItem) => {
- // Nobody paid, so nobody owes: the ledger skips these entirely (#2225), and
- // counting them here left the tile contradicting the balances right beside it.
- if (isUnfinished(e)) return 0
- const myMember = (e.members || []).find(m => m.user_id === me)
- if (!myMember) return 0
- if (myMember.amount !== null && myMember.amount !== undefined) {
- return booked(myMember.amount, e)
+ // A member's part of an expense: the custom amount when one was set, else the
+ // equal split the server settles with, in the display currency.
+ const shareOf = (e: BudgetItem, userId: number) => {
+ const member = (e.members || []).find(m => m.user_id === userId)
+ if (!member) return 0
+ if (member.amount !== null && member.amount !== undefined) {
+ return booked(member.amount, e)
}
const shares = splitEqualShares(e.total_price || 0, e.members || [], e.id)
- const myShare = shares[me] || 0
- return booked(myShare, e)
+ return booked(shares[userId] || 0, e)
}
+ // Nobody paid, so nobody owes: the ledger skips these entirely (#2225), and
+ // counting them here left the tile contradicting the balances right beside it.
+ const myShareOf = (e: BudgetItem) => (isUnfinished(e) ? 0 : shareOf(e, me))
// `booked` carries the rates. They can land after the expenses, and without it in the
// deps the cards kept the sums they were first added up with while the rows moved on.
@@ -889,6 +893,7 @@ export default function CostsPanel({ tripId, tripMembers = [] }: CostsPanelProps
))}
)}
+ {SplitChips({ e })}
{/* The note, marked by a rule in the category colour instead of a box, so
@@ -912,6 +917,39 @@ export default function CostsPanel({ tripId, tripMembers = [] }: CostsPanelProps
)
}
+ // Who an expense is split between and each one's part (#1763), so checking that
+ // the right people are in no longer means opening every expense. Quieter than
+ // the payer chips above, and capped so a large group cannot crowd the row.
+ function SplitChips({ e }: { e: BudgetItem }) {
+ const members = e.members || []
+ if (members.length === 0) return null
+ const shown = members.length > SPLIT_CHIP_MAX ? members.slice(0, SPLIT_CHIP_MAX - 1) : members
+ const rest = members.slice(shown.length)
+ const chip = 'inline-flex items-center gap-[5px] rounded-full bg-surface-tertiary text-content-secondary'
+ return (
+
+
{t('costs.split')}
+ {shown.map(m => {
+ const share = fmt(shareOf(e, m.user_id))
+ return (
+
+
+
+ {share}
+ {m.paid ? : null}
+
+
+ )
+ })}
+ {rest.length > 0 && (
+
t('costs.splitChipLabel', { name: personName(m.user_id), amount: fmt(shareOf(e, m.user_id)) })).join(', ')}>
+ +{rest.length}
+
+ )}
+
+ )
+ }
+
// A row's note as a pill that opens in place; an empty one keeps the column.
function NoteCell({ note, noteKey, color }: { note: string; noteKey: string; color: string }) {
if (!note) return
diff --git a/shared/src/i18n/ar/budget.ts b/shared/src/i18n/ar/budget.ts
index a0ffdf20b..fc8cf9c05 100644
--- a/shared/src/i18n/ar/budget.ts
+++ b/shared/src/i18n/ar/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'اضغط للتضمين',
'costs.amount': 'المبلغ',
'costs.split': 'التقسيم',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'بالتساوي',
'costs.splitCustom': 'مخصص',
'costs.splitTicket': 'الإيصال',
diff --git a/shared/src/i18n/az/budget.ts b/shared/src/i18n/az/budget.ts
index 627e494f7..c681a2b23 100644
--- a/shared/src/i18n/az/budget.ts
+++ b/shared/src/i18n/az/budget.ts
@@ -141,6 +141,7 @@ const budget: TranslationStrings = {
'costs.splitBetween': 'Bərabər bölüşdürüləcək şəxslər',
'costs.split': 'Bölüşdür',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Bərabər',
'costs.splitCustom': 'Fərdi',
'costs.splitTicket': 'Qəbz üzrə',
diff --git a/shared/src/i18n/br/budget.ts b/shared/src/i18n/br/budget.ts
index e8ed7d58b..e49e08525 100644
--- a/shared/src/i18n/br/budget.ts
+++ b/shared/src/i18n/br/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Toque para incluir',
'costs.amount': 'Valor',
'costs.split': 'Dividir',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Igualmente',
'costs.splitCustom': 'Personalizado',
'costs.splitTicket': 'Recibo',
diff --git a/shared/src/i18n/ca/budget.ts b/shared/src/i18n/ca/budget.ts
index b93468f4b..f95b85dc4 100644
--- a/shared/src/i18n/ca/budget.ts
+++ b/shared/src/i18n/ca/budget.ts
@@ -118,6 +118,7 @@ const budget: TranslationStrings = {
'costs.whoPaid': 'Qui ha pagat?',
'costs.splitBetween': 'Dividir a parts iguals entre',
'costs.split': 'Dividir',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'A parts iguals',
'costs.splitCustom': 'Personalitzat',
'costs.splitTicket': 'Tiquet',
diff --git a/shared/src/i18n/cs/budget.ts b/shared/src/i18n/cs/budget.ts
index 424784b07..ab7cdd868 100644
--- a/shared/src/i18n/cs/budget.ts
+++ b/shared/src/i18n/cs/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Klepnutím zahrnout',
'costs.amount': 'Částka',
'costs.split': 'Rozdělení',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Rovným dílem',
'costs.splitCustom': 'Vlastní',
'costs.splitTicket': 'Účtenka',
diff --git a/shared/src/i18n/de/budget.ts b/shared/src/i18n/de/budget.ts
index 36a4a953b..859b14358 100644
--- a/shared/src/i18n/de/budget.ts
+++ b/shared/src/i18n/de/budget.ts
@@ -153,6 +153,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Zum Einbeziehen tippen',
'costs.amount': 'Betrag',
'costs.split': 'Aufteilen',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Gleichmäßig',
'costs.splitCustom': 'Individuell',
'costs.splitTicket': 'Beleg',
diff --git a/shared/src/i18n/en/budget.ts b/shared/src/i18n/en/budget.ts
index 8e166f094..001fd9a98 100644
--- a/shared/src/i18n/en/budget.ts
+++ b/shared/src/i18n/en/budget.ts
@@ -126,6 +126,7 @@ const budget: TranslationStrings = {
'costs.payersUnbalanced': 'Payer amounts must add up to {amount}',
'costs.splitBetween': 'Split equally between',
'costs.split': 'Split',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Equally',
'costs.splitCustom': 'Custom',
'costs.splitTicket': 'Ticket',
diff --git a/shared/src/i18n/es/budget.ts b/shared/src/i18n/es/budget.ts
index ae24697e2..5ff2440db 100644
--- a/shared/src/i18n/es/budget.ts
+++ b/shared/src/i18n/es/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Toca para incluir',
'costs.amount': 'Importe',
'costs.split': 'Dividir',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'A partes iguales',
'costs.splitCustom': 'Personalizado',
'costs.splitTicket': 'Recibo',
diff --git a/shared/src/i18n/et/budget.ts b/shared/src/i18n/et/budget.ts
index c25ca0c1f..3fb20e8d9 100644
--- a/shared/src/i18n/et/budget.ts
+++ b/shared/src/i18n/et/budget.ts
@@ -126,6 +126,7 @@ const budget: TranslationStrings = {
'costs.payersUnbalanced': 'Maksjate summad peavad kokku andma {amount}',
'costs.splitBetween': 'Jaga võrdselt järgmiste vahel',
'costs.split': 'Jaotus',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Võrdselt',
'costs.splitCustom': 'Kohandatud',
'costs.splitTicket': 'Tšekk',
diff --git a/shared/src/i18n/fr/budget.ts b/shared/src/i18n/fr/budget.ts
index 699920229..b7abca6e0 100644
--- a/shared/src/i18n/fr/budget.ts
+++ b/shared/src/i18n/fr/budget.ts
@@ -151,6 +151,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Toucher pour inclure',
'costs.amount': 'Montant',
'costs.split': 'Répartition',
+ 'costs.splitChipLabel': '{name} : {amount}',
'costs.splitEqually': 'À parts égales',
'costs.splitCustom': 'Personnalisé',
'costs.splitTicket': 'Reçu',
diff --git a/shared/src/i18n/gr/budget.ts b/shared/src/i18n/gr/budget.ts
index b1ce59847..fef4b18f4 100644
--- a/shared/src/i18n/gr/budget.ts
+++ b/shared/src/i18n/gr/budget.ts
@@ -151,6 +151,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Πατήστε για προσθήκη',
'costs.amount': 'Ποσό',
'costs.split': 'Διαμοιρασμός',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Εξίσου',
'costs.splitCustom': 'Προσαρμογή',
'costs.splitTicket': 'Απόδειξη',
diff --git a/shared/src/i18n/hu/budget.ts b/shared/src/i18n/hu/budget.ts
index 182a3b44d..2584386b1 100644
--- a/shared/src/i18n/hu/budget.ts
+++ b/shared/src/i18n/hu/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Koppintson a hozzáadáshoz',
'costs.amount': 'Összeg',
'costs.split': 'Felosztás',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Egyenlően',
'costs.splitCustom': 'Egyéni',
'costs.splitTicket': 'Blokk',
diff --git a/shared/src/i18n/id/budget.ts b/shared/src/i18n/id/budget.ts
index 2881de1f6..e465e0d52 100644
--- a/shared/src/i18n/id/budget.ts
+++ b/shared/src/i18n/id/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Ketuk untuk menyertakan',
'costs.amount': 'Jumlah',
'costs.split': 'Pembagian',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Rata',
'costs.splitCustom': 'Kustom',
'costs.splitTicket': 'Struk',
diff --git a/shared/src/i18n/it/budget.ts b/shared/src/i18n/it/budget.ts
index bd83689b2..2fbb9c5d8 100644
--- a/shared/src/i18n/it/budget.ts
+++ b/shared/src/i18n/it/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Tocca per includere',
'costs.amount': 'Importo',
'costs.split': 'Suddivisione',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'In parti uguali',
'costs.splitCustom': 'Personalizzato',
'costs.splitTicket': 'Scontrino',
diff --git a/shared/src/i18n/ja/budget.ts b/shared/src/i18n/ja/budget.ts
index 97e2955fb..cd0f4b1c6 100644
--- a/shared/src/i18n/ja/budget.ts
+++ b/shared/src/i18n/ja/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'タップして追加',
'costs.amount': '金額',
'costs.split': '分け方',
+ 'costs.splitChipLabel': '{name}:{amount}',
'costs.splitEqually': '均等',
'costs.splitCustom': 'カスタム',
'costs.splitTicket': 'レシート',
diff --git a/shared/src/i18n/ko/budget.ts b/shared/src/i18n/ko/budget.ts
index ec525d5c4..4b0330fea 100644
--- a/shared/src/i18n/ko/budget.ts
+++ b/shared/src/i18n/ko/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': '탭하여 포함',
'costs.amount': '금액',
'costs.split': '나누기',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': '균등하게',
'costs.splitCustom': '직접 설정',
'costs.splitTicket': '영수증',
diff --git a/shared/src/i18n/nl/budget.ts b/shared/src/i18n/nl/budget.ts
index a18962385..74a699694 100644
--- a/shared/src/i18n/nl/budget.ts
+++ b/shared/src/i18n/nl/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Tik om toe te voegen',
'costs.amount': 'Bedrag',
'costs.split': 'Verdelen',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Gelijk',
'costs.splitCustom': 'Aangepast',
'costs.splitTicket': 'Bon',
diff --git a/shared/src/i18n/pl/budget.ts b/shared/src/i18n/pl/budget.ts
index 06ed62e32..66693a969 100644
--- a/shared/src/i18n/pl/budget.ts
+++ b/shared/src/i18n/pl/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Dotknij, aby dodać',
'costs.amount': 'Kwota',
'costs.split': 'Podział',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Po równo',
'costs.splitCustom': 'Własny',
'costs.splitTicket': 'Paragon',
diff --git a/shared/src/i18n/ru/budget.ts b/shared/src/i18n/ru/budget.ts
index 643ac802a..606024ed8 100644
--- a/shared/src/i18n/ru/budget.ts
+++ b/shared/src/i18n/ru/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Нажмите, чтобы добавить',
'costs.amount': 'Сумма',
'costs.split': 'Разделение',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Поровну',
'costs.splitCustom': 'Вручную',
'costs.splitTicket': 'Чек',
diff --git a/shared/src/i18n/sk/budget.ts b/shared/src/i18n/sk/budget.ts
index 606d2d849..099ba0826 100644
--- a/shared/src/i18n/sk/budget.ts
+++ b/shared/src/i18n/sk/budget.ts
@@ -126,6 +126,7 @@ const budget: TranslationStrings = {
'costs.payersUnbalanced': 'Súčet súm platcov musí byť {amount}',
'costs.splitBetween': 'Rozdeliť rovným dielom medzi',
'costs.split': 'Rozdeliť',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Rovnakým dielom',
'costs.splitCustom': 'Vlastné',
'costs.splitTicket': 'Účtenka',
diff --git a/shared/src/i18n/sv/budget.ts b/shared/src/i18n/sv/budget.ts
index 0bb6ab37b..86cd5dfd8 100644
--- a/shared/src/i18n/sv/budget.ts
+++ b/shared/src/i18n/sv/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Tryck för att inkludera',
'costs.amount': 'Summa',
'costs.split': 'Fördelning',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Lika',
'costs.splitCustom': 'Anpassat',
'costs.splitTicket': 'Kvitto',
diff --git a/shared/src/i18n/th/budget.ts b/shared/src/i18n/th/budget.ts
index c460efa40..77972a19a 100644
--- a/shared/src/i18n/th/budget.ts
+++ b/shared/src/i18n/th/budget.ts
@@ -122,6 +122,7 @@ const budget: TranslationStrings = {
'costs.payersUnbalanced': 'จำนวนเงินของผู้ชำระจะต้องรวมกันไม่เกิน {amount}',
'costs.splitBetween': 'แบ่งเท่าๆ กันระหว่าง',
'costs.split': 'แยก',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'เท่าๆ กัน',
'costs.splitCustom': 'กำหนดเอง',
'costs.splitTicket': 'ตั๋ว',
diff --git a/shared/src/i18n/tr/budget.ts b/shared/src/i18n/tr/budget.ts
index f33ad395a..79c133229 100644
--- a/shared/src/i18n/tr/budget.ts
+++ b/shared/src/i18n/tr/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Eklemek için dokun',
'costs.amount': 'Tutar',
'costs.split': 'Paylaşım',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Eşit',
'costs.splitCustom': 'Özel',
'costs.splitTicket': 'Fiş',
diff --git a/shared/src/i18n/uk/budget.ts b/shared/src/i18n/uk/budget.ts
index 69657f7de..3116e462b 100644
--- a/shared/src/i18n/uk/budget.ts
+++ b/shared/src/i18n/uk/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Натисніть, щоб додати',
'costs.amount': 'Сума',
'costs.split': 'Розподіл',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Порівну',
'costs.splitCustom': 'Вручну',
'costs.splitTicket': 'Чек',
diff --git a/shared/src/i18n/vi/budget.ts b/shared/src/i18n/vi/budget.ts
index 60a63c2b8..2fdd8c8af 100644
--- a/shared/src/i18n/vi/budget.ts
+++ b/shared/src/i18n/vi/budget.ts
@@ -150,6 +150,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': 'Nhấn để bao gồm',
'costs.amount': 'Số lượng',
'costs.split': 'Chia tiền',
+ 'costs.splitChipLabel': '{name}: {amount}',
'costs.splitEqually': 'Chia đều',
'costs.splitCustom': 'Tùy chỉnh',
'costs.splitTicket': 'Hóa đơn',
diff --git a/shared/src/i18n/zh-TW/budget.ts b/shared/src/i18n/zh-TW/budget.ts
index 03399853f..cac4c8238 100644
--- a/shared/src/i18n/zh-TW/budget.ts
+++ b/shared/src/i18n/zh-TW/budget.ts
@@ -149,6 +149,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': '點按以加入',
'costs.amount': '金額',
'costs.split': '分攤',
+ 'costs.splitChipLabel': '{name}:{amount}',
'costs.splitEqually': '平均',
'costs.splitCustom': '自訂',
'costs.splitTicket': '收據',
diff --git a/shared/src/i18n/zh/budget.ts b/shared/src/i18n/zh/budget.ts
index 084db3155..24f5768eb 100644
--- a/shared/src/i18n/zh/budget.ts
+++ b/shared/src/i18n/zh/budget.ts
@@ -149,6 +149,7 @@ const budget: TranslationStrings = {
'costs.tapToInclude': '点按以加入',
'costs.amount': '金额',
'costs.split': '分摊',
+ 'costs.splitChipLabel': '{name}:{amount}',
'costs.splitEqually': '平均分摊',
'costs.splitCustom': '自定义金额',
'costs.splitTicket': '按账单明细',