mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 07:40:59 +00:00
fix(packing): show who brings a shared item in a card with their pictures, and the others as a +N circle
The row's badges said who brings an item, who it is shared with and who takes care of it in the browser's own tooltip. They now use TREK's, and the bringers one lists everyone with their picture on a line of their own. The others show as a +N circle tucked under the first picture. Tooltip takes an optional content for tips that hold more than words.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// FE-PACK-BRINGERCARD-001 to FE-PACK-BRINGERCARD-004
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { render, screen } from '../../../tests/helpers/render';
|
||||
import { BringersCard, MoreBringers, bringersOf } from './PackingBringersCard';
|
||||
|
||||
const members = [
|
||||
{ id: 1, username: 'anna', avatar_url: '/uploads/avatars/anna.jpg' },
|
||||
{ id: 2, username: 'ben', avatar: '/uploads/avatars/ben.jpg' },
|
||||
{ id: 3, username: 'carla' },
|
||||
];
|
||||
|
||||
describe('bringersOf', () => {
|
||||
it('FE-PACK-BRINGERCARD-001: lists the bringer first, then the others, each with the picture the trip knows', () => {
|
||||
const item = {
|
||||
owner_id: 1,
|
||||
owner_username: 'anna',
|
||||
contributors: [
|
||||
{ user_id: 2, username: 'ben' },
|
||||
{ user_id: 3, username: 'carla' },
|
||||
],
|
||||
};
|
||||
expect(bringersOf(item, members)).toEqual([
|
||||
{ name: 'anna', avatar: '/uploads/avatars/anna.jpg' },
|
||||
{ name: 'ben', avatar: '/uploads/avatars/ben.jpg' },
|
||||
{ name: 'carla', avatar: null },
|
||||
]);
|
||||
});
|
||||
|
||||
it('FE-PACK-BRINGERCARD-002: lists a person once and copes with a bringer the trip no longer has', () => {
|
||||
const item = { owner_id: 9, owner_username: 'dora', contributors: [{ user_id: 9, username: 'dora' }] };
|
||||
expect(bringersOf(item, members)).toEqual([{ name: 'dora', avatar: null }]);
|
||||
expect(bringersOf({}, members)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BringersCard', () => {
|
||||
it('FE-PACK-BRINGERCARD-003: names everyone on a line of their own under the heading', () => {
|
||||
render(
|
||||
<BringersCard
|
||||
bringers={bringersOf(
|
||||
{ owner_id: 1, owner_username: 'anna', contributors: [{ user_id: 3, username: 'carla' }] },
|
||||
members
|
||||
)}
|
||||
/>
|
||||
);
|
||||
const rows = screen.getAllByRole('listitem');
|
||||
expect(rows).toHaveLength(2);
|
||||
expect(rows[0]).toHaveTextContent('anna');
|
||||
expect(rows[1]).toHaveTextContent('carla');
|
||||
expect(screen.getByText('packing.bringersTitle')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('FE-PACK-BRINGERCARD-004: the "+N" circle counts the others', () => {
|
||||
const { container } = render(<MoreBringers count={3} />);
|
||||
expect(container).toHaveTextContent('+3');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,122 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useTranslation } from '../../i18n';
|
||||
import type { TripMember } from './usePackingListPanel';
|
||||
|
||||
/** One person bringing a common item: the name, and the picture when the trip knows one. */
|
||||
export interface Bringer {
|
||||
name: string;
|
||||
avatar: string | null;
|
||||
}
|
||||
|
||||
/** The item's bringer first, then whoever also brings it, each once, with their picture from the trip's members. */
|
||||
export function bringersOf(
|
||||
item: {
|
||||
owner_id?: number | null;
|
||||
owner_username?: string | null;
|
||||
contributors?: { user_id: number; username: string }[] | null;
|
||||
},
|
||||
members: readonly TripMember[]
|
||||
): Bringer[] {
|
||||
const avatarOf = (id: number | null | undefined) => {
|
||||
const member = id == null ? undefined : members.find((m) => m.id === id);
|
||||
return member?.avatar_url ?? member?.avatar ?? null;
|
||||
};
|
||||
const all = [
|
||||
...(item.owner_username ? [{ id: item.owner_id ?? null, name: item.owner_username }] : []),
|
||||
...(item.contributors ?? []).map((c) => ({ id: c.user_id, name: c.username })),
|
||||
];
|
||||
const bringers: Bringer[] = [];
|
||||
for (const person of all) {
|
||||
if (bringers.some((b) => b.name === person.name)) continue;
|
||||
bringers.push({ name: person.name, avatar: avatarOf(person.id) });
|
||||
}
|
||||
return bringers;
|
||||
}
|
||||
|
||||
const CIRCLE: CSSProperties = {
|
||||
width: 20,
|
||||
height: 20,
|
||||
borderRadius: '50%',
|
||||
flexShrink: 0,
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
fontSize: 'calc(9px * var(--fs-scale-caption, 1))',
|
||||
fontWeight: 700,
|
||||
};
|
||||
|
||||
/** A bringer's picture, or the first letter of the name on a colour taken from it. */
|
||||
export function BringerAvatar({ bringer }: { bringer: Bringer }) {
|
||||
if (bringer.avatar) {
|
||||
return <img src={bringer.avatar} alt="" style={{ ...CIRCLE, objectFit: 'cover', display: 'block' }} />;
|
||||
}
|
||||
return (
|
||||
<span
|
||||
aria-hidden
|
||||
style={{
|
||||
...CIRCLE,
|
||||
background: `hsl(${((bringer.name.codePointAt(0) ?? 0) * 37) % 360}, 55%, 55%)`, // theme-lint-disable: the colour comes from the bringer's name
|
||||
color: 'white', // theme-lint-disable: the letter sits on the name's own colour
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
{bringer.name[0]}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/** The "+N" of the others bringing it, as a circle tucked under the first bringer's picture. */
|
||||
export function MoreBringers({ count }: { count: number }) {
|
||||
return (
|
||||
<span
|
||||
aria-hidden
|
||||
style={{
|
||||
...CIRCLE,
|
||||
marginInlineStart: -6,
|
||||
color: 'var(--text-muted)',
|
||||
background: 'var(--bg-tertiary)',
|
||||
boxShadow: '0 0 0 2px var(--bg-card)',
|
||||
fontVariantNumeric: 'tabular-nums',
|
||||
}}
|
||||
>
|
||||
+{count}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/** What the row's tooltip shows: everyone bringing the item, one per line with their picture. */
|
||||
export function BringersCard({ bringers }: { bringers: readonly Bringer[] }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div style={{ minWidth: 150 }}>
|
||||
<div
|
||||
className="text-content-faint"
|
||||
style={{
|
||||
fontSize: 'calc(9.5px * var(--fs-scale-caption, 1))',
|
||||
fontWeight: 700,
|
||||
letterSpacing: '.06em',
|
||||
textTransform: 'uppercase',
|
||||
paddingBottom: 6,
|
||||
}}
|
||||
>
|
||||
{t('packing.bringersTitle')}
|
||||
</div>
|
||||
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
|
||||
{bringers.map((b, i) => (
|
||||
<li
|
||||
key={b.name}
|
||||
className={i > 0 ? 'border-t border-edge-faint' : undefined}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 0' }}
|
||||
>
|
||||
<BringerAvatar bringer={b} />
|
||||
<span
|
||||
className="text-content"
|
||||
style={{ fontSize: 'calc(12px * var(--fs-scale-body, 1))', fontWeight: 600 }}
|
||||
>
|
||||
{b.name}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import { POPOVER, POPOVER_CAPTION, POPOVER_DIVIDER } from './packingPopoverStyle
|
||||
import type { TripMember } from './usePackingListPanel'
|
||||
import { NumericInput } from '../shared/NumericInput'
|
||||
import { packingBringerNames } from './packingBringers'
|
||||
import { Tooltip } from '../shared/Tooltip'
|
||||
import { BringerAvatar, BringersCard, MoreBringers, bringersOf } from './PackingBringersCard'
|
||||
|
||||
interface ArtikelZeileProps {
|
||||
item: PackingItem
|
||||
@@ -78,7 +80,6 @@ export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCa
|
||||
const recipients = item.recipients || []
|
||||
const sharedByMe = !!item.is_private && item.owner_id === currentUserId && recipients.length > 0
|
||||
const broughtBy = !item.is_private && item.owner_username ? item.owner_username : null
|
||||
const contributors = item.contributors || []
|
||||
const canShare = canEdit && !isPlaceholder && !!onSetSharing
|
||||
|
||||
const handleToggle = () => togglePackingItem(tripId, item.id, !item.checked)
|
||||
@@ -114,8 +115,7 @@ export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCa
|
||||
// A multi-piece item counts its pieces into the bag (#2296).
|
||||
const packed = packedOf(item)
|
||||
const partlyPacked = !item.checked && packed > 0
|
||||
const owner = tripMembers.find(m => m.id === item.owner_id)
|
||||
const ownerAvatar = owner?.avatar_url ?? owner?.avatar ?? null
|
||||
const bringers = bringersOf(item, tripMembers)
|
||||
|
||||
// At rest a row reads as what it holds (owner, a quantity above one, a
|
||||
// weight, the bag) and everything else stays in place but dimmed: nothing
|
||||
@@ -215,31 +215,29 @@ export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCa
|
||||
{/* Sharing badges (#858 three-tier), as compact chips like the phone's
|
||||
(#1525): the full sentence is the tooltip and the accessible name. */}
|
||||
{!isPlaceholder && sharedToMe && (
|
||||
<span className="packing-row-badge" title={t('packing.takenCareOf', { name: item.owner_username || '' })} aria-label={t('packing.takenCareOf', { name: item.owner_username || '' })}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, width: 22, height: 22, borderRadius: 99, color: 'var(--accent)', background: 'color-mix(in srgb, var(--accent) 12%, transparent)' }}>
|
||||
<HandHelping size={12} />
|
||||
</span>
|
||||
<Tooltip label={t('packing.takenCareOf', { name: item.owner_username || '' })}>
|
||||
<span className="packing-row-badge" aria-label={t('packing.takenCareOf', { name: item.owner_username || '' })}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, width: 22, height: 22, borderRadius: 99, color: 'var(--accent)', background: 'color-mix(in srgb, var(--accent) 12%, transparent)' }}>
|
||||
<HandHelping size={12} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isPlaceholder && sharedByMe && (
|
||||
<span className="packing-row-badge" title={`${t('packing.sharedWithCount', { count: recipients.length })}: ${recipients.map(r => r.username).join(', ')}`} aria-label={t('packing.sharedWithCount', { count: recipients.length })}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: 3, flexShrink: 0, height: 22, padding: '0 7px', borderRadius: 99, fontSize: 'calc(10px * var(--fs-scale-caption, 1))', fontWeight: 700, color: 'var(--text-muted)', background: 'var(--bg-tertiary)' }}>
|
||||
<UserRound size={11} /> {recipients.length}
|
||||
</span>
|
||||
<Tooltip label={`${t('packing.sharedWithCount', { count: recipients.length })}: ${recipients.map(r => r.username).join(', ')}`}>
|
||||
<span className="packing-row-badge" aria-label={t('packing.sharedWithCount', { count: recipients.length })}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: 3, flexShrink: 0, height: 22, padding: '0 7px', borderRadius: 99, fontSize: 'calc(10px * var(--fs-scale-caption, 1))', fontWeight: 700, color: 'var(--text-muted)', background: 'var(--bg-tertiary)' }}>
|
||||
<UserRound size={11} /> {recipients.length}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isPlaceholder && broughtBy && (
|
||||
<span className="packing-row-badge" title={t('packing.broughtBy', { name: packingBringerNames(item) })} aria-label={t('packing.broughtBy', { name: packingBringerNames(item) })}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: 3, flexShrink: 0 }}>
|
||||
{ownerAvatar
|
||||
? <img src={ownerAvatar} alt="" style={{ width: 20, height: 20, borderRadius: '50%', objectFit: 'cover', display: 'block' }} />
|
||||
: (
|
||||
<span aria-hidden style={{
|
||||
width: 20, height: 20, borderRadius: '50%', display: 'grid', placeItems: 'center',
|
||||
background: `hsl(${(broughtBy.codePointAt(0) ?? 0) * 37 % 360}, 55%, 55%)`, // theme-lint-disable: the colour comes from the bringer's name
|
||||
color: 'white', fontSize: 'calc(9px * var(--fs-scale-caption, 1))', fontWeight: 700, textTransform: 'uppercase',
|
||||
}}>{broughtBy[0]}</span>
|
||||
)}
|
||||
{contributors.length > 0 && <span style={{ fontSize: 'calc(10px * var(--fs-scale-caption, 1))', fontWeight: 700, color: 'var(--text-faint)' }}>+{contributors.length}</span>}
|
||||
</span>
|
||||
<Tooltip label={t('packing.broughtBy', { name: packingBringerNames(item) })} content={<BringersCard bringers={bringers} />}>
|
||||
<span className="packing-row-badge" aria-label={t('packing.broughtBy', { name: packingBringerNames(item) })}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', flexShrink: 0 }}>
|
||||
<BringerAvatar bringer={bringers[0]} />
|
||||
{bringers.length > 1 && <MoreBringers count={bringers.length - 1} />}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div className="packing-row-inline-actions" style={{ display: 'flex', alignItems: 'center', gap: 6, flexShrink: 0 }}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// FE-W4TIP-001 to FE-W4TIP-017
|
||||
// FE-W4TIP-001 to FE-W4TIP-018
|
||||
import { createRef, useRef } from 'react'
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import { render, screen, fireEvent, act } from '../../../tests/helpers/render'
|
||||
@@ -213,3 +213,14 @@ describe('Tooltip — with a shortcut', () => {
|
||||
expect(screen.getByRole('tooltip').querySelector('kbd')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Tooltip — with content', () => {
|
||||
it('FE-W4TIP-018: shows the content in place of the label, which still opens it', () => {
|
||||
render(<Tooltip label="brought by Anna, Ben" content={<ul><li>Anna</li><li>Ben</li></ul>}><button>x</button></Tooltip>)
|
||||
hover(screen.getByRole('button'))
|
||||
|
||||
const tip = screen.getByRole('tooltip')
|
||||
expect(tip.querySelectorAll('li')).toHaveLength(2)
|
||||
expect(tip).not.toHaveTextContent('brought by')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,10 +11,15 @@ interface TooltipProps {
|
||||
disabled?: boolean
|
||||
/** The keys that do the same, shown beside the label: ["⌘", "K"]. */
|
||||
shortcut?: readonly string[]
|
||||
/**
|
||||
* Shown in place of the label when the tip holds more than words, such as a list of
|
||||
* people with their pictures. `label` still names it and decides whether it opens.
|
||||
*/
|
||||
content?: React.ReactNode
|
||||
children: React.ReactElement
|
||||
}
|
||||
|
||||
export function Tooltip({ label, placement = 'bottom', delay = 250, disabled, shortcut, children }: TooltipProps) {
|
||||
export function Tooltip({ label, placement = 'bottom', delay = 250, disabled, shortcut, content, children }: TooltipProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [coords, setCoords] = useState<{ top: number; left: number } | null>(null)
|
||||
const triggerRef = useRef<HTMLElement | null>(null)
|
||||
@@ -93,8 +98,8 @@ export function Tooltip({ label, placement = 'bottom', delay = 250, disabled, sh
|
||||
zIndex: 100000,
|
||||
fontSize: 'calc(11px * var(--fs-scale-caption, 1))',
|
||||
fontWeight: 500,
|
||||
padding: '5px 10px',
|
||||
borderRadius: 8,
|
||||
padding: content ? '8px 10px' : '5px 10px',
|
||||
borderRadius: content ? 10 : 8,
|
||||
// A label of several lines (the paddle check bench) breaks at its lines and wraps within the window.
|
||||
whiteSpace: label.includes('\n') ? 'pre-line' : 'nowrap',
|
||||
maxWidth: label.includes('\n') ? 'min(22rem, calc(100vw - 12px))' : undefined,
|
||||
@@ -103,12 +108,12 @@ export function Tooltip({ label, placement = 'bottom', delay = 250, disabled, sh
|
||||
transformOrigin: placement === 'top' ? 'bottom center' : placement === 'bottom' ? 'top center' : placement === 'left' ? 'center right' : 'center left',
|
||||
}}
|
||||
>
|
||||
{shortcut && shortcut.length > 0 ? (
|
||||
{content ?? (shortcut && shortcut.length > 0 ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
{label}
|
||||
<Kbd keys={shortcut} />
|
||||
</span>
|
||||
) : label}
|
||||
) : label)}
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
|
||||
@@ -135,6 +135,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount.few': 'مشترك مع {count} أشخاص',
|
||||
'packing.sharedWithCount.many': 'مشترك مع {count} شخصًا',
|
||||
'packing.broughtBy': 'يحضره {name}',
|
||||
'packing.bringersTitle': 'يحضره',
|
||||
'packing.alsoBring': 'يمكنني إحضاره أيضًا',
|
||||
'packing.alsoBringingStop': 'لن أحضره',
|
||||
'packing.cloneToMine': 'نسخ إلى قائمتي',
|
||||
|
||||
@@ -105,6 +105,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': '{count} nəfərlə paylaşılıb',
|
||||
'packing.sharedWithCount.one': '{count} nəfərlə paylaşılıb',
|
||||
'packing.broughtBy': '{name} gətirir',
|
||||
'packing.bringersTitle': 'Gətirənlər',
|
||||
'packing.alsoBring': 'Mən də bunu gətirə bilərəm',
|
||||
'packing.alsoBringingStop': 'Mən bunu gətirməyəcəyəm',
|
||||
'packing.cloneToMine': 'Mənim siyahıma köçür',
|
||||
|
||||
@@ -102,6 +102,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'compartilhado com {count}',
|
||||
'packing.sharedWithCount.one': 'compartilhado com {count}',
|
||||
'packing.broughtBy': 'trazido por {name}',
|
||||
'packing.bringersTitle': 'Quem leva',
|
||||
'packing.alsoBring': 'Eu também posso levar',
|
||||
'packing.alsoBringingStop': 'Não vou levar',
|
||||
'packing.cloneToMine': 'Copiar para minha lista',
|
||||
|
||||
@@ -91,6 +91,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'compartit amb {count}',
|
||||
'packing.sharedWithCount.one': 'compartit amb {count}',
|
||||
'packing.broughtBy': 'portat per {name}',
|
||||
'packing.bringersTitle': 'Ho porta',
|
||||
'packing.alsoBring': 'Jo també puc portar això',
|
||||
'packing.alsoBringingStop': 'Ja no ho porto',
|
||||
'packing.cloneToMine': 'Copiar a la meva llista',
|
||||
|
||||
@@ -111,6 +111,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount.one': 'sdíleno s {count} osobou',
|
||||
'packing.sharedWithCount.few': 'sdíleno s {count} osobami',
|
||||
'packing.broughtBy': 'přináší {name}',
|
||||
'packing.bringersTitle': 'Přinese',
|
||||
'packing.alsoBring': 'Můžu to vzít taky',
|
||||
'packing.alsoBringingStop': 'Neberu to',
|
||||
'packing.cloneToMine': 'Kopírovat do mého seznamu',
|
||||
|
||||
@@ -94,6 +94,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'geteilt mit {count}',
|
||||
'packing.sharedWithCount.one': 'geteilt mit {count}',
|
||||
'packing.broughtBy': 'bringt {name}',
|
||||
'packing.bringersTitle': 'Bringt mit',
|
||||
'packing.alsoBring': 'Bring ich auch mit',
|
||||
'packing.alsoBringingStop': 'Bring ich doch nicht mit',
|
||||
'packing.cloneToMine': 'In meine Liste kopieren',
|
||||
|
||||
@@ -93,6 +93,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'shared with {count}',
|
||||
'packing.sharedWithCount.one': 'shared with {count}',
|
||||
'packing.broughtBy': 'brought by {name}',
|
||||
'packing.bringersTitle': 'Brought by',
|
||||
'packing.alsoBring': 'I can bring that too',
|
||||
'packing.alsoBringingStop': "I'm not bringing it",
|
||||
'packing.cloneToMine': 'Copy to my list',
|
||||
|
||||
@@ -103,6 +103,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'compartido con {count}',
|
||||
'packing.sharedWithCount.one': 'compartido con {count}',
|
||||
'packing.broughtBy': 'lo trae {name}',
|
||||
'packing.bringersTitle': 'Lo lleva',
|
||||
'packing.alsoBring': 'Yo también puedo llevarlo',
|
||||
'packing.alsoBringingStop': 'No lo llevo',
|
||||
'packing.cloneToMine': 'Copiar a mi lista',
|
||||
|
||||
@@ -93,6 +93,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'jagatud {count} inimesega',
|
||||
'packing.sharedWithCount.one': 'jagatud {count} inimesega',
|
||||
'packing.broughtBy': 'toob {name}',
|
||||
'packing.bringersTitle': 'Toob kaasa',
|
||||
'packing.alsoBring': 'Võin selle ka kaasa võtta',
|
||||
'packing.alsoBringingStop': 'Ma ei võta seda kaasa',
|
||||
'packing.cloneToMine': 'Kopeeri minu nimekirja',
|
||||
|
||||
@@ -103,6 +103,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'partagé avec {count}',
|
||||
'packing.sharedWithCount.one': 'partagé avec {count}',
|
||||
'packing.broughtBy': 'apporté par {name}',
|
||||
'packing.bringersTitle': 'Apporté par',
|
||||
'packing.alsoBring': "Je peux l'apporter aussi",
|
||||
'packing.alsoBringingStop': "Je ne l'apporte pas",
|
||||
'packing.cloneToMine': 'Copier dans ma liste',
|
||||
|
||||
@@ -104,6 +104,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'κοινό με {count} άτομα',
|
||||
'packing.sharedWithCount.one': 'κοινό με {count} άτομο',
|
||||
'packing.broughtBy': 'φέρνει ο/η {name}',
|
||||
'packing.bringersTitle': 'Το φέρνει',
|
||||
'packing.alsoBring': 'Μπορώ να το φέρω κι εγώ',
|
||||
'packing.alsoBringingStop': 'Δεν το φέρνω',
|
||||
'packing.cloneToMine': 'Αντιγραφή στη λίστα μου',
|
||||
|
||||
@@ -99,6 +99,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount.one': 'משותף עם אדם אחד',
|
||||
'packing.sharedWithCount.two': 'משותף עם שני אנשים',
|
||||
'packing.broughtBy': '{name} מביא/ה',
|
||||
'packing.bringersTitle': 'מביאים',
|
||||
'packing.alsoBring': 'גם אני יכול/ה להביא',
|
||||
'packing.alsoBringingStop': 'אני לא מביא/ה',
|
||||
'packing.cloneToMine': 'העתקה לרשימה שלי',
|
||||
|
||||
@@ -105,6 +105,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'megosztva {count} fővel',
|
||||
'packing.sharedWithCount.one': 'megosztva {count} fővel',
|
||||
'packing.broughtBy': 'hozza: {name}',
|
||||
'packing.bringersTitle': 'Hozza',
|
||||
'packing.alsoBring': 'Én is tudom hozni',
|
||||
'packing.alsoBringingStop': 'Mégsem hozom',
|
||||
'packing.cloneToMine': 'Másolás a listámra',
|
||||
|
||||
@@ -95,6 +95,7 @@ const packing: TranslationStrings = {
|
||||
'packing.takenCareOf': 'oleh {name}',
|
||||
'packing.sharedWithCount': 'dibagikan dengan {count}',
|
||||
'packing.broughtBy': 'dibawa oleh {name}',
|
||||
'packing.bringersTitle': 'Dibawa oleh',
|
||||
'packing.alsoBring': 'Saya juga bisa membawanya',
|
||||
'packing.alsoBringingStop': 'Saya tidak membawanya',
|
||||
'packing.cloneToMine': 'Salin ke daftar saya',
|
||||
|
||||
@@ -103,6 +103,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'condiviso con {count}',
|
||||
'packing.sharedWithCount.one': 'condiviso con {count}',
|
||||
'packing.broughtBy': 'lo porta {name}',
|
||||
'packing.bringersTitle': 'Lo porta',
|
||||
'packing.alsoBring': "Posso portarlo anch'io",
|
||||
'packing.alsoBringingStop': 'Non lo porto',
|
||||
'packing.cloneToMine': 'Copia nella mia lista',
|
||||
|
||||
@@ -94,6 +94,7 @@ const packing: TranslationStrings = {
|
||||
'packing.takenCareOf': '{name} さん担当',
|
||||
'packing.sharedWithCount': '{count} 人と共有',
|
||||
'packing.broughtBy': '{name} さんが持参',
|
||||
'packing.bringersTitle': '持っていく人',
|
||||
'packing.alsoBring': '私も持っていけます',
|
||||
'packing.alsoBringingStop': 'やっぱり持っていきません',
|
||||
'packing.cloneToMine': 'マイリストにコピー',
|
||||
|
||||
@@ -94,6 +94,7 @@ const packing: TranslationStrings = {
|
||||
'packing.takenCareOf': '{name} 담당',
|
||||
'packing.sharedWithCount': '{count}명과 공유',
|
||||
'packing.broughtBy': '{name}님이 가져옴',
|
||||
'packing.bringersTitle': '가져오는 사람',
|
||||
'packing.alsoBring': '저도 가져갈 수 있어요',
|
||||
'packing.alsoBringingStop': '안 가져갈게요',
|
||||
'packing.cloneToMine': '내 목록으로 복사',
|
||||
|
||||
@@ -103,6 +103,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'gedeeld met {count}',
|
||||
'packing.sharedWithCount.one': 'gedeeld met {count}',
|
||||
'packing.broughtBy': 'meegenomen door {name}',
|
||||
'packing.bringersTitle': 'Neemt mee',
|
||||
'packing.alsoBring': 'Ik kan het ook meenemen',
|
||||
'packing.alsoBringingStop': 'Ik neem het niet mee',
|
||||
'packing.cloneToMine': 'Kopiëren naar mijn lijst',
|
||||
|
||||
@@ -119,6 +119,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount.few': 'udostępniono {count} osobom',
|
||||
'packing.sharedWithCount.many': 'udostępniono {count} osobom',
|
||||
'packing.broughtBy': 'przynosi {name}',
|
||||
'packing.bringersTitle': 'Przyniesie',
|
||||
'packing.alsoBring': 'Ja też mogę to wziąć',
|
||||
'packing.alsoBringingStop': 'Jednak nie biorę',
|
||||
'packing.cloneToMine': 'Kopiuj do mojej listy',
|
||||
|
||||
@@ -119,6 +119,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount.few': 'доступно {count} участникам',
|
||||
'packing.sharedWithCount.many': 'доступно {count} участникам',
|
||||
'packing.broughtBy': 'приносит {name}',
|
||||
'packing.bringersTitle': 'Возьмёт',
|
||||
'packing.alsoBring': 'Я тоже могу принести',
|
||||
'packing.alsoBringingStop': 'Я не принесу',
|
||||
'packing.cloneToMine': 'Копировать в мой список',
|
||||
|
||||
@@ -99,6 +99,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount.one': 'zdieľané s {count} osobou',
|
||||
'packing.sharedWithCount.few': 'zdieľané s {count} osobami',
|
||||
'packing.broughtBy': 'prináša {name}',
|
||||
'packing.bringersTitle': 'Prinesie',
|
||||
'packing.alsoBring': 'Môžem to vziať tiež',
|
||||
'packing.alsoBringingStop': 'Neberiem to',
|
||||
'packing.cloneToMine': 'Kopírovať do môjho zoznamu',
|
||||
|
||||
@@ -103,6 +103,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': 'delat med {count}',
|
||||
'packing.sharedWithCount.one': 'delat med {count}',
|
||||
'packing.broughtBy': 'tas med av {name}',
|
||||
'packing.bringersTitle': 'Tar med',
|
||||
'packing.alsoBring': 'Jag kan ta med det också',
|
||||
'packing.alsoBringingStop': 'Jag tar inte med det',
|
||||
'packing.cloneToMine': 'Kopiera till min lista',
|
||||
|
||||
@@ -87,6 +87,7 @@ const packing: TranslationStrings = {
|
||||
'packing.takenCareOf': 'โดย {name}',
|
||||
'packing.sharedWithCount': 'แชร์กับ {count}',
|
||||
'packing.broughtBy': '{name} จะนำมา',
|
||||
'packing.bringersTitle': 'ผู้นำมา',
|
||||
'packing.alsoBring': 'ฉันก็สามารถนำสิ่งนั้นมาได้เช่นกัน',
|
||||
'packing.alsoBringingStop': 'ฉันไม่เอามันมา',
|
||||
'packing.cloneToMine': 'คัดลอกไปยังรายการของฉัน',
|
||||
|
||||
@@ -103,6 +103,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount': '{count} kişiyle paylaşıldı',
|
||||
'packing.sharedWithCount.one': '{count} kişiyle paylaşıldı',
|
||||
'packing.broughtBy': '{name} getiriyor',
|
||||
'packing.bringersTitle': 'Getiren',
|
||||
'packing.alsoBring': 'Ben de getirebilirim',
|
||||
'packing.alsoBringingStop': 'Getirmiyorum',
|
||||
'packing.cloneToMine': 'Listeme kopyala',
|
||||
|
||||
@@ -119,6 +119,7 @@ const packing: TranslationStrings = {
|
||||
'packing.sharedWithCount.few': 'надано {count} учасникам',
|
||||
'packing.sharedWithCount.many': 'надано {count} учасникам',
|
||||
'packing.broughtBy': 'приносить {name}',
|
||||
'packing.bringersTitle': 'Візьме',
|
||||
'packing.alsoBring': 'Я теж можу принести',
|
||||
'packing.alsoBringingStop': 'Я не принесу',
|
||||
'packing.cloneToMine': 'Копіювати до мого списку',
|
||||
|
||||
@@ -95,6 +95,7 @@ const packing: TranslationStrings = {
|
||||
'packing.takenCareOf': 'bởi {name}',
|
||||
'packing.sharedWithCount': 'chia sẻ với {count}',
|
||||
'packing.broughtBy': '{name} mang',
|
||||
'packing.bringersTitle': 'Người mang',
|
||||
'packing.alsoBring': 'Tôi cũng có thể mang',
|
||||
'packing.alsoBringingStop': 'Tôi không mang',
|
||||
'packing.cloneToMine': 'Sao chép vào danh sách của tôi',
|
||||
|
||||
@@ -94,6 +94,7 @@ const packing: TranslationStrings = {
|
||||
'packing.takenCareOf': '由 {name} 負責',
|
||||
'packing.sharedWithCount': '已共用給 {count} 人',
|
||||
'packing.broughtBy': '由 {name} 帶',
|
||||
'packing.bringersTitle': '攜帶者',
|
||||
'packing.alsoBring': '我也可以帶',
|
||||
'packing.alsoBringingStop': '我不帶了',
|
||||
'packing.cloneToMine': '複製到我的清單',
|
||||
|
||||
@@ -94,6 +94,7 @@ const packing: TranslationStrings = {
|
||||
'packing.takenCareOf': '由 {name} 负责',
|
||||
'packing.sharedWithCount': '已共享给 {count} 人',
|
||||
'packing.broughtBy': '由 {name} 带',
|
||||
'packing.bringersTitle': '携带者',
|
||||
'packing.alsoBring': '我也可以带',
|
||||
'packing.alsoBringingStop': '我不带了',
|
||||
'packing.cloneToMine': '复制到我的清单',
|
||||
|
||||
Reference in New Issue
Block a user