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 ? (
- {t('roadtrip.warn.missedLeave', { minutes: leave.missedBy })}
+ {t('roadtrip.warn.missedLeave', { count: leave.missedBy })}
) : (
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}',