diff --git a/client/src/components/Admin/AddonManager.test.tsx b/client/src/components/Admin/AddonManager.test.tsx index 14afcc882..e97dc008a 100644 --- a/client/src/components/Admin/AddonManager.test.tsx +++ b/client/src/components/Admin/AddonManager.test.tsx @@ -1,5 +1,5 @@ // FE-ADMIN-ADDON-001 to FE-ADMIN-ADDON-025 -import { render, screen, waitFor, within } from '../../../tests/helpers/render'; +import { render, screen, waitFor } from '../../../tests/helpers/render'; import userEvent from '@testing-library/user-event'; import { delay, http, HttpResponse } from 'msw'; import { server } from '../../../tests/helpers/msw/server'; @@ -44,20 +44,16 @@ function modelsRoute(names: string[], seen?: (string | null)[]) { }); } -/** The pill toggle of a top-level addon row. */ +/** Every switch is labelled with the thing it controls, top-level tile or shelf + * row alike, so one lookup covers both. */ function addonToggle(name: string): HTMLElement { - const row = screen.getByText(name).closest('.px-6.py-4') as HTMLElement; - return within(row).getByRole('button'); + return screen.getByRole('button', { name }); } -/** The pill toggle of an indented sub-row (bag tracking, collab feature, photo provider). */ -function subToggle(label: string): HTMLElement { - const row = screen.getByText(label).closest('.flex.items-center.gap-4') as HTMLElement; - return within(row).getByRole('button'); -} +const subToggle = addonToggle; function isOn(toggle: HTMLElement): boolean { - return toggle.style.background === 'var(--text-primary)'; + return toggle.getAttribute('aria-pressed') === 'true'; } beforeAll(() => { @@ -82,6 +78,7 @@ beforeEach(() => { afterEach(() => { vi.restoreAllMocks(); + document.documentElement.classList.remove('dark'); }); describe('AddonManager', () => { @@ -144,13 +141,10 @@ describe('AddonManager', () => { render(<>); await screen.findByText('Todo List'); - // Get toggle button - use getAllByRole since there might be multiple buttons - const buttons = screen.getAllByRole('button'); - const toggleBtn = buttons.find(b => b.classList.contains('rounded-full')); - expect(toggleBtn).toBeInTheDocument(); + const toggleBtn = addonToggle('Todo List'); + expect(isOn(toggleBtn)).toBe(false); - // Before click - disabled state (border-primary bg) - await user.click(toggleBtn!); + await user.click(toggleBtn); // After click - success toast await screen.findByText('Addon updated'); @@ -169,17 +163,14 @@ describe('AddonManager', () => { render(<>); await screen.findByText('Todo List'); - const buttons = screen.getAllByRole('button'); - const toggleBtn = buttons.find(b => b.classList.contains('rounded-full')); - await user.click(toggleBtn!); + await user.click(addonToggle('Todo List')); // Error toast appears await screen.findByText('Failed to update addon'); - // The disabled text should be back after rollback + // The switch is back off after rollback await waitFor(() => { - const disabledTexts = screen.getAllByText('Disabled'); - expect(disabledTexts.length).toBeGreaterThan(0); + expect(isOn(addonToggle('Todo List'))).toBe(false); }); }); @@ -195,13 +186,7 @@ describe('AddonManager', () => { ); await screen.findByText('Bag Tracking'); - const bagTrackingToggle = screen.getAllByRole('button').find(b => - b.closest('[style*="paddingLeft: 70"]') !== null || b.closest('div')?.textContent?.includes('Bag Tracking') - ); - // Click the bag tracking toggle button (the h-6 w-11 button near "Bag Tracking") - const allBtns = screen.getAllByRole('button').filter(b => b.classList.contains('rounded-full')); - // There should be two toggle buttons: one for the addon, one for bag tracking - await user.click(allBtns[allBtns.length - 1]); + await user.click(subToggle('Bag Tracking')); expect(mockToggle).toHaveBeenCalled(); }); @@ -251,9 +236,10 @@ describe('AddonManager', () => { // Journey addon is rendered expect(screen.getByText('Journey')).toBeInTheDocument(); - // Toggle buttons: journey toggle + 2 provider toggles - const toggleBtns = screen.getAllByRole('button').filter(b => b.classList.contains('rounded-full')); - expect(toggleBtns.length).toBe(3); + // Journey's own switch plus one per provider, each labelled with what it controls + expect(isOn(addonToggle('Journey'))).toBe(true); + expect(isOn(subToggle('Unsplash'))).toBe(true); + expect(isOn(subToggle('Pexels'))).toBe(false); }); it('FE-ADMIN-ADDON-011: icon falls back to Puzzle when icon name unknown', async () => { @@ -278,7 +264,8 @@ describe('AddonManager', () => { }); it('FE-ADMIN-ADDON-013: dark mode swaps the wordmark in the header', async () => { - seedStore(useSettingsStore, { settings: { dark_mode: 'dark' } }); + // The wordmark reads the .dark class, the same source applyAppearance() writes. + document.documentElement.classList.add('dark'); render(); await screen.findByText('No addons available'); @@ -309,11 +296,11 @@ describe('AddonManager', () => { await screen.findByText('Immich'); // immich and synologyphotos ship a vendor glyph, unsplash does not - const immichRow = screen.getByText('Immich').closest('.flex.items-center.gap-4') as HTMLElement; + const immichRow = screen.getByText('Immich').closest('li') as HTMLElement; expect(immichRow.querySelector('svg')).toBeInTheDocument(); - const synologyRow = screen.getByText('Synology Photos').closest('.flex.items-center.gap-4') as HTMLElement; + const synologyRow = screen.getByText('Synology Photos').closest('li') as HTMLElement; expect(synologyRow.querySelector('svg')).toBeInTheDocument(); - const unsplashRow = screen.getByText('Unsplash').closest('.flex.items-center.gap-4') as HTMLElement; + const unsplashRow = screen.getByText('Unsplash').closest('li') as HTMLElement; expect(unsplashRow.querySelector('svg')).not.toBeInTheDocument(); expect(isOn(subToggle('Immich'))).toBe(true); @@ -396,7 +383,9 @@ describe('AddonManager', () => { render(); await screen.findByText('AI Parsing'); - expect(screen.getByText('Extract bookings from files')).toBeInTheDocument(); + // The catalog key now exists, so the tile shows the translation rather than falling + // back to the English description the server sent. + expect(screen.getByText('Reads bookings the built-in parser cannot, using an AI model you choose')).toBeInTheDocument(); expect(screen.queryByText('Connection')).not.toBeInTheDocument(); }); diff --git a/client/src/components/Admin/AddonManager.tsx b/client/src/components/Admin/AddonManager.tsx index c5ea9e6ce..9a3c39318 100644 --- a/client/src/components/Admin/AddonManager.tsx +++ b/client/src/components/Admin/AddonManager.tsx @@ -1,14 +1,20 @@ import { useEffect, useState, type ComponentType } from 'react' import { adminApi } from '../../api/client' import { useTranslation } from '../../i18n' -import { useSettingsStore } from '../../store/settingsStore' import { useAddonStore } from '../../store/addonStore' +import { useIsDark } from '../../hooks/useIsDark' import { useToast } from '../shared/Toast' -import { Puzzle, ListChecks, Wallet, FileText, CalendarDays, Globe, Briefcase, Image, Terminal, Link2, Compass, BookOpen, MessageCircle, StickyNote, BarChart3, Sparkles, Luggage, Plane, Server, Cloud, Bookmark } from 'lucide-react' +import { Puzzle, ListChecks, Wallet, FileText, CalendarDays, Globe, Briefcase, Image, Terminal, Link2, Compass, BookOpen, MessageCircle, StickyNote, BarChart3, Sparkles, Luggage, Plane, Server, Cloud, Bookmark, Users, Loader2 } from 'lucide-react' import CustomSelect from '../shared/CustomSelect' +import EmptyState from '../shared/EmptyState' +import AddonTile from './AddonTile' +import AddonSubRow from './AddonSubRow' +// Keys are the `icon` column from the addons table (see server seeds.ts); anything +// unknown falls back to Puzzle. Users/Sparkles cover collab and llm_parsing, which +// used to land on the fallback. const ICON_MAP = { - ListChecks, Wallet, FileText, CalendarDays, Puzzle, Globe, Briefcase, Image, Terminal, Link2, Compass, BookOpen, Plane, Bookmark, + ListChecks, Wallet, FileText, CalendarDays, Puzzle, Globe, Briefcase, Image, Terminal, Link2, Compass, BookOpen, Plane, Bookmark, Users, Sparkles, } function ImmichIcon({ size = 14 }: { size?: number }) { @@ -60,6 +66,14 @@ function AddonIcon({ name, size = 20 }: AddonIconProps) { return } +/** What each type means, shown on the tile itself now that the sections are gone. The + * hint rides along as the tooltip, so "Global" still explains itself. */ +const TYPE_META: Record; labelKey: string; hintKey: string }> = { + trip: { icon: Briefcase, labelKey: 'admin.addons.type.trip', hintKey: 'admin.addons.tripHint' }, + global: { icon: Globe, labelKey: 'admin.addons.type.global', hintKey: 'admin.addons.globalHint' }, + integration: { icon: Link2, labelKey: 'admin.addons.type.integration', hintKey: 'admin.addons.integrationHint' }, +} + interface CollabFeatures { chat: boolean; notes: boolean; polls: boolean; whatsnext: boolean } const COLLAB_SUB_FEATURES = [ @@ -71,8 +85,7 @@ const COLLAB_SUB_FEATURES = [ export default function AddonManager({ bagTrackingEnabled, onToggleBagTracking, collabFeatures, onToggleCollabFeature }: { bagTrackingEnabled?: boolean; onToggleBagTracking?: () => void; collabFeatures?: CollabFeatures; onToggleCollabFeature?: (key: string) => void }) { const { t } = useTranslation() - const dm = useSettingsStore(s => s.settings.dark_mode) - const dark = dm === true || dm === 'dark' || (dm === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) + const dark = useIsDark() const toast = useToast() const refreshGlobalAddons = useAddonStore(s => s.loadAddons) const [addons, setAddons] = useState([]) @@ -94,16 +107,16 @@ export default function AddonManager({ bagTrackingEnabled, onToggleBagTracking, } } + /** Optimistic flip, per-row rollback on failure. Rolling the whole addons + * snapshot back instead would undo a toggle the user hit in parallel. */ const handleToggle = async (addon: Addon) => { const newEnabled = !addon.enabled - // Optimistic update setAddons(prev => prev.map(a => a.id === addon.id ? { ...a, enabled: newEnabled } : a)) try { await adminApi.updateAddon(addon.id, { enabled: newEnabled }) refreshGlobalAddons() toast.success(t('admin.addons.toast.updated')) } catch (err: unknown) { - // Rollback setAddons(prev => prev.map(a => a.id === addon.id ? { ...a, enabled: !newEnabled } : a)) toast.error(t('admin.addons.toast.error')) } @@ -118,24 +131,7 @@ export default function AddonManager({ bagTrackingEnabled, onToggleBagTracking, return addon.type === 'trip' && (addon.icon === 'Image' || haystack.includes('photo') || haystack.includes('memories')) } - const handleTogglePhotoProvider = async (providerAddon: Addon) => { - const enableProvider = !providerAddon.enabled - const prev = addons - - setAddons(current => current.map(a => a.id === providerAddon.id ? { ...a, enabled: enableProvider } : a)) - - try { - await adminApi.updateAddon(providerAddon.id, { enabled: enableProvider }) - refreshGlobalAddons() - toast.success(t('admin.addons.toast.updated')) - } catch { - setAddons(prev) - toast.error(t('admin.addons.toast.error')) - } - } - const photoProviderAddons = addons.filter(isPhotoProviderAddon) - const photosAddon = addons.filter(a => a.type === 'trip').find(isPhotosAddon) const tripAddons = addons.filter(a => a.type === 'trip' && !isPhotosAddon(a)) const globalAddons = addons.filter(a => a.type === 'global') const integrationAddons = addons.filter(a => a.type === 'integration') @@ -144,173 +140,163 @@ export default function AddonManager({ bagTrackingEnabled, onToggleBagTracking, label: provider.name, description: provider.description, enabled: provider.enabled, - toggle: () => handleTogglePhotoProvider(provider), + toggle: () => handleToggle(provider), })) - const photosDerivedEnabled = providerOptions.some(p => p.enabled) if (loading) { return ( -
-
+
+
) } + /** Bag tracking, the collab features and the photo providers all hang off their + * parent as shelf rows. The conditions are deliberately the ones that were here + * before — note that the providers render even when Journey itself is off. */ + const shelfFor = (addon: Addon) => { + if (addon.id === 'packing' && addon.enabled && onToggleBagTracking) { + return ( + } + title={t('admin.bagTracking.title')} + description={t('admin.bagTracking.subtitle')} + enabled={!!bagTrackingEnabled} + onToggle={onToggleBagTracking} + /> + ) + } + if (addon.id === 'collab' && addon.enabled && collabFeatures && onToggleCollabFeature) { + return COLLAB_SUB_FEATURES.map(feat => { + const Icon = feat.icon + return ( + } + title={t(feat.titleKey)} + description={t(feat.subtitleKey)} + enabled={collabFeatures[feat.key]} + onToggle={() => onToggleCollabFeature(feat.key)} + /> + ) + }) + } + if (addon.id === 'journey' && providerOptions.length > 0) { + return providerOptions.map(provider => { + const ProviderIcon = PROVIDER_ICONS[provider.key] + return ( + : undefined} + title={provider.label} + description={provider.description} + enabled={provider.enabled} + onToggle={provider.toggle} + /> + ) + }) + } + return null + } + + const tile = (addon: Addon) => { + const label = getAddonLabel(t, addon) + return ( + } + name={label.name} + description={label.description} + enabled={addon.enabled} + onToggle={() => handleToggle(addon)} + > + {shelfFor(addon)} + + ) + } + + const llmAddon = integrationAddons.find(a => a.id === 'llm_parsing') + + /* One column per type, side by side, instead of three stacked sections. Stacked, each + section opened its own set of columns and the tall tiles (Collab 261px, Journey + 185px) set a section's height while its neighbours ran out early. Side by side the + column heading carries the type, so the tiles need no badge of their own. */ + const groups = [ + { key: 'trip', addons: tripAddons }, + { key: 'global', addons: globalAddons }, + { key: 'integration', addons: integrationAddons }, + ].filter(g => g.addons.length > 0) + const enabledCount = addons.filter(a => a.type !== 'photo_provider' && a.enabled).length + const totalCount = tripAddons.length + globalAddons.length + integrationAddons.length + return ( -
- {/* Header */} -
-
-

{t('admin.addons.title')}

-

- {t('admin.addons.subtitleBefore')}TREK{t('admin.addons.subtitleAfter')} +

+
+
+

{t('admin.addons.title')}

+

+ {t('admin.addons.subtitleBefore')} + TREK + {t('admin.addons.subtitleAfter')}

- - {addons.length === 0 ? ( -
- {t('admin.addons.noAddons')} -
- ) : ( -
- {/* Trip Addons */} - {tripAddons.length > 0 && ( -
-
- - - {t('admin.addons.type.trip')} — {t('admin.addons.tripHint')} - -
- {tripAddons.map(addon => ( -
- - {addon.id === 'packing' && addon.enabled && onToggleBagTracking && ( -
- -
-
{t('admin.bagTracking.title')}
-
{t('admin.bagTracking.subtitle')}
-
-
- - {bagTrackingEnabled ? t('admin.addons.enabled') : t('admin.addons.disabled')} - - -
-
- )} - {addon.id === 'collab' && addon.enabled && collabFeatures && onToggleCollabFeature && ( -
-
- {COLLAB_SUB_FEATURES.map(feat => { - const enabled = collabFeatures[feat.key] - const Icon = feat.icon - return ( -
- -
-
{t(feat.titleKey)}
-
{t(feat.subtitleKey)}
-
-
- - {enabled ? t('admin.addons.enabled') : t('admin.addons.disabled')} - - -
-
- ) - })} -
-
- )} -
- ))} -
- )} - - {/* Global Addons */} - {globalAddons.length > 0 && ( -
-
- - - {t('admin.addons.type.global')} — {t('admin.addons.globalHint')} - -
- {globalAddons.map(addon => ( -
- - {/* Memories providers as sub-items under Journey addon */} - {addon.id === 'journey' && providerOptions.length > 0 && ( -
-
- {providerOptions.map(provider => { - const ProviderIcon = PROVIDER_ICONS[provider.key] - return ( -
- {ProviderIcon && } -
-
{provider.label}
-
{provider.description}
-
-
- - {provider.enabled ? t('admin.addons.enabled') : t('admin.addons.disabled')} - - -
-
- ) - })} -
-
- )} -
- ))} -
- )} - - {/* Integration Addons */} - {integrationAddons.length > 0 && ( -
-
- - - {t('admin.addons.type.integration')} — {t('admin.addons.integrationHint')} - -
- {integrationAddons.map(addon => ( -
- - {addon.id === 'llm_parsing' && addon.enabled && ( - - )} -
- ))} -
- )} -
+ {totalCount > 0 && ( + + {enabledCount}/{totalCount} + )}
+ + {addons.length === 0 ? ( + + ) : ( + <> +
+ {groups.map(g => { + const meta = TYPE_META[g.key] + return ( +
+ +
{g.addons.map(tile)}
+
+ ) + })} +
+ {/* The AI-parsing form is far too wide for a tile, so it sits as a band under + the columns — no span, no reflow, and the next service that needs settings + stacks below it. */} + {llmAddon?.enabled && } + + )} +
+ ) +} + +function GroupHead({ icon: Icon, label, hint, addons, t }: { + icon: ComponentType<{ size?: number; className?: string }> + label: string + hint: string + addons: Addon[] + t: (key: string, params?: Record) => string +}) { + const enabled = addons.filter(a => a.enabled).length + return ( +
+
+ +

{label}

+ + {enabled}/{addons.length} + +
+ {/* Says what the type means — the only place that still explains it. */} +

{hint}

) } @@ -408,9 +394,9 @@ function LlmParsingConfig({ addon }: { addon: Addon }) { } } - const fieldCls = 'w-full rounded-lg border border-edge-secondary bg-surface px-3 py-2 text-sm text-content placeholder:text-content-faint transition-colors focus:border-edge focus:outline-none' - const labelCls = 'mb-1.5 block text-xs font-medium text-content-secondary' - const sectionCls = 'text-[11px] font-semibold uppercase tracking-wide text-content-faint' + const fieldCls = 'w-full rounded-lg border border-edge-secondary bg-surface px-3 py-2 text-body text-content placeholder:text-content-faint transition-colors focus:border-edge focus:outline-none' + const labelCls = 'mb-1.5 block text-caption font-medium text-content-secondary' + const sectionCls = 'text-caption font-semibold uppercase tracking-wide text-content-faint' const providerOptions = [ { value: 'local', label: 'Local · OpenAI-compatible', icon: , badge: 'Ollama' }, @@ -419,9 +405,13 @@ function LlmParsingConfig({ addon }: { addon: Addon }) { ] return ( -
-
-

+

+
+ + {addon.name} +
+
+

Set instance-wide config (applies to all users). Leave blank to let each user configure their own provider.

@@ -464,7 +454,7 @@ function LlmParsingConfig({ addon }: { addon: Addon }) { {loadingModels ? 'Loading…' : 'Refresh'}
- {modelsErr &&

{modelsErr}

} + {modelsErr &&

{modelsErr}

} {!modelsErr && installed.length === 0 && !loadingModels && (

No models installed yet — pull one below.

)} @@ -496,7 +486,7 @@ function LlmParsingConfig({ addon }: { addon: Addon }) {
{m.label} {m.recommended && ( - Recommended + Recommended )}
{m.note}
@@ -505,7 +495,7 @@ function LlmParsingConfig({ addon }: { addon: Addon }) {
-
{pullStatus}{pullPct ? ` · ${pullPct}%` : ''}
+
{pullStatus}{pullPct ? ` · ${pullPct}%` : ''}
)}
@@ -527,22 +517,14 @@ function LlmParsingConfig({ addon }: { addon: Addon }) { )} -
-
+ ) } -interface AddonRowProps { - addon: Addon - onToggle: (addon: Addon) => void - t: (key: string) => string - statusOverride?: boolean - hideToggle?: boolean -} - function getAddonLabel(t: (key: string) => string, addon: Addon): { name: string; description: string } { const nameKey = `admin.addons.catalog.${addon.id}.name` const descKey = `admin.addons.catalog.${addon.id}.description` @@ -555,56 +537,3 @@ function getAddonLabel(t: (key: string) => string, addon: Addon): { name: string } } -function AddonRow({ addon, onToggle, t, nameOverride, descriptionOverride, statusOverride, hideToggle }: AddonRowProps & { nameOverride?: string; descriptionOverride?: string }) { - const isComingSoon = false - const label = getAddonLabel(t, addon) - const displayName = nameOverride || label.name - const displayDescription = descriptionOverride || label.description - const enabledState = statusOverride ?? addon.enabled - return ( -
- {/* Icon */} -
- -
- - {/* Info */} -
-
- {displayName} - {isComingSoon && ( - - Coming Soon - - )} - - {addon.type === 'global' ? t('admin.addons.type.global') : addon.type === 'integration' ? t('admin.addons.type.integration') : t('admin.addons.type.trip')} - -
-

{displayDescription}

-
- - {/* Toggle */} -
- - {isComingSoon ? t('admin.addons.disabled') : enabledState ? t('admin.addons.enabled') : t('admin.addons.disabled')} - - {!hideToggle && ( - - )} -
-
- ) -} diff --git a/client/src/components/Admin/AddonSubRow.tsx b/client/src/components/Admin/AddonSubRow.tsx new file mode 100644 index 000000000..dc529c9e8 --- /dev/null +++ b/client/src/components/Admin/AddonSubRow.tsx @@ -0,0 +1,40 @@ +import type { ReactNode } from 'react' +import ToggleSwitch from '../Settings/ToggleSwitch' + +/** + * One child row inside an {@link AddonTile}'s sub-shelf: bag tracking under + * Lists, a collab feature under Collab, a photo provider under Journey. + * + * A photo provider without a vendor mark has to stay free of `` + * (FE-ADMIN-ADDON-015), which is why `icon` falls back to a fixed-width spacer + * rather than a placeholder glyph. Nothing else belongs in this row either: no + * chevron, no status dot, no lucide fallback. + * + * The row carries its title only; the description rides along as the `title` + * tooltip, because a second line of text is what makes a child weigh as much as + * an addon. + */ +export default function AddonSubRow({ + icon, + title, + description, + enabled, + onToggle, +}: { + /** Vendor mark or lucide glyph at 14px. Omit it and the slot keeps its width. */ + icon?: ReactNode + title: string + description?: string + enabled: boolean + onToggle: () => void +}) { + return ( +
  • + {icon ? {icon} : } + + {title} + + +
  • + ) +} diff --git a/client/src/components/Admin/AddonTile.tsx b/client/src/components/Admin/AddonTile.tsx new file mode 100644 index 000000000..99e68f1c6 --- /dev/null +++ b/client/src/components/Admin/AddonTile.tsx @@ -0,0 +1,80 @@ +import type { ReactNode } from 'react' +import ToggleSwitch from '../Settings/ToggleSwitch' + +/** + * One addon in the admin grid: a card whose fill carries the on/off state, with + * an optional shelf at the foot holding its sub-features. + * + * The fill ladder is `--bg-secondary` (on) against `--bg-card` (the panel it + * sits on) — that pair reads in both themes, because secondary is *darker* than + * the card in light and *lighter* in dark. The obvious alternative, on = + * `bg-surface-card`, inverts in dark and turns the disabled tiles into the loud + * ones. + * + * Hover and focus ride on border and fill rather than a shadow: `--shadow-card` + * is only declared in `:root`, so the `.dark` palette never overrides it and a + * shadow-based affordance would be invisible there. + * + * The card is not a click target — only the switches are. No lift, no pointer + * cursor, no `role="button"`. + */ +export default function AddonTile({ + icon, + name, + description, + enabled, + onToggle, + children, +}: { + icon: ReactNode + name: string + description?: string + enabled: boolean + onToggle: () => void + /** The sub-shelf: `` children. */ + children?: ReactNode +}) { + return ( +
    +
    +
    + {icon} +
    + +
    + {/* The name stays full-strength when off: it is the tile's identity, not its + state. Greying it out reads as "broken" rather than "not enabled". */} +

    + {name} +

    +

    + {description} +

    +
    + + +
    + + {children && ( +
    + {/* Sitting inside the parent on its own fill is enough to say these belong + to it — no rail, no indent guides. */} +
      {children}
    +
    + )} +
    + ) +} diff --git a/client/src/components/Settings/ToggleSwitch.tsx b/client/src/components/Settings/ToggleSwitch.tsx index a4a7b5fdf..1c339580a 100644 --- a/client/src/components/Settings/ToggleSwitch.tsx +++ b/client/src/components/Settings/ToggleSwitch.tsx @@ -9,10 +9,14 @@ export default function ToggleSwitch({ on, onToggle, label }: { on: boolean; onT background: on ? 'var(--accent, #111827)' : 'var(--border-primary, #d1d5db)', transition: 'background 0.2s', }}> + {/* The knob reads against its own track: --accent-text is what the palette + already picked to sit on --accent, so a light accent gets a dark knob + instead of the white-on-near-white it used to be in dark mode. */} ) diff --git a/client/src/hooks/useIsDark.ts b/client/src/hooks/useIsDark.ts new file mode 100644 index 000000000..0e3854aac --- /dev/null +++ b/client/src/hooks/useIsDark.ts @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react' + +/** + * Returns true while the dark palette is active, read off the `.dark` class on + * `` — the single source of truth that `applyAppearance()` writes. + * + * Deriving it from the settings store instead (`dark_mode === 'auto' && + * matchMedia(...)`) duplicates theme state into React and misses an OS-level + * switch under `auto`, because nothing re-renders when the media query flips. + * The observer sees every path, including the pre-paint replay in + * `public/theme-boot.js`. + * + * Only for the handful of places that need the flag in JS — an `` that + * differs per theme. Colours belong on tokens, not on this hook. + */ +export function useIsDark(): boolean { + const [dark, setDark] = useState( + () => typeof document !== 'undefined' && document.documentElement.classList.contains('dark'), + ) + + useEffect(() => { + if (typeof document === 'undefined') return + const mo = new MutationObserver(() => setDark(document.documentElement.classList.contains('dark'))) + mo.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }) + return () => mo.disconnect() + }, []) + + return dark +} diff --git a/shared/src/i18n/ar/admin.ts b/shared/src/i18n/ar/admin.ts index 9a0a222f7..c218af3e3 100644 --- a/shared/src/i18n/ar/admin.ts +++ b/shared/src/i18n/ar/admin.ts @@ -513,7 +513,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'القوائم', 'admin.addons.catalog.packing.description': 'قوائم التعبئة والمهام لرحلاتك', 'admin.addons.catalog.budget.name': 'التكاليف', - 'admin.addons.catalog.budget.description': 'تتبع النفقات وخطط ميزانية الرحلة', + 'admin.addons.catalog.budget.description': 'تتبّع مصاريف الرحلة وقسّمها بين المسافرين', 'admin.addons.catalog.documents.name': 'المستندات', 'admin.addons.catalog.documents.description': 'حفظ وإدارة وثائق السفر', 'admin.addons.catalog.vacay.name': 'الإجازة', @@ -521,12 +521,18 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'الأطلس', 'admin.addons.catalog.atlas.description': 'خريطة العالم مع الدول التي تمت زيارتها وإحصائيات السفر', 'admin.addons.catalog.collab.name': 'التعاون', - 'admin.addons.catalog.collab.description': 'ملاحظات واستطلاعات ودردشة لحظية لتخطيط الرحلة', + 'admin.addons.catalog.collab.description': 'ملاحظات واستطلاعات ومحادثة واقتراحات للتخطيط المشترك', 'admin.addons.catalog.memories.name': 'صور (Immich)', 'admin.addons.catalog.memories.description': 'شارك صور رحلتك عبر Immich', 'admin.addons.catalog.mcp.description': 'بروتوكول سياق النموذج لتكامل مساعد الذكاء الاصطناعي', 'admin.addons.subtitleBefore': 'فعّل أو عطّل الميزات لتخصيص تجربة ', 'admin.addons.subtitleAfter': '.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'استيراد الأماكن من قائمة Naver Maps مشتركة', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'مزامنة الرحلات من خادم AirTrail الخاص بك', + 'admin.addons.catalog.llm_parsing.name': 'التحليل بالذكاء الاصطناعي', + 'admin.addons.catalog.llm_parsing.description': 'يقرأ الحجوزات التي يعجز المحلل المدمج عن فهمها، بنموذج ذكاء اصطناعي تختاره', 'admin.addons.enabled': 'مفعّل', 'admin.addons.disabled': 'معطّل', 'admin.addons.type.trip': 'رحلة', @@ -537,6 +543,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'خدمات الواجهة الخلفية وتكاملات API بدون صفحة مخصصة', 'admin.addons.toast.updated': 'تم تحديث الإضافة', 'admin.addons.toast.error': 'فشل تحديث الإضافة', + 'admin.addons.group.count': '{enabled} من {total} مُفعّل', 'admin.addons.noAddons': 'لا توجد إضافات متاحة', 'admin.weather.title': 'بيانات الطقس', 'admin.weather.badge': 'منذ 24 مارس 2026', @@ -645,8 +652,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Trip tracking & travel journal with check-ins, photos, and daily stories', // en-fallback 'admin.addons.catalog.collections.name': 'المجموعات', - 'admin.addons.catalog.collections.description': - 'مكتبة أماكن شخصية — احفظ الأماكن عبر الرحلات في قوائم مسماة، وانسخها إلى أي رحلة، وشاركها مع الآخرين', + 'admin.addons.catalog.collections.description': 'اجمع الأماكن من أي رحلة في قوائم مسماة وأعد استخدامها', 'admin.passkey.title': 'تسجيل الدخول بمفتاح المرور', 'admin.passkey.cardHint': 'اسمح للمستخدمين بتسجيل الدخول باستخدام مفاتيح المرور (WebAuthn). معطّل افتراضيًا.', 'admin.passkey.login': 'تفعيل تسجيل الدخول بمفتاح المرور', diff --git a/shared/src/i18n/br/admin.ts b/shared/src/i18n/br/admin.ts index 6bf9f811b..6d5d75891 100644 --- a/shared/src/i18n/br/admin.ts +++ b/shared/src/i18n/br/admin.ts @@ -504,7 +504,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listas', 'admin.addons.catalog.packing.description': 'Listas de bagagem e tarefas a fazer para suas viagens', 'admin.addons.catalog.budget.name': 'Custos', - 'admin.addons.catalog.budget.description': 'Acompanhe despesas e planeje o orçamento da viagem', + 'admin.addons.catalog.budget.description': 'Acompanhe as despesas da viagem e divida-as entre os viajantes', 'admin.addons.catalog.documents.name': 'Documentos', 'admin.addons.catalog.documents.description': 'Armazene e gerencie documentos de viagem', 'admin.addons.catalog.vacay.name': 'Férias', @@ -512,11 +512,17 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Mapa mundial com países visitados e estatísticas', 'admin.addons.catalog.collab.name': 'Colab', - 'admin.addons.catalog.collab.description': 'Notas, enquetes e chat em tempo real para planejar a viagem', + 'admin.addons.catalog.collab.description': 'Notas, enquetes, chat e sugestões para planejar em grupo', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Model Context Protocol para integração com assistentes de IA', 'admin.addons.subtitleBefore': 'Ative ou desative recursos para personalizar sua ', 'admin.addons.subtitleAfter': ' experiência.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importe lugares de uma lista compartilhada do Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Sincronize voos da sua instância AirTrail auto-hospedada', + 'admin.addons.catalog.llm_parsing.name': 'Análise por IA', + 'admin.addons.catalog.llm_parsing.description': 'Lê reservas que o analisador integrado não entende, usando um modelo de IA à sua escolha', 'admin.addons.enabled': 'Ativado', 'admin.addons.disabled': 'Desativado', 'admin.addons.type.trip': 'Viagem', @@ -526,6 +532,7 @@ const admin: TranslationStrings = { 'admin.addons.globalHint': 'Disponível como seção própria na navegação principal', 'admin.addons.toast.updated': 'Complemento atualizado', 'admin.addons.toast.error': 'Falha ao atualizar complemento', + 'admin.addons.group.count': '{enabled} de {total} ativados', 'admin.addons.noAddons': 'Nenhum complemento disponível', 'admin.addons.integrationHint': 'Serviços de backend e integrações de API sem página dedicada', 'admin.weather.title': 'Dados meteorológicos', @@ -661,8 +668,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Rastreamento de viagens e diário de viajante com check-ins, fotos e histórias diárias', 'admin.addons.catalog.collections.name': 'Coleções', - 'admin.addons.catalog.collections.description': - 'Biblioteca pessoal de lugares — salve lugares entre viagens em listas nomeadas, copie para qualquer viagem, compartilhe com outras pessoas', + 'admin.addons.catalog.collections.description': 'Reúna lugares de qualquer viagem em listas nomeadas e reutilize-os', 'admin.passkey.title': 'Login com passkey', 'admin.passkey.cardHint': 'Permite que os usuários entrem com passkeys (WebAuthn). Desativado por padrão.', 'admin.passkey.login': 'Ativar login com passkey', diff --git a/shared/src/i18n/ca/admin.ts b/shared/src/i18n/ca/admin.ts index 9a2098607..546720520 100644 --- a/shared/src/i18n/ca/admin.ts +++ b/shared/src/i18n/ca/admin.ts @@ -171,6 +171,12 @@ const admin: TranslationStrings = { 'admin.addons.subtitle': 'Activa o desactiva funcions per personalitzar la teva experiència a TREK.', 'admin.addons.subtitleBefore': 'Activa o desactiva funcions per personalitzar la teva experiència a ', 'admin.addons.subtitleAfter': '.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importa llocs des d’una llista compartida de Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Sincronitza vols des de la teva instància autoallotjada d’AirTrail', + 'admin.addons.catalog.llm_parsing.name': 'Anàlisi amb IA', + 'admin.addons.catalog.llm_parsing.description': 'Llegeix reserves que l’analitzador integrat no entén, amb un model d’IA que triïs', 'admin.addons.enabled': 'Actiu', 'admin.addons.disabled': 'Desactivat', 'admin.addons.type.trip': 'Viatge', @@ -181,6 +187,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': "Serveis backend i integracions d'API sense pàgina dedicada", 'admin.addons.toast.updated': 'Complement actualitzat', 'admin.addons.toast.error': "No s'ha pogut actualitzar el complement", + 'admin.addons.group.count': '{enabled} de {total} actius', 'admin.addons.noAddons': 'No hi ha complements disponibles', 'admin.weather.title': 'Dades meteorològiques', 'admin.weather.badge': 'Des del 24 de març de 2026', @@ -275,7 +282,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Llistes', 'admin.addons.catalog.packing.description': "Llistes d'equipatge i tasques pendents per als teus viatges", 'admin.addons.catalog.budget.name': 'Pressupost', - 'admin.addons.catalog.budget.description': 'Controla les despeses i planifica el pressupost del viatge', + 'admin.addons.catalog.budget.description': 'Controla les despeses del viatge i reparteix-les entre els viatgers', 'admin.addons.catalog.documents.name': 'Documents', 'admin.addons.catalog.documents.description': 'Guarda i gestiona la documentació del viatge', 'admin.addons.catalog.vacay.name': 'Vacances', @@ -283,10 +290,9 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atles', 'admin.addons.catalog.atlas.description': 'Mapa del món amb els països visitats i estadístiques de viatge', 'admin.addons.catalog.collab.name': 'Col·laboració', - 'admin.addons.catalog.collab.description': 'Notes, enquestes i xat en temps real per organitzar el viatge', + 'admin.addons.catalog.collab.description': 'Notes, enquestes, xat i suggeriments per planificar en grup', 'admin.addons.catalog.collections.name': 'Col·leccions', - 'admin.addons.catalog.collections.description': - 'Permet agrupar i organitzar complements de catàleg en col·leccions temàtiques o paquets personalitzats.', + 'admin.addons.catalog.collections.description': 'Recull llocs de qualsevol viatge en llistes amb nom i reutilitza’ls', 'admin.oidcOnlyMode': 'Desactivar autenticació per contrasenya', 'admin.oidcOnlyModeHint': "Si està activat, només es permet l'inici de sessió amb SSO. L'inici de sessió i registre amb contrasenya es bloquegen.", diff --git a/shared/src/i18n/cs/admin.ts b/shared/src/i18n/cs/admin.ts index 52cd3c641..230b7e717 100644 --- a/shared/src/i18n/cs/admin.ts +++ b/shared/src/i18n/cs/admin.ts @@ -502,7 +502,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Seznamy', 'admin.addons.catalog.packing.description': 'Balicí seznamy a úkoly pro vaše výlety', 'admin.addons.catalog.budget.name': 'Náklady', - 'admin.addons.catalog.budget.description': 'Sledování výdajů a plánování rozpočtu cesty', + 'admin.addons.catalog.budget.description': 'Sledujte výdaje na cestu a rozdělte je mezi cestující', 'admin.addons.catalog.documents.name': 'Dokumenty', 'admin.addons.catalog.documents.description': 'Ukládání a správa cestovních dokladů', 'admin.addons.catalog.vacay.name': 'Dovolená (Vacay)', @@ -510,7 +510,13 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Mapa světa s navštívenými zeměmi a statistikami', 'admin.addons.catalog.collab.name': 'Spolupráce', - 'admin.addons.catalog.collab.description': 'Poznámky v reálném čase, hlasování a chat pro plánování', + 'admin.addons.catalog.collab.description': 'Poznámky, ankety, chat a návrhy pro společné plánování', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importujte místa ze sdíleného seznamu Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Synchronizujte lety z vlastní instance AirTrail', + 'admin.addons.catalog.llm_parsing.name': 'Analýza pomocí AI', + 'admin.addons.catalog.llm_parsing.description': 'Přečte rezervace, které vestavěný analyzátor nezvládne, pomocí zvoleného modelu AI', 'admin.addons.enabled': 'Povoleno', 'admin.addons.disabled': 'Zakázáno', 'admin.addons.type.trip': 'Cesta', @@ -521,6 +527,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Backendové služby a API integrace bez vlastní stránky', 'admin.addons.toast.updated': 'Doplněk byl aktualizován', 'admin.addons.toast.error': 'Aktualizace doplňku se nezdařila', + 'admin.addons.group.count': '{enabled} z {total} zapnuto', 'admin.addons.noAddons': 'Žádné doplňky nejsou k dispozici', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Model Context Protocol pro integraci AI asistentů', @@ -655,8 +662,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Cestovní deník', 'admin.addons.catalog.journey.description': 'Sledování cest a cestovní deník s odbaveními, fotkami a denními příběhy', 'admin.addons.catalog.collections.name': 'Sbírky', - 'admin.addons.catalog.collections.description': - 'Osobní knihovna míst — ukládejte místa napříč výlety do pojmenovaných seznamů, kopírujte do libovolného výletu, sdílejte s ostatními', + 'admin.addons.catalog.collections.description': 'Sbírejte místa ze všech cest do pojmenovaných seznamů a znovu je používejte', 'admin.passkey.title': 'Přihlášení přístupovým klíčem', 'admin.passkey.cardHint': 'Umožněte uživatelům přihlašovat se pomocí přístupových klíčů (WebAuthn). Ve výchozím nastavení vypnuto.', diff --git a/shared/src/i18n/de/admin.ts b/shared/src/i18n/de/admin.ts index c28db2d17..7fa612022 100644 --- a/shared/src/i18n/de/admin.ts +++ b/shared/src/i18n/de/admin.ts @@ -509,7 +509,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listen', 'admin.addons.catalog.packing.description': 'Packlisten und To-Do-Aufgaben für deine Reisen', 'admin.addons.catalog.budget.name': 'Kosten', - 'admin.addons.catalog.budget.description': 'Ausgaben verfolgen und Reisebudget planen', + 'admin.addons.catalog.budget.description': 'Reisekosten erfassen und unter den Mitreisenden aufteilen', 'admin.addons.catalog.documents.name': 'Dokumente', 'admin.addons.catalog.documents.description': 'Reisedokumente speichern und verwalten', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -517,13 +517,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Weltkarte mit besuchten Ländern und Reisestatistiken', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': 'Echtzeit-Notizen, Umfragen und Chat für die Reiseplanung', + 'admin.addons.catalog.collab.description': 'Notizen, Umfragen, Chat und Vorschläge für die gemeinsame Planung', 'admin.addons.catalog.memories.name': 'Fotos (Immich)', 'admin.addons.catalog.memories.description': 'Reisefotos über deine Immich-Instanz teilen', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Model Context Protocol für die KI-Assistenten-Integration', 'admin.addons.subtitleBefore': 'Aktiviere oder deaktiviere Funktionen, um ', 'admin.addons.subtitleAfter': ' nach deinen Wünschen anzupassen.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Orte aus einer geteilten Naver-Maps-Liste importieren', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Flüge aus deiner selbst gehosteten AirTrail-Instanz synchronisieren', + 'admin.addons.catalog.llm_parsing.name': 'KI-Auswertung', + 'admin.addons.catalog.llm_parsing.description': 'Liest Buchungen, an denen der eingebaute Parser scheitert, mit einem KI-Modell deiner Wahl', 'admin.addons.enabled': 'Aktiviert', 'admin.addons.disabled': 'Deaktiviert', 'admin.addons.type.trip': 'Reise', @@ -534,6 +540,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Backend-Dienste und API-Integrationen ohne eigene Seite', 'admin.addons.toast.updated': 'Addon aktualisiert', 'admin.addons.toast.error': 'Addon konnte nicht aktualisiert werden', + 'admin.addons.group.count': '{enabled} von {total} aktiviert', 'admin.addons.noAddons': 'Keine Addons verfügbar', 'admin.weather.title': 'Wetterdaten', 'admin.weather.badge': 'Seit 24. März 2026', @@ -668,8 +675,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Journey', 'admin.addons.catalog.journey.description': 'Reise-Tracking & Tagebuch mit Check-ins, Fotos und Tagesberichten', 'admin.addons.catalog.collections.name': 'Sammlungen', - 'admin.addons.catalog.collections.description': - 'Persönliche Orte-Bibliothek — speichere Orte aus Reisen in benannten Listen, kopiere sie in jede Reise, teile sie mit anderen', + 'admin.addons.catalog.collections.description': 'Orte aus allen Reisen in benannten Listen sammeln und wiederverwenden', 'admin.passkey.title': 'Passkey-Anmeldung', 'admin.passkey.cardHint': 'Erlaube Benutzern die Anmeldung mit Passkeys (WebAuthn). Standardmäßig deaktiviert.', 'admin.passkey.login': 'Passkey-Anmeldung aktivieren', diff --git a/shared/src/i18n/en/admin.ts b/shared/src/i18n/en/admin.ts index edccd8c4b..eb3d17a06 100644 --- a/shared/src/i18n/en/admin.ts +++ b/shared/src/i18n/en/admin.ts @@ -547,7 +547,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Lists', 'admin.addons.catalog.packing.description': 'Packing lists and to-do tasks for your trips', 'admin.addons.catalog.budget.name': 'Costs', - 'admin.addons.catalog.budget.description': 'Track expenses and plan your trip budget', + 'admin.addons.catalog.budget.description': 'Track trip expenses and split them between travellers', 'admin.addons.catalog.documents.name': 'Documents', 'admin.addons.catalog.documents.description': 'Store and manage travel documents', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -555,13 +555,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'World map with visited countries and travel stats', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': 'Real-time notes, polls, and chat for trip planning', + 'admin.addons.catalog.collab.description': 'Notes, polls, chat and suggestions for planning together', 'admin.addons.catalog.memories.name': 'Photos (Immich)', 'admin.addons.catalog.memories.description': 'Share trip photos via your Immich instance', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Model Context Protocol for AI assistant integration', 'admin.addons.subtitleBefore': 'Enable or disable features to customize your ', 'admin.addons.subtitleAfter': ' experience.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Import places from a shared Naver Maps list', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Sync flights from your self-hosted AirTrail instance', + 'admin.addons.catalog.llm_parsing.name': 'AI Parsing', + 'admin.addons.catalog.llm_parsing.description': 'Reads bookings the built-in parser cannot, using an AI model you choose', 'admin.addons.enabled': 'Enabled', 'admin.addons.disabled': 'Disabled', 'admin.addons.type.trip': 'Trip', @@ -572,6 +578,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Backend services and API integrations with no dedicated page', 'admin.addons.toast.updated': 'Addon updated', 'admin.addons.toast.error': 'Failed to update addon', + 'admin.addons.group.count': '{enabled} of {total} enabled', 'admin.addons.noAddons': 'No addons available', 'admin.weather.title': 'Weather Data', 'admin.weather.badge': 'Since March 24, 2026', @@ -663,8 +670,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Trip tracking & travel journal with check-ins, photos, and daily stories', 'admin.addons.catalog.collections.name': 'Collections', - 'admin.addons.catalog.collections.description': - 'Personal place library — save places across trips into named lists, copy into any trip, share with others', + 'admin.addons.catalog.collections.description': 'Collect places from any trip into named lists and reuse them', 'admin.passkey.title': 'Passkey login', 'admin.passkey.cardHint': 'Let users sign in with passkeys (WebAuthn). Off by default.', 'admin.passkey.login': 'Enable passkey login', diff --git a/shared/src/i18n/es/admin.ts b/shared/src/i18n/es/admin.ts index 0bb906ae6..8d7b42614 100644 --- a/shared/src/i18n/es/admin.ts +++ b/shared/src/i18n/es/admin.ts @@ -510,6 +510,12 @@ const admin: TranslationStrings = { 'admin.addons.subtitle': 'Activa o desactiva funciones para personalizar tu experiencia en TREK.', 'admin.addons.subtitleBefore': 'Activa o desactiva funciones para personalizar tu experiencia en ', 'admin.addons.subtitleAfter': '.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importa lugares desde una lista compartida de Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Sincroniza vuelos desde tu instancia autoalojada de AirTrail', + 'admin.addons.catalog.llm_parsing.name': 'Análisis con IA', + 'admin.addons.catalog.llm_parsing.description': 'Lee reservas que el analizador integrado no entiende, con un modelo de IA que tú elijas', 'admin.addons.enabled': 'Activo', 'admin.addons.disabled': 'Desactivado', 'admin.addons.type.trip': 'Viaje', @@ -520,6 +526,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Servicios backend e integraciones de API sin página dedicada', 'admin.addons.toast.updated': 'Complemento actualizado', 'admin.addons.toast.error': 'No se pudo actualizar el complemento', + 'admin.addons.group.count': '{enabled} de {total} activados', 'admin.addons.noAddons': 'No hay complementos disponibles', 'admin.weather.title': 'Datos meteorológicos', 'admin.weather.badge': 'Desde el 24 de marzo de 2026', @@ -615,7 +622,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listas', 'admin.addons.catalog.packing.description': 'Listas de equipaje y tareas pendientes para tus viajes', 'admin.addons.catalog.budget.name': 'Costes', - 'admin.addons.catalog.budget.description': 'Controla los gastos y planifica el presupuesto del viaje', + 'admin.addons.catalog.budget.description': 'Controla los gastos del viaje y repártelos entre los viajeros', 'admin.addons.catalog.documents.name': 'Documentos', 'admin.addons.catalog.documents.description': 'Guarda y gestiona la documentación del viaje', 'admin.addons.catalog.vacay.name': 'Vacaciones', @@ -623,7 +630,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Mapa del mundo con los países visitados y estadísticas de viaje', 'admin.addons.catalog.collab.name': 'Colaboración', - 'admin.addons.catalog.collab.description': 'Notas, encuestas y chat en tiempo real para organizar el viaje', + 'admin.addons.catalog.collab.description': 'Notas, encuestas, chat y sugerencias para planificar en grupo', 'admin.oidcOnlyMode': 'Desactivar autenticación por contraseña', 'admin.oidcOnlyModeHint': 'Si está activado, solo se permite el inicio de sesión con SSO. El inicio de sesión y registro con contraseña se bloquean.', @@ -676,8 +683,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Seguimiento de viajes y diario de viajero con registros de ubicación, fotos e historias diarias', 'admin.addons.catalog.collections.name': 'Colecciones', - 'admin.addons.catalog.collections.description': - 'Biblioteca personal de lugares: guarda lugares de tus viajes en listas con nombre, cópialos a cualquier viaje y compártelos con otros', + 'admin.addons.catalog.collections.description': 'Reúne lugares de cualquier viaje en listas con nombre y reutilízalos', 'admin.passkey.title': 'Inicio de sesión con passkey', 'admin.passkey.cardHint': 'Permite que los usuarios inicien sesión con passkeys (WebAuthn). Desactivado de forma predeterminada.', diff --git a/shared/src/i18n/fr/admin.ts b/shared/src/i18n/fr/admin.ts index ef0a3b325..19ee1db7f 100644 --- a/shared/src/i18n/fr/admin.ts +++ b/shared/src/i18n/fr/admin.ts @@ -518,7 +518,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listes', 'admin.addons.catalog.packing.description': 'Listes de bagages et tâches à faire pour vos voyages', 'admin.addons.catalog.budget.name': 'Coûts', - 'admin.addons.catalog.budget.description': 'Suivez les dépenses et planifiez votre budget de voyage', + 'admin.addons.catalog.budget.description': 'Suivez les dépenses du voyage et répartissez-les entre les voyageurs', 'admin.addons.catalog.documents.name': 'Documents', 'admin.addons.catalog.documents.description': 'Stockez et gérez vos documents de voyage', 'admin.addons.catalog.vacay.name': 'Vacances', @@ -526,9 +526,15 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Carte du monde avec pays visités et statistiques de voyage', 'admin.addons.catalog.collab.name': 'Collaboration', - 'admin.addons.catalog.collab.description': 'Notes en temps réel, sondages et chat pour la planification de voyage', + 'admin.addons.catalog.collab.description': 'Notes, sondages, chat et suggestions pour planifier à plusieurs', 'admin.addons.subtitleBefore': 'Activez ou désactivez des fonctionnalités pour personnaliser votre expérience ', 'admin.addons.subtitleAfter': '.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importez des lieux depuis une liste Naver Maps partagée', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Synchronisez les vols depuis votre instance AirTrail auto-hébergée', + 'admin.addons.catalog.llm_parsing.name': 'Analyse par IA', + 'admin.addons.catalog.llm_parsing.description': 'Lit les réservations que l’analyseur intégré ne comprend pas, via un modèle d’IA de votre choix', 'admin.addons.enabled': 'Activé', 'admin.addons.disabled': 'Désactivé', 'admin.addons.type.trip': 'Voyage', @@ -539,6 +545,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Services backend et intégrations API sans page dédiée', 'admin.addons.toast.updated': 'Extension mise à jour', 'admin.addons.toast.error': "Échec de la mise à jour de l'extension", + 'admin.addons.group.count': '{enabled} sur {total} activés', 'admin.addons.noAddons': 'Aucune extension disponible', 'admin.weather.title': 'Données météo', 'admin.weather.badge': 'Depuis le 24 mars 2026', @@ -674,8 +681,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Journal de voyage', 'admin.addons.catalog.journey.description': 'Suivi de voyages et journal avec check-ins, photos et récits quotidiens', 'admin.addons.catalog.collections.name': 'Collections', - 'admin.addons.catalog.collections.description': - 'Bibliothèque personnelle de lieux — enregistrez des lieux de vos voyages dans des listes nommées, copiez-les dans n’importe quel voyage et partagez-les', + 'admin.addons.catalog.collections.description': 'Rassemblez des lieux de tous vos voyages dans des listes nommées et réutilisez-les', 'admin.passkey.title': 'Connexion par passkey', 'admin.passkey.cardHint': 'Permettez aux utilisateurs de se connecter avec des passkeys (WebAuthn). Désactivé par défaut.', diff --git a/shared/src/i18n/gr/admin.ts b/shared/src/i18n/gr/admin.ts index 2176c7dfd..fa7852da9 100644 --- a/shared/src/i18n/gr/admin.ts +++ b/shared/src/i18n/gr/admin.ts @@ -563,7 +563,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Λίστες', 'admin.addons.catalog.packing.description': 'Λίστες πακεταρίσματος και εργασίες προς εκτέλεση για τα ταξίδια σας', 'admin.addons.catalog.budget.name': 'Κόστη', - 'admin.addons.catalog.budget.description': 'Παρακολουθήστε τα έξοδα και σχεδιάστε τον προϋπολογισμό του ταξιδιού σας', + 'admin.addons.catalog.budget.description': 'Παρακολουθήστε τα έξοδα του ταξιδιού και μοιράστε τα στους ταξιδιώτες', 'admin.addons.catalog.documents.name': 'Έγγραφα', 'admin.addons.catalog.documents.description': 'Αποθηκεύστε και διαχειριστείτε ταξιδιωτικά έγγραφα', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -572,8 +572,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.description': 'Παγκόσμιος χάρτης με χώρες που έχετε επισκεφθεί και ταξιδιωτικά στατιστικά', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': - 'Σημειώσεις, δημοσκοπήσεις και συνομιλία πραγματικού χρόνου για σχεδιασμό ταξιδιού', + 'admin.addons.catalog.collab.description': 'Σημειώσεις, ψηφοφορίες, συνομιλία και προτάσεις για κοινό σχεδιασμό', 'admin.addons.catalog.memories.name': 'Φωτογραφίες (Immich)', 'admin.addons.catalog.memories.description': 'Μοιραστείτε φωτογραφίες ταξιδιού μέσω της δικής σας εγκατάστασης Immich', @@ -582,6 +581,12 @@ const admin: TranslationStrings = { 'admin.addons.subtitleBefore': 'Ενεργοποιήστε ή απενεργοποιήστε λειτουργίες για να εξατομικεύσετε την εμπειρία σας στο ', 'admin.addons.subtitleAfter': '.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Εισαγωγή τοποθεσιών από κοινόχρηστη λίστα Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Συγχρονισμός πτήσεων από τη δική σας εγκατάσταση AirTrail', + 'admin.addons.catalog.llm_parsing.name': 'Ανάλυση με τεχνητή νοημοσύνη', + 'admin.addons.catalog.llm_parsing.description': 'Διαβάζει κρατήσεις που δεν καταλαβαίνει ο ενσωματωμένος αναλυτής, με μοντέλο ΤΝ της επιλογής σας', 'admin.addons.enabled': 'Ενεργοποιημένο', 'admin.addons.disabled': 'Απενεργοποιημένο', 'admin.addons.type.trip': 'Ταξίδι', @@ -592,6 +597,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Υπηρεσίες backend και ενσωματώσεις API χωρίς ειδική σελίδα', 'admin.addons.toast.updated': 'Το πρόσθετο ενημερώθηκε', 'admin.addons.toast.error': 'Αποτυχία ενημέρωσης προσθέτου', + 'admin.addons.group.count': '{enabled} από {total} ενεργοποιημένα', 'admin.addons.noAddons': 'Δεν υπάρχουν διαθέσιμα πρόσθετα', 'admin.weather.title': 'Δεδομένα Καιρού', 'admin.weather.badge': 'Από 24 Μαρτίου 2026', @@ -684,8 +690,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Παρακολούθηση ταξιδιών & ημερολόγιο ταξιδιών με αφίξεις, φωτογραφίες και καθημερινές ιστορίες', 'admin.addons.catalog.collections.name': 'Συλλογές', - 'admin.addons.catalog.collections.description': - 'Προσωπική βιβλιοθήκη μερών — αποθήκευσε μέρη από όλα τα ταξίδια σε ονομασμένες λίστες, αντίγραψέ τα σε οποιοδήποτε ταξίδι, μοιράσου τα με άλλους', + 'admin.addons.catalog.collections.description': 'Συγκεντρώστε τοποθεσίες από κάθε ταξίδι σε λίστες με όνομα και χρησιμοποιήστε τις ξανά', 'admin.passkey.title': 'Σύνδεση με passkey', 'admin.passkey.cardHint': 'Επιτρέψτε στους χρήστες να συνδέονται με passkeys (WebAuthn). Απενεργοποιημένο από προεπιλογή.', diff --git a/shared/src/i18n/hu/admin.ts b/shared/src/i18n/hu/admin.ts index cd62b76a6..de82619e5 100644 --- a/shared/src/i18n/hu/admin.ts +++ b/shared/src/i18n/hu/admin.ts @@ -514,7 +514,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listák', 'admin.addons.catalog.packing.description': 'Csomagolási listák és teendők az utazásaidhoz', 'admin.addons.catalog.budget.name': 'Költségek', - 'admin.addons.catalog.budget.description': 'Kiadások nyomon követése és az utazási költségvetés tervezése', + 'admin.addons.catalog.budget.description': 'Kövesd az utazás kiadásait, és oszd meg őket az utastársak között', 'admin.addons.catalog.documents.name': 'Dokumentumok', 'admin.addons.catalog.documents.description': 'Úti dokumentumok tárolása és kezelése', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -522,13 +522,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlasz', 'admin.addons.catalog.atlas.description': 'Világtérkép meglátogatott országokkal és utazási statisztikákkal', 'admin.addons.catalog.collab.name': 'Együttműködés', - 'admin.addons.catalog.collab.description': 'Valós idejű jegyzetek, szavazások és csevegés az utazás tervezéséhez', + 'admin.addons.catalog.collab.description': 'Jegyzetek, szavazások, csevegés és javaslatok a közös tervezéshez', 'admin.addons.catalog.memories.name': 'Fotók (Immich)', 'admin.addons.catalog.memories.description': 'Utazási fotók megosztása az Immich példányon keresztül', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Model Context Protocol AI asszisztens integrációhoz', 'admin.addons.subtitleBefore': 'Funkciók engedélyezése vagy letiltása a ', 'admin.addons.subtitleAfter': ' testreszabásához.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Helyek importálása megosztott Naver Maps-listából', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Járatok szinkronizálása a saját üzemeltetésű AirTrail-példányodból', + 'admin.addons.catalog.llm_parsing.name': 'MI-elemzés', + 'admin.addons.catalog.llm_parsing.description': 'Beolvassa azokat a foglalásokat, amelyeket a beépített értelmező nem tud, az általad választott MI-modellel', 'admin.addons.enabled': 'Engedélyezve', 'admin.addons.disabled': 'Letiltva', 'admin.addons.type.trip': 'Utazás', @@ -539,6 +545,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Háttérszolgáltatások és API integrációk dedikált oldal nélkül', 'admin.addons.toast.updated': 'Bővítmény frissítve', 'admin.addons.toast.error': 'Nem sikerült frissíteni a bővítményt', + 'admin.addons.group.count': '{total} közül {enabled} bekapcsolva', 'admin.addons.noAddons': 'Nincsenek elérhető bővítmények', 'admin.weather.title': 'Időjárás adatok', 'admin.weather.badge': '2026. március 24. óta', @@ -674,8 +681,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Utazáskövetés és útinapló bejelentkezésekkel, fotókkal és napi történetekkel', 'admin.addons.catalog.collections.name': 'Gyűjtemények', - 'admin.addons.catalog.collections.description': - 'Személyes helykönyvtár — ments helyeket utazásokon átívelően elnevezett listákba, másold bármelyik utazásba, oszd meg másokkal', + 'admin.addons.catalog.collections.description': 'Gyűjtsd össze bármely utazás helyeit elnevezett listákba, és használd fel őket újra', 'admin.passkey.title': 'Passkey-bejelentkezés', 'admin.passkey.cardHint': 'Engedélyezd a felhasználóknak a bejelentkezést passkey-vel (WebAuthn). Alapból kikapcsolva.', diff --git a/shared/src/i18n/id/admin.ts b/shared/src/i18n/id/admin.ts index 1782f3b2d..bd5be63ae 100644 --- a/shared/src/i18n/id/admin.ts +++ b/shared/src/i18n/id/admin.ts @@ -556,7 +556,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Daftar', 'admin.addons.catalog.packing.description': 'Daftar packing dan tugas to-do untuk perjalananmu', 'admin.addons.catalog.budget.name': 'Biaya', - 'admin.addons.catalog.budget.description': 'Lacak pengeluaran dan rencanakan anggaran perjalananmu', + 'admin.addons.catalog.budget.description': 'Catat pengeluaran perjalanan dan bagi di antara para pelancong', 'admin.addons.catalog.documents.name': 'Dokumen', 'admin.addons.catalog.documents.description': 'Simpan dan kelola dokumen perjalanan', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -564,13 +564,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Peta dunia dengan negara yang pernah dikunjungi dan statistik perjalanan', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': 'Catatan real-time, polling, dan chat untuk perencanaan perjalanan', + 'admin.addons.catalog.collab.description': 'Catatan, jajak pendapat, obrolan, dan saran untuk merencanakan bersama', 'admin.addons.catalog.memories.name': 'Foto (Immich)', 'admin.addons.catalog.memories.description': 'Bagikan foto perjalanan melalui instans Immich kamu', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Model Context Protocol untuk integrasi asisten AI', 'admin.addons.subtitleBefore': 'Aktifkan atau nonaktifkan fitur untuk menyesuaikan ', 'admin.addons.subtitleAfter': ' kamu.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Impor tempat dari daftar Naver Maps yang dibagikan', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Sinkronkan penerbangan dari instans AirTrail milikmu sendiri', + 'admin.addons.catalog.llm_parsing.name': 'Analisis AI', + 'admin.addons.catalog.llm_parsing.description': 'Membaca pemesanan yang tidak dipahami pengurai bawaan, dengan model AI pilihanmu', 'admin.addons.enabled': 'Aktif', 'admin.addons.disabled': 'Nonaktif', 'admin.addons.type.trip': 'Perjalanan', @@ -581,6 +587,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Layanan backend dan integrasi API tanpa halaman tersendiri', 'admin.addons.toast.updated': 'Addon diperbarui', 'admin.addons.toast.error': 'Gagal memperbarui addon', + 'admin.addons.group.count': '{enabled} dari {total} aktif', 'admin.addons.noAddons': 'Tidak ada addon yang tersedia', 'admin.weather.title': 'Data Cuaca', 'admin.weather.badge': 'Sejak 24 Maret 2026', @@ -671,8 +678,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Journey', 'admin.addons.catalog.journey.description': 'Pelacakan perjalanan & jurnal dengan check-in, foto, dan cerita harian', 'admin.addons.catalog.collections.name': 'Koleksi', - 'admin.addons.catalog.collections.description': - 'Pustaka tempat pribadi — simpan tempat lintas perjalanan ke dalam daftar bernama, salin ke perjalanan mana pun, bagikan dengan orang lain', + 'admin.addons.catalog.collections.description': 'Kumpulkan tempat dari perjalanan mana pun ke dalam daftar bernama dan gunakan lagi', 'admin.passkey.title': 'Login dengan passkey', 'admin.passkey.cardHint': 'Izinkan pengguna masuk dengan passkey (WebAuthn). Nonaktif secara default.', 'admin.passkey.login': 'Aktifkan login dengan passkey', diff --git a/shared/src/i18n/it/admin.ts b/shared/src/i18n/it/admin.ts index e76c501fa..39ae58964 100644 --- a/shared/src/i18n/it/admin.ts +++ b/shared/src/i18n/it/admin.ts @@ -509,7 +509,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Liste', 'admin.addons.catalog.packing.description': 'Liste di imballaggio e attività da svolgere per i tuoi viaggi', 'admin.addons.catalog.budget.name': 'Costi', - 'admin.addons.catalog.budget.description': 'Tieni traccia delle spese e pianifica il budget del tuo viaggio', + 'admin.addons.catalog.budget.description': 'Tieni traccia delle spese del viaggio e dividile tra i viaggiatori', 'admin.addons.catalog.documents.name': 'Documenti', 'admin.addons.catalog.documents.description': 'Archivia e gestisci i documenti di viaggio', 'admin.addons.catalog.vacay.name': 'Ferie', @@ -517,13 +517,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlante', 'admin.addons.catalog.atlas.description': 'Mappa del mondo con paesi visitati e statistiche di viaggio', 'admin.addons.catalog.collab.name': 'Collaborazione', - 'admin.addons.catalog.collab.description': 'Note, sondaggi e chat in tempo reale per la pianificazione del viaggio', + 'admin.addons.catalog.collab.description': 'Note, sondaggi, chat e suggerimenti per pianificare insieme', 'admin.addons.catalog.memories.name': 'Foto (Immich)', 'admin.addons.catalog.memories.description': 'Condividi le foto del viaggio tramite la tua istanza Immich', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': "Model Context Protocol per l'integrazione di assistenti AI", 'admin.addons.subtitleBefore': 'Abilita o disabilita le funzionalità per personalizzare la tua ', 'admin.addons.subtitleAfter': ' esperienza.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importa luoghi da un elenco condiviso di Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Sincronizza i voli dalla tua istanza AirTrail self-hosted', + 'admin.addons.catalog.llm_parsing.name': 'Analisi con IA', + 'admin.addons.catalog.llm_parsing.description': 'Legge le prenotazioni che l’analizzatore integrato non riesce a interpretare, con un modello di IA a tua scelta', 'admin.addons.enabled': 'Abilitato', 'admin.addons.disabled': 'Disabilitato', 'admin.addons.type.trip': 'Viaggio', @@ -534,6 +540,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Servizi backend e integrazioni API senza pagina dedicata', 'admin.addons.toast.updated': 'Modulo aggiornato', 'admin.addons.toast.error': 'Impossibile aggiornare il modulo', + 'admin.addons.group.count': '{enabled} di {total} attivi', 'admin.addons.noAddons': 'Nessun modulo disponibile', 'admin.weather.title': 'Dati meteo', 'admin.weather.badge': 'Dal 24 marzo 2026', @@ -669,8 +676,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Diario di viaggio', 'admin.addons.catalog.journey.description': 'Tracciamento viaggi e diario con check-in, foto e storie quotidiane', 'admin.addons.catalog.collections.name': 'Raccolte', - 'admin.addons.catalog.collections.description': - 'Libreria personale di luoghi: salva luoghi dai tuoi viaggi in liste con nome, copiali in qualsiasi viaggio e condividili con altri', + 'admin.addons.catalog.collections.description': 'Raccogli luoghi da tutti i viaggi in elenchi con nome e riutilizzali', 'admin.passkey.title': 'Accesso con passkey', 'admin.passkey.cardHint': 'Consenti agli utenti di accedere con le passkey (WebAuthn). Disattivato per impostazione predefinita.', diff --git a/shared/src/i18n/ja/admin.ts b/shared/src/i18n/ja/admin.ts index 029301bb9..5313fd4c2 100644 --- a/shared/src/i18n/ja/admin.ts +++ b/shared/src/i18n/ja/admin.ts @@ -521,7 +521,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'リスト', 'admin.addons.catalog.packing.description': '旅行用の持ち物リストとToDo', 'admin.addons.catalog.budget.name': '費用', - 'admin.addons.catalog.budget.description': '支出の管理と予算計画', + 'admin.addons.catalog.budget.description': '旅行の支出を記録し、同行者と分担', 'admin.addons.catalog.documents.name': 'ドキュメント', 'admin.addons.catalog.documents.description': '旅行書類の保存・管理', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -529,13 +529,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': '訪問国と旅行統計の世界地図', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': 'リアルタイムのメモ、投票、チャット', + 'admin.addons.catalog.collab.description': 'メモ、投票、チャット、提案で一緒に計画', 'admin.addons.catalog.memories.name': '写真(Immich)', 'admin.addons.catalog.memories.description': 'Immichで旅行写真を共有', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'AI連携のためのModel Context Protocol', 'admin.addons.subtitleBefore': '機能を有効/無効にして ', 'admin.addons.subtitleAfter': ' をカスタマイズします。', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': '共有された Naver マップのリストから場所をインポート', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': '自分でホストしている AirTrail から フライトを同期', + 'admin.addons.catalog.llm_parsing.name': 'AI 解析', + 'admin.addons.catalog.llm_parsing.description': '内蔵パーサーが読めない予約を、選んだ AI モデルで読み取ります', 'admin.addons.enabled': '有効', 'admin.addons.disabled': '無効', 'admin.addons.type.trip': '旅行', @@ -546,6 +552,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': '専用ページのないバックエンド/API連携', 'admin.addons.toast.updated': 'アドオンを更新しました', 'admin.addons.toast.error': 'アドオンの更新に失敗しました', + 'admin.addons.group.count': '{total} 件中 {enabled} 件が有効', 'admin.addons.noAddons': '利用可能なアドオンはありません', 'admin.weather.title': '天気データ', 'admin.weather.badge': '2026年3月24日以降', @@ -635,8 +642,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': '日記', 'admin.addons.catalog.journey.description': 'チェックイン、写真、日ごとのストーリーで旅を記録', 'admin.addons.catalog.collections.name': 'コレクション', - 'admin.addons.catalog.collections.description': - '個人の場所ライブラリ — 旅行をまたいで場所を名前付きリストに保存し、どの旅行にもコピーして、他の人と共有', + 'admin.addons.catalog.collections.description': 'どの旅行の場所も名前付きリストにまとめて再利用', 'admin.passkey.title': 'パスキーログイン', 'admin.passkey.cardHint': 'ユーザーがパスキー(WebAuthn)でサインインできるようにします。既定では無効です。', 'admin.passkey.login': 'パスキーログインを有効化', diff --git a/shared/src/i18n/ko/admin.ts b/shared/src/i18n/ko/admin.ts index 510699613..ee38631fb 100644 --- a/shared/src/i18n/ko/admin.ts +++ b/shared/src/i18n/ko/admin.ts @@ -521,7 +521,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': '목록', 'admin.addons.catalog.packing.description': '여행을 위한 짐 목록 및 할 일 작업', 'admin.addons.catalog.budget.name': '비용', - 'admin.addons.catalog.budget.description': '지출 추적 및 여행 예산 계획', + 'admin.addons.catalog.budget.description': '여행 지출을 기록하고 동행자끼리 나누기', 'admin.addons.catalog.documents.name': '문서', 'admin.addons.catalog.documents.description': '여행 서류 저장 및 관리', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -529,13 +529,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': '방문한 나라와 여행 통계가 있는 세계 지도', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': '여행 계획을 위한 실시간 메모, 투표, 채팅', + 'admin.addons.catalog.collab.description': '메모, 투표, 채팅, 제안으로 함께 계획하기', 'admin.addons.catalog.memories.name': '사진 (Immich)', 'admin.addons.catalog.memories.description': 'Immich 인스턴스를 통해 여행 사진 공유', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'AI 어시스턴트 통합을 위한 모델 컨텍스트 프로토콜', 'admin.addons.subtitleBefore': '기능을 활성화 또는 비활성화하여 ', 'admin.addons.subtitleAfter': ' 경험을 맞춤 설정하세요.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': '공유된 네이버 지도 목록에서 장소 가져오기', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': '직접 운영하는 AirTrail 인스턴스에서 항공편 동기화', + 'admin.addons.catalog.llm_parsing.name': 'AI 분석', + 'admin.addons.catalog.llm_parsing.description': '기본 분석기가 읽지 못하는 예약을 원하는 AI 모델로 읽어냅니다', 'admin.addons.enabled': '활성화됨', 'admin.addons.disabled': '비활성화됨', 'admin.addons.type.trip': '여행', @@ -546,6 +552,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': '전용 페이지가 없는 백엔드 서비스 및 API 통합', 'admin.addons.toast.updated': '애드온이 업데이트되었습니다', 'admin.addons.toast.error': '애드온 업데이트 실패', + 'admin.addons.group.count': '{total}개 중 {enabled}개 활성화됨', 'admin.addons.noAddons': '사용 가능한 애드온이 없습니다', 'admin.weather.title': '날씨 데이터', 'admin.weather.badge': '2026년 3월 24일부터', @@ -633,8 +640,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Journey', 'admin.addons.catalog.journey.description': '체크인, 사진, 일별 이야기가 있는 여행 기록 및 여행 일지', 'admin.addons.catalog.collections.name': '컬렉션', - 'admin.addons.catalog.collections.description': - '개인 장소 라이브러리 — 여행 전반의 장소를 이름이 있는 목록에 저장하고, 어떤 여행에도 복사하고, 다른 사람과 공유하세요', + 'admin.addons.catalog.collections.description': '어떤 여행의 장소든 이름 붙인 목록에 모아 다시 사용', 'admin.passkey.title': '패스키 로그인', 'admin.passkey.cardHint': '사용자가 패스키(WebAuthn)로 로그인할 수 있게 합니다. 기본값은 꺼짐입니다.', 'admin.passkey.login': '패스키 로그인 활성화', diff --git a/shared/src/i18n/nl/admin.ts b/shared/src/i18n/nl/admin.ts index 969119336..f65dda616 100644 --- a/shared/src/i18n/nl/admin.ts +++ b/shared/src/i18n/nl/admin.ts @@ -512,7 +512,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Lijsten', 'admin.addons.catalog.packing.description': 'Paklijsten en to-dotaken voor je reizen', 'admin.addons.catalog.budget.name': 'Onkosten', - 'admin.addons.catalog.budget.description': 'Houd uitgaven bij en plan je reisbudget', + 'admin.addons.catalog.budget.description': 'Houd reiskosten bij en verdeel ze onder de reisgenoten', 'admin.addons.catalog.documents.name': 'Documenten', 'admin.addons.catalog.documents.description': 'Bewaar en beheer reisdocumenten', 'admin.addons.catalog.vacay.name': 'Vakantie', @@ -520,9 +520,15 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Wereldkaart met bezochte landen en reisstatistieken', 'admin.addons.catalog.collab.name': 'Samenwerking', - 'admin.addons.catalog.collab.description': 'Realtime notities, polls en chat voor het plannen van reizen', + 'admin.addons.catalog.collab.description': 'Notities, peilingen, chat en suggesties om samen te plannen', 'admin.addons.subtitleBefore': 'Schakel functies in of uit om je ', 'admin.addons.subtitleAfter': '-ervaring aan te passen.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importeer plaatsen uit een gedeelde Naver Maps-lijst', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Synchroniseer vluchten vanuit je zelfgehoste AirTrail-instantie', + 'admin.addons.catalog.llm_parsing.name': 'AI-analyse', + 'admin.addons.catalog.llm_parsing.description': 'Leest boekingen die de ingebouwde parser niet begrijpt, met een AI-model naar keuze', 'admin.addons.enabled': 'Ingeschakeld', 'admin.addons.disabled': 'Uitgeschakeld', 'admin.addons.type.trip': 'Reis', @@ -533,6 +539,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Backenddiensten en API-integraties zonder eigen pagina', 'admin.addons.toast.updated': 'Add-on bijgewerkt', 'admin.addons.toast.error': 'Add-on bijwerken mislukt', + 'admin.addons.group.count': '{enabled} van {total} ingeschakeld', 'admin.addons.noAddons': 'Geen add-ons beschikbaar', 'admin.weather.title': 'Weergegevens', 'admin.weather.badge': 'Sinds 24 maart 2026', @@ -668,8 +675,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Reisverslag', 'admin.addons.catalog.journey.description': "Reistracking & reisdagboek met check-ins, foto's en dagelijkse verhalen", 'admin.addons.catalog.collections.name': 'Collecties', - 'admin.addons.catalog.collections.description': - 'Persoonlijke plekkenbibliotheek — sla plekken uit je reizen op in benoemde lijsten, kopieer ze naar elke reis en deel ze met anderen', + 'admin.addons.catalog.collections.description': 'Verzamel plaatsen uit al je reizen in benoemde lijsten en gebruik ze opnieuw', 'admin.passkey.title': 'Inloggen met passkey', 'admin.passkey.cardHint': 'Laat gebruikers inloggen met passkeys (WebAuthn). Standaard uit.', 'admin.passkey.login': 'Inloggen met passkey inschakelen', diff --git a/shared/src/i18n/pl/admin.ts b/shared/src/i18n/pl/admin.ts index 5dfe7a857..72a64d4a4 100644 --- a/shared/src/i18n/pl/admin.ts +++ b/shared/src/i18n/pl/admin.ts @@ -506,7 +506,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listy', 'admin.addons.catalog.packing.description': 'Listy pakowania i zadania do wykonania dla Twoich podróży', 'admin.addons.catalog.budget.name': 'Koszty', - 'admin.addons.catalog.budget.description': 'Śledź wydatki i planuj budżet podróży', + 'admin.addons.catalog.budget.description': 'Śledź wydatki na podróż i dziel je między uczestników', 'admin.addons.catalog.documents.name': 'Dokumenty', 'admin.addons.catalog.documents.description': 'Przechowuj i zarządzaj dokumentami podróżnymi', 'admin.addons.catalog.vacay.name': 'Urlopy', @@ -514,13 +514,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Mapa świata z odwiedzonymi krajami i statystykami podróży', 'admin.addons.catalog.collab.name': 'Współpraca', - 'admin.addons.catalog.collab.description': 'Notatki w czasie rzeczywistym, ankiety i czat do planowania podróży', + 'admin.addons.catalog.collab.description': 'Notatki, ankiety, czat i podpowiedzi do wspólnego planowania', 'admin.addons.catalog.memories.name': 'Zdjęcia (Immich)', 'admin.addons.catalog.memories.description': 'Udostępniaj zdjęcia z podróży za pośrednictwem swojej instancji Immich', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Model Context Protocol dla integracji asystenta AI', 'admin.addons.subtitleBefore': 'Włączaj lub wyłączaj funkcje, aby dostosować swoje doświadczenie w ', 'admin.addons.subtitleAfter': '.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importuj miejsca z udostępnionej listy Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Synchronizuj loty z własnej instancji AirTrail', + 'admin.addons.catalog.llm_parsing.name': 'Analiza AI', + 'admin.addons.catalog.llm_parsing.description': 'Odczytuje rezerwacje, których nie rozumie wbudowany parser, przy użyciu wybranego modelu AI', 'admin.addons.enabled': 'Włączone', 'admin.addons.disabled': 'Wyłączone', 'admin.addons.type.trip': 'Podróż', @@ -531,6 +537,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Usługi backendowe i integracje API bez dedykowanej strony', 'admin.addons.toast.updated': 'Dodatek został zaktualizowany', 'admin.addons.toast.error': 'Nie udało się zaktualizować dodatku', + 'admin.addons.group.count': '{enabled} z {total} włączonych', 'admin.addons.noAddons': 'Brak dostępnych dodatków', 'admin.weather.title': 'Dane pogodowe', 'admin.weather.badge': 'Od 24 marca 2026', @@ -677,8 +684,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Śledzenie podróży i dziennik z zameldowaniami, zdjęciami i codziennymi historiami', 'admin.addons.catalog.collections.name': 'Kolekcje', - 'admin.addons.catalog.collections.description': - 'Osobista biblioteka miejsc — zapisuj miejsca z podróży na nazwanych listach, kopiuj je do dowolnej podróży i udostępniaj innym', + 'admin.addons.catalog.collections.description': 'Zbieraj miejsca ze wszystkich podróży na nazwane listy i używaj ich ponownie', 'admin.passkey.title': 'Logowanie kluczem dostępu', 'admin.passkey.cardHint': 'Pozwól użytkownikom logować się kluczami dostępu (WebAuthn). Domyślnie wyłączone.', 'admin.passkey.login': 'Włącz logowanie kluczem dostępu', diff --git a/shared/src/i18n/ru/admin.ts b/shared/src/i18n/ru/admin.ts index 541e3f568..67b6672a3 100644 --- a/shared/src/i18n/ru/admin.ts +++ b/shared/src/i18n/ru/admin.ts @@ -514,7 +514,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Списки', 'admin.addons.catalog.packing.description': 'Списки вещей и задачи для ваших поездок', 'admin.addons.catalog.budget.name': 'Расходы', - 'admin.addons.catalog.budget.description': 'Отслеживайте расходы и планируйте бюджет поездки', + 'admin.addons.catalog.budget.description': 'Учитывайте расходы на поездку и делите их между участниками', 'admin.addons.catalog.documents.name': 'Документы', 'admin.addons.catalog.documents.description': 'Храните и управляйте документами для путешествий', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -522,9 +522,15 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Карта мира с посещёнными странами и статистикой путешествий', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': 'Заметки в реальном времени, опросы и чат для планирования поездок', + 'admin.addons.catalog.collab.description': 'Заметки, опросы, чат и подсказки для совместного планирования', 'admin.addons.subtitleBefore': 'Включайте или отключайте функции для настройки ', 'admin.addons.subtitleAfter': ' под себя.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Импорт мест из общего списка Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Синхронизация рейсов с вашего сервера AirTrail', + 'admin.addons.catalog.llm_parsing.name': 'Разбор с помощью ИИ', + 'admin.addons.catalog.llm_parsing.description': 'Читает брони, которые не понимает встроенный анализатор, с помощью выбранной вами модели ИИ', 'admin.addons.enabled': 'Включено', 'admin.addons.disabled': 'Отключено', 'admin.addons.type.trip': 'Поездка', @@ -535,6 +541,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Фоновые сервисы и API-интеграции без отдельной страницы', 'admin.addons.toast.updated': 'Дополнение обновлено', 'admin.addons.toast.error': 'Не удалось обновить дополнение', + 'admin.addons.group.count': 'Включено {enabled} из {total}', 'admin.addons.noAddons': 'Нет доступных дополнений', 'admin.weather.title': 'Данные о погоде', 'admin.weather.badge': 'С 24 марта 2026', @@ -669,8 +676,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Отслеживание поездок и дневник путешествий с отметками, фото и ежедневными историями', 'admin.addons.catalog.collections.name': 'Коллекции', - 'admin.addons.catalog.collections.description': - 'Личная библиотека мест — сохраняйте места из разных поездок в именованные списки, копируйте в любую поездку, делитесь с другими', + 'admin.addons.catalog.collections.description': 'Собирайте места из любых поездок в именованные списки и используйте их снова', 'admin.passkey.title': 'Вход по passkey', 'admin.passkey.cardHint': 'Разрешите пользователям входить с помощью passkeys (WebAuthn). По умолчанию выключено.', 'admin.passkey.login': 'Включить вход по passkey', diff --git a/shared/src/i18n/sv/admin.ts b/shared/src/i18n/sv/admin.ts index 08dc66cd9..4d5c8cbf3 100644 --- a/shared/src/i18n/sv/admin.ts +++ b/shared/src/i18n/sv/admin.ts @@ -567,7 +567,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listor', 'admin.addons.catalog.packing.description': 'Packlistor och saker att göra inför dina resor', 'admin.addons.catalog.budget.name': 'Kostnader', - 'admin.addons.catalog.budget.description': 'Håll koll på utgifterna och planera din resebudget', + 'admin.addons.catalog.budget.description': 'Håll koll på resans utgifter och dela dem mellan resenärerna', 'admin.addons.catalog.documents.name': 'Dokument', 'admin.addons.catalog.documents.description': 'Spara och hantera resedokument', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -575,13 +575,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Världskarta med besökta länder och resestatistik', 'admin.addons.catalog.collab.name': 'Samarbete', - 'admin.addons.catalog.collab.description': 'Noteringar, omröstningar och chatt i realtid för resplanering', + 'admin.addons.catalog.collab.description': 'Anteckningar, omröstningar, chatt och förslag för gemensam planering', 'admin.addons.catalog.memories.name': 'Foton (Immich)', 'admin.addons.catalog.memories.description': 'Dela resefoton via din Immich-instans', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Modellkontextprotokoll för integration av AI-assistenter', 'admin.addons.subtitleBefore': 'Aktivera eller inaktivera funktioner för att anpassa din ', 'admin.addons.subtitleAfter': ' erfarenhet.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Importera platser från en delad Naver Maps-lista', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Synkronisera flyg från din egen AirTrail-instans', + 'admin.addons.catalog.llm_parsing.name': 'AI-tolkning', + 'admin.addons.catalog.llm_parsing.description': 'Läser bokningar som den inbyggda tolken inte klarar, med en AI-modell du väljer', 'admin.addons.enabled': 'Aktiverad', 'admin.addons.disabled': 'Inaktiverad', 'admin.addons.type.trip': 'Resa', @@ -592,6 +598,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Backend-tjänster och API-integrationer utan egen sida', 'admin.addons.toast.updated': 'Tillägget har uppdaterats', 'admin.addons.toast.error': 'Det gick inte att uppdatera tillägget', + 'admin.addons.group.count': '{enabled} av {total} aktiverade', 'admin.addons.noAddons': 'Inga tillägg tillgängliga', 'admin.weather.title': 'Väderdata', 'admin.weather.badge': 'Sedan den 24 mars 2026', @@ -684,8 +691,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': 'Journey', 'admin.addons.catalog.journey.description': 'Resespårning och resedagbok med incheckningar, foton och dagliga inlägg', 'admin.addons.catalog.collections.name': 'Samlingar', - 'admin.addons.catalog.collections.description': - 'Personligt platsbibliotek — spara platser mellan resor i namngivna listor, kopiera till valfri resa, dela med andra', + 'admin.addons.catalog.collections.description': 'Samla platser från alla resor i namngivna listor och återanvänd dem', 'admin.passkey.title': 'Inloggningsnyckel', 'admin.passkey.cardHint': 'Låter användare att logga in med inloggningsnyckel (WebAuthn). Avstängt som standard.', 'admin.passkey.login': 'Aktivera inloggningsnyckel', diff --git a/shared/src/i18n/tr/admin.ts b/shared/src/i18n/tr/admin.ts index b0768eee3..8ed1c605a 100644 --- a/shared/src/i18n/tr/admin.ts +++ b/shared/src/i18n/tr/admin.ts @@ -553,7 +553,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Listeler', 'admin.addons.catalog.packing.description': 'Seyahatleriniz için paket listeleri ve yapılacak işler', 'admin.addons.catalog.budget.name': 'Maliyetler', - 'admin.addons.catalog.budget.description': 'Harcamaları takip edin ve seyahat bütçenizi planlayın', + 'admin.addons.catalog.budget.description': 'Gezi masraflarını takip edin ve yolcular arasında paylaştırın', 'admin.addons.catalog.documents.name': 'Belgeler', 'admin.addons.catalog.documents.description': 'Seyahat belgelerini saklayın ve yönetin', 'admin.addons.catalog.vacay.name': 'Tatil', @@ -561,7 +561,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Ziyaret edilen ülkeleri ve seyahat istatistiklerini içeren dünya haritası', 'admin.addons.catalog.collab.name': 'İş birliği', - 'admin.addons.catalog.collab.description': 'Seyahat planlaması için gerçek zamanlı notlar, anketler ve sohbet', + 'admin.addons.catalog.collab.description': 'Birlikte planlamak için notlar, anketler, sohbet ve öneriler', 'admin.addons.catalog.memories.name': 'Fotoğraflar (Immich)', 'admin.addons.catalog.memories.description': 'Immich örneğiniz aracılığıyla gezi fotoğraflarını paylaşın', 'admin.addons.catalog.mcp.name': 'MCP', @@ -569,6 +569,12 @@ const admin: TranslationStrings = { 'admin.addons.subtitleBefore': 'Kişiselleştirmenizi özelleştirmek için özellikleri etkinleştirin veya devre dışı bırakın.', 'admin.addons.subtitleAfter': 'deneyim.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Paylaşılan bir Naver Haritalar listesinden yerleri içe aktarın', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Kendi sunucunuzdaki AirTrail örneğinden uçuşları eşitleyin', + 'admin.addons.catalog.llm_parsing.name': 'Yapay zekâ ile ayrıştırma', + 'admin.addons.catalog.llm_parsing.description': 'Yerleşik ayrıştırıcının okuyamadığı rezervasyonları, seçtiğiniz yapay zekâ modeliyle okur', 'admin.addons.enabled': 'Etkinleştirilmiş', 'admin.addons.disabled': 'Engelli', 'admin.addons.type.trip': 'Seyahat', @@ -579,6 +585,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Özel bir sayfa olmadan arka uç hizmetleri ve API entegrasyonları', 'admin.addons.toast.updated': 'Eklenti güncellendi', 'admin.addons.toast.error': 'Eklenti güncellenemedi', + 'admin.addons.group.count': '{total} eklentiden {enabled} tanesi etkin', 'admin.addons.noAddons': 'Kullanılabilir eklenti yok', 'admin.weather.title': 'Hava Durumu Verileri', 'admin.weather.badge': "24 Mart 2026'dan beri", @@ -671,8 +678,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Check-in, fotoğraf ve günlük hikâyelerle seyahat takibi ve seyahat günlüğü', 'admin.addons.catalog.collections.name': 'Koleksiyonlar', - 'admin.addons.catalog.collections.description': - 'Kişisel yer kitaplığı — yerleri geziler arasında adlandırılmış listelere kaydet, herhangi bir geziye kopyala, başkalarıyla paylaş', + 'admin.addons.catalog.collections.description': 'Her geziden yerleri adlandırılmış listelerde toplayın ve yeniden kullanın', 'admin.passkey.title': 'Passkey ile oturum açma', 'admin.passkey.cardHint': 'Kullanıcıların passkey (WebAuthn) ile oturum açmasına izin verin. Varsayılan olarak kapalı.', diff --git a/shared/src/i18n/uk/admin.ts b/shared/src/i18n/uk/admin.ts index 610dafdd2..85e1cba87 100644 --- a/shared/src/i18n/uk/admin.ts +++ b/shared/src/i18n/uk/admin.ts @@ -511,7 +511,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Списки', 'admin.addons.catalog.packing.description': 'Списки речей та завдання для ваших подорожей', 'admin.addons.catalog.budget.name': 'Витрати', - 'admin.addons.catalog.budget.description': 'Відстежуйте витрати та плануйте бюджет подорожі', + 'admin.addons.catalog.budget.description': 'Стежте за витратами на подорож і діліть їх між учасниками', 'admin.addons.catalog.documents.name': 'Документи', 'admin.addons.catalog.documents.description': 'Зберігайте та керуйте документами для подорожей', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -519,9 +519,15 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': 'Карта світу з відвіданими країнами та статистикою подорожей', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': 'Нотатки в реальному часі, опитування та чат для планування подорожей', + 'admin.addons.catalog.collab.description': 'Нотатки, опитування, чат і підказки для спільного планування', 'admin.addons.subtitleBefore': 'Увімкніть або вимкніть функції для налаштування ', 'admin.addons.subtitleAfter': ' під себе.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Імпорт місць зі спільного списку Naver Maps', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Синхронізація рейсів із вашого сервера AirTrail', + 'admin.addons.catalog.llm_parsing.name': 'Розбір за допомогою ШІ', + 'admin.addons.catalog.llm_parsing.description': 'Читає бронювання, які не розпізнає вбудований аналізатор, за допомогою обраної вами моделі ШІ', 'admin.addons.enabled': 'Увімкнено', 'admin.addons.disabled': 'Вимкнено', 'admin.addons.type.trip': 'Поїздка', @@ -532,6 +538,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Фонові сервіси та API-інтеграції без окремої сторінки', 'admin.addons.toast.updated': 'Доповнення оновлено', 'admin.addons.toast.error': 'Не вдалося оновити доповнення', + 'admin.addons.group.count': 'Увімкнено {enabled} з {total}', 'admin.addons.noAddons': 'Немає доступних доповнень', 'admin.weather.title': 'Дані про погоду', 'admin.weather.badge': 'З 24 березня 2026', @@ -665,8 +672,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Відстеження поїздок і щоденник подорожей з позначками, фото та щоденними історіями', 'admin.addons.catalog.collections.name': 'Колекції', - 'admin.addons.catalog.collections.description': - 'Особиста бібліотека місць — зберігайте місця з різних подорожей у іменовані списки, копіюйте в будь-яку подорож, діліться з іншими', + 'admin.addons.catalog.collections.description': 'Збирайте місця з будь-яких подорожей у іменовані списки та використовуйте їх знову', 'admin.passkey.title': 'Вхід за допомогою passkey', 'admin.passkey.cardHint': 'Дозволити користувачам входити за допомогою passkey (WebAuthn). За замовчуванням вимкнено.', diff --git a/shared/src/i18n/vi/admin.ts b/shared/src/i18n/vi/admin.ts index 5b6bdf681..909912990 100644 --- a/shared/src/i18n/vi/admin.ts +++ b/shared/src/i18n/vi/admin.ts @@ -509,7 +509,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': 'Danh sách', 'admin.addons.catalog.packing.description': 'Danh sách đóng gói và nhiệm vụ cần làm cho chuyến đi của bạn', 'admin.addons.catalog.budget.name': 'Chi phí', - 'admin.addons.catalog.budget.description': 'Theo dõi chi phí và lập kế hoạch ngân sách chuyến đi của bạn', + 'admin.addons.catalog.budget.description': 'Theo dõi chi phí chuyến đi và chia cho những người cùng đi', 'admin.addons.catalog.documents.name': 'Tài liệu', 'admin.addons.catalog.documents.description': 'Lưu trữ và quản lý tài liệu du lịch', 'admin.addons.catalog.vacay.name': 'Kỳ nghỉ', @@ -517,14 +517,19 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Bản đồ', 'admin.addons.catalog.atlas.description': 'Bản đồ thế giới với các quốc gia đã ghé thăm và số liệu thống kê du lịch', 'admin.addons.catalog.collab.name': 'Cộng tác', - 'admin.addons.catalog.collab.description': - 'Ghi chú, thăm dò ý kiến ​​và trò chuyện theo thời gian thực để lập kế hoạch chuyến đi', + 'admin.addons.catalog.collab.description': 'Ghi chú, bình chọn, trò chuyện và gợi ý để cùng lên kế hoạch', 'admin.addons.catalog.memories.name': 'Hình ảnh (Immich)', 'admin.addons.catalog.memories.description': 'Chia sẻ ảnh chuyến đi qua phiên bản Immich của bạn', 'admin.addons.catalog.mcp.name': 'MCP', 'admin.addons.catalog.mcp.description': 'Giao thức ngữ cảnh mẫu để tích hợp trợ lý AI', 'admin.addons.subtitleBefore': 'Bật hoặc tắt các tính năng để tùy chỉnh', 'admin.addons.subtitleAfter': 'kinh nghiệm.', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': 'Nhập địa điểm từ danh sách Naver Maps được chia sẻ', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': 'Đồng bộ chuyến bay từ máy chủ AirTrail của bạn', + 'admin.addons.catalog.llm_parsing.name': 'Phân tích bằng AI', + 'admin.addons.catalog.llm_parsing.description': 'Đọc các đặt chỗ mà bộ phân tích tích hợp không hiểu, bằng mô hình AI bạn chọn', 'admin.addons.enabled': 'Đã bật', 'admin.addons.disabled': 'Tắt', 'admin.addons.type.trip': 'Chuyến đi', @@ -535,6 +540,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': 'Dịch vụ phụ trợ và tích hợp API không có trang chuyên dụng', 'admin.addons.toast.updated': 'Đã cập nhật tiện ích bổ sung', 'admin.addons.toast.error': 'Không thể cập nhật tiện ích bổ sung', + 'admin.addons.group.count': '{enabled} trên {total} đã bật', 'admin.addons.noAddons': 'Không có tiện ích bổ sung nào', 'admin.weather.title': 'Dữ liệu thời tiết', 'admin.weather.badge': 'Kể từ ngày 24 tháng 3 năm 2026', @@ -627,8 +633,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.description': 'Theo dõi chuyến đi & nhật ký du lịch với nhận phòng, ảnh và câu chuyện hàng ngày', 'admin.addons.catalog.collections.name': 'Bộ sưu tập', - 'admin.addons.catalog.collections.description': - 'Thư viện địa điểm cá nhân — lưu địa điểm qua các chuyến đi vào danh sách có tên, sao chép vào bất kỳ chuyến đi nào, chia sẻ với người khác', + 'admin.addons.catalog.collections.description': 'Gom địa điểm từ mọi chuyến đi vào danh sách có tên và dùng lại', 'admin.passkey.title': 'Đăng nhập bằng mật mã', 'admin.passkey.cardHint': 'Cho phép người dùng đăng nhập bằng mật mã (WebAuthn). Tắt theo mặc định.', 'admin.passkey.login': 'Kích hoạt đăng nhập bằng mật mã', diff --git a/shared/src/i18n/zh-TW/admin.ts b/shared/src/i18n/zh-TW/admin.ts index 0bceec3ec..2247ffb7a 100644 --- a/shared/src/i18n/zh-TW/admin.ts +++ b/shared/src/i18n/zh-TW/admin.ts @@ -507,7 +507,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': '行李', 'admin.addons.catalog.packing.description': '每次旅行的行李準備清單', 'admin.addons.catalog.budget.name': '費用', - 'admin.addons.catalog.budget.description': '跟蹤支出並規劃旅行預算', + 'admin.addons.catalog.budget.description': '記錄行程開銷並在同行者之間分攤', 'admin.addons.catalog.documents.name': '文件', 'admin.addons.catalog.documents.description': '儲存和管理旅行文件', 'admin.addons.catalog.vacay.name': 'Vacay', @@ -515,9 +515,15 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': 'Atlas', 'admin.addons.catalog.atlas.description': '標記已訪問國家和旅行統計的世界地圖', 'admin.addons.catalog.collab.name': 'Collab', - 'admin.addons.catalog.collab.description': '旅行規劃的即時筆記、投票和聊天', + 'admin.addons.catalog.collab.description': '備註、投票、聊天與建議,方便一起規劃', 'admin.addons.subtitleBefore': '啟用或停用功能以自定義你的 ', 'admin.addons.subtitleAfter': ' 體驗。', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': '從共享的 Naver 地圖清單匯入地點', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': '從你自架的 AirTrail 執行個體同步航班', + 'admin.addons.catalog.llm_parsing.name': 'AI 解析', + 'admin.addons.catalog.llm_parsing.description': '用你選擇的 AI 模型讀取內建解析器無法識別的訂位', 'admin.addons.enabled': '已啟用', 'admin.addons.disabled': '已停用', 'admin.addons.type.trip': '旅行', @@ -528,6 +534,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': '後端服務和 API 整合,無專屬頁面', 'admin.addons.toast.updated': '擴充套件已更新', 'admin.addons.toast.error': '更新擴充套件失敗', + 'admin.addons.group.count': '{total} 項中已啟用 {enabled} 項', 'admin.addons.noAddons': '暫無可用擴充套件', 'admin.weather.title': '天氣資料', 'admin.weather.badge': '自 2026 年 3 月 24 日起', @@ -614,8 +621,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': '旅程', 'admin.addons.catalog.journey.description': '旅行追蹤與旅行日誌,包含打卡、照片和每日故事', 'admin.addons.catalog.collections.name': '收藏', - 'admin.addons.catalog.collections.description': - '個人地點庫 — 將各行程的地點儲存到具名清單中,複製到任意行程,並與他人分享', + 'admin.addons.catalog.collections.description': '把任何行程的地點收進命名清單,隨時再次使用', 'admin.passkey.title': 'Passkey 登入', 'admin.passkey.cardHint': '讓使用者使用 Passkey(WebAuthn)登入。預設為關閉。', 'admin.passkey.login': '啟用 Passkey 登入', diff --git a/shared/src/i18n/zh/admin.ts b/shared/src/i18n/zh/admin.ts index 43b9ca469..55e65dc17 100644 --- a/shared/src/i18n/zh/admin.ts +++ b/shared/src/i18n/zh/admin.ts @@ -467,7 +467,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.packing.name': '列表', 'admin.addons.catalog.packing.description': '行程打包清单与待办任务', 'admin.addons.catalog.budget.name': '费用', - 'admin.addons.catalog.budget.description': '跟踪支出并规划旅行预算', + 'admin.addons.catalog.budget.description': '记录行程开销并在同行者之间分摊', 'admin.addons.catalog.documents.name': '文档', 'admin.addons.catalog.documents.description': '存储和管理旅行文档', 'admin.addons.catalog.vacay.name': '假期', @@ -475,9 +475,15 @@ const admin: TranslationStrings = { 'admin.addons.catalog.atlas.name': '足迹', 'admin.addons.catalog.atlas.description': '标记已访问国家和旅行统计的世界地图', 'admin.addons.catalog.collab.name': '协作', - 'admin.addons.catalog.collab.description': '旅行规划的实时笔记、投票和聊天', + 'admin.addons.catalog.collab.description': '备忘、投票、聊天和建议,方便一起规划', 'admin.addons.subtitleBefore': '启用或禁用功能以自定义你的 ', 'admin.addons.subtitleAfter': ' 体验。', + 'admin.addons.catalog.naver_list_import.name': 'Naver List Import', + 'admin.addons.catalog.naver_list_import.description': '从共享的 Naver 地图列表导入地点', + 'admin.addons.catalog.airtrail.name': 'AirTrail', + 'admin.addons.catalog.airtrail.description': '从你自建的 AirTrail 实例同步航班', + 'admin.addons.catalog.llm_parsing.name': 'AI 解析', + 'admin.addons.catalog.llm_parsing.description': '用你选择的 AI 模型读取内置解析器无法识别的预订', 'admin.addons.enabled': '已启用', 'admin.addons.disabled': '已禁用', 'admin.addons.type.trip': '旅行', @@ -488,6 +494,7 @@ const admin: TranslationStrings = { 'admin.addons.integrationHint': '后端服务和 API 集成,无专属页面', 'admin.addons.toast.updated': '扩展已更新', 'admin.addons.toast.error': '更新扩展失败', + 'admin.addons.group.count': '{total} 项中已启用 {enabled} 项', 'admin.addons.noAddons': '暂无可用扩展', 'admin.weather.title': '天气数据', 'admin.weather.badge': '自 2026 年 3 月 24 日起', @@ -612,8 +619,7 @@ const admin: TranslationStrings = { 'admin.addons.catalog.journey.name': '旅程', 'admin.addons.catalog.journey.description': '旅行追踪与旅行日志,包含签到、照片和每日故事', 'admin.addons.catalog.collections.name': '收藏', - 'admin.addons.catalog.collections.description': - '个人地点库 — 将各行程的地点保存到命名列表中,复制到任意行程,并与他人共享', + 'admin.addons.catalog.collections.description': '把任意行程的地点收进命名列表,随时再次使用', 'admin.passkey.title': '通行密钥登录', 'admin.passkey.cardHint': '允许用户使用通行密钥(WebAuthn)登录。默认关闭。', 'admin.passkey.login': '启用通行密钥登录',