From 9a7c17e7cc042210018dd8fb50ff443753c060ea Mon Sep 17 00:00:00 2001 From: Maurice Date: Thu, 8 Oct 2026 13:00:39 +0200 Subject: [PATCH] fix(i18n): a quantity under another name than count is a plural group too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The count-string gate only knew {count} and {n}, the params t() picks a plural form by. A quantity named anything else kept one form in every language and passed: Russian read "Все {days} дней" for 2 days and "{objects} объектов" for 3 objects, Greek "{minutes} λεπτά" for 1 minute. Twelve such en strings become plural groups with their forms in all 27 locales, the quantity renamed to {count}: the trip length limit, the chat image limit, the API key limit, the storage move prompt and its reclaimable line, the Dawarich wish rule and suggestion duration, the road trip lateness warnings, and the road trip echo and window texts. Their callers pass the count as a number; the storage panels used to pass it as a string, which no form is picked by. The reclaimable line loses its dash for a comma in every locale while it is rewritten. i18n-parity now treats the quantity names in QUANTITY_PARAMS ({days}, {minutes}, {objects}, {max} and the like) like {count}. The four strings where the number is followed by nothing but a unit symbol (the Dawarich durations, the upload size limit) go on NOT_PLURAL with that reason. --- .../Admin/storage/AdminStoragePanel.test.tsx | 15 ++++++- .../Admin/storage/AdminStoragePanel.tsx | 4 +- client/src/components/Collab/useCollabChat.ts | 2 +- .../Dawarich/DawarichAcceptDialog.tsx | 4 +- .../Dawarich/DawarichAtlasDialog.tsx | 2 +- .../Roadtrip/RoadtripSidebarBadges.tsx | 2 +- .../components/Roadtrip/RoadtripStayModal.tsx | 2 +- .../components/Settings/ApiKeysSection.tsx | 2 +- client/src/components/Trips/TripFormModal.tsx | 2 +- .../screens/admin/MAdminStoragePanel.tsx | 4 +- .../screens/dashboard/MNewTripSheet.tsx | 2 +- .../screens/trip/roadtrip/MRtStaySheet.tsx | 2 +- .../screens/trip/roadtrip/MRtStopSheet.tsx | 4 +- .../mobile/screens/trip/tabs/MCollabChat.tsx | 2 +- client/src/utils/dayAdd.ts | 2 +- .../mobile/admin/MAdminStoragePanel.test.tsx | 2 +- shared/CLAUDE.md | 2 +- shared/scripts/fixtures/i18n/de/a.ts | 1 + shared/scripts/fixtures/i18n/en/a.ts | 1 + shared/scripts/i18n-parity.mjs | 43 ++++++++++++++++--- shared/src/i18n/ar/collab.ts | 7 ++- shared/src/i18n/ar/dashboard.ts | 7 ++- shared/src/i18n/ar/dawarich.ts | 14 +++++- shared/src/i18n/ar/roadtrip.ts | 35 ++++++++++++--- shared/src/i18n/ar/settings.ts | 13 +++++- shared/src/i18n/ar/storage.ts | 14 +++++- shared/src/i18n/az/collab.ts | 4 +- shared/src/i18n/az/dashboard.ts | 3 +- shared/src/i18n/az/dawarich.ts | 8 +++- shared/src/i18n/az/roadtrip.ts | 16 ++++--- shared/src/i18n/az/settings.ts | 4 +- shared/src/i18n/az/storage.ts | 8 ++-- shared/src/i18n/br/collab.ts | 3 +- shared/src/i18n/br/dashboard.ts | 3 +- shared/src/i18n/br/dawarich.ts | 6 ++- shared/src/i18n/br/roadtrip.ts | 15 ++++--- shared/src/i18n/br/settings.ts | 5 ++- shared/src/i18n/br/storage.ts | 6 ++- shared/src/i18n/ca/collab.ts | 3 +- shared/src/i18n/ca/dashboard.ts | 3 +- shared/src/i18n/ca/dawarich.ts | 6 ++- shared/src/i18n/ca/roadtrip.ts | 16 ++++--- shared/src/i18n/ca/settings.ts | 4 +- shared/src/i18n/ca/storage.ts | 6 ++- shared/src/i18n/cs/collab.ts | 4 +- shared/src/i18n/cs/dashboard.ts | 4 +- shared/src/i18n/cs/dawarich.ts | 8 +++- shared/src/i18n/cs/roadtrip.ts | 20 ++++++--- shared/src/i18n/cs/settings.ts | 7 ++- shared/src/i18n/cs/storage.ts | 8 +++- shared/src/i18n/de/collab.ts | 3 +- shared/src/i18n/de/dashboard.ts | 3 +- shared/src/i18n/de/dawarich.ts | 6 ++- shared/src/i18n/de/roadtrip.ts | 16 ++++--- shared/src/i18n/de/settings.ts | 5 ++- shared/src/i18n/de/storage.ts | 6 ++- shared/src/i18n/en/collab.ts | 3 +- shared/src/i18n/en/dashboard.ts | 3 +- shared/src/i18n/en/dawarich.ts | 6 ++- shared/src/i18n/en/roadtrip.ts | 16 ++++--- shared/src/i18n/en/settings.ts | 5 ++- shared/src/i18n/en/storage.ts | 6 ++- shared/src/i18n/es/collab.ts | 3 +- shared/src/i18n/es/dashboard.ts | 3 +- shared/src/i18n/es/dawarich.ts | 6 ++- shared/src/i18n/es/roadtrip.ts | 15 ++++--- shared/src/i18n/es/settings.ts | 5 ++- shared/src/i18n/es/storage.ts | 6 ++- shared/src/i18n/et/collab.ts | 3 +- shared/src/i18n/et/dashboard.ts | 3 +- shared/src/i18n/et/dawarich.ts | 7 ++- shared/src/i18n/et/roadtrip.ts | 15 ++++--- shared/src/i18n/et/settings.ts | 4 +- shared/src/i18n/et/storage.ts | 6 ++- shared/src/i18n/fr/collab.ts | 3 +- shared/src/i18n/fr/dashboard.ts | 3 +- shared/src/i18n/fr/dawarich.ts | 6 ++- shared/src/i18n/fr/roadtrip.ts | 16 ++++--- shared/src/i18n/fr/settings.ts | 4 +- shared/src/i18n/fr/storage.ts | 6 ++- shared/src/i18n/gr/collab.ts | 3 +- shared/src/i18n/gr/dashboard.ts | 3 +- shared/src/i18n/gr/dawarich.ts | 8 +++- shared/src/i18n/gr/roadtrip.ts | 16 ++++--- shared/src/i18n/gr/settings.ts | 5 ++- shared/src/i18n/gr/storage.ts | 6 ++- shared/src/i18n/hu/collab.ts | 3 +- shared/src/i18n/hu/dashboard.ts | 3 +- shared/src/i18n/hu/dawarich.ts | 8 +++- shared/src/i18n/hu/roadtrip.ts | 16 ++++--- shared/src/i18n/hu/settings.ts | 5 ++- shared/src/i18n/hu/storage.ts | 6 ++- shared/src/i18n/i18n-parity.spec.ts | 9 +++- shared/src/i18n/id/collab.ts | 2 +- shared/src/i18n/id/dashboard.ts | 2 +- shared/src/i18n/id/dawarich.ts | 5 ++- shared/src/i18n/id/roadtrip.ts | 10 ++--- shared/src/i18n/id/settings.ts | 3 +- shared/src/i18n/id/storage.ts | 4 +- shared/src/i18n/it/collab.ts | 3 +- shared/src/i18n/it/dashboard.ts | 3 +- shared/src/i18n/it/dawarich.ts | 6 ++- shared/src/i18n/it/roadtrip.ts | 16 ++++--- shared/src/i18n/it/settings.ts | 4 +- shared/src/i18n/it/storage.ts | 6 ++- shared/src/i18n/ja/collab.ts | 2 +- shared/src/i18n/ja/dashboard.ts | 2 +- shared/src/i18n/ja/dawarich.ts | 4 +- shared/src/i18n/ja/roadtrip.ts | 10 ++--- shared/src/i18n/ja/settings.ts | 2 +- shared/src/i18n/ja/storage.ts | 4 +- shared/src/i18n/ko/collab.ts | 2 +- shared/src/i18n/ko/dashboard.ts | 2 +- shared/src/i18n/ko/dawarich.ts | 4 +- shared/src/i18n/ko/roadtrip.ts | 10 ++--- shared/src/i18n/ko/settings.ts | 2 +- shared/src/i18n/ko/storage.ts | 4 +- shared/src/i18n/nl/collab.ts | 3 +- shared/src/i18n/nl/dashboard.ts | 3 +- shared/src/i18n/nl/dawarich.ts | 6 ++- shared/src/i18n/nl/roadtrip.ts | 15 ++++--- shared/src/i18n/nl/settings.ts | 5 ++- shared/src/i18n/nl/storage.ts | 6 ++- shared/src/i18n/pl/collab.ts | 5 ++- shared/src/i18n/pl/dashboard.ts | 5 ++- shared/src/i18n/pl/dawarich.ts | 13 +++++- shared/src/i18n/pl/roadtrip.ts | 27 +++++++++--- shared/src/i18n/pl/settings.ts | 9 +++- shared/src/i18n/pl/storage.ts | 10 ++++- shared/src/i18n/ru/collab.ts | 5 ++- shared/src/i18n/ru/dashboard.ts | 5 ++- shared/src/i18n/ru/dawarich.ts | 10 ++++- shared/src/i18n/ru/roadtrip.ts | 28 +++++++++--- shared/src/i18n/ru/settings.ts | 9 +++- shared/src/i18n/ru/storage.ts | 10 ++++- shared/src/i18n/sk/collab.ts | 4 +- shared/src/i18n/sk/dashboard.ts | 4 +- shared/src/i18n/sk/dawarich.ts | 8 +++- shared/src/i18n/sk/roadtrip.ts | 22 +++++++--- shared/src/i18n/sk/settings.ts | 6 ++- shared/src/i18n/sk/storage.ts | 8 +++- shared/src/i18n/sv/collab.ts | 3 +- shared/src/i18n/sv/dashboard.ts | 3 +- shared/src/i18n/sv/dawarich.ts | 6 ++- shared/src/i18n/sv/roadtrip.ts | 15 ++++--- shared/src/i18n/sv/settings.ts | 5 ++- shared/src/i18n/sv/storage.ts | 6 ++- shared/src/i18n/th/collab.ts | 2 +- shared/src/i18n/th/dashboard.ts | 2 +- shared/src/i18n/th/dawarich.ts | 4 +- shared/src/i18n/th/roadtrip.ts | 10 ++--- shared/src/i18n/th/settings.ts | 2 +- shared/src/i18n/th/storage.ts | 4 +- shared/src/i18n/tr/collab.ts | 3 +- shared/src/i18n/tr/dashboard.ts | 3 +- shared/src/i18n/tr/dawarich.ts | 6 ++- shared/src/i18n/tr/roadtrip.ts | 16 ++++--- shared/src/i18n/tr/settings.ts | 5 ++- shared/src/i18n/tr/storage.ts | 6 ++- shared/src/i18n/uk/collab.ts | 5 ++- shared/src/i18n/uk/dashboard.ts | 5 ++- shared/src/i18n/uk/dawarich.ts | 10 ++++- shared/src/i18n/uk/roadtrip.ts | 27 +++++++++--- shared/src/i18n/uk/settings.ts | 9 +++- shared/src/i18n/uk/storage.ts | 10 ++++- shared/src/i18n/vi/collab.ts | 2 +- shared/src/i18n/vi/dashboard.ts | 2 +- shared/src/i18n/vi/dawarich.ts | 4 +- shared/src/i18n/vi/roadtrip.ts | 10 ++--- shared/src/i18n/vi/settings.ts | 3 +- shared/src/i18n/vi/storage.ts | 4 +- shared/src/i18n/zh-TW/collab.ts | 2 +- shared/src/i18n/zh-TW/dashboard.ts | 2 +- shared/src/i18n/zh-TW/dawarich.ts | 4 +- shared/src/i18n/zh-TW/roadtrip.ts | 10 ++--- shared/src/i18n/zh-TW/settings.ts | 2 +- shared/src/i18n/zh-TW/storage.ts | 4 +- shared/src/i18n/zh/collab.ts | 2 +- shared/src/i18n/zh/dashboard.ts | 2 +- shared/src/i18n/zh/dawarich.ts | 4 +- shared/src/i18n/zh/roadtrip.ts | 10 ++--- shared/src/i18n/zh/settings.ts | 2 +- shared/src/i18n/zh/storage.ts | 4 +- 183 files changed, 864 insertions(+), 357 deletions(-) diff --git a/client/src/components/Admin/storage/AdminStoragePanel.test.tsx b/client/src/components/Admin/storage/AdminStoragePanel.test.tsx index 166bb45d6..823275608 100644 --- a/client/src/components/Admin/storage/AdminStoragePanel.test.tsx +++ b/client/src/components/Admin/storage/AdminStoragePanel.test.tsx @@ -982,11 +982,24 @@ describe('AdminStoragePanel', () => { ); expect(screen.getByText(/Moving Trip documents… 2\/5/)).toBeInTheDocument(); expect(screen.getByText(/Move finished: 4 copied, 0 skipped/)).toBeInTheDocument(); - expect(screen.getByText(/4 objects \(4\.0 KB\) remain on uploads-local — reclaim manually/)).toBeInTheDocument(); + expect(screen.getByText(/4 objects \(4\.0 KB\) remain on uploads-local, reclaim manually/)).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: 'Cancel move' })); await waitFor(() => expect(cancelledCategory).toBe('files')); }); + it('FE-ADMIN-STOR-050: the reclaimable line takes the plural form for the object count', async () => { + const state = baseState(); + (state as StorageAdminState).migrations = [ + { + category: 'journey', from: 'uploads-local', to: 'off-box', + status: 'done', done: 1, total: 1, copied: 1, skipped: 0, failed: 0, startedAt: 1, finishedAt: 2, + reclaimable: { objects: 1, bytes: 1024 }, + }, + ]; + await renderPanel(state); + expect(screen.getByText(/1 object \(1\.0 KB\) remains on uploads-local, reclaim manually/)).toBeInTheDocument(); + }); + it('FE-ADMIN-STOR-038: a done migration with sweep failures shows the doneFailures line; a clean one shows nothing extra', async () => { const state = baseState(); (state as StorageAdminState).migrations = [ diff --git a/client/src/components/Admin/storage/AdminStoragePanel.tsx b/client/src/components/Admin/storage/AdminStoragePanel.tsx index b613f1b66..acae89b84 100644 --- a/client/src/components/Admin/storage/AdminStoragePanel.tsx +++ b/client/src/components/Admin/storage/AdminStoragePanel.tsx @@ -716,7 +716,7 @@ export default function AdminStoragePanel(): React.ReactElement { {m.reclaimable && (

{t('storage.migrate.reclaimable', { - objects: String(m.reclaimable.objects), + count: m.reclaimable.objects, size: formatBytes(m.reclaimable.bytes), from: m.from, })} @@ -837,7 +837,7 @@ export default function AdminStoragePanel(): React.ReactElement { }) : t('storage.migrate.promptLine', { category: t(`storage.category.${c.category}`), - objects: String(c.objects), + count: c.objects, size: formatBytes(c.bytes ?? 0), from: c.from, to: c.to, diff --git a/client/src/components/Collab/useCollabChat.ts b/client/src/components/Collab/useCollabChat.ts index 51e071ae4..7660efdbd 100644 --- a/client/src/components/Collab/useCollabChat.ts +++ b/client/src/components/Collab/useCollabChat.ts @@ -124,7 +124,7 @@ export function useCollabChat(tripId: any, currentUser: any) { const addImageFiles = useCallback((incoming: File[] | FileList) => { images.add(incoming, ({ rejected, overflow }) => { if (rejected) toast.error(t('collab.chat.imageRejected')) - if (overflow) toast.error(t('collab.chat.imageLimit', { max: MAX_CHAT_IMAGES })) + if (overflow) toast.error(t('collab.chat.imageLimit', { count: MAX_CHAT_IMAGES })) }) }, [images, toast, t]) const removeImage = images.remove diff --git a/client/src/components/Dawarich/DawarichAcceptDialog.tsx b/client/src/components/Dawarich/DawarichAcceptDialog.tsx index f949e91b9..55706947a 100644 --- a/client/src/components/Dawarich/DawarichAcceptDialog.tsx +++ b/client/src/components/Dawarich/DawarichAcceptDialog.tsx @@ -147,7 +147,7 @@ export default function DawarichAcceptDialog({

- {t('dawarich.accept.duration', { minutes: suggestion.durationMinutes })} + {t('dawarich.accept.duration', { count: suggestion.durationMinutes })} {/* The source's own verdict, shown here rather than on every row of the list: since Dawarich 1.12.0 "unconfirmed" is the normal state @@ -180,7 +180,7 @@ export default function DawarichAcceptDialog({ {' — '} {t('dawarich.bucket.rule', { meters: DAWARICH_BUCKET_MATCH_RADIUS_M, - minutes: DAWARICH_BUCKET_MATCH_MIN_MINUTES, + count: DAWARICH_BUCKET_MATCH_MIN_MINUTES, })}

diff --git a/client/src/components/Dawarich/DawarichAtlasDialog.tsx b/client/src/components/Dawarich/DawarichAtlasDialog.tsx index afb5f0e88..c54aca4ec 100644 --- a/client/src/components/Dawarich/DawarichAtlasDialog.tsx +++ b/client/src/components/Dawarich/DawarichAtlasDialog.tsx @@ -216,7 +216,7 @@ export default function DawarichAtlasDialog({ scan?.truncated ? t('dawarich.bucket.truncated') : null, t('dawarich.bucket.rule', { meters: DAWARICH_BUCKET_MATCH_RADIUS_M, - minutes: DAWARICH_BUCKET_MATCH_MIN_MINUTES, + count: DAWARICH_BUCKET_MATCH_MIN_MINUTES, }), ]} > diff --git a/client/src/components/Roadtrip/RoadtripSidebarBadges.tsx b/client/src/components/Roadtrip/RoadtripSidebarBadges.tsx index b4610c8fe..3ed893449 100644 --- a/client/src/components/Roadtrip/RoadtripSidebarBadges.tsx +++ b/client/src/components/Roadtrip/RoadtripSidebarBadges.tsx @@ -208,7 +208,7 @@ export function Arrival({ entry }: { entry: ScheduleEntry }): React.ReactElement */ export function LateBadge({ late }: { late: ScheduleWarning }): React.ReactElement { const { t } = useTranslation() - const label = t(late.code === 'missedLeave' ? 'roadtrip.warn.missedLeave' : 'roadtrip.warn.late', { minutes: late.minutes ?? 0 }) + const label = t(late.code === 'missedLeave' ? 'roadtrip.warn.missedLeave' : 'roadtrip.warn.late', { count: late.minutes ?? 0 }) // The same two-part shell the stay and the drive findings wear, so a row of badges reads // as one set instead of a pill among boxes. return ( diff --git a/client/src/components/Roadtrip/RoadtripStayModal.tsx b/client/src/components/Roadtrip/RoadtripStayModal.tsx index 88457ff8b..b4c038c88 100644 --- a/client/src/components/Roadtrip/RoadtripStayModal.tsx +++ b/client/src/components/Roadtrip/RoadtripStayModal.tsx @@ -264,7 +264,7 @@ function LeaveTime({ stop, leave, until: leaveAt, is12h, onClose }: { {leave.missedBy !== null ? (

- {t('roadtrip.warn.missedLeave', { minutes: leave.missedBy })} + {t('roadtrip.warn.missedLeave', { count: leave.missedBy })}

) : (

diff --git a/client/src/components/Settings/ApiKeysSection.tsx b/client/src/components/Settings/ApiKeysSection.tsx index b65ef35a0..ca5a7ec1e 100644 --- a/client/src/components/Settings/ApiKeysSection.tsx +++ b/client/src/components/Settings/ApiKeysSection.tsx @@ -250,7 +250,7 @@ export default function ApiKeysSection(): React.ReactElement { {atLimit && (

-

{t('settings.apiKeys.limitReached', { max: MAX_KEYS })}

+

{t('settings.apiKeys.limitReached', { count: MAX_KEYS })}

)} diff --git a/client/src/components/Trips/TripFormModal.tsx b/client/src/components/Trips/TripFormModal.tsx index 0d6a07299..cc12fdb49 100644 --- a/client/src/components/Trips/TripFormModal.tsx +++ b/client/src/components/Trips/TripFormModal.tsx @@ -169,7 +169,7 @@ export default function TripFormModal({ isOpen, onClose, onSave, trip, onCoverUp // Only a range being set is held to the limit, as on the server: a trip that // already carries a longer one can still be renamed. const datesTouched = !trip || formData.start_date !== (trip.start_date || '') || formData.end_date !== (trip.end_date || '') - if (datesTouched && span > MAX_TRIP_DAYS) { setError(t('dashboard.tripTooLong', { days: MAX_TRIP_DAYS })); return } + if (datesTouched && span > MAX_TRIP_DAYS) { setError(t('dashboard.tripTooLong', { count: MAX_TRIP_DAYS })); return } } if (!formData.start_date && !formData.end_date) { const dc = Number(formData.day_count) diff --git a/client/src/mobile/screens/admin/MAdminStoragePanel.tsx b/client/src/mobile/screens/admin/MAdminStoragePanel.tsx index d662ba49f..9d82dfa59 100644 --- a/client/src/mobile/screens/admin/MAdminStoragePanel.tsx +++ b/client/src/mobile/screens/admin/MAdminStoragePanel.tsx @@ -703,7 +703,7 @@ export default function MAdminStoragePanel(): React.ReactElement { {m.reclaimable && (

{t('storage.migrate.reclaimable', { - objects: String(m.reclaimable.objects), + count: m.reclaimable.objects, size: formatBytes(m.reclaimable.bytes), from: m.from, })} @@ -834,7 +834,7 @@ export default function MAdminStoragePanel(): React.ReactElement { }) : t('storage.migrate.promptLine', { category: t(`storage.category.${c.category}`), - objects: String(c.objects), + count: c.objects, size: formatBytes(c.bytes ?? 0), from: c.from, to: c.to, diff --git a/client/src/mobile/screens/dashboard/MNewTripSheet.tsx b/client/src/mobile/screens/dashboard/MNewTripSheet.tsx index ea052d83b..38bece089 100644 --- a/client/src/mobile/screens/dashboard/MNewTripSheet.tsx +++ b/client/src/mobile/screens/dashboard/MNewTripSheet.tsx @@ -127,7 +127,7 @@ export default function MNewTripSheet({ open, trip, onClose, onSave, onCoverUpda const span = tripSpanDays(startDate, endDate) if (span < 1) { setError(t('dashboard.endDateError')); return null } const datesTouched = !trip || startDate !== (trip.start_date || '') || endDate !== (trip.end_date || '') - if (datesTouched && span > MAX_TRIP_DAYS) { setError(t('dashboard.tripTooLong', { days: MAX_TRIP_DAYS })); return null } + if (datesTouched && span > MAX_TRIP_DAYS) { setError(t('dashboard.tripTooLong', { count: MAX_TRIP_DAYS })); return null } } return { title: title.trim(), diff --git a/client/src/mobile/screens/trip/roadtrip/MRtStaySheet.tsx b/client/src/mobile/screens/trip/roadtrip/MRtStaySheet.tsx index 27915e81e..e8b18fcf0 100644 --- a/client/src/mobile/screens/trip/roadtrip/MRtStaySheet.tsx +++ b/client/src/mobile/screens/trip/roadtrip/MRtStaySheet.tsx @@ -345,7 +345,7 @@ function RtLeaveTime({ name, leave, arrival, departure, onClose }: { {leave.missedBy !== null ? (

) : (
diff --git a/client/src/mobile/screens/trip/roadtrip/MRtStopSheet.tsx b/client/src/mobile/screens/trip/roadtrip/MRtStopSheet.tsx index 1758f5295..5a6c02063 100644 --- a/client/src/mobile/screens/trip/roadtrip/MRtStopSheet.tsx +++ b/client/src/mobile/screens/trip/roadtrip/MRtStopSheet.tsx @@ -209,8 +209,8 @@ export default function MRtStopSheet({ planner, shell }: MTripSheetsProps) { ...day.driveWarnings.filter(w => w.index === index && (w.code === 'leg' || w.code === 'range')), ] const warningText = (w: ScheduleWarning): string => { - if (w.code === 'late') return t('roadtrip.warn.late', { minutes: w.minutes ?? 0 }) - if (w.code === 'missedLeave') return t('roadtrip.warn.missedLeave', { minutes: w.minutes ?? 0 }) + if (w.code === 'late') return t('roadtrip.warn.late', { count: w.minutes ?? 0 }) + if (w.code === 'missedLeave') return t('roadtrip.warn.missedLeave', { count: w.minutes ?? 0 }) if (w.code === 'range') return t('roadtrip.limit.range', { distance: formatDistance(w.sinceKm ?? 0, unit) }) return t('roadtrip.limit.legOver', { time: formatDurationShort((w.overMinutes ?? 0) * 60) }) } diff --git a/client/src/mobile/screens/trip/tabs/MCollabChat.tsx b/client/src/mobile/screens/trip/tabs/MCollabChat.tsx index bb43887ba..50147a5d1 100644 --- a/client/src/mobile/screens/trip/tabs/MCollabChat.tsx +++ b/client/src/mobile/screens/trip/tabs/MCollabChat.tsx @@ -162,7 +162,7 @@ export default function MCollabChat({ planner }: MCollabChatProps) { const addImageFiles = (incoming: FileList | File[]) => { images.add(incoming, ({ rejected, overflow }) => { if (rejected) toast.error(t('collab.chat.imageRejected')) - if (overflow) toast.error(t('collab.chat.imageLimit', { max: MAX_CHAT_IMAGES })) + if (overflow) toast.error(t('collab.chat.imageLimit', { count: MAX_CHAT_IMAGES })) }) } diff --git a/client/src/utils/dayAdd.ts b/client/src/utils/dayAdd.ts index 0c884d27e..343f26ac0 100644 --- a/client/src/utils/dayAdd.ts +++ b/client/src/utils/dayAdd.ts @@ -33,5 +33,5 @@ export function datedDayOption( ): Pick { const plan = trip ? planDatedAppend(trip, days.map(d => d.date)) : null if (!plan) return { nextDate: null, datedBlocked: null } - return { nextDate: plan.date, datedBlocked: plan.fits ? null : t('dashboard.tripTooLong', { days: MAX_TRIP_DAYS }) } + return { nextDate: plan.date, datedBlocked: plan.fits ? null : t('dashboard.tripTooLong', { count: MAX_TRIP_DAYS }) } } diff --git a/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx b/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx index 6da31e47e..3b5b8fa63 100644 --- a/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx +++ b/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx @@ -415,7 +415,7 @@ describe('MAdminStoragePanel', () => { ); expect(screen.getByText(/Moving Trip documents… 2\/5/)).toBeInTheDocument(); expect(screen.getByText(/Move finished: 4 copied, 0 skipped/)).toBeInTheDocument(); - expect(screen.getByText(/4 objects \(4\.0 KB\) remain on uploads-local — reclaim manually/)).toBeInTheDocument(); + expect(screen.getByText(/4 objects \(4\.0 KB\) remain on uploads-local, reclaim manually/)).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: 'Cancel move' })); await waitFor(() => expect(cancelledCategory).toBe('files')); }); diff --git a/shared/CLAUDE.md b/shared/CLAUDE.md index 656f760de..f8d70ab91 100644 --- a/shared/CLAUDE.md +++ b/shared/CLAUDE.md @@ -51,7 +51,7 @@ The parity rule governs **existing** routes; it is not a license to mint new deb - **`en/` is canonical.** Every other locale must have the identical file set and top-level keys — `i18n:parity:strict` enforces it in CI. When you add or rename a key, update every locale. - **Every locale gets a real translation; an English placeholder is not acceptable.** Write native phrasing with the locale's own punctuation and the same `{placeholders}` as `en`; if you genuinely cannot translate one, say so instead of filling it with English. Parity only checks that the key exists, so `i18n:parity:strict` also runs a ratchet (`scripts/i18n-untranslated.mjs`): per locale and file, the `// en-fallback` markers and the unmarked values identical to `en` may not grow past `scripts/i18n-untranslated-baseline.json`. An identical value is excused only when, after dropping placeholders, markup, URLs/e-mail/paths, the product names in `BRAND_NAMES` (Google Maps, Atlas, Immich) and the unit symbols in `UNITS`, no plain word is left (a word with a capital after its first letter, an acronym or a single letter does not count), so "{count} km", "GPX", "OAuth" and "Atlas" pass while "Settings" and "Source code" count. A single ordinary word that really is the locale's own (German "Status", French "Transport") carries `// same-as-en` on its declaration; the marker is honoured only on a value holding one plain word once the excused parts are dropped (on a phrase it is ignored, so it cannot wave a copied sentence through) and only in Latin-script locales (`LATIN_LOCALES`), because a Latin word in Japanese or Arabic is English whatever it says. A plural form en has no key for (Russian `.few`, Arabic `.two`) counts when it equals any form en spells out for that group. Every locale folder must be on `LATIN_LOCALES` or `NON_LATIN_LOCALES`, so a new language is classified there first. Deleting a marker without translating moves the string from one count to the other and still fails. After translating, run `npm run i18n:untranslated:update` to lower the baseline; nothing raises it. - **Count-bearing strings use plural forms, never `count === 1` in the caller.** The key holds the general form (the CLDR "other" category) and `key.zero|one|two|few|many` hold the forms the language's `Intl.PluralRules` selects for whole numbers (`plural.ts`); `t(key, { count })` (or `{ n }`) picks one. Pass the count as a number. Russian needs `.one`, `.few` and `.many`, Arabic all five, Japanese none. `i18n:parity` checks that every locale has exactly the forms its language reaches with whole numbers and none it never selects. A form may spell the number out (Arabic "ملف واحد") only where its category is that one number; French `one` also covers 0 and Russian `one` covers 21, so they keep `{count}`. A string whose wording changes with the number (the old "Copy to trip" for one place, "Copy {count} to trip" for more) is a plural group whose `one` drops the number where the language allows it, not two keys switched in the caller. -- **An `en` string carrying `{count}` or `{n}` must be a plural group**, even when the forms read the same (a label such as "Notes: {count}"), so every language can inflect. `i18n:parity` refuses one that is not, unless `NOT_PLURAL` in `scripts/i18n-parity.mjs` lists it with a reason: only for a number that is never a quantity (a day number, a step, a multiplier). An entry that stops matching fails too. +- **An `en` string carrying `{count}` or `{n}` must be a plural group**, even when the forms read the same (a label such as "Notes: {count}"), so every language can inflect. **A quantity is always named `{count}` (or `{n}`)**, the only params `t()` picks a form by: "All {days} days" read "Все 3 дней" in Russian. `i18n:parity` refuses an ungrouped string carrying `{count}`, `{n}` or one of the quantity names in `QUANTITY_PARAMS` (`{days}`, `{minutes}`, `{objects}`, `{max}` …), unless `NOT_PLURAL` in `scripts/i18n-parity.mjs` lists it with a reason: only for a number that is never a quantity (a day number, a step, a multiplier) or one followed by nothing but a unit symbol ("{minutes} min", "{max} MB"). A second number in the same string keeps its own name ("{over} of {count} days"). An entry that stops matching fails too. - **Further i18n specs run in `npm test`** and fail changes that parity lets through: placeholder parity (every `{placeholder}` in an `en` string must appear in each translation), no `en` string may call TREK "self-hosted" (the same build runs on managed installs; exemptions live in the spec), and a wording regression test for plugin permission descriptions (currently one string; add a case there when a permission's real behavior changes). None of these are part of `i18n:parity`, which checks key sets, plural forms, count strings and the untranslated ratchet. - **The i18n scripts read the tables as text** through `scripts/i18n-catalog.mjs`: a value must be one string literal on the key's line or the next, and anything else (a concatenation, a template literal) is an error rather than a skipped key. Their specs use the fixtures in `scripts/fixtures/`. That en holds every key the client names is checked on the client side (`npm run lint:i18n-keys` in `client/`). diff --git a/shared/scripts/fixtures/i18n/de/a.ts b/shared/scripts/fixtures/i18n/de/a.ts index 161ef0d78..ea0e1726c 100644 --- a/shared/scripts/fixtures/i18n/de/a.ts +++ b/shared/scripts/fixtures/i18n/de/a.ts @@ -10,5 +10,6 @@ const a = { 'a.quote': 'Es ist hier', 'a.multi': 'A long sentence that wraps onto the next line', + 'a.stay': '{days} Tage bleiben', }; export default a; diff --git a/shared/scripts/fixtures/i18n/en/a.ts b/shared/scripts/fixtures/i18n/en/a.ts index 83affa1e6..bc48e500b 100644 --- a/shared/scripts/fixtures/i18n/en/a.ts +++ b/shared/scripts/fixtures/i18n/en/a.ts @@ -10,5 +10,6 @@ const a = { 'a.quote': "It's here", 'a.multi': 'A long sentence that wraps onto the next line', + 'a.stay': 'Stay {days} days', }; export default a; diff --git a/shared/scripts/i18n-parity.mjs b/shared/scripts/i18n-parity.mjs index 33411f80e..64aefd1c9 100644 --- a/shared/scripts/i18n-parity.mjs +++ b/shared/scripts/i18n-parity.mjs @@ -21,8 +21,11 @@ // `Intl.PluralRules`, through the same helpers the client resolves with. // 4. Count strings: an en string carrying {count} or {n} (the two params // `t()` picks a plural form by) must be a plural group, so every -// language gets the forms it needs. The exceptions are NOT_PLURAL: a -// number that is never a quantity ("Day {n}", a step, a multiplier). +// language gets the forms it needs. So must one carrying a quantity +// under another name ({days}, {minutes}, {objects}, QUANTITY_PARAMS), +// which no form is picked by: it is renamed to {count}. The exceptions +// are NOT_PLURAL: a number that is never a quantity ("Day {n}", a step, +// a multiplier), or one followed only by a unit symbol. // 5. Untranslated strings: the marked `// en-fallback` and unmarked English // copies per locale and file may only shrink (i18n-untranslated.mjs, // which also lowers the baseline with --update). @@ -94,13 +97,37 @@ export const NOT_PLURAL = [ { key: 'system_notice.pager.goto', because: 'the position of a notice in the pager' }, { key: 'tours.addedToDay', because: 'the number of the day a tour was added to' }, { key: 'tours.planner.waypointLabel', because: 'the position of a waypoint on the tour' }, + { key: 'dawarich.duration.minutes', because: 'a duration in a unit symbol (min), the same for any number' }, + { key: 'dawarich.duration.hours', because: 'a duration in a unit symbol (h), the same for any number' }, + { key: 'dawarich.duration.hoursMinutes', because: 'a duration in unit symbols (h, min), the same for any number' }, + { key: 'files.uploadErrorSize', because: 'a size limit in a unit symbol (MB), the same for any number' }, ]; -const COUNT_PARAM_RE = /\{(?:count|n)\}/; +/** + * Param names that hold a quantity of something but are not the ones `t()` + * picks a plural form by. A string using one keeps a single form in every + * language ("Все {days} дней" for 2 days), so it is held like {count}: rename + * the param to {count} and make the string a plural group. + */ +export const QUANTITY_PARAMS = [ + 'days', + 'nights', + 'weeks', + 'months', + 'years', + 'hours', + 'minutes', + 'seconds', + 'items', + 'objects', + 'max', +]; + +const COUNT_PARAM_RE = new RegExp(`\\{(?:count|n|${QUANTITY_PARAMS.join('|')})\\}`); /** - * en strings carrying a count param outside any plural group, and NOT_PLURAL - * entries that no longer name such a string. + * en strings carrying a count or quantity param outside any plural group, + * and NOT_PLURAL entries that no longer name such a string. */ function checkCountStrings(enFiles, notPlural = NOT_PLURAL, root = I18N_ROOT) { const allowed = new Set(notPlural.map((e) => e.key)); @@ -233,9 +260,11 @@ function formatReport(report) { if (ungrouped.length === 0 && stale.length === 0) { lines.push('Count strings: OK'); } else { - lines.push('Count strings: en strings with {count} or {n} that are no plural group'); + lines.push('Count strings: en strings with {count}, {n} or a quantity param that are no plural group'); for (const { file, key } of ungrouped) { - lines.push(` en/${file}: ${key} (add ${key}.one and the other forms, see shared/CLAUDE.md)`); + lines.push( + ` en/${file}: ${key} (name the quantity {count} and add ${key}.one and the other forms, see shared/CLAUDE.md)`, + ); } for (const key of stale) lines.push(` NOT_PLURAL lists ${key}, which is no ungrouped count string: remove it`); } diff --git a/shared/src/i18n/ar/collab.ts b/shared/src/i18n/ar/collab.ts index 74958147d..d1ae3dbc3 100644 --- a/shared/src/i18n/ar/collab.ts +++ b/shared/src/i18n/ar/collab.ts @@ -116,7 +116,12 @@ const collab: TranslationStrings = { 'collab.polls.delete': 'حذف', 'collab.polls.closedSection': 'مغلق', 'collab.notes.websitePlaceholder': 'https://...', // en-fallback - 'collab.chat.imageLimit': 'يمكنك إرفاق ما يصل إلى {max} صور لكل رسالة', + 'collab.chat.imageLimit': 'يمكنك إرفاق ما يصل إلى {count} صورة لكل رسالة', + 'collab.chat.imageLimit.zero': 'يمكنك إرفاق ما يصل إلى {count} صورة لكل رسالة', + 'collab.chat.imageLimit.one': 'يمكنك إرفاق صورة واحدة فقط لكل رسالة', + 'collab.chat.imageLimit.two': 'يمكنك إرفاق صورتين كحد أقصى لكل رسالة', + 'collab.chat.imageLimit.few': 'يمكنك إرفاق ما يصل إلى {count} صور لكل رسالة', + 'collab.chat.imageLimit.many': 'يمكنك إرفاق ما يصل إلى {count} صورة لكل رسالة', 'collab.chat.uploading': 'جارٍ الرفع {percent}%', 'collab.chat.attachImages': 'إرفاق صور', 'collab.chat.attachedImage': 'صورة مرفقة', diff --git a/shared/src/i18n/ar/dashboard.ts b/shared/src/i18n/ar/dashboard.ts index d7eedef6f..05dd4bfa7 100644 --- a/shared/src/i18n/ar/dashboard.ts +++ b/shared/src/i18n/ar/dashboard.ts @@ -127,7 +127,12 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'استخدم صورة من Unsplash بواسطة {photographer}', 'dashboard.titleRequired': 'العنوان مطلوب', 'dashboard.endDateError': 'يجب أن يكون تاريخ النهاية بعد البداية', - 'dashboard.tripTooLong': 'لا يمكن أن تتجاوز مدة الرحلة {days} يومًا', + 'dashboard.tripTooLong': 'لا يمكن أن تتجاوز مدة الرحلة {count} يوم', + 'dashboard.tripTooLong.zero': 'لا يمكن أن تتجاوز مدة الرحلة {count} يوم', + 'dashboard.tripTooLong.one': 'لا يمكن أن تتجاوز مدة الرحلة يومًا واحدًا', + 'dashboard.tripTooLong.two': 'لا يمكن أن تتجاوز مدة الرحلة يومين', + 'dashboard.tripTooLong.few': 'لا يمكن أن تتجاوز مدة الرحلة {count} أيام', + 'dashboard.tripTooLong.many': 'لا يمكن أن تتجاوز مدة الرحلة {count} يومًا', 'dashboard.dateShiftTitle': 'تاريخ بداية جديد', 'dashboard.dateShiftIntro': 'لقد غيّرت موعد بداية هذه الرحلة. كيف تريد أن تتبع خططك التواريخ الجديدة؟', 'dashboard.dateShiftKeepBookings': 'إبقاء الحجوزات في تواريخها', diff --git a/shared/src/i18n/ar/dawarich.ts b/shared/src/i18n/ar/dawarich.ts index 7324b8c50..8c7b51ab5 100644 --- a/shared/src/i18n/ar/dawarich.ts +++ b/shared/src/i18n/ar/dawarich.ts @@ -132,7 +132,12 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'إضافة المدوّنة', 'dawarich.accept.confirm.bucket_list': 'اشطبها', 'dawarich.accept.recorded': 'مسجَّلة من {from} حتى {to}', - 'dawarich.accept.duration': '{minutes} دقيقة', + 'dawarich.accept.duration': '{count} دقيقة', + 'dawarich.accept.duration.zero': '{count} دقيقة', + 'dawarich.accept.duration.one': 'دقيقة واحدة', + 'dawarich.accept.duration.two': 'دقيقتان', + 'dawarich.accept.duration.few': '{count} دقائق', + 'dawarich.accept.duration.many': '{count} دقيقة', 'dawarich.accept.name': 'الاسم', 'dawarich.accept.date': 'التاريخ', 'dawarich.accept.from': 'الوصول', @@ -225,7 +230,12 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'تحقق مرة أخرى', 'dawarich.bucket.metersAway': 'على بُعد {meters} م', 'dawarich.bucket.kilometersAway': 'على بُعد {km} كم', - 'dawarich.bucket.rule': 'تُحتسب الأمنية محققة ضمن {meters} م وبعد {minutes} دقيقة في المكان.', + 'dawarich.bucket.rule': 'تُحتسب الأمنية محققة ضمن {meters} م وبعد {count} دقيقة في المكان.', + 'dawarich.bucket.rule.zero': 'تُحتسب الأمنية محققة ضمن {meters} م وبعد {count} دقيقة في المكان.', + 'dawarich.bucket.rule.one': 'تُحتسب الأمنية محققة ضمن {meters} م وبعد دقيقة واحدة في المكان.', + 'dawarich.bucket.rule.two': 'تُحتسب الأمنية محققة ضمن {meters} م وبعد دقيقتين في المكان.', + 'dawarich.bucket.rule.few': 'تُحتسب الأمنية محققة ضمن {meters} م وبعد {count} دقائق في المكان.', + 'dawarich.bucket.rule.many': 'تُحتسب الأمنية محققة ضمن {meters} م وبعد {count} دقيقة في المكان.', 'dawarich.journey.dayStays.other': '{count} إقامة من Dawarich', 'dawarich.journey.dayStays.zero': 'لا توجد إقامات من Dawarich', 'dawarich.journey.dayStays.one': 'إقامة واحدة من Dawarich', diff --git a/shared/src/i18n/ar/roadtrip.ts b/shared/src/i18n/ar/roadtrip.ts index 01da48bd8..82e18103b 100644 --- a/shared/src/i18n/ar/roadtrip.ts +++ b/shared/src/i18n/ar/roadtrip.ts @@ -54,7 +54,12 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'يلزم توفر جميع المسارات الرابطة لحساب الاستراحات اليومية.', 'roadtrip.window.conflict': 'يتعارض وقت ثابت مع القيادة والاستراحات. تبقى الأوقات اليدوية كما هي؛ تم إيقاف التخطيط التلقائي مؤقتًا.', - 'roadtrip.window.tooLong': 'تتطلب هذه الأوقات أكثر من {days} يومًا إضافيًا. اختر فترة يومية أطول.', + 'roadtrip.window.tooLong': 'تتطلب هذه الأوقات أكثر من {count} يوم إضافي. اختر فترة يومية أطول.', + 'roadtrip.window.tooLong.zero': 'تتطلب هذه الأوقات أكثر من {count} يوم إضافي. اختر فترة يومية أطول.', + 'roadtrip.window.tooLong.one': 'تتطلب هذه الأوقات أكثر من يوم إضافي واحد. اختر فترة يومية أطول.', + 'roadtrip.window.tooLong.two': 'تتطلب هذه الأوقات أكثر من يومين إضافيين. اختر فترة يومية أطول.', + 'roadtrip.window.tooLong.few': 'تتطلب هذه الأوقات أكثر من {count} أيام إضافية. اختر فترة يومية أطول.', + 'roadtrip.window.tooLong.many': 'تتطلب هذه الأوقات أكثر من {count} يومًا إضافيًا. اختر فترة يومية أطول.', 'roadtrip.mode.roadtrip': 'رحلة برية', 'roadtrip.mode.days': 'الأيام', 'roadtrip.mode.label': 'عرض الخطة', @@ -105,8 +110,18 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'دقيقة', 'roadtrip.limit.off': 'معطل', 'roadtrip.limit.blockNote': 'كتلة واحدة = {distance}', - 'roadtrip.limit.echoOver': '{over} من {days} أيام تتجاوز حدّك.', - 'roadtrip.limit.echoNone': 'كل الأيام {days} ضمن حدودك.', + 'roadtrip.limit.echoOver': '{over} من {count} يوم تتجاوز حدّك.', + 'roadtrip.limit.echoOver.zero': '{over} من {count} يوم تتجاوز حدّك.', + 'roadtrip.limit.echoOver.one': '{over} من يوم واحد يتجاوز حدّك.', + 'roadtrip.limit.echoOver.two': '{over} من يومين تتجاوز حدّك.', + 'roadtrip.limit.echoOver.few': '{over} من {count} أيام تتجاوز حدّك.', + 'roadtrip.limit.echoOver.many': '{over} من {count} يومًا تتجاوز حدّك.', + 'roadtrip.limit.echoNone': 'كل الأيام {count} ضمن حدودك.', + 'roadtrip.limit.echoNone.zero': 'كل الأيام {count} ضمن حدودك.', + 'roadtrip.limit.echoNone.one': 'اليوم الوحيد ضمن حدودك.', + 'roadtrip.limit.echoNone.two': 'اليومان كلاهما ضمن حدودك.', + 'roadtrip.limit.echoNone.few': 'كل الأيام {count} ضمن حدودك.', + 'roadtrip.limit.echoNone.many': 'كل الأيام {count} ضمن حدودك.', 'roadtrip.limit.echoDry': '{count} مقطع بلا محطة يمكن بلوغها.', 'roadtrip.limit.echoDry.zero': 'لا توجد مقاطع بلا محطة يمكن بلوغها.', 'roadtrip.limit.echoDry.one': 'مقطع واحد بلا محطة يمكن بلوغها.', @@ -308,8 +323,18 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'محسوب من زمن القيادة', 'roadtrip.stop.endIsLeave': 'في الرحلة البرية، تنطلق الرحلة في هذا الوقت.', 'roadtrip.via.hint': 'اسحب لإعادة تشكيل المسار، وانقر بالزر الأيمن للإزالة', - 'roadtrip.warn.late': 'الوصول بعد {minutes} دقيقة من الوقت المحدد', - 'roadtrip.warn.missedLeave': 'الوصول بعد {minutes} دقيقة من وقت المغادرة المحدد', + 'roadtrip.warn.late': 'الوصول بعد {count} دقيقة من الوقت المحدد', + 'roadtrip.warn.late.zero': 'الوصول بعد {count} دقيقة من الوقت المحدد', + 'roadtrip.warn.late.one': 'الوصول بعد دقيقة واحدة من الوقت المحدد', + 'roadtrip.warn.late.two': 'الوصول بعد دقيقتين من الوقت المحدد', + 'roadtrip.warn.late.few': 'الوصول بعد {count} دقائق من الوقت المحدد', + 'roadtrip.warn.late.many': 'الوصول بعد {count} دقيقة من الوقت المحدد', + 'roadtrip.warn.missedLeave': 'الوصول بعد {count} دقيقة من وقت المغادرة المحدد', + 'roadtrip.warn.missedLeave.zero': 'الوصول بعد {count} دقيقة من وقت المغادرة المحدد', + 'roadtrip.warn.missedLeave.one': 'الوصول بعد دقيقة واحدة من وقت المغادرة المحدد', + 'roadtrip.warn.missedLeave.two': 'الوصول بعد دقيقتين من وقت المغادرة المحدد', + 'roadtrip.warn.missedLeave.few': 'الوصول بعد {count} دقائق من وقت المغادرة المحدد', + 'roadtrip.warn.missedLeave.many': 'الوصول بعد {count} دقيقة من وقت المغادرة المحدد', 'roadtrip.warn.overnight': 'اليوم التالي', 'roadtrip.spill.title': 'من اليوم {number}', 'roadtrip.spill.departs': 'المغادرة {time}', diff --git a/shared/src/i18n/ar/settings.ts b/shared/src/i18n/ar/settings.ts index 614d19387..1aea8f4f2 100644 --- a/shared/src/i18n/ar/settings.ts +++ b/shared/src/i18n/ar/settings.ts @@ -640,7 +640,18 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'نقطة النهاية', 'settings.apiKeys.neverUsed': 'لم يُستخدم قط', 'settings.apiKeys.loadFailed': 'تعذّر تحميل مفاتيحك. أعد تحميل الصفحة للمحاولة مرة أخرى.', - 'settings.apiKeys.limitReached': 'لديك {max} مفاتيح، وهو الحد الأقصى للحساب الواحد. احذف مفتاحًا لم تعد تستخدمه لإنشاء مفتاح جديد.', + 'settings.apiKeys.limitReached': + 'لديك {count} مفتاح، وهو الحد الأقصى للحساب الواحد. احذف مفتاحًا لم تعد تستخدمه لإنشاء مفتاح جديد.', + 'settings.apiKeys.limitReached.zero': + 'لديك {count} مفتاح، وهو الحد الأقصى للحساب الواحد. احذف مفتاحًا لم تعد تستخدمه لإنشاء مفتاح جديد.', + 'settings.apiKeys.limitReached.one': + 'لديك مفتاح واحد، وهو الحد الأقصى للحساب الواحد. احذفه إن لم تعد تستخدمه لإنشاء مفتاح جديد.', + 'settings.apiKeys.limitReached.two': + 'لديك مفتاحان، وهو الحد الأقصى للحساب الواحد. احذف مفتاحًا لم تعد تستخدمه لإنشاء مفتاح جديد.', + 'settings.apiKeys.limitReached.few': + 'لديك {count} مفاتيح، وهو الحد الأقصى للحساب الواحد. احذف مفتاحًا لم تعد تستخدمه لإنشاء مفتاح جديد.', + 'settings.apiKeys.limitReached.many': + 'لديك {count} مفتاحًا، وهو الحد الأقصى للحساب الواحد. احذف مفتاحًا لم تعد تستخدمه لإنشاء مفتاح جديد.', 'settings.apiKeys.copyFailed': 'تعذّر النسخ. حدّد النص وانسخه يدويًا.', 'settings.apiKeys.modal.createTitle': 'إنشاء مفتاح API', 'settings.apiKeys.modal.name': 'الاسم', diff --git a/shared/src/i18n/ar/storage.ts b/shared/src/i18n/ar/storage.ts index 523925570..f87417d5e 100644 --- a/shared/src/i18n/ar/storage.ts +++ b/shared/src/i18n/ar/storage.ts @@ -124,7 +124,12 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'هل تريد نقل الكائنات الحالية إلى الواجهة الخلفية الجديدة؟', - 'storage.migrate.promptLine': '{category}: {objects} كائن ({size}) من {from} إلى {to}', + 'storage.migrate.promptLine': '{category}: {count} كائن ({size}) من {from} إلى {to}', + 'storage.migrate.promptLine.zero': '{category}: {count} كائن ({size}) من {from} إلى {to}', + 'storage.migrate.promptLine.one': '{category}: كائن واحد ({size}) من {from} إلى {to}', + 'storage.migrate.promptLine.two': '{category}: كائنان ({size}) من {from} إلى {to}', + 'storage.migrate.promptLine.few': '{category}: {count} كائنات ({size}) من {from} إلى {to}', + 'storage.migrate.promptLine.many': '{category}: {count} كائنًا ({size}) من {from} إلى {to}', 'storage.migrate.promptLineUnknown': '{category}: حجم غير معروف (لم يُجرَ فحص استخدام بعد) من {from} إلى {to}', 'storage.migrate.move': 'نقل الكائنات الحالية', 'storage.migrate.routeOnly': 'توجيه الكتابات الجديدة فقط', @@ -133,7 +138,12 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} فشل — لم يتم نسخ تلك الكائنات إلى الواجهة الخلفية الجديدة', 'storage.migrate.failed': 'فشل النقل: {error} — لم يتم تبديل الفئة', 'storage.migrate.cancelled': 'تم إلغاء النقل — لم يتم تبديل أي شيء', - 'storage.migrate.reclaimable': '{objects} كائن ({size}) لا تزال على {from} — استرجعها يدويًا', + 'storage.migrate.reclaimable': '{count} كائن ({size}) لا تزال على {from}، استرجعها يدويًا', + 'storage.migrate.reclaimable.zero': '{count} كائن ({size}) لا تزال على {from}، استرجعها يدويًا', + 'storage.migrate.reclaimable.one': 'كائن واحد ({size}) لا يزال على {from}، استرجعه يدويًا', + 'storage.migrate.reclaimable.two': 'كائنان ({size}) لا يزالان على {from}، استرجعهما يدويًا', + 'storage.migrate.reclaimable.few': '{count} كائنات ({size}) لا تزال على {from}، استرجعها يدويًا', + 'storage.migrate.reclaimable.many': '{count} كائنًا ({size}) لا تزال على {from}، استرجعها يدويًا', 'storage.migrate.cancel': 'إلغاء النقل', 'storage.migrate.promptCancel': 'إلغاء', 'storage.migrate.queued': 'في قائمة الانتظار: {categories}', diff --git a/shared/src/i18n/az/collab.ts b/shared/src/i18n/az/collab.ts index 37c55147c..4ad2c2b66 100644 --- a/shared/src/i18n/az/collab.ts +++ b/shared/src/i18n/az/collab.ts @@ -112,8 +112,8 @@ const collab: TranslationStrings = { 'collab.polls.delete': 'Sil', 'collab.polls.closedSection': 'Bağlanmış sorğular', - 'collab.chat.imageLimit': - 'Hər mesaja maksimum {max} şəkil əlavə edə bilərsiniz', + 'collab.chat.imageLimit': 'Hər mesaja maksimum {count} şəkil əlavə edə bilərsiniz', + 'collab.chat.imageLimit.one': 'Hər mesaja yalnız {count} şəkil əlavə edə bilərsiniz', 'collab.chat.uploading': 'Yüklənir: {percent}%', 'collab.chat.attachImages': 'Şəkillər əlavə et', 'collab.chat.attachedImage': 'Əlavə edilmiş şəkil', diff --git a/shared/src/i18n/az/dashboard.ts b/shared/src/i18n/az/dashboard.ts index 4d676f3b2..cdfceb5a6 100644 --- a/shared/src/i18n/az/dashboard.ts +++ b/shared/src/i18n/az/dashboard.ts @@ -113,7 +113,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': '{photographer} tərəfindən çəkilmiş Unsplash şəklindən istifadə et', 'dashboard.titleRequired': 'Başlıq tələb olunur', 'dashboard.endDateError': 'Bitmə tarixi başlama tarixindən sonra olmalıdır', - 'dashboard.tripTooLong': 'Səyahət ən çox {days} gün davam edə bilər', + 'dashboard.tripTooLong': 'Səyahət ən çox {count} gün davam edə bilər', + 'dashboard.tripTooLong.one': 'Səyahət ən çox {count} gün davam edə bilər', 'dashboard.dateShiftTitle': 'Yeni başlama tarixi', 'dashboard.dateShiftIntro': 'Səyahətin başlama tarixini dəyişdiniz. Planlarınız yeni tarixlərə necə uyğunlaşdırılsın?', 'dashboard.dateShiftKeepBookings': 'Rezervasiyaların tarixlərini saxla', diff --git a/shared/src/i18n/az/dawarich.ts b/shared/src/i18n/az/dawarich.ts index 2574ce164..3cfd43c13 100644 --- a/shared/src/i18n/az/dawarich.ts +++ b/shared/src/i18n/az/dawarich.ts @@ -116,7 +116,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Qeyd əlavə et', 'dawarich.accept.confirm.bucket_list': 'Tamamlandı kimi işarələ', 'dawarich.accept.recorded': '{from}–{to} aralığında qeydə alınıb', - 'dawarich.accept.duration': '{minutes} dəq.', + 'dawarich.accept.duration': '{count} dəq.', + 'dawarich.accept.duration.one': '{count} dəq.', 'dawarich.accept.name': 'Ad', 'dawarich.accept.date': 'Tarix', 'dawarich.accept.from': 'Çatma', @@ -181,7 +182,10 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Yenidən yoxla', 'dawarich.bucket.metersAway': '{meters} m uzaqda', 'dawarich.bucket.kilometersAway': '{km} km uzaqda', - 'dawarich.bucket.rule': 'Arzu məkandan {meters} m məsafə daxilində və orada ən azı {minutes} dəqiqə qaldıqda tamamlanmış sayılır.', + 'dawarich.bucket.rule': + 'Arzu məkandan {meters} m məsafə daxilində və orada ən azı {count} dəqiqə qaldıqda tamamlanmış sayılır.', + 'dawarich.bucket.rule.one': + 'Arzu məkandan {meters} m məsafə daxilində və orada ən azı {count} dəqiqə qaldıqda tamamlanmış sayılır.', 'dawarich.journey.dayStays.one': 'Dawarich-dən 1 dayanma qeydi', 'dawarich.journey.dayStays.other': 'Dawarich-dən {count} dayanma qeydi', diff --git a/shared/src/i18n/az/roadtrip.ts b/shared/src/i18n/az/roadtrip.ts index 0236c55ff..b3a6357ec 100644 --- a/shared/src/i18n/az/roadtrip.ts +++ b/shared/src/i18n/az/roadtrip.ts @@ -54,7 +54,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Sabit vaxt yol və günlük fasilələrlə ziddiyyət təşkil edir. Əl ilə təyin edilmiş vaxtlar dəyişdirilmir; avtomatik planlaşdırma dayandırılıb.', 'roadtrip.window.tooLong': - 'Bu səyahət vaxtları {days} ədəddən çox əlavə gün tələb edəcək. Daha uzun günlük vaxt aralığı seçin.', + 'Bu səyahət vaxtları {count} gündən çox əlavə vaxt tələb edəcək. Daha uzun günlük vaxt aralığı seçin.', + 'roadtrip.window.tooLong.one': + 'Bu səyahət vaxtları {count} gündən çox əlavə vaxt tələb edəcək. Daha uzun günlük vaxt aralığı seçin.', 'roadtrip.mode.roadtrip': 'Avtomobil səyahəti', 'roadtrip.mode.days': 'Günlər', 'roadtrip.mode.label': 'Plan görünüşü', @@ -101,8 +103,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'dəq.', 'roadtrip.limit.off': 'deaktiv', 'roadtrip.limit.blockNote': 'Bir blok = {distance}', - 'roadtrip.limit.echoOver': '{days} gündən {over} günü limitinizi keçir.', - 'roadtrip.limit.echoNone': 'Bütün {days} gün limitləriniz daxilindədir.', + 'roadtrip.limit.echoOver': '{count} gündən {over} günü limitinizi keçir.', + 'roadtrip.limit.echoOver.one': '{count} gündən {over} günü limitinizi keçir.', + 'roadtrip.limit.echoNone': 'Bütün {count} gün limitləriniz daxilindədir.', + 'roadtrip.limit.echoNone.one': 'Yeganə gün limitləriniz daxilindədir.', 'roadtrip.limit.echoDry': 'Çatmaq mümkün olan dayanacağı olmayan {count} marşrut hissəsi.', 'roadtrip.limit.echoDry.one': 'Çatmaq mümkün olan dayanacağı olmayan {count} marşrut hissəsi.', 'roadtrip.limit.echoEmpty': 'Bu tənzimləmələrin təsirini görmək üçün avtomobil səyahəti açın.', @@ -268,8 +272,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Yoldan hesablanıb', 'roadtrip.stop.endIsLeave': 'Avtomobil səyahətində yol bu vaxtda davam edir.', 'roadtrip.via.hint': 'Marşrutu dəyişmək üçün sürükləyin, silmək üçün sağ klikləyin', - 'roadtrip.warn.late': 'Təyin etdiyiniz vaxtdan {minutes} dəq. sonra çatır', - 'roadtrip.warn.missedLeave': 'Təyin etdiyiniz çıxış vaxtından {minutes} dəq. sonra çatır', + 'roadtrip.warn.late': 'Təyin etdiyiniz vaxtdan {count} dəq. sonra çatır', + 'roadtrip.warn.late.one': 'Təyin etdiyiniz vaxtdan {count} dəq. sonra çatır', + 'roadtrip.warn.missedLeave': 'Təyin etdiyiniz çıxış vaxtından {count} dəq. sonra çatır', + 'roadtrip.warn.missedLeave.one': 'Təyin etdiyiniz çıxış vaxtından {count} dəq. sonra çatır', 'roadtrip.warn.overnight': 'Növbəti gün', 'roadtrip.spill.title': '{number}-ci gündən', 'roadtrip.spill.departs': 'saat {time}-da yola düşür', diff --git a/shared/src/i18n/az/settings.ts b/shared/src/i18n/az/settings.ts index 0e3757260..5df33bf1e 100644 --- a/shared/src/i18n/az/settings.ts +++ b/shared/src/i18n/az/settings.ts @@ -761,7 +761,9 @@ const settings: TranslationStrings = { 'settings.apiKeys.loadFailed': 'Açarlarınızı yükləmək mümkün olmadı. Yenidən sınamaq üçün səhifəni yeniləyin.', 'settings.apiKeys.limitReached': - 'Hesab üçün maksimum say olan {max} açarınız var. Yeni açar yaratmaq üçün artıq istifadə etmədiyiniz açarlardan birini silin.', + 'Hesab üçün maksimum say olan {count} açarınız var. Yeni açar yaratmaq üçün artıq istifadə etmədiyiniz açarlardan birini silin.', + 'settings.apiKeys.limitReached.one': + 'Hesab üçün maksimum say olan {count} açarınız var. Yeni açar yaratmaq üçün ondan artıq istifadə etmirsinizsə, onu silin.', 'settings.apiKeys.copyFailed': 'Kopyalamaq mümkün olmadı. Mətni seçərək əl ilə kopyalayın.', diff --git a/shared/src/i18n/az/storage.ts b/shared/src/i18n/az/storage.ts index a41b39069..be5df9fb5 100644 --- a/shared/src/i18n/az/storage.ts +++ b/shared/src/i18n/az/storage.ts @@ -136,8 +136,8 @@ const storage: TranslationStrings = { 'storage.usage.legacyNote': 'köhnə foto kitabxanası daxildir', // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Mövcud obyektlər yeni saxlama sisteminə köçürülsün?', - 'storage.migrate.promptLine': - '{category}: {from} sistemindən {to} sisteminə {objects} obyekt ({size})', + 'storage.migrate.promptLine': '{category}: {from} sistemindən {to} sisteminə {count} obyekt ({size})', + 'storage.migrate.promptLine.one': '{category}: {from} sistemindən {to} sisteminə {count} obyekt ({size})', 'storage.migrate.promptLineUnknown': '{category}: {from} sistemindən {to} sisteminə naməlum ölçü (istifadə hələ skan edilməyib)', 'storage.migrate.move': 'Mövcud obyektləri köçür', @@ -149,8 +149,8 @@ const storage: TranslationStrings = { 'storage.migrate.failed': 'Köçürmə uğursuz oldu: {error} — kateqoriya dəyişdirilmədi', 'storage.migrate.cancelled': 'Köçürmə ləğv edildi — heç nə dəyişdirilmədi', - 'storage.migrate.reclaimable': - '{objects} obyekt ({size}) {from} üzərində qalıb — əl ilə geri qazanın', + 'storage.migrate.reclaimable': '{count} obyekt ({size}) {from} üzərində qalıb, əl ilə geri qazanın', + 'storage.migrate.reclaimable.one': '{count} obyekt ({size}) {from} üzərində qalıb, əl ilə geri qazanın', 'storage.migrate.cancel': 'Köçürməni ləğv et', 'storage.migrate.promptCancel': 'Ləğv et', 'storage.migrate.queued': 'Növbədədir: {categories}', diff --git a/shared/src/i18n/br/collab.ts b/shared/src/i18n/br/collab.ts index 9b09325cf..fba440f1d 100644 --- a/shared/src/i18n/br/collab.ts +++ b/shared/src/i18n/br/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Opções', 'collab.polls.delete': 'Excluir', 'collab.polls.closedSection': 'Encerradas', - 'collab.chat.imageLimit': 'Podes anexar até {max} imagens por mensagem', + 'collab.chat.imageLimit': 'Podes anexar até {count} imagens por mensagem', + 'collab.chat.imageLimit.one': 'Podes anexar até {count} imagem por mensagem', 'collab.chat.uploading': 'A enviar {percent}%', 'collab.chat.attachImages': 'Anexar imagens', 'collab.chat.attachedImage': 'Imagem anexada', diff --git a/shared/src/i18n/br/dashboard.ts b/shared/src/i18n/br/dashboard.ts index ec063bc02..b078bdfa2 100644 --- a/shared/src/i18n/br/dashboard.ts +++ b/shared/src/i18n/br/dashboard.ts @@ -99,7 +99,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Usar foto do Unsplash por {photographer}', 'dashboard.titleRequired': 'O título é obrigatório', 'dashboard.endDateError': 'A data final deve ser depois da inicial', - 'dashboard.tripTooLong': 'Uma viagem pode ter no máximo {days} dias', + 'dashboard.tripTooLong': 'Uma viagem pode ter no máximo {count} dias', + 'dashboard.tripTooLong.one': 'Uma viagem pode ter no máximo {count} dia', 'dashboard.dateShiftTitle': 'Nova data de início', 'dashboard.dateShiftIntro': 'Você alterou o início desta viagem. Como seus planos devem acompanhar as novas datas?', 'dashboard.dateShiftKeepBookings': 'Manter reservas em suas datas', diff --git a/shared/src/i18n/br/dawarich.ts b/shared/src/i18n/br/dawarich.ts index f5fdfff59..9008427f5 100644 --- a/shared/src/i18n/br/dawarich.ts +++ b/shared/src/i18n/br/dawarich.ts @@ -118,7 +118,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Adicionar entrada', 'dawarich.accept.confirm.bucket_list': 'Riscar', 'dawarich.accept.recorded': 'Gravado de {from} até {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Nome', 'dawarich.accept.date': 'Data', 'dawarich.accept.from': 'Chegada', @@ -183,7 +184,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Verificar de novo', 'dawarich.bucket.metersAway': 'a {meters} m', 'dawarich.bucket.kilometersAway': 'a {km} km', - 'dawarich.bucket.rule': 'Um desejo conta como alcançado a até {meters} m e após {minutes} minutos no local.', + 'dawarich.bucket.rule': 'Um desejo conta como alcançado a até {meters} m e após {count} minutos no local.', + 'dawarich.bucket.rule.one': 'Um desejo conta como alcançado a até {meters} m e após {count} minuto no local.', 'dawarich.journey.dayStays.one': '{count} estadia do Dawarich', 'dawarich.journey.dayStays.other': '{count} estadias do Dawarich', diff --git a/shared/src/i18n/br/roadtrip.ts b/shared/src/i18n/br/roadtrip.ts index e5cdd5baf..c6a64206b 100644 --- a/shared/src/i18n/br/roadtrip.ts +++ b/shared/src/i18n/br/roadtrip.ts @@ -55,7 +55,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Todas as rotas de conexão são necessárias para calcular as pausas diárias.', 'roadtrip.window.conflict': 'Um horário fixo conflita com o trajeto e as pausas. Os horários manuais são mantidos; o planejamento automático está pausado.', - 'roadtrip.window.tooLong': 'Esses horários exigiriam mais de {days} dias extras. Escolha um período diário maior.', + 'roadtrip.window.tooLong': 'Esses horários exigiriam mais de {count} dias extras. Escolha um período diário maior.', + 'roadtrip.window.tooLong.one': 'Esses horários exigiriam mais de {count} dia extra. Escolha um período diário maior.', 'roadtrip.mode.roadtrip': 'Viagem de carro', 'roadtrip.mode.days': 'Dias', 'roadtrip.mode.label': 'Visão do plano', @@ -102,8 +103,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'desligado', 'roadtrip.limit.blockNote': 'Um bloco = {distance}', - 'roadtrip.limit.echoOver': '{over} de {days} dias acima do seu limite.', - 'roadtrip.limit.echoNone': 'Todos os {days} dias dentro dos limites.', + 'roadtrip.limit.echoOver': '{over} de {count} dias acima do seu limite.', + 'roadtrip.limit.echoOver.one': '{over} de {count} dia acima do seu limite.', + 'roadtrip.limit.echoNone': 'Todos os {count} dias dentro dos limites.', + 'roadtrip.limit.echoNone.one': '{count} dia dentro dos limites.', 'roadtrip.limit.echoDry': '{count} trechos sem parada alcançável.', 'roadtrip.limit.echoDry.one': '{count} trecho sem parada alcançável.', 'roadtrip.limit.echoEmpty': 'Abra uma viagem para ver o efeito.', @@ -273,8 +276,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Calculado a partir do trajeto', 'roadtrip.stop.endIsLeave': 'Na viagem de carro, a partida é neste horário.', 'roadtrip.via.hint': 'Arraste para remodelar a rota, clique com o botão direito para remover', - 'roadtrip.warn.late': 'Chega {minutes} min depois do horário definido', - 'roadtrip.warn.missedLeave': 'Chega {minutes} min depois do horário de partida definido', + 'roadtrip.warn.late': 'Chega {count} min depois do horário definido', + 'roadtrip.warn.late.one': 'Chega {count} min depois do horário definido', + 'roadtrip.warn.missedLeave': 'Chega {count} min depois do horário de partida definido', + 'roadtrip.warn.missedLeave.one': 'Chega {count} min depois do horário de partida definido', 'roadtrip.warn.overnight': 'Dia seguinte', 'roadtrip.spill.title': 'Do dia {number}', 'roadtrip.spill.departs': 'saída {time}', diff --git a/shared/src/i18n/br/settings.ts b/shared/src/i18n/br/settings.ts index e834f77ce..66884fac8 100644 --- a/shared/src/i18n/br/settings.ts +++ b/shared/src/i18n/br/settings.ts @@ -633,7 +633,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpoint', 'settings.apiKeys.neverUsed': 'nunca usada', 'settings.apiKeys.loadFailed': 'Não foi possível carregar suas chaves. Recarregue a página para tentar de novo.', - 'settings.apiKeys.limitReached': 'Você tem {max} chaves, o máximo por conta. Exclua uma que não usa mais para criar outra.', + 'settings.apiKeys.limitReached': + 'Você tem {count} chaves, o máximo por conta. Exclua uma que não usa mais para criar outra.', + 'settings.apiKeys.limitReached.one': + 'Você tem {count} chave, o máximo por conta. Exclua-a se não a usa mais para criar outra.', 'settings.apiKeys.copyFailed': 'Não foi possível copiar. Selecione o texto e copie manualmente.', 'settings.apiKeys.modal.createTitle': 'Criar chave de API', 'settings.apiKeys.modal.name': 'Nome', diff --git a/shared/src/i18n/br/storage.ts b/shared/src/i18n/br/storage.ts index f780d20f9..51cd4197e 100644 --- a/shared/src/i18n/br/storage.ts +++ b/shared/src/i18n/br/storage.ts @@ -121,7 +121,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Mover objetos existentes para o novo backend?', - 'storage.migrate.promptLine': '{category}: {objects} objetos ({size}) de {from} para {to}', + 'storage.migrate.promptLine': '{category}: {count} objetos ({size}) de {from} para {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objeto ({size}) de {from} para {to}', 'storage.migrate.promptLineUnknown': '{category}: tamanho desconhecido (uso ainda não calculado) de {from} para {to}', 'storage.migrate.move': 'Mover objetos existentes', 'storage.migrate.routeOnly': 'Apenas rotear novas gravações', @@ -130,7 +131,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} falharam — esses objetos não foram copiados para o novo backend', 'storage.migrate.failed': 'Falha ao mover: {error} — a categoria não foi alterada', 'storage.migrate.cancelled': 'Movimentação cancelada — nada foi alterado', - 'storage.migrate.reclaimable': '{objects} objetos ({size}) permanecem em {from} — recupere manualmente', + 'storage.migrate.reclaimable': '{count} objetos ({size}) permanecem em {from}, recupere manualmente', + 'storage.migrate.reclaimable.one': '{count} objeto ({size}) permanece em {from}, recupere manualmente', 'storage.migrate.cancel': 'Cancelar movimentação', 'storage.migrate.promptCancel': 'Cancelar', 'storage.migrate.queued': 'Na fila: {categories}', diff --git a/shared/src/i18n/ca/collab.ts b/shared/src/i18n/ca/collab.ts index b00d6e105..17a6bd949 100644 --- a/shared/src/i18n/ca/collab.ts +++ b/shared/src/i18n/ca/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.countdownHoursMinutes': '{h}h {m}m restants', 'collab.polls.countdownMinutes': '{m}m restants', 'collab.polls.clearDeadline': 'Esborra la data límit', - 'collab.chat.imageLimit': 'Pots adjuntar fins a {max} imatges per missatge', + 'collab.chat.imageLimit': 'Pots adjuntar fins a {count} imatges per missatge', + 'collab.chat.imageLimit.one': 'Només pots adjuntar {count} imatge per missatge', 'collab.chat.uploading': 'Pujant {percent} %', 'collab.chat.attachImages': 'Adjunta imatges', 'collab.chat.attachedImage': 'Imatge adjunta', diff --git a/shared/src/i18n/ca/dashboard.ts b/shared/src/i18n/ca/dashboard.ts index 947d72555..1183c26a3 100644 --- a/shared/src/i18n/ca/dashboard.ts +++ b/shared/src/i18n/ca/dashboard.ts @@ -87,7 +87,8 @@ const dashboard: TranslationStrings = { 'dashboard.coverRemoveError': 'Error en eliminar la imatge', 'dashboard.titleRequired': 'El títol és obligatori', 'dashboard.endDateError': "La data de fi ha de ser posterior a la d'inici", - 'dashboard.tripTooLong': 'Un viatge pot durar com a màxim {days} dies', + 'dashboard.tripTooLong': 'Un viatge pot durar com a màxim {count} dies', + 'dashboard.tripTooLong.one': 'Un viatge pot durar com a màxim {count} dia', 'dashboard.greeting.morning': 'Bon dia,', 'dashboard.greeting.afternoon': 'Bona tarda,', 'dashboard.greeting.evening': 'Bona nit,', diff --git a/shared/src/i18n/ca/dawarich.ts b/shared/src/i18n/ca/dawarich.ts index 4803f56dc..fd4d4b2a7 100644 --- a/shared/src/i18n/ca/dawarich.ts +++ b/shared/src/i18n/ca/dawarich.ts @@ -117,7 +117,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Afegeix l’entrada', 'dawarich.accept.confirm.bucket_list': 'Marca’l', 'dawarich.accept.recorded': 'Enregistrat des de {from} fins a {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Nom', 'dawarich.accept.date': 'Data', 'dawarich.accept.from': 'Arribada', @@ -182,7 +183,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Tornar a comprovar', 'dawarich.bucket.metersAway': 'a {meters} m', 'dawarich.bucket.kilometersAway': 'a {km} km', - 'dawarich.bucket.rule': 'Un desig es considera assolit a menys de {meters} m i després de {minutes} minuts al lloc.', + 'dawarich.bucket.rule': 'Un desig es considera assolit a menys de {meters} m i després de {count} minuts al lloc.', + 'dawarich.bucket.rule.one': 'Un desig es considera assolit a menys de {meters} m i després de {count} minut al lloc.', 'dawarich.journey.dayStays.other': '{count} estades de Dawarich', 'dawarich.journey.dayStays.one': '{count} estada de Dawarich', diff --git a/shared/src/i18n/ca/roadtrip.ts b/shared/src/i18n/ca/roadtrip.ts index 1707df9ad..91e01b637 100644 --- a/shared/src/i18n/ca/roadtrip.ts +++ b/shared/src/i18n/ca/roadtrip.ts @@ -56,7 +56,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Una hora fixa entra en conflicte amb el trajecte i les pauses. Es mantenen les hores manuals; la planificació automàtica està en pausa.', 'roadtrip.window.tooLong': - 'Aquests horaris requereixen més de {days} dies addicionals. Tria una franja diària més llarga.', + 'Aquests horaris requereixen més de {count} dies addicionals. Tria una franja diària més llarga.', + 'roadtrip.window.tooLong.one': + 'Aquests horaris requereixen més de {count} dia addicional. Tria una franja diària més llarga.', 'roadtrip.mode.roadtrip': 'Viatge per carretera', 'roadtrip.mode.days': 'Dies', 'roadtrip.mode.label': 'Vista del pla', @@ -103,8 +105,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'desactivat', 'roadtrip.limit.blockNote': 'Un bloc = {distance}', - 'roadtrip.limit.echoOver': '{over} de {days} dies superen el teu límit.', - 'roadtrip.limit.echoNone': 'Els {days} dies són dins dels límits.', + 'roadtrip.limit.echoOver': '{over} de {count} dies superen el teu límit.', + 'roadtrip.limit.echoOver.one': '{over} de {count} dia supera el teu límit.', + 'roadtrip.limit.echoNone': 'Els {count} dies són dins dels límits.', + 'roadtrip.limit.echoNone.one': 'L’únic dia és dins dels límits.', 'roadtrip.limit.echoDry': '{count} trams sense parada assolible.', 'roadtrip.limit.echoDry.one': '{count} tram sense parada assolible.', 'roadtrip.limit.echoEmpty': 'Obre una ruta per veure’n l’efecte.', @@ -276,8 +280,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Calculat a partir del trajecte', 'roadtrip.stop.endIsLeave': 'En el viatge per carretera, se surt a aquesta hora.', 'roadtrip.via.hint': 'Arrossega per remodelar la ruta, clic dret per eliminar', - 'roadtrip.warn.late': 'Arriba {minutes} min després de l’hora fixada', - 'roadtrip.warn.missedLeave': 'Arriba {minutes} min després de l’hora de sortida fixada', + 'roadtrip.warn.late': 'Arriba {count} min després de l’hora fixada', + 'roadtrip.warn.late.one': 'Arriba {count} min després de l’hora fixada', + 'roadtrip.warn.missedLeave': 'Arriba {count} min després de l’hora de sortida fixada', + 'roadtrip.warn.missedLeave.one': 'Arriba {count} min després de l’hora de sortida fixada', 'roadtrip.warn.overnight': 'Dia següent', 'roadtrip.spill.title': 'Del dia {number}', 'roadtrip.spill.departs': 'surt {time}', diff --git a/shared/src/i18n/ca/settings.ts b/shared/src/i18n/ca/settings.ts index 1f520c088..465b23b93 100644 --- a/shared/src/i18n/ca/settings.ts +++ b/shared/src/i18n/ca/settings.ts @@ -638,7 +638,9 @@ const settings: TranslationStrings = { 'settings.apiKeys.loadFailed': "No s'han pogut carregar les teves claus. Torna a carregar la pàgina per tornar-ho a provar.", 'settings.apiKeys.limitReached': - "Tens {max} claus, el màxim per compte. Esborra'n una que ja no facis servir per crear-ne una altra.", + "Tens {count} claus, el màxim per compte. Esborra'n una que ja no facis servir per crear-ne una altra.", + 'settings.apiKeys.limitReached.one': + 'Tens {count} clau, el màxim per compte. Esborra-la si ja no la fas servir per crear-ne una altra.', 'settings.apiKeys.copyFailed': "No s'ha pogut copiar. Selecciona el text i copia'l a mà.", 'settings.apiKeys.modal.createTitle': 'Crea una clau API', 'settings.apiKeys.modal.name': 'Nom', diff --git a/shared/src/i18n/ca/storage.ts b/shared/src/i18n/ca/storage.ts index 704277fd0..c119cd342 100644 --- a/shared/src/i18n/ca/storage.ts +++ b/shared/src/i18n/ca/storage.ts @@ -125,7 +125,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Vols moure els objectes existents al nou backend?', - 'storage.migrate.promptLine': '{category}: {objects} objectes ({size}) de {from} a {to}', + 'storage.migrate.promptLine': '{category}: {count} objectes ({size}) de {from} a {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objecte ({size}) de {from} a {to}', 'storage.migrate.promptLineUnknown': "{category}: mida desconeguda (encara no s'ha calculat l'ús) de {from} a {to}", 'storage.migrate.move': 'Mou els objectes existents', 'storage.migrate.routeOnly': 'Només enruta les escriptures noves', @@ -134,7 +135,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': "{failed} han fallat — aquests objectes no s'han copiat al nou backend", 'storage.migrate.failed': "Ha fallat el trasllat: {error} — la categoria no s'ha canviat", 'storage.migrate.cancelled': "Trasllat cancel·lat — no s'ha canviat res", - 'storage.migrate.reclaimable': "{objects} objectes ({size}) romanen a {from} — recupera'ls manualment", + 'storage.migrate.reclaimable': "{count} objectes ({size}) romanen a {from}, recupera'ls manualment", + 'storage.migrate.reclaimable.one': "{count} objecte ({size}) roman a {from}, recupera'l manualment", 'storage.migrate.cancel': 'Cancel·la el trasllat', 'storage.migrate.promptCancel': 'Cancel·la', 'storage.migrate.queued': 'En cua: {categories}', diff --git a/shared/src/i18n/cs/collab.ts b/shared/src/i18n/cs/collab.ts index 65f98ae71..502364e35 100644 --- a/shared/src/i18n/cs/collab.ts +++ b/shared/src/i18n/cs/collab.ts @@ -104,7 +104,9 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Možnosti', 'collab.polls.delete': 'Smazat', 'collab.polls.closedSection': 'Uzavřené', - 'collab.chat.imageLimit': 'K jedné zprávě můžete připojit nejvýše {max} obrázků', + 'collab.chat.imageLimit': 'K jedné zprávě můžete připojit nejvýše {count} obrázků', + 'collab.chat.imageLimit.one': 'K jedné zprávě můžete připojit jen {count} obrázek', + 'collab.chat.imageLimit.few': 'K jedné zprávě můžete připojit nejvýše {count} obrázky', 'collab.chat.uploading': 'Nahrávání {percent} %', 'collab.chat.attachImages': 'Připojit obrázky', 'collab.chat.attachedImage': 'Připojený obrázek', diff --git a/shared/src/i18n/cs/dashboard.ts b/shared/src/i18n/cs/dashboard.ts index 8b217341a..3a0c562dd 100644 --- a/shared/src/i18n/cs/dashboard.ts +++ b/shared/src/i18n/cs/dashboard.ts @@ -106,7 +106,9 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Použít fotku z Unsplash od {photographer}', 'dashboard.titleRequired': 'Název je povinný', 'dashboard.endDateError': 'Datum konce musí být po datu začátku', - 'dashboard.tripTooLong': 'Cesta může trvat nejvýše {days} dní', + 'dashboard.tripTooLong': 'Cesta může trvat nejvýše {count} dní', + 'dashboard.tripTooLong.one': 'Cesta může trvat nejvýše {count} den', + 'dashboard.tripTooLong.few': 'Cesta může trvat nejvýše {count} dny', 'dashboard.dateShiftTitle': 'Nové datum začátku', 'dashboard.dateShiftIntro': 'Změnili jste začátek této cesty. Jak se mají vaše plány přizpůsobit novým termínům?', 'dashboard.dateShiftKeepBookings': 'Ponechat rezervace v původních termínech', diff --git a/shared/src/i18n/cs/dawarich.ts b/shared/src/i18n/cs/dawarich.ts index f27a05a75..1e6a22e37 100644 --- a/shared/src/i18n/cs/dawarich.ts +++ b/shared/src/i18n/cs/dawarich.ts @@ -121,7 +121,9 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Přidat záznam', 'dawarich.accept.confirm.bucket_list': 'Odškrtnout', 'dawarich.accept.recorded': 'Zaznamenáno od {from} do {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', + 'dawarich.accept.duration.few': '{count} min', 'dawarich.accept.name': 'Název', 'dawarich.accept.date': 'Datum', 'dawarich.accept.from': 'Příchod', @@ -193,7 +195,9 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Zkontrolovat znovu', 'dawarich.bucket.metersAway': '{meters} m daleko', 'dawarich.bucket.kilometersAway': '{km} km daleko', - 'dawarich.bucket.rule': 'Přání se počítá jako splněné do {meters} m a po {minutes} minutách na místě.', + 'dawarich.bucket.rule': 'Přání se počítá jako splněné do {meters} m a po {count} minutách na místě.', + 'dawarich.bucket.rule.one': 'Přání se počítá jako splněné do {meters} m a po {count} minutě na místě.', + 'dawarich.bucket.rule.few': 'Přání se počítá jako splněné do {meters} m a po {count} minutách na místě.', 'dawarich.journey.dayStays.other': '{count} pobytů z Dawarichu', 'dawarich.journey.dayStays.one': '{count} pobyt z Dawarichu', 'dawarich.journey.dayStays.few': '{count} pobyty z Dawarichu', diff --git a/shared/src/i18n/cs/roadtrip.ts b/shared/src/i18n/cs/roadtrip.ts index 44f78b610..39c3afbf9 100644 --- a/shared/src/i18n/cs/roadtrip.ts +++ b/shared/src/i18n/cs/roadtrip.ts @@ -55,7 +55,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Denní přestávky lze spočítat až po získání všech navazujících tras.', 'roadtrip.window.conflict': 'Pevný čas je v rozporu s jízdou a přestávkami. Ruční časy zůstávají zachovány; automatické plánování je pozastaveno.', - 'roadtrip.window.tooLong': 'Tyto časy vyžadují více než {days} dalších dní. Zvolte delší denní interval.', + 'roadtrip.window.tooLong': 'Tyto časy vyžadují více než {count} dalších dní. Zvolte delší denní interval.', + 'roadtrip.window.tooLong.one': 'Tyto časy vyžadují více než {count} další den. Zvolte delší denní interval.', + 'roadtrip.window.tooLong.few': 'Tyto časy vyžadují více než {count} další dny. Zvolte delší denní interval.', 'roadtrip.mode.roadtrip': 'Cesta autem', 'roadtrip.mode.days': 'Dny', 'roadtrip.mode.label': 'Zobrazení plánu', @@ -103,8 +105,12 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'vypnuto', 'roadtrip.limit.blockNote': 'Jeden blok = {distance}', - 'roadtrip.limit.echoOver': '{over} z {days} dnů nad tvým limitem.', - 'roadtrip.limit.echoNone': 'Všech {days} dnů je v limitech.', + 'roadtrip.limit.echoOver': '{over} z {count} dnů nad tvým limitem.', + 'roadtrip.limit.echoOver.one': '{over} z {count} dne nad tvým limitem.', + 'roadtrip.limit.echoOver.few': '{over} z {count} dnů nad tvým limitem.', + 'roadtrip.limit.echoNone': 'Všech {count} dnů je v limitech.', + 'roadtrip.limit.echoNone.one': 'Jediný den je v limitech.', + 'roadtrip.limit.echoNone.few': 'Všechny {count} dny jsou v limitech.', 'roadtrip.limit.echoDry': '{count} úseků bez dosažitelné zastávky.', 'roadtrip.limit.echoDry.one': '{count} úsek bez dosažitelné zastávky.', 'roadtrip.limit.echoDry.few': '{count} úseky bez dosažitelné zastávky.', @@ -281,8 +287,12 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Vypočteno z doby jízdy', 'roadtrip.stop.endIsLeave': 'Na cestě autem se v tento čas odjíždí.', 'roadtrip.via.hint': 'Tažením změníte trasu, pravým tlačítkem odeberete', - 'roadtrip.warn.late': 'Příjezd {minutes} min po nastaveném čase', - 'roadtrip.warn.missedLeave': 'Příjezd {minutes} min po nastaveném čase odjezdu', + 'roadtrip.warn.late': 'Příjezd {count} min po nastaveném čase', + 'roadtrip.warn.late.one': 'Příjezd {count} min po nastaveném čase', + 'roadtrip.warn.late.few': 'Příjezd {count} min po nastaveném čase', + 'roadtrip.warn.missedLeave': 'Příjezd {count} min po nastaveném čase odjezdu', + 'roadtrip.warn.missedLeave.one': 'Příjezd {count} min po nastaveném čase odjezdu', + 'roadtrip.warn.missedLeave.few': 'Příjezd {count} min po nastaveném čase odjezdu', 'roadtrip.warn.overnight': 'Následující den', 'roadtrip.spill.title': 'Ze dne {number}', 'roadtrip.spill.departs': 'odjezd {time}', diff --git a/shared/src/i18n/cs/settings.ts b/shared/src/i18n/cs/settings.ts index 21c88c341..a7707ac5c 100644 --- a/shared/src/i18n/cs/settings.ts +++ b/shared/src/i18n/cs/settings.ts @@ -628,7 +628,12 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpoint', 'settings.apiKeys.neverUsed': 'nikdy nepoužit', 'settings.apiKeys.loadFailed': 'Klíče se nepodařilo načíst. Obnov stránku a zkus to znovu.', - 'settings.apiKeys.limitReached': 'Máš {max} klíčů, víc jeden účet mít nemůže. Smaž klíč, který už nepotřebuješ, a vytvoř nový.', + 'settings.apiKeys.limitReached': + 'Máš {count} klíčů, víc jeden účet mít nemůže. Smaž klíč, který už nepotřebuješ, a vytvoř nový.', + 'settings.apiKeys.limitReached.one': + 'Máš {count} klíč, víc jeden účet mít nemůže. Smaž ho, pokud ho už nepotřebuješ, a vytvoř nový.', + 'settings.apiKeys.limitReached.few': + 'Máš {count} klíče, víc jeden účet mít nemůže. Smaž klíč, který už nepotřebuješ, a vytvoř nový.', 'settings.apiKeys.copyFailed': 'Kopírování se nepodařilo. Označ text a zkopíruj ho ručně.', 'settings.apiKeys.modal.createTitle': 'Vytvořit klíč API', 'settings.apiKeys.modal.name': 'Název', diff --git a/shared/src/i18n/cs/storage.ts b/shared/src/i18n/cs/storage.ts index 76321f601..c3375f545 100644 --- a/shared/src/i18n/cs/storage.ts +++ b/shared/src/i18n/cs/storage.ts @@ -121,7 +121,9 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Přesunout stávající objekty do nového backendu?', - 'storage.migrate.promptLine': '{category}: {objects} objektů ({size}) z {from} do {to}', + 'storage.migrate.promptLine': '{category}: {count} objektů ({size}) z {from} do {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objekt ({size}) z {from} do {to}', + 'storage.migrate.promptLine.few': '{category}: {count} objekty ({size}) z {from} do {to}', 'storage.migrate.promptLineUnknown': '{category}: neznámá velikost (využití ještě nebylo vypočteno) z {from} do {to}', 'storage.migrate.move': 'Přesunout stávající objekty', 'storage.migrate.routeOnly': 'Pouze směrovat nové zápisy', @@ -130,7 +132,9 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} se nezdařilo — tyto objekty nebyly zkopírovány do nového backendu', 'storage.migrate.failed': 'Přesun selhal: {error} — kategorie nebyla přepnuta', 'storage.migrate.cancelled': 'Přesun zrušen — nic nebylo přepnuto', - 'storage.migrate.reclaimable': '{objects} objektů ({size}) zůstává na {from} — uvolněte ručně', + 'storage.migrate.reclaimable': '{count} objektů ({size}) zůstává na {from}, uvolněte ručně', + 'storage.migrate.reclaimable.one': '{count} objekt ({size}) zůstává na {from}, uvolněte ručně', + 'storage.migrate.reclaimable.few': '{count} objekty ({size}) zůstávají na {from}, uvolněte ručně', 'storage.migrate.cancel': 'Zrušit přesun', 'storage.migrate.promptCancel': 'Zrušit', 'storage.migrate.queued': 'Ve frontě: {categories}', diff --git a/shared/src/i18n/de/collab.ts b/shared/src/i18n/de/collab.ts index 1e6d68f91..a3aa98a79 100644 --- a/shared/src/i18n/de/collab.ts +++ b/shared/src/i18n/de/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Optionen', 'collab.polls.delete': 'Löschen', 'collab.polls.closedSection': 'Geschlossen', - 'collab.chat.imageLimit': 'Pro Nachricht kannst du bis zu {max} Bilder anhängen', + 'collab.chat.imageLimit': 'Pro Nachricht kannst du bis zu {count} Bilder anhängen', + 'collab.chat.imageLimit.one': 'Pro Nachricht kannst du nur {count} Bild anhängen', 'collab.chat.uploading': 'Wird hochgeladen, {percent} %', 'collab.chat.attachImages': 'Bilder anhängen', 'collab.chat.attachedImage': 'Angehängtes Bild', diff --git a/shared/src/i18n/de/dashboard.ts b/shared/src/i18n/de/dashboard.ts index 60b650b86..89016e0b8 100644 --- a/shared/src/i18n/de/dashboard.ts +++ b/shared/src/i18n/de/dashboard.ts @@ -100,7 +100,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Unsplash-Foto von {photographer} verwenden', 'dashboard.titleRequired': 'Titel ist erforderlich', 'dashboard.endDateError': 'Enddatum muss nach dem Startdatum liegen', - 'dashboard.tripTooLong': 'Eine Reise kann höchstens {days} Tage umfassen', + 'dashboard.tripTooLong': 'Eine Reise kann höchstens {count} Tage umfassen', + 'dashboard.tripTooLong.one': 'Eine Reise kann höchstens {count} Tag umfassen', 'dashboard.dateShiftTitle': 'Neues Startdatum', 'dashboard.dateShiftIntro': 'Du hast den Beginn dieser Reise geändert. Wie sollen deine Pläne den neuen Daten folgen?', diff --git a/shared/src/i18n/de/dawarich.ts b/shared/src/i18n/de/dawarich.ts index ab569805e..fdb94a744 100644 --- a/shared/src/i18n/de/dawarich.ts +++ b/shared/src/i18n/de/dawarich.ts @@ -120,7 +120,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Eintrag hinzufügen', 'dawarich.accept.confirm.bucket_list': 'Abhaken', 'dawarich.accept.recorded': 'Aufgezeichnet {from} bis {to}', - 'dawarich.accept.duration': '{minutes} Min.', + 'dawarich.accept.duration': '{count} Min.', + 'dawarich.accept.duration.one': '{count} Min.', 'dawarich.accept.name': 'Name', 'dawarich.accept.date': 'Datum', 'dawarich.accept.from': 'Angekommen', @@ -186,7 +187,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Erneut prüfen', 'dawarich.bucket.metersAway': '{meters} m entfernt', 'dawarich.bucket.kilometersAway': '{km} km entfernt', - 'dawarich.bucket.rule': 'Ein Wunsch gilt ab {meters} m Nähe und {minutes} Minuten vor Ort als erreicht.', + 'dawarich.bucket.rule': 'Ein Wunsch gilt ab {meters} m Nähe und {count} Minuten vor Ort als erreicht.', + 'dawarich.bucket.rule.one': 'Ein Wunsch gilt ab {meters} m Nähe und {count} Minute vor Ort als erreicht.', 'dawarich.journey.dayStays.other': '{count} Aufenthalte aus Dawarich', 'dawarich.journey.dayStays.one': '{count} Aufenthalt aus Dawarich', diff --git a/shared/src/i18n/de/roadtrip.ts b/shared/src/i18n/de/roadtrip.ts index e64996c9f..5fbd8269c 100644 --- a/shared/src/i18n/de/roadtrip.ts +++ b/shared/src/i18n/de/roadtrip.ts @@ -54,7 +54,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Eine feste Uhrzeit passt nicht zu Fahrzeit und Tagespausen. Manuelle Zeiten bleiben unverändert; die automatische Aufteilung pausiert.', 'roadtrip.window.tooLong': - 'Diese Reisezeiten erfordern mehr als {days} zusätzliche Tage. Wähle ein längeres Tagesfenster.', + 'Diese Reisezeiten erfordern mehr als {count} zusätzliche Tage. Wähle ein längeres Tagesfenster.', + 'roadtrip.window.tooLong.one': + 'Diese Reisezeiten erfordern mehr als {count} zusätzlichen Tag. Wähle ein längeres Tagesfenster.', 'roadtrip.mode.roadtrip': 'Roadtrip', 'roadtrip.mode.days': 'Tage', 'roadtrip.mode.label': 'Plan-Ansicht', @@ -101,8 +103,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'Min', 'roadtrip.limit.off': 'aus', 'roadtrip.limit.blockNote': 'Ein Block = {distance}', - 'roadtrip.limit.echoOver': '{over} von {days} Tagen über deinem Limit.', - 'roadtrip.limit.echoNone': 'Alle {days} Tage innerhalb deiner Grenzen.', + 'roadtrip.limit.echoOver': '{over} von {count} Tagen über deinem Limit.', + 'roadtrip.limit.echoOver.one': '{over} von {count} Tag über deinem Limit.', + 'roadtrip.limit.echoNone': 'Alle {count} Tage innerhalb deiner Grenzen.', + 'roadtrip.limit.echoNone.one': 'Der eine Tag liegt innerhalb deiner Grenzen.', 'roadtrip.limit.echoDry': '{count} Abschnitte ohne erreichbaren Stopp.', 'roadtrip.limit.echoDry.one': '{count} Abschnitt ohne erreichbaren Stopp.', 'roadtrip.limit.echoEmpty': 'Öffne einen Roadtrip, um zu sehen, was das bewirkt.', @@ -270,8 +274,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Aus der Fahrzeit gerechnet', 'roadtrip.stop.endIsLeave': 'Im Roadtrip geht die Fahrt zu dieser Zeit weiter.', 'roadtrip.via.hint': 'Ziehen formt die Route um, Rechtsklick entfernt', - 'roadtrip.warn.late': 'Ankunft {minutes} min nach der gesetzten Zeit', - 'roadtrip.warn.missedLeave': 'Ankunft {minutes} min nach der gesetzten Abfahrtszeit', + 'roadtrip.warn.late': 'Ankunft {count} min nach der gesetzten Zeit', + 'roadtrip.warn.late.one': 'Ankunft {count} min nach der gesetzten Zeit', + 'roadtrip.warn.missedLeave': 'Ankunft {count} min nach der gesetzten Abfahrtszeit', + 'roadtrip.warn.missedLeave.one': 'Ankunft {count} min nach der gesetzten Abfahrtszeit', 'roadtrip.warn.overnight': 'Folgetag', 'roadtrip.spill.title': 'Von Tag {number}', 'roadtrip.spill.departs': 'ab {time}', diff --git a/shared/src/i18n/de/settings.ts b/shared/src/i18n/de/settings.ts index 638e6e246..7ffa7e73d 100644 --- a/shared/src/i18n/de/settings.ts +++ b/shared/src/i18n/de/settings.ts @@ -636,7 +636,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpunkt', 'settings.apiKeys.neverUsed': 'nie genutzt', 'settings.apiKeys.loadFailed': 'Deine Schlüssel konnten nicht geladen werden. Lade die Seite neu, um es noch einmal zu versuchen.', - 'settings.apiKeys.limitReached': 'Du hast {max} Schlüssel, mehr geht pro Konto nicht. Lösche einen, den du nicht mehr brauchst, um einen neuen zu erstellen.', + 'settings.apiKeys.limitReached': + 'Du hast {count} Schlüssel, mehr geht pro Konto nicht. Lösche einen, den du nicht mehr brauchst, um einen neuen zu erstellen.', + 'settings.apiKeys.limitReached.one': + 'Du hast {count} Schlüssel, mehr geht pro Konto nicht. Lösche ihn, wenn du ihn nicht mehr brauchst, um einen neuen zu erstellen.', 'settings.apiKeys.copyFailed': 'Kopieren hat nicht geklappt. Markiere den Text und kopiere ihn von Hand.', 'settings.apiKeys.modal.createTitle': 'API-Schlüssel erstellen', 'settings.apiKeys.modal.name': 'Name', diff --git a/shared/src/i18n/de/storage.ts b/shared/src/i18n/de/storage.ts index 6607156c3..41abbde27 100644 --- a/shared/src/i18n/de/storage.ts +++ b/shared/src/i18n/de/storage.ts @@ -124,7 +124,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Vorhandene Objekte auf das neue Backend verschieben?', - 'storage.migrate.promptLine': '{category}: {objects} Objekte ({size}) von {from} nach {to}', + 'storage.migrate.promptLine': '{category}: {count} Objekte ({size}) von {from} nach {to}', + 'storage.migrate.promptLine.one': '{category}: {count} Objekt ({size}) von {from} nach {to}', 'storage.migrate.promptLineUnknown': '{category}: unbekannte Größe (Nutzung noch nicht gescannt) von {from} nach {to}', 'storage.migrate.move': 'Vorhandene Objekte verschieben', @@ -134,7 +135,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} fehlgeschlagen — diese Objekte wurden nicht auf das neue Backend kopiert', 'storage.migrate.failed': 'Verschieben fehlgeschlagen: {error} — die Kategorie wurde nicht umgestellt', 'storage.migrate.cancelled': 'Verschieben abgebrochen — nichts wurde umgestellt', - 'storage.migrate.reclaimable': '{objects} Objekte ({size}) verbleiben auf {from} — manuell freigeben', + 'storage.migrate.reclaimable': '{count} Objekte ({size}) verbleiben auf {from}, manuell freigeben', + 'storage.migrate.reclaimable.one': '{count} Objekt ({size}) verbleibt auf {from}, manuell freigeben', 'storage.migrate.cancel': 'Verschieben abbrechen', 'storage.migrate.promptCancel': 'Abbrechen', 'storage.migrate.queued': 'In Warteschlange: {categories}', diff --git a/shared/src/i18n/en/collab.ts b/shared/src/i18n/en/collab.ts index fb6baacab..15fe2a01d 100644 --- a/shared/src/i18n/en/collab.ts +++ b/shared/src/i18n/en/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Options', 'collab.polls.delete': 'Delete', 'collab.polls.closedSection': 'Closed', - 'collab.chat.imageLimit': 'You can attach up to {max} images per message', + 'collab.chat.imageLimit': 'You can attach up to {count} images per message', + 'collab.chat.imageLimit.one': 'You can attach only {count} image per message', 'collab.chat.uploading': 'Uploading {percent}%', 'collab.chat.attachImages': 'Attach images', 'collab.chat.attachedImage': 'Attached image', diff --git a/shared/src/i18n/en/dashboard.ts b/shared/src/i18n/en/dashboard.ts index 0e44e6242..cdfbdd67a 100644 --- a/shared/src/i18n/en/dashboard.ts +++ b/shared/src/i18n/en/dashboard.ts @@ -113,7 +113,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Use Unsplash photo by {photographer}', 'dashboard.titleRequired': 'Title is required', 'dashboard.endDateError': 'End date must be after start date', - 'dashboard.tripTooLong': 'A trip can span at most {days} days', + 'dashboard.tripTooLong': 'A trip can span at most {count} days', + 'dashboard.tripTooLong.one': 'A trip can span at most {count} day', 'dashboard.dateShiftTitle': 'New start date', 'dashboard.dateShiftIntro': 'You changed when this trip starts. How should your plans follow the new dates?', 'dashboard.dateShiftKeepBookings': 'Keep bookings on their dates', diff --git a/shared/src/i18n/en/dawarich.ts b/shared/src/i18n/en/dawarich.ts index 1e0411fdc..5b664feb1 100644 --- a/shared/src/i18n/en/dawarich.ts +++ b/shared/src/i18n/en/dawarich.ts @@ -116,7 +116,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Add entry', 'dawarich.accept.confirm.bucket_list': 'Tick it off', 'dawarich.accept.recorded': 'Recorded {from} until {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Name', 'dawarich.accept.date': 'Date', 'dawarich.accept.from': 'Arrived', @@ -181,7 +182,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Check again', 'dawarich.bucket.metersAway': '{meters} m away', 'dawarich.bucket.kilometersAway': '{km} km away', - 'dawarich.bucket.rule': 'A wish counts as reached within {meters} m and after {minutes} minutes on the spot.', + 'dawarich.bucket.rule': 'A wish counts as reached within {meters} m and after {count} minutes on the spot.', + 'dawarich.bucket.rule.one': 'A wish counts as reached within {meters} m and after {count} minute on the spot.', 'dawarich.journey.dayStays.other': '{count} stays from Dawarich', 'dawarich.journey.dayStays.one': '{count} stay from Dawarich', diff --git a/shared/src/i18n/en/roadtrip.ts b/shared/src/i18n/en/roadtrip.ts index 58c3a831a..cc81da421 100644 --- a/shared/src/i18n/en/roadtrip.ts +++ b/shared/src/i18n/en/roadtrip.ts @@ -54,7 +54,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'A fixed time conflicts with the drive and daily breaks. Manual times are unchanged; automatic scheduling is paused.', 'roadtrip.window.tooLong': - 'These travel times would require more than {days} extra days. Choose a longer daily window.', + 'These travel times would require more than {count} extra days. Choose a longer daily window.', + 'roadtrip.window.tooLong.one': + 'These travel times would require more than {count} extra day. Choose a longer daily window.', 'roadtrip.mode.roadtrip': 'Road trip', 'roadtrip.mode.days': 'Days', 'roadtrip.mode.label': 'Plan view', @@ -101,8 +103,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'off', 'roadtrip.limit.blockNote': 'One block = {distance}', - 'roadtrip.limit.echoOver': '{over} of {days} days over your limit.', - 'roadtrip.limit.echoNone': 'All {days} days within your limits.', + 'roadtrip.limit.echoOver': '{over} of {count} days over your limit.', + 'roadtrip.limit.echoOver.one': '{over} of {count} day over your limit.', + 'roadtrip.limit.echoNone': 'All {count} days within your limits.', + 'roadtrip.limit.echoNone.one': 'The one day is within your limits.', 'roadtrip.limit.echoDry': '{count} stretches with no reachable stop.', 'roadtrip.limit.echoDry.one': '{count} stretch with no reachable stop.', 'roadtrip.limit.echoEmpty': 'Open a road trip to see what these do to it.', @@ -266,8 +270,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Calculated from the drive', 'roadtrip.stop.endIsLeave': 'On the road trip, the drive leaves at this time.', 'roadtrip.via.hint': 'Drag to reshape the route, right-click to remove', - 'roadtrip.warn.late': 'Arrives {minutes} min after the time you set', - 'roadtrip.warn.missedLeave': 'Arrives {minutes} min after the time you set to leave', + 'roadtrip.warn.late': 'Arrives {count} min after the time you set', + 'roadtrip.warn.late.one': 'Arrives {count} min after the time you set', + 'roadtrip.warn.missedLeave': 'Arrives {count} min after the time you set to leave', + 'roadtrip.warn.missedLeave.one': 'Arrives {count} min after the time you set to leave', 'roadtrip.warn.overnight': 'Next day', 'roadtrip.spill.title': 'From day {number}', 'roadtrip.spill.departs': 'leaves {time}', diff --git a/shared/src/i18n/en/settings.ts b/shared/src/i18n/en/settings.ts index 1ee65e059..650c12abc 100644 --- a/shared/src/i18n/en/settings.ts +++ b/shared/src/i18n/en/settings.ts @@ -622,7 +622,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpoint', 'settings.apiKeys.neverUsed': 'never used', 'settings.apiKeys.loadFailed': 'Could not load your keys. Reload the page to try again.', - 'settings.apiKeys.limitReached': 'You have {max} keys, the most an account can hold. Delete one you no longer use to create another.', + 'settings.apiKeys.limitReached': + 'You have {count} keys, the most an account can hold. Delete one you no longer use to create another.', + 'settings.apiKeys.limitReached.one': + 'You have {count} key, the most an account can hold. Delete it if you no longer use it to create another.', 'settings.apiKeys.copyFailed': 'Could not copy. Select the text and copy it by hand.', 'settings.apiKeys.modal.createTitle': 'Create API key', 'settings.apiKeys.modal.name': 'Name', diff --git a/shared/src/i18n/en/storage.ts b/shared/src/i18n/en/storage.ts index 97bcb30ef..4c98fac5e 100644 --- a/shared/src/i18n/en/storage.ts +++ b/shared/src/i18n/en/storage.ts @@ -136,7 +136,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Move existing objects to the new backend?', - 'storage.migrate.promptLine': '{category}: {objects} objects ({size}) from {from} to {to}', + 'storage.migrate.promptLine': '{category}: {count} objects ({size}) from {from} to {to}', + 'storage.migrate.promptLine.one': '{category}: {count} object ({size}) from {from} to {to}', 'storage.migrate.promptLineUnknown': '{category}: unknown size (no usage scan yet) from {from} to {to}', 'storage.migrate.move': 'Move existing objects', 'storage.migrate.routeOnly': 'Just route new writes', @@ -145,7 +146,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} failed — those objects were not copied to the new backend', 'storage.migrate.failed': 'Move failed: {error} — the category was not switched', 'storage.migrate.cancelled': 'Move cancelled — nothing was switched', - 'storage.migrate.reclaimable': '{objects} objects ({size}) remain on {from} — reclaim manually', + 'storage.migrate.reclaimable': '{count} objects ({size}) remain on {from}, reclaim manually', + 'storage.migrate.reclaimable.one': '{count} object ({size}) remains on {from}, reclaim manually', 'storage.migrate.cancel': 'Cancel move', 'storage.migrate.promptCancel': 'Cancel', 'storage.migrate.queued': 'Queued: {categories}', diff --git a/shared/src/i18n/es/collab.ts b/shared/src/i18n/es/collab.ts index 704dc3269..65ef69bed 100644 --- a/shared/src/i18n/es/collab.ts +++ b/shared/src/i18n/es/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Opciones', 'collab.polls.delete': 'Eliminar', 'collab.polls.closedSection': 'Cerradas', - 'collab.chat.imageLimit': 'Puedes adjuntar hasta {max} imágenes por mensaje', + 'collab.chat.imageLimit': 'Puedes adjuntar hasta {count} imágenes por mensaje', + 'collab.chat.imageLimit.one': 'Solo puedes adjuntar {count} imagen por mensaje', 'collab.chat.uploading': 'Subiendo {percent} %', 'collab.chat.attachImages': 'Adjuntar imágenes', 'collab.chat.attachedImage': 'Imagen adjunta', diff --git a/shared/src/i18n/es/dashboard.ts b/shared/src/i18n/es/dashboard.ts index b5365009a..00ba21568 100644 --- a/shared/src/i18n/es/dashboard.ts +++ b/shared/src/i18n/es/dashboard.ts @@ -99,7 +99,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Usar foto de Unsplash de {photographer}', 'dashboard.titleRequired': 'El título es obligatorio', 'dashboard.endDateError': 'La fecha de fin debe ser posterior a la de inicio', - 'dashboard.tripTooLong': 'Un viaje puede durar como máximo {days} días', + 'dashboard.tripTooLong': 'Un viaje puede durar como máximo {count} días', + 'dashboard.tripTooLong.one': 'Un viaje puede durar como máximo {count} día', 'dashboard.dateShiftTitle': 'Nueva fecha de inicio', 'dashboard.dateShiftIntro': 'Has cambiado cuándo empieza este viaje. ¿Cómo deben seguir tus planes las nuevas fechas?', diff --git a/shared/src/i18n/es/dawarich.ts b/shared/src/i18n/es/dawarich.ts index e0081434d..4157092ac 100644 --- a/shared/src/i18n/es/dawarich.ts +++ b/shared/src/i18n/es/dawarich.ts @@ -118,7 +118,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Añadir entrada', 'dawarich.accept.confirm.bucket_list': 'Tacharlo', 'dawarich.accept.recorded': 'Grabado de {from} a {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Nombre', 'dawarich.accept.date': 'Fecha', 'dawarich.accept.from': 'Llegada', @@ -183,7 +184,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Volver a comprobar', 'dawarich.bucket.metersAway': 'a {meters} m', 'dawarich.bucket.kilometersAway': 'a {km} km', - 'dawarich.bucket.rule': 'Un deseo cuenta como cumplido a menos de {meters} m y tras {minutes} minutos en el lugar.', + 'dawarich.bucket.rule': 'Un deseo cuenta como cumplido a menos de {meters} m y tras {count} minutos en el lugar.', + 'dawarich.bucket.rule.one': 'Un deseo cuenta como cumplido a menos de {meters} m y tras {count} minuto en el lugar.', 'dawarich.journey.dayStays.other': '{count} estancias de Dawarich', 'dawarich.journey.dayStays.one': '{count} estancia de Dawarich', diff --git a/shared/src/i18n/es/roadtrip.ts b/shared/src/i18n/es/roadtrip.ts index 90cbb693e..cc2fcbb02 100644 --- a/shared/src/i18n/es/roadtrip.ts +++ b/shared/src/i18n/es/roadtrip.ts @@ -56,7 +56,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Las pausas diarias necesitan todas las rutas de conexión para calcularse.', 'roadtrip.window.conflict': 'Una hora fija entra en conflicto con el trayecto y las pausas. Las horas manuales se conservan; la planificación automática está en pausa.', - 'roadtrip.window.tooLong': 'Estos horarios requieren más de {days} días adicionales. Amplía el horario diario.', + 'roadtrip.window.tooLong': 'Estos horarios requieren más de {count} días adicionales. Amplía el horario diario.', + 'roadtrip.window.tooLong.one': 'Estos horarios requieren más de {count} día adicional. Amplía el horario diario.', 'roadtrip.mode.roadtrip': 'Viaje por carretera', 'roadtrip.mode.days': 'Días', 'roadtrip.mode.label': 'Vista del plan', @@ -103,8 +104,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'desactivado', 'roadtrip.limit.blockNote': 'Un bloque = {distance}', - 'roadtrip.limit.echoOver': '{over} de {days} días superan tu límite.', - 'roadtrip.limit.echoNone': 'Los {days} días están dentro de tus límites.', + 'roadtrip.limit.echoOver': '{over} de {count} días superan tu límite.', + 'roadtrip.limit.echoOver.one': '{over} de {count} día supera tu límite.', + 'roadtrip.limit.echoNone': 'Los {count} días están dentro de tus límites.', + 'roadtrip.limit.echoNone.one': 'El único día está dentro de tus límites.', 'roadtrip.limit.echoDry': '{count} tramos sin parada alcanzable.', 'roadtrip.limit.echoDry.one': '{count} tramo sin parada alcanzable.', 'roadtrip.limit.echoEmpty': 'Abre una ruta para ver el efecto.', @@ -274,8 +277,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Calculado a partir del trayecto', 'roadtrip.stop.endIsLeave': 'En el viaje por carretera, se sale a esta hora.', 'roadtrip.via.hint': 'Arrastra para remodelar la ruta, clic derecho para quitar', - 'roadtrip.warn.late': 'Llega {minutes} min después de la hora fijada', - 'roadtrip.warn.missedLeave': 'Llega {minutes} min después de la hora de salida fijada', + 'roadtrip.warn.late': 'Llega {count} min después de la hora fijada', + 'roadtrip.warn.late.one': 'Llega {count} min después de la hora fijada', + 'roadtrip.warn.missedLeave': 'Llega {count} min después de la hora de salida fijada', + 'roadtrip.warn.missedLeave.one': 'Llega {count} min después de la hora de salida fijada', 'roadtrip.warn.overnight': 'Día siguiente', 'roadtrip.spill.title': 'Del día {number}', 'roadtrip.spill.departs': 'sale {time}', diff --git a/shared/src/i18n/es/settings.ts b/shared/src/i18n/es/settings.ts index 3bcbbe06a..fad331426 100644 --- a/shared/src/i18n/es/settings.ts +++ b/shared/src/i18n/es/settings.ts @@ -633,7 +633,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpoint', 'settings.apiKeys.neverUsed': 'nunca usada', 'settings.apiKeys.loadFailed': 'No se pudieron cargar tus claves. Recarga la página para volver a intentarlo.', - 'settings.apiKeys.limitReached': 'Tienes {max} claves, el máximo por cuenta. Elimina una que ya no uses para crear otra.', + 'settings.apiKeys.limitReached': + 'Tienes {count} claves, el máximo por cuenta. Elimina una que ya no uses para crear otra.', + 'settings.apiKeys.limitReached.one': + 'Tienes {count} clave, el máximo por cuenta. Elimínala si ya no la usas para crear otra.', 'settings.apiKeys.copyFailed': 'No se pudo copiar. Selecciona el texto y cópialo a mano.', 'settings.apiKeys.modal.createTitle': 'Crear clave API', 'settings.apiKeys.modal.name': 'Nombre', diff --git a/shared/src/i18n/es/storage.ts b/shared/src/i18n/es/storage.ts index be28e822c..e6a6d8019 100644 --- a/shared/src/i18n/es/storage.ts +++ b/shared/src/i18n/es/storage.ts @@ -126,7 +126,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': '¿Mover los objetos existentes al nuevo backend?', - 'storage.migrate.promptLine': '{category}: {objects} objetos ({size}) de {from} a {to}', + 'storage.migrate.promptLine': '{category}: {count} objetos ({size}) de {from} a {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objeto ({size}) de {from} a {to}', 'storage.migrate.promptLineUnknown': '{category}: tamaño desconocido (uso aún no calculado) de {from} a {to}', 'storage.migrate.move': 'Mover objetos existentes', 'storage.migrate.routeOnly': 'Solo enrutar las escrituras nuevas', @@ -135,7 +136,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} fallaron — esos objetos no se copiaron al nuevo backend', 'storage.migrate.failed': 'Error al mover: {error} — la categoría no se cambió', 'storage.migrate.cancelled': 'Movimiento cancelado — no se cambió nada', - 'storage.migrate.reclaimable': '{objects} objetos ({size}) permanecen en {from} — recupéralos manualmente', + 'storage.migrate.reclaimable': '{count} objetos ({size}) permanecen en {from}, recupéralos manualmente', + 'storage.migrate.reclaimable.one': '{count} objeto ({size}) permanece en {from}, recupéralo manualmente', 'storage.migrate.cancel': 'Cancelar movimiento', 'storage.migrate.promptCancel': 'Cancelar', 'storage.migrate.queued': 'En cola: {categories}', diff --git a/shared/src/i18n/et/collab.ts b/shared/src/i18n/et/collab.ts index df6eae0ff..26485420d 100644 --- a/shared/src/i18n/et/collab.ts +++ b/shared/src/i18n/et/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Valikud', 'collab.polls.delete': 'Kustuta', 'collab.polls.closedSection': 'Suletud', - 'collab.chat.imageLimit': 'Ühele sõnumile saad lisada kuni {max} pilti', + 'collab.chat.imageLimit': 'Ühele sõnumile saad lisada kuni {count} pilti', + 'collab.chat.imageLimit.one': 'Ühele sõnumile saad lisada vaid {count} pildi', 'collab.chat.uploading': 'Üleslaadimine {percent}%', 'collab.chat.attachImages': 'Lisa pildid', 'collab.chat.attachedImage': 'Lisatud pilt', diff --git a/shared/src/i18n/et/dashboard.ts b/shared/src/i18n/et/dashboard.ts index bc406da92..5c9f4f9a0 100644 --- a/shared/src/i18n/et/dashboard.ts +++ b/shared/src/i18n/et/dashboard.ts @@ -113,7 +113,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Kasuta Unsplashi fotot autorilt {photographer}', 'dashboard.titleRequired': 'Pealkiri on kohustuslik', 'dashboard.endDateError': 'Lõppkuupäev peab olema alguskuupäevast hilisem', - 'dashboard.tripTooLong': 'Reis võib kesta kuni {days} päeva', + 'dashboard.tripTooLong': 'Reis võib kesta kuni {count} päeva', + 'dashboard.tripTooLong.one': 'Reis võib kesta kuni {count} päev', 'dashboard.dateShiftTitle': 'Uus alguskuupäev', 'dashboard.dateShiftIntro': 'Muutsid reisi algust. Kuidas peaksid plaanid uute kuupäevadega kohanema?', 'dashboard.dateShiftKeepBookings': 'Jäta broneeringud oma kuupäevadele', diff --git a/shared/src/i18n/et/dawarich.ts b/shared/src/i18n/et/dawarich.ts index da5ce5e2a..7c36789c2 100644 --- a/shared/src/i18n/et/dawarich.ts +++ b/shared/src/i18n/et/dawarich.ts @@ -119,7 +119,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Lisa sissekanne', 'dawarich.accept.confirm.bucket_list': 'Märgi täidetuks', 'dawarich.accept.recorded': 'Salvestatud {from} kuni {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Nimi', 'dawarich.accept.date': 'Kuupäev', 'dawarich.accept.from': 'Saabusid', @@ -184,7 +185,9 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Kontrolli uuesti', 'dawarich.bucket.metersAway': '{meters} m kaugusel', 'dawarich.bucket.kilometersAway': '{km} km kaugusel', - 'dawarich.bucket.rule': 'Soov loetakse täidetuks, kui viibid vähemalt {minutes} minutit kohast {meters} m raadiuses.', + 'dawarich.bucket.rule': 'Soov loetakse täidetuks, kui viibid vähemalt {count} minutit kohast {meters} m raadiuses.', + 'dawarich.bucket.rule.one': + 'Soov loetakse täidetuks, kui viibid vähemalt {count} minuti kohast {meters} m raadiuses.', 'dawarich.journey.dayStays.other': '{count} peatumist Dawarichist', 'dawarich.journey.dayStays.one': '{count} peatumine Dawarichist', diff --git a/shared/src/i18n/et/roadtrip.ts b/shared/src/i18n/et/roadtrip.ts index f5949a71e..7b0fe7d4a 100644 --- a/shared/src/i18n/et/roadtrip.ts +++ b/shared/src/i18n/et/roadtrip.ts @@ -52,7 +52,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Päevaseid pause ei saa arvutada, kuni kõik ühendavad marsruudid on saadaval.', 'roadtrip.window.conflict': 'Kindel kellaaeg on vastuolus sõidu ja päevapausidega. Käsitsi määratud ajad ei muutu; automaatne ajastamine on peatatud.', - 'roadtrip.window.tooLong': 'Nende sõiduaegadega oleks vaja üle {days} lisapäeva. Vali pikem päevane ajavahemik.', + 'roadtrip.window.tooLong': 'Nende sõiduaegadega oleks vaja üle {count} lisapäeva. Vali pikem päevane ajavahemik.', + 'roadtrip.window.tooLong.one': 'Nende sõiduaegadega oleks vaja üle {count} lisapäeva. Vali pikem päevane ajavahemik.', 'roadtrip.mode.roadtrip': 'Autoreis', 'roadtrip.mode.days': 'Päevad', 'roadtrip.mode.label': 'Plaanivaade', @@ -99,8 +100,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'väljas', 'roadtrip.limit.blockNote': 'Üks plokk = {distance}', - 'roadtrip.limit.echoOver': '{over} päeva {days}-st ületab sinu piirangu.', - 'roadtrip.limit.echoNone': 'Kõik {days} päeva jäävad sinu piirangutesse.', + 'roadtrip.limit.echoOver': '{over} päeva {count}-st ületab sinu piirangu.', + 'roadtrip.limit.echoOver.one': '{over} päeva {count}-st ületab sinu piirangu.', + 'roadtrip.limit.echoNone': 'Kõik {count} päeva jäävad sinu piirangutesse.', + 'roadtrip.limit.echoNone.one': 'Ainus päev jääb sinu piirangutesse.', 'roadtrip.limit.echoDry': '{count} lõiku ilma kättesaadava peatuseta.', 'roadtrip.limit.echoDry.one': '{count} lõik ilma kättesaadava peatuseta.', 'roadtrip.limit.echoEmpty': 'Ava autoreis, et näha nende mõju.', @@ -266,8 +269,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Arvutatud sõidu põhjal', 'roadtrip.stop.endIsLeave': 'Autoreisil algab sõit sel kellaajal.', 'roadtrip.via.hint': 'Marsruudi muutmiseks lohista, eemaldamiseks tee paremklõps', - 'roadtrip.warn.late': 'Saabub {minutes} minutit pärast määratud aega', - 'roadtrip.warn.missedLeave': 'Saabub {minutes} minutit pärast määratud väljumisaega', + 'roadtrip.warn.late': 'Saabub {count} minutit pärast määratud aega', + 'roadtrip.warn.late.one': 'Saabub {count} minut pärast määratud aega', + 'roadtrip.warn.missedLeave': 'Saabub {count} minutit pärast määratud väljumisaega', + 'roadtrip.warn.missedLeave.one': 'Saabub {count} minut pärast määratud väljumisaega', 'roadtrip.warn.overnight': 'Järgmine päev', 'roadtrip.spill.title': 'Päevast {number}', 'roadtrip.spill.departs': 'väljub {time}', diff --git a/shared/src/i18n/et/settings.ts b/shared/src/i18n/et/settings.ts index 81a92ee66..61e0bef43 100644 --- a/shared/src/i18n/et/settings.ts +++ b/shared/src/i18n/et/settings.ts @@ -633,7 +633,9 @@ const settings: TranslationStrings = { 'settings.apiKeys.neverUsed': 'pole kunagi kasutatud', 'settings.apiKeys.loadFailed': 'Võtmeid ei saanud laadida. Uuesti proovimiseks laadi leht uuesti.', 'settings.apiKeys.limitReached': - 'Sul on {max} võtit — konto suurim lubatud arv. Uue loomiseks kustuta võti, mida enam ei kasuta.', + 'Sul on {count} võtit, konto suurim lubatud arv. Uue loomiseks kustuta võti, mida enam ei kasuta.', + 'settings.apiKeys.limitReached.one': + 'Sul on {count} võti, konto suurim lubatud arv. Uue loomiseks kustuta see, kui sa seda enam ei kasuta.', 'settings.apiKeys.copyFailed': 'Kopeerimine ebaõnnestus. Vali tekst ja kopeeri käsitsi.', 'settings.apiKeys.modal.createTitle': 'Loo API-võti', 'settings.apiKeys.modal.name': 'Nimi', diff --git a/shared/src/i18n/et/storage.ts b/shared/src/i18n/et/storage.ts index d237cae62..e67ce89b5 100644 --- a/shared/src/i18n/et/storage.ts +++ b/shared/src/i18n/et/storage.ts @@ -139,7 +139,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Kas teisaldada olemasolevad objektid uude taustsüsteemi?', - 'storage.migrate.promptLine': '{category}: {objects} objekti ({size}), {from} → {to}', + 'storage.migrate.promptLine': '{category}: {count} objekti ({size}), {from} → {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objekt ({size}), {from} → {to}', 'storage.migrate.promptLineUnknown': '{category}: suurus teadmata (kasutust pole veel kontrollitud), {from} → {to}', 'storage.migrate.move': 'Teisalda olemasolevad objektid', 'storage.migrate.routeOnly': 'Suuna ainult uued kirjutused', @@ -148,7 +149,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} ebaõnnestus — neid objekte ei kopeeritud uude taustsüsteemi', 'storage.migrate.failed': 'Teisaldamine ebaõnnestus: {error} — kategooriat ei suunatud ümber', 'storage.migrate.cancelled': 'Teisaldamine tühistatud — midagi ei suunatud ümber', - 'storage.migrate.reclaimable': '{objects} objekti ({size}) jäid taustsüsteemi {from} — vabasta ruum käsitsi', + 'storage.migrate.reclaimable': '{count} objekti ({size}) jäid taustsüsteemi {from}, vabasta ruum käsitsi', + 'storage.migrate.reclaimable.one': '{count} objekt ({size}) jäi taustsüsteemi {from}, vabasta ruum käsitsi', 'storage.migrate.cancel': 'Tühista teisaldamine', 'storage.migrate.promptCancel': 'Tühista', 'storage.migrate.queued': 'Järjekorras: {categories}', diff --git a/shared/src/i18n/fr/collab.ts b/shared/src/i18n/fr/collab.ts index a69476aef..979d27321 100644 --- a/shared/src/i18n/fr/collab.ts +++ b/shared/src/i18n/fr/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Options', 'collab.polls.delete': 'Supprimer', 'collab.polls.closedSection': 'Fermés', - 'collab.chat.imageLimit': 'Vous pouvez joindre jusqu’à {max} images par message', + 'collab.chat.imageLimit': 'Vous pouvez joindre jusqu’à {count} images par message', + 'collab.chat.imageLimit.one': 'Vous pouvez joindre jusqu’à {count} image par message', 'collab.chat.uploading': 'Envoi {percent} %', 'collab.chat.attachImages': 'Joindre des images', 'collab.chat.attachedImage': 'Image jointe', diff --git a/shared/src/i18n/fr/dashboard.ts b/shared/src/i18n/fr/dashboard.ts index 0e969d96e..ef282fc3d 100644 --- a/shared/src/i18n/fr/dashboard.ts +++ b/shared/src/i18n/fr/dashboard.ts @@ -101,7 +101,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Utiliser la photo Unsplash de {photographer}', 'dashboard.titleRequired': 'Le titre est obligatoire', 'dashboard.endDateError': 'La date de fin doit être postérieure à la date de début', - 'dashboard.tripTooLong': 'Un voyage ne peut pas dépasser {days} jours', + 'dashboard.tripTooLong': 'Un voyage ne peut pas dépasser {count} jours', + 'dashboard.tripTooLong.one': 'Un voyage ne peut pas dépasser {count} jour', 'dashboard.dateShiftTitle': 'Nouvelle date de début', 'dashboard.dateShiftIntro': 'Vous avez modifié la date de début de ce voyage. Comment vos plans doivent-ils suivre les nouvelles dates ?', diff --git a/shared/src/i18n/fr/dawarich.ts b/shared/src/i18n/fr/dawarich.ts index 4c1045a98..db7739900 100644 --- a/shared/src/i18n/fr/dawarich.ts +++ b/shared/src/i18n/fr/dawarich.ts @@ -119,7 +119,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Ajouter l’entrée', 'dawarich.accept.confirm.bucket_list': 'Le cocher', 'dawarich.accept.recorded': 'Enregistré du {from} au {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Nom', 'dawarich.accept.date': 'Date', 'dawarich.accept.from': 'Arrivée', @@ -185,7 +186,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Vérifier à nouveau', 'dawarich.bucket.metersAway': 'à {meters} m', 'dawarich.bucket.kilometersAway': 'à {km} km', - 'dawarich.bucket.rule': 'Une envie est atteinte à moins de {meters} m et après {minutes} minutes sur place.', + 'dawarich.bucket.rule': 'Une envie est atteinte à moins de {meters} m et après {count} minutes sur place.', + 'dawarich.bucket.rule.one': 'Une envie est atteinte à moins de {meters} m et après {count} minute sur place.', 'dawarich.journey.dayStays.other': '{count} séjours issus de Dawarich', 'dawarich.journey.dayStays.one': '{count} séjour issu de Dawarich', diff --git a/shared/src/i18n/fr/roadtrip.ts b/shared/src/i18n/fr/roadtrip.ts index 0079c8cdc..d1bd752e3 100644 --- a/shared/src/i18n/fr/roadtrip.ts +++ b/shared/src/i18n/fr/roadtrip.ts @@ -57,7 +57,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Une heure fixe est incompatible avec le trajet et les pauses. Les heures manuelles sont conservées ; la planification automatique est suspendue.', 'roadtrip.window.tooLong': - 'Ces horaires nécessitent plus de {days} jours supplémentaires. Élargissez la plage horaire quotidienne.', + 'Ces horaires nécessitent plus de {count} jours supplémentaires. Élargissez la plage horaire quotidienne.', + 'roadtrip.window.tooLong.one': + 'Ces horaires nécessitent plus de {count} jour supplémentaire. Élargissez la plage horaire quotidienne.', 'roadtrip.mode.roadtrip': 'Road trip', 'roadtrip.mode.days': 'Jours', 'roadtrip.mode.label': 'Vue du plan', @@ -104,8 +106,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'désactivé', 'roadtrip.limit.blockNote': 'Un bloc = {distance}', - 'roadtrip.limit.echoOver': '{over} jours sur {days} dépassent ta limite.', - 'roadtrip.limit.echoNone': 'Les {days} jours respectent tes limites.', + 'roadtrip.limit.echoOver': '{over} jours sur {count} dépassent ta limite.', + 'roadtrip.limit.echoOver.one': '{over} jours sur {count} dépassent ta limite.', + 'roadtrip.limit.echoNone': 'Les {count} jours respectent tes limites.', + 'roadtrip.limit.echoNone.one': '{count} jour, dans tes limites.', 'roadtrip.limit.echoDry': '{count} tronçons sans arrêt accessible.', 'roadtrip.limit.echoDry.one': '{count} tronçon sans arrêt accessible.', 'roadtrip.limit.echoEmpty': 'Ouvre un road trip pour voir l’effet.', @@ -275,8 +279,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Calculé d’après le trajet', 'roadtrip.stop.endIsLeave': 'Pendant le road trip, le départ se fait à cette heure.', 'roadtrip.via.hint': 'Faites glisser pour remodeler l’itinéraire, clic droit pour supprimer', - 'roadtrip.warn.late': 'Arrivée {minutes} min après l’heure fixée', - 'roadtrip.warn.missedLeave': 'Arrivée {minutes} min après l’heure de départ fixée', + 'roadtrip.warn.late': 'Arrivée {count} min après l’heure fixée', + 'roadtrip.warn.late.one': 'Arrivée {count} min après l’heure fixée', + 'roadtrip.warn.missedLeave': 'Arrivée {count} min après l’heure de départ fixée', + 'roadtrip.warn.missedLeave.one': 'Arrivée {count} min après l’heure de départ fixée', 'roadtrip.warn.overnight': 'Jour suivant', 'roadtrip.spill.title': 'Du jour {number}', 'roadtrip.spill.departs': 'départ {time}', diff --git a/shared/src/i18n/fr/settings.ts b/shared/src/i18n/fr/settings.ts index 53061a0b9..3be380750 100644 --- a/shared/src/i18n/fr/settings.ts +++ b/shared/src/i18n/fr/settings.ts @@ -644,7 +644,9 @@ const settings: TranslationStrings = { 'settings.apiKeys.neverUsed': 'jamais utilisée', 'settings.apiKeys.loadFailed': 'Impossible de charger vos clés. Rechargez la page pour réessayer.', 'settings.apiKeys.limitReached': - "Vous avez {max} clés, le maximum pour un compte. Supprimez-en une dont vous n'avez plus besoin pour en créer une autre.", + "Vous avez {count} clés, le maximum pour un compte. Supprimez-en une dont vous n'avez plus besoin pour en créer une autre.", + 'settings.apiKeys.limitReached.one': + "Vous avez {count} clé, le maximum pour un compte. Supprimez-la si vous n'en avez plus besoin pour en créer une autre.", 'settings.apiKeys.copyFailed': 'Impossible de copier. Sélectionnez le texte et copiez-le manuellement.', 'settings.apiKeys.modal.createTitle': 'Créer une clé API', 'settings.apiKeys.modal.name': 'Nom', diff --git a/shared/src/i18n/fr/storage.ts b/shared/src/i18n/fr/storage.ts index 2532e4f33..6dcd95135 100644 --- a/shared/src/i18n/fr/storage.ts +++ b/shared/src/i18n/fr/storage.ts @@ -126,7 +126,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Déplacer les objets existants vers le nouveau backend ?', - 'storage.migrate.promptLine': '{category} : {objects} objets ({size}) de {from} vers {to}', + 'storage.migrate.promptLine': '{category} : {count} objets ({size}) de {from} vers {to}', + 'storage.migrate.promptLine.one': '{category} : {count} objet ({size}) de {from} vers {to}', 'storage.migrate.promptLineUnknown': '{category} : taille inconnue (utilisation pas encore calculée) de {from} vers {to}', 'storage.migrate.move': 'Déplacer les objets existants', @@ -136,7 +137,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': "{failed} en échec — ces objets n'ont pas été copiés vers le nouveau backend", 'storage.migrate.failed': "Échec du déplacement : {error} — la catégorie n'a pas été changée", 'storage.migrate.cancelled': "Déplacement annulé — rien n'a été changé", - 'storage.migrate.reclaimable': '{objects} objets ({size}) restent sur {from} — à récupérer manuellement', + 'storage.migrate.reclaimable': '{count} objets ({size}) restent sur {from}, à récupérer manuellement', + 'storage.migrate.reclaimable.one': '{count} objet ({size}) reste sur {from}, à récupérer manuellement', 'storage.migrate.cancel': 'Annuler le déplacement', 'storage.migrate.promptCancel': 'Annuler', 'storage.migrate.queued': "En file d'attente : {categories}", diff --git a/shared/src/i18n/gr/collab.ts b/shared/src/i18n/gr/collab.ts index 8884f46ca..9e75a5916 100644 --- a/shared/src/i18n/gr/collab.ts +++ b/shared/src/i18n/gr/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Επιλογές', 'collab.polls.delete': 'Διαγραφή', 'collab.polls.closedSection': 'Κλειστές', - 'collab.chat.imageLimit': 'Μπορείτε να επισυνάψετε έως {max} εικόνες ανά μήνυμα', + 'collab.chat.imageLimit': 'Μπορείτε να επισυνάψετε έως {count} εικόνες ανά μήνυμα', + 'collab.chat.imageLimit.one': 'Μπορείτε να επισυνάψετε μόνο {count} εικόνα ανά μήνυμα', 'collab.chat.uploading': 'Μεταφόρτωση {percent}%', 'collab.chat.attachImages': 'Επισύναψη εικόνων', 'collab.chat.attachedImage': 'Συνημμένη εικόνα', diff --git a/shared/src/i18n/gr/dashboard.ts b/shared/src/i18n/gr/dashboard.ts index 74cc3ef82..761286762 100644 --- a/shared/src/i18n/gr/dashboard.ts +++ b/shared/src/i18n/gr/dashboard.ts @@ -111,7 +111,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Χρήση φωτογραφίας Unsplash από {photographer}', 'dashboard.titleRequired': 'Ο τίτλος είναι υποχρεωτικός', 'dashboard.endDateError': 'Η ημερομηνία λήξης πρέπει να είναι μετά την ημερομηνία έναρξης', - 'dashboard.tripTooLong': 'Ένα ταξίδι μπορεί να διαρκεί το πολύ {days} ημέρες', + 'dashboard.tripTooLong': 'Ένα ταξίδι μπορεί να διαρκεί το πολύ {count} ημέρες', + 'dashboard.tripTooLong.one': 'Ένα ταξίδι μπορεί να διαρκεί το πολύ {count} ημέρα', 'dashboard.dateShiftTitle': 'Νέα ημερομηνία έναρξης', 'dashboard.dateShiftIntro': 'Αλλάξατε την ημερομηνία έναρξης αυτού του ταξιδιού. Πώς θέλετε να ακολουθήσουν τα σχέδιά σας τις νέες ημερομηνίες;', diff --git a/shared/src/i18n/gr/dawarich.ts b/shared/src/i18n/gr/dawarich.ts index c040cf882..353ae8be5 100644 --- a/shared/src/i18n/gr/dawarich.ts +++ b/shared/src/i18n/gr/dawarich.ts @@ -120,7 +120,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Προσθήκη καταχώρησης', 'dawarich.accept.confirm.bucket_list': 'Σημείωση ως εκπληρωμένης', 'dawarich.accept.recorded': 'Καταγράφηκε από {from} έως {to}', - 'dawarich.accept.duration': '{minutes} λεπτά', + 'dawarich.accept.duration': '{count} λεπτά', + 'dawarich.accept.duration.one': '{count} λεπτό', 'dawarich.accept.name': 'Όνομα', 'dawarich.accept.date': 'Ημερομηνία', 'dawarich.accept.from': 'Άφιξη', @@ -186,7 +187,10 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Έλεγχος ξανά', 'dawarich.bucket.metersAway': '{meters} μ. μακριά', 'dawarich.bucket.kilometersAway': '{km} χλμ. μακριά', - 'dawarich.bucket.rule': 'Μια επιθυμία μετράει ως εκπληρωμένη σε απόσταση έως {meters} μ. και μετά από {minutes} λεπτά στο σημείο.', + 'dawarich.bucket.rule': + 'Μια επιθυμία μετράει ως εκπληρωμένη σε απόσταση έως {meters} μ. και μετά από {count} λεπτά στο σημείο.', + 'dawarich.bucket.rule.one': + 'Μια επιθυμία μετράει ως εκπληρωμένη σε απόσταση έως {meters} μ. και μετά από {count} λεπτό στο σημείο.', 'dawarich.journey.dayStays.other': '{count} στάσεις από το Dawarich', 'dawarich.journey.dayStays.one': '1 στάση από το Dawarich', diff --git a/shared/src/i18n/gr/roadtrip.ts b/shared/src/i18n/gr/roadtrip.ts index c9e7ecb1d..011bdc668 100644 --- a/shared/src/i18n/gr/roadtrip.ts +++ b/shared/src/i18n/gr/roadtrip.ts @@ -58,7 +58,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Μια σταθερή ώρα συγκρούεται με την οδήγηση και τις παύσεις. Οι χειροκίνητες ώρες διατηρούνται· ο αυτόματος σχεδιασμός έχει παύσει.', 'roadtrip.window.tooLong': - 'Αυτές οι ώρες απαιτούν πάνω από {days} επιπλέον ημέρες. Επιλέξτε μεγαλύτερο ημερήσιο διάστημα.', + 'Αυτές οι ώρες απαιτούν πάνω από {count} επιπλέον ημέρες. Επιλέξτε μεγαλύτερο ημερήσιο διάστημα.', + 'roadtrip.window.tooLong.one': + 'Αυτές οι ώρες απαιτούν πάνω από {count} επιπλέον ημέρα. Επιλέξτε μεγαλύτερο ημερήσιο διάστημα.', 'roadtrip.mode.roadtrip': 'Οδικό ταξίδι', 'roadtrip.mode.days': 'Ημέρες', 'roadtrip.mode.label': 'Προβολή σχεδίου', @@ -105,8 +107,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'λεπ', 'roadtrip.limit.off': 'ανενεργό', 'roadtrip.limit.blockNote': 'Ένα μπλοκ = {distance}', - 'roadtrip.limit.echoOver': '{over} από {days} ημέρες πάνω από το όριο.', - 'roadtrip.limit.echoNone': 'Και οι {days} ημέρες εντός ορίων.', + 'roadtrip.limit.echoOver': '{over} από {count} ημέρες πάνω από το όριο.', + 'roadtrip.limit.echoOver.one': '{over} από {count} ημέρα πάνω από το όριο.', + 'roadtrip.limit.echoNone': 'Και οι {count} ημέρες εντός ορίων.', + 'roadtrip.limit.echoNone.one': 'Η μία ημέρα είναι εντός ορίων.', 'roadtrip.limit.echoDry': '{count} τμήματα χωρίς στάση εντός αυτονομίας.', 'roadtrip.limit.echoDry.one': '{count} τμήμα χωρίς στάση εντός αυτονομίας.', 'roadtrip.limit.echoEmpty': 'Άνοιξε ένα ταξίδι για να δεις το αποτέλεσμα.', @@ -277,8 +281,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Υπολογισμένη από τη διαδρομή', 'roadtrip.stop.endIsLeave': 'Στο οδικό ταξίδι, η αναχώρηση γίνεται αυτή την ώρα.', 'roadtrip.via.hint': 'Σύρετε για να αλλάξετε τη διαδρομή, δεξί κλικ για αφαίρεση', - 'roadtrip.warn.late': 'Άφιξη {minutes} λεπτά μετά την ορισμένη ώρα', - 'roadtrip.warn.missedLeave': 'Άφιξη {minutes} λεπτά μετά την ορισμένη ώρα αναχώρησης', + 'roadtrip.warn.late': 'Άφιξη {count} λεπτά μετά την ορισμένη ώρα', + 'roadtrip.warn.late.one': 'Άφιξη {count} λεπτό μετά την ορισμένη ώρα', + 'roadtrip.warn.missedLeave': 'Άφιξη {count} λεπτά μετά την ορισμένη ώρα αναχώρησης', + 'roadtrip.warn.missedLeave.one': 'Άφιξη {count} λεπτό μετά την ορισμένη ώρα αναχώρησης', 'roadtrip.warn.overnight': 'Επόμενη μέρα', 'roadtrip.spill.title': 'Από την ημέρα {number}', 'roadtrip.spill.departs': 'αναχώρηση {time}', diff --git a/shared/src/i18n/gr/settings.ts b/shared/src/i18n/gr/settings.ts index 75d6746df..d28f824b0 100644 --- a/shared/src/i18n/gr/settings.ts +++ b/shared/src/i18n/gr/settings.ts @@ -642,7 +642,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpoint', 'settings.apiKeys.neverUsed': 'δεν χρησιμοποιήθηκε ποτέ', 'settings.apiKeys.loadFailed': 'Δεν ήταν δυνατή η φόρτωση των κλειδιών σου. Φόρτωσε ξανά τη σελίδα για να δοκιμάσεις πάλι.', - 'settings.apiKeys.limitReached': 'Έχεις {max} κλειδιά, το μέγιστο για έναν λογαριασμό. Διάγραψε ένα που δεν χρησιμοποιείς πια για να δημιουργήσεις νέο.', + 'settings.apiKeys.limitReached': + 'Έχεις {count} κλειδιά, το μέγιστο για έναν λογαριασμό. Διάγραψε ένα που δεν χρησιμοποιείς πια για να δημιουργήσεις νέο.', + 'settings.apiKeys.limitReached.one': + 'Έχεις {count} κλειδί, το μέγιστο για έναν λογαριασμό. Διάγραψέ το αν δεν το χρησιμοποιείς πια για να δημιουργήσεις νέο.', 'settings.apiKeys.copyFailed': 'Η αντιγραφή απέτυχε. Επίλεξε το κείμενο και αντίγραψέ το χειροκίνητα.', 'settings.apiKeys.modal.createTitle': 'Δημιουργία κλειδιού API', 'settings.apiKeys.modal.name': 'Όνομα', diff --git a/shared/src/i18n/gr/storage.ts b/shared/src/i18n/gr/storage.ts index 96306858a..2d57a6e85 100644 --- a/shared/src/i18n/gr/storage.ts +++ b/shared/src/i18n/gr/storage.ts @@ -127,7 +127,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Μετακίνηση των υπαρχόντων αντικειμένων στο νέο backend;', - 'storage.migrate.promptLine': '{category}: {objects} αντικείμενα ({size}) από {from} σε {to}', + 'storage.migrate.promptLine': '{category}: {count} αντικείμενα ({size}) από {from} σε {to}', + 'storage.migrate.promptLine.one': '{category}: {count} αντικείμενο ({size}) από {from} σε {to}', 'storage.migrate.promptLineUnknown': '{category}: άγνωστο μέγεθος (δεν έχει γίνει ακόμα σάρωση χρήσης) από {from} σε {to}', 'storage.migrate.move': 'Μετακίνηση υπαρχόντων αντικειμένων', @@ -137,7 +138,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} απέτυχαν — αυτά τα αντικείμενα δεν αντιγράφηκαν στο νέο backend', 'storage.migrate.failed': 'Η μετακίνηση απέτυχε: {error} — η κατηγορία δεν άλλαξε', 'storage.migrate.cancelled': 'Η μετακίνηση ακυρώθηκε — τίποτα δεν άλλαξε', - 'storage.migrate.reclaimable': '{objects} αντικείμενα ({size}) παραμένουν στο {from} — ανάκτησέ τα χειροκίνητα', + 'storage.migrate.reclaimable': '{count} αντικείμενα ({size}) παραμένουν στο {from}, ανάκτησέ τα χειροκίνητα', + 'storage.migrate.reclaimable.one': '{count} αντικείμενο ({size}) παραμένει στο {from}, ανάκτησέ το χειροκίνητα', 'storage.migrate.cancel': 'Ακύρωση μετακίνησης', 'storage.migrate.promptCancel': 'Ακύρωση', 'storage.migrate.queued': 'Σε αναμονή: {categories}', diff --git a/shared/src/i18n/hu/collab.ts b/shared/src/i18n/hu/collab.ts index 7271d8ae1..806f9dd30 100644 --- a/shared/src/i18n/hu/collab.ts +++ b/shared/src/i18n/hu/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Opciók', 'collab.polls.delete': 'Törlés', 'collab.polls.closedSection': 'Lezárva', - 'collab.chat.imageLimit': 'Üzenetenként legfeljebb {max} képet csatolhatsz', + 'collab.chat.imageLimit': 'Üzenetenként legfeljebb {count} képet csatolhatsz', + 'collab.chat.imageLimit.one': 'Üzenetenként csak {count} képet csatolhatsz', 'collab.chat.uploading': 'Feltöltés {percent}%', 'collab.chat.attachImages': 'Képek csatolása', 'collab.chat.attachedImage': 'Csatolt kép', diff --git a/shared/src/i18n/hu/dashboard.ts b/shared/src/i18n/hu/dashboard.ts index 0f6ae2495..52b8ae1ab 100644 --- a/shared/src/i18n/hu/dashboard.ts +++ b/shared/src/i18n/hu/dashboard.ts @@ -101,7 +101,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': '{photographer} Unsplash-fotójának használata', 'dashboard.titleRequired': 'A cím megadása kötelező', 'dashboard.endDateError': 'A záró dátumnak a kezdő dátum után kell lennie', - 'dashboard.tripTooLong': 'Egy utazás legfeljebb {days} napos lehet', + 'dashboard.tripTooLong': 'Egy utazás legfeljebb {count} napos lehet', + 'dashboard.tripTooLong.one': 'Egy utazás legfeljebb {count} napos lehet', 'dashboard.dateShiftTitle': 'Új kezdő dátum', 'dashboard.dateShiftIntro': 'Megváltoztattad az út kezdetét. Hogyan kövessék a terveid az új dátumokat?', 'dashboard.dateShiftKeepBookings': 'A foglalások maradjanak a saját dátumaikon', diff --git a/shared/src/i18n/hu/dawarich.ts b/shared/src/i18n/hu/dawarich.ts index 8a8ecd018..f81a43595 100644 --- a/shared/src/i18n/hu/dawarich.ts +++ b/shared/src/i18n/hu/dawarich.ts @@ -119,7 +119,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Bejegyzés hozzáadása', 'dawarich.accept.confirm.bucket_list': 'Pipáld ki', 'dawarich.accept.recorded': 'Rögzítve {from} és {to} között', - 'dawarich.accept.duration': '{minutes} perc', + 'dawarich.accept.duration': '{count} perc', + 'dawarich.accept.duration.one': '{count} perc', 'dawarich.accept.name': 'Név', 'dawarich.accept.date': 'Dátum', 'dawarich.accept.from': 'Érkezés', @@ -184,7 +185,10 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Ellenőrzés újra', 'dawarich.bucket.metersAway': '{meters} m-re', 'dawarich.bucket.kilometersAway': '{km} km-re', - 'dawarich.bucket.rule': 'Egy kívánság {meters} méteren belül és {minutes} perc helyszíni idő után számít teljesítettnek.', + 'dawarich.bucket.rule': + 'Egy kívánság {meters} méteren belül és {count} perc helyszíni idő után számít teljesítettnek.', + 'dawarich.bucket.rule.one': + 'Egy kívánság {meters} méteren belül és {count} perc helyszíni idő után számít teljesítettnek.', 'dawarich.journey.dayStays.other': '{count} tartózkodás a Dawarichból', 'dawarich.journey.dayStays.one': '{count} tartózkodás a Dawarichból', diff --git a/shared/src/i18n/hu/roadtrip.ts b/shared/src/i18n/hu/roadtrip.ts index 9e4c09fbb..136921ec3 100644 --- a/shared/src/i18n/hu/roadtrip.ts +++ b/shared/src/i18n/hu/roadtrip.ts @@ -56,7 +56,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Egy rögzített időpont ütközik az utazással és a szünetekkel. A kézi időpontok megmaradnak; az automatikus tervezés szünetel.', 'roadtrip.window.tooLong': - 'Ezek az időpontok több mint {days} további napot igényelnének. Válassz hosszabb napi időszakot.', + 'Ezek az időpontok több mint {count} további napot igényelnének. Válassz hosszabb napi időszakot.', + 'roadtrip.window.tooLong.one': + 'Ezek az időpontok több mint {count} további napot igényelnének. Válassz hosszabb napi időszakot.', 'roadtrip.mode.roadtrip': 'Autós út', 'roadtrip.mode.days': 'Napok', 'roadtrip.mode.label': 'Terv nézet', @@ -103,8 +105,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'perc', 'roadtrip.limit.off': 'ki', 'roadtrip.limit.blockNote': 'Egy blokk = {distance}', - 'roadtrip.limit.echoOver': '{days} napból {over} a határ felett.', - 'roadtrip.limit.echoNone': 'Mind a(z) {days} nap a határokon belül.', + 'roadtrip.limit.echoOver': '{count} napból {over} a határ felett.', + 'roadtrip.limit.echoOver.one': '{count} napból {over} a határ felett.', + 'roadtrip.limit.echoNone': 'Mind a(z) {count} nap a határokon belül.', + 'roadtrip.limit.echoNone.one': 'Az egyetlen nap a határokon belül.', 'roadtrip.limit.echoDry': '{count} szakasz elérhető megálló nélkül.', 'roadtrip.limit.echoDry.one': '{count} szakasz elérhető megálló nélkül.', 'roadtrip.limit.echoEmpty': 'Nyiss meg egy utat, hogy lásd a hatást.', @@ -274,8 +278,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'A menetidőből számolva', 'roadtrip.stop.endIsLeave': 'Az autós úton ekkor indul tovább az út.', 'roadtrip.via.hint': 'Húzza az útvonal átalakításához, jobb gombbal eltávolítható', - 'roadtrip.warn.late': 'Érkezés {minutes} perccel a megadott idő után', - 'roadtrip.warn.missedLeave': 'Érkezés {minutes} perccel a megadott indulási idő után', + 'roadtrip.warn.late': 'Érkezés {count} perccel a megadott idő után', + 'roadtrip.warn.late.one': 'Érkezés {count} perccel a megadott idő után', + 'roadtrip.warn.missedLeave': 'Érkezés {count} perccel a megadott indulási idő után', + 'roadtrip.warn.missedLeave.one': 'Érkezés {count} perccel a megadott indulási idő után', 'roadtrip.warn.overnight': 'Következő nap', 'roadtrip.spill.title': 'A(z) {number}. napról', 'roadtrip.spill.departs': 'indulás {time}', diff --git a/shared/src/i18n/hu/settings.ts b/shared/src/i18n/hu/settings.ts index 7fe142a07..5ed0260b9 100644 --- a/shared/src/i18n/hu/settings.ts +++ b/shared/src/i18n/hu/settings.ts @@ -631,7 +631,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Végpont', 'settings.apiKeys.neverUsed': 'még nem használt', 'settings.apiKeys.loadFailed': 'Nem sikerült betölteni a kulcsaidat. Töltsd újra az oldalt, és próbáld újra.', - 'settings.apiKeys.limitReached': '{max} kulcsod van, ennél több egy fiókhoz nem tartozhat. Törölj egy már nem használt kulcsot, hogy újat hozhass létre.', + 'settings.apiKeys.limitReached': + '{count} kulcsod van, ennél több egy fiókhoz nem tartozhat. Törölj egy már nem használt kulcsot, hogy újat hozhass létre.', + 'settings.apiKeys.limitReached.one': + '{count} kulcsod van, ennél több egy fiókhoz nem tartozhat. Töröld, ha már nem használod, hogy újat hozhass létre.', 'settings.apiKeys.copyFailed': 'A másolás nem sikerült. Jelöld ki a szöveget, és másold ki kézzel.', 'settings.apiKeys.modal.createTitle': 'API-kulcs létrehozása', 'settings.apiKeys.modal.name': 'Név', diff --git a/shared/src/i18n/hu/storage.ts b/shared/src/i18n/hu/storage.ts index 072d71d0a..399550671 100644 --- a/shared/src/i18n/hu/storage.ts +++ b/shared/src/i18n/hu/storage.ts @@ -125,7 +125,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Áthelyezed a meglévő objektumokat az új backendre?', - 'storage.migrate.promptLine': '{category}: {objects} objektum ({size}) innen: {from} ide: {to}', + 'storage.migrate.promptLine': '{category}: {count} objektum ({size}) innen: {from} ide: {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objektum ({size}) innen: {from} ide: {to}', 'storage.migrate.promptLineUnknown': '{category}: ismeretlen méret (a használat még nincs kiszámítva) innen: {from} ide: {to}', 'storage.migrate.move': 'Meglévő objektumok áthelyezése', @@ -135,7 +136,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} sikertelen — ezek az objektumok nem lettek átmásolva az új backendre', 'storage.migrate.failed': 'Az áthelyezés sikertelen: {error} — a kategória nem lett átváltva', 'storage.migrate.cancelled': 'Áthelyezés megszakítva — semmi sem lett átváltva', - 'storage.migrate.reclaimable': '{objects} objektum ({size}) marad a(z) {from} helyen — kézzel szabadítsd fel', + 'storage.migrate.reclaimable': '{count} objektum ({size}) marad a(z) {from} helyen, kézzel szabadítsd fel', + 'storage.migrate.reclaimable.one': '{count} objektum ({size}) marad a(z) {from} helyen, kézzel szabadítsd fel', 'storage.migrate.cancel': 'Áthelyezés megszakítása', 'storage.migrate.promptCancel': 'Mégse', 'storage.migrate.queued': 'Várólistán: {categories}', diff --git a/shared/src/i18n/i18n-parity.spec.ts b/shared/src/i18n/i18n-parity.spec.ts index 20f48eea6..f11097f9b 100644 --- a/shared/src/i18n/i18n-parity.spec.ts +++ b/shared/src/i18n/i18n-parity.spec.ts @@ -35,11 +35,13 @@ describe('i18n parity', () => { describe('i18n count strings', () => { const ROOT = new URL('../../scripts/fixtures/i18n/', import.meta.url); - it('refuses an en string with {count} or {n} outside a plural group', () => { + it('refuses an en string with {count}, {n} or a quantity param outside a plural group', () => { const { ungrouped, stale } = checkCountStrings(['a.ts'], [], ROOT); + // a.stay carries its quantity as {days}, which no plural form is picked by. expect(ungrouped).toEqual([ { file: 'a.ts', key: 'a.lone' }, { file: 'a.ts', key: 'a.day' }, + { file: 'a.ts', key: 'a.stay' }, ]); expect(stale).toEqual([]); }); @@ -50,7 +52,10 @@ describe('i18n count strings', () => { { key: 'a.count', because: 'a group now' }, ]; expect(checkCountStrings(['a.ts'], allowed, ROOT)).toEqual({ - ungrouped: [{ file: 'a.ts', key: 'a.lone' }], + ungrouped: [ + { file: 'a.ts', key: 'a.lone' }, + { file: 'a.ts', key: 'a.stay' }, + ], stale: ['a.count'], }); }); diff --git a/shared/src/i18n/id/collab.ts b/shared/src/i18n/id/collab.ts index 5eba1191f..811e923a6 100644 --- a/shared/src/i18n/id/collab.ts +++ b/shared/src/i18n/id/collab.ts @@ -96,7 +96,7 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Pilihan', 'collab.polls.delete': 'Hapus', 'collab.polls.closedSection': 'Ditutup', - 'collab.chat.imageLimit': 'Anda dapat melampirkan hingga {max} gambar per pesan', + 'collab.chat.imageLimit': 'Anda dapat melampirkan hingga {count} gambar per pesan', 'collab.chat.uploading': 'Mengunggah {percent}%', 'collab.chat.attachImages': 'Lampirkan gambar', 'collab.chat.attachedImage': 'Gambar terlampir', diff --git a/shared/src/i18n/id/dashboard.ts b/shared/src/i18n/id/dashboard.ts index 55b0205a9..b12dcdcc3 100644 --- a/shared/src/i18n/id/dashboard.ts +++ b/shared/src/i18n/id/dashboard.ts @@ -92,7 +92,7 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Gunakan foto Unsplash oleh {photographer}', 'dashboard.titleRequired': 'Judul wajib diisi', 'dashboard.endDateError': 'Tanggal selesai harus setelah tanggal mulai', - 'dashboard.tripTooLong': 'Perjalanan paling lama {days} hari', + 'dashboard.tripTooLong': 'Perjalanan paling lama {count} hari', 'dashboard.dateShiftTitle': 'Tanggal mulai baru', 'dashboard.dateShiftIntro': 'Kamu mengubah tanggal mulai perjalanan ini. Bagaimana rencanamu harus mengikuti tanggal baru?', diff --git a/shared/src/i18n/id/dawarich.ts b/shared/src/i18n/id/dawarich.ts index 458f8404f..ddc825fd6 100644 --- a/shared/src/i18n/id/dawarich.ts +++ b/shared/src/i18n/id/dawarich.ts @@ -114,7 +114,7 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Tambah entri', 'dawarich.accept.confirm.bucket_list': 'Coret', 'dawarich.accept.recorded': 'Terekam {from} sampai {to}', - 'dawarich.accept.duration': '{minutes} mnt', + 'dawarich.accept.duration': '{count} mnt', 'dawarich.accept.name': 'Nama', 'dawarich.accept.date': 'Tanggal', 'dawarich.accept.from': 'Tiba', @@ -172,7 +172,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Periksa lagi', 'dawarich.bucket.metersAway': '{meters} m dari sana', 'dawarich.bucket.kilometersAway': '{km} km dari sana', - 'dawarich.bucket.rule': 'Sebuah keinginan dianggap tercapai dalam radius {meters} m dan setelah {minutes} menit di lokasi.', + 'dawarich.bucket.rule': + 'Sebuah keinginan dianggap tercapai dalam radius {meters} m dan setelah {count} menit di lokasi.', 'dawarich.journey.dayStays.other': '{count} persinggahan dari Dawarich', }; diff --git a/shared/src/i18n/id/roadtrip.ts b/shared/src/i18n/id/roadtrip.ts index 9d174333d..1afe87591 100644 --- a/shared/src/i18n/id/roadtrip.ts +++ b/shared/src/i18n/id/roadtrip.ts @@ -56,7 +56,7 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Waktu tetap bertentangan dengan perjalanan dan jeda. Waktu manual tidak berubah; perencanaan otomatis dijeda.', 'roadtrip.window.tooLong': - 'Waktu ini memerlukan lebih dari {days} hari tambahan. Pilih rentang waktu harian yang lebih panjang.', + 'Waktu ini memerlukan lebih dari {count} hari tambahan. Pilih rentang waktu harian yang lebih panjang.', 'roadtrip.mode.roadtrip': 'Perjalanan darat', 'roadtrip.mode.days': 'Hari', 'roadtrip.mode.label': 'Tampilan rencana', @@ -102,8 +102,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'mnt', 'roadtrip.limit.off': 'mati', 'roadtrip.limit.blockNote': 'Satu blok = {distance}', - 'roadtrip.limit.echoOver': '{over} dari {days} hari melebihi batasmu.', - 'roadtrip.limit.echoNone': 'Semua {days} hari dalam batasmu.', + 'roadtrip.limit.echoOver': '{over} dari {count} hari melebihi batasmu.', + 'roadtrip.limit.echoNone': 'Semua {count} hari dalam batasmu.', 'roadtrip.limit.echoDry': '{count} ruas tanpa perhentian terjangkau.', 'roadtrip.limit.echoEmpty': 'Buka sebuah road trip untuk melihat efeknya.', 'roadtrip.limit.specToggle': 'Hitung dari data mobil', @@ -262,8 +262,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Dihitung dari waktu berkendara', 'roadtrip.stop.endIsLeave': 'Dalam perjalanan darat, keberangkatan pada waktu ini.', 'roadtrip.via.hint': 'Seret untuk mengubah rute, klik kanan untuk menghapus', - 'roadtrip.warn.late': 'Tiba {minutes} menit setelah waktu yang ditetapkan', - 'roadtrip.warn.missedLeave': 'Tiba {minutes} menit setelah waktu berangkat yang ditetapkan', + 'roadtrip.warn.late': 'Tiba {count} menit setelah waktu yang ditetapkan', + 'roadtrip.warn.missedLeave': 'Tiba {count} menit setelah waktu berangkat yang ditetapkan', 'roadtrip.warn.overnight': 'Hari berikutnya', 'roadtrip.spill.title': 'Dari hari {number}', 'roadtrip.spill.departs': 'berangkat {time}', diff --git a/shared/src/i18n/id/settings.ts b/shared/src/i18n/id/settings.ts index 3c9b6da68..387dcc526 100644 --- a/shared/src/i18n/id/settings.ts +++ b/shared/src/i18n/id/settings.ts @@ -621,7 +621,8 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpoint', 'settings.apiKeys.neverUsed': 'belum pernah dipakai', 'settings.apiKeys.loadFailed': 'Kunci tidak dapat dimuat. Muat ulang halaman untuk mencoba lagi.', - 'settings.apiKeys.limitReached': 'Kamu punya {max} kunci, batas maksimum per akun. Hapus kunci yang tidak lagi dipakai untuk membuat yang baru.', + 'settings.apiKeys.limitReached': + 'Kamu punya {count} kunci, batas maksimum per akun. Hapus kunci yang tidak lagi dipakai untuk membuat yang baru.', 'settings.apiKeys.copyFailed': 'Gagal menyalin. Pilih teksnya dan salin secara manual.', 'settings.apiKeys.modal.createTitle': 'Buat kunci API', 'settings.apiKeys.modal.name': 'Nama', diff --git a/shared/src/i18n/id/storage.ts b/shared/src/i18n/id/storage.ts index c05f702b3..d86ec9a8d 100644 --- a/shared/src/i18n/id/storage.ts +++ b/shared/src/i18n/id/storage.ts @@ -122,7 +122,7 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Pindahkan objek yang ada ke backend baru?', - 'storage.migrate.promptLine': '{category}: {objects} objek ({size}) dari {from} ke {to}', + 'storage.migrate.promptLine': '{category}: {count} objek ({size}) dari {from} ke {to}', 'storage.migrate.promptLineUnknown': '{category}: ukuran tidak diketahui (penggunaan belum dipindai) dari {from} ke {to}', 'storage.migrate.move': 'Pindahkan objek yang ada', @@ -132,7 +132,7 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} gagal — objek tersebut tidak disalin ke backend baru', 'storage.migrate.failed': 'Pemindahan gagal: {error} — kategori tidak diubah', 'storage.migrate.cancelled': 'Pemindahan dibatalkan — tidak ada yang diubah', - 'storage.migrate.reclaimable': '{objects} objek ({size}) masih tersisa di {from} — ambil kembali secara manual', + 'storage.migrate.reclaimable': '{count} objek ({size}) masih tersisa di {from}, ambil kembali secara manual', 'storage.migrate.cancel': 'Batalkan pemindahan', 'storage.migrate.promptCancel': 'Batal', 'storage.migrate.queued': 'Dalam antrean: {categories}', diff --git a/shared/src/i18n/it/collab.ts b/shared/src/i18n/it/collab.ts index 33c96d3fe..6ee49c2f1 100644 --- a/shared/src/i18n/it/collab.ts +++ b/shared/src/i18n/it/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Opzioni', 'collab.polls.delete': 'Elimina', 'collab.polls.closedSection': 'Chiusi', - 'collab.chat.imageLimit': 'Puoi allegare fino a {max} immagini per messaggio', + 'collab.chat.imageLimit': 'Puoi allegare fino a {count} immagini per messaggio', + 'collab.chat.imageLimit.one': 'Puoi allegare solo {count} immagine per messaggio', 'collab.chat.uploading': 'Caricamento {percent}%', 'collab.chat.attachImages': 'Allega immagini', 'collab.chat.attachedImage': 'Immagine allegata', diff --git a/shared/src/i18n/it/dashboard.ts b/shared/src/i18n/it/dashboard.ts index 3d2f7c7d7..fb6b9fb24 100644 --- a/shared/src/i18n/it/dashboard.ts +++ b/shared/src/i18n/it/dashboard.ts @@ -101,7 +101,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Usa foto Unsplash di {photographer}', 'dashboard.titleRequired': 'Il titolo è obbligatorio', 'dashboard.endDateError': 'La data di fine deve essere successiva alla data di inizio', - 'dashboard.tripTooLong': 'Un viaggio può durare al massimo {days} giorni', + 'dashboard.tripTooLong': 'Un viaggio può durare al massimo {count} giorni', + 'dashboard.tripTooLong.one': 'Un viaggio può durare al massimo {count} giorno', 'dashboard.dateShiftTitle': 'Nuova data di inizio', 'dashboard.dateShiftIntro': 'Hai modificato la data di inizio di questo viaggio. Come devono adattarsi i tuoi piani alle nuove date?', diff --git a/shared/src/i18n/it/dawarich.ts b/shared/src/i18n/it/dawarich.ts index a7808afc0..2cc7a0e24 100644 --- a/shared/src/i18n/it/dawarich.ts +++ b/shared/src/i18n/it/dawarich.ts @@ -119,7 +119,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Aggiungi voce', 'dawarich.accept.confirm.bucket_list': 'Spuntalo', 'dawarich.accept.recorded': 'Registrata dal {from} al {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Nome', 'dawarich.accept.date': 'Data', 'dawarich.accept.from': 'Arrivo', @@ -185,7 +186,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Controlla di nuovo', 'dawarich.bucket.metersAway': 'a {meters} m', 'dawarich.bucket.kilometersAway': 'a {km} km', - 'dawarich.bucket.rule': 'Un desiderio è raggiunto entro {meters} m e dopo {minutes} minuti sul posto.', + 'dawarich.bucket.rule': 'Un desiderio è raggiunto entro {meters} m e dopo {count} minuti sul posto.', + 'dawarich.bucket.rule.one': 'Un desiderio è raggiunto entro {meters} m e dopo {count} minuto sul posto.', 'dawarich.journey.dayStays.other': '{count} soste da Dawarich', 'dawarich.journey.dayStays.one': '{count} sosta da Dawarich', diff --git a/shared/src/i18n/it/roadtrip.ts b/shared/src/i18n/it/roadtrip.ts index 0beaae137..0e4e5818d 100644 --- a/shared/src/i18n/it/roadtrip.ts +++ b/shared/src/i18n/it/roadtrip.ts @@ -56,7 +56,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Un orario fisso è incompatibile con il tragitto e le pause. Gli orari manuali restano invariati; la pianificazione automatica è sospesa.', 'roadtrip.window.tooLong': - 'Questi orari richiederebbero oltre {days} giorni aggiuntivi. Scegli un intervallo giornaliero più lungo.', + 'Questi orari richiederebbero oltre {count} giorni aggiuntivi. Scegli un intervallo giornaliero più lungo.', + 'roadtrip.window.tooLong.one': + 'Questi orari richiederebbero oltre {count} giorno aggiuntivo. Scegli un intervallo giornaliero più lungo.', 'roadtrip.mode.roadtrip': 'Viaggio su strada', 'roadtrip.mode.days': 'Giorni', 'roadtrip.mode.label': 'Vista del piano', @@ -103,8 +105,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'off', 'roadtrip.limit.blockNote': 'Un blocco = {distance}', - 'roadtrip.limit.echoOver': '{over} giorni su {days} superano il limite.', - 'roadtrip.limit.echoNone': 'Tutti i {days} giorni rientrano nei limiti.', + 'roadtrip.limit.echoOver': '{over} giorni su {count} superano il limite.', + 'roadtrip.limit.echoOver.one': '{over} giorni su {count} superano il limite.', + 'roadtrip.limit.echoNone': 'Tutti i {count} giorni rientrano nei limiti.', + 'roadtrip.limit.echoNone.one': 'L’unico giorno rientra nei limiti.', 'roadtrip.limit.echoDry': '{count} tratti senza sosta raggiungibile.', 'roadtrip.limit.echoDry.one': '{count} tratto senza sosta raggiungibile.', 'roadtrip.limit.echoEmpty': 'Apri un viaggio per vedere l’effetto.', @@ -274,8 +278,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Calcolato dal tragitto', 'roadtrip.stop.endIsLeave': 'Nel viaggio su strada si riparte a quest’ora.', 'roadtrip.via.hint': 'Trascina per rimodellare il percorso, clic destro per rimuovere', - 'roadtrip.warn.late': 'Arrivo {minutes} min dopo l’orario impostato', - 'roadtrip.warn.missedLeave': 'Arrivo {minutes} min dopo l’orario di partenza impostato', + 'roadtrip.warn.late': 'Arrivo {count} min dopo l’orario impostato', + 'roadtrip.warn.late.one': 'Arrivo {count} min dopo l’orario impostato', + 'roadtrip.warn.missedLeave': 'Arrivo {count} min dopo l’orario di partenza impostato', + 'roadtrip.warn.missedLeave.one': 'Arrivo {count} min dopo l’orario di partenza impostato', 'roadtrip.warn.overnight': 'Giorno successivo', 'roadtrip.spill.title': 'Dal giorno {number}', 'roadtrip.spill.departs': 'partenza {time}', diff --git a/shared/src/i18n/it/settings.ts b/shared/src/i18n/it/settings.ts index 12fc435d3..8f28f163e 100644 --- a/shared/src/i18n/it/settings.ts +++ b/shared/src/i18n/it/settings.ts @@ -637,7 +637,9 @@ const settings: TranslationStrings = { 'settings.apiKeys.neverUsed': 'mai usata', 'settings.apiKeys.loadFailed': 'Impossibile caricare le tue chiavi. Ricarica la pagina per riprovare.', 'settings.apiKeys.limitReached': - "Hai {max} chiavi, il massimo per un account. Eliminane una che non usi più per crearne un'altra.", + "Hai {count} chiavi, il massimo per un account. Eliminane una che non usi più per crearne un'altra.", + 'settings.apiKeys.limitReached.one': + "Hai {count} chiave, il massimo per un account. Eliminala se non la usi più per crearne un'altra.", 'settings.apiKeys.copyFailed': 'Impossibile copiare. Seleziona il testo e copialo a mano.', 'settings.apiKeys.modal.createTitle': 'Crea chiave API', 'settings.apiKeys.modal.name': 'Nome', diff --git a/shared/src/i18n/it/storage.ts b/shared/src/i18n/it/storage.ts index d8275b142..521e21972 100644 --- a/shared/src/i18n/it/storage.ts +++ b/shared/src/i18n/it/storage.ts @@ -125,7 +125,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Spostare gli oggetti esistenti nel nuovo backend?', - 'storage.migrate.promptLine': '{category}: {objects} oggetti ({size}) da {from} a {to}', + 'storage.migrate.promptLine': '{category}: {count} oggetti ({size}) da {from} a {to}', + 'storage.migrate.promptLine.one': '{category}: {count} oggetto ({size}) da {from} a {to}', 'storage.migrate.promptLineUnknown': '{category}: dimensione sconosciuta (utilizzo non ancora calcolato) da {from} a {to}', 'storage.migrate.move': 'Sposta oggetti esistenti', @@ -135,7 +136,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} non riusciti — questi oggetti non sono stati copiati nel nuovo backend', 'storage.migrate.failed': 'Spostamento non riuscito: {error} — la categoria non è stata cambiata', 'storage.migrate.cancelled': 'Spostamento annullato — nulla è stato cambiato', - 'storage.migrate.reclaimable': '{objects} oggetti ({size}) restano su {from} — recuperali manualmente', + 'storage.migrate.reclaimable': '{count} oggetti ({size}) restano su {from}, recuperali manualmente', + 'storage.migrate.reclaimable.one': '{count} oggetto ({size}) resta su {from}, recuperalo manualmente', 'storage.migrate.cancel': 'Annulla spostamento', 'storage.migrate.promptCancel': 'Annulla', 'storage.migrate.queued': 'In coda: {categories}', diff --git a/shared/src/i18n/ja/collab.ts b/shared/src/i18n/ja/collab.ts index 47efbb4df..33bc6bba5 100644 --- a/shared/src/i18n/ja/collab.ts +++ b/shared/src/i18n/ja/collab.ts @@ -96,7 +96,7 @@ const collab: TranslationStrings = { 'collab.polls.options': '選択肢', 'collab.polls.delete': '削除', 'collab.polls.closedSection': '終了', - 'collab.chat.imageLimit': '1件のメッセージに添付できる画像は{max}枚までです', + 'collab.chat.imageLimit': '1件のメッセージに添付できる画像は{count}枚までです', 'collab.chat.uploading': 'アップロード中 {percent}%', 'collab.chat.attachImages': '画像を添付', 'collab.chat.attachedImage': '添付画像', diff --git a/shared/src/i18n/ja/dashboard.ts b/shared/src/i18n/ja/dashboard.ts index 122dd933d..d28a10991 100644 --- a/shared/src/i18n/ja/dashboard.ts +++ b/shared/src/i18n/ja/dashboard.ts @@ -107,7 +107,7 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': '{photographer}さんのUnsplash写真を使用', 'dashboard.titleRequired': 'タイトルは必須です', 'dashboard.endDateError': '終了日は開始日より後にしてください', - 'dashboard.tripTooLong': '旅行の期間は最大{days}日までです', + 'dashboard.tripTooLong': '旅行の期間は最大{count}日までです', 'dashboard.dateShiftTitle': '新しい開始日', 'dashboard.dateShiftIntro': '旅行の開始日が変更されました。予定を新しい日程にどのように合わせますか?', 'dashboard.dateShiftKeepBookings': '予約を元の日付のままにする', diff --git a/shared/src/i18n/ja/dawarich.ts b/shared/src/i18n/ja/dawarich.ts index 47c9e27ab..71ae8c96b 100644 --- a/shared/src/i18n/ja/dawarich.ts +++ b/shared/src/i18n/ja/dawarich.ts @@ -116,7 +116,7 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'エントリーを追加', 'dawarich.accept.confirm.bucket_list': 'チェックを付ける', 'dawarich.accept.recorded': '記録:{from}から{to}まで', - 'dawarich.accept.duration': '{minutes}分', + 'dawarich.accept.duration': '{count}分', 'dawarich.accept.name': '名前', 'dawarich.accept.date': '日付', 'dawarich.accept.from': '到着', @@ -175,7 +175,7 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'もう一度確認', 'dawarich.bucket.metersAway': '{meters} m 先', 'dawarich.bucket.kilometersAway': '{km} km 先', - 'dawarich.bucket.rule': '{meters} m 以内に {minutes} 分以上滞在すると、願いがかなったとみなします。', + 'dawarich.bucket.rule': '{meters} m 以内に {count} 分以上滞在すると、願いがかなったとみなします。', 'dawarich.journey.dayStays.other': 'Dawarich の滞在 {count} 件', }; diff --git a/shared/src/i18n/ja/roadtrip.ts b/shared/src/i18n/ja/roadtrip.ts index 08a84c8e7..72b0f26cf 100644 --- a/shared/src/i18n/ja/roadtrip.ts +++ b/shared/src/i18n/ja/roadtrip.ts @@ -56,7 +56,7 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': '毎日の休憩を計算するには、すべての接続ルートが必要です。', 'roadtrip.window.conflict': '固定時刻が移動や休憩と両立しません。手動の時刻は保持され、自動計画は一時停止しています。', - 'roadtrip.window.tooLong': 'この設定では{days}日を超える追加日数が必要です。一日の移動時間を長くしてください。', + 'roadtrip.window.tooLong': 'この設定では{count}日を超える追加日数が必要です。一日の移動時間を長くしてください。', 'roadtrip.mode.roadtrip': 'ロードトリップ', 'roadtrip.mode.days': '日ごと', 'roadtrip.mode.label': 'プラン表示', @@ -102,8 +102,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': '分', 'roadtrip.limit.off': 'なし', 'roadtrip.limit.blockNote': '1ブロック = {distance}', - 'roadtrip.limit.echoOver': '{days}日のうち{over}日が上限超過。', - 'roadtrip.limit.echoNone': '{days}日すべてが上限内です。', + 'roadtrip.limit.echoOver': '{count}日のうち{over}日が上限超過。', + 'roadtrip.limit.echoNone': '{count}日すべてが上限内です。', 'roadtrip.limit.echoDry': '到達できる立ち寄り先のない区間が{count}件。', 'roadtrip.limit.echoEmpty': 'ロードトリップを開くと効果が見えます。', 'roadtrip.limit.specToggle': '車のデータから計算する', @@ -258,8 +258,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': '運転時間からの計算値', 'roadtrip.stop.endIsLeave': 'ロードトリップでは、この時刻に出発します。', 'roadtrip.via.hint': 'ドラッグでルートを変え、右クリックで削除', - 'roadtrip.warn.late': '設定した時刻より {minutes} 分遅く到着します', - 'roadtrip.warn.missedLeave': '設定した出発時刻より {minutes} 分遅く到着します', + 'roadtrip.warn.late': '設定した時刻より {count} 分遅く到着します', + 'roadtrip.warn.missedLeave': '設定した出発時刻より {count} 分遅く到着します', 'roadtrip.warn.overnight': '翌日', 'roadtrip.spill.title': '{number}日目から', 'roadtrip.spill.departs': '{time} 発', diff --git a/shared/src/i18n/ja/settings.ts b/shared/src/i18n/ja/settings.ts index 874b9d1df..d89c3271e 100644 --- a/shared/src/i18n/ja/settings.ts +++ b/shared/src/i18n/ja/settings.ts @@ -592,7 +592,7 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'エンドポイント', 'settings.apiKeys.neverUsed': '未使用', 'settings.apiKeys.loadFailed': 'キーを読み込めませんでした。ページを再読み込みしてもう一度お試しください。', - 'settings.apiKeys.limitReached': 'キーは {max} 個あり、1 アカウントの上限に達しています。新しく作成するには、使わなくなったキーを削除してください。', + 'settings.apiKeys.limitReached': 'キーは {count} 個あり、1 アカウントの上限に達しています。新しく作成するには、使わなくなったキーを削除してください。', 'settings.apiKeys.copyFailed': 'コピーできませんでした。テキストを選択して手動でコピーしてください。', 'settings.apiKeys.modal.createTitle': 'API キーを作成', 'settings.apiKeys.modal.name': '名前', diff --git a/shared/src/i18n/ja/storage.ts b/shared/src/i18n/ja/storage.ts index 09de172f6..417eaafb8 100644 --- a/shared/src/i18n/ja/storage.ts +++ b/shared/src/i18n/ja/storage.ts @@ -121,7 +121,7 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': '既存のオブジェクトを新しいバックエンドに移動しますか?', - 'storage.migrate.promptLine': '{category}: {from}から{to}へ {objects}件のオブジェクト({size})', + 'storage.migrate.promptLine': '{category}: {from}から{to}へ {count}件のオブジェクト({size})', 'storage.migrate.promptLineUnknown': '{category}: サイズ不明(使用量スキャン未実施){from}から{to}へ', 'storage.migrate.move': '既存のオブジェクトを移動', 'storage.migrate.routeOnly': '新規書き込みのみ切り替える', @@ -130,7 +130,7 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed}件失敗 — それらのオブジェクトは新しいバックエンドにコピーされませんでした', 'storage.migrate.failed': '移動に失敗しました: {error} — カテゴリは切り替えられませんでした', 'storage.migrate.cancelled': '移動をキャンセルしました — 何も切り替えられていません', - 'storage.migrate.reclaimable': '{objects}件のオブジェクト({size})が{from}に残っています — 手動で回収してください', + 'storage.migrate.reclaimable': '{count}件のオブジェクト({size})が{from}に残っています。手動で回収してください', 'storage.migrate.cancel': '移動をキャンセル', 'storage.migrate.promptCancel': 'キャンセル', 'storage.migrate.queued': 'キュー待ち: {categories}', diff --git a/shared/src/i18n/ko/collab.ts b/shared/src/i18n/ko/collab.ts index ef9cf6ddb..f9009b984 100644 --- a/shared/src/i18n/ko/collab.ts +++ b/shared/src/i18n/ko/collab.ts @@ -96,7 +96,7 @@ const collab: TranslationStrings = { 'collab.polls.options': '옵션', 'collab.polls.delete': '삭제', 'collab.polls.closedSection': '종료됨', - 'collab.chat.imageLimit': '메시지당 최대 {max}장의 이미지를 첨부할 수 있습니다', + 'collab.chat.imageLimit': '메시지당 최대 {count}장의 이미지를 첨부할 수 있습니다', 'collab.chat.uploading': '업로드 중 {percent}%', 'collab.chat.attachImages': '이미지 첨부', 'collab.chat.attachedImage': '첨부된 이미지', diff --git a/shared/src/i18n/ko/dashboard.ts b/shared/src/i18n/ko/dashboard.ts index 92da95cc8..f5ee267e2 100644 --- a/shared/src/i18n/ko/dashboard.ts +++ b/shared/src/i18n/ko/dashboard.ts @@ -107,7 +107,7 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': '{photographer}님의 Unsplash 사진 사용', 'dashboard.titleRequired': '제목을 입력하세요', 'dashboard.endDateError': '종료일은 시작일 이후여야 합니다', - 'dashboard.tripTooLong': '여행 기간은 최대 {days}일까지입니다', + 'dashboard.tripTooLong': '여행 기간은 최대 {count}일까지입니다', 'dashboard.dateShiftTitle': '새 시작일', 'dashboard.dateShiftIntro': '여행 시작일이 변경되었습니다. 계획을 새 날짜에 어떻게 맞출까요?', 'dashboard.dateShiftKeepBookings': '예약은 원래 날짜에 유지', diff --git a/shared/src/i18n/ko/dawarich.ts b/shared/src/i18n/ko/dawarich.ts index 9b43f21fb..032f848ab 100644 --- a/shared/src/i18n/ko/dawarich.ts +++ b/shared/src/i18n/ko/dawarich.ts @@ -112,7 +112,7 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': '항목 추가', 'dawarich.accept.confirm.bucket_list': '완료 처리', 'dawarich.accept.recorded': '{from}부터 {to}까지 기록됨', - 'dawarich.accept.duration': '{minutes}분', + 'dawarich.accept.duration': '{count}분', 'dawarich.accept.name': '이름', 'dawarich.accept.date': '날짜', 'dawarich.accept.from': '도착', @@ -170,7 +170,7 @@ const dawarich: TranslationStrings = { 'dawarich.again': '다시 확인', 'dawarich.bucket.metersAway': '{meters} m 거리', 'dawarich.bucket.kilometersAway': '{km} km 거리', - 'dawarich.bucket.rule': '{meters} m 이내에서 {minutes}분 이상 머무르면 소원을 이룬 것으로 봅니다.', + 'dawarich.bucket.rule': '{meters} m 이내에서 {count}분 이상 머무르면 소원을 이룬 것으로 봅니다.', 'dawarich.journey.dayStays.other': 'Dawarich 방문 기록 {count}건', }; diff --git a/shared/src/i18n/ko/roadtrip.ts b/shared/src/i18n/ko/roadtrip.ts index 2a36ff761..b4aff5547 100644 --- a/shared/src/i18n/ko/roadtrip.ts +++ b/shared/src/i18n/ko/roadtrip.ts @@ -55,7 +55,7 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': '매일 휴식을 계산하려면 모든 연결 경로가 필요합니다.', 'roadtrip.window.conflict': '고정 시간이 이동 및 휴식과 충돌합니다. 수동 시간은 유지되며 자동 계획이 일시 중지됩니다.', - 'roadtrip.window.tooLong': '이 시간 설정에는 {days}일이 넘는 추가 날짜가 필요합니다. 일일 이동 시간을 늘려 주세요.', + 'roadtrip.window.tooLong': '이 시간 설정에는 {count}일이 넘는 추가 날짜가 필요합니다. 일일 이동 시간을 늘려 주세요.', 'roadtrip.mode.roadtrip': '로드트립', 'roadtrip.mode.days': '일자별', 'roadtrip.mode.label': '계획 보기', @@ -101,8 +101,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': '분', 'roadtrip.limit.off': '끔', 'roadtrip.limit.blockNote': '한 블록 = {distance}', - 'roadtrip.limit.echoOver': '{days}일 중 {over}일이 한도 초과.', - 'roadtrip.limit.echoNone': '{days}일 모두 한도 안입니다.', + 'roadtrip.limit.echoOver': '{count}일 중 {over}일이 한도 초과.', + 'roadtrip.limit.echoNone': '{count}일 모두 한도 안입니다.', 'roadtrip.limit.echoDry': '도달 가능한 정차지가 없는 구간 {count}곳.', 'roadtrip.limit.echoEmpty': '로드트립을 열면 효과가 보입니다.', 'roadtrip.limit.specToggle': '차량 데이터로 계산하기', @@ -255,8 +255,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': '주행 시간으로 계산됨', 'roadtrip.stop.endIsLeave': '로드트립에서는 이 시간에 출발합니다.', 'roadtrip.via.hint': '끌어서 경로를 바꾸고, 오른쪽 클릭으로 삭제', - 'roadtrip.warn.late': '지정한 시간보다 {minutes}분 늦게 도착', - 'roadtrip.warn.missedLeave': '지정한 출발 시간보다 {minutes}분 늦게 도착', + 'roadtrip.warn.late': '지정한 시간보다 {count}분 늦게 도착', + 'roadtrip.warn.missedLeave': '지정한 출발 시간보다 {count}분 늦게 도착', 'roadtrip.warn.overnight': '다음 날', 'roadtrip.spill.title': '{number}일차에서', 'roadtrip.spill.departs': '{time} 출발', diff --git a/shared/src/i18n/ko/settings.ts b/shared/src/i18n/ko/settings.ts index d332872c5..898017f81 100644 --- a/shared/src/i18n/ko/settings.ts +++ b/shared/src/i18n/ko/settings.ts @@ -609,7 +609,7 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': '엔드포인트', 'settings.apiKeys.neverUsed': '사용한 적 없음', 'settings.apiKeys.loadFailed': '키를 불러오지 못했습니다. 페이지를 새로 고친 뒤 다시 시도하세요.', - 'settings.apiKeys.limitReached': '키가 {max}개로 계정당 최대 개수입니다. 새 키를 만들려면 더 이상 쓰지 않는 키를 삭제하세요.', + 'settings.apiKeys.limitReached': '키가 {count}개로 계정당 최대 개수입니다. 새 키를 만들려면 더 이상 쓰지 않는 키를 삭제하세요.', 'settings.apiKeys.copyFailed': '복사하지 못했습니다. 텍스트를 선택해 직접 복사하세요.', 'settings.apiKeys.modal.createTitle': 'API 키 만들기', 'settings.apiKeys.modal.name': '이름', diff --git a/shared/src/i18n/ko/storage.ts b/shared/src/i18n/ko/storage.ts index 856f02ceb..f88a63b37 100644 --- a/shared/src/i18n/ko/storage.ts +++ b/shared/src/i18n/ko/storage.ts @@ -118,7 +118,7 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': '기존 객체를 새 백엔드로 이동할까요?', - 'storage.migrate.promptLine': '{category}: {from}에서 {to}(으)로 {objects}개 객체 ({size})', + 'storage.migrate.promptLine': '{category}: {from}에서 {to}(으)로 {count}개 객체 ({size})', 'storage.migrate.promptLineUnknown': '{category}: 크기 알 수 없음 (아직 사용량 스캔 안 됨) {from}에서 {to}(으)로', 'storage.migrate.move': '기존 객체 이동', 'storage.migrate.routeOnly': '새 쓰기만 전환', @@ -127,7 +127,7 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed}개 실패 — 해당 객체는 새 백엔드로 복사되지 않았습니다', 'storage.migrate.failed': '이동 실패: {error} — 카테고리가 전환되지 않았습니다', 'storage.migrate.cancelled': '이동이 취소되었습니다 — 아무것도 전환되지 않았습니다', - 'storage.migrate.reclaimable': '{objects}개 객체 ({size})가 {from}에 남아 있습니다 — 수동으로 회수하세요', + 'storage.migrate.reclaimable': '{count}개 객체 ({size})가 {from}에 남아 있습니다. 수동으로 회수하세요', 'storage.migrate.cancel': '이동 취소', 'storage.migrate.promptCancel': '취소', 'storage.migrate.queued': '대기 중: {categories}', diff --git a/shared/src/i18n/nl/collab.ts b/shared/src/i18n/nl/collab.ts index 72b3db932..5b1b0d92c 100644 --- a/shared/src/i18n/nl/collab.ts +++ b/shared/src/i18n/nl/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Opties', 'collab.polls.delete': 'Verwijderen', 'collab.polls.closedSection': 'Gesloten', - 'collab.chat.imageLimit': 'Je kunt maximaal {max} afbeeldingen per bericht toevoegen', + 'collab.chat.imageLimit': 'Je kunt maximaal {count} afbeeldingen per bericht toevoegen', + 'collab.chat.imageLimit.one': 'Je kunt maar {count} afbeelding per bericht toevoegen', 'collab.chat.uploading': 'Uploaden {percent}%', 'collab.chat.attachImages': 'Afbeeldingen toevoegen', 'collab.chat.attachedImage': 'Bijgevoegde afbeelding', diff --git a/shared/src/i18n/nl/dashboard.ts b/shared/src/i18n/nl/dashboard.ts index e26827969..1b0eae22d 100644 --- a/shared/src/i18n/nl/dashboard.ts +++ b/shared/src/i18n/nl/dashboard.ts @@ -99,7 +99,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Unsplash-foto van {photographer} gebruiken', 'dashboard.titleRequired': 'Titel is verplicht', 'dashboard.endDateError': 'Einddatum moet na de startdatum liggen', - 'dashboard.tripTooLong': 'Een reis kan maximaal {days} dagen duren', + 'dashboard.tripTooLong': 'Een reis kan maximaal {count} dagen duren', + 'dashboard.tripTooLong.one': 'Een reis kan maximaal {count} dag duren', 'dashboard.dateShiftTitle': 'Nieuwe startdatum', 'dashboard.dateShiftIntro': 'Je hebt gewijzigd wanneer deze reis begint. Hoe moeten je plannen de nieuwe datums volgen?', diff --git a/shared/src/i18n/nl/dawarich.ts b/shared/src/i18n/nl/dawarich.ts index 37936ee87..b18145c0b 100644 --- a/shared/src/i18n/nl/dawarich.ts +++ b/shared/src/i18n/nl/dawarich.ts @@ -118,7 +118,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Vermelding toevoegen', 'dawarich.accept.confirm.bucket_list': 'Afvinken', 'dawarich.accept.recorded': 'Opgenomen van {from} tot {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Naam', 'dawarich.accept.date': 'Datum', 'dawarich.accept.from': 'Aangekomen', @@ -183,7 +184,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Opnieuw controleren', 'dawarich.bucket.metersAway': '{meters} m verderop', 'dawarich.bucket.kilometersAway': '{km} km verderop', - 'dawarich.bucket.rule': 'Een wens telt als bereikt binnen {meters} m en na {minutes} minuten ter plaatse.', + 'dawarich.bucket.rule': 'Een wens telt als bereikt binnen {meters} m en na {count} minuten ter plaatse.', + 'dawarich.bucket.rule.one': 'Een wens telt als bereikt binnen {meters} m en na {count} minuut ter plaatse.', 'dawarich.journey.dayStays.other': '{count} bezoeken uit Dawarich', 'dawarich.journey.dayStays.one': '{count} bezoek uit Dawarich', diff --git a/shared/src/i18n/nl/roadtrip.ts b/shared/src/i18n/nl/roadtrip.ts index ee9fc5000..e2b713e57 100644 --- a/shared/src/i18n/nl/roadtrip.ts +++ b/shared/src/i18n/nl/roadtrip.ts @@ -56,7 +56,8 @@ const roadtrip: TranslationStrings = { 'Dagelijkse pauzes kunnen pas worden berekend als alle verbindingsroutes beschikbaar zijn.', 'roadtrip.window.conflict': 'Een vaste tijd past niet bij de rit en pauzes. Handmatige tijden blijven behouden; automatische planning is gepauzeerd.', - 'roadtrip.window.tooLong': 'Deze tijden vragen meer dan {days} extra dagen. Kies een langer dagelijks tijdvak.', + 'roadtrip.window.tooLong': 'Deze tijden vragen meer dan {count} extra dagen. Kies een langer dagelijks tijdvak.', + 'roadtrip.window.tooLong.one': 'Deze tijden vragen meer dan {count} extra dag. Kies een langer dagelijks tijdvak.', 'roadtrip.mode.roadtrip': 'Roadtrip', 'roadtrip.mode.days': 'Dagen', 'roadtrip.mode.label': 'Planweergave', @@ -104,8 +105,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'uit', 'roadtrip.limit.blockNote': 'Eén blok = {distance}', - 'roadtrip.limit.echoOver': '{over} van {days} dagen boven je limiet.', - 'roadtrip.limit.echoNone': 'Alle {days} dagen binnen je limieten.', + 'roadtrip.limit.echoOver': '{over} van {count} dagen boven je limiet.', + 'roadtrip.limit.echoOver.one': '{over} van {count} dag boven je limiet.', + 'roadtrip.limit.echoNone': 'Alle {count} dagen binnen je limieten.', + 'roadtrip.limit.echoNone.one': 'De ene dag binnen je limieten.', 'roadtrip.limit.echoDry': '{count} stukken zonder bereikbare stop.', 'roadtrip.limit.echoDry.one': '{count} stuk zonder bereikbare stop.', 'roadtrip.limit.echoEmpty': 'Open een roadtrip om het effect te zien.', @@ -274,8 +277,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Berekend uit de rit', 'roadtrip.stop.endIsLeave': 'Op de roadtrip vertrek je op dit tijdstip.', 'roadtrip.via.hint': 'Sleep om de route te hervormen, rechtsklik om te verwijderen', - 'roadtrip.warn.late': 'Aankomst {minutes} min na de gezette tijd', - 'roadtrip.warn.missedLeave': 'Aankomst {minutes} min na de gezette vertrektijd', + 'roadtrip.warn.late': 'Aankomst {count} min na de gezette tijd', + 'roadtrip.warn.late.one': 'Aankomst {count} min na de gezette tijd', + 'roadtrip.warn.missedLeave': 'Aankomst {count} min na de gezette vertrektijd', + 'roadtrip.warn.missedLeave.one': 'Aankomst {count} min na de gezette vertrektijd', 'roadtrip.warn.overnight': 'Volgende dag', 'roadtrip.spill.title': 'Van dag {number}', 'roadtrip.spill.departs': 'vertrek {time}', diff --git a/shared/src/i18n/nl/settings.ts b/shared/src/i18n/nl/settings.ts index 8751d90a1..fa426926f 100644 --- a/shared/src/i18n/nl/settings.ts +++ b/shared/src/i18n/nl/settings.ts @@ -633,7 +633,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Eindpunt', 'settings.apiKeys.neverUsed': 'nooit gebruikt', 'settings.apiKeys.loadFailed': 'Je sleutels konden niet worden geladen. Herlaad de pagina om het opnieuw te proberen.', - 'settings.apiKeys.limitReached': 'Je hebt {max} sleutels, het maximum per account. Verwijder er een die je niet meer gebruikt om een nieuwe te maken.', + 'settings.apiKeys.limitReached': + 'Je hebt {count} sleutels, het maximum per account. Verwijder er een die je niet meer gebruikt om een nieuwe te maken.', + 'settings.apiKeys.limitReached.one': + 'Je hebt {count} sleutel, het maximum per account. Verwijder hem als je hem niet meer gebruikt om een nieuwe te maken.', 'settings.apiKeys.copyFailed': 'Kopiëren is mislukt. Selecteer de tekst en kopieer hem handmatig.', 'settings.apiKeys.modal.createTitle': 'API-sleutel aanmaken', 'settings.apiKeys.modal.name': 'Naam', diff --git a/shared/src/i18n/nl/storage.ts b/shared/src/i18n/nl/storage.ts index 043ea434b..06b54b7fa 100644 --- a/shared/src/i18n/nl/storage.ts +++ b/shared/src/i18n/nl/storage.ts @@ -125,7 +125,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Bestaande objecten naar de nieuwe backend verplaatsen?', - 'storage.migrate.promptLine': '{category}: {objects} objecten ({size}) van {from} naar {to}', + 'storage.migrate.promptLine': '{category}: {count} objecten ({size}) van {from} naar {to}', + 'storage.migrate.promptLine.one': '{category}: {count} object ({size}) van {from} naar {to}', 'storage.migrate.promptLineUnknown': '{category}: onbekende grootte (gebruik nog niet gescand) van {from} naar {to}', 'storage.migrate.move': 'Bestaande objecten verplaatsen', 'storage.migrate.routeOnly': 'Alleen nieuwe schrijfacties omleiden', @@ -134,7 +135,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} mislukt — die objecten zijn niet gekopieerd naar de nieuwe backend', 'storage.migrate.failed': 'Verplaatsen mislukt: {error} — de categorie is niet omgezet', 'storage.migrate.cancelled': 'Verplaatsen geannuleerd — er is niets omgezet', - 'storage.migrate.reclaimable': '{objects} objecten ({size}) blijven op {from} — handmatig terugwinnen', + 'storage.migrate.reclaimable': '{count} objecten ({size}) blijven op {from}, handmatig terugwinnen', + 'storage.migrate.reclaimable.one': '{count} object ({size}) blijft op {from}, handmatig terugwinnen', 'storage.migrate.cancel': 'Verplaatsen annuleren', 'storage.migrate.promptCancel': 'Annuleren', 'storage.migrate.queued': 'In wachtrij: {categories}', diff --git a/shared/src/i18n/pl/collab.ts b/shared/src/i18n/pl/collab.ts index d20301cf7..6d1aa0257 100644 --- a/shared/src/i18n/pl/collab.ts +++ b/shared/src/i18n/pl/collab.ts @@ -108,7 +108,10 @@ const collab: TranslationStrings = { 'collab.polls.delete': 'Usuń', 'collab.polls.closedSection': 'Zamknięte', 'collab.chat.reply': 'Odpowiedz', - 'collab.chat.imageLimit': 'Do jednej wiadomości możesz dołączyć maksymalnie {max} obrazów', + 'collab.chat.imageLimit': 'Do jednej wiadomości możesz dołączyć maksymalnie {count} obrazu', + 'collab.chat.imageLimit.one': 'Do jednej wiadomości możesz dołączyć tylko {count} obraz', + 'collab.chat.imageLimit.few': 'Do jednej wiadomości możesz dołączyć maksymalnie {count} obrazy', + 'collab.chat.imageLimit.many': 'Do jednej wiadomości możesz dołączyć maksymalnie {count} obrazów', 'collab.chat.uploading': 'Przesyłanie {percent}%', 'collab.chat.attachImages': 'Dołącz obrazy', 'collab.chat.attachedImage': 'Załączony obraz', diff --git a/shared/src/i18n/pl/dashboard.ts b/shared/src/i18n/pl/dashboard.ts index 2250f3924..f22785b2d 100644 --- a/shared/src/i18n/pl/dashboard.ts +++ b/shared/src/i18n/pl/dashboard.ts @@ -105,7 +105,10 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Użyj zdjęcia z Unsplash autorstwa {photographer}', 'dashboard.titleRequired': 'Nazwa podróży jest wymagana', 'dashboard.endDateError': 'Data zakończenia musi być po dacie rozpoczęcia', - 'dashboard.tripTooLong': 'Podróż może trwać maksymalnie {days} dni', + 'dashboard.tripTooLong': 'Podróż może trwać maksymalnie {count} dnia', + 'dashboard.tripTooLong.one': 'Podróż może trwać maksymalnie {count} dzień', + 'dashboard.tripTooLong.few': 'Podróż może trwać maksymalnie {count} dni', + 'dashboard.tripTooLong.many': 'Podróż może trwać maksymalnie {count} dni', 'dashboard.dateShiftTitle': 'Nowa data rozpoczęcia', 'dashboard.dateShiftIntro': 'Data rozpoczęcia podróży została zmieniona. Jak plany mają dostosować się do nowych dat?', diff --git a/shared/src/i18n/pl/dawarich.ts b/shared/src/i18n/pl/dawarich.ts index ec62e5c1e..b879f56f0 100644 --- a/shared/src/i18n/pl/dawarich.ts +++ b/shared/src/i18n/pl/dawarich.ts @@ -124,7 +124,10 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Dodaj wpis', 'dawarich.accept.confirm.bucket_list': 'Odhacz', 'dawarich.accept.recorded': 'Zapisano od {from} do {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', + 'dawarich.accept.duration.few': '{count} min', + 'dawarich.accept.duration.many': '{count} min', 'dawarich.accept.name': 'Nazwa', 'dawarich.accept.date': 'Data', 'dawarich.accept.from': 'Przyjazd', @@ -203,7 +206,13 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Sprawdź ponownie', 'dawarich.bucket.metersAway': '{meters} m stąd', 'dawarich.bucket.kilometersAway': '{km} km stąd', - 'dawarich.bucket.rule': 'Marzenie uznaje się za spełnione w promieniu {meters} m i po {minutes} minutach na miejscu.', + 'dawarich.bucket.rule': 'Marzenie uznaje się za spełnione w promieniu {meters} m i po {count} minuty na miejscu.', + 'dawarich.bucket.rule.one': + 'Marzenie uznaje się za spełnione w promieniu {meters} m i po {count} minucie na miejscu.', + 'dawarich.bucket.rule.few': + 'Marzenie uznaje się za spełnione w promieniu {meters} m i po {count} minutach na miejscu.', + 'dawarich.bucket.rule.many': + 'Marzenie uznaje się za spełnione w promieniu {meters} m i po {count} minutach na miejscu.', 'dawarich.journey.dayStays.other': '{count} pobytu z Dawarich', 'dawarich.journey.dayStays.one': '{count} pobyt z Dawarich', 'dawarich.journey.dayStays.few': '{count} pobyty z Dawarich', diff --git a/shared/src/i18n/pl/roadtrip.ts b/shared/src/i18n/pl/roadtrip.ts index 38b88f584..d9ee02d37 100644 --- a/shared/src/i18n/pl/roadtrip.ts +++ b/shared/src/i18n/pl/roadtrip.ts @@ -55,7 +55,12 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Obliczenie codziennych przerw wymaga wszystkich tras łączących.', 'roadtrip.window.conflict': 'Stała godzina koliduje z jazdą i przerwami. Ręczne godziny pozostają bez zmian; automatyczne planowanie jest wstrzymane.', - 'roadtrip.window.tooLong': 'Te godziny wymagają ponad {days} dodatkowych dni. Wybierz dłuższy dzienny przedział.', + 'roadtrip.window.tooLong': 'Te godziny wymagają ponad {count} dodatkowego dnia. Wybierz dłuższy dzienny przedział.', + 'roadtrip.window.tooLong.one': + 'Te godziny wymagają ponad {count} dodatkowy dzień. Wybierz dłuższy dzienny przedział.', + 'roadtrip.window.tooLong.few': 'Te godziny wymagają ponad {count} dodatkowe dni. Wybierz dłuższy dzienny przedział.', + 'roadtrip.window.tooLong.many': + 'Te godziny wymagają ponad {count} dodatkowych dni. Wybierz dłuższy dzienny przedział.', 'roadtrip.mode.roadtrip': 'Podróż samochodowa', 'roadtrip.mode.days': 'Dni', 'roadtrip.mode.label': 'Widok planu', @@ -104,8 +109,14 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'wył.', 'roadtrip.limit.blockNote': 'Jeden blok = {distance}', - 'roadtrip.limit.echoOver': '{over} z {days} dni ponad limit.', - 'roadtrip.limit.echoNone': 'Wszystkie {days} dni w granicach.', + 'roadtrip.limit.echoOver': '{over} z {count} dnia ponad limit.', + 'roadtrip.limit.echoOver.one': '{over} z {count} dnia ponad limit.', + 'roadtrip.limit.echoOver.few': '{over} z {count} dni ponad limit.', + 'roadtrip.limit.echoOver.many': '{over} z {count} dni ponad limit.', + 'roadtrip.limit.echoNone': 'Wszystkie {count} dni w granicach.', + 'roadtrip.limit.echoNone.one': 'Jedyny dzień w granicach.', + 'roadtrip.limit.echoNone.few': 'Wszystkie {count} dni w granicach.', + 'roadtrip.limit.echoNone.many': 'Wszystkie {count} dni w granicach.', 'roadtrip.limit.echoDry': '{count} odcinka bez osiągalnego postoju.', 'roadtrip.limit.echoDry.one': '{count} odcinek bez osiągalnego postoju.', 'roadtrip.limit.echoDry.few': '{count} odcinki bez osiągalnego postoju.', @@ -295,8 +306,14 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Obliczone na podstawie jazdy', 'roadtrip.stop.endIsLeave': 'W podróży samochodowej odjazd następuje o tej godzinie.', 'roadtrip.via.hint': 'Przeciągnij, aby zmienić trasę, kliknij prawym, aby usunąć', - 'roadtrip.warn.late': 'Przyjazd {minutes} min po ustawionej godzinie', - 'roadtrip.warn.missedLeave': 'Przyjazd {minutes} min po ustawionej godzinie odjazdu', + 'roadtrip.warn.late': 'Przyjazd {count} min po ustawionej godzinie', + 'roadtrip.warn.late.one': 'Przyjazd {count} min po ustawionej godzinie', + 'roadtrip.warn.late.few': 'Przyjazd {count} min po ustawionej godzinie', + 'roadtrip.warn.late.many': 'Przyjazd {count} min po ustawionej godzinie', + 'roadtrip.warn.missedLeave': 'Przyjazd {count} min po ustawionej godzinie odjazdu', + 'roadtrip.warn.missedLeave.one': 'Przyjazd {count} min po ustawionej godzinie odjazdu', + 'roadtrip.warn.missedLeave.few': 'Przyjazd {count} min po ustawionej godzinie odjazdu', + 'roadtrip.warn.missedLeave.many': 'Przyjazd {count} min po ustawionej godzinie odjazdu', 'roadtrip.warn.overnight': 'Następny dzień', 'roadtrip.spill.title': 'Z dnia {number}', 'roadtrip.spill.departs': 'odjazd {time}', diff --git a/shared/src/i18n/pl/settings.ts b/shared/src/i18n/pl/settings.ts index 2e192f10c..df1e83ac6 100644 --- a/shared/src/i18n/pl/settings.ts +++ b/shared/src/i18n/pl/settings.ts @@ -643,7 +643,14 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Endpoint', 'settings.apiKeys.neverUsed': 'nigdy nieużywany', 'settings.apiKeys.loadFailed': 'Nie udało się wczytać kluczy. Odśwież stronę, aby spróbować ponownie.', - 'settings.apiKeys.limitReached': 'Masz {max} kluczy, czyli maksimum na konto. Usuń klucz, którego już nie używasz, aby utworzyć nowy.', + 'settings.apiKeys.limitReached': + 'Masz {count} klucza, czyli maksimum na konto. Usuń klucz, którego już nie używasz, aby utworzyć nowy.', + 'settings.apiKeys.limitReached.one': + 'Masz {count} klucz, czyli maksimum na konto. Usuń go, jeśli już go nie używasz, aby utworzyć nowy.', + 'settings.apiKeys.limitReached.few': + 'Masz {count} klucze, czyli maksimum na konto. Usuń klucz, którego już nie używasz, aby utworzyć nowy.', + 'settings.apiKeys.limitReached.many': + 'Masz {count} kluczy, czyli maksimum na konto. Usuń klucz, którego już nie używasz, aby utworzyć nowy.', 'settings.apiKeys.copyFailed': 'Nie udało się skopiować. Zaznacz tekst i skopiuj go ręcznie.', 'settings.apiKeys.modal.createTitle': 'Utwórz klucz API', 'settings.apiKeys.modal.name': 'Nazwa', diff --git a/shared/src/i18n/pl/storage.ts b/shared/src/i18n/pl/storage.ts index b96f34610..3a7b33468 100644 --- a/shared/src/i18n/pl/storage.ts +++ b/shared/src/i18n/pl/storage.ts @@ -125,7 +125,10 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Przenieść istniejące obiekty do nowego backendu?', - 'storage.migrate.promptLine': '{category}: {objects} obiektów ({size}) z {from} do {to}', + 'storage.migrate.promptLine': '{category}: {count} obiektu ({size}) z {from} do {to}', + 'storage.migrate.promptLine.one': '{category}: {count} obiekt ({size}) z {from} do {to}', + 'storage.migrate.promptLine.few': '{category}: {count} obiekty ({size}) z {from} do {to}', + 'storage.migrate.promptLine.many': '{category}: {count} obiektów ({size}) z {from} do {to}', 'storage.migrate.promptLineUnknown': '{category}: nieznany rozmiar (wykorzystanie jeszcze nie obliczone) z {from} do {to}', 'storage.migrate.move': 'Przenieś istniejące obiekty', @@ -135,7 +138,10 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} nie powiodło się — te obiekty nie zostały skopiowane do nowego backendu', 'storage.migrate.failed': 'Przenoszenie nie powiodło się: {error} — kategoria nie została przełączona', 'storage.migrate.cancelled': 'Przenoszenie anulowane — nic nie zostało przełączone', - 'storage.migrate.reclaimable': '{objects} obiektów ({size}) pozostaje na {from} — odzyskaj ręcznie', + 'storage.migrate.reclaimable': '{count} obiektu ({size}) pozostaje na {from}, odzyskaj ręcznie', + 'storage.migrate.reclaimable.one': '{count} obiekt ({size}) pozostaje na {from}, odzyskaj ręcznie', + 'storage.migrate.reclaimable.few': '{count} obiekty ({size}) pozostają na {from}, odzyskaj ręcznie', + 'storage.migrate.reclaimable.many': '{count} obiektów ({size}) pozostaje na {from}, odzyskaj ręcznie', 'storage.migrate.cancel': 'Anuluj przenoszenie', 'storage.migrate.promptCancel': 'Anuluj', 'storage.migrate.queued': 'W kolejce: {categories}', diff --git a/shared/src/i18n/ru/collab.ts b/shared/src/i18n/ru/collab.ts index 247487a66..6a8941064 100644 --- a/shared/src/i18n/ru/collab.ts +++ b/shared/src/i18n/ru/collab.ts @@ -108,7 +108,10 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Варианты', 'collab.polls.delete': 'Удалить', 'collab.polls.closedSection': 'Закрытые', - 'collab.chat.imageLimit': 'К одному сообщению можно прикрепить до {max} изображений', + 'collab.chat.imageLimit': 'К одному сообщению можно прикрепить до {count} изображения', + 'collab.chat.imageLimit.one': 'К одному сообщению можно прикрепить до {count} изображения', + 'collab.chat.imageLimit.few': 'К одному сообщению можно прикрепить до {count} изображений', + 'collab.chat.imageLimit.many': 'К одному сообщению можно прикрепить до {count} изображений', 'collab.chat.uploading': 'Загрузка {percent}%', 'collab.chat.attachImages': 'Прикрепить изображения', 'collab.chat.attachedImage': 'Прикреплённое изображение', diff --git a/shared/src/i18n/ru/dashboard.ts b/shared/src/i18n/ru/dashboard.ts index e06ce70b2..a9204711e 100644 --- a/shared/src/i18n/ru/dashboard.ts +++ b/shared/src/i18n/ru/dashboard.ts @@ -113,7 +113,10 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Использовать фото Unsplash от {photographer}', 'dashboard.titleRequired': 'Название обязательно', 'dashboard.endDateError': 'Дата окончания должна быть позже даты начала', - 'dashboard.tripTooLong': 'Поездка может длиться не более {days} дней', + 'dashboard.tripTooLong': 'Поездка может длиться не более {count} дня', + 'dashboard.tripTooLong.one': 'Поездка может длиться не более {count} дня', + 'dashboard.tripTooLong.few': 'Поездка может длиться не более {count} дней', + 'dashboard.tripTooLong.many': 'Поездка может длиться не более {count} дней', 'dashboard.dateShiftTitle': 'Новая дата начала', 'dashboard.dateShiftIntro': 'Вы изменили дату начала поездки. Как перенести ваши планы на новые даты?', 'dashboard.dateShiftKeepBookings': 'Оставить бронирования на своих датах', diff --git a/shared/src/i18n/ru/dawarich.ts b/shared/src/i18n/ru/dawarich.ts index 3bfba7739..47a544461 100644 --- a/shared/src/i18n/ru/dawarich.ts +++ b/shared/src/i18n/ru/dawarich.ts @@ -124,7 +124,10 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Добавить запись', 'dawarich.accept.confirm.bucket_list': 'Отметить', 'dawarich.accept.recorded': 'Записано с {from} по {to}', - 'dawarich.accept.duration': '{minutes} мин', + 'dawarich.accept.duration': '{count} мин', + 'dawarich.accept.duration.one': '{count} мин', + 'dawarich.accept.duration.few': '{count} мин', + 'dawarich.accept.duration.many': '{count} мин', 'dawarich.accept.name': 'Название', 'dawarich.accept.date': 'Дата', 'dawarich.accept.from': 'Прибытие', @@ -203,7 +206,10 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Проверить снова', 'dawarich.bucket.metersAway': 'в {meters} м', 'dawarich.bucket.kilometersAway': 'в {km} км', - 'dawarich.bucket.rule': 'Желание считается исполненным в пределах {meters} м и после {minutes} минут на месте.', + 'dawarich.bucket.rule': 'Желание считается исполненным в пределах {meters} м и после {count} минуты на месте.', + 'dawarich.bucket.rule.one': 'Желание считается исполненным в пределах {meters} м и после {count} минуты на месте.', + 'dawarich.bucket.rule.few': 'Желание считается исполненным в пределах {meters} м и после {count} минут на месте.', + 'dawarich.bucket.rule.many': 'Желание считается исполненным в пределах {meters} м и после {count} минут на месте.', 'dawarich.journey.dayStays.other': '{count} остановки из Dawarich', 'dawarich.journey.dayStays.one': '{count} остановка из Dawarich', 'dawarich.journey.dayStays.few': '{count} остановки из Dawarich', diff --git a/shared/src/i18n/ru/roadtrip.ts b/shared/src/i18n/ru/roadtrip.ts index b5da1bc94..f23f242aa 100644 --- a/shared/src/i18n/ru/roadtrip.ts +++ b/shared/src/i18n/ru/roadtrip.ts @@ -56,7 +56,13 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Фиксированное время противоречит поездке и перерывам. Ручные значения сохранены; автоматическое планирование приостановлено.', 'roadtrip.window.tooLong': - 'При этих настройках потребуется более {days} дополнительных дней. Увеличьте ежедневный интервал.', + 'При этих настройках потребуется более {count} дополнительного дня. Увеличьте ежедневный интервал.', + 'roadtrip.window.tooLong.one': + 'При этих настройках потребуется более {count} дополнительного дня. Увеличьте ежедневный интервал.', + 'roadtrip.window.tooLong.few': + 'При этих настройках потребуется более {count} дополнительных дней. Увеличьте ежедневный интервал.', + 'roadtrip.window.tooLong.many': + 'При этих настройках потребуется более {count} дополнительных дней. Увеличьте ежедневный интервал.', 'roadtrip.mode.roadtrip': 'Автопутешествие', 'roadtrip.mode.days': 'Дни', 'roadtrip.mode.label': 'Вид плана', @@ -105,8 +111,14 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'мин', 'roadtrip.limit.off': 'выкл', 'roadtrip.limit.blockNote': 'Один блок = {distance}', - 'roadtrip.limit.echoOver': '{over} из {days} дней сверх лимита.', - 'roadtrip.limit.echoNone': 'Все {days} дней в пределах лимитов.', + 'roadtrip.limit.echoOver': '{over} из {count} дня сверх лимита.', + 'roadtrip.limit.echoOver.one': '{over} из {count} дня сверх лимита.', + 'roadtrip.limit.echoOver.few': '{over} из {count} дней сверх лимита.', + 'roadtrip.limit.echoOver.many': '{over} из {count} дней сверх лимита.', + 'roadtrip.limit.echoNone': 'Все {count} дня в пределах лимитов.', + 'roadtrip.limit.echoNone.one': '{count} день в пределах лимитов.', + 'roadtrip.limit.echoNone.few': 'Все {count} дня в пределах лимитов.', + 'roadtrip.limit.echoNone.many': 'Все {count} дней в пределах лимитов.', 'roadtrip.limit.echoDry': '{count} участка без достижимой остановки.', 'roadtrip.limit.echoDry.one': '{count} участок без достижимой остановки.', 'roadtrip.limit.echoDry.few': '{count} участка без достижимой остановки.', @@ -300,8 +312,14 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Рассчитано по времени в пути', 'roadtrip.stop.endIsLeave': 'В автопутешествии отправление будет в это время.', 'roadtrip.via.hint': 'Перетащите, чтобы изменить маршрут, правый клик — удалить', - 'roadtrip.warn.late': 'Прибытие на {minutes} мин позже заданного времени', - 'roadtrip.warn.missedLeave': 'Прибытие на {minutes} мин позже заданного времени отправления', + 'roadtrip.warn.late': 'Прибытие на {count} мин позже заданного времени', + 'roadtrip.warn.late.one': 'Прибытие на {count} мин позже заданного времени', + 'roadtrip.warn.late.few': 'Прибытие на {count} мин позже заданного времени', + 'roadtrip.warn.late.many': 'Прибытие на {count} мин позже заданного времени', + 'roadtrip.warn.missedLeave': 'Прибытие на {count} мин позже заданного времени отправления', + 'roadtrip.warn.missedLeave.one': 'Прибытие на {count} мин позже заданного времени отправления', + 'roadtrip.warn.missedLeave.few': 'Прибытие на {count} мин позже заданного времени отправления', + 'roadtrip.warn.missedLeave.many': 'Прибытие на {count} мин позже заданного времени отправления', 'roadtrip.warn.overnight': 'Следующий день', 'roadtrip.spill.title': 'Со дня {number}', 'roadtrip.spill.departs': 'выезд {time}', diff --git a/shared/src/i18n/ru/settings.ts b/shared/src/i18n/ru/settings.ts index 8d0e7c53f..fafa5b186 100644 --- a/shared/src/i18n/ru/settings.ts +++ b/shared/src/i18n/ru/settings.ts @@ -648,7 +648,14 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Эндпоинт', 'settings.apiKeys.neverUsed': 'не использовался', 'settings.apiKeys.loadFailed': 'Не удалось загрузить ключи. Обновите страницу, чтобы попробовать снова.', - 'settings.apiKeys.limitReached': 'У вас {max} ключей, это максимум для одного аккаунта. Удалите ненужный ключ, чтобы создать новый.', + 'settings.apiKeys.limitReached': + 'У вас {count} ключа, это максимум для одного аккаунта. Удалите ненужный ключ, чтобы создать новый.', + 'settings.apiKeys.limitReached.one': + 'У вас {count} ключ, это максимум для одного аккаунта. Удалите ненужный ключ, чтобы создать новый.', + 'settings.apiKeys.limitReached.few': + 'У вас {count} ключа, это максимум для одного аккаунта. Удалите ненужный ключ, чтобы создать новый.', + 'settings.apiKeys.limitReached.many': + 'У вас {count} ключей, это максимум для одного аккаунта. Удалите ненужный ключ, чтобы создать новый.', 'settings.apiKeys.copyFailed': 'Не удалось скопировать. Выделите текст и скопируйте его вручную.', 'settings.apiKeys.modal.createTitle': 'Создать ключ API', 'settings.apiKeys.modal.name': 'Название', diff --git a/shared/src/i18n/ru/storage.ts b/shared/src/i18n/ru/storage.ts index 3f8207f28..5da8a7a82 100644 --- a/shared/src/i18n/ru/storage.ts +++ b/shared/src/i18n/ru/storage.ts @@ -127,7 +127,10 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Перенести существующие объекты на новый бэкенд?', - 'storage.migrate.promptLine': '{category}: {objects} объектов ({size}) из {from} в {to}', + 'storage.migrate.promptLine': '{category}: {count} объекта ({size}) из {from} в {to}', + 'storage.migrate.promptLine.one': '{category}: {count} объект ({size}) из {from} в {to}', + 'storage.migrate.promptLine.few': '{category}: {count} объекта ({size}) из {from} в {to}', + 'storage.migrate.promptLine.many': '{category}: {count} объектов ({size}) из {from} в {to}', 'storage.migrate.promptLineUnknown': '{category}: неизвестный размер (использование ещё не просканировано) из {from} в {to}', 'storage.migrate.move': 'Перенести существующие объекты', @@ -137,7 +140,10 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} не удалось — эти объекты не были скопированы на новый бэкенд', 'storage.migrate.failed': 'Перенос не удался: {error} — категория не была переключена', 'storage.migrate.cancelled': 'Перенос отменён — ничего не было переключено', - 'storage.migrate.reclaimable': '{objects} объектов ({size}) остаются на {from} — освободите вручную', + 'storage.migrate.reclaimable': '{count} объекта ({size}) остаются на {from}, освободите вручную', + 'storage.migrate.reclaimable.one': '{count} объект ({size}) остаётся на {from}, освободите вручную', + 'storage.migrate.reclaimable.few': '{count} объекта ({size}) остаются на {from}, освободите вручную', + 'storage.migrate.reclaimable.many': '{count} объектов ({size}) остаются на {from}, освободите вручную', 'storage.migrate.cancel': 'Отменить перенос', 'storage.migrate.promptCancel': 'Отмена', 'storage.migrate.queued': 'В очереди: {categories}', diff --git a/shared/src/i18n/sk/collab.ts b/shared/src/i18n/sk/collab.ts index fc3cd39ca..2d0fe1b60 100644 --- a/shared/src/i18n/sk/collab.ts +++ b/shared/src/i18n/sk/collab.ts @@ -104,7 +104,9 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Možnosti', 'collab.polls.delete': 'Vymazať', 'collab.polls.closedSection': 'Uzavreté', - 'collab.chat.imageLimit': 'K jednej správe môžete priložiť najviac {max} obrázkov', + 'collab.chat.imageLimit': 'K jednej správe môžete priložiť najviac {count} obrázkov', + 'collab.chat.imageLimit.one': 'K jednej správe môžete priložiť iba {count} obrázok', + 'collab.chat.imageLimit.few': 'K jednej správe môžete priložiť najviac {count} obrázky', 'collab.chat.uploading': 'Nahráva sa {percent} %', 'collab.chat.attachImages': 'Priložiť obrázky', 'collab.chat.attachedImage': 'Priložený obrázok', diff --git a/shared/src/i18n/sk/dashboard.ts b/shared/src/i18n/sk/dashboard.ts index d8909b808..577815ba9 100644 --- a/shared/src/i18n/sk/dashboard.ts +++ b/shared/src/i18n/sk/dashboard.ts @@ -120,7 +120,9 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Použiť fotku z Unsplash od {photographer}', 'dashboard.titleRequired': 'Názov je povinný', 'dashboard.endDateError': 'Dátum konca musí byť po dátume začiatku', - 'dashboard.tripTooLong': 'Cesta môže trvať najviac {days} dní', + 'dashboard.tripTooLong': 'Cesta môže trvať najviac {count} dní', + 'dashboard.tripTooLong.one': 'Cesta môže trvať najviac {count} deň', + 'dashboard.tripTooLong.few': 'Cesta môže trvať najviac {count} dni', 'dashboard.dateShiftTitle': 'Nový dátum začiatku', 'dashboard.dateShiftIntro': 'Zmenili ste začiatok tejto cesty. Ako sa majú vaše plány prispôsobiť novým termínom?', 'dashboard.dateShiftKeepBookings': 'Ponechať rezervácie v pôvodných termínoch', diff --git a/shared/src/i18n/sk/dawarich.ts b/shared/src/i18n/sk/dawarich.ts index 099d55e9b..2d641a95e 100644 --- a/shared/src/i18n/sk/dawarich.ts +++ b/shared/src/i18n/sk/dawarich.ts @@ -122,7 +122,9 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Pridať záznam', 'dawarich.accept.confirm.bucket_list': 'Odškrtnúť', 'dawarich.accept.recorded': 'Zaznamenané od {from} do {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', + 'dawarich.accept.duration.few': '{count} min', 'dawarich.accept.name': 'Názov', 'dawarich.accept.date': 'Dátum', 'dawarich.accept.from': 'Príchod', @@ -194,7 +196,9 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Skontrolovať znova', 'dawarich.bucket.metersAway': '{meters} m ďaleko', 'dawarich.bucket.kilometersAway': '{km} km ďaleko', - 'dawarich.bucket.rule': 'Prianie sa počíta ako splnené do {meters} m a po {minutes} minútach na mieste.', + 'dawarich.bucket.rule': 'Prianie sa počíta ako splnené do {meters} m a po {count} minútach na mieste.', + 'dawarich.bucket.rule.one': 'Prianie sa počíta ako splnené do {meters} m a po {count} minúte na mieste.', + 'dawarich.bucket.rule.few': 'Prianie sa počíta ako splnené do {meters} m a po {count} minútach na mieste.', 'dawarich.journey.dayStays.other': '{count} pobytov z Dawarichu', 'dawarich.journey.dayStays.one': '{count} pobyt z Dawarichu', 'dawarich.journey.dayStays.few': '{count} pobyty z Dawarichu', diff --git a/shared/src/i18n/sk/roadtrip.ts b/shared/src/i18n/sk/roadtrip.ts index 1fa7daab2..7a4dcb3ba 100644 --- a/shared/src/i18n/sk/roadtrip.ts +++ b/shared/src/i18n/sk/roadtrip.ts @@ -54,7 +54,11 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.conflict': 'Pevný čas je v rozpore s jazdou a dennými prestávkami. Ručné časy zostávajú nezmenené; automatické plánovanie je pozastavené.', 'roadtrip.window.tooLong': - 'Tieto časy cestovania by vyžadovali viac než {days} dní navyše. Zvoľte dlhšie denné okno.', + 'Tieto časy cestovania by vyžadovali viac než {count} dní navyše. Zvoľte dlhšie denné okno.', + 'roadtrip.window.tooLong.one': + 'Tieto časy cestovania by vyžadovali viac než {count} deň navyše. Zvoľte dlhšie denné okno.', + 'roadtrip.window.tooLong.few': + 'Tieto časy cestovania by vyžadovali viac než {count} dni navyše. Zvoľte dlhšie denné okno.', 'roadtrip.mode.roadtrip': 'Cesta autom', 'roadtrip.mode.days': 'Dni', 'roadtrip.mode.label': 'Zobrazenie plánu', @@ -102,8 +106,12 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'vypnuté', 'roadtrip.limit.blockNote': 'Jeden blok = {distance}', - 'roadtrip.limit.echoOver': '{over} z {days} dní nad vaším limitom.', - 'roadtrip.limit.echoNone': 'Všetkých {days} dní je v rámci vašich limitov.', + 'roadtrip.limit.echoOver': '{over} z {count} dní nad vaším limitom.', + 'roadtrip.limit.echoOver.one': '{over} z {count} dňa nad vaším limitom.', + 'roadtrip.limit.echoOver.few': '{over} z {count} dní nad vaším limitom.', + 'roadtrip.limit.echoNone': 'Všetkých {count} dní je v rámci vašich limitov.', + 'roadtrip.limit.echoNone.one': 'Jediný deň je v rámci vašich limitov.', + 'roadtrip.limit.echoNone.few': 'Všetky {count} dni sú v rámci vašich limitov.', 'roadtrip.limit.echoDry': '{count} úsekov bez dosiahnuteľnej zastávky.', 'roadtrip.limit.echoDry.one': '{count} úsek bez dosiahnuteľnej zastávky.', 'roadtrip.limit.echoDry.few': '{count} úseky bez dosiahnuteľnej zastávky.', @@ -282,8 +290,12 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Vypočítané z jazdy', 'roadtrip.stop.endIsLeave': 'Na ceste autom sa v tomto čase odchádza.', 'roadtrip.via.hint': 'Potiahnutím zmeníte trasu, pravým tlačidlom odstránite', - 'roadtrip.warn.late': 'Príchod {minutes} min po čase, ktorý ste nastavili', - 'roadtrip.warn.missedLeave': 'Príchod {minutes} min po čase, ktorý ste nastavili na odchod', + 'roadtrip.warn.late': 'Príchod {count} min po čase, ktorý ste nastavili', + 'roadtrip.warn.late.one': 'Príchod {count} min po čase, ktorý ste nastavili', + 'roadtrip.warn.late.few': 'Príchod {count} min po čase, ktorý ste nastavili', + 'roadtrip.warn.missedLeave': 'Príchod {count} min po čase, ktorý ste nastavili na odchod', + 'roadtrip.warn.missedLeave.one': 'Príchod {count} min po čase, ktorý ste nastavili na odchod', + 'roadtrip.warn.missedLeave.few': 'Príchod {count} min po čase, ktorý ste nastavili na odchod', 'roadtrip.warn.overnight': 'Nasledujúci deň', 'roadtrip.spill.title': 'Zo dňa {number}', 'roadtrip.spill.departs': 'odchod {time}', diff --git a/shared/src/i18n/sk/settings.ts b/shared/src/i18n/sk/settings.ts index ed89ab6c2..8b5ac40d9 100644 --- a/shared/src/i18n/sk/settings.ts +++ b/shared/src/i18n/sk/settings.ts @@ -640,7 +640,11 @@ const settings: TranslationStrings = { 'settings.apiKeys.neverUsed': 'nikdy nepoužitý', 'settings.apiKeys.loadFailed': 'Vaše kľúče sa nepodarilo načítať. Obnovte stránku a skúste to znovu.', 'settings.apiKeys.limitReached': - 'Máte {max} kľúčov, čo je najviac, koľko účet môže mať. Zmažte niektorý, ktorý už nepoužívate, a vytvorte ďalší.', + 'Máte {count} kľúčov, čo je najviac, koľko účet môže mať. Zmažte niektorý, ktorý už nepoužívate, a vytvorte ďalší.', + 'settings.apiKeys.limitReached.one': + 'Máte {count} kľúč, čo je najviac, koľko účet môže mať. Zmažte ho, ak ho už nepoužívate, a vytvorte ďalší.', + 'settings.apiKeys.limitReached.few': + 'Máte {count} kľúče, čo je najviac, koľko účet môže mať. Zmažte niektorý, ktorý už nepoužívate, a vytvorte ďalší.', 'settings.apiKeys.copyFailed': 'Kopírovanie sa nepodarilo. Označte text a skopírujte ho ručne.', 'settings.apiKeys.modal.createTitle': 'Vytvoriť API kľúč', 'settings.apiKeys.modal.name': 'Názov', diff --git a/shared/src/i18n/sk/storage.ts b/shared/src/i18n/sk/storage.ts index 2eebd98d6..d2cc4d536 100644 --- a/shared/src/i18n/sk/storage.ts +++ b/shared/src/i18n/sk/storage.ts @@ -137,7 +137,9 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Presunúť existujúce objekty do nového backendu?', - 'storage.migrate.promptLine': '{category}: {objects} objektov ({size}) z {from} do {to}', + 'storage.migrate.promptLine': '{category}: {count} objektov ({size}) z {from} do {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objekt ({size}) z {from} do {to}', + 'storage.migrate.promptLine.few': '{category}: {count} objekty ({size}) z {from} do {to}', 'storage.migrate.promptLineUnknown': '{category}: neznáma veľkosť (využitie ešte nebolo vypočítané) z {from} do {to}', 'storage.migrate.move': 'Presunúť existujúce objekty', 'storage.migrate.routeOnly': 'Iba smerovať nové zápisy', @@ -146,7 +148,9 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} zlyhalo — tieto objekty neboli skopírované do nového backendu', 'storage.migrate.failed': 'Presun zlyhal: {error} — kategória nebola prepnutá', 'storage.migrate.cancelled': 'Presun zrušený — nič nebolo prepnuté', - 'storage.migrate.reclaimable': '{objects} objektov ({size}) zostáva na {from} — uvoľnite ručne', + 'storage.migrate.reclaimable': '{count} objektov ({size}) zostáva na {from}, uvoľnite ručne', + 'storage.migrate.reclaimable.one': '{count} objekt ({size}) zostáva na {from}, uvoľnite ručne', + 'storage.migrate.reclaimable.few': '{count} objekty ({size}) zostávajú na {from}, uvoľnite ručne', 'storage.migrate.cancel': 'Zrušiť presun', 'storage.migrate.promptCancel': 'Zrušiť', 'storage.migrate.queued': 'V poradí: {categories}', diff --git a/shared/src/i18n/sv/collab.ts b/shared/src/i18n/sv/collab.ts index b316ffd27..efd0bb5c5 100644 --- a/shared/src/i18n/sv/collab.ts +++ b/shared/src/i18n/sv/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Val', 'collab.polls.delete': 'Ta bort', 'collab.polls.closedSection': 'Stängd', - 'collab.chat.imageLimit': 'Du kan bifoga upp till {max} bilder per meddelande', + 'collab.chat.imageLimit': 'Du kan bifoga upp till {count} bilder per meddelande', + 'collab.chat.imageLimit.one': 'Du kan bifoga högst {count} bild per meddelande', 'collab.chat.uploading': 'Laddar upp {percent} %', 'collab.chat.attachImages': 'Bifoga bilder', 'collab.chat.attachedImage': 'Bifogad bild', diff --git a/shared/src/i18n/sv/dashboard.ts b/shared/src/i18n/sv/dashboard.ts index ae5232fb0..930a1206b 100644 --- a/shared/src/i18n/sv/dashboard.ts +++ b/shared/src/i18n/sv/dashboard.ts @@ -113,7 +113,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Använd Unsplash-foto av {photographer}', 'dashboard.titleRequired': 'Titel är obligatoriskt', 'dashboard.endDateError': 'Slutdatumet måste ligga efter startdatumet', - 'dashboard.tripTooLong': 'En resa kan vara högst {days} dagar', + 'dashboard.tripTooLong': 'En resa kan vara högst {count} dagar', + 'dashboard.tripTooLong.one': 'En resa kan vara högst {count} dag', 'dashboard.dateShiftTitle': 'Nytt startdatum', 'dashboard.dateShiftIntro': 'Du har ändrat när resan börjar. Hur ska dina planer följa de nya datumen?', 'dashboard.dateShiftKeepBookings': 'Behåll bokningar på sina datum', diff --git a/shared/src/i18n/sv/dawarich.ts b/shared/src/i18n/sv/dawarich.ts index 72afa2b26..9596719eb 100644 --- a/shared/src/i18n/sv/dawarich.ts +++ b/shared/src/i18n/sv/dawarich.ts @@ -116,7 +116,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Lägg till inlägg', 'dawarich.accept.confirm.bucket_list': 'Bocka av den', 'dawarich.accept.recorded': 'Inspelad {from} till {to}', - 'dawarich.accept.duration': '{minutes} min', + 'dawarich.accept.duration': '{count} min', + 'dawarich.accept.duration.one': '{count} min', 'dawarich.accept.name': 'Namn', 'dawarich.accept.date': 'Datum', 'dawarich.accept.from': 'Ankom', @@ -181,7 +182,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Kontrollera igen', 'dawarich.bucket.metersAway': '{meters} m bort', 'dawarich.bucket.kilometersAway': '{km} km bort', - 'dawarich.bucket.rule': 'En önskning räknas som uppfylld inom {meters} m och efter {minutes} minuter på plats.', + 'dawarich.bucket.rule': 'En önskning räknas som uppfylld inom {meters} m och efter {count} minuter på plats.', + 'dawarich.bucket.rule.one': 'En önskning räknas som uppfylld inom {meters} m och efter {count} minut på plats.', 'dawarich.journey.dayStays.other': '{count} vistelser från Dawarich', 'dawarich.journey.dayStays.one': '{count} vistelse från Dawarich', diff --git a/shared/src/i18n/sv/roadtrip.ts b/shared/src/i18n/sv/roadtrip.ts index 71ac10e99..c3dc3e40f 100644 --- a/shared/src/i18n/sv/roadtrip.ts +++ b/shared/src/i18n/sv/roadtrip.ts @@ -54,7 +54,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Alla anslutande rutter behövs för att beräkna dagliga pauser.', 'roadtrip.window.conflict': 'En fast tid krockar med körningen och pauserna. Manuella tider behålls; automatisk planering är pausad.', - 'roadtrip.window.tooLong': 'Dessa tider kräver fler än {days} extra dagar. Välj ett längre dagligt tidsintervall.', + 'roadtrip.window.tooLong': 'Dessa tider kräver fler än {count} extra dagar. Välj ett längre dagligt tidsintervall.', + 'roadtrip.window.tooLong.one': 'Dessa tider kräver mer än {count} extra dag. Välj ett längre dagligt tidsintervall.', 'roadtrip.mode.roadtrip': 'Bilresa', 'roadtrip.mode.days': 'Dagar', 'roadtrip.mode.label': 'Planvy', @@ -101,8 +102,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'min', 'roadtrip.limit.off': 'av', 'roadtrip.limit.blockNote': 'Ett block = {distance}', - 'roadtrip.limit.echoOver': '{over} av {days} dagar över din gräns.', - 'roadtrip.limit.echoNone': 'Alla {days} dagar inom dina gränser.', + 'roadtrip.limit.echoOver': '{over} av {count} dagar över din gräns.', + 'roadtrip.limit.echoOver.one': '{over} av {count} dag över din gräns.', + 'roadtrip.limit.echoNone': 'Alla {count} dagar inom dina gränser.', + 'roadtrip.limit.echoNone.one': 'Den enda dagen inom dina gränser.', 'roadtrip.limit.echoDry': '{count} sträckor utan nåbart stopp.', 'roadtrip.limit.echoDry.one': '{count} sträcka utan nåbart stopp.', 'roadtrip.limit.echoEmpty': 'Öppna en roadtrip för att se effekten.', @@ -270,8 +273,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Beräknad utifrån körningen', 'roadtrip.stop.endIsLeave': 'På bilresan åker ni vidare vid den här tiden.', 'roadtrip.via.hint': 'Dra för att forma om rutten, högerklicka för att ta bort', - 'roadtrip.warn.late': 'Ankomst {minutes} min efter den satta tiden', - 'roadtrip.warn.missedLeave': 'Ankomst {minutes} min efter den satta avfärdstiden', + 'roadtrip.warn.late': 'Ankomst {count} min efter den satta tiden', + 'roadtrip.warn.late.one': 'Ankomst {count} min efter den satta tiden', + 'roadtrip.warn.missedLeave': 'Ankomst {count} min efter den satta avfärdstiden', + 'roadtrip.warn.missedLeave.one': 'Ankomst {count} min efter den satta avfärdstiden', 'roadtrip.warn.overnight': 'Nästa dag', 'roadtrip.spill.title': 'Från dag {number}', 'roadtrip.spill.departs': 'avgång {time}', diff --git a/shared/src/i18n/sv/settings.ts b/shared/src/i18n/sv/settings.ts index facd91def..8ace59d55 100644 --- a/shared/src/i18n/sv/settings.ts +++ b/shared/src/i18n/sv/settings.ts @@ -626,7 +626,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Ändpunkt', 'settings.apiKeys.neverUsed': 'aldrig använd', 'settings.apiKeys.loadFailed': 'Det gick inte att läsa in dina nycklar. Ladda om sidan för att försöka igen.', - 'settings.apiKeys.limitReached': 'Du har {max} nycklar, det högsta antalet per konto. Ta bort en som du inte längre använder för att skapa en ny.', + 'settings.apiKeys.limitReached': + 'Du har {count} nycklar, det högsta antalet per konto. Ta bort en som du inte längre använder för att skapa en ny.', + 'settings.apiKeys.limitReached.one': + 'Du har {count} nyckel, det högsta antalet per konto. Ta bort den om du inte längre använder den för att skapa en ny.', 'settings.apiKeys.copyFailed': 'Det gick inte att kopiera. Markera texten och kopiera den manuellt.', 'settings.apiKeys.modal.createTitle': 'Skapa API-nyckel', 'settings.apiKeys.modal.name': 'Namn', diff --git a/shared/src/i18n/sv/storage.ts b/shared/src/i18n/sv/storage.ts index 0fba58e5d..0954d1aaf 100644 --- a/shared/src/i18n/sv/storage.ts +++ b/shared/src/i18n/sv/storage.ts @@ -122,7 +122,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Flytta befintliga objekt till den nya backend?', - 'storage.migrate.promptLine': '{category}: {objects} objekt ({size}) från {from} till {to}', + 'storage.migrate.promptLine': '{category}: {count} objekt ({size}) från {from} till {to}', + 'storage.migrate.promptLine.one': '{category}: {count} objekt ({size}) från {from} till {to}', 'storage.migrate.promptLineUnknown': '{category}: okänd storlek (användning inte skannad än) från {from} till {to}', 'storage.migrate.move': 'Flytta befintliga objekt', 'storage.migrate.routeOnly': 'Dirigera bara nya skrivningar', @@ -131,7 +132,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} misslyckades — dessa objekt kopierades inte till den nya backend', 'storage.migrate.failed': 'Flytt misslyckades: {error} — kategorin byttes inte', 'storage.migrate.cancelled': 'Flytt avbruten — inget byttes', - 'storage.migrate.reclaimable': '{objects} objekt ({size}) finns kvar på {from} — återvinn manuellt', + 'storage.migrate.reclaimable': '{count} objekt ({size}) finns kvar på {from}, återvinn manuellt', + 'storage.migrate.reclaimable.one': '{count} objekt ({size}) finns kvar på {from}, återvinn manuellt', 'storage.migrate.cancel': 'Avbryt flytt', 'storage.migrate.promptCancel': 'Avbryt', 'storage.migrate.queued': 'I kö: {categories}', diff --git a/shared/src/i18n/th/collab.ts b/shared/src/i18n/th/collab.ts index 0d5076573..7cbd113f8 100644 --- a/shared/src/i18n/th/collab.ts +++ b/shared/src/i18n/th/collab.ts @@ -81,7 +81,7 @@ const collab: TranslationStrings = { 'collab.polls.options': 'ตัวเลือก', 'collab.polls.delete': 'ลบ', 'collab.polls.closedSection': 'ปิดแล้ว', - 'collab.chat.imageLimit': 'คุณแนบรูปภาพได้สูงสุด {max} รูปต่อข้อความ', + 'collab.chat.imageLimit': 'คุณแนบรูปภาพได้สูงสุด {count} รูปต่อข้อความ', 'collab.chat.uploading': 'กำลังอัปโหลด {percent}%', 'collab.chat.attachImages': 'แนบรูปภาพ', 'collab.chat.attachedImage': 'รูปภาพที่แนบ', diff --git a/shared/src/i18n/th/dashboard.ts b/shared/src/i18n/th/dashboard.ts index f2d6db7e4..94566fa9d 100644 --- a/shared/src/i18n/th/dashboard.ts +++ b/shared/src/i18n/th/dashboard.ts @@ -106,7 +106,7 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'ใช้รูปภาพ Unsplash โดย {photographer}', 'dashboard.titleRequired': 'ต้องระบุชื่อ', 'dashboard.endDateError': 'วันที่สิ้นสุดต้องอยู่หลังวันที่เริ่มต้น', - 'dashboard.tripTooLong': 'การเดินทางหนึ่งครั้งยาวได้สูงสุด {days} วัน', + 'dashboard.tripTooLong': 'การเดินทางหนึ่งครั้งยาวได้สูงสุด {count} วัน', 'dashboard.dateShiftTitle': 'วันที่เริ่มต้นใหม่', 'dashboard.dateShiftIntro': 'คุณเปลี่ยนวันที่เริ่มต้นของการเดินทางครั้งนี้ แผนของคุณควรปรับตามวันที่ใหม่อย่างไร', 'dashboard.dateShiftKeepBookings': 'ทำการจองตามวันที่ของพวกเขา', diff --git a/shared/src/i18n/th/dawarich.ts b/shared/src/i18n/th/dawarich.ts index c9c2517ef..39ffd2ebc 100644 --- a/shared/src/i18n/th/dawarich.ts +++ b/shared/src/i18n/th/dawarich.ts @@ -98,7 +98,7 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'เพิ่มรายการ', 'dawarich.accept.confirm.bucket_list': 'ทำเครื่องหมายว่าสำเร็จ', 'dawarich.accept.recorded': 'บันทึกตั้งแต่ {from} ถึง {to}', - 'dawarich.accept.duration': '{minutes} นาที', + 'dawarich.accept.duration': '{count} นาที', 'dawarich.accept.name': 'ชื่อ', 'dawarich.accept.date': 'วันที่', 'dawarich.accept.from': 'มาถึง', @@ -149,7 +149,7 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'ตรวจสอบอีกครั้ง', 'dawarich.bucket.metersAway': 'ห่าง {meters} ม.', 'dawarich.bucket.kilometersAway': 'ห่าง {km} กม.', - 'dawarich.bucket.rule': 'จะนับว่าไปถึงเมื่ออยู่ในระยะ {meters} ม. และอยู่ที่จุดนั้นอย่างน้อย {minutes} นาที', + 'dawarich.bucket.rule': 'จะนับว่าไปถึงเมื่ออยู่ในระยะ {meters} ม. และอยู่ที่จุดนั้นอย่างน้อย {count} นาที', 'dawarich.journey.dayStays.other': 'การพักจาก Dawarich {count} รายการ', }; diff --git a/shared/src/i18n/th/roadtrip.ts b/shared/src/i18n/th/roadtrip.ts index 2e6898389..116815900 100644 --- a/shared/src/i18n/th/roadtrip.ts +++ b/shared/src/i18n/th/roadtrip.ts @@ -52,7 +52,7 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'ยังคำนวณช่วงพักรายวันไม่ได้จนกว่าจะมีเส้นทางเชื่อมต่อครบ', 'roadtrip.window.conflict': 'เวลาที่กำหนดขัดกับการขับรถและช่วงพักรายวัน เวลาที่กำหนดเองจะไม่เปลี่ยน แต่การจัดตารางอัตโนมัติถูกพักไว้', - 'roadtrip.window.tooLong': 'เวลาเดินทางเหล่านี้จะต้องใช้วันเพิ่มมากกว่า {days} วัน เลือกกรอบเวลารายวันที่นานขึ้น', + 'roadtrip.window.tooLong': 'เวลาเดินทางเหล่านี้จะต้องใช้วันเพิ่มมากกว่า {count} วัน เลือกกรอบเวลารายวันที่นานขึ้น', 'roadtrip.mode.roadtrip': 'โรดทริป', 'roadtrip.mode.days': 'วัน', 'roadtrip.mode.label': 'มุมมองแผน', @@ -98,8 +98,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'นาที', 'roadtrip.limit.off': 'ปิด', 'roadtrip.limit.blockNote': 'หนึ่งช่วง = {distance}', - 'roadtrip.limit.echoOver': 'เกินขีดจำกัด {over} จาก {days} วัน', - 'roadtrip.limit.echoNone': 'ทั้ง {days} วันอยู่ในขีดจำกัด', + 'roadtrip.limit.echoOver': 'เกินขีดจำกัด {over} จาก {count} วัน', + 'roadtrip.limit.echoNone': 'ทั้ง {count} วันอยู่ในขีดจำกัด', 'roadtrip.limit.echoDry': 'มี {count} ช่วงที่ไม่มีจุดแวะที่ไปถึงได้', 'roadtrip.limit.echoEmpty': 'เปิดโรดทริปเพื่อดูผลของการตั้งค่าเหล่านี้', 'roadtrip.limit.specToggle': 'คำนวณจากข้อมูลรถ', @@ -249,8 +249,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'คำนวณจากการขับรถ', 'roadtrip.stop.endIsLeave': 'ในโรดทริป รถจะออกเดินทางเวลานี้', 'roadtrip.via.hint': 'ลากเพื่อปรับเส้นทาง คลิกขวาเพื่อลบ', - 'roadtrip.warn.late': 'ถึงช้ากว่าเวลาที่กำหนด {minutes} นาที', - 'roadtrip.warn.missedLeave': 'ถึงช้ากว่าเวลาที่คุณตั้งไว้ว่าจะออก {minutes} นาที', + 'roadtrip.warn.late': 'ถึงช้ากว่าเวลาที่กำหนด {count} นาที', + 'roadtrip.warn.missedLeave': 'ถึงช้ากว่าเวลาที่คุณตั้งไว้ว่าจะออก {count} นาที', 'roadtrip.warn.overnight': 'วันถัดไป', 'roadtrip.spill.title': 'จากวันที่ {number}', 'roadtrip.spill.departs': 'ออกเวลา {time}', diff --git a/shared/src/i18n/th/settings.ts b/shared/src/i18n/th/settings.ts index 665c8f553..882156037 100644 --- a/shared/src/i18n/th/settings.ts +++ b/shared/src/i18n/th/settings.ts @@ -624,7 +624,7 @@ const settings: TranslationStrings = { 'settings.apiKeys.neverUsed': 'ไม่เคยใช้', 'settings.apiKeys.loadFailed': 'ไม่สามารถโหลดคีย์ของคุณได้ โหลดหน้านี้ใหม่เพื่อลองอีกครั้ง', 'settings.apiKeys.limitReached': - 'คุณมีคีย์ {max} อัน ซึ่งเป็นจำนวนสูงสุดที่บัญชีหนึ่งมีได้ ลบคีย์ที่ไม่ได้ใช้ออกเพื่อสร้างคีย์ใหม่', + 'คุณมีคีย์ {count} อัน ซึ่งเป็นจำนวนสูงสุดที่บัญชีหนึ่งมีได้ ลบคีย์ที่ไม่ได้ใช้ออกเพื่อสร้างคีย์ใหม่', 'settings.apiKeys.copyFailed': 'ไม่สามารถคัดลอกได้ เลือกข้อความและคัดลอกด้วยตนเอง', }; diff --git a/shared/src/i18n/th/storage.ts b/shared/src/i18n/th/storage.ts index a8782f3b3..a80320511 100644 --- a/shared/src/i18n/th/storage.ts +++ b/shared/src/i18n/th/storage.ts @@ -136,7 +136,7 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'ย้ายวัตถุที่มีอยู่ไปยังแบ็กเอนด์ใหม่หรือไม่', - 'storage.migrate.promptLine': '{category}: {objects} วัตถุ ({size}) จาก {from} ถึง {to}', + 'storage.migrate.promptLine': '{category}: {count} วัตถุ ({size}) จาก {from} ถึง {to}', 'storage.migrate.promptLineUnknown': '{category}: ไม่ทราบขนาด (ยังไม่มีการสแกนการใช้งาน) จาก {from} ถึง {to}', 'storage.migrate.move': 'ย้ายวัตถุที่มีอยู่', 'storage.migrate.routeOnly': 'เพียงกำหนดเส้นทางการเขียนใหม่', @@ -145,7 +145,7 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} ล้มเหลว — ออบเจ็กต์เหล่านั้นไม่ได้ถูกคัดลอกไปยังแบ็กเอนด์ใหม่', 'storage.migrate.failed': 'การย้ายล้มเหลว: {error} — หมวดหมู่ไม่ได้ถูกเปลี่ยน', 'storage.migrate.cancelled': 'การย้ายถูกยกเลิก — ไม่มีอะไรถูกเปลี่ยน', - 'storage.migrate.reclaimable': 'วัตถุ {objects} ({size}) ยังคงอยู่บน {from} — เรียกคืนด้วยตนเอง', + 'storage.migrate.reclaimable': 'วัตถุ {count} ({size}) ยังคงอยู่บน {from} เรียกคืนด้วยตนเอง', 'storage.migrate.cancel': 'ยกเลิกการย้าย', 'storage.migrate.promptCancel': 'ยกเลิก', 'storage.migrate.queued': 'อยู่ในคิว: {categories}', diff --git a/shared/src/i18n/tr/collab.ts b/shared/src/i18n/tr/collab.ts index a88004db9..34f6e4023 100644 --- a/shared/src/i18n/tr/collab.ts +++ b/shared/src/i18n/tr/collab.ts @@ -100,7 +100,8 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Seçenekler', 'collab.polls.delete': 'Sil', 'collab.polls.closedSection': 'Kapalı', - 'collab.chat.imageLimit': 'Her mesaja en fazla {max} görsel ekleyebilirsiniz', + 'collab.chat.imageLimit': 'Her mesaja en fazla {count} görsel ekleyebilirsiniz', + 'collab.chat.imageLimit.one': 'Her mesaja yalnızca {count} görsel ekleyebilirsiniz', 'collab.chat.uploading': 'Yükleniyor %{percent}', 'collab.chat.attachImages': 'Görsel ekle', 'collab.chat.attachedImage': 'Eklenen görsel', diff --git a/shared/src/i18n/tr/dashboard.ts b/shared/src/i18n/tr/dashboard.ts index f783aabe7..8ce328d66 100644 --- a/shared/src/i18n/tr/dashboard.ts +++ b/shared/src/i18n/tr/dashboard.ts @@ -113,7 +113,8 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': '{photographer} tarafından çekilen Unsplash fotoğrafını kullan', 'dashboard.titleRequired': 'Başlık gerekli', 'dashboard.endDateError': 'Bitiş tarihi başlangıçtan sonra olmalı', - 'dashboard.tripTooLong': 'Bir seyahat en fazla {days} gün sürebilir', + 'dashboard.tripTooLong': 'Bir seyahat en fazla {count} gün sürebilir', + 'dashboard.tripTooLong.one': 'Bir seyahat en fazla {count} gün sürebilir', 'dashboard.dateShiftTitle': 'Yeni başlangıç tarihi', 'dashboard.dateShiftIntro': 'Bu seyahatin başlangıç tarihini değiştirdiniz. Planlarınız yeni tarihlere nasıl uyum sağlasın?', diff --git a/shared/src/i18n/tr/dawarich.ts b/shared/src/i18n/tr/dawarich.ts index 6d8303622..5d2870a7a 100644 --- a/shared/src/i18n/tr/dawarich.ts +++ b/shared/src/i18n/tr/dawarich.ts @@ -119,7 +119,8 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Kayıt ekle', 'dawarich.accept.confirm.bucket_list': 'İşaretle', 'dawarich.accept.recorded': '{from} ile {to} arasında kaydedildi', - 'dawarich.accept.duration': '{minutes} dk', + 'dawarich.accept.duration': '{count} dk', + 'dawarich.accept.duration.one': '{count} dk', 'dawarich.accept.name': 'Ad', 'dawarich.accept.date': 'Tarih', 'dawarich.accept.from': 'Varış', @@ -184,7 +185,8 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Yeniden kontrol et', 'dawarich.bucket.metersAway': '{meters} m uzakta', 'dawarich.bucket.kilometersAway': '{km} km uzakta', - 'dawarich.bucket.rule': 'Bir dilek {meters} m yakınlıkta ve yerinde {minutes} dakika sonra ulaşılmış sayılır.', + 'dawarich.bucket.rule': 'Bir dilek {meters} m yakınlıkta ve yerinde {count} dakika sonra ulaşılmış sayılır.', + 'dawarich.bucket.rule.one': 'Bir dilek {meters} m yakınlıkta ve yerinde {count} dakika sonra ulaşılmış sayılır.', 'dawarich.journey.dayStays.other': 'Dawarich’ten {count} konaklama', 'dawarich.journey.dayStays.one': 'Dawarich’ten {count} konaklama', diff --git a/shared/src/i18n/tr/roadtrip.ts b/shared/src/i18n/tr/roadtrip.ts index df25d594a..4c0155088 100644 --- a/shared/src/i18n/tr/roadtrip.ts +++ b/shared/src/i18n/tr/roadtrip.ts @@ -56,7 +56,9 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Günlük molaları hesaplamak için tüm bağlantı rotaları gereklidir.', 'roadtrip.window.conflict': 'Sabit bir saat sürüş ve molalarla çakışıyor. Elle girilen saatler korunur; otomatik planlama duraklatıldı.', - 'roadtrip.window.tooLong': 'Bu saatler {days} günden fazla ek süre gerektiriyor. Daha uzun bir günlük aralık seçin.', + 'roadtrip.window.tooLong': 'Bu saatler {count} günden fazla ek süre gerektiriyor. Daha uzun bir günlük aralık seçin.', + 'roadtrip.window.tooLong.one': + 'Bu saatler {count} günden fazla ek süre gerektiriyor. Daha uzun bir günlük aralık seçin.', 'roadtrip.mode.roadtrip': 'Yol gezisi', 'roadtrip.mode.days': 'Günler', 'roadtrip.mode.label': 'Plan görünümü', @@ -103,8 +105,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'dk', 'roadtrip.limit.off': 'kapalı', 'roadtrip.limit.blockNote': 'Bir blok = {distance}', - 'roadtrip.limit.echoOver': '{days} günün {over} tanesi sınırının üzerinde.', - 'roadtrip.limit.echoNone': '{days} günün tamamı sınırlar içinde.', + 'roadtrip.limit.echoOver': '{count} günün {over} tanesi sınırının üzerinde.', + 'roadtrip.limit.echoOver.one': '{count} günün {over} tanesi sınırının üzerinde.', + 'roadtrip.limit.echoNone': '{count} günün tamamı sınırlar içinde.', + 'roadtrip.limit.echoNone.one': 'Tek gün sınırlar içinde.', 'roadtrip.limit.echoDry': 'Ulaşılabilir durağı olmayan {count} kesim.', 'roadtrip.limit.echoDry.one': 'Ulaşılabilir durağı olmayan {count} kesim.', 'roadtrip.limit.echoEmpty': 'Etkiyi görmek için bir yol gezisi aç.', @@ -272,8 +276,10 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Sürüşe göre hesaplandı', 'roadtrip.stop.endIsLeave': 'Yol gezisinde bu saatte yola çıkılır.', 'roadtrip.via.hint': 'Rotayı yeniden şekillendirmek için sürükleyin, kaldırmak için sağ tıklayın', - 'roadtrip.warn.late': 'Belirlenen saatten {minutes} dk sonra varış', - 'roadtrip.warn.missedLeave': 'Belirlenen kalkış saatinden {minutes} dk sonra varış', + 'roadtrip.warn.late': 'Belirlenen saatten {count} dk sonra varış', + 'roadtrip.warn.late.one': 'Belirlenen saatten {count} dk sonra varış', + 'roadtrip.warn.missedLeave': 'Belirlenen kalkış saatinden {count} dk sonra varış', + 'roadtrip.warn.missedLeave.one': 'Belirlenen kalkış saatinden {count} dk sonra varış', 'roadtrip.warn.overnight': 'Ertesi gün', 'roadtrip.spill.title': '{number}. günden', 'roadtrip.spill.departs': 'kalkış {time}', diff --git a/shared/src/i18n/tr/settings.ts b/shared/src/i18n/tr/settings.ts index ee59f5b33..9a054a10c 100644 --- a/shared/src/i18n/tr/settings.ts +++ b/shared/src/i18n/tr/settings.ts @@ -629,7 +629,10 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Uç nokta', 'settings.apiKeys.neverUsed': 'hiç kullanılmadı', 'settings.apiKeys.loadFailed': 'Anahtarların yüklenemedi. Tekrar denemek için sayfayı yenile.', - 'settings.apiKeys.limitReached': '{max} anahtarın var, bir hesap için üst sınır bu. Yeni bir anahtar oluşturmak için artık kullanmadığın birini sil.', + 'settings.apiKeys.limitReached': + '{count} anahtarın var, bir hesap için üst sınır bu. Yeni bir anahtar oluşturmak için artık kullanmadığın birini sil.', + 'settings.apiKeys.limitReached.one': + '{count} anahtarın var, bir hesap için üst sınır bu. Yeni bir anahtar oluşturmak için artık kullanmıyorsan onu sil.', 'settings.apiKeys.copyFailed': 'Kopyalanamadı. Metni seçip elle kopyala.', 'settings.apiKeys.modal.createTitle': 'API anahtarı oluştur', 'settings.apiKeys.modal.name': 'Ad', diff --git a/shared/src/i18n/tr/storage.ts b/shared/src/i18n/tr/storage.ts index 814d11d39..a457b6c11 100644 --- a/shared/src/i18n/tr/storage.ts +++ b/shared/src/i18n/tr/storage.ts @@ -123,7 +123,8 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Mevcut nesneler yeni arka uca taşınsın mı?', - 'storage.migrate.promptLine': '{category}: {from} konumundan {to} konumuna {objects} nesne ({size})', + 'storage.migrate.promptLine': '{category}: {from} konumundan {to} konumuna {count} nesne ({size})', + 'storage.migrate.promptLine.one': '{category}: {from} konumundan {to} konumuna {count} nesne ({size})', 'storage.migrate.promptLineUnknown': '{category}: bilinmeyen boyut (henüz kullanım taraması yapılmadı) {from} konumundan {to} konumuna', 'storage.migrate.move': 'Mevcut nesneleri taşı', @@ -133,7 +134,8 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} başarısız oldu — bu nesneler yeni arka uca kopyalanmadı', 'storage.migrate.failed': 'Taşıma başarısız oldu: {error} — kategori değiştirilmedi', 'storage.migrate.cancelled': 'Taşıma iptal edildi — hiçbir şey değiştirilmedi', - 'storage.migrate.reclaimable': '{objects} nesne ({size}) {from} üzerinde kalıyor — manuel olarak geri kazanın', + 'storage.migrate.reclaimable': '{count} nesne ({size}) {from} üzerinde kalıyor, manuel olarak geri kazanın', + 'storage.migrate.reclaimable.one': '{count} nesne ({size}) {from} üzerinde kalıyor, manuel olarak geri kazanın', 'storage.migrate.cancel': 'Taşımayı iptal et', 'storage.migrate.promptCancel': 'İptal', 'storage.migrate.queued': 'Sırada: {categories}', diff --git a/shared/src/i18n/uk/collab.ts b/shared/src/i18n/uk/collab.ts index 1d04753c1..4cf501179 100644 --- a/shared/src/i18n/uk/collab.ts +++ b/shared/src/i18n/uk/collab.ts @@ -108,7 +108,10 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Варіанти', 'collab.polls.delete': 'Видалити', 'collab.polls.closedSection': 'Закриті', - 'collab.chat.imageLimit': 'До одного повідомлення можна додати до {max} зображень', + 'collab.chat.imageLimit': 'До одного повідомлення можна додати до {count} зображення', + 'collab.chat.imageLimit.one': 'До одного повідомлення можна додати до {count} зображення', + 'collab.chat.imageLimit.few': 'До одного повідомлення можна додати до {count} зображень', + 'collab.chat.imageLimit.many': 'До одного повідомлення можна додати до {count} зображень', 'collab.chat.uploading': 'Завантаження {percent}%', 'collab.chat.attachImages': 'Додати зображення', 'collab.chat.attachedImage': 'Додане зображення', diff --git a/shared/src/i18n/uk/dashboard.ts b/shared/src/i18n/uk/dashboard.ts index e1d98441c..6e023a585 100644 --- a/shared/src/i18n/uk/dashboard.ts +++ b/shared/src/i18n/uk/dashboard.ts @@ -129,7 +129,10 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': 'Використати фото Unsplash від {photographer}', 'dashboard.titleRequired': "Назва обов'язкова", 'dashboard.endDateError': 'Дата закінчення повинна бути пізніше дати початку', - 'dashboard.tripTooLong': 'Подорож може тривати не більше {days} днів', + 'dashboard.tripTooLong': 'Подорож може тривати не більше {count} дня', + 'dashboard.tripTooLong.one': 'Подорож може тривати не більше {count} дня', + 'dashboard.tripTooLong.few': 'Подорож може тривати не більше {count} днів', + 'dashboard.tripTooLong.many': 'Подорож може тривати не більше {count} днів', 'dashboard.dateShiftTitle': 'Нова дата початку', 'dashboard.dateShiftIntro': 'Ви змінили дату початку поїздки. Як ваші плани мають перейти на нові дати?', 'dashboard.dateShiftKeepBookings': 'Залишити бронювання на своїх датах', diff --git a/shared/src/i18n/uk/dawarich.ts b/shared/src/i18n/uk/dawarich.ts index 139a5d978..257f0c0ea 100644 --- a/shared/src/i18n/uk/dawarich.ts +++ b/shared/src/i18n/uk/dawarich.ts @@ -124,7 +124,10 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Додати запис', 'dawarich.accept.confirm.bucket_list': 'Позначити виконаним', 'dawarich.accept.recorded': 'Записано з {from} до {to}', - 'dawarich.accept.duration': '{minutes} хв', + 'dawarich.accept.duration': '{count} хв', + 'dawarich.accept.duration.one': '{count} хв', + 'dawarich.accept.duration.few': '{count} хв', + 'dawarich.accept.duration.many': '{count} хв', 'dawarich.accept.name': 'Назва', 'dawarich.accept.date': 'Дата', 'dawarich.accept.from': 'Прибуття', @@ -203,7 +206,10 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Перевірити знову', 'dawarich.bucket.metersAway': 'за {meters} м', 'dawarich.bucket.kilometersAway': 'за {km} км', - 'dawarich.bucket.rule': 'Бажання вважається здійсненим у межах {meters} м і після {minutes} хвилин на місці.', + 'dawarich.bucket.rule': 'Бажання вважається здійсненим у межах {meters} м і після {count} хвилини на місці.', + 'dawarich.bucket.rule.one': 'Бажання вважається здійсненим у межах {meters} м і після {count} хвилини на місці.', + 'dawarich.bucket.rule.few': 'Бажання вважається здійсненим у межах {meters} м і після {count} хвилин на місці.', + 'dawarich.bucket.rule.many': 'Бажання вважається здійсненим у межах {meters} м і після {count} хвилин на місці.', 'dawarich.journey.dayStays.other': '{count} перебування з Dawarich', 'dawarich.journey.dayStays.one': '{count} перебування з Dawarich', 'dawarich.journey.dayStays.few': '{count} перебування з Dawarich', diff --git a/shared/src/i18n/uk/roadtrip.ts b/shared/src/i18n/uk/roadtrip.ts index bd2208601..d6b84a9fb 100644 --- a/shared/src/i18n/uk/roadtrip.ts +++ b/shared/src/i18n/uk/roadtrip.ts @@ -55,7 +55,12 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Для розрахунку щоденних перерв потрібні всі сполучні маршрути.', 'roadtrip.window.conflict': 'Фіксований час суперечить поїздці та перервам. Ручні значення збережено; автоматичне планування призупинено.', - 'roadtrip.window.tooLong': 'За цих налаштувань потрібно понад {days} додаткових днів. Збільште щоденний інтервал.', + 'roadtrip.window.tooLong': 'За цих налаштувань потрібно понад {count} додаткового дня. Збільште щоденний інтервал.', + 'roadtrip.window.tooLong.one': + 'За цих налаштувань потрібно понад {count} додатковий день. Збільште щоденний інтервал.', + 'roadtrip.window.tooLong.few': 'За цих налаштувань потрібно понад {count} додаткові дні. Збільште щоденний інтервал.', + 'roadtrip.window.tooLong.many': + 'За цих налаштувань потрібно понад {count} додаткових днів. Збільште щоденний інтервал.', 'roadtrip.mode.roadtrip': 'Автоподорож', 'roadtrip.mode.days': 'Дні', 'roadtrip.mode.label': 'Вигляд плану', @@ -104,8 +109,14 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'хв', 'roadtrip.limit.off': 'вимк', 'roadtrip.limit.blockNote': 'Один блок = {distance}', - 'roadtrip.limit.echoOver': '{over} із {days} днів понад твій ліміт.', - 'roadtrip.limit.echoNone': 'Усі {days} днів у межах лімітів.', + 'roadtrip.limit.echoOver': '{over} із {count} дня понад твій ліміт.', + 'roadtrip.limit.echoOver.one': '{over} із {count} дня понад твій ліміт.', + 'roadtrip.limit.echoOver.few': '{over} із {count} днів понад твій ліміт.', + 'roadtrip.limit.echoOver.many': '{over} із {count} днів понад твій ліміт.', + 'roadtrip.limit.echoNone': 'Усі {count} дня у межах лімітів.', + 'roadtrip.limit.echoNone.one': '{count} день у межах лімітів.', + 'roadtrip.limit.echoNone.few': 'Усі {count} дні у межах лімітів.', + 'roadtrip.limit.echoNone.many': 'Усі {count} днів у межах лімітів.', 'roadtrip.limit.echoDry': '{count} відрізка без досяжної зупинки.', 'roadtrip.limit.echoDry.one': '{count} відрізок без досяжної зупинки.', 'roadtrip.limit.echoDry.few': '{count} відрізки без досяжної зупинки.', @@ -297,8 +308,14 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Розраховано за часом у дорозі', 'roadtrip.stop.endIsLeave': 'В автоподорожі відʼїзд відбувається в цей час.', 'roadtrip.via.hint': 'Перетягніть, щоб змінити маршрут, права кнопка — видалити', - 'roadtrip.warn.late': 'Прибуття на {minutes} хв пізніше заданого часу', - 'roadtrip.warn.missedLeave': 'Прибуття на {minutes} хв пізніше заданого часу відʼїзду', + 'roadtrip.warn.late': 'Прибуття на {count} хв пізніше заданого часу', + 'roadtrip.warn.late.one': 'Прибуття на {count} хв пізніше заданого часу', + 'roadtrip.warn.late.few': 'Прибуття на {count} хв пізніше заданого часу', + 'roadtrip.warn.late.many': 'Прибуття на {count} хв пізніше заданого часу', + 'roadtrip.warn.missedLeave': 'Прибуття на {count} хв пізніше заданого часу відʼїзду', + 'roadtrip.warn.missedLeave.one': 'Прибуття на {count} хв пізніше заданого часу відʼїзду', + 'roadtrip.warn.missedLeave.few': 'Прибуття на {count} хв пізніше заданого часу відʼїзду', + 'roadtrip.warn.missedLeave.many': 'Прибуття на {count} хв пізніше заданого часу відʼїзду', 'roadtrip.warn.overnight': 'Наступний день', 'roadtrip.spill.title': 'З дня {number}', 'roadtrip.spill.departs': 'виїзд {time}', diff --git a/shared/src/i18n/uk/settings.ts b/shared/src/i18n/uk/settings.ts index ca2315ee4..e33b2af3b 100644 --- a/shared/src/i18n/uk/settings.ts +++ b/shared/src/i18n/uk/settings.ts @@ -644,7 +644,14 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Ендпоінт', 'settings.apiKeys.neverUsed': 'не використовувався', 'settings.apiKeys.loadFailed': 'Не вдалося завантажити ключі. Оновіть сторінку, щоб спробувати ще раз.', - 'settings.apiKeys.limitReached': 'У вас {max} ключів, це максимум для одного облікового запису. Видаліть непотрібний ключ, щоб створити новий.', + 'settings.apiKeys.limitReached': + 'У вас {count} ключа, це максимум для одного облікового запису. Видаліть непотрібний ключ, щоб створити новий.', + 'settings.apiKeys.limitReached.one': + 'У вас {count} ключ, це максимум для одного облікового запису. Видаліть непотрібний ключ, щоб створити новий.', + 'settings.apiKeys.limitReached.few': + 'У вас {count} ключі, це максимум для одного облікового запису. Видаліть непотрібний ключ, щоб створити новий.', + 'settings.apiKeys.limitReached.many': + 'У вас {count} ключів, це максимум для одного облікового запису. Видаліть непотрібний ключ, щоб створити новий.', 'settings.apiKeys.copyFailed': 'Не вдалося скопіювати. Виділіть текст і скопіюйте його вручну.', 'settings.apiKeys.modal.createTitle': 'Створити ключ API', 'settings.apiKeys.modal.name': 'Назва', diff --git a/shared/src/i18n/uk/storage.ts b/shared/src/i18n/uk/storage.ts index 06ddbaed0..d0a4e0f07 100644 --- a/shared/src/i18n/uk/storage.ts +++ b/shared/src/i18n/uk/storage.ts @@ -126,7 +126,10 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': "Перенести наявні об'єкти на новий бекенд?", - 'storage.migrate.promptLine': "{category}: {objects} об'єктів ({size}) з {from} до {to}", + 'storage.migrate.promptLine': "{category}: {count} об'єкта ({size}) з {from} до {to}", + 'storage.migrate.promptLine.one': "{category}: {count} об'єкт ({size}) з {from} до {to}", + 'storage.migrate.promptLine.few': "{category}: {count} об'єкти ({size}) з {from} до {to}", + 'storage.migrate.promptLine.many': "{category}: {count} об'єктів ({size}) з {from} до {to}", 'storage.migrate.promptLineUnknown': '{category}: невідомий розмір (використання ще не проскановано) з {from} до {to}', 'storage.migrate.move': "Перенести наявні об'єкти", @@ -136,7 +139,10 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': "{failed} не вдалося — ці об'єкти не було скопійовано на новий бекенд", 'storage.migrate.failed': 'Перенесення не вдалося: {error} — категорію не перемкнуто', 'storage.migrate.cancelled': 'Перенесення скасовано — нічого не перемкнуто', - 'storage.migrate.reclaimable': "{objects} об'єктів ({size}) залишаються на {from} — звільніть вручну", + 'storage.migrate.reclaimable': "{count} об'єкта ({size}) залишаються на {from}, звільніть вручну", + 'storage.migrate.reclaimable.one': "{count} об'єкт ({size}) залишається на {from}, звільніть вручну", + 'storage.migrate.reclaimable.few': "{count} об'єкти ({size}) залишаються на {from}, звільніть вручну", + 'storage.migrate.reclaimable.many': "{count} об'єктів ({size}) залишаються на {from}, звільніть вручну", 'storage.migrate.cancel': 'Скасувати перенесення', 'storage.migrate.promptCancel': 'Скасувати', 'storage.migrate.queued': 'У черзі: {categories}', diff --git a/shared/src/i18n/vi/collab.ts b/shared/src/i18n/vi/collab.ts index 75bbdce47..ae7a3cc3a 100644 --- a/shared/src/i18n/vi/collab.ts +++ b/shared/src/i18n/vi/collab.ts @@ -96,7 +96,7 @@ const collab: TranslationStrings = { 'collab.polls.options': 'Tùy chọn', 'collab.polls.delete': 'Xóa bỏ', 'collab.polls.closedSection': 'Đã đóng', - 'collab.chat.imageLimit': 'Bạn có thể đính kèm tối đa {max} ảnh cho mỗi tin nhắn', + 'collab.chat.imageLimit': 'Bạn có thể đính kèm tối đa {count} ảnh cho mỗi tin nhắn', 'collab.chat.uploading': 'Đang tải lên {percent}%', 'collab.chat.attachImages': 'Đính kèm ảnh', 'collab.chat.attachedImage': 'Ảnh đính kèm', diff --git a/shared/src/i18n/vi/dashboard.ts b/shared/src/i18n/vi/dashboard.ts index f35f02fbf..039da08b3 100644 --- a/shared/src/i18n/vi/dashboard.ts +++ b/shared/src/i18n/vi/dashboard.ts @@ -100,7 +100,7 @@ const dashboard: TranslationStrings = { 'dashboard.coverRemoveError': 'Không thể xóa', 'dashboard.titleRequired': 'Tiêu đề là bắt buộc', 'dashboard.endDateError': 'Ngày kết thúc phải sau ngày bắt đầu', - 'dashboard.tripTooLong': 'Một chuyến đi có thể kéo dài tối đa {days} ngày', + 'dashboard.tripTooLong': 'Một chuyến đi có thể kéo dài tối đa {count} ngày', 'dashboard.dateShiftTitle': 'Ngày bắt đầu mới', 'dashboard.dateShiftIntro': 'Bạn đã thay đổi thời điểm bắt đầu chuyến đi. Các kế hoạch của bạn nên theo ngày mới như thế nào?', diff --git a/shared/src/i18n/vi/dawarich.ts b/shared/src/i18n/vi/dawarich.ts index 801013b6b..db7615860 100644 --- a/shared/src/i18n/vi/dawarich.ts +++ b/shared/src/i18n/vi/dawarich.ts @@ -112,7 +112,7 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': 'Thêm mục nhập', 'dawarich.accept.confirm.bucket_list': 'Đánh dấu hoàn thành', 'dawarich.accept.recorded': 'Được ghi từ {from} đến {to}', - 'dawarich.accept.duration': '{minutes} phút', + 'dawarich.accept.duration': '{count} phút', 'dawarich.accept.name': 'Tên', 'dawarich.accept.date': 'Ngày', 'dawarich.accept.from': 'Đến nơi', @@ -170,7 +170,7 @@ const dawarich: TranslationStrings = { 'dawarich.again': 'Kiểm tra lại', 'dawarich.bucket.metersAway': 'cách {meters} m', 'dawarich.bucket.kilometersAway': 'cách {km} km', - 'dawarich.bucket.rule': 'Một điều ước được tính là đã đến khi trong vòng {meters} m và sau {minutes} phút tại chỗ.', + 'dawarich.bucket.rule': 'Một điều ước được tính là đã đến khi trong vòng {meters} m và sau {count} phút tại chỗ.', 'dawarich.journey.dayStays.other': '{count} điểm lưu trú từ Dawarich', }; diff --git a/shared/src/i18n/vi/roadtrip.ts b/shared/src/i18n/vi/roadtrip.ts index 35b5741bc..f84dcef73 100644 --- a/shared/src/i18n/vi/roadtrip.ts +++ b/shared/src/i18n/vi/roadtrip.ts @@ -55,7 +55,7 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.incomplete': 'Cần có tất cả tuyến đường nối để tính thời gian nghỉ hằng ngày.', 'roadtrip.window.conflict': 'Một giờ cố định xung đột với việc lái xe và nghỉ. Giờ thủ công được giữ nguyên; lập lịch tự động tạm dừng.', - 'roadtrip.window.tooLong': 'Các giờ này cần hơn {days} ngày bổ sung. Hãy chọn khoảng thời gian mỗi ngày dài hơn.', + 'roadtrip.window.tooLong': 'Các giờ này cần hơn {count} ngày bổ sung. Hãy chọn khoảng thời gian mỗi ngày dài hơn.', 'roadtrip.mode.roadtrip': 'Chuyến đi đường bộ', 'roadtrip.mode.days': 'Ngày', 'roadtrip.mode.label': 'Chế độ xem kế hoạch', @@ -101,8 +101,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': 'phút', 'roadtrip.limit.off': 'tắt', 'roadtrip.limit.blockNote': 'Một khối = {distance}', - 'roadtrip.limit.echoOver': '{over} trong {days} ngày vượt giới hạn.', - 'roadtrip.limit.echoNone': 'Cả {days} ngày đều trong giới hạn.', + 'roadtrip.limit.echoOver': '{over} trong {count} ngày vượt giới hạn.', + 'roadtrip.limit.echoNone': 'Cả {count} ngày đều trong giới hạn.', 'roadtrip.limit.echoDry': '{count} đoạn không có điểm dừng trong tầm với.', 'roadtrip.limit.echoEmpty': 'Mở một chuyến road trip để thấy tác dụng.', 'roadtrip.limit.specToggle': 'Tính từ thông số xe', @@ -260,8 +260,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': 'Tính từ thời gian lái xe', 'roadtrip.stop.endIsLeave': 'Trong chuyến đi đường bộ, sẽ khởi hành vào giờ này.', 'roadtrip.via.hint': 'Kéo để định hình lại lộ trình, chuột phải để xóa', - 'roadtrip.warn.late': 'Đến muộn {minutes} phút so với giờ đã đặt', - 'roadtrip.warn.missedLeave': 'Đến muộn {minutes} phút so với giờ rời đi đã đặt', + 'roadtrip.warn.late': 'Đến muộn {count} phút so với giờ đã đặt', + 'roadtrip.warn.missedLeave': 'Đến muộn {count} phút so với giờ rời đi đã đặt', 'roadtrip.warn.overnight': 'Ngày hôm sau', 'roadtrip.spill.title': 'Từ ngày {number}', 'roadtrip.spill.departs': 'khởi hành {time}', diff --git a/shared/src/i18n/vi/settings.ts b/shared/src/i18n/vi/settings.ts index f1a430b1f..36e1a2228 100644 --- a/shared/src/i18n/vi/settings.ts +++ b/shared/src/i18n/vi/settings.ts @@ -624,7 +624,8 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': 'Điểm cuối', 'settings.apiKeys.neverUsed': 'chưa từng dùng', 'settings.apiKeys.loadFailed': 'Không thể tải các khóa của bạn. Hãy tải lại trang để thử lại.', - 'settings.apiKeys.limitReached': 'Bạn đã có {max} khóa, mức tối đa cho một tài khoản. Hãy xóa một khóa không còn dùng để tạo khóa mới.', + 'settings.apiKeys.limitReached': + 'Bạn đã có {count} khóa, mức tối đa cho một tài khoản. Hãy xóa một khóa không còn dùng để tạo khóa mới.', 'settings.apiKeys.copyFailed': 'Không thể sao chép. Hãy chọn đoạn văn bản và sao chép thủ công.', 'settings.apiKeys.modal.createTitle': 'Tạo khóa API', 'settings.apiKeys.modal.name': 'Tên', diff --git a/shared/src/i18n/vi/storage.ts b/shared/src/i18n/vi/storage.ts index 149b7079f..aef26531c 100644 --- a/shared/src/i18n/vi/storage.ts +++ b/shared/src/i18n/vi/storage.ts @@ -122,7 +122,7 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': 'Di chuyển các đối tượng hiện có sang backend mới?', - 'storage.migrate.promptLine': '{category}: {objects} đối tượng ({size}) từ {from} sang {to}', + 'storage.migrate.promptLine': '{category}: {count} đối tượng ({size}) từ {from} sang {to}', 'storage.migrate.promptLineUnknown': '{category}: không rõ dung lượng (chưa quét mức sử dụng) từ {from} sang {to}', 'storage.migrate.move': 'Di chuyển các đối tượng hiện có', 'storage.migrate.routeOnly': 'Chỉ định tuyến các ghi mới', @@ -131,7 +131,7 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} thất bại — các đối tượng đó chưa được sao chép sang backend mới', 'storage.migrate.failed': 'Di chuyển thất bại: {error} — danh mục chưa được chuyển đổi', 'storage.migrate.cancelled': 'Đã hủy di chuyển — chưa có gì được chuyển đổi', - 'storage.migrate.reclaimable': '{objects} đối tượng ({size}) vẫn còn trên {from} — hãy thu hồi thủ công', + 'storage.migrate.reclaimable': '{count} đối tượng ({size}) vẫn còn trên {from}, hãy thu hồi thủ công', 'storage.migrate.cancel': 'Hủy di chuyển', 'storage.migrate.promptCancel': 'Hủy', 'storage.migrate.queued': 'Đang chờ: {categories}', diff --git a/shared/src/i18n/zh-TW/collab.ts b/shared/src/i18n/zh-TW/collab.ts index f2aec4e45..c17e4b434 100644 --- a/shared/src/i18n/zh-TW/collab.ts +++ b/shared/src/i18n/zh-TW/collab.ts @@ -96,7 +96,7 @@ const collab: TranslationStrings = { 'collab.polls.options': '選項', 'collab.polls.delete': '刪除', 'collab.polls.closedSection': '已關閉', - 'collab.chat.imageLimit': '每則訊息最多可附加 {max} 張圖片', + 'collab.chat.imageLimit': '每則訊息最多可附加 {count} 張圖片', 'collab.chat.uploading': '上傳中 {percent}%', 'collab.chat.attachImages': '附加圖片', 'collab.chat.attachedImage': '附加的圖片', diff --git a/shared/src/i18n/zh-TW/dashboard.ts b/shared/src/i18n/zh-TW/dashboard.ts index ca9832f17..bd349b8a7 100644 --- a/shared/src/i18n/zh-TW/dashboard.ts +++ b/shared/src/i18n/zh-TW/dashboard.ts @@ -92,7 +92,7 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': '使用 {photographer} 的 Unsplash 照片', 'dashboard.titleRequired': '標題為必填項', 'dashboard.endDateError': '結束日期必須晚於開始日期', - 'dashboard.tripTooLong': '行程最長為 {days} 天', + 'dashboard.tripTooLong': '行程最長為 {count} 天', 'dashboard.dateShiftTitle': '新的開始日期', 'dashboard.dateShiftIntro': '你變更了旅行的開始時間。你的計劃要如何配合新日期?', 'dashboard.dateShiftKeepBookings': '預訂保留在原日期', diff --git a/shared/src/i18n/zh-TW/dawarich.ts b/shared/src/i18n/zh-TW/dawarich.ts index 46c96c942..e4af0370b 100644 --- a/shared/src/i18n/zh-TW/dawarich.ts +++ b/shared/src/i18n/zh-TW/dawarich.ts @@ -112,7 +112,7 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': '新增條目', 'dawarich.accept.confirm.bucket_list': '打勾', 'dawarich.accept.recorded': '記錄時間 {from} 至 {to}', - 'dawarich.accept.duration': '{minutes} 分鐘', + 'dawarich.accept.duration': '{count} 分鐘', 'dawarich.accept.name': '名稱', 'dawarich.accept.date': '日期', 'dawarich.accept.from': '抵達', @@ -170,7 +170,7 @@ const dawarich: TranslationStrings = { 'dawarich.again': '再查一次', 'dawarich.bucket.metersAway': '{meters} 公尺外', 'dawarich.bucket.kilometersAway': '{km} 公里外', - 'dawarich.bucket.rule': '在 {meters} 公尺內停留 {minutes} 分鐘以上,才算實現心願。', + 'dawarich.bucket.rule': '在 {meters} 公尺內停留 {count} 分鐘以上,才算實現心願。', 'dawarich.journey.dayStays.other': '來自 Dawarich 的 {count} 個停留點', }; diff --git a/shared/src/i18n/zh-TW/roadtrip.ts b/shared/src/i18n/zh-TW/roadtrip.ts index d37e5433b..3450e7cf9 100644 --- a/shared/src/i18n/zh-TW/roadtrip.ts +++ b/shared/src/i18n/zh-TW/roadtrip.ts @@ -49,7 +49,7 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.showPoint': '在地圖上顯示', 'roadtrip.window.incomplete': '所有連接路線可用後才能計算每日休息。', 'roadtrip.window.conflict': '固定時間與行車和休息安排衝突。手動時間保持不變,自動規劃已暫停。', - 'roadtrip.window.tooLong': '這些時間需要超過{days}個額外天數。請延長每日出行時段。', + 'roadtrip.window.tooLong': '這些時間需要超過{count}個額外天數。請延長每日出行時段。', 'roadtrip.mode.roadtrip': '公路旅行', 'roadtrip.mode.days': '依日', 'roadtrip.mode.label': '計畫檢視', @@ -95,8 +95,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': '分鐘', 'roadtrip.limit.off': '關閉', 'roadtrip.limit.blockNote': '一格 = {distance}', - 'roadtrip.limit.echoOver': '{days} 天中有 {over} 天超出上限。', - 'roadtrip.limit.echoNone': '{days} 天都在上限之內。', + 'roadtrip.limit.echoOver': '{count} 天中有 {over} 天超出上限。', + 'roadtrip.limit.echoNone': '{count} 天都在上限之內。', 'roadtrip.limit.echoDry': '{count} 個路段沒有可抵達的停靠點。', 'roadtrip.limit.echoEmpty': '開啟一次公路旅行即可看到效果。', 'roadtrip.limit.specToggle': '依車輛資料推算', @@ -244,8 +244,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': '依行駛時間推算', 'roadtrip.stop.endIsLeave': '在公路旅行中,將在此時間出發。', 'roadtrip.via.hint': '拖曳可改變路線,右鍵刪除', - 'roadtrip.warn.late': '比設定時間晚到 {minutes} 分鐘', - 'roadtrip.warn.missedLeave': '比設定的出發時間晚到 {minutes} 分鐘', + 'roadtrip.warn.late': '比設定時間晚到 {count} 分鐘', + 'roadtrip.warn.missedLeave': '比設定的出發時間晚到 {count} 分鐘', 'roadtrip.warn.overnight': '次日', 'roadtrip.spill.title': '來自第 {number} 天', 'roadtrip.spill.departs': '{time} 出發', diff --git a/shared/src/i18n/zh-TW/settings.ts b/shared/src/i18n/zh-TW/settings.ts index a80e05b53..e884fac3d 100644 --- a/shared/src/i18n/zh-TW/settings.ts +++ b/shared/src/i18n/zh-TW/settings.ts @@ -584,7 +584,7 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': '端點', 'settings.apiKeys.neverUsed': '從未使用', 'settings.apiKeys.loadFailed': '無法載入你的金鑰。請重新整理頁面後再試一次。', - 'settings.apiKeys.limitReached': '你已有 {max} 組金鑰,已達每個帳號的上限。請刪除一組不再使用的金鑰後再建立。', + 'settings.apiKeys.limitReached': '你已有 {count} 組金鑰,已達每個帳號的上限。請刪除一組不再使用的金鑰後再建立。', 'settings.apiKeys.copyFailed': '無法複製。請選取文字後手動複製。', 'settings.apiKeys.modal.createTitle': '建立 API 金鑰', 'settings.apiKeys.modal.name': '名稱', diff --git a/shared/src/i18n/zh-TW/storage.ts b/shared/src/i18n/zh-TW/storage.ts index 5b730ab12..5887ac87a 100644 --- a/shared/src/i18n/zh-TW/storage.ts +++ b/shared/src/i18n/zh-TW/storage.ts @@ -112,7 +112,7 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': '要將現有物件移動到新的後端嗎?', - 'storage.migrate.promptLine': '{category}:從 {from} 到 {to} 共 {objects} 個物件({size})', + 'storage.migrate.promptLine': '{category}:從 {from} 到 {to} 共 {count} 個物件({size})', 'storage.migrate.promptLineUnknown': '{category}:大小未知(尚未掃描用量)從 {from} 到 {to}', 'storage.migrate.move': '移動現有物件', 'storage.migrate.routeOnly': '僅路由新寫入', @@ -121,7 +121,7 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} 個失敗 — 這些物件未複製到新的後端', 'storage.migrate.failed': '移動失敗:{error} — 該分類未切換', 'storage.migrate.cancelled': '移動已取消 — 未切換任何內容', - 'storage.migrate.reclaimable': '{objects} 個物件({size})仍保留在 {from} 上 — 請手動回收', + 'storage.migrate.reclaimable': '{count} 個物件({size})仍保留在 {from} 上,請手動回收', 'storage.migrate.cancel': '取消移動', 'storage.migrate.promptCancel': '取消', 'storage.migrate.queued': '排隊中:{categories}', diff --git a/shared/src/i18n/zh/collab.ts b/shared/src/i18n/zh/collab.ts index 5edba8643..a82fa9dc0 100644 --- a/shared/src/i18n/zh/collab.ts +++ b/shared/src/i18n/zh/collab.ts @@ -96,7 +96,7 @@ const collab: TranslationStrings = { 'collab.polls.options': '选项', 'collab.polls.delete': '删除', 'collab.polls.closedSection': '已关闭', - 'collab.chat.imageLimit': '每条消息最多可附加 {max} 张图片', + 'collab.chat.imageLimit': '每条消息最多可附加 {count} 张图片', 'collab.chat.uploading': '上传中 {percent}%', 'collab.chat.attachImages': '附加图片', 'collab.chat.attachedImage': '附加的图片', diff --git a/shared/src/i18n/zh/dashboard.ts b/shared/src/i18n/zh/dashboard.ts index e518e562d..438bcfdeb 100644 --- a/shared/src/i18n/zh/dashboard.ts +++ b/shared/src/i18n/zh/dashboard.ts @@ -92,7 +92,7 @@ const dashboard: TranslationStrings = { 'dashboard.useUnsplashPhoto': '使用 {photographer} 的 Unsplash 照片', 'dashboard.titleRequired': '标题为必填项', 'dashboard.endDateError': '结束日期必须晚于开始日期', - 'dashboard.tripTooLong': '行程最长为 {days} 天', + 'dashboard.tripTooLong': '行程最长为 {count} 天', 'dashboard.dateShiftTitle': '新的开始日期', 'dashboard.dateShiftIntro': '您更改了旅行的开始时间。您的计划应如何适应新的日期?', 'dashboard.dateShiftKeepBookings': '保留预订的原定日期', diff --git a/shared/src/i18n/zh/dawarich.ts b/shared/src/i18n/zh/dawarich.ts index b892da5e5..83ce4c584 100644 --- a/shared/src/i18n/zh/dawarich.ts +++ b/shared/src/i18n/zh/dawarich.ts @@ -112,7 +112,7 @@ const dawarich: TranslationStrings = { 'dawarich.accept.confirm.journal': '添加条目', 'dawarich.accept.confirm.bucket_list': '勾掉它', 'dawarich.accept.recorded': '记录时间:{from} 至 {to}', - 'dawarich.accept.duration': '{minutes} 分钟', + 'dawarich.accept.duration': '{count} 分钟', 'dawarich.accept.name': '名称', 'dawarich.accept.date': '日期', 'dawarich.accept.from': '到达', @@ -170,7 +170,7 @@ const dawarich: TranslationStrings = { 'dawarich.again': '再查一次', 'dawarich.bucket.metersAway': '{meters} 米外', 'dawarich.bucket.kilometersAway': '{km} 公里外', - 'dawarich.bucket.rule': '在 {meters} 米以内停留 {minutes} 分钟以上,才算实现心愿。', + 'dawarich.bucket.rule': '在 {meters} 米以内停留 {count} 分钟以上,才算实现心愿。', 'dawarich.journey.dayStays.other': '来自 Dawarich 的 {count} 条停留记录', }; diff --git a/shared/src/i18n/zh/roadtrip.ts b/shared/src/i18n/zh/roadtrip.ts index f38a5b47c..9cbafe138 100644 --- a/shared/src/i18n/zh/roadtrip.ts +++ b/shared/src/i18n/zh/roadtrip.ts @@ -49,7 +49,7 @@ const roadtrip: TranslationStrings = { 'roadtrip.window.showPoint': '在地图上显示', 'roadtrip.window.incomplete': '所有连接路线可用后才能计算每日休息。', 'roadtrip.window.conflict': '固定时间与行车和休息安排冲突。手动时间保持不变,自动规划已暂停。', - 'roadtrip.window.tooLong': '这些时间需要超过{days}个额外天数。请延长每日出行时段。', + 'roadtrip.window.tooLong': '这些时间需要超过{count}个额外天数。请延长每日出行时段。', 'roadtrip.mode.roadtrip': '公路旅行', 'roadtrip.mode.days': '按天', 'roadtrip.mode.label': '计划视图', @@ -95,8 +95,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.limit.minutes': '分钟', 'roadtrip.limit.off': '关闭', 'roadtrip.limit.blockNote': '一格 = {distance}', - 'roadtrip.limit.echoOver': '{days} 天中有 {over} 天超出上限。', - 'roadtrip.limit.echoNone': '{days} 天都在上限之内。', + 'roadtrip.limit.echoOver': '{count} 天中有 {over} 天超出上限。', + 'roadtrip.limit.echoNone': '{count} 天都在上限之内。', 'roadtrip.limit.echoDry': '{count} 个路段没有可到达的停留点。', 'roadtrip.limit.echoEmpty': '打开一次公路旅行即可看到效果。', 'roadtrip.limit.specToggle': '根据车辆数据推算', @@ -245,8 +245,8 @@ const roadtrip: TranslationStrings = { 'roadtrip.stop.computed': '根据行驶时间推算', 'roadtrip.stop.endIsLeave': '在公路旅行中,将在此时间出发。', 'roadtrip.via.hint': '拖动可改变路线,右键删除', - 'roadtrip.warn.late': '比设定时间晚到 {minutes} 分钟', - 'roadtrip.warn.missedLeave': '比设定的出发时间晚到 {minutes} 分钟', + 'roadtrip.warn.late': '比设定时间晚到 {count} 分钟', + 'roadtrip.warn.missedLeave': '比设定的出发时间晚到 {count} 分钟', 'roadtrip.warn.overnight': '次日', 'roadtrip.spill.title': '来自第 {number} 天', 'roadtrip.spill.departs': '{time} 出发', diff --git a/shared/src/i18n/zh/settings.ts b/shared/src/i18n/zh/settings.ts index 0d52e00fb..12d0b1df1 100644 --- a/shared/src/i18n/zh/settings.ts +++ b/shared/src/i18n/zh/settings.ts @@ -580,7 +580,7 @@ const settings: TranslationStrings = { 'settings.apiKeys.endpoint': '端点', 'settings.apiKeys.neverUsed': '从未使用', 'settings.apiKeys.loadFailed': '无法加载你的密钥。请刷新页面后重试。', - 'settings.apiKeys.limitReached': '你已有 {max} 个密钥,已达到每个账户的上限。请删除一个不再使用的密钥后再创建。', + 'settings.apiKeys.limitReached': '你已有 {count} 个密钥,已达到每个账户的上限。请删除一个不再使用的密钥后再创建。', 'settings.apiKeys.copyFailed': '无法复制。请选中文本后手动复制。', 'settings.apiKeys.modal.createTitle': '创建 API 密钥', 'settings.apiKeys.modal.name': '名称', diff --git a/shared/src/i18n/zh/storage.ts b/shared/src/i18n/zh/storage.ts index 2b66c0b55..eb838cc61 100644 --- a/shared/src/i18n/zh/storage.ts +++ b/shared/src/i18n/zh/storage.ts @@ -111,7 +111,7 @@ const storage: TranslationStrings = { // Category migration (copy → flip → delta sweep) 'storage.migrate.promptTitle': '将现有对象移动到新后端?', - 'storage.migrate.promptLine': '{category}:从 {from} 到 {to} 共 {objects} 个对象({size})', + 'storage.migrate.promptLine': '{category}:从 {from} 到 {to} 共 {count} 个对象({size})', 'storage.migrate.promptLineUnknown': '{category}:大小未知(尚未扫描用量)从 {from} 到 {to}', 'storage.migrate.move': '移动现有对象', 'storage.migrate.routeOnly': '仅路由新写入', @@ -120,7 +120,7 @@ const storage: TranslationStrings = { 'storage.migrate.doneFailures': '{failed} 个失败 — 这些对象未复制到新后端', 'storage.migrate.failed': '移动失败:{error} — 该分类未切换', 'storage.migrate.cancelled': '移动已取消 — 未切换任何内容', - 'storage.migrate.reclaimable': '{objects} 个对象({size})仍保留在 {from} 上 — 请手动回收', + 'storage.migrate.reclaimable': '{count} 个对象({size})仍保留在 {from} 上,请手动回收', 'storage.migrate.cancel': '取消移动', 'storage.migrate.promptCancel': '取消', 'storage.migrate.queued': '排队中:{categories}',