* 3.3.0 (#1472)

* feat(plugins): grow the frame bridge — fill pages, confirm, openExternal, live context

- page/trip-page hosts pass fill: the frame pins to 100% height and ignores
  trek:resize, so a kit plugin's auto height report no longer collapses a full
  page into a floating island with dead space below (widgets keep self-sizing)
- trek:confirm renders the native ConfirmDialog host-side (the sandbox has no
  allow-modals) and answers trek:confirm:result; one at a time
- trek:openExternal opens validated http(s) URLs in a noopener tab — the
  sandbox has no allow-popups, so plugins simply couldn't link out before
- trek:notify accepts an optional duration, clamped to 1.5-15s
- context gains dir (rtl/ltr) and is re-pushed when locale or format settings
  change, not just on appearance mutations
- core events for the trip in view are forwarded as trek:event — names only,
  never payloads, mirroring the server-side events surface

* fix(plugins): move trip warnings out of the content area

The warning pills overlaid the top of every planner tab at full width, sitting
on the map and its toolbar. Now a warning from a plugin that owns a trip-page
tab renders as a compact chip in the navbar centre (click jumps to the tab; the
navbar centre is free on trip pages), and everything else floats above the
content at the bottom instead. Mobile keeps all warnings in the bottom overlay
since the desktop navbar isn't there. The trip tab's frame also opts into the
new fill mode.

* feat(plugin-sdk): 1.4.0 — motion library + new bridge helpers in the kit

Mirrors the host's animation vocabulary 1:1 into TREK_UI_CSS (menu/popover/
modal/backdrop/toast enters, drawer variant under 640px, page-enter, stagger,
skeleton shimmer, chart reveals) including the reduced-motion degrade to a
gentle fade. window.trek grows confirm(), openExternal(), onEvent() and a
notify duration, and applyContext now stamps lang/dir on the document so RTL
hosts get RTL plugin UIs.

* feat(plugins): surface registry download counts in browse

The registry now aggregates GitHub release download counts per plugin
(TREK-Plugins#18) as an entry-level downloadCount. Project it through
browse/detail and show it as a compact stat on the browse cards and in the
detail meta grid. Counts are raw asset downloads (updates and CI included),
so the UI says downloads, not installs.

* docs(plugins): document the grown bridge surface and motion classes

* fix(plugins): harden the new bridge paths

Review pass over the bridge additions:

- keep the unstable useToast() object out of the effect deps (ref instead) —
  it re-created the effect on every parent render, and with the new live
  repost that meant a trek:context flood into the frame
- reset loads/height/confirm state and key the iframe when a host swaps
  pluginId in place (tab bar, /plugins/:id) — the new plugin's document was
  refused as a 'navigated' frame and every kit promise hung
- confirm dialogs always lead with the host-controlled plugin name so a
  plugin can't dress its dialog up as a TREK system prompt; answer/refuse
  moved out of setState updaters (StrictMode ran them twice)
- Number.isFinite on the notify duration (NaN parked a sticky toast)
- don't forward other plugins' namespaced broadcasts as trek:event; a
  plugin's own plugin:{id}:* broadcasts now reach its frame though
- teach the SDK dev preview the confirm/openExternal contract so
  trek.confirm() resolves in /preview
- 999,950 downloads formats as 1M, not 1000k

* fix(planner): let plugin warning chips grow wider before truncating

The nav-centre chip capped at 340px, so a longer warning (e.g. the TREK x
Japan weather prompt) was ellipsised almost immediately. Scale it with the
viewport up to 520px so most messages read in full while still yielding on
narrow desktops.

* feat(plugins): sort the plugin browser by download count

Discover now honours the sort dropdown (it was always alphabetical) and adds
a 'Most downloads' option that ranks the registry by downloadCount. The sort
keys are scoped per tab — updates-first stays with Installed, most-downloads
with Discover — and snap back to name when the tab can't offer them.

* feat(plugin-sdk): auto-upgrade native <select> to a host-styled dropdown

A sandboxed plugin can't reach the host's components, and a native <select>
draws its popup from the OS — so plugin dropdowns never matched TREK. The design
kit now enhances every <select> into a keyboard-accessible listbox that uses the
kit tokens, keeping the real element as the value/form source (it still fires
change). Authors write a plain <select> and get the host look for free; opt a
field out with data-trek-native. validate warns when a plugin ships a <select>
without inlining the kit.

* feat(plugins): add issue url link

* feat(plugins): reservations write + cross-trip reads

- db:write:reservations -> reservations.create/update/delete, gated exactly like
  the REST/MCP path (reservation_edit + trip membership, acting user host-bound,
  no impersonation) and delegating to ReservationsService so the accommodation,
  budget-sync, booking-notification and reservation:* broadcasts match the web
  app 1:1 — a booking/flight/import plugin can finally write a reservation
- trips.listMine / reservations.listMine: enumerate every trip and booking the
  acting user can access (membership baked into listTrips, never a raw
  cross-tenant SELECT) — dashboards/aggregates were impossible before
- audit: derive auditability from METHOD_PERMISSION so a new capability method
  can't be added un-audited by omission
- typed ctx.reservations.* / ctx.trips.listMine, perm label (en/de), wiki

* feat(plugins): read scopes for journal, atlas, vacay and day notes

- db:read:journal / db:read:atlas / db:read:vacay expose the acting user's OWN
  journals / visited countries+regions / vacation plan across all their trips
  (user-scoped like costs.listMine, each gated on its addon being enabled),
  reusing the addon's existing readers
- db:read:daynotes -> daynotes.list(tripId, dayId), trip-scoped and
  membership-checked like the other trip reads
- typed ctx.journal / atlas / vacay / daynotes, perm labels (en/de), wiki,
  audit resource labels, tests

* feat(plugins): day notes write scope

- db:write:daynotes -> daynotes.create/update/delete, gated under the app's
  'day_edit' permission (like days) with the day verified to belong to the trip;
  reuses dayNoteService and broadcasts the same dayNote:* events so open
  sessions update live
- typed ctx.daynotes.create/update/delete, perm label (en/de), wiki, tests

* feat(plugins): run declared background jobs on a schedule

- plugins already declared jobs {id, schedule} but the cron was never wired. The
  host now schedules them: host-entry reports each job's schedule, the supervisor
  starts the jobs (node-cron) when the plugin goes active and stops them on
  kill/deactivate so nothing leaks
- opt-in via a new jobs:run permission — scheduled work runs with NO acting user
  (its trip reads stay refused; it can only use ctx.db and declared egress), so
  background execution is a distinct, admin-granted capability. Invalid crons are
  skipped and a throwing job can't break the host
- extracted a small, unit-tested scheduler (plugin-jobs.ts); perm label (en/de),
  wiki, tests

* feat(plugins): read scope for saved-place collections

- db:read:collections -> collections.listMine() / collections.get(id): the acting
  user's own collections (user-scoped, gated on the Collections addon), reusing
  collectionsService
- typed ctx.collections, perm label (en/de), wiki, audit resource labels, tests

* fix(plugins): translate new permission labels to all locales + cover the new wiring

- add the 8 new admin.plugins.perm.* labels (reservations/day-notes writes, the
  journal/atlas/vacay/day-notes/collections reads and jobs:run) to the remaining
  20 locales so the strict i18n key-parity test passes again
- cover the create-rpc-host reservation / day-note / cross-trip / addon-read deps
  (the real side-effect wiring the mocked rpc-host tests don't exercise) so the
  src/nest 80% branch-coverage gate holds

* feat(plugins): dev-link — hot-reload a local plugin against real data

Answers a plugin developer's ask: today you either get `trek-plugin-sdk dev`
(fast hot-reload but MOCK/fixture data) or the full build->pack->upload->activate
cycle (real data, no watcher). Neither gives "local dir + hot-reload + real data".

- POST /admin/plugins/link registers a plugin from a LOCAL built directory by
  symlinking it into the plugins volume — the loader already forks the resolved
  real path, so ZERO loader change — and registering it INACTIVE as `local:link`.
  Validates the manifest + refuses native binaries exactly like a sideload.
- POST /admin/plugins/:id/reload re-forks a linked plugin via the existing
  deactivate->activate primitive (same grants, no re-consent unless the manifest
  widened perms). A best-effort fs.watch auto-reloads on rebuild.
- It runs through the UNCHANGED capability RPC host: real, membership-gated data,
  acting user host-bound, no impersonation — code origin never touches the gate.
- Gated behind TREK_PLUGINS_DEV_LINK on top of admin + kill-switch, because a
  linked plugin bypasses the install-time signature model and, under `npm run
  dev`, the OS jail is off. Off by default; never reachable in production.
- discovery follows a symlinked <root>/<id>; uninstall/link never delete the
  author's source (link-safe removal for POSIX symlinks and Windows junctions).

* docs(plugins): document the dev-link real-data hot-reload workflow

Adds a "Test against a real instance's data (dev-link)" subsection next to the
mock-data SDK preview: TREK_PLUGINS_DEV_LINK, POST /link with a local built dir,
activate + consent, hot-reload via the file-watch / POST /:id/reload / Restart,
and the dev-only security caveats.

* feat(plugins): dev-link admin UI

- surface devLink (TREK_PLUGINS_DEV_LINK) in GET /admin/plugins so the panel shows
  the link form only where dev-link is enabled
- AdminPluginsPanel: a "Link a local plugin" form (path -> POST /link), a Dev-Link
  badge for source_repo=local:link, and adminApi.pluginLink/pluginReload
- fix: the plugin menu treated any non-local:upload source_repo as a GitHub repo,
  so a dev-linked plugin rendered github.com/local:link links — exclude local:link
- labels for the 6 new dev-link UI strings across all 22 locales

* docs(plugins): document the dev-link admin UI

The dev-link section showed only the curl call — surface the Admin → Plugins
"Link a local plugin" field (the primary path) and the Dev-link badge, with curl
kept as the scripting alternative.

* feat(plugins): enrich core events with { entity, entityId }

Subscribed plugins now learn WHICH entity changed, not just the event name — a
reservation/place/day/... id derived host-side from an explicit per-family
whitelist. Threaded through the six event hops WITHOUT touching actingUserId: the
handler still runs with no user, so the id is not dereferenceable (a trip read is
still refused; the id says what to react to, not what it contains). A non-entity id
can never surface — budget:member-paid-updated yields the itemId, never the userId
— and bulk/reorder/sub-entity payloads carry no id. The mapper is pure, synchronous
and never throws into the core broadcast. No new permission (reuses
events:subscribe); backend-only.

* feat(plugins): packing write scope with #858 privacy-scoped broadcasts

- db:write:packing -> packing.create/update/delete, gated under the app's
  'packing_edit' permission (like the REST path) with the host-bound acting user
  as owner; reuses packingService
- replicates the packing privacy model 1:1 (the controller/service helpers aren't
  exported): create/delete fan out to the item's viewers only (owner + recipients,
  or the whole room for a Common item); update runs the four public<->private
  transitions, dropping a freshly-privatized item from the room BEFORE re-adding it
  owner-only so it never leaks. A stale write is BAD_PARAMS with no broadcast
- typed ctx.packing.create/update/delete, perm label (22 locales), wiki, tests
  (rpc-host gating + the four transitions + owner-scoped delete)

* feat(plugins): tableContributor hook — host-rendered view columns/actions (backend)

The registry backend for plugin-contributed columns/actions in the native planner
views (the tabular-reservations use case), mirroring placeDetailProvider:
- hook:table-contributor + the tableContributor hook (getContributions(view,
  tripId, ctx) -> TableContribution[]), double-gated (implement + grant) like the
  other provider hooks
- GET /api/view-contributions/:view/:tripId — view whitelist + membership gate +
  per-provider timeout/fail-safe, plus the hardening the older provider hooks lack:
  every field is String-coerced + length-capped, kind/tone/target enum-whitelisted,
  per-provider counts capped (<=20 columns / <=10 actions), and a column url must be
  http/https/mailto (a javascript:/data: url is click-XSS into the native DOM)
- typed pluginsApi.viewContributions + the ViewContribution union, perm label
  (22 locales), wiki, hardening tests

* feat(plugins): render tableContributor columns/actions in the reservations view

The frontend for the tableContributor hook: a reusable PluginContributions layer
(usePluginViewContributions + PluginColumns/PluginActions) that renders the
host-normalized column/action leaves NATIVELY — a column is text/badge/link, an
action is a button that calls the plugin route or opens its sandboxed frame in a
modal (plugin markup only ever runs inside the opaque-origin iframe). Wired into the
reservations cards (both ReservationCard and TransitJourneyCard) as a strictly-
additive footer keyed by reservation id: zero change to a card when no plugin
contributes. Fetched once per view, fail-safe.

* docs(plugins): bring the permissions wikis current with this cycle

Plugin-Permissions.md was missing every permission added this cycle — add rows for
the read scopes (journal/atlas/vacay/daynotes/collections), the write scopes
(reservations/daynotes/packing, packing noting the #858 owner-scoping), jobs:run
and hook:table-contributor, and correct the events:subscribe row for the new
{ entity, entityId } hint. Add the jobs:run row to Plugin-Development.md too.

* fix(maps): stop quick one-finger pans zooming the map on mobile (#1440)

The global drag-drop-touch polyfill installs document-level touch listeners
on phones. On every single-finger touchend it records a timestamp, and if the
next touch starts within 500ms it synthesises a dblclick on the target, which
the map's default double-click-zoom turns into a zoom-in. Two quick one-finger
pans therefore zoomed instead of panning.

The polyfill only bridges HTML5 drag-and-drop to touch for planner reordering,
which is already disabled on mobile (#1432), so gate its import to viewports
>=1024px (the lg breakpoint useIsMobile uses). Removes the phantom-dblclick
source on phones while keeping touch DnD on large viewports; fixes both the
Leaflet and GL renderers.

* fix(feeds): emit TZID + VTIMEZONE so subscribed calendars respect time zones (#1453)

exportICS emitted timed DTSTART/DTEND as bare floating times (no Z, no TZID),
which iOS/Google Calendar render in the subscriber's local zone instead of the
zone TREK shows. Resolve an IANA zone per timed event — transport endpoints use
their stored timezone (departure drives DTSTART, arrival drives DTEND), while
assignments and hotel/restaurant reservations derive it from place coordinates
via tz-lookup — and attach TZID backed by a VTIMEZONE component. The all-trips
feed now carries deduped VTIMEZONE blocks so TZID references still resolve.

* feat(plugins): render tableContributor contributions in the places + day views

Extends the tableContributor frontend to all three planner views: hoist the shared
PluginCardFooter into PluginContributions, wire the places sidebar (keyed by place
id, rendered as a sibling after each row so the drag/scroll row stays untouched)
and the day panel (keyed by day id, guarded for a null day). Strictly additive +
fail-safe like the reservations view — nothing renders when no plugin contributes.

* fix(vacay): source holiday subdivisions from ISO 3166-2 so all states show

The state/region picker for public-holiday calendars was built from the
union of each holiday's counties for the current year, so a subdivision
only appeared if some holiday that year was tagged with it. States with
no state-specific holiday (e.g. US-WA, and AR/FL/NV/WY in 2026) silently
vanished, blocking calendar creation (#1456).

Source the full, correctly-named subdivision list per country from
ISO 3166-2 instead, merged with any nager county code ISO lacks. Only
region-partitioned countries get a picker, so nationwide-only countries
keep allowing a country-level calendar. No server change needed —
selecting a state already yields federal holidays via applyHolidayCalendars.

* fix(costs): settlements honor custom per-member splits (#1458)

calculateSettlement read each member's custom split amount but its query
never selected budget_item_members.amount, so hasCustomSplit was always
false and every settlement fell back to the equal split. Select bm.amount
so custom amounts drive the balances.

Also blank the Overview 'Per Person' / 'Per Person·Day' columns and CSV
for custom-split items, where a single averaged figure is meaningless.

* feat(plugins): read-convenience + todos + packing bags + tags + roster

A wave of small, high-value capabilities:
- weather:read (ctx.weather.get) — the host's cached forecast, tenant-free
- db:read:categories (ctx.categories.list) — the global place-category list
- db:read:tags / db:write:tags (ctx.tags) — the acting user's own tags, ownership
  re-checked before each write
- trips.members (ctx.trips.members) — the trip roster (id + display fields),
  membership-checked
- db:read:todos / db:write:todos (ctx.todos) — a trip's to-dos, gated by the app's
  packing_edit like the REST path, broadcasts todo:*
- packing bags on ctx.packing (listBags/createBag/updateBag/deleteBag/setBagMembers)
  under db:write:packing — no privacy, plain room broadcasts
perm labels (22 locales), both wikis, rpc-host gating + create-rpc-host wiring tests

* fix(dashboard): render next-trip boarding pass stats on Safari (#1459)

The boarding-pass bar carved its ticket-stub notches with a two-layer
radial-gradient mask composited via mask-composite: intersect (and legacy
-webkit-mask-composite: source-in). Safari mis-composites that multi-layer
path to fully transparent, hiding the entire stats bar while Chrome renders
it fine.

Split .hero-pass into an outer wrapper (left notch) and a .hero-pass-inner
glass panel (right notch), each carrying a single-layer mask so the
mask-composite path is never exercised. Renders identically across engines
and degrades safely where mask-image is unsupported.

* feat(plugins): write scopes for atlas, vacay, journal and collections

The write half of the user-scoped addon reads:
- db:write:atlas -> ctx.atlas.markCountry/unmarkCountry/markRegion/unmarkRegion +
  bucket-list create/delete. Every row is the acting user's own (visited_countries/
  visited_regions/bucket) — no trip scoping, no cross-tenant surface. Unblocks
  AirTrail-style two-way sync (#214)
- db:write:vacay -> ctx.vacay.toggleEntry/toggleCompanyHoliday. The plan is
  resolved HOST-SIDE from the acting user's active plan — a plugin can never name
  another plan, and toggleEntry only toggles the acting user's own PTO day
- db:write:journal -> ctx.journal.createEntry/updateEntry/deleteEntry, self-gated
  by journeyService.canEdit (owner/contributor) against the acting user
- db:write:collections -> ctx.collections.create/update/savePlace/copyToTrip/
  deletePlace, schema-validated; the service's per-collection role checks
  (assertAccess 404 / assertCanEdit 403) map onto RESOURCE_FORBIDDEN
All addon-gated, userless contexts refused, audited. Perm labels (22 locales),
both wikis, gating + wiring tests.

* fix(admin): name the Costs add-on consistently in the catalog

The budget add-on catalog entry still resolved to 'Budget' while the
feature is labeled 'Costs' everywhere else (trip tab, navbar). Align
admin.addons.catalog.budget.name with each locale's trip.tabs.budget
label. Closes #1464

* feat(plugins): file attach, collab content and gated member-add

- db:write:files -> ctx.files.create/createLink/update/softDelete under the app's
  separate file_upload/file_edit/file_delete rights. Content arrives as bounded
  base64 (10MB decoded cap, well under the app's 50MB), the extension is validated
  against the central blocklist BEFORE anything touches disk, and link targets
  must live on the same trip (findForeignLinkTarget). Broadcasts file:*
- db:write:collab -> ctx.collab.createNote/createPoll/votePoll/createMessage
  under collab_edit + the Collab addon, emitting the same collab:* events as the
  app; service-reported errors surface as BAD_PARAMS
- db:write:members -> ctx.trips.addMember. Adding a member GRANTS TRIP ACCESS, so
  it is deliberately its own permission behind the app's member_manage right
  (default: trip owner only) and never bundled with a lower-risk write; the acting
  user is recorded as the inviter, target must exist, owner/duplicate adds no-op
Perm labels (22 locales), both wikis, gating + wiring tests.

* fix(maps): honor check-in/out times for hotel bookend legs (#1465)

The day route drew the accommodation as the day's start/end whenever the
edge stop was a place, ignoring the morningIsSleptHere/eveningIsOvernight
provenance already computed by getDayBookendHotels. On a check-in day an
airport placed before check-in got a spurious hotel -> airport leg, and on
a check-out day a later "home" stop still got a home -> hotel return leg.

Add time-aware shouldDrawMorningLeg/shouldDrawEveningLeg helpers: the
morning leg is the home-base default on a check-in day but is dropped when
the first place is timed before check-in; the evening return leg is off on
a check-out day unless the last place is timed at/before check-out. Wire
them into the map polyline, the sidebar hotel connectors, and the Google
Maps export so all three stay consistent.

* feat(plugins): host-mediated notifications and LLM access

Two host-owned integration primitives — the plugin supplies intent, the host
owns the sensitive part:

- notify:send -> ctx.notify.send({title, body, link?, scope, targetId}). Delegates
  to notificationService.send with a new plugin_notification event (raw title/body
  carried as passthrough params), so recipient resolution, channel fan-out
  (bell inbox + email/ntfy/webhook) and per-user preferences all match core 1:1.
  Recipients are FORCED to the acting user (scope 'user', targetId === uid) or a
  trip they belong to (scope 'trip'); scope 'admin' refused; the in-app link must
  be a relative /path (open-redirect-safe). No arbitrary recipient, no impersonation.
  Users can mute plugin notifications like any other event.
- ai:invoke -> ctx.ai.complete(prompt) / ctx.ai.extract(text, jsonSchema). Runs the
  admin/user-configured provider via resolveLlmConfig + the existing extraction
  client under the acting user; the host holds the (encrypted) key, the plugin
  never sees it. Refused when no provider is configured; 20k-char caps. Output is
  DATA (complete -> {text}, extract -> {results}) and never auto-written, so
  prompt-injection can't reach a write without the plugin's own gated call.

plugin_notification wired through the shared NotificationEventKey + all 22 locales
(inbox passthrough + external channels). Perm labels (22 locales), both wikis,
gating + wiring tests.

* fix(budget): offer every Frankfurter-supported currency (#1470)

The cost currency picker was gated by a hardcoded 47-code list, so
currencies the app can actually convert (OMR, CRC, UGX, MKD, ALL, and
~115 more) couldn't be selected. Replace CURRENCIES/SYMBOLS with the full
set the Frankfurter v2 FX API supports (archived BGN/HRK dropped), unify
the dashboard offline fallback onto it, and teach currencyDecimals about
the newly reachable zero- and three-decimal currencies. A currenciesWith
helper keeps a previously saved (now-archived) selection selectable so it
isn't silently wiped.

* feat(plugins): tableContributor into the costs, packing and files views

Extends the shipped tableContributor hook to three more native views — no new
permission, no new attack surface: the same host-normalized, length-capped,
url-allowlisted (http/https/mailto), enum-bounded, fail-safe pipeline, just more
render sites.

- server: add costs/packing/files to the view-contributions whitelist
- client: widen the ViewName union + the api view type; render PluginCardFooter
  keyed by entityId in the budget category table (a colSpan footer row per item),
  the packing category group (footer after each item row, drag untouched) and the
  files list (footer after each row)

A currency plugin can now drop a converted-amount column onto a cost row, a
receipts plugin a 'view receipt' action onto a file, etc. Controller test asserts
the three new views are accepted; both wikis updated.

* fix(pdf): repeat day header on overflowing itinerary export pages (#1471)

* feat(plugins): map-marker provider hook — plugins can overlay trip-map markers

New declarative provider hook `mapMarkerProvider` (#587 "show bookings on map",
the single most-requested contribution class, with zero contribution point until
now):

- hook:map-marker-provider permission + MapMarkerProvider/MapMarkerContribution SDK
  types + HOOK_PERMISSION wiring
- GET /api/map-markers/:tripId (MapMarkersController) mirrors the view-contributions
  hardening: membership-gated, providers invoked host->plugin on a 5s timeout,
  fail-safe. Every field normalized server-side — coordinates range-checked
  (-90..90 / -180..180), strings String-coerced + length-capped, icon/tone enum-
  whitelisted, popup url http/https/mailto only (a javascript:/data: url would be
  click-XSS), marker count capped at 200 per plugin
- client: PluginMapMarkers layer renders the markers as plain Leaflet Marker+Popup
  inside the trip map; plugin JS NEVER runs on the map canvas, every value is
  host-vetted data. Threaded tripId through MapView; fail-safe fetch

Declarative-only by design, mirroring placeDetailProvider/tableContributor. Perm
label (22 locales), controller hardening test, both wikis.

* feat(plugins): show page plugins in the mobile bottom nav

Page plugins were reachable from the desktop nav pill (Navbar) but not the mobile
tab bar — you had to type /plugins/:id. BottomNav now reads page plugins from the
plugin store and appends them the same way global addons are, mirroring Navbar.
One-file client nav wiring; no new capability surface.

* feat(plugins): per-user plugin settings form + ctx.settings runtime read

Users can now enter their own per-plugin config (an API key, a preference) —
the prerequisite for almost every real integration, previously unreachable
(scope:'user' settings were only listed read-only in the admin panel).

- migration: plugin_user_config (plugin_id, user_id, config JSON) — each user's
  own values, separate from the admin-owned instance plugins.config
- PluginsService.getUserConfig / updateUserConfig / getUserConfigDecrypted +
  readUserSettingDecrypted: secrets encrypted at rest (apiKeyCrypto), masked to
  the client, an unchanged secret (the mask) keeps its stored ciphertext, and only
  DECLARED scope:'user' keys are ever stored
- GET/POST /api/plugin-settings/:id (PluginUserSettingsController) — its own
  user-gated path (not the admin surface, not the /:id/* proxy), JwtAuthGuard only,
  scoped to the acting user
- runtime: ctx.settings.get(key) -> the acting user's decrypted value (unconditional
  RPC, not sensitive cross-tenant; userless job/onLoad gets undefined)
- client: a Plugins tab in Settings host-renders each active plugin's scope:'user'
  fields as an editable form (secrets write-only), reusing the declarative field
  shape — no plugin markup executes

i18n (22 locales), wiki, rpc-host + service + masking/encryption tests.

* fix(journey): keep skeleton suggestions in sync with linked trip places (#1473)

Journey skeleton suggestions mirror a linked trip's day-assigned places, but
sync relied on scattered per-event hooks that several assignment mutation paths
never called: unassign, move and time-change fired nothing, no remove-on-unassign
capability existed, and every MCP assignment tool synced nothing. Skeletons drifted
from the trip.

Add an idempotent reconcileTripSkeletons(tripId) that re-mirrors the trip's
day-assigned places onto every linked journey (add missing skeletons, refresh
date/time/location on move, remove skeletons for unassigned places; filled entries
are detached + noted, never destroyed). Call it from every REST assignment handler
and MCP assignment tool, and fire onPlaceDeleted on single MCP delete_place for
parity. Extract a shared insertSkeletonEntry helper.

* fix(memories): drop hidden Immich assets so Live Photo motion parts don't show a broken thumbnail (#1474)

* fix(transit): anchor arrive-by search time to the destination timezone (#1479)

* feat(plugins): host-brokered OAuth client + trustworthy inbound webhooks

Two integration primitives where the host owns the sensitive part.

Trustworthy webhooks:
- auth:false routes now receive req.headers, but ONLY an explicit, credential-free
  allowlist (the common provider signature/event headers — stripe-signature,
  x-hub-signature-256, svix-*, x-gitlab-event, …). Cookie/Authorization/X-Socket-Id
  and every session/forwarded-auth header are stripped; authenticated routes get {}.
  A plugin can finally verify a provider signature without any way to leak a session.

Host-brokered outbound OAuth (oauth:client):
- the HOST runs the whole flow — authorize -> callback -> token exchange -> refresh —
  with PKCE + single-use, user-bound, TTL'd state, and HOLDS the tokens. The client
  secret + refresh token never leave the host; the plugin only triggers connect and
  reads a short-lived access token via ctx.oauth.getAccessToken() for the acting user.
- provider config (authorize/token url + scopes + client id/secret) is the plugin's
  admin-owned instance settings; endpoints must be https (SSRF backstop, private/local
  hosts refused). Tokens per-user + encrypted at rest (apiKeyCrypto).
- GET/POST /api/plugin-oauth/:id/{status,connect,callback,disconnect} — JwtAuth-gated,
  the callback always redirects to an in-app /settings path (never leaks an error).
- Settings -> Plugins gains a Connect/Disconnect control per configured plugin.

migration: plugin_oauth_tokens + plugin_oauth_state. Perm labels + form strings
(22 locales), both wikis, service (PKCE/state/exchange/refresh/encrypt) + controller
+ proxy header-allowlist + rpc-host gating + create-rpc-host wiring tests.

* fix(navbar): re-measure sliding tab pill after font load and resize (#1481)

The active tab pill was measured once in a layout effect keyed only on activeTab, so on a hard reload it captured the active (bold) label's width against fallback-font metrics and never re-ran when the web font swapped in, leaving the pill slightly offset.

Re-measure after document.fonts.ready resolves and on ResizeObserver changes (container + active button), with an idempotent state update to avoid redundant renders.

* fix(collections): keep the Add-place button reachable after the first save

On a wide/desktop layout the collection toolbar (which hosts the Add
button) was gated on !mapOverlay, so it unmounted as soon as the list
gained its first place with coordinates — leaving only an easy-to-miss
"+" in the map overlay. Keep the toolbar rendered whenever the user can
add a place, and drop the now-redundant map-overlay Add button so there
is a single, predictable Add affordance in every state.

Fixes #1485

* feat(plugins): days + accommodations reads/writes, endpoints on the reservation write path

Community feedback on the 3.2.1 plugin surface: a plugin could write days but
never list them (no way to learn day ids), day_accommodations had no surface at
all, and trips.getReservations was the one reservation read that dropped the
endpoints/day_positions hydration.

- trips.getDays / trips.getAccommodations under db:read:trips (tripRead gate),
  wired to the same dayService lists the REST GETs use
- trips.getReservations now returns the hydrated REST-parity list (endpoints,
  day_positions, joins, normalized accommodation_id) - strict superset
- new db:write:accommodations scope: ctx.accommodations create/update/delete
  gated by day_edit like the accommodations REST path, with the partner-hotel
  reservation + delete cascade and broadcasts intact
- reservation create/update pin the endpoints shape up front (BadParams instead
  of a mid-transaction NOT-NULL or a silently dropped row)
- perm label in all 22 locales, consent PERM_KEYS, wiki tables

* feat(plugins): day-detail widget slot in the day panel

Widgets can now mount inside the trip planner's day panel
(capabilities.widget.slot: 'day-detail'), scoped to the open day via a dayId in
trek:context - the same pattern as the place-detail slot. Covers the requested
per-day plugin content (logistics, outfit planning, live flight status) without
a new plugin type. Day-detail widgets stay off the dashboard, the consent panel
labels the slot in all 22 locales.

* feat(plugins): let the frame CSP serve a plugin's own static assets

The sandboxed frame runs at an opaque origin, so script-src 'self' never
matched and a plugin's own <script src>/<link> files were blocked - authors had
to inline entire React builds into index.html. Add a scheme-less host-source
pinned to the plugin's own /plugin-frame/<id>/ path (charset-checked Host +
plugin id so a stray token can't widen the policy; malformed Host falls back to
inline-only). Multi-file client builds now load as-is; remote hosts stay
blocked, so script URLs remain useless as an egress channel.

* fix(plugin-sdk): catch the package up to the server capability surface

The npm SDK's validator still knew only the 3.2.1 permission set, so
'trek-plugin-sdk validate' (and pack/publish, which run it) hard-rejected any
manifest using the newer scopes - db:write:reservations, notify:send,
hook:map-marker-provider and 25 more. Sync KNOWN_PERMISSIONS with the server
envelope (48 entries), mirror the full PluginContext (reservations,
accommodations, notify/ai/oauth/settings, packing writes + bags, file writes,
collab, tags/todos/daynotes/collections/atlas/vacay/journal, weather,
categories), type the tableContributor/mapMarkerProvider hooks + the
entity/entityId event hint, accept the day-detail widget slot, and extend
createMockHost so plugin unit tests can exercise all of it.

* feat(plugins): grant-scoped entity snapshots on core events

An events:subscribe handler so far learned only WHICH entity changed - useful
for cache busting, useless for reacting to content, and the userless handler
can't refetch. Now the broadcast tap derives a whitelisted field snapshot of
the changed entity and the supervisor attaches it per plugin, only where the
granted set holds the family's matching db:read:* permission (trips family ->
db:read:trips, budget -> db:read:costs, packing -> db:read:packing, dayNote ->
db:read:daynotes, file -> db:read:files). No acting user is ever synthesized.

The whitelists are explicit per family, so user ids (owner/paid_by/uploaded_by/
participants/members), trips.feed_token and future migration columns never
travel; a private packing item (#858) yields no snapshot at all because its
core broadcast is owner-scoped; deletes/reorders/bulk ops carry none.

* feat(plugins): pdf-section, atlas-layer and journal-entry provider hooks

Three more declarative provider surfaces in the map-marker mould - plugins
return data specs, the host normalizes, caps and renders; a slow or failing
provider contributes nothing:

- hook:pdf-section-provider: sections (title + paragraphs + a simple table)
  appended to the trip PDF export, escaped into the same HTML/print pipeline
  as the core content
- hook:atlas-layer-provider: per-user country tint layers on the Atlas map
  (ISO 3166-1 alpha-2 codes only, tone-whitelisted, non-interactive pane so
  mark/unmark clicks keep working)
- hook:journal-entry-provider: extra rows on a journal entry card, gated by
  the same journey access check as the journal routes + the Journey addon

Permission labels in all 22 locales, consent PERM_KEYS, SDK types + manifest
validator in both SDK copies, wiki tables, per-controller hardening tests.

* feat(plugins): trip-page plugins can replace core planner tabs and pick their spot

A trip-page plugin that takes over a core surface (a transit planner
superseding Transports, a costs plugin superseding the budget tab) had to sit
awkwardly next to the tab it replaces. capabilities.tripPage now names the
core tabs to hide while the plugin is active - whitelisted (transports,
buchungen, listen, finanzplan, dateien, collab), 'plan' deliberately not
replaceable, and the tabs return the moment the plugin is deactivated - plus
an optional 0-based position for the plugin's own tab. The feed re-validates
the values out of the DB blob so a hand-edited row can't hide anything else,
the admin list chips a replacing plugin (all 22 locales), and a saved session
tab that got replaced falls back to the plan view.

Also fixes the plugins feed dropping the day-detail widget slot to 'sidebar',
which would have mounted a day-panel widget on the dashboard.

* fix(plugins): audit follow-ups — normalization, secret cleanup, cron leak, slot filter

Adversarial audit of the whole plugin PR surfaced 12 confirmed issues; this
addresses them:

- place-details provider was the ONE hook controller with no normalization: a
  plugin's href/label/value went to the client raw and unbounded. Now normalized
  like journal-entry-rows (safeUrl http/https/mailto, length + count caps).
- trip-warnings capped message length + per-provider count (was unbounded).
- uninstall(deleteData) now also purges plugin_user_config, plugin_oauth_tokens,
  plugin_oauth_state, plugin_meta_migrations and the capability audit — encrypted
  per-user API keys + OAuth refresh tokens no longer survive a 'delete all data'
  and get silently re-adopted on a same-id reinstall.
- supervisor: a crash-restart cycle leaked the dead child's node-cron tasks and
  re-scheduled fresh ones, so a job fired N+1 times per tick after N crashes.
  onExit now stops them, mirroring kill().
- dashboard sidebar no longer mounts place-detail/day-detail widgets (they belong
  in the planner panels).
- reservation endpoint validation relaxed to match the 3.2.1 service: a coord-less
  endpoint is accepted and dropped downstream instead of BadParams (no breaking
  change), while a bad role/non-string still rejects up front.
- a replaced core tab reached by programmatic nav now falls back to the plan view.
- trips.update caps title/description like the places path; plugin-db guard bans
  load_extension as defense-in-depth.
- wiki: event snapshots, string-typed context ids, dayId in the payload, the live
  provider hooks and the costs update/delete grant are now documented correctly.

* feat(plugins): phase-0 lifecycle hardening + per-plugin RPC rate limit

Operational-readiness fixes from the completeness audit:

- Re-activation after a failure worked again: a plugin left in 'error' state by
  a load-failure or crash-auto-disable stayed in the running map, so the admin's
  'enable' button was a silent no-op. activate() now replaces a dead entry.
- Per-plugin RPC rate limit at the dispatch boundary: every ctx.* call runs
  synchronously on the host thread, so a plugin in a tight loop could freeze the
  whole instance (and the reap sweep). A token bucket (generous burst) + an
  in-flight cap now throttle a runaway plugin with a retryable HOST_ERROR; a
  legitimate plugin never notices.
- plugin_error_log retention (500 rows/plugin) so a crash-looper can't grow
  trek.db without bound; the crash-timestamp array is trimmed to its window too.
- TREK_PLUGIN_PERMISSIONS=off now logs a loud one-time warning that the OS
  permission jail is disabled.

* feat(plugins): read symmetry + broker — collab/journal/atlas reads, file content, trip create, rates

The plugin API leaned write-heavy: collab and journal could be written but not
read, files listed but not read, and there was no way to create a trip or see
exchange rates. This closes those gaps in the established RPC+gate pattern (zero
architecture risk), and it's what unlocks the importer + finance plugin classes:

- collab reads: ctx.collab.listNotes/listPolls/listMessages under a new
  db:read:collab (membership + Collab addon, like the REST GETs)
- ctx.journal.getEntries(journeyId): a journey's entries, journey-access-checked,
  under the existing db:read:journal
- ctx.atlas.bucketList(): the acting user's bucket list, under db:read:atlas
- ctx.files.getContent(tripId, fileId): a file's bytes as base64 under a NEW
  db:read:files:content grant (reading a passport scan is more sensitive than its
  filename), size-capped at 10MB before it crosses the IPC pipe, trashed files
  refused
- ctx.trips.create(input): a new trip owned by the acting user, gated by the app's
  trip_create right + a bound user — the capability importers need
- ctx.rates.get(base): cached currency exchange rates, tenant-free like weather

Also caps trips.update title/description like the places path, and the plugin-db
guard now bans load_extension (defense-in-depth). SDK, mock-host, i18n (22
locales), consent labels and the wikis are all in lockstep.

* feat(plugins): deeper integration + user-facing activity transparency

Wave 2 of the completeness work — richer extension points, deeper metadata, and
the transparency that makes the broad read grants accountable:

- db:meta now attaches to reservations + accommodations too (not just
  trip/place/day), gated by reservation_edit / day_edit respectively — the
  natural home for an external-id mapping (AirTrail/calendar/booking-import sync)
  without forking the core schema.
- reservation-detail widget slot: a widget can mount on a booking card, scoped to
  the open reservation via reservationId in trek:context (the place-detail /
  day-detail pattern, third instance).
- tableContributor gains the transports + todos views, so a plugin can add
  host-rendered columns/actions there too.
- User activity log: GET /api/plugin-activity + a Settings → Plugins panel showing
  every host-mediated action a plugin took bound to the signed-in user, across all
  plugins, newest first — the user-facing half of the hash-chained audit. This is
  what legitimizes the deliberately broad read grants: not just the admin, the
  person whose data is read can see what was done in their name.
- DX: the local dev server now binds a default acting user, so the canonical
  ctx.trips.getPlaces(tripId) call works locally instead of failing RESOURCE_
  FORBIDDEN; the create scaffold drops the dead manifest routes[] / capabilities.nav
  fields the host ignores.

SDK, i18n (22 locales), consent labels and the wikis are all in lockstep.

* fix(memories): load Immich album photos on Immich v3

Immich v3 removed the `assets` property from AlbumResponseDto, so
`GET /api/albums/:id` no longer carries album contents. TREK read album
photos from that property, which now parses as undefined and degrades to
an empty array — hence "No photos yet" in the Journey gallery picker even
though the album header shows the right count (that count comes from
`GET /api/albums` -> assetCount, which v3 still returns).

Two call sites read the removed property. Besides getAlbumPhotos (the
reported bug), syncAlbumAssets failed silently on v3: it reported
`success: true, added: 0` while syncing nothing.

Fetch album contents via an `albumIds`-filtered `POST /api/search/metadata`
when `assets` is absent, and feature-detect rather than probe a version.
The two paths are not interchangeable: on v2, searchMetadata
unconditionally scopes results to `[self, ...partners]`
(`asset.ownerId = ANY(userIds)`), so an albumIds search against an album
shared by a non-partner returns nothing. v3 added an albumIds branch that
checks AlbumRead and skips that owner filter. v2 also hard-defaults
`visibility` to `timeline`, dropping archived assets. So v2 must keep
reading the album detail body, which this preserves exactly.

`withExif: true` is required on the search path: it has no default and
gates an inner join, so without it Immich omits `exifInfo` entirely and
every photo's city/country goes null.

The existing test mock returned an album detail body *with* `assets` — it
encoded the v2 assumption, which is why this shipped green. It now models
v3 by default, with explicit v2 coverage asserting no search call is made.

Fixes #1492

* feat(plugins): daily AI/notify budgets, runtime scheduler & reliable event redelivery

Per-plugin daily caps on ai.complete/ai.extract and notify.send (defaults
200 / 100, overridable via TREK_PLUGIN_AI_PER_DAY / TREK_PLUGIN_NOTIFY_PER_DAY),
seeded from the capability audit so a mid-day restart resumes the count instead
of resetting it. Surfaced at GET /plugins/:id/budget.

ctx.scheduler (at / in / every / cancel): persistent, userless timers that
survive restarts and fire a scheduled() handler, riding the existing jobs:run
grant so no new consent or admin setup is needed. Backed by
plugin_scheduled_tasks, swept every 30s, capped at 100 tasks/plugin with an 8 KB
payload and a 60s recurring floor; rows are removed on uninstall.

Core events that fire while a subscriber is mid-restart are now held in a
bounded in-memory buffer (200/plugin, 15 min TTL) and replayed once it goes
active again, with the events:subscribe grant and snapshot gating re-evaluated
at replay time so nothing leaks if a grant was revoked while the plugin was down.

* feat(plugins): GDPR data-subject rights — durable per-plugin erasure + export

New hook:user-data grant with two userless lifecycle handlers a plugin can put
on its definition: deleteUserData and exportUserData. Neither carries an acting
user — the plugin only learns the userId and touches its own db — so the grant
reads nothing from core data; it exists purely so a plugin can honour a GDPR
erasure or data-access request.

When a TREK account is deleted (admin or self-service), every installed plugin
holding the grant gets a row in a new durable erasure queue and its
deleteUserData runs on the next sweep, retried until it ACKs — so erasure
survives the plugin being offline or the server restarting. The core deletion
path notifies the runtime through a dependency-free relay (like the event sink),
keeping the auth/admin services decoupled from the plugins layer, and a plugin
bookkeeping error can never fail the account deletion.

Portability is served by GET /api/admin/plugins/user-data/:userId/export, which
fans exportUserData out to the active granted plugins and aggregates what each
holds about the user. Queue rows are purged on uninstall; the grant is labelled
in all 22 locales.

* feat(plugins): atomic ctx.db.tx for consistent multi-write on a plugin's own db

Plugins could already query/exec/migrate their own SQLite file, but a multi-step
write (move an item between tables, decrement one row and increment another) had
no way to be atomic. db.tx([{sql, args?}, …]) runs up to 100 statements in a
single transaction — all commit or all roll back — and reads within the batch see
its own earlier writes, so read-modify-write is safe. Each op is one statement:
a read returns { rows }, a write { changes }. The same guard (no ATTACH/PRAGMA/
RECURSIVE, size + row caps) applies to every statement in the batch.

* fix(memories): filter hidden Immich assets at the source, not just the picker

#1474 has the same root cause as #1492: the Immich v3 migration. On v2,
searchAssetBuilder hard-defaulted metadata search to `timeline` visibility
(`visibility = options.visibility ?? Timeline`), so hidden Live Photo
motion parts could never come back from a search. v3 defaults to any
visibility except `locked`, so they do — which is why the reporter is on
Immich 3.0.1 and why the bug never appeared before.

Ask for `visibility: 'timeline'` explicitly on the search path. That
restores v2 semantics on both versions and stops hidden assets crossing
the wire, which also fixes a pagination wart: a full page half-made of
motion parts previously rendered as a half-empty page, because hasMore
counts the raw page length while the filter shrinks the rendered set.

The client-side filter was display-only, applied in searchPhotos and
getAlbumPhotos — both picker-listing paths. Nothing guarded persistence
or rendering: getOrCreateTrekPhoto stores any id it is handed, pipeAsset
forwards Immich's 400/404 verbatim, and the photo grid is a plain <img>
with no onError. So syncAlbumAssets, which filtered `type === 'IMAGE'`
only, could persist a hidden IMAGE as a permanently broken tile. It now
applies the same guard, extracted as isVisibleAsset().

Albums keep their filter rather than requesting `timeline` visibility:
albums legitimately contain archived assets, and both the v2 album body
and the v3 album search return them.

Does not address tiles already persisted before this — those still render
broken and need a separate fix.

Refs #1474

* docs(memories): correct Immich version boundaries in the hidden-asset comments

Verified against the v1.120.0 → v3.0.0 OpenAPI specs and server source. The
previous comments said "Immich v2 hard-defaulted metadata search to timeline
visibility". That is true only for 1.133–1.144.

- `visibility` was added in 1.133.0. Before that, searchAssetBuilder applied
  `.$if(options.isVisible !== undefined, ...)` with no default, so pre-1.133
  servers returned hidden assets too. #1474 was therefore not purely a v3
  regression.
- Those servers strip the `visibility: 'timeline'` filter rather than
  rejecting it: Immich validates with `whitelist: true` and no
  `forbidNonWhitelisted`. So the request stays valid, the filter is a no-op,
  and isVisibleAsset() is the ONLY guard there. Say so, so it does not get
  removed later as redundant.
- `albumIds` only exists from 1.135.0. Because unknown properties are stripped,
  an albumIds search against an older server would silently drop the album
  filter and return the entire library as the album's contents. Feature
  detection on `assets` (present through 1.144.1, absent on v3) makes that
  unreachable; a version probe with a wrong boundary would not.

Also cite Immich's own enum, which documents AssetVisibility.Hidden as
"Video part of the LivePhotos and MotionPhotos".

Comments only — no behavior change.

* feat(plugins): dashboard trip-card badges + a mock-host driver for plugin tests

Two additions that round out the plugin platform's breadth and its authoring DX.

tripCardProvider hook (hook:trip-card-provider): a plugin returns small declarative
badges for the dashboard trip cards. The dashboard fetches all visible cards in one
call; the host access-checks every tripId for the acting user, bounds each field
(label/value length, enum tone, http/https/mailto-only url), caps the count and drops
any badge for a card that wasn't requested — plugin JS never runs on the dashboard.
Rendered as text chips under the card meta; labelled + gated in all 22 locales.

createMockHost now exposes run(def) — the other half of a plugin unit test. Where the
ctx recorders capture what a plugin read, run() fires its own entry points (route, job,
scheduled, event, plugin-event, deleteUserData, exportUserData, provider hooks) against
the same mock ctx, and host.scheduled surfaces the timers it armed. A handler the plugin
didn't declare throws a clear error instead of a silent no-op.

* feat(plugins): include plugin data + code in backups, applied on restart

A TREK backup archived travel.db + uploads + the encryption key, but each plugin's
own SQLite file — the ONLY copy of the user data it holds — and its installed code
lived in separate trees that were never captured, so a restore left the plugins rows
with no data or code behind them.

createBackup now adds plugins-data/ (each plugin's db + WAL sidecars, so SQLite
recovers a consistent snapshot) and plugins-code/ (skipping dev-links by realpath, so
an author's linked source is never bundled). Restore can't swap those live — the
runtime holds each plugin db open — so it STAGES the extracted trees beside the live
ones and the runtime swaps them in at the next boot, before it opens anything. Same
"applies on restart" model the bundled encryption key already uses: no plugin quiesce,
no swap under open handles, no new admin setup. Older archives without the trees restore
exactly as before.

* fix(plugins): audit — runtime robustness, security & data-lifecycle fixes

Fixes from an adversarial audit of the plugin system, host/runtime side:

Robustness:
- getPluginDataDb recreated a handle a terminal-failure dispose had closed but
  left cached, so a re-enabled plugin's db:own threw on every call — recreate
  when the cached handle is shut.
- ctx.ws.broadcast* now carry _inv, so the host can bind the acting user (the
  capability was silently refused, i.e. dead, without it).
- ctx.events.emit swallows a rejected emit instead of crashing the child into a
  terminal 'error'; an uncaught throw AFTER activation is treated as a crash
  (restart with backoff), not a load failure.
- A crash-respawned child gets the same activation deadline as a first activation
  and the buffered-event queue is cleared on the timeout path, so a hung onLoad
  after a crash can't peg a core and orphan events forever.
- Expired buffered events are pruned by the reaper, not only at flush; the
  scheduler + erasure sweeps scope their LIMIT window to ACTIVE plugins so a
  backlog for inactive plugins can't starve deliverable work.

Security / integrity:
- Unix-domain-socket / named-pipe connects are refused by default in the egress
  guard (a host-local pivot to docker.sock / DB sockets), under the same policy
  as private IPs.
- db.tx refuses transaction-control statements (a raw COMMIT would break its
  atomicity) and caps rows across the WHOLE batch, not per statement.
- plugin_capability_audit is retention-capped per plugin (chain-safe: retained
  rows stay self-verifying), so it can't grow unbounded in the shared db.
- A cap of 0 in TREK_PLUGIN_AI_PER_DAY / _NOTIFY_PER_DAY now disables the broker
  instead of falling back to the default.

GDPR data lifecycle:
- Account deletion now erases host-side per-user plugin tables (config, OAuth
  tokens/state) and enqueues the own-db erasure from the CORE path, so it works
  even when the runtime is disabled or pre-boot; guest deletion does the same.
- uninstall keeps a pending erasure when data is retained (deleteData=false);
  erasure delivery is no longer grant-re-checked (a queued erasure is a duty);
  export flags installed-but-inactive plugins as pending instead of omitting them.

Backup/restore:
- Plugin DBs are WAL-checkpointed before archiving (no torn/stale snapshots).
- Restore applies the staged trees immediately by quiescing the plugins (no
  unbounded gap where a later unrelated restart would revert diverged data);
  the swap is content-level (safe on a volume-mounted root) and preserves
  dev-links; the decompressed-size cap is operator-raisable.

* fix(plugins): audit — hook-output hardening, dashboard slot & mock-host parity

- Map-marker and atlas-layer tones were validated on String(tone) but emitted
  raw, so a non-string tone (an object with a matching toString) slipped through
  and crashed the client that renders it — check the raw value against the enum.
- View-contribution column/action caps are now PER ENTITY, not per view, so a
  plugin's columns no longer vanish from every table row past the first 20; the
  dashboard trip-card badge cap is per card (≥ one on every visible card).
- A reservation-detail widget no longer also renders as a context-free dashboard
  sidebar card (the inline filter was missing that slot).
- mock-host matches the real host: it ignores asUserId on trip reads (bind the
  acting user), throws on a wrong user-scope notify target instead of coercing,
  enforces the scheduler caps, and detects RETURNING as a read in db.tx — so a
  passing author test can't hide a production RESOURCE_FORBIDDEN.

* feat(plugins): full ctx parity in the dev server + fire jobs/events/hooks locally

The trek-plugin dev server injected only ~6 of the ~35 ctx areas, so any plugin
touching ctx.costs/packing/files/notify/ai/settings/scheduler/meta/oauth/db.tx/…
hit a TypeError in local dev while the same code passed mock-host tests and worked
installed. It also could only exercise routes.

Delegate every non-db-own capability to a grant-enforcing mock host (the same one
unit tests use) while keeping the real node:sqlite for db:own and dev-native ws
capture + logging — so the whole surface works in dev with the exact production
permission rules. dev-fixtures.json now takes the createMockHost options shape, so
you can seed the full surface. New GET /__dev/fire/<kind>[/<name>][/<fn>] fires a
job, scheduled timer, event subscription, GDPR handler or provider hook against the
dev ctx, closing the "can't test non-routes locally" gap.

* feat(plugins): wire the photoProvider + calendarSource hooks to real core consumers

Both hooks were declared, typed and documented but NO core code ever invoked them,
so an author could build, mock-test and install a photo or calendar plugin that
silently did nothing. Give each a real consumer that fans out to it, exactly like
the other eight provider hooks:

- GET /api/plugin-photos/search (+ /sources, /item) aggregates photoProvider results
  for the picker — {id, title?, thumbnailUrl, fullUrl, takenAt?}, thumbnail/full URLs
  http/https-only (they become <img src>), per-source count capped, failing source
  skipped.
- GET /api/plugin-calendar?start=&end= aggregates calendarSource events for the
  signed-in user — {id, title, start, end, allDay} ISO, count capped, failing source
  skipped, sensible default window.

Both run with the acting user bound. The SDK interfaces now pass ctx as the last arg
(so a source can reach ctx.settings/oauth/http for its backend), and the wiki marks
them live instead of "reserved — no core consumer".

* feat(plugins): close the create-heavy API asymmetries importers/sync hit

Core services implemented these but plugins had no path to them, so the flagship
importer/sync integrations hit real walls. Added, each reusing the EXISTING grant
(no new consent):

- ctx.trips.removeMember(tripId, userId) — reconcile DEPARTURES, not just additions
  (db:write:members + member_manage). Never removes the owner (that would orphan the
  trip); ownership transfer stays a separate deliberate action.
- ctx.journal.createJourney({title, subtitle?, trip_ids?}) / deleteJourney(journeyId)
  — an importer can now bootstrap the journal it fills with entries and clean it up
  (db:write:journal), instead of only appending to journals a human created first.

Wired end-to-end (envelope → rpc-host → create-rpc-host reusing tripService/
journeyService → both SDK copies → mock-host) and documented. (trips.delete needs its
own destructive permission + consent copy and collab edit/delete + collections.delete
remain — tracked as small follow-ups.)

* feat(plugins): strip emojis from plugin-rendered text so it matches TREK's lucide UI

Plugin authors (especially AI-generated ones) sprinkle emojis into the declarative
text TREK renders in its OWN chrome — hook contributions (badges, columns, warnings,
PDF sections, map-marker/atlas labels, journal rows, place details, trip-card badges,
calendar + photo titles) and notifications — which clashes with TREK's lucide-only icon
language.

A shared stripEmoji() removes emojis (incl. flag/ZWJ/variation-selector sequences) and
tidies the leftover whitespace, applied at the render boundary in every hook-contribution
normalizer and in notify.send — so no matter what a plugin returns, the text TREK draws
stays emoji-free. It does NOT touch a plugin's own sandboxed /ui frame (the author's to
design), and it leaves photo ids verbatim (they round-trip to getById). The validate CLI
warns when a manifest name/description contains emojis, nudging authors to the declarative
`icon` field (a lucide name) instead.

* fix(plugins): harden the restore-apply path — regressions from the backup/dev fix pass

A final audit of the fix pass caught three regressions clustered in the two newest
surfaces; the restore path could both crash the server and destroy data.

- CRITICAL: a restore quiesces plugins via supervisor.shutdownAll() AFTER closeDb(), but
  shutdownAll killed children without first marking them stopped, so each child 'exit'
  took the CRASH path and wrote crash-accounting rows into the now-closed core DB — the
  throw escaped an EventEmitter listener as an uncaughtException and killed the whole
  process mid-restore. shutdownAll now marks every entry stopped and drops it from
  `running` BEFORE the kills (so onExit early-returns), and the onStatus/onLog DB hooks
  are wrapped in try/catch (also covers the stderr→onLog path). This also stops a normal
  shutdown from logging phantom "crashed" rows.
- HIGH: swapContents cleared live entries then MOVED staged ones in, so a crash mid-move
  permanently deleted a plugin's only data copy (staging was already emptied, so a retry
  couldn't restore it). It now COPIES each staged entry over the live one and only deletes
  staging at the very end — `staged` stays the complete source of truth, making the whole
  operation crash-idempotent.
- HIGH: the dev server lost the actingUserId=1 default in the mock-host refactor, so a
  fresh scaffold refused every user-bound capability. Restored.

* fix(plugins): final-audit medium/low findings

- GDPR export flags an active plugin whose export errored/timed out as `pending`
  instead of silently omitting it (collectUserExport now returns a discriminated
  result), so a data-access export never reads complete while missing data.
- Account deletion also enqueues an erasure for plugins UNINSTALLED with retained
  data (an orphan data dir) — a same-id reinstall now honours the deletion instead
  of re-adopting the user's data forever.
- oauth.getToken returns null in a userless context (matching the SDK/mock contract)
  instead of throwing RESOURCE_FORBIDDEN a background caller can't handle.
- Crash-backoff restart is identity-guarded (+ the timer is tracked and cleared like
  the activation timer), so a disable + re-enable during the backoff window can no
  longer respawn a ghost child from the replaced entry.
- db.tx transaction-control guard strips leading comments first, so `/* */COMMIT`
  can't slip past the start-anchored check and break batch atomicity.
- createJournal inherits its cover only from a trip that was actually LINKED
  (access-checked), closing a cross-tenant cover-image read on plugin + REST paths.
- trip-warnings drops a null array element instead of losing ALL of that provider's
  warnings; plugin-activity floors a non-integer ?limit so it can't 500.
- The trek-plugin dev server binds loopback only and refuses cross-site requests to
  its side-effectful /__dev/fire endpoints (it serves real routes + no-auth dev
  actions).

* fix(plugins): clear no-misleading-character-class in the emoji stripper

The character class listed the ZWJ, variation selectors and combining keycap
marks as members, which eslint reads as an accidental combined grapheme and
rejected on CI. Pull the emoji glyphs out into Extended_Pictographic /
Regional_Indicator alternatives so only the joiner/selector code points stay in
the class, with a scoped disable where the rule still can't tell them apart.
While here, reset lastIndex before the /g regex is reused in hasEmoji() so a
second call can't resume mid-string and miss a leading emoji.

* fix(security): trip-scope note-file deletion and guard the LLM base URL

Two reported issues:

- deleteNoteFile only matched on the note id and file id, so a member of trip A
  could delete a file attached to a note in trip B by guessing its id. Thread the
  trip id through the service and controller and scope the delete to it, the way
  every other collab operation already does.

- The LLM extraction clients fetched the user-configured base URL directly, so a
  user could point it at the cloud-metadata endpoint (169.254.169.254) and read
  the echoed error body. Route both clients through a new safeFetchLlm() that
  blocks the link-local/metadata range while still allowing a local or LAN Ollama
  (loopback and private ranges stay reachable), pinned to the resolved IP so a
  hostname can't rebind to the metadata address after the check.

* fix(security): route every LLM client through the SSRF guard

The base-URL SSRF fix covered the openai-compatible and anthropic clients but
missed the native Ollama /api/chat client and the /api/tags + /api/pull model-
management calls, whic…

* fix(plugins): repair plain-HTTP egress and forward the private-egress opt-out

Two pre-existing bugs in the plugin egress guard, found by running a plugin
against a real service end to end.

1. Every plain-HTTP request a plugin made was refused, whatever host it had
   declared. Node pre-normalises `net.connect()` args into an [options, cb]
   array and passes THAT array as the single argument; undici's plain-HTTP
   connector takes this path, its TLS connector does not. classifyConnect read
   `host` off the array, got undefined, and fell back to 'localhost' — so a
   fetch to a declared, public host was rejected with the nonsense message
   "localhost is not in the plugin's declared hosts". It failed closed, so it
   was never a security hole, and it went unnoticed because the only shipped
   egress plugin uses HTTPS. unwrapConnectArgs() unwraps the normalised form
   before anything reads host/path.

2. TREK_PLUGIN_ALLOW_PRIVATE_EGRESS could never have any effect. The guard that
   reads it runs INSIDE the child, whose env is scrubbed to a four-entry
   whitelist that never included it — so a documented setting (wiki/
   Environment-Variables.md) was wired to nothing, and no plugin could reach a
   self-hoster's LAN service no matter what the operator set. Forwarded only
   when set, so the default stays the secure block-private policy.

Regression tests cover the normalised form in both directions: the real host is
now resolved, and an undeclared host, a private IP and a unix socket are all
still refused when passed that way.

* feat(notifications): let a plugin register a notification channel

TREK's four channels (in-app, email, webhook, ntfy) were a closed set:
notificationService.send() dispatched with four copy-pasted `if` blocks and no
provider abstraction, so a fifth channel meant editing eight files by hand. A
plugin could produce a notification via ctx.notify.send(), but never deliver
one.

A plugin now registers a channel with `hooks.notificationChannel` +
`hook:notification-channel` on a plain `type: 'integration'` — not a new manifest
type, so the TREK-Plugins registry schema and both its CI gates are untouched.

Core refactor
- New channel registry (services/notifications/): email/webhook/ntfy become
  ExternalChannel providers wrapping the EXISTING send functions — no delivery
  logic is rewritten, only relocated. In-app deliberately stays out: it writes
  typed rows with scope/target/callbacks, not a rendered title+body, the same
  line shared/ already draws with i18n/externalNotifications.
- The event text is now rendered once per recipient instead of once per channel.
- The channel set is open: NotifChannel becomes a string, the matrix is
  registry-derived, and the UI columns are server-driven. The DB column was
  already bare TEXT and the Zod contract already a string record — only the
  TypeScript and the two UIs were ever closed.

The hook runs USERLESS. Every other hook is user-initiated, so actingUserId falls
out of the request; a notification is host-initiated for an ARBITRARY recipient,
so ctx.settings.get() would return undefined. The host resolves the recipient's
decrypted scope:'user' settings itself and passes them as an argument. That is
what lets a channel plugin be handed someone's push token WITHOUT being handed
the right to read their trips as them.

Enabling the plugin is the opt-in: a plugin channel is not gated on the admin's
`notification_channels` list. A built-in always exists in code and needs an
explicit switch; a plugin channel only exists because an admin enabled that
plugin. (Nothing could write a `plugin:` id into that CSV anyway, and the admin
toggle rebuilt it from three booleans, silently dropping anything else — so
requiring a second opt-in meant the channel could never be turned on at all.)

Also fixed, found while building this:
- Plugin settings keys were unvalidated, so a field named `__proto__` or
  `constructor` resolved off Object.prototype: a REQUIRED field with such a name
  reported as configured for every user who had configured nothing — enough, for
  a channel, to be dispatched to everyone with no credentials. Keys are now
  constrained at install and the config blob is parsed null-prototype, so it is
  impossible even for an already-installed plugin.
- A `select` field's options were cast straight through, so the obvious
  `["1","5"]` form rendered every dropdown entry BLANK (the client reads
  value/label). Now coerced, and malformed options are rejected.

Also adds: operator-supplied egress hosts (a plugin talking to a self-hosted
service can't name the operator's host at publish time, so an admin adds it
post-install and the runtime re-spawns the child with the widened allow-list —
only for a plugin that DECLARED operatorEgress, and only an admin, never a user);
settings-page actions (a "Test connection" button, user-initiated so
ctx.settings.get() returns the clicking user's own value); and a Gotify-shaped
notification-channel template in the SDK.

Verified end to end against a real Gotify container, not just in tests.

* docs(wiki): document the plugin notification-channel surface

Covers the pieces added in the previous commits, in the pages a reader would
actually reach for:

- Plugins.md (the admin-facing page) had none of it: notification channels,
  settings actions, and a full "Allowed hosts" section — including what
  operator-supplied egress deliberately does NOT let anyone do.
- Plugin-Development.md: the notificationChannel hook (and why it is the one hook
  with no acting user), settings-page actions, operatorEgress, and the manifest
  reference rows.
- Plugin-Cookbook.md: a "become a notification channel" recipe and a
  "Test connection button" recipe.
- Plugin-Permissions.md: hook:notification-channel, operatorEgress under the
  outbound section, and settings actions under "not a permission".
- Notifications.md: plugin channels alongside the four built-ins.

* fix(sdk): allow empty egress if and only if operatorEgress is true

* ci: don't run repo-specific workflows on forks

Guard release, publish, wiki-deploy and issue/PR-triage workflows with a
`github.repository` check so they no-op in forks instead of failing or
acting on the fork's own issues, PRs, tags and registries.

Also skip the Docker Scout scan for pull requests from forks: Docker Hub
secrets are never exposed there, so the login step could not succeed.

Tests and lint stay ungated — they need no secrets and are the gate for
incoming fork PRs.

* feat(sdk): add missing methods in mock-host

* fix(airports): rebuild the json file

* fix(airports.json): add small airports too

* fix(public transit): only show public transit option when a trip has actual dates

* fix(plugins): reap a queued erasure only once the plugin's data is gone

The orphan reap deleted every queue row whose plugin had left the registry, but
uninstall(deleteData=false) removes the plugins row while deliberately keeping the
data dir AND the queued erasure so a same-id reinstall can still honour it. The reap
now deletes a row only when the plugin's data dir is actually gone; a deleteData=true
uninstall already clears the rows itself.

* fix(backup): snapshot the core DB and swap restores atomically

createBackup archived travel.db via the archiver's lazy live-file read, so a WAL
auto-checkpoint firing mid-stream could write a torn database into the zip. It now
VACUUM INTOs a point-in-time snapshot and archives that, the same guarantee plugin
DBs already get. restoreFromZip swapped the DB by unlink-then-copy, which on an
interrupted restore could leave no valid travel.db; it now copies to a temp file and
renames it into place (atomic), dropping the stale -wal/-shm sidecars first.

* fix(deploy): Recreate strategy for the SQLite volume, pin the root compose image

The Helm Deployment had no strategy, so the default RollingUpdate would start a second
pod holding the same ReadWriteOnce PVC before the old one exits — a Multi-Attach
deadlock or two writers on one SQLite file. Default to Recreate (overridable for
ReadWriteMany). The root docker-compose.yml pinned trek:dev, a tag no workflow builds,
so a clone-and-up at the release tag ran a stale image; pin it to :latest like the README.

* fix(security): re-validate LLM endpoint fetch redirects per hop (GHSA-fmq9)

safeFetchLlm left undici's default redirect:'follow', so a configured LLM
endpoint could 302 to http://169.254.169.254/ and reach cloud-metadata
credentials — the DNS pin does not cover an IP-literal redirect hop, since
net.connect skips the pinned lookup for a literal IP. Follow redirects
manually now, re-resolving/re-checking/re-pinning each hop (allowing LAN/
localhost as before). Also block the Alibaba metadata IPs directly.

* fix(plugins): throttle the plugin log channel to prevent host-thread DoS

The per-plugin RpcRateLimiter only guarded the ctx.* (req) channel; ctx.log.*,
stdout/stderr and unknown evt topics reached a synchronous INSERT+prune on the
host thread unthrottled, so a while(true) ctx.log.error(...) loop could freeze
the instance. Route every plugin-driven log path through a per-plugin log token
bucket; excess lines are dropped with a summary line on resume.

---------

Co-authored-by: jubnl <jgunther021@gmail.com>
Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: sld272 <zjrdmczh@outlook.com>
Co-authored-by: Nguyen Trong Binh <nguytb15@VN1N07HO1CD1015.local>
This commit is contained in:
Maurice
2026-07-11 22:28:32 +02:00
committed by GitHub
parent 3db2495bcd
commit 19064b3917
539 changed files with 25810 additions and 2292 deletions
+47
View File
@@ -223,6 +223,53 @@ export const CONTINENT_MAP: Record<string, string> = {
LI: 'Europe',
GI: 'Europe',
PR: 'North America',
// Countries present in the bundled admin0 borders but long absent from this table,
// so they bucketed into 'Other' once resolved. Spain is the notable one — it always
// had a bounding box, just no continent (#1490).
ES: 'Europe',
BY: 'Europe',
GL: 'North America',
AG: 'North America',
BB: 'North America',
BS: 'North America',
BZ: 'North America',
DM: 'North America',
GD: 'North America',
KN: 'North America',
LC: 'North America',
VC: 'North America',
GY: 'South America',
SR: 'South America',
BT: 'Asia',
KP: 'Asia',
TL: 'Asia',
CF: 'Africa',
EH: 'Africa',
GA: 'Africa',
GM: 'Africa',
GQ: 'Africa',
GW: 'Africa',
KM: 'Africa',
LR: 'Africa',
LS: 'Africa',
SL: 'Africa',
SS: 'Africa',
ST: 'Africa',
SZ: 'Africa',
TD: 'Africa',
FJ: 'Oceania',
FM: 'Oceania',
KI: 'Oceania',
MH: 'Oceania',
NR: 'Oceania',
PW: 'Oceania',
SB: 'Oceania',
TO: 'Oceania',
TV: 'Oceania',
VU: 'Oceania',
WS: 'Oceania',
AQ: 'Antarctica',
};
/** Continent for an ISO alpha-2 country code; 'Other' when unknown. */
+81 -1
View File
@@ -186,6 +186,12 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'أفلت ملف .zip لتثبيت الإضافة',
'admin.plugins.uploaded': 'تم رفع الإضافة “{name}” — فعّلها لتشغيلها',
'admin.plugins.sideloaded': 'محمّلة يدويًا',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint': 'يُحمّل من دليل بناء محلي ويُعاد تحميله فوريًا ببيانات حقيقية — للتطوير فقط',
'admin.plugins.devLinkTitle': 'ربط إضافة محلية',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'ربط',
'admin.plugins.devLinkLinked': 'تم ربط {id} — فعّلها للتشغيل',
'admin.plugins.sideloadedHint': 'تم رفعها يدويًا — ليست من السجل، غير موقّعة وغير مراجَعة',
'admin.plugins.browse': 'تصفح الإضافات',
'admin.plugins.installed': 'مثبّتة',
@@ -198,6 +204,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'تم تعطيل الإضافة',
'admin.plugins.viewErrors': 'عرض سجل الأخطاء',
'admin.plugins.errorLog': 'سجل الأخطاء',
'admin.plugins.allowedHosts': 'المضيفون المسموح بهم',
'admin.plugins.allowedHosts.hint': 'تتصل هذه الإضافة بخدمة لا يمكن لأحد سواك تسميتها (خادم مستضاف ذاتيًا). أضف المضيفين الذين يمكنها الوصول إليهم — ولن تصل إلى غيرهم.',
'admin.plugins.allowedHosts.none': 'لم تتم إضافة أي مضيف بعد.',
'admin.plugins.allowedHosts.unsupported': 'لا تستخدم هذه الإضافة مضيفين يحددهم المشغّل. مضيفوها المسموح بهم ثابتون في ملف التعريف.',
'admin.plugins.allowedHosts.restartNote': 'الحفظ يعيد تشغيل الإضافة لتأخذ القائمة الجديدة.',
'admin.plugins.allowedHosts.add': 'إضافة مضيف مسموح به',
'admin.plugins.allowedHosts.count': '{n} مضيف مسموح به',
'admin.plugins.operatorEgressPill': '+ مضيفون تضيفهم',
'admin.plugins.operatorEgressHint': 'تتصل هذه الإضافة بخدمة لا يمكن لأحد سواك تسميتها (خادم مستضاف ذاتيًا). بعد التثبيت أضف المضيفين من ⋯ ← المضيفون المسموح بهم. ولن تصل إلى غيرهم.',
'admin.plugins.noErrors': 'لا توجد أخطاء مسجّلة.',
'admin.plugins.uninstalled': 'تمت إزالة الإضافة',
'admin.plugins.uninstallTitle': 'إزالة الإضافة؟',
@@ -239,6 +254,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'لكل مستخدم',
'admin.plugins.fieldRequired': 'مطلوب',
'admin.plugins.sourceRepo': 'مستودع المصدر',
'admin.plugins.reportIssue': 'الإبلاغ عن مشكلة',
'admin.plugins.homepage': 'الصفحة الرئيسية',
'admin.plugins.requiresTrek': 'يتطلب TREK {version}+',
'admin.plugins.reviewedOn': 'تمت المراجعة {date}',
@@ -248,7 +264,49 @@ const admin: TranslationStrings = {
'قراءة معلومات الملف الشخصي الأساسية (الاسم والصورة الرمزية — وليس بيانات الاعتماد أبدًا)',
'admin.plugins.perm.db:read:costs': 'قراءة المصروفات (بنود الميزانية) التي يمكن للمستخدم الحالي الوصول إليها',
'admin.plugins.perm.db:read:packing': 'قراءة قوائم الأمتعة للرحلات التي يمكن للمستخدم الحالي الوصول إليها',
'admin.plugins.perm.db:write:packing':
'إنشاء عناصر قائمة الأمتعة وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
'admin.plugins.perm.weather:read': 'قراءة توقعات الطقس المخزنة مؤقتًا للمضيف (حسب الإحداثيات)',
'admin.plugins.perm.db:read:categories': 'قراءة قائمة فئات الأماكن العامة',
'admin.plugins.perm.db:read:tags': 'قراءة وسوم المستخدم الحالي',
'admin.plugins.perm.db:write:tags': 'إنشاء وسوم المستخدم الحالي وتعديلها وحذفها',
'admin.plugins.perm.db:read:todos': 'قراءة مهام الرحلات التي يمكن للمستخدم الحالي الوصول إليها',
'admin.plugins.perm.db:write:todos': 'إنشاء مهام وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
'admin.plugins.perm.db:write:atlas':
'وضع علامة على البلدان والمناطق كمُزارة وإدارة قائمة أمنيات المستخدم الحالي (يتطلب إضافة Atlas)',
'admin.plugins.perm.db:write:vacay':
'تبديل أيام الإجازة وعطلات الشركة في الخطة النشطة للمستخدم الحالي (يتطلب إضافة Vacay)',
'admin.plugins.perm.db:write:journal':
'إنشاء إدخالات اليوميات وتعديلها وحذفها في اليوميات التي يمكن للمستخدم الحالي تعديلها (يتطلب إضافة Journey)',
'admin.plugins.perm.db:write:collections':
'إنشاء المجموعات وتعديلها وحفظ الأماكن فيها، بدور المجموعة الخاص بالمستخدم الحالي (يتطلب إضافة Collections)',
'admin.plugins.perm.db:write:files':
'إرفاق ملفات بالرحلات التي يمكن للمستخدم الحالي تعديلها (حد 10 ميغابايت، الامتدادات المحظورة مرفوضة) وإدارة روابطها',
'admin.plugins.perm.db:write:collab':
'نشر ملاحظات واستطلاعات ورسائل دردشة في الرحلات التي يمكن للمستخدم الحالي تعديلها (يتطلب إضافة Collab)',
'admin.plugins.perm.db:write:members':
'إضافة مستخدمين إلى الرحلات — يمنح الوصول إلى الرحلة؛ محمي بحق إدارة الأعضاء الخاص بالمستخدم الحالي',
'admin.plugins.perm.notify:send':
'إرسال إشعار (صندوق الجرس + بريد إلكتروني/ntfy) إلى المستخدم الحالي أو رحلة ينتمي إليها — لا إلى مستلم عشوائي أبدًا',
'admin.plugins.perm.ai:invoke':
'تشغيل مزود الذكاء الاصطناعي الذي أعده المسؤول (إكمال النص + استخراج المستندات) نيابة عن المستخدم الحالي — لا تحتفظ الإضافة بمفتاح أبدًا',
'admin.plugins.perm.oauth:client':
'الاتصال بخدمة خارجية نيابة عنك عبر OAuth يديره المضيف (يحتفظ المضيف بالرموز، ولا تراها الإضافة أبدًا)',
'admin.plugins.perm.db:read:files': 'قراءة ملفات الرحلات التي يمكن للمستخدم الحالي الوصول إليها',
'admin.plugins.perm.db:write:reservations':
'إنشاء الحجوزات وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
'admin.plugins.perm.db:write:accommodations':
'إنشاء أماكن الإقامة وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
'admin.plugins.perm.db:read:journal': 'قراءة يوميات السفر الخاصة بالمستخدم الحالي (يتطلب إضافة Journey)',
'admin.plugins.perm.db:read:atlas': 'قراءة البلدان والمناطق التي زارها المستخدم الحالي (يتطلب إضافة Atlas)',
'admin.plugins.perm.db:read:vacay': 'قراءة خطة الإجازة الخاصة بالمستخدم الحالي (يتطلب إضافة Vacay)',
'admin.plugins.perm.db:read:daynotes': 'قراءة ملاحظات اليوم للرحلات التي يمكن للمستخدم الحالي الوصول إليها',
'admin.plugins.perm.db:read:collections':
'قراءة مجموعات الأماكن المحفوظة الخاصة بالمستخدم الحالي (يتطلب إضافة Collections)',
'admin.plugins.perm.db:write:daynotes':
'إنشاء ملاحظات اليوم وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
'admin.plugins.perm.jobs:run':
'تشغيل مهام الخلفية المُعلنة وفق جدول زمني (بدون سياق مستخدم — لا يمكنه قراءة بيانات المستخدم)',
'admin.plugins.perm.db:write:costs': 'إنشاء مصروفات (بنود الميزانية) على الرحلات التي يمكن للمستخدم الحالي تعديلها',
'admin.plugins.perm.db:write:places': 'إضافة الأماكن وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
'admin.plugins.perm.db:write:days': 'إضافة الأيام وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
@@ -262,8 +320,24 @@ const admin: TranslationStrings = {
'admin.plugins.perm.hook:calendar-source': 'توفير الأحداث للتقويم',
'admin.plugins.perm.hook:place-detail-provider': 'المساهمة بتفاصيل إضافية (مراجعات، تقييمات، روابط) لمكان ما',
'admin.plugins.perm.hook:trip-warning-provider': 'إظهار تحذيرات التحقق على الرحلة (تظهر في المخطط)',
'admin.plugins.perm.hook:table-contributor': 'إضافة أعمدة وإجراءات إلى عروض الرحلة (الحجوزات، الأماكن، الأيام)',
'admin.plugins.perm.hook:map-marker-provider': 'إضافة علامات إلى خريطة الرحلة (مثل عرض الحجوزات أو نقاط الاهتمام)',
'admin.plugins.perm.hook:pdf-section-provider': 'إضافة أقسام نصية إلى تصدير PDF للرحلة',
'admin.plugins.perm.hook:atlas-layer-provider':
'إبراز دول على خريطة أطلس العالمية (مثل قوائم الأمنيات أو تحذيرات السفر)',
'admin.plugins.perm.hook:journal-entry-provider': 'إضافة صفوف إضافية (روابط، إحصاءات) إلى مدخلات اليوميات',
'admin.plugins.perm.hook:user-data':
'محو أو تصدير البيانات التي يخزّنها عن مستخدم (اللائحة العامة لحماية البيانات: حذف الحساب وطلبات البيانات)',
'admin.plugins.perm.hook:trip-card-provider': 'إضافة شارات صغيرة (الحالة، الأعداد) إلى بطاقات الرحلات في لوحة التحكم',
'admin.plugins.perm.hook:notification-channel': 'تسليم إشعاراتك عبر قناة إضافية',
'admin.plugins.perm.events:subscribe': 'التفاعل مع أحداث النشاط الأساسية (اسم الحدث والرحلة فقط، دون المحتوى مطلقًا)',
'admin.plugins.perm.http:outbound': 'إجراء طلبات صادرة إلى المضيفات المُعلنة الخاصة بها',
'admin.plugins.perm.db:read:collab':
'قراءة الملاحظات والاستطلاعات ورسائل الدردشة في الرحلات التي يمكن للمستخدم الحالي الوصول إليها (يتطلب إضافة Collab)',
'admin.plugins.perm.db:read:files:content':
'قراءة محتوى (بايتات) ملفات الرحلات التي يمكن للمستخدم الحالي الوصول إليها',
'admin.plugins.perm.db:create:trips': 'إنشاء رحلات جديدة يملكها المستخدم الحالي',
'admin.plugins.perm.rates:read': 'قراءة أسعار صرف العملات المخزنة مؤقتًا للمضيف',
'admin.plugins.updateConsentTitle': 'يتطلب هذا التحديث أذونات جديدة',
'admin.plugins.updateConsentBody':
'{name} v{version} يطلب أذونات لم تمنحها بعد. تم تثبيت الإصدار الجديد لكنه يبقى متوقفًا حتى توافق عليه.',
@@ -306,6 +380,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'الاسم',
'admin.plugins.sortRecent': 'المحدّثة حديثًا',
'admin.plugins.sortUpdates': 'التحديثات أولًا',
'admin.plugins.sortDownloads': 'الأكثر تنزيلًا',
'admin.plugins.updatesAvailable': '{count} تحديثات متاحة لإضافاتك.',
'admin.plugins.updateAll': 'تحديث الكل',
'admin.plugins.noMatchInstalled': 'لا توجد إضافات مثبّتة تطابق بحثك.',
@@ -326,7 +401,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'أداة لوحة التحكم',
'admin.plugins.cap.heroWidget': 'أداة بطاقة الصعود',
'admin.plugins.cap.placeSlot': 'تفاصيل المكان',
'admin.plugins.cap.daySlot': 'تفاصيل اليوم',
'admin.plugins.cap.reservationSlot': 'تفاصيل الحجز',
'admin.plugins.cap.replacesTabs': 'يستبدل تبويبات المخطط',
'admin.plugins.cap.realtime': 'تحديثات فورية',
'admin.plugins.cap.notificationChannel': 'قناة الإشعارات',
'admin.plugins.cap.photos': 'يوفّر الصور',
'admin.plugins.cap.calendar': 'يوفّر أحداث التقويم',
'admin.plugins.cap.placeDetails': 'يُثري الأماكن',
@@ -352,11 +431,12 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'الحجم',
'admin.plugins.metaRequires': 'يتطلب',
'admin.plugins.metaReviewed': 'تمت المراجعة',
'admin.plugins.downloads': 'التنزيلات',
'admin.addons.title': 'الإضافات',
'admin.addons.subtitle': 'فعّل أو عطّل الميزات لتخصيص تجربة TREK.',
'admin.addons.catalog.packing.name': 'القوائم',
'admin.addons.catalog.packing.description': 'قوائم التعبئة والمهام لرحلاتك',
'admin.addons.catalog.budget.name': 'الميزانية',
'admin.addons.catalog.budget.name': 'التكاليف',
'admin.addons.catalog.budget.description': 'تتبع النفقات وخطط ميزانية الرحلة',
'admin.addons.catalog.documents.name': 'المستندات',
'admin.addons.catalog.documents.description': 'حفظ وإدارة وثائق السفر',
@@ -52,6 +52,7 @@ const ar: NotificationLocale = {
title: 'تمت إعادة تعيين جلسة Synology',
body: 'تغيّر حسابك أو رابط Synology. تم تسجيل خروجك من Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'إعادة تعيين كلمة المرور',
+1 -1
View File
@@ -8,7 +8,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places ومسارات وتجميع',
'login.features.realtime': 'مزامنة فورية',
'login.features.realtimeDesc': 'خططوا معًا عبر WebSocket',
'login.features.budget': 'تتبع الميزانية',
'login.features.budget': 'تتبع التكاليف',
'login.features.budgetDesc': 'فئات ورسوم وتقسيم لكل شخص',
'login.features.collab': 'تعاون',
'login.features.collabDesc': 'عدة مستخدمين مع رحلات مشتركة',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'عرض',
'notif.action.accept': 'قبول',
'notif.action.decline': 'رفض',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'إشعار',
'notif.generic.text': 'لديك إشعار جديد',
'notif.dev.unknown_event.title': '[DEV] حدث غير معروف',
+6 -6
View File
@@ -21,7 +21,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'تمت إضافة جميع الاقتراحات',
'packing.allPacked': 'تم تجهيز الكل!',
'packing.addPlaceholder': 'إضافة عنصر جديد...',
'packing.categoryPlaceholder': 'الفئة...',
'packing.categoryPlaceholder': 'قائمة...',
'packing.filterAll': 'الكل',
'packing.filterOpen': 'مفتوح',
'packing.filterDone': 'تم',
@@ -31,12 +31,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'إعادة تسمية',
'packing.menuCheckAll': 'تحديد الكل',
'packing.menuUncheckAll': 'إلغاء تحديد الكل',
'packing.menuDeleteCat': 'حذف الفئة',
'packing.menuDeleteCat': 'حذف القائمة',
'packing.noMembers': 'لا أعضاء',
'packing.addItem': 'إضافة عنصر',
'packing.addItemPlaceholder': 'اسم العنصر...',
'packing.addCategory': 'إضافة فئة',
'packing.newCategoryPlaceholder': 'اسم الفئة (مثال: ملابس)',
'packing.addCategory': 'إضافة قائمة',
'packing.newCategoryPlaceholder': 'اسم القائمة (مثال: الملابس)',
'packing.applyTemplate': 'تطبيق قالب',
'packing.template': 'قالب',
'packing.templateApplied': 'تمت إضافة {count} عنصر من القالب',
@@ -49,9 +49,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'الوزن الإجمالي',
'packing.bagName': 'الاسم...',
'packing.addBag': 'إضافة أمتعة',
'packing.changeCategory': 'تغيير الفئة',
'packing.changeCategory': 'نقل إلى قائمة',
'packing.confirm.clearChecked': 'هل تريد إزالة {count} عنصر محدد؟',
'packing.confirm.deleteCat': 'هل تريد حذف الفئة "{name}" مع {count} عنصر؟',
'packing.confirm.deleteCat': 'هل أنت متأكد أنك تريد حذف القائمة "{name}" التي تحتوي على {count} عنصرًا؟',
'packing.defaultCategory': 'أخرى',
'packing.toast.saveError': 'فشل الحفظ',
'packing.toast.deleteError': 'فشل الحذف',
+26
View File
@@ -7,6 +7,15 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'الخريطة',
'settings.tabs.notifications': 'الإشعارات',
'settings.tabs.integrations': 'التكاملات',
'settings.plugins.oauth.connected': 'متصل',
'settings.plugins.oauth.notConnected': 'غير متصل',
'settings.plugins.oauth.connect': 'اتصال',
'settings.plugins.oauth.disconnect': 'قطع الاتصال',
'settings.tabs.plugins': 'الإضافات',
'settings.plugins.title': 'إعدادات الإضافات',
'settings.plugins.subtitle': 'إعداداتك الشخصية للإضافات التي تستخدمها (مفاتيح API، التفضيلات).',
'settings.plugins.empty': 'لا توجد إضافات نشطة.',
'settings.plugins.saved': 'تم حفظ الإعدادات',
'settings.tabs.account': 'الحساب',
'settings.tabs.about': 'حول',
'settings.map': 'الخريطة',
@@ -261,6 +270,14 @@ const settings: TranslationStrings = {
'settings.notificationPreferences.email': 'Email', // en-fallback
'settings.notificationPreferences.webhook': 'Webhook', // en-fallback
'settings.notificationPreferences.inapp': 'In-App', // en-fallback
'settings.notificationPreferences.notConfigured': 'لم يتم الإعداد بعد — قم بإعداده من إعدادات الإضافة',
'settings.plugins.actions': 'الإجراءات',
'settings.plugins.actions.confirm': 'تنفيذ هذا الإجراء؟',
'settings.notificationPreferences.sendTest': 'إرسال اختبار',
'settings.notificationPreferences.configure': 'تهيئة',
'settings.notificationPreferences.testSuccess': 'تم إرسال إشعار اختباري.',
'settings.notificationPreferences.testFailed': 'فشل الاختبار.',
'settings.notificationPreferences.pluginConfigured': 'تم الإعداد. أدر بيانات الاعتماد من صفحة إعدادات الإضافة.',
'settings.notificationPreferences.ntfy': 'Ntfy', // en-fallback
'settings.webhookUrl.placeholder': 'https://discord.com/api/webhooks/...', // en-fallback
'settings.ntfyUrl.topicPlaceholder': 'my-trek-alerts', // en-fallback
@@ -452,6 +469,15 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'جارٍ مزامنة {count}…',
'settings.offline.banner.failed': 'فشلت المزامنة: {count}',
'settings.offline.banner.conflicts': 'تعارضات: {count}',
'settings.pluginActivity.title': 'نشاط الإضافات',
'settings.pluginActivity.description': 'كل إجراء نفّذته إضافة نيابةً عنك، الأحدث أولاً.',
'settings.pluginActivity.empty': 'لا يوجد نشاط للإضافات بعد.',
'settings.pluginActivity.refresh': 'تحديث',
'settings.pluginActivity.columns.plugin': 'الإضافة',
'settings.pluginActivity.columns.action': 'الإجراء',
'settings.pluginActivity.columns.resource': 'المورد',
'settings.pluginActivity.columns.when': 'الوقت',
'settings.pluginActivity.columns.status': 'النتيجة',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'الخريطة والخطة',
'share.permBookings': 'الحجوزات',
'share.permPacking': 'الأمتعة',
'share.permBudget': 'الميزانية',
'share.permBudget': 'التكاليف',
'share.permCollab': 'الدردشة',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'الخطة',
'shared.tabBookings': 'الحجوزات',
'shared.tabPacking': 'قائمة التعبئة',
'shared.tabBudget': 'الميزانية',
'shared.tabBudget': 'التكاليف',
'shared.tabChat': 'الدردشة',
'shared.days': 'أيام',
'shared.places': 'أماكن',
'shared.other': 'أخرى',
'shared.totalBudget': 'إجمالي الميزانية',
'shared.totalBudget': 'إجمالي التكاليف',
'shared.messages': 'رسائل',
'shared.sharedVia': 'تمت المشاركة عبر',
'shared.confirmed': 'مؤكد',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'الكل',
'todo.filter.open': 'مفتوح',
'todo.filter.done': 'منجز',
'todo.uncategorized': 'بدون تصنيف',
'todo.uncategorized': 'بلا قائمة',
'todo.namePlaceholder': 'اسم المهمة',
'todo.descriptionPlaceholder': 'وصف (اختياري)',
'todo.unassigned': 'غير مُسنَد',
'todo.noCategory': 'بدون فئة',
'todo.noCategory': 'بلا قائمة',
'todo.hasDescription': 'له وصف',
'todo.addItem': 'إضافة مهمة جديدة',
'todo.sidebar.sortBy': 'ترتيب حسب',
'todo.priority': 'الأولوية',
'todo.newCategoryLabel': 'جديد',
'todo.newCategory': 'اسم الفئة',
'todo.addCategory': 'إضافة فئة',
'todo.newCategory': 'اسم القائمة',
'todo.addCategory': 'إضافة قائمة',
'todo.newItem': 'مهمة جديدة',
'todo.empty': 'لا توجد مهام بعد. أضف مهمة للبدء!',
'todo.filter.my': 'مهامي',
'todo.filter.overdue': 'متأخرة',
'todo.sidebar.tasks': 'المهام',
'todo.sidebar.categories': 'الفئات',
'todo.sidebar.categories': 'القوائم',
'todo.detail.title': 'مهمة',
'todo.detail.description': 'وصف',
'todo.detail.category': 'فئة',
'todo.detail.category': 'قائمة',
'todo.detail.dueDate': 'تاريخ الاستحقاق',
'todo.detail.assignedTo': 'مسند إلى',
'todo.detail.delete': 'حذف',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'تجهيز',
'trip.tabs.lists': 'القوائم',
'trip.tabs.listsShort': 'القوائم',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'التكاليف',
'trip.tabs.files': 'الملفات',
'trip.loading': 'جارٍ تحميل الرحلة...',
'trip.loadingPhotos': 'جارٍ تحميل صور الأماكن...',
+87 -1
View File
@@ -165,6 +165,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Solte um .zip de plugin para instalar',
'admin.plugins.uploaded': 'Plugin “{name}” enviado — ative para executar',
'admin.plugins.sideloaded': 'Carregado manualmente',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Carregado de um diretório de build local e recarregado a quente com dados reais — apenas para desenvolvimento',
'admin.plugins.devLinkTitle': 'Vincular um plugin local',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Vincular',
'admin.plugins.devLinkLinked': '{id} vinculado — ative-o para executar',
'admin.plugins.sideloadedHint': 'Enviado manualmente — não é do registro, sem assinatura e sem revisão',
'admin.plugins.browse': 'Explorar plugins',
'admin.plugins.installed': 'Instalado',
@@ -177,6 +184,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin desativado',
'admin.plugins.viewErrors': 'Ver registro de erros',
'admin.plugins.errorLog': 'Registro de erros',
'admin.plugins.allowedHosts': 'Hosts permitidos',
'admin.plugins.allowedHosts.hint': 'Este plugin fala com um serviço que só você pode nomear (um servidor auto-hospedado). Adicione os hosts que ele pode alcançar — nenhum outro.',
'admin.plugins.allowedHosts.none': 'Nenhum host adicionado ainda.',
'admin.plugins.allowedHosts.unsupported': 'Este plugin não usa hosts fornecidos pelo operador. Seus hosts permitidos são fixos no manifesto.',
'admin.plugins.allowedHosts.restartNote': 'Salvar reinicia o plugin para que ele use a nova lista.',
'admin.plugins.allowedHosts.add': 'Adicionar host permitido',
'admin.plugins.allowedHosts.count': '{n} host(s) permitido(s)',
'admin.plugins.operatorEgressPill': '+ hosts que você adicionar',
'admin.plugins.operatorEgressHint': 'Este plugin fala com um serviço que só você pode nomear (um servidor auto-hospedado). Após instalar, adicione os hosts que ele pode alcançar em ⋯ → Hosts permitidos. Nenhum outro.',
'admin.plugins.noErrors': 'Nenhum erro registrado.',
'admin.plugins.uninstalled': 'Plugin desinstalado',
'admin.plugins.uninstallTitle': 'Desinstalar plugin?',
@@ -218,6 +234,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Por usuário',
'admin.plugins.fieldRequired': 'Obrigatório',
'admin.plugins.sourceRepo': 'Repositório de origem',
'admin.plugins.reportIssue': 'Relatar um problema',
'admin.plugins.homepage': 'Página inicial',
'admin.plugins.requiresTrek': 'Requer TREK {version}+',
'admin.plugins.reviewedOn': 'Revisado em {date}',
@@ -226,7 +243,50 @@ const admin: TranslationStrings = {
'admin.plugins.perm.db:read:users': 'Ler informações básicas de perfil (nome, avatar — nunca credenciais)',
'admin.plugins.perm.db:read:costs': 'Ler despesas (itens de orçamento) às quais o usuário atual tem acesso',
'admin.plugins.perm.db:read:packing': 'Ler listas de bagagem de viagens às quais o usuário atual tem acesso',
'admin.plugins.perm.db:write:packing':
'Criar, editar e remover itens da lista de bagagem em viagens que o usuário atual pode editar',
'admin.plugins.perm.weather:read': 'Ler a previsão do tempo em cache do host (por coordenadas)',
'admin.plugins.perm.db:read:categories': 'Ler a lista global de categorias de locais',
'admin.plugins.perm.db:read:tags': 'Ler as etiquetas do usuário atual',
'admin.plugins.perm.db:write:tags': 'Criar, editar e remover as etiquetas do usuário atual',
'admin.plugins.perm.db:read:todos': 'Ler as tarefas de viagens às quais o usuário atual tem acesso',
'admin.plugins.perm.db:write:todos': 'Criar, editar e remover tarefas em viagens que o usuário atual pode editar',
'admin.plugins.perm.db:write:atlas':
'Marcar países e regiões como visitados e gerenciar a lista de desejos do usuário atual (requer o complemento Atlas)',
'admin.plugins.perm.db:write:vacay':
'Alternar dias de férias e feriados da empresa no plano ativo do usuário atual (requer o complemento Vacay)',
'admin.plugins.perm.db:write:journal':
'Criar, editar e remover entradas de diário em diários que o usuário atual pode editar (requer o complemento Journey)',
'admin.plugins.perm.db:write:collections':
'Criar e editar coleções e salvar locais nelas, com o papel de coleção do usuário atual (requer o complemento Collections)',
'admin.plugins.perm.db:write:files':
'Anexar arquivos a viagens que o usuário atual pode editar (limite de 10 MB, extensões bloqueadas recusadas) e gerenciar seus vínculos',
'admin.plugins.perm.db:write:collab':
'Publicar notas, enquetes e mensagens de chat em viagens que o usuário atual pode editar (requer o complemento Collab)',
'admin.plugins.perm.db:write:members':
'Adicionar usuários a viagens — concede acesso à viagem; protegido pelo direito de gerenciar membros do usuário atual',
'admin.plugins.perm.notify:send':
'Enviar uma notificação (caixa do sino + e-mail/ntfy) ao usuário atual ou a uma viagem à qual ele pertence — nunca a um destinatário arbitrário',
'admin.plugins.perm.ai:invoke':
'Usar o provedor de IA configurado pelo administrador (conclusão de texto + extração de documentos) em nome do usuário atual — o complemento nunca guarda uma chave',
'admin.plugins.perm.oauth:client':
'Conectar-se a um serviço de terceiros em seu nome via OAuth intermediado pelo host (o host guarda os tokens, o plugin nunca os vê)',
'admin.plugins.perm.db:read:files': 'Ler arquivos de viagens às quais o usuário atual tem acesso',
'admin.plugins.perm.db:write:reservations':
'Criar, editar e remover reservas em viagens que o usuário atual pode editar',
'admin.plugins.perm.db:write:accommodations':
'Criar, editar e remover acomodações em viagens que o usuário atual pode editar',
'admin.plugins.perm.db:read:journal': 'Ler os diários de viagem do usuário atual (requer o complemento Journey)',
'admin.plugins.perm.db:read:atlas':
'Ler os países e regiões visitados pelo usuário atual (requer o complemento Atlas)',
'admin.plugins.perm.db:read:vacay': 'Ler o plano de férias do usuário atual (requer o complemento Vacay)',
'admin.plugins.perm.db:read:daynotes': 'Ler as notas do dia de viagens às quais o usuário atual tem acesso',
'admin.plugins.perm.db:read:collections':
'Ler as coleções de locais salvos do usuário atual (requer o complemento Collections)',
'admin.plugins.perm.db:write:daynotes':
'Criar, editar e remover notas do dia em viagens que o usuário atual pode editar',
'admin.plugins.perm.jobs:run':
'Executar seus jobs em segundo plano de forma agendada (sem contexto de usuário — não pode ler dados do usuário)',
'admin.plugins.perm.db:write:costs': 'Criar despesas (itens de orçamento) em viagens que o usuário atual pode editar',
'admin.plugins.perm.db:write:places': 'Adicionar, editar e remover locais em viagens que o usuário atual pode editar',
'admin.plugins.perm.db:write:days': 'Adicionar, editar e remover dias em viagens que o usuário atual pode editar',
@@ -243,9 +303,29 @@ const admin: TranslationStrings = {
'admin.plugins.perm.hook:place-detail-provider':
'Contribuir com detalhes extras (avaliações, notas, links) para um local',
'admin.plugins.perm.hook:trip-warning-provider': 'Emitir avisos de validação em uma viagem (exibidos no planejador)',
'admin.plugins.perm.hook:table-contributor':
'Adicionar colunas e ações às visualizações da viagem (reservas, locais, dias)',
'admin.plugins.perm.hook:map-marker-provider':
'Adicionar marcadores ao mapa da viagem (ex. mostrar reservas ou POIs)',
'admin.plugins.perm.hook:pdf-section-provider': 'Anexar seções de texto à exportação em PDF da viagem',
'admin.plugins.perm.hook:atlas-layer-provider':
'Destacar países no mapa-múndi do Atlas (ex. listas de desejos ou avisos de viagem)',
'admin.plugins.perm.hook:journal-entry-provider':
'Adicionar linhas extras (links, estatísticas) às entradas do diário',
'admin.plugins.perm.hook:user-data':
'Apagar ou exportar os dados que armazena sobre um usuário (LGPD/GDPR: exclusão de conta e solicitações de dados)',
'admin.plugins.perm.hook:trip-card-provider':
'Adicionar pequenos selos (status, contagens) aos cartões de viagem do painel',
'admin.plugins.perm.hook:notification-channel': 'Entregar suas notificações por um canal adicional',
'admin.plugins.perm.events:subscribe':
'Reagir a eventos de atividade do núcleo (apenas nome do evento + viagem, nunca o conteúdo)',
'admin.plugins.perm.http:outbound': 'Fazer requisições de saída para os hosts declarados',
'admin.plugins.perm.db:read:collab':
'Ler notas, enquetes e mensagens de chat de viagens às quais o usuário atual tem acesso (requer o complemento Collab)',
'admin.plugins.perm.db:read:files:content':
'Ler o conteúdo (bytes) dos arquivos de viagens às quais o usuário atual tem acesso',
'admin.plugins.perm.db:create:trips': 'Criar novas viagens pertencentes ao usuário atual',
'admin.plugins.perm.rates:read': 'Ler as taxas de câmbio em cache do host',
'admin.plugins.updateConsentTitle': 'Esta atualização precisa de novas permissões',
'admin.plugins.updateConsentBody':
'{name} v{version} está solicitando permissões que você ainda não concedeu. A nova versão está instalada, mas permanece desativada até que você a aprove.',
@@ -288,6 +368,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Nome',
'admin.plugins.sortRecent': 'Atualizados recentemente',
'admin.plugins.sortUpdates': 'Atualizações primeiro',
'admin.plugins.sortDownloads': 'Mais baixados',
'admin.plugins.updatesAvailable': '{count} atualizações disponíveis para seus plugins.',
'admin.plugins.updateAll': 'Atualizar todos',
'admin.plugins.noMatchInstalled': 'Nenhum plugin instalado corresponde à sua busca.',
@@ -308,7 +389,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Widget do painel',
'admin.plugins.cap.heroWidget': 'Widget de cartão de embarque',
'admin.plugins.cap.placeSlot': 'Detalhe do local',
'admin.plugins.cap.daySlot': 'Detalhe do dia',
'admin.plugins.cap.reservationSlot': 'Detalhe da reserva',
'admin.plugins.cap.replacesTabs': 'Substitui abas do planejador',
'admin.plugins.cap.realtime': 'Atualizações em tempo real',
'admin.plugins.cap.notificationChannel': 'Canal de notificação',
'admin.plugins.cap.photos': 'Fornece fotos',
'admin.plugins.cap.calendar': 'Fornece eventos de calendário',
'admin.plugins.cap.placeDetails': 'Enriquece locais',
@@ -334,13 +419,14 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Tamanho',
'admin.plugins.metaRequires': 'Requer',
'admin.plugins.metaReviewed': 'Revisado em',
'admin.plugins.downloads': 'Downloads',
'admin.addons.title': 'Complementos',
'admin.addons.subtitle': 'Ative ou desative recursos para personalizar sua experiência no TREK.',
'admin.addons.catalog.memories.name': 'Memórias',
'admin.addons.catalog.memories.description': 'Álbuns de fotos compartilhados em cada viagem',
'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': 'Orçamento',
'admin.addons.catalog.budget.name': 'Custos',
'admin.addons.catalog.budget.description': 'Acompanhe despesas e planeje o orçamento da viagem',
'admin.addons.catalog.documents.name': 'Documentos',
'admin.addons.catalog.documents.description': 'Armazene e gerencie documentos de viagem',
@@ -52,6 +52,7 @@ const br: NotificationLocale = {
title: 'Sessão Synology encerrada',
body: 'Sua conta ou URL do Synology foi alterada. Você foi desconectado do Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Redefinir sua senha',
+1 -1
View File
@@ -8,7 +8,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, rotas e agrupamento',
'login.features.realtime': 'Sincronização em tempo real',
'login.features.realtimeDesc': 'Planejem juntos via WebSocket',
'login.features.budget': 'Controle de orçamento',
'login.features.budget': 'Controle de custos',
'login.features.budgetDesc': 'Categorias, gráficos e custo por pessoa',
'login.features.collab': 'Colaboração',
'login.features.collabDesc': 'Vários usuários com viagens compartilhadas',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Ver',
'notif.action.accept': 'Aceitar',
'notif.action.decline': 'Recusar',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Notificação',
'notif.generic.text': 'Você tem uma nova notificação',
'notif.dev.unknown_event.title': '[DEV] Evento desconhecido',
+6 -6
View File
@@ -21,7 +21,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Todas as sugestões adicionadas',
'packing.allPacked': 'Tudo na mala!',
'packing.addPlaceholder': 'Adicionar item...',
'packing.categoryPlaceholder': 'Categoria...',
'packing.categoryPlaceholder': 'Lista...',
'packing.saveAsTemplate': 'Salvar como modelo',
'packing.templateName': 'Nome do modelo',
'packing.templateSaved': 'Lista de bagagem salva como modelo',
@@ -34,12 +34,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Renomear',
'packing.menuCheckAll': 'Marcar todos',
'packing.menuUncheckAll': 'Desmarcar todos',
'packing.menuDeleteCat': 'Excluir categoria',
'packing.menuDeleteCat': 'Excluir lista',
'packing.noMembers': 'Nenhum membro na viagem',
'packing.addItem': 'Adicionar item',
'packing.addItemPlaceholder': 'Nome do item...',
'packing.addCategory': 'Adicionar categoria',
'packing.newCategoryPlaceholder': 'Nome da categoria (ex.: Roupas)',
'packing.addCategory': 'Adicionar lista',
'packing.newCategoryPlaceholder': 'Nome da lista (ex: Roupas)',
'packing.applyTemplate': 'Aplicar modelo',
'packing.template': 'Modelo',
'packing.templateApplied': '{count} itens adicionados do modelo',
@@ -49,9 +49,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Peso total',
'packing.bagName': 'Nome da mala...',
'packing.addBag': 'Adicionar mala',
'packing.changeCategory': 'Alterar categoria',
'packing.changeCategory': 'Mover para lista',
'packing.confirm.clearChecked': 'Remover {count} item(ns) marcado(s)?',
'packing.confirm.deleteCat': 'Excluir a categoria "{name}" com {count} item(ns)?',
'packing.confirm.deleteCat': 'Tem certeza de que deseja excluir a lista "{name}" com {count} itens?',
'packing.defaultCategory': 'Outros',
'packing.toast.saveError': 'Falha ao salvar',
'packing.toast.deleteError': 'Falha ao excluir',
+27
View File
@@ -7,6 +7,16 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Mapa',
'settings.tabs.notifications': 'Notificações',
'settings.tabs.integrations': 'Integrações',
'settings.plugins.oauth.connected': 'Conectado',
'settings.plugins.oauth.notConnected': 'Não conectado',
'settings.plugins.oauth.connect': 'Conectar',
'settings.plugins.oauth.disconnect': 'Desconectar',
'settings.tabs.plugins': 'Plugins',
'settings.plugins.title': 'Configurações de plugins',
'settings.plugins.subtitle':
'Suas configurações pessoais para os plugins que você usa (chaves de API, preferências).',
'settings.plugins.empty': 'Nenhum plugin ativo.',
'settings.plugins.saved': 'Configurações salvas',
'settings.tabs.account': 'Conta',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'Sobre',
@@ -279,6 +289,14 @@ const settings: TranslationStrings = {
'settings.ntfyUrl.testFailed': 'Falha na notificação de teste do Ntfy',
'settings.ntfyUrl.tokenCleared': 'Token de acesso removido',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured': 'Ainda não configurado — configure nas definições do plugin',
'settings.plugins.actions': 'Ações',
'settings.plugins.actions.confirm': 'Executar esta ação?',
'settings.notificationPreferences.sendTest': 'Enviar teste',
'settings.notificationPreferences.configure': 'Configurar',
'settings.notificationPreferences.testSuccess': 'Notificação de teste enviada.',
'settings.notificationPreferences.testFailed': 'Falha no teste.',
'settings.notificationPreferences.pluginConfigured': 'Configurado. Gerencie suas credenciais na página de configurações do plugin.',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.ntfy': 'Ntfy',
@@ -466,6 +484,15 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'Sincronizando {count}…',
'settings.offline.banner.failed': 'Falha ao sincronizar: {count}',
'settings.offline.banner.conflicts': 'Conflitos: {count}',
'settings.pluginActivity.title': 'Atividade dos plugins',
'settings.pluginActivity.description': 'Cada ação que um plugin realizou em seu nome, das mais recentes primeiro.',
'settings.pluginActivity.empty': 'Ainda não há atividade de plugins.',
'settings.pluginActivity.refresh': 'Atualizar',
'settings.pluginActivity.columns.plugin': 'Plugin',
'settings.pluginActivity.columns.action': 'Ação',
'settings.pluginActivity.columns.resource': 'Recurso',
'settings.pluginActivity.columns.when': 'Quando',
'settings.pluginActivity.columns.status': 'Resultado',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Mapa e plano',
'share.permBookings': 'Reservas',
'share.permPacking': 'Mala',
'share.permBudget': 'Orçamento',
'share.permBudget': 'Custos',
'share.permCollab': 'Chat',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Plano',
'shared.tabBookings': 'Reservas',
'shared.tabPacking': 'Bagagem',
'shared.tabBudget': 'Orçamento',
'shared.tabBudget': 'Custos',
'shared.tabChat': 'Chat',
'shared.days': 'dias',
'shared.places': 'lugares',
'shared.other': 'Outros',
'shared.totalBudget': 'Orçamento total',
'shared.totalBudget': 'Custos totais',
'shared.messages': 'mensagens',
'shared.sharedVia': 'Compartilhado via',
'shared.confirmed': 'Confirmado',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Todos',
'todo.filter.open': 'Aberto',
'todo.filter.done': 'Concluído',
'todo.uncategorized': 'Sem categoria',
'todo.uncategorized': 'Sem lista',
'todo.namePlaceholder': 'Nome da tarefa',
'todo.descriptionPlaceholder': 'Descrição (opcional)',
'todo.unassigned': 'Não atribuído',
'todo.noCategory': 'Sem categoria',
'todo.noCategory': 'Sem lista',
'todo.hasDescription': 'Com descrição',
'todo.addItem': 'Nova tarefa',
'todo.sidebar.sortBy': 'Ordenar por',
'todo.priority': 'Prioridade',
'todo.newCategoryLabel': 'nova',
'todo.newCategory': 'Nome da categoria',
'todo.addCategory': 'Adicionar categoria',
'todo.newCategory': 'Nome da lista',
'todo.addCategory': 'Adicionar lista',
'todo.newItem': 'Nova tarefa',
'todo.empty': 'Nenhuma tarefa ainda. Adicione uma tarefa para começar!',
'todo.filter.my': 'Minhas tarefas',
'todo.filter.overdue': 'Atrasada',
'todo.sidebar.tasks': 'Tarefas',
'todo.sidebar.categories': 'Categorias',
'todo.sidebar.categories': 'Listas',
'todo.detail.title': 'Tarefa',
'todo.detail.description': 'Descrição',
'todo.detail.category': 'Categoria',
'todo.detail.category': 'Lista',
'todo.detail.dueDate': 'Data de vencimento',
'todo.detail.assignedTo': 'Atribuído a',
'todo.detail.delete': 'Excluir',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'Mala',
'trip.tabs.lists': 'Listas',
'trip.tabs.listsShort': 'Listas',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'Custos',
'trip.tabs.files': 'Arquivos',
'trip.loading': 'Carregando viagem...',
'trip.mobilePlan': 'Plano',
+82 -1
View File
@@ -166,6 +166,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Přetáhněte sem .zip pluginu pro instalaci',
'admin.plugins.uploaded': 'Plugin “{name}” nahrán — aktivujte jej pro spuštění',
'admin.plugins.sideloaded': 'Načteno ručně',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Načteno z místního adresáře sestavení a za běhu znovu načítáno s reálnými daty — jen pro vývoj',
'admin.plugins.devLinkTitle': 'Připojit místní plugin',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Připojit',
'admin.plugins.devLinkLinked': '{id} připojen — aktivujte jej pro spuštění',
'admin.plugins.sideloadedHint': 'Nahráno ručně — není z registru, nepodepsané a nezkontrolované',
'admin.plugins.browse': 'Procházet pluginy',
'admin.plugins.installed': 'Nainstalováno',
@@ -178,6 +185,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin deaktivován',
'admin.plugins.viewErrors': 'Zobrazit protokol chyb',
'admin.plugins.errorLog': 'Protokol chyb',
'admin.plugins.allowedHosts': 'Povolení hostitelé',
'admin.plugins.allowedHosts.hint': 'Tento plugin komunikuje se službou, kterou umíš pojmenovat jen ty (self-hosted server). Přidej hostitele, na které smí — na jiné se nedostane.',
'admin.plugins.allowedHosts.none': 'Zatím nebyli přidáni žádní hostitelé.',
'admin.plugins.allowedHosts.unsupported': 'Tento plugin nepoužívá hostitele zadané provozovatelem. Povolení hostitelé jsou pevně dáni v manifestu.',
'admin.plugins.allowedHosts.restartNote': 'Uložení restartuje plugin, aby načetl nový seznam.',
'admin.plugins.allowedHosts.add': 'Přidat povoleného hostitele',
'admin.plugins.allowedHosts.count': '{n} povolených hostitelů',
'admin.plugins.operatorEgressPill': '+ hostitelé, které přidáš',
'admin.plugins.operatorEgressHint': 'Tento plugin komunikuje se službou, kterou umíš pojmenovat jen ty (self-hosted server). Po instalaci přidej hostitele v ⋯ → Povolení hostitelé. Na jiné se nedostane.',
'admin.plugins.noErrors': 'Žádné zaznamenané chyby.',
'admin.plugins.uninstalled': 'Plugin odinstalován',
'admin.plugins.uninstallTitle': 'Odinstalovat plugin?',
@@ -219,6 +235,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Pro jednotlivé uživatele',
'admin.plugins.fieldRequired': 'Povinné',
'admin.plugins.sourceRepo': 'Zdrojový repozitář',
'admin.plugins.reportIssue': 'Nahlásit problém',
'admin.plugins.homepage': 'Domovská stránka',
'admin.plugins.requiresTrek': 'Vyžaduje TREK {version}+',
'admin.plugins.reviewedOn': 'Zkontrolováno {date}',
@@ -227,7 +244,50 @@ const admin: TranslationStrings = {
'admin.plugins.perm.db:read:users': 'Číst základní údaje profilu (jméno, avatar — nikdy přihlašovací údaje)',
'admin.plugins.perm.db:read:costs': 'Číst výdaje (položky rozpočtu), ke kterým má aktuální uživatel přístup',
'admin.plugins.perm.db:read:packing': 'Číst seznamy věcí k zabalení u cest, ke kterým má aktuální uživatel přístup',
'admin.plugins.perm.db:write:packing':
'Vytvářet, upravovat a mazat položky seznamu balení na cestách, které může aktuální uživatel upravovat',
'admin.plugins.perm.weather:read': 'Číst hostitelem uloženou předpověď počasí (podle souřadnic)',
'admin.plugins.perm.db:read:categories': 'Číst globální seznam kategorií míst',
'admin.plugins.perm.db:read:tags': 'Číst štítky aktuálního uživatele',
'admin.plugins.perm.db:write:tags': 'Vytvářet, upravovat a mazat štítky aktuálního uživatele',
'admin.plugins.perm.db:read:todos': 'Číst úkoly cest, ke kterým má aktuální uživatel přístup',
'admin.plugins.perm.db:write:todos':
'Vytvářet, upravovat a mazat úkoly na cestách, které může aktuální uživatel upravovat',
'admin.plugins.perm.db:write:atlas':
'Označovat země a regiony jako navštívené a spravovat bucket list aktuálního uživatele (vyžaduje doplněk Atlas)',
'admin.plugins.perm.db:write:vacay':
'Přepínat dny dovolené a firemní svátky v aktivním plánu aktuálního uživatele (vyžaduje doplněk Vacay)',
'admin.plugins.perm.db:write:journal':
'Vytvářet, upravovat a mazat záznamy v denících, které může aktuální uživatel upravovat (vyžaduje doplněk Journey)',
'admin.plugins.perm.db:write:collections':
'Vytvářet a upravovat sbírky a ukládat do nich místa, s rolí aktuálního uživatele ve sbírce (vyžaduje doplněk Collections)',
'admin.plugins.perm.db:write:files':
'Přikládat soubory k cestám, které může aktuální uživatel upravovat (limit 10 MB, blokované přípony odmítnuty), a spravovat jejich propojení',
'admin.plugins.perm.db:write:collab':
'Publikovat poznámky, ankety a zprávy chatu na cestách, které může aktuální uživatel upravovat (vyžaduje doplněk Collab)',
'admin.plugins.perm.db:write:members':
'Přidávat uživatele do cest — uděluje přístup k cestě; chráněno právem správy členů aktuálního uživatele',
'admin.plugins.perm.notify:send':
'Odeslat oznámení (schránka zvonku + e-mail/ntfy) aktuálnímu uživateli nebo jeho cestě — nikdy libovolnému příjemci',
'admin.plugins.perm.ai:invoke':
'Použít poskytovatele AI nakonfigurovaného správcem (dokončení textu + extrakce z dokumentů) jménem aktuálního uživatele — doplněk nikdy nedrží klíč',
'admin.plugins.perm.oauth:client':
'Připojit se k službě třetí strany tvým jménem přes OAuth zprostředkované hostitelem (hostitel drží tokeny, doplněk je nikdy nevidí)',
'admin.plugins.perm.db:read:files': 'Číst soubory cest, ke kterým má aktuální uživatel přístup',
'admin.plugins.perm.db:write:reservations':
'Vytvářet, upravovat a mazat rezervace na cestách, které může aktuální uživatel upravovat',
'admin.plugins.perm.db:write:accommodations':
'Vytvářet, upravovat a mazat ubytování na cestách, které může aktuální uživatel upravovat',
'admin.plugins.perm.db:read:journal': 'Číst cestovní deníky aktuálního uživatele (vyžaduje doplněk Journey)',
'admin.plugins.perm.db:read:atlas': 'Číst navštívené země a regiony aktuálního uživatele (vyžaduje doplněk Atlas)',
'admin.plugins.perm.db:read:vacay': 'Číst plán dovolené aktuálního uživatele (vyžaduje doplněk Vacay)',
'admin.plugins.perm.db:read:daynotes': 'Číst denní poznámky cest, ke kterým má aktuální uživatel přístup',
'admin.plugins.perm.db:read:collections':
'Číst sbírky uložených míst aktuálního uživatele (vyžaduje doplněk Collections)',
'admin.plugins.perm.db:write:daynotes':
'Vytvářet, upravovat a mazat denní poznámky na cestách, které může aktuální uživatel upravovat',
'admin.plugins.perm.jobs:run':
'Spouštět deklarované úlohy na pozadí podle plánu (bez kontextu uživatele — nemůže číst uživatelská data)',
'admin.plugins.perm.db:write:costs':
'Vytvářet výdaje (položky rozpočtu) na cestách, které může aktuální uživatel upravovat',
'admin.plugins.perm.db:write:places':
@@ -246,9 +306,24 @@ const admin: TranslationStrings = {
'admin.plugins.perm.hook:calendar-source': 'Poskytovat události do kalendáře',
'admin.plugins.perm.hook:place-detail-provider': 'Přidávat další podrobnosti (recenze, hodnocení, odkazy) k místu',
'admin.plugins.perm.hook:trip-warning-provider': 'Vyvolávat ověřovací upozornění u cesty (zobrazená v plánovači)',
'admin.plugins.perm.hook:table-contributor': 'Přidávat sloupce a akce do zobrazení cesty (rezervace, místa, dny)',
'admin.plugins.perm.hook:map-marker-provider': 'Přidávat značky na mapu cesty (např. zobrazit rezervace nebo POI)',
'admin.plugins.perm.hook:pdf-section-provider': 'Připojovat textové sekce k PDF exportu cesty',
'admin.plugins.perm.hook:atlas-layer-provider':
'Zvýrazňovat země na mapě světa v Atlasu (např. seznamy přání nebo cestovní upozornění)',
'admin.plugins.perm.hook:journal-entry-provider': 'Přidávat další řádky (odkazy, statistiky) k záznamům deníku',
'admin.plugins.perm.hook:user-data':
'Vymazat nebo exportovat data, která o uživateli uchovává (GDPR: smazání účtu a žádosti o data)',
'admin.plugins.perm.hook:trip-card-provider': 'Přidávat malé odznaky (stav, počty) na karty cest na nástěnce',
'admin.plugins.perm.hook:notification-channel': 'Doručovat vaše oznámení dalším kanálem',
'admin.plugins.perm.events:subscribe':
'Reagovat na základní události aktivity (pouze název události a cesta, nikdy obsah)',
'admin.plugins.perm.http:outbound': 'Odesílat odchozí požadavky na deklarované hostitele',
'admin.plugins.perm.db:read:collab':
'Číst poznámky, ankety a zprávy chatu cest, ke kterým má aktuální uživatel přístup (vyžaduje doplněk Collab)',
'admin.plugins.perm.db:read:files:content': 'Číst obsah (bajty) souborů cest, ke kterým má aktuální uživatel přístup',
'admin.plugins.perm.db:create:trips': 'Vytvářet nové cesty vlastněné aktuálním uživatelem',
'admin.plugins.perm.rates:read': 'Číst hostitelem uložené směnné kurzy měn',
'admin.plugins.updateConsentTitle': 'Tato aktualizace vyžaduje nová oprávnění',
'admin.plugins.updateConsentBody':
'{name} v{version} žádá o oprávnění, která jste zatím neudělili. Nová verze je nainstalována, ale zůstane vypnutá, dokud ji neschválíte.',
@@ -291,6 +366,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Název',
'admin.plugins.sortRecent': 'Naposledy aktualizované',
'admin.plugins.sortUpdates': 'Nejdřív aktualizace',
'admin.plugins.sortDownloads': 'Nejstahovanější',
'admin.plugins.updatesAvailable': 'Pro vaše pluginy je k dispozici {count} aktualizací.',
'admin.plugins.updateAll': 'Aktualizovat vše',
'admin.plugins.noMatchInstalled': 'Žádné nainstalované pluginy neodpovídají vašemu hledání.',
@@ -311,7 +387,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Widget na nástěnce',
'admin.plugins.cap.heroWidget': 'Widget palubní vstupenky',
'admin.plugins.cap.placeSlot': 'Detail místa',
'admin.plugins.cap.daySlot': 'Detail dne',
'admin.plugins.cap.reservationSlot': 'Detail rezervace',
'admin.plugins.cap.replacesTabs': 'Nahrazuje karty plánovače',
'admin.plugins.cap.realtime': 'Aktualizace v reálném čase',
'admin.plugins.cap.notificationChannel': 'Kanál oznámení',
'admin.plugins.cap.photos': 'Poskytuje fotky',
'admin.plugins.cap.calendar': 'Poskytuje události kalendáře',
'admin.plugins.cap.placeDetails': 'Obohacuje místa',
@@ -337,13 +417,14 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Velikost',
'admin.plugins.metaRequires': 'Vyžaduje',
'admin.plugins.metaReviewed': 'Zkontrolováno',
'admin.plugins.downloads': 'Stažení',
'admin.addons.title': 'Doplňky',
'admin.addons.subtitle': 'Zapněte nebo vypněte funkce a přizpůsobte si TREK.',
'admin.addons.catalog.memories.name': 'Fotky (Immich)',
'admin.addons.catalog.memories.description': 'Sdílejte cestovní fotky přes vaši instanci Immich',
'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': 'Rozpočet',
'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.documents.name': 'Dokumenty',
'admin.addons.catalog.documents.description': 'Ukládání a správa cestovních dokladů',
@@ -52,6 +52,7 @@ const cs: NotificationLocale = {
title: 'Relace Synology byla zrušena',
body: 'Váš účet nebo URL Synology se změnil. Byli jste odhlášeni ze Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Obnovení hesla',
+1 -1
View File
@@ -8,7 +8,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, trasy a shlukování bodů',
'login.features.realtime': 'Synchronizace v reálném čase',
'login.features.realtimeDesc': 'Plánujte společně přes WebSocket',
'login.features.budget': 'Sledování rozpočtu',
'login.features.budget': 'Sledování nákladů',
'login.features.budgetDesc': 'Kategorie, grafy a náklady na osobu',
'login.features.collab': 'Spolupráce',
'login.features.collabDesc': 'Více uživatelů se sdílenými cestami',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Zobrazit',
'notif.action.accept': 'Přijmout',
'notif.action.decline': 'Odmítnout',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Oznámení',
'notif.generic.text': 'Máte nové oznámení',
'notif.dev.unknown_event.title': '[DEV] Neznámá událost',
+6 -6
View File
@@ -22,7 +22,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Všechny návrhy byly přidány',
'packing.allPacked': 'Vše je zabaleno!',
'packing.addPlaceholder': 'Přidat novou položku...',
'packing.categoryPlaceholder': 'Kategorie...',
'packing.categoryPlaceholder': 'Seznam...',
'packing.filterAll': 'Vše',
'packing.filterOpen': 'K zabalení',
'packing.filterDone': 'Hotovo',
@@ -32,12 +32,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Přejmenovat',
'packing.menuCheckAll': 'Označit vše',
'packing.menuUncheckAll': 'Odznačit vše',
'packing.menuDeleteCat': 'Smazat kategorii',
'packing.menuDeleteCat': 'Smazat seznam',
'packing.noMembers': 'Žádní členové cesty',
'packing.addItem': 'Přidat položku',
'packing.addItemPlaceholder': 'Název položky...',
'packing.addCategory': 'Přidat kategorii',
'packing.newCategoryPlaceholder': 'Název kategorie (např. Oblečení)',
'packing.addCategory': 'Přidat seznam',
'packing.newCategoryPlaceholder': 'Název seznamu (např. Oblečení)',
'packing.applyTemplate': 'Použít šablonu',
'packing.template': 'Šablona',
'packing.templateApplied': '{count} položek přidáno ze šablony',
@@ -50,9 +50,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Celková váha',
'packing.bagName': 'Název zavazadla...',
'packing.addBag': 'Přidat zavazadlo',
'packing.changeCategory': 'Změnit kategorii',
'packing.changeCategory': 'Přesunout do seznamu',
'packing.confirm.clearChecked': 'Opravdu chcete odstranit {count} zabalených položek?',
'packing.confirm.deleteCat': 'Opravdu chcete smazat kategorii „{name}" s {count} položkami?',
'packing.confirm.deleteCat': 'Opravdu chcete smazat seznam „{name}“ s {count} položkami?',
'packing.defaultCategory': 'Ostatní',
'packing.toast.saveError': 'Uložení se nezdařilo',
'packing.toast.deleteError': 'Smazání se nezdařilo',
+26
View File
@@ -7,6 +7,15 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Mapa',
'settings.tabs.notifications': 'Oznámení',
'settings.tabs.integrations': 'Integrace',
'settings.plugins.oauth.connected': 'Připojeno',
'settings.plugins.oauth.notConnected': 'Nepřipojeno',
'settings.plugins.oauth.connect': 'Připojit',
'settings.plugins.oauth.disconnect': 'Odpojit',
'settings.tabs.plugins': 'Doplňky',
'settings.plugins.title': 'Nastavení doplňků',
'settings.plugins.subtitle': 'Tvá osobní nastavení pro doplňky, které používáš (klíče API, předvolby).',
'settings.plugins.empty': 'Nejsou aktivní žádné doplňky.',
'settings.plugins.saved': 'Nastavení uloženo',
'settings.tabs.account': 'Účet',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'O aplikaci',
@@ -275,6 +284,14 @@ const settings: TranslationStrings = {
'settings.ntfyUrl.testFailed': 'Testovací notifikace Ntfy selhala',
'settings.ntfyUrl.tokenCleared': 'Přístupový token byl vymazán',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured': 'Zatím nenastaveno — nastavte to v nastavení pluginu',
'settings.plugins.actions': 'Akce',
'settings.plugins.actions.confirm': 'Spustit tuto akci?',
'settings.notificationPreferences.sendTest': 'Odeslat test',
'settings.notificationPreferences.configure': 'Nastavit',
'settings.notificationPreferences.testSuccess': 'Testovací oznámení odesláno.',
'settings.notificationPreferences.testFailed': 'Test selhal.',
'settings.notificationPreferences.pluginConfigured': 'Nastaveno. Přihlašovací údaje spravuj v nastavení pluginu.',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.ntfy': 'Ntfy',
@@ -460,6 +477,15 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'Synchronizace {count}…',
'settings.offline.banner.failed': 'Synchronizace selhala: {count}',
'settings.offline.banner.conflicts': 'Konflikty: {count}',
'settings.pluginActivity.title': 'Aktivita pluginů',
'settings.pluginActivity.description': 'Každá akce, kterou plugin provedl vaším jménem, od nejnovějších.',
'settings.pluginActivity.empty': 'Zatím žádná aktivita pluginů.',
'settings.pluginActivity.refresh': 'Obnovit',
'settings.pluginActivity.columns.plugin': 'Plugin',
'settings.pluginActivity.columns.action': 'Akce',
'settings.pluginActivity.columns.resource': 'Zdroj',
'settings.pluginActivity.columns.when': 'Kdy',
'settings.pluginActivity.columns.status': 'Výsledek',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Mapa a plán',
'share.permBookings': 'Rezervace',
'share.permPacking': 'Balení',
'share.permBudget': 'Rozpočet',
'share.permBudget': 'Náklady',
'share.permCollab': 'Chat',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Plán',
'shared.tabBookings': 'Rezervace',
'shared.tabPacking': 'Balení',
'shared.tabBudget': 'Rozpočet',
'shared.tabBudget': 'Náklady',
'shared.tabChat': 'Chat',
'shared.days': 'dní',
'shared.places': 'míst',
'shared.other': 'Ostatní',
'shared.totalBudget': 'Celkový rozpočet',
'shared.totalBudget': 'Celkové náklady',
'shared.messages': 'zpráv',
'shared.sharedVia': 'Sdíleno přes',
'shared.confirmed': 'Potvrzeno',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Vše',
'todo.filter.open': 'Otevřené',
'todo.filter.done': 'Hotové',
'todo.uncategorized': 'Bez kategorie',
'todo.uncategorized': 'Bez seznamu',
'todo.namePlaceholder': 'Název úkolu',
'todo.descriptionPlaceholder': 'Popis (volitelné)',
'todo.unassigned': 'Nepřiřazeno',
'todo.noCategory': 'Bez kategorie',
'todo.noCategory': 'Bez seznamu',
'todo.hasDescription': 'Má popis',
'todo.addItem': 'Přidat nový úkol',
'todo.sidebar.sortBy': 'Řadit podle',
'todo.priority': 'Priorita',
'todo.newCategoryLabel': 'nová',
'todo.newCategory': 'Název kategorie',
'todo.addCategory': 'Přidat kategorii',
'todo.newCategory': 'Název seznamu',
'todo.addCategory': 'Přidat seznam',
'todo.newItem': 'Nový úkol',
'todo.empty': 'Zatím žádné úkoly. Přidejte úkol a začněte!',
'todo.filter.my': 'Moje úkoly',
'todo.filter.overdue': 'Po termínu',
'todo.sidebar.tasks': 'Úkoly',
'todo.sidebar.categories': 'Kategorie',
'todo.sidebar.categories': 'Seznamy',
'todo.detail.title': 'Úkol',
'todo.detail.description': 'Popis',
'todo.detail.category': 'Kategorie',
'todo.detail.category': 'Seznam',
'todo.detail.dueDate': 'Termín splnění',
'todo.detail.assignedTo': 'Přiřazeno',
'todo.detail.delete': 'Smazat',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'Balení',
'trip.tabs.lists': 'Seznamy',
'trip.tabs.listsShort': 'Seznamy',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'Náklady',
'trip.tabs.files': 'Soubory',
'trip.loading': 'Načítání cesty...',
'trip.loadingPhotos': 'Načítání fotek míst...',
+87 -1
View File
@@ -168,6 +168,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Plugin-.zip zum Installieren hier ablegen',
'admin.plugins.uploaded': 'Plugin „{name}“ hochgeladen — zum Ausführen aktivieren',
'admin.plugins.sideloaded': 'Manuell geladen',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Aus einem lokalen Build-Verzeichnis geladen und gegen echte Daten neu geladen — nur für Entwicklung',
'admin.plugins.devLinkTitle': 'Lokales Plugin verknüpfen',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Verknüpfen',
'admin.plugins.devLinkLinked': '{id} verknüpft — zum Ausführen aktivieren',
'admin.plugins.sideloadedHint': 'Manuell hochgeladen — nicht aus der Registry, unsigniert und ungeprüft',
'admin.plugins.browse': 'Plugins durchsuchen',
'admin.plugins.installed': 'Installiert',
@@ -180,6 +187,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin deaktiviert',
'admin.plugins.viewErrors': 'Fehlerprotokoll ansehen',
'admin.plugins.errorLog': 'Fehlerprotokoll',
'admin.plugins.allowedHosts': 'Erlaubte Hosts',
'admin.plugins.allowedHosts.hint': 'Dieses Plugin spricht mit einem Dienst, den nur du benennen kannst (ein selbst gehosteter Server). Füge die Hosts hinzu, die es erreichen darf — andere erreicht es nicht.',
'admin.plugins.allowedHosts.none': 'Noch keine Hosts hinzugefügt.',
'admin.plugins.allowedHosts.unsupported': 'Dieses Plugin nutzt keine vom Betreiber angegebenen Hosts. Seine erlaubten Hosts stehen fest im Manifest.',
'admin.plugins.allowedHosts.restartNote': 'Beim Speichern wird das Plugin neu gestartet, damit es die neue Liste übernimmt.',
'admin.plugins.allowedHosts.add': 'Erlaubten Host hinzufügen',
'admin.plugins.allowedHosts.count': '{n} erlaubte Hosts',
'admin.plugins.operatorEgressPill': '+ von dir ergänzte Hosts',
'admin.plugins.operatorEgressHint': 'Dieses Plugin spricht mit einem Dienst, den nur du benennen kannst (ein selbst gehosteter Server). Füge nach der Installation unter ⋯ → Erlaubte Hosts die erreichbaren Hosts hinzu. Andere erreicht es nicht.',
'admin.plugins.noErrors': 'Keine Fehler protokolliert.',
'admin.plugins.uninstalled': 'Plugin deinstalliert',
'admin.plugins.uninstallTitle': 'Plugin deinstallieren?',
@@ -221,6 +237,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Pro Nutzer',
'admin.plugins.fieldRequired': 'Erforderlich',
'admin.plugins.sourceRepo': 'Quell-Repository',
'admin.plugins.reportIssue': 'Problem melden',
'admin.plugins.homepage': 'Homepage',
'admin.plugins.requiresTrek': 'Benötigt TREK {version}+',
'admin.plugins.reviewedOn': 'Geprüft am {date}',
@@ -229,7 +246,43 @@ const admin: TranslationStrings = {
'admin.plugins.perm.db:read:users': 'Grundlegende Profilinfos lesen (Name, Avatar — nie Zugangsdaten)',
'admin.plugins.perm.db:read:costs': 'Ausgaben (Budgetposten) lesen, auf die der handelnde Nutzer Zugriff hat',
'admin.plugins.perm.db:read:packing': 'Packlisten von Reisen lesen, auf die der handelnde Nutzer Zugriff hat',
'admin.plugins.perm.db:write:packing':
'Packlisten-Einträge auf Reisen anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten kann',
'admin.plugins.perm.weather:read': 'Die zwischengespeicherte Wettervorhersage des Hosts lesen (nach Koordinaten)',
'admin.plugins.perm.db:read:categories': 'Die globale Orts-Kategorienliste lesen',
'admin.plugins.perm.db:read:tags': 'Die eigenen Tags des handelnden Nutzers lesen',
'admin.plugins.perm.db:write:tags': 'Die eigenen Tags des handelnden Nutzers anlegen, bearbeiten und löschen',
'admin.plugins.perm.db:read:todos': 'To-dos von Reisen lesen, auf die der handelnde Nutzer Zugriff hat',
'admin.plugins.perm.db:write:todos':
'To-dos auf Reisen anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten kann',
'admin.plugins.perm.db:write:atlas':
'Länder und Regionen als besucht markieren und die Bucket-List des handelnden Nutzers verwalten (benötigt das Atlas-Addon)',
'admin.plugins.perm.db:write:vacay':
'Urlaubstage und Betriebsferien im aktiven Plan des handelnden Nutzers umschalten (benötigt das Vacay-Addon)',
'admin.plugins.perm.db:write:journal':
'Tagebucheinträge in Reisetagebüchern anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten kann (benötigt das Journey-Addon)',
'admin.plugins.perm.db:write:collections':
'Sammlungen anlegen und bearbeiten und Orte darin speichern, mit der Sammlungsrolle des handelnden Nutzers (benötigt das Collections-Addon)',
'admin.plugins.perm.db:write:files':
'Dateien an Reisen anhängen, die der handelnde Nutzer bearbeiten kann (10-MB-Limit, gesperrte Endungen abgelehnt), und deren Verknüpfungen verwalten',
'admin.plugins.perm.db:write:collab':
'Notizen, Umfragen und Chat-Nachrichten auf Reisen erstellen, die der handelnde Nutzer bearbeiten kann (benötigt das Collab-Addon)',
'admin.plugins.perm.db:write:members':
'Nutzer zu Reisen hinzufügen — gewährt Reise-Zugriff; über das Mitglieder-verwalten-Recht des handelnden Nutzers abgesichert',
'admin.plugins.perm.notify:send':
'Eine Benachrichtigung (Glocken-Postfach + E-Mail/ntfy) an den handelnden Nutzer oder eine seiner Reisen senden — nie an beliebige Empfänger',
'admin.plugins.perm.ai:invoke':
'Den vom Admin konfigurierten KI-Anbieter (Textvervollständigung + Dokumentenextraktion) im Namen des handelnden Nutzers nutzen — das Plugin hält nie einen Schlüssel',
'admin.plugins.perm.oauth:client':
'Über host-vermitteltes OAuth in deinem Namen mit einem Drittanbieter verbinden (der Host hält die Tokens, das Plugin sieht sie nie)',
'admin.plugins.perm.db:read:files': 'Dateien von Reisen lesen, auf die der handelnde Nutzer Zugriff hat',
'admin.plugins.perm.db:read:journal': 'Reisetagebücher des handelnden Nutzers lesen (benötigt das Journey-Addon)',
'admin.plugins.perm.db:read:atlas':
'Besuchte Länder und Regionen des handelnden Nutzers lesen (benötigt das Atlas-Addon)',
'admin.plugins.perm.db:read:vacay': 'Urlaubsplanung des handelnden Nutzers lesen (benötigt das Vacay-Addon)',
'admin.plugins.perm.db:read:daynotes': 'Tagesnotizen von Reisen lesen, auf die der handelnde Nutzer Zugriff hat',
'admin.plugins.perm.db:read:collections':
'Sammlungen gespeicherter Orte des handelnden Nutzers lesen (benötigt das Collections-Addon)',
'admin.plugins.perm.db:write:costs':
'Ausgaben (Budgetposten) auf Reisen erstellen, die der handelnde Nutzer bearbeiten kann',
'admin.plugins.perm.db:write:places':
@@ -240,6 +293,12 @@ const admin: TranslationStrings = {
'Orte den Tagen von Reisen zuordnen und entfernen, die der handelnde Nutzer bearbeiten kann',
'admin.plugins.perm.db:write:trips':
'Reisedetails (Titel, Daten, Währung …) auf Reisen bearbeiten, die der handelnde Nutzer bearbeiten kann',
'admin.plugins.perm.db:write:reservations':
'Buchungen (Reservierungen) auf Reisen anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten kann',
'admin.plugins.perm.db:write:accommodations':
'Unterkünfte (Übernachtungsblöcke) auf Reisen anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten darf',
'admin.plugins.perm.db:write:daynotes':
'Tagesnotizen auf Reisen anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten kann',
'admin.plugins.perm.db:meta':
'Eigene private Daten an Reisen, Orte und Tage anhängen, auf die der handelnde Nutzer Zugriff hat',
'admin.plugins.perm.ws:broadcast:trip': 'Echtzeit-Updates an Reisemitglieder senden',
@@ -250,9 +309,30 @@ const admin: TranslationStrings = {
'Zusatzdetails (Bewertungen, Ratings, Links) zu einem Ort beisteuern',
'admin.plugins.perm.hook:trip-warning-provider':
'Validierungswarnungen zu einer Reise ausgeben (im Planer angezeigt)',
'admin.plugins.perm.hook:table-contributor':
'Spalten und Aktionen zu Reise-Ansichten hinzufügen (Reservierungen, Orte, Tage)',
'admin.plugins.perm.hook:map-marker-provider':
'Marker zur Reisekarte hinzufügen (z. B. Buchungen oder POIs anzeigen)',
'admin.plugins.perm.hook:pdf-section-provider': 'Textabschnitte an den PDF-Export einer Reise anhängen',
'admin.plugins.perm.hook:atlas-layer-provider':
'Länder auf der Atlas-Weltkarte hervorheben (z. B. Wunschlisten oder Reisehinweise)',
'admin.plugins.perm.hook:journal-entry-provider': 'Zusatzzeilen (Links, Statistiken) zu Journal-Einträgen beisteuern',
'admin.plugins.perm.hook:user-data':
'Von ihm gespeicherte Nutzerdaten löschen oder exportieren (DSGVO: Kontolöschung & Auskunft)',
'admin.plugins.perm.hook:trip-card-provider':
'Kleine Badges (Status, Zähler) zu deinen Dashboard-Trip-Karten hinzufügen',
'admin.plugins.perm.hook:notification-channel': 'Deine Benachrichtigungen über einen zusätzlichen Kanal zustellen',
'admin.plugins.perm.events:subscribe':
'Auf Kern-Aktivitäts-Events reagieren (nur Event-Name + Reise, nie der Inhalt)',
'admin.plugins.perm.jobs:run':
'Deklarierte Hintergrund-Jobs zeitgesteuert ausführen (kein Nutzerkontext — kann keine Nutzerdaten lesen)',
'admin.plugins.perm.http:outbound': 'Ausgehende Anfragen an deklarierte Hosts stellen',
'admin.plugins.perm.db:read:collab':
'Notizen, Umfragen und Chat-Nachrichten von Reisen lesen, auf die der handelnde Nutzer Zugriff hat (benötigt das Collab-Addon)',
'admin.plugins.perm.db:read:files:content':
'Dateiinhalte (Bytes) von Reisen lesen, auf die der handelnde Nutzer Zugriff hat',
'admin.plugins.perm.db:create:trips': 'Neue Reisen im Besitz des handelnden Nutzers anlegen',
'admin.plugins.perm.rates:read': 'Die zwischengespeicherten Wechselkurse des Hosts lesen',
'admin.plugins.updateConsentTitle': 'Dieses Update braucht neue Berechtigungen',
'admin.plugins.updateConsentBody':
'{name} v{version} verlangt Rechte, die du bisher nicht freigegeben hast. Die neue Version ist installiert, bleibt aber aus, bis du sie freigibst.',
@@ -295,6 +375,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Name',
'admin.plugins.sortRecent': 'Zuletzt aktualisiert',
'admin.plugins.sortUpdates': 'Updates zuerst',
'admin.plugins.sortDownloads': 'Meiste Downloads',
'admin.plugins.updatesAvailable': '{count} Updates für deine Plugins verfügbar.',
'admin.plugins.updateAll': 'Alle aktualisieren',
'admin.plugins.noMatchInstalled': 'Keine installierten Plugins passen zu deiner Suche.',
@@ -315,7 +396,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Dashboard-Widget',
'admin.plugins.cap.heroWidget': 'Boarding-Pass-Widget',
'admin.plugins.cap.placeSlot': 'Ortsdetails',
'admin.plugins.cap.daySlot': 'Tagesdetails',
'admin.plugins.cap.reservationSlot': 'Reservierungsdetails',
'admin.plugins.cap.replacesTabs': 'Ersetzt Planer-Tabs',
'admin.plugins.cap.realtime': 'Echtzeit-Updates',
'admin.plugins.cap.notificationChannel': 'Benachrichtigungskanal',
'admin.plugins.cap.photos': 'Liefert Fotos',
'admin.plugins.cap.calendar': 'Liefert Kalender-Events',
'admin.plugins.cap.placeDetails': 'Reichert Orte an',
@@ -341,11 +426,12 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Größe',
'admin.plugins.metaRequires': 'Benötigt',
'admin.plugins.metaReviewed': 'Geprüft am',
'admin.plugins.downloads': 'Downloads',
'admin.addons.title': 'Addons',
'admin.addons.subtitle': 'Aktiviere oder deaktiviere Funktionen, um TREK nach deinen Wünschen anzupassen.',
'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': 'Budget',
'admin.addons.catalog.budget.name': 'Kosten',
'admin.addons.catalog.budget.description': 'Ausgaben verfolgen und Reisebudget planen',
'admin.addons.catalog.documents.name': 'Dokumente',
'admin.addons.catalog.documents.description': 'Reisedokumente speichern und verwalten',
@@ -52,6 +52,7 @@ const de: NotificationLocale = {
title: 'Synology-Sitzung beendet',
body: 'Dein Synology-Konto oder die URL hat sich geändert. Du wurdest von Synology Photos abgemeldet.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Passwort zurücksetzen',
+1 -1
View File
@@ -8,7 +8,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, Routen & Clustering',
'login.features.realtime': 'Echtzeit-Sync',
'login.features.realtimeDesc': 'Gemeinsam planen via WebSocket',
'login.features.budget': 'Budget-Tracking',
'login.features.budget': 'Kosten-Tracking',
'login.features.budgetDesc': 'Kategorien, Diagramme & Pro-Kopf',
'login.features.collab': 'Zusammenarbeit',
'login.features.collabDesc': 'Multi-User mit geteilten Reisen',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Ansehen',
'notif.action.accept': 'Annehmen',
'notif.action.decline': 'Ablehnen',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Benachrichtigung',
'notif.generic.text': 'Du hast eine neue Benachrichtigung',
'notif.dev.unknown_event.title': '[DEV] Unbekanntes Ereignis',
+6 -6
View File
@@ -22,7 +22,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Alle Vorschläge hinzugefügt',
'packing.allPacked': 'Alles gepackt!',
'packing.addPlaceholder': 'Neuen Gegenstand hinzufügen...',
'packing.categoryPlaceholder': 'Kategorie...',
'packing.categoryPlaceholder': 'Liste...',
'packing.filterAll': 'Alle',
'packing.filterOpen': 'Offen',
'packing.filterDone': 'Erledigt',
@@ -32,12 +32,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Umbenennen',
'packing.menuCheckAll': 'Alle abhaken',
'packing.menuUncheckAll': 'Alle Haken entfernen',
'packing.menuDeleteCat': 'Kategorie löschen',
'packing.menuDeleteCat': 'Liste löschen',
'packing.noMembers': 'Keine Mitglieder',
'packing.addItem': 'Eintrag hinzufügen',
'packing.addItemPlaceholder': 'Artikelname...',
'packing.addCategory': 'Kategorie hinzufügen',
'packing.newCategoryPlaceholder': 'Kategoriename (z.B. Kleidung)',
'packing.addCategory': 'Liste hinzufügen',
'packing.newCategoryPlaceholder': 'Listenname (z. B. Kleidung)',
'packing.applyTemplate': 'Vorlage anwenden',
'packing.template': 'Vorlage',
'packing.templateApplied': '{count} Einträge aus Vorlage hinzugefügt',
@@ -50,7 +50,7 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Gesamtgewicht',
'packing.bagName': 'Name...',
'packing.addBag': 'Gepäck hinzufügen',
'packing.changeCategory': 'Kategorie ändern',
'packing.changeCategory': 'In Liste verschieben',
'packing.makePrivate': 'Privat machen',
'packing.makePublic': 'Teilen',
'packing.privateHint': 'Privat — nur für dich sichtbar',
@@ -69,7 +69,7 @@ const packing: TranslationStrings = {
'packing.alsoBringingStop': 'Bring ich doch nicht mit',
'packing.cloneToMine': 'In meine Liste kopieren',
'packing.confirm.clearChecked': 'Möchtest du {count} abgehakte Gegenstände wirklich entfernen?',
'packing.confirm.deleteCat': 'Möchtest du die Kategorie "{name}" mit {count} Gegenständen wirklich löschen?',
'packing.confirm.deleteCat': 'Möchtest du die Liste „{name}“ mit {count} Einträgen wirklich löschen?',
'packing.defaultCategory': 'Sonstiges',
'packing.toast.saveError': 'Fehler beim Speichern',
'packing.toast.deleteError': 'Fehler beim Löschen',
+28
View File
@@ -7,6 +7,16 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Karte',
'settings.tabs.notifications': 'Mitteilungen',
'settings.tabs.integrations': 'Integrationen',
'settings.plugins.oauth.connected': 'Verbunden',
'settings.plugins.oauth.notConnected': 'Nicht verbunden',
'settings.plugins.oauth.connect': 'Verbinden',
'settings.plugins.oauth.disconnect': 'Trennen',
'settings.tabs.plugins': 'Plugins',
'settings.plugins.title': 'Plugin-Einstellungen',
'settings.plugins.subtitle':
'Deine persönlichen Einstellungen für die von dir genutzten Plugins (API-Schlüssel, Präferenzen).',
'settings.plugins.empty': 'Es sind keine Plugins aktiv.',
'settings.plugins.saved': 'Einstellungen gespeichert',
'settings.tabs.account': 'Konto',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'Über',
@@ -280,6 +290,15 @@ const settings: TranslationStrings = {
'settings.ntfyUrl.testFailed': 'Test-Ntfy-Benachrichtigung fehlgeschlagen',
'settings.ntfyUrl.tokenCleared': 'Zugriffstoken gelöscht',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured':
'Noch nicht eingerichtet — in den Einstellungen des Plugins konfigurieren',
'settings.plugins.actions': 'Aktionen',
'settings.plugins.actions.confirm': 'Diese Aktion ausführen?',
'settings.notificationPreferences.sendTest': 'Test senden',
'settings.notificationPreferences.configure': 'Einrichten',
'settings.notificationPreferences.testSuccess': 'Testbenachrichtigung gesendet.',
'settings.notificationPreferences.testFailed': 'Test fehlgeschlagen.',
'settings.notificationPreferences.pluginConfigured': 'Eingerichtet. Zugangsdaten auf der Einstellungsseite des Plugins verwalten.',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.ntfy': 'Ntfy',
@@ -468,6 +487,15 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'Synchronisiert {count}…',
'settings.offline.banner.failed': 'Synchronisierung fehlgeschlagen: {count}',
'settings.offline.banner.conflicts': 'Konflikte: {count}',
'settings.pluginActivity.title': 'Plugin-Aktivität',
'settings.pluginActivity.description': 'Jede Aktion, die ein Plugin in deinem Namen ausgeführt hat – neueste zuerst.',
'settings.pluginActivity.empty': 'Noch keine Plugin-Aktivität.',
'settings.pluginActivity.refresh': 'Aktualisieren',
'settings.pluginActivity.columns.plugin': 'Plugin',
'settings.pluginActivity.columns.action': 'Aktion',
'settings.pluginActivity.columns.resource': 'Ressource',
'settings.pluginActivity.columns.when': 'Zeitpunkt',
'settings.pluginActivity.columns.status': 'Ergebnis',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Karte & Plan',
'share.permBookings': 'Buchungen',
'share.permPacking': 'Packliste',
'share.permBudget': 'Budget',
'share.permBudget': 'Kosten',
'share.permCollab': 'Chat',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Plan',
'shared.tabBookings': 'Buchungen',
'shared.tabPacking': 'Packliste',
'shared.tabBudget': 'Budget',
'shared.tabBudget': 'Kosten',
'shared.tabChat': 'Chat',
'shared.days': 'Tage',
'shared.places': 'Orte',
'shared.other': 'Sonstige',
'shared.totalBudget': 'Gesamtbudget',
'shared.totalBudget': 'Gesamtkosten',
'shared.messages': 'Nachrichten',
'shared.sharedVia': 'Geteilt über',
'shared.confirmed': 'Bestätigt',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Alle',
'todo.filter.open': 'Offen',
'todo.filter.done': 'Erledigt',
'todo.uncategorized': 'Ohne Kategorie',
'todo.uncategorized': 'Keine Liste',
'todo.namePlaceholder': 'Aufgabenname',
'todo.descriptionPlaceholder': 'Beschreibung (optional)',
'todo.unassigned': 'Nicht zugewiesen',
'todo.noCategory': 'Keine Kategorie',
'todo.noCategory': 'Keine Liste',
'todo.hasDescription': 'Hat Beschreibung',
'todo.addItem': 'Neue Aufgabe hinzufügen',
'todo.sidebar.sortBy': 'Sortieren nach',
'todo.priority': 'Priorität',
'todo.newCategoryLabel': 'neu',
'todo.newCategory': 'Kategoriename',
'todo.addCategory': 'Kategorie hinzufügen',
'todo.newCategory': 'Listenname',
'todo.addCategory': 'Liste hinzufügen',
'todo.newItem': 'Neue Aufgabe',
'todo.empty': 'Noch keine Aufgaben. Erstelle eine Aufgabe um loszulegen!',
'todo.filter.my': 'Meine Aufgaben',
'todo.filter.overdue': 'Überfällig',
'todo.sidebar.tasks': 'Aufgaben',
'todo.sidebar.categories': 'Kategorien',
'todo.sidebar.categories': 'Listen',
'todo.detail.title': 'Aufgabe',
'todo.detail.description': 'Beschreibung',
'todo.detail.category': 'Kategorie',
'todo.detail.category': 'Liste',
'todo.detail.dueDate': 'Fällig am',
'todo.detail.assignedTo': 'Zuständig',
'todo.detail.delete': 'Löschen',
+79 -1
View File
@@ -220,6 +220,12 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Drop a plugin .zip to install',
'admin.plugins.uploaded': 'Plugin “{name}” uploaded — activate it to run',
'admin.plugins.sideloaded': 'Sideloaded',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint': 'Loaded from a local build directory and hot-reloaded against real data — dev only',
'admin.plugins.devLinkTitle': 'Link a local plugin',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Link',
'admin.plugins.devLinkLinked': 'Linked {id} — activate it to run',
'admin.plugins.sideloadedHint': 'Uploaded manually — not from the registry, unsigned and unreviewed',
'admin.plugins.browse': 'Browse plugins',
'admin.plugins.installed': 'Installed',
@@ -232,6 +238,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin deactivated',
'admin.plugins.viewErrors': 'View error log',
'admin.plugins.errorLog': 'Error log',
'admin.plugins.allowedHosts': 'Allowed hosts',
'admin.plugins.allowedHosts.hint': 'This plugin talks to a service only you can name (a self-hosted server). Add the hosts it may reach — it can reach no others.',
'admin.plugins.allowedHosts.none': 'No hosts added yet.',
'admin.plugins.allowedHosts.unsupported': 'This plugin does not use operator-supplied hosts. Its allowed hosts are fixed in its manifest.',
'admin.plugins.allowedHosts.restartNote': 'Saving restarts the plugin so it picks up the new list.',
'admin.plugins.allowedHosts.add': 'Add allowed host',
'admin.plugins.allowedHosts.count': '{n} allowed host(s)',
'admin.plugins.operatorEgressPill': '+ hosts you add',
'admin.plugins.operatorEgressHint': 'This plugin talks to a service only you can name (a self-hosted server). After installing, add the hosts it may reach under ⋯ → Allowed hosts. It can reach no others.',
'admin.plugins.noErrors': 'No errors logged.',
'admin.plugins.uninstalled': 'Plugin uninstalled',
'admin.plugins.uninstallTitle': 'Uninstall plugin?',
@@ -273,6 +288,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Per user',
'admin.plugins.fieldRequired': 'Required',
'admin.plugins.sourceRepo': 'Source repository',
'admin.plugins.reportIssue': 'Report an issue',
'admin.plugins.homepage': 'Homepage',
'admin.plugins.requiresTrek': 'Requires TREK {version}+',
'admin.plugins.reviewedOn': 'Reviewed {date}',
@@ -281,12 +297,51 @@ const admin: TranslationStrings = {
'admin.plugins.perm.db:read:users': 'Read basic profile info (name, avatar — never credentials)',
'admin.plugins.perm.db:read:costs': 'Read costs (budget items) the acting user can access',
'admin.plugins.perm.db:read:packing': 'Read packing lists of trips the acting user can access',
'admin.plugins.perm.db:write:packing': 'Create, edit and delete packing items on trips the acting user can edit',
'admin.plugins.perm.weather:read': "Read the host's cached weather forecast (by coordinates)",
'admin.plugins.perm.db:read:categories': 'Read the global place-category list',
'admin.plugins.perm.db:read:tags': "Read the acting user's own tags",
'admin.plugins.perm.db:write:tags': "Create, edit and delete the acting user's own tags",
'admin.plugins.perm.db:read:todos': 'Read the to-dos of trips the acting user can access',
'admin.plugins.perm.db:write:todos': 'Create, edit and delete to-dos on trips the acting user can edit',
'admin.plugins.perm.db:write:atlas':
'Mark countries and regions visited and manage the bucket list of the acting user (needs the Atlas addon)',
'admin.plugins.perm.db:write:vacay':
"Toggle vacation days and company holidays on the acting user's active plan (needs the Vacay addon)",
'admin.plugins.perm.db:write:journal':
'Create, edit and delete journal entries on journeys the acting user can edit (needs the Journey addon)',
'admin.plugins.perm.db:write:collections':
"Create and edit collections and save places to them, with the acting user's collection role (needs the Collections addon)",
'admin.plugins.perm.db:write:files':
'Attach files to trips the acting user can edit (10MB cap, blocked extensions refused) and manage their links',
'admin.plugins.perm.db:write:collab':
'Post notes, polls and chat messages on trips the acting user can edit (needs the Collab addon)',
'admin.plugins.perm.db:write:members':
"Add users to trips — grants trip access; gated by the acting user's member-manage right",
'admin.plugins.perm.notify:send':
'Send a notification (bell inbox + email/ntfy) to the acting user or a trip they belong to — never an arbitrary recipient',
'admin.plugins.perm.ai:invoke':
"Run the admin-configured AI provider (text completion + document extraction) on the acting user's behalf — the plugin never holds a key",
'admin.plugins.perm.oauth:client':
'Connect to a third-party service on your behalf via host-brokered OAuth (the host holds the tokens, the plugin never sees them)',
'admin.plugins.perm.db:read:files': 'Read files of trips the acting user can access',
'admin.plugins.perm.db:read:journal': 'Read the travel journals of the acting user (needs the Journey addon)',
'admin.plugins.perm.db:read:atlas':
'Read the visited countries and regions of the acting user (needs the Atlas addon)',
'admin.plugins.perm.db:read:vacay': 'Read the vacation plan of the acting user (needs the Vacay addon)',
'admin.plugins.perm.db:read:daynotes': 'Read the day notes of trips the acting user can access',
'admin.plugins.perm.db:read:collections':
'Read the saved-place collections of the acting user (needs the Collections addon)',
'admin.plugins.perm.db:write:costs': 'Create costs (budget items) on trips the acting user can edit',
'admin.plugins.perm.db:write:places': 'Add, edit and remove places on trips the acting user can edit',
'admin.plugins.perm.db:write:days': 'Add, edit and remove days on trips the acting user can edit',
'admin.plugins.perm.db:write:itinerary': 'Assign and remove places on days of trips the acting user can edit',
'admin.plugins.perm.db:write:trips': 'Edit trip details (title, dates, currency…) on trips the acting user can edit',
'admin.plugins.perm.db:write:reservations':
'Create, edit and delete bookings (reservations) on trips the acting user can edit',
'admin.plugins.perm.db:write:accommodations':
'Create, edit and delete accommodations (lodging blocks) on trips the acting user can edit',
'admin.plugins.perm.db:write:daynotes': 'Create, edit and delete day notes on trips the acting user can edit',
'admin.plugins.perm.db:meta': 'Attach its own private data to trips, places and days the acting user can access',
'admin.plugins.perm.ws:broadcast:trip': 'Send real-time updates to trip members',
'admin.plugins.perm.ws:broadcast:user': 'Send real-time updates to individual users',
@@ -294,8 +349,25 @@ const admin: TranslationStrings = {
'admin.plugins.perm.hook:calendar-source': 'Provide events to the calendar',
'admin.plugins.perm.hook:place-detail-provider': 'Contribute extra details (reviews, ratings, links) to a place',
'admin.plugins.perm.hook:trip-warning-provider': 'Raise validation warnings on a trip (shown in the planner)',
'admin.plugins.perm.hook:table-contributor': 'Add columns and actions to trip views (reservations, places, days)',
'admin.plugins.perm.hook:map-marker-provider': 'Add markers to the trip map (e.g. show bookings or POIs)',
'admin.plugins.perm.hook:pdf-section-provider': 'Append text sections to the trip PDF export',
'admin.plugins.perm.hook:atlas-layer-provider':
'Highlight countries on the Atlas world map (e.g. wishlists or travel advisories)',
'admin.plugins.perm.hook:journal-entry-provider': 'Contribute extra rows (links, stats) to journal entries',
'admin.plugins.perm.hook:trip-card-provider': 'Add small badges (status, counts) to your dashboard trip cards',
'admin.plugins.perm.hook:notification-channel': 'Deliver your notifications over an additional channel',
'admin.plugins.perm.hook:user-data':
'Erase or export the data it stores about a user (GDPR account deletion & data requests)',
'admin.plugins.perm.events:subscribe': 'React to core activity events (event name + trip only, never the content)',
'admin.plugins.perm.jobs:run':
'Run its declared background jobs on a schedule (no user context — cannot read user data)',
'admin.plugins.perm.http:outbound': 'Make outbound requests to its declared hosts',
'admin.plugins.perm.db:read:collab':
'Read notes, polls and chat messages of trips the acting user can access (needs the Collab addon)',
'admin.plugins.perm.db:read:files:content': 'Read the byte content of files on trips the acting user can access',
'admin.plugins.perm.db:create:trips': 'Create new trips owned by the acting user',
'admin.plugins.perm.rates:read': "Read the host's cached currency exchange rates",
'admin.plugins.updateConsentTitle': 'This update needs new permissions',
'admin.plugins.updateConsentBody':
"{name} v{version} is asking for rights you haven't granted yet. The new version is installed but stays off until you approve it.",
@@ -338,6 +410,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Name',
'admin.plugins.sortRecent': 'Recently updated',
'admin.plugins.sortUpdates': 'Updates first',
'admin.plugins.sortDownloads': 'Most downloads',
'admin.plugins.updatesAvailable': '{count} updates available for your plugins.',
'admin.plugins.updateAll': 'Update all',
'admin.plugins.noMatchInstalled': 'No installed plugins match your search.',
@@ -358,7 +431,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Dashboard widget',
'admin.plugins.cap.heroWidget': 'Boarding-pass widget',
'admin.plugins.cap.placeSlot': 'Place detail',
'admin.plugins.cap.daySlot': 'Day detail',
'admin.plugins.cap.reservationSlot': 'Reservation detail',
'admin.plugins.cap.replacesTabs': 'Replaces planner tabs',
'admin.plugins.cap.realtime': 'Real-time updates',
'admin.plugins.cap.notificationChannel': 'Notification channel',
'admin.plugins.cap.photos': 'Provides photos',
'admin.plugins.cap.calendar': 'Provides calendar events',
'admin.plugins.cap.placeDetails': 'Enriches places',
@@ -384,11 +461,12 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Size',
'admin.plugins.metaRequires': 'Requires',
'admin.plugins.metaReviewed': 'Reviewed on',
'admin.plugins.downloads': 'Downloads',
'admin.addons.title': 'Addons',
'admin.addons.subtitle': 'Enable or disable features to customize your TREK experience.',
'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': 'Budget',
'admin.addons.catalog.budget.name': 'Costs',
'admin.addons.catalog.budget.description': 'Track expenses and plan your trip budget',
'admin.addons.catalog.documents.name': 'Documents',
'admin.addons.catalog.documents.description': 'Store and manage travel documents',
@@ -52,6 +52,7 @@ const en: NotificationLocale = {
title: 'Synology session cleared',
body: 'Your Synology account or URL changed. You have been logged out of Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Reset your password',
+1 -1
View File
@@ -8,7 +8,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, routes & clustering',
'login.features.realtime': 'Real-Time Sync',
'login.features.realtimeDesc': 'Plan together via WebSocket',
'login.features.budget': 'Budget Tracking',
'login.features.budget': 'Cost Tracking',
'login.features.budgetDesc': 'Categories, charts & per-person costs',
'login.features.collab': 'Collaboration',
'login.features.collabDesc': 'Multi-user with shared trips',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'View',
'notif.action.accept': 'Accept',
'notif.action.decline': 'Decline',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Notification',
'notif.generic.text': 'You have a new notification',
'notif.dev.unknown_event.title': '[DEV] Unknown Event',
+6 -6
View File
@@ -22,7 +22,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'All suggestions added',
'packing.allPacked': 'All packed!',
'packing.addPlaceholder': 'Add new item...',
'packing.categoryPlaceholder': 'Category...',
'packing.categoryPlaceholder': 'List...',
'packing.filterAll': 'All',
'packing.filterOpen': 'Open',
'packing.filterDone': 'Done',
@@ -32,12 +32,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Rename',
'packing.menuCheckAll': 'Check All',
'packing.menuUncheckAll': 'Uncheck All',
'packing.menuDeleteCat': 'Delete Category',
'packing.menuDeleteCat': 'Delete List',
'packing.noMembers': 'No trip members',
'packing.addItem': 'Add item',
'packing.addItemPlaceholder': 'Item name...',
'packing.addCategory': 'Add category',
'packing.newCategoryPlaceholder': 'Category name (e.g. Clothing)',
'packing.addCategory': 'Add list',
'packing.newCategoryPlaceholder': 'List name (e.g. Clothing)',
'packing.applyTemplate': 'Apply template',
'packing.template': 'Template',
'packing.templateApplied': '{count} items added from template',
@@ -50,7 +50,7 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Total weight',
'packing.bagName': 'Bag name...',
'packing.addBag': 'Add bag',
'packing.changeCategory': 'Change Category',
'packing.changeCategory': 'Move to List',
'packing.makePrivate': 'Make private',
'packing.makePublic': 'Make shared',
'packing.privateHint': 'Private — only visible to you',
@@ -69,7 +69,7 @@ const packing: TranslationStrings = {
'packing.alsoBringingStop': "I'm not bringing it",
'packing.cloneToMine': 'Copy to my list',
'packing.confirm.clearChecked': 'Are you sure you want to remove {count} checked items?',
'packing.confirm.deleteCat': 'Are you sure you want to delete the category "{name}" with {count} items?',
'packing.confirm.deleteCat': 'Are you sure you want to delete the list "{name}" with {count} items?',
'packing.defaultCategory': 'Other',
'packing.toast.saveError': 'Failed to save',
'packing.toast.deleteError': 'Failed to delete',
+26
View File
@@ -7,6 +7,24 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Map',
'settings.tabs.notifications': 'Notifications',
'settings.tabs.integrations': 'Integrations',
'settings.plugins.oauth.connected': 'Connected',
'settings.plugins.oauth.notConnected': 'Not connected',
'settings.plugins.oauth.connect': 'Connect',
'settings.plugins.oauth.disconnect': 'Disconnect',
'settings.tabs.plugins': 'Plugins',
'settings.plugins.title': 'Plugin settings',
'settings.plugins.subtitle': 'Your personal settings for the plugins you use (API keys, preferences).',
'settings.plugins.empty': 'No plugins are active.',
'settings.plugins.saved': 'Settings saved',
'settings.pluginActivity.title': 'Plugin activity',
'settings.pluginActivity.description': 'Every action a plugin took on your behalf, newest first.',
'settings.pluginActivity.empty': 'No plugin activity yet.',
'settings.pluginActivity.refresh': 'Refresh',
'settings.pluginActivity.columns.plugin': 'Plugin',
'settings.pluginActivity.columns.action': 'Action',
'settings.pluginActivity.columns.resource': 'Resource',
'settings.pluginActivity.columns.when': 'When',
'settings.pluginActivity.columns.status': 'Result',
'settings.tabs.account': 'Account',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'About',
@@ -83,6 +101,14 @@ const settings: TranslationStrings = {
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured': 'Not configured yet — set it up in the plugin’s settings',
'settings.plugins.actions': 'Actions',
'settings.plugins.actions.confirm': 'Run this action?',
'settings.notificationPreferences.sendTest': 'Send test',
'settings.notificationPreferences.configure': 'Configure',
'settings.notificationPreferences.testSuccess': 'Test notification sent.',
'settings.notificationPreferences.testFailed': 'Test failed.',
'settings.notificationPreferences.pluginConfigured': 'Configured. Manage credentials on the plugin’s settings page.',
'settings.notificationPreferences.ntfy': 'Ntfy',
'settings.notificationPreferences.noChannels':
'No notification channels are configured. Ask an admin to set up email or webhook notifications.',
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Map & Plan',
'share.permBookings': 'Bookings',
'share.permPacking': 'Packing',
'share.permBudget': 'Budget',
'share.permBudget': 'Costs',
'share.permCollab': 'Chat',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Plan',
'shared.tabBookings': 'Bookings',
'shared.tabPacking': 'Packing',
'shared.tabBudget': 'Budget',
'shared.tabBudget': 'Costs',
'shared.tabChat': 'Chat',
'shared.days': 'days',
'shared.places': 'places',
'shared.other': 'Other',
'shared.totalBudget': 'Total Budget',
'shared.totalBudget': 'Total Costs',
'shared.messages': 'messages',
'shared.sharedVia': 'Shared via',
'shared.confirmed': 'Confirmed',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'All',
'todo.filter.open': 'Open',
'todo.filter.done': 'Done',
'todo.uncategorized': 'Uncategorized',
'todo.uncategorized': 'No list',
'todo.namePlaceholder': 'Task name',
'todo.descriptionPlaceholder': 'Description (optional)',
'todo.unassigned': 'Unassigned',
'todo.noCategory': 'No category',
'todo.noCategory': 'No list',
'todo.hasDescription': 'Has description',
'todo.addItem': 'Add new task',
'todo.sidebar.sortBy': 'Sort by',
'todo.priority': 'Priority',
'todo.newCategoryLabel': 'new',
'todo.newCategory': 'Category name',
'todo.addCategory': 'Add category',
'todo.newCategory': 'List name',
'todo.addCategory': 'Add list',
'todo.newItem': 'New task',
'todo.empty': 'No tasks yet. Add a task to get started!',
'todo.filter.my': 'My Tasks',
'todo.filter.overdue': 'Overdue',
'todo.sidebar.tasks': 'Tasks',
'todo.sidebar.categories': 'Categories',
'todo.sidebar.categories': 'Lists',
'todo.detail.title': 'Task',
'todo.detail.description': 'Description',
'todo.detail.category': 'Category',
'todo.detail.category': 'List',
'todo.detail.dueDate': 'Due date',
'todo.detail.assignedTo': 'Assigned to',
'todo.detail.delete': 'Delete',
+89 -1
View File
@@ -167,6 +167,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Suelta un .zip de plugin para instalar',
'admin.plugins.uploaded': 'Plugin “{name}” subido — actívalo para ejecutarlo',
'admin.plugins.sideloaded': 'Cargado manualmente',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Cargado desde un directorio de compilación local y recargado en caliente con datos reales: solo para desarrollo',
'admin.plugins.devLinkTitle': 'Vincular un plugin local',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Vincular',
'admin.plugins.devLinkLinked': '{id} vinculado: actívalo para ejecutarlo',
'admin.plugins.sideloadedHint': 'Subido manualmente — no proviene del registro, sin firmar ni revisar',
'admin.plugins.browse': 'Explorar plugins',
'admin.plugins.installed': 'Instalado',
@@ -179,6 +186,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin desactivado',
'admin.plugins.viewErrors': 'Ver registro de errores',
'admin.plugins.errorLog': 'Registro de errores',
'admin.plugins.allowedHosts': 'Hosts permitidos',
'admin.plugins.allowedHosts.hint': 'Este plugin se comunica con un servicio que solo tú puedes nombrar (un servidor autoalojado). Añade los hosts a los que puede acceder — no accederá a ningún otro.',
'admin.plugins.allowedHosts.none': 'Aún no se han añadido hosts.',
'admin.plugins.allowedHosts.unsupported': 'Este plugin no usa hosts proporcionados por el operador. Sus hosts permitidos están fijados en su manifiesto.',
'admin.plugins.allowedHosts.restartNote': 'Al guardar se reinicia el plugin para que tome la nueva lista.',
'admin.plugins.allowedHosts.add': 'Añadir host permitido',
'admin.plugins.allowedHosts.count': '{n} host(s) permitido(s)',
'admin.plugins.operatorEgressPill': '+ hosts que añadas',
'admin.plugins.operatorEgressHint': 'Este plugin se comunica con un servicio que solo tú puedes nombrar (un servidor autoalojado). Tras instalarlo, añade los hosts a los que puede acceder en ⋯ → Hosts permitidos. No accederá a ningún otro.',
'admin.plugins.noErrors': 'No hay errores registrados.',
'admin.plugins.uninstalled': 'Plugin desinstalado',
'admin.plugins.uninstallTitle': '¿Desinstalar el plugin?',
@@ -220,6 +236,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Por usuario',
'admin.plugins.fieldRequired': 'Obligatorio',
'admin.plugins.sourceRepo': 'Repositorio de código',
'admin.plugins.reportIssue': 'Informar de un problema',
'admin.plugins.homepage': 'Sitio web',
'admin.plugins.requiresTrek': 'Requiere TREK {version}+',
'admin.plugins.reviewedOn': 'Revisado el {date}',
@@ -230,7 +247,53 @@ const admin: TranslationStrings = {
'Leer los gastos (partidas de presupuesto) a los que el usuario activo tiene acceso',
'admin.plugins.perm.db:read:packing':
'Leer las listas de equipaje de los viajes a los que el usuario activo tiene acceso',
'admin.plugins.perm.db:write:packing':
'Crear, editar y eliminar artículos de la lista de equipaje en los viajes que el usuario activo puede editar',
'admin.plugins.perm.weather:read': 'Leer la previsión meteorológica en caché del host (por coordenadas)',
'admin.plugins.perm.db:read:categories': 'Leer la lista global de categorías de lugares',
'admin.plugins.perm.db:read:tags': 'Leer las etiquetas del usuario activo',
'admin.plugins.perm.db:write:tags': 'Crear, editar y eliminar las etiquetas del usuario activo',
'admin.plugins.perm.db:read:todos': 'Leer las tareas de los viajes a los que el usuario activo tiene acceso',
'admin.plugins.perm.db:write:todos':
'Crear, editar y eliminar tareas en los viajes que el usuario activo puede editar',
'admin.plugins.perm.db:write:atlas':
'Marcar países y regiones como visitados y gestionar la lista de deseos del usuario activo (requiere el complemento Atlas)',
'admin.plugins.perm.db:write:vacay':
'Alternar días de vacaciones y festivos de empresa en el plan activo del usuario activo (requiere el complemento Vacay)',
'admin.plugins.perm.db:write:journal':
'Crear, editar y eliminar entradas de diario en los diarios que el usuario activo puede editar (requiere el complemento Journey)',
'admin.plugins.perm.db:write:collections':
'Crear y editar colecciones y guardar lugares en ellas, con el rol de colección del usuario activo (requiere el complemento Collections)',
'admin.plugins.perm.db:write:files':
'Adjuntar archivos a los viajes que el usuario activo puede editar (límite de 10 MB, extensiones bloqueadas rechazadas) y gestionar sus enlaces',
'admin.plugins.perm.db:write:collab':
'Publicar notas, encuestas y mensajes de chat en los viajes que el usuario activo puede editar (requiere el complemento Collab)',
'admin.plugins.perm.db:write:members':
'Añadir usuarios a los viajes — concede acceso al viaje; protegido por el permiso de gestión de miembros del usuario activo',
'admin.plugins.perm.notify:send':
'Enviar una notificación (bandeja de campana + correo/ntfy) al usuario activo o a un viaje al que pertenece — nunca a un destinatario arbitrario',
'admin.plugins.perm.ai:invoke':
'Usar el proveedor de IA configurado por el administrador (completado de texto + extracción de documentos) en nombre del usuario activo — el complemento nunca guarda una clave',
'admin.plugins.perm.oauth:client':
'Conectarse a un servicio de terceros en tu nombre mediante OAuth gestionado por el host (el host guarda los tokens, el complemento nunca los ve)',
'admin.plugins.perm.db:read:files': 'Leer los archivos de los viajes a los que el usuario activo tiene acceso',
'admin.plugins.perm.db:write:reservations':
'Crear, editar y eliminar reservas en los viajes que el usuario activo puede editar',
'admin.plugins.perm.db:write:accommodations':
'Crear, editar y eliminar alojamientos en los viajes que el usuario activo puede editar',
'admin.plugins.perm.db:read:journal':
'Leer los diarios de viaje del usuario activo (requiere el complemento Journey)',
'admin.plugins.perm.db:read:atlas':
'Leer los países y regiones visitados por el usuario activo (requiere el complemento Atlas)',
'admin.plugins.perm.db:read:vacay': 'Leer el plan de vacaciones del usuario activo (requiere el complemento Vacay)',
'admin.plugins.perm.db:read:daynotes':
'Leer las notas del día de los viajes a los que el usuario activo tiene acceso',
'admin.plugins.perm.db:read:collections':
'Leer las colecciones de lugares guardados del usuario activo (requiere el complemento Collections)',
'admin.plugins.perm.db:write:daynotes':
'Crear, editar y eliminar notas del día en los viajes que el usuario activo puede editar',
'admin.plugins.perm.jobs:run':
'Ejecutar sus tareas en segundo plano de forma programada (sin contexto de usuario: no puede leer datos del usuario)',
'admin.plugins.perm.db:write:costs':
'Crear gastos (partidas de presupuesto) en los viajes que el usuario activo puede editar',
'admin.plugins.perm.db:write:places':
@@ -250,9 +313,28 @@ const admin: TranslationStrings = {
'Aportar detalles adicionales (reseñas, valoraciones, enlaces) a un lugar',
'admin.plugins.perm.hook:trip-warning-provider':
'Generar advertencias de validación en un viaje (mostradas en el planificador)',
'admin.plugins.perm.hook:table-contributor':
'Añadir columnas y acciones a las vistas del viaje (reservas, lugares, días)',
'admin.plugins.perm.hook:map-marker-provider': 'Añadir marcadores al mapa del viaje (p. ej. mostrar reservas o POIs)',
'admin.plugins.perm.hook:pdf-section-provider': 'Añadir secciones de texto a la exportación PDF del viaje',
'admin.plugins.perm.hook:atlas-layer-provider':
'Resaltar países en el mapamundi del Atlas (p. ej. listas de deseos o avisos de viaje)',
'admin.plugins.perm.hook:journal-entry-provider':
'Añadir filas adicionales (enlaces, estadísticas) a las entradas del diario',
'admin.plugins.perm.hook:user-data':
'Borrar o exportar los datos que almacena sobre un usuario (RGPD: borrado de cuenta y solicitudes de datos)',
'admin.plugins.perm.hook:trip-card-provider':
'Añadir pequeñas insignias (estado, recuentos) a las tarjetas de viaje del panel',
'admin.plugins.perm.hook:notification-channel': 'Enviar tus notificaciones a través de un canal adicional',
'admin.plugins.perm.events:subscribe':
'Reaccionar a eventos de actividad del núcleo (solo el nombre del evento y el viaje, nunca el contenido)',
'admin.plugins.perm.http:outbound': 'Realizar solicitudes salientes a sus hosts declarados',
'admin.plugins.perm.db:read:collab':
'Leer las notas, encuestas y mensajes de chat de los viajes a los que el usuario activo tiene acceso (requiere el complemento Collab)',
'admin.plugins.perm.db:read:files:content':
'Leer el contenido (bytes) de los archivos de los viajes a los que el usuario activo tiene acceso',
'admin.plugins.perm.db:create:trips': 'Crear nuevos viajes propiedad del usuario activo',
'admin.plugins.perm.rates:read': 'Leer los tipos de cambio en caché del host',
'admin.plugins.updateConsentTitle': 'Esta actualización necesita nuevos permisos',
'admin.plugins.updateConsentBody':
'{name} v{version} solicita permisos que aún no has concedido. La nueva versión está instalada, pero permanece desactivada hasta que la apruebes.',
@@ -295,6 +377,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Nombre',
'admin.plugins.sortRecent': 'Actualizados recientemente',
'admin.plugins.sortUpdates': 'Actualizaciones primero',
'admin.plugins.sortDownloads': 'Más descargados',
'admin.plugins.updatesAvailable': '{count} actualizaciones disponibles para tus plugins.',
'admin.plugins.updateAll': 'Actualizar todo',
'admin.plugins.noMatchInstalled': 'Ningún plugin instalado coincide con tu búsqueda.',
@@ -315,7 +398,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Widget del panel',
'admin.plugins.cap.heroWidget': 'Widget de tarjeta de embarque',
'admin.plugins.cap.placeSlot': 'Detalle del lugar',
'admin.plugins.cap.daySlot': 'Detalle del día',
'admin.plugins.cap.reservationSlot': 'Detalle de la reserva',
'admin.plugins.cap.replacesTabs': 'Reemplaza pestañas del planificador',
'admin.plugins.cap.realtime': 'Actualizaciones en tiempo real',
'admin.plugins.cap.notificationChannel': 'Canal de notificaciones',
'admin.plugins.cap.photos': 'Proporciona fotos',
'admin.plugins.cap.calendar': 'Proporciona eventos de calendario',
'admin.plugins.cap.placeDetails': 'Enriquece lugares',
@@ -341,6 +428,7 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Tamaño',
'admin.plugins.metaRequires': 'Requiere',
'admin.plugins.metaReviewed': 'Revisado el',
'admin.plugins.downloads': 'Descargas',
'admin.addons.title': 'Complementos',
'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 ',
@@ -449,7 +537,7 @@ const admin: TranslationStrings = {
'admin.addons.catalog.mcp.description': 'Protocolo de contexto de modelo para integración con asistentes de IA',
'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': 'Presupuesto',
'admin.addons.catalog.budget.name': 'Costes',
'admin.addons.catalog.budget.description': 'Controla los gastos y planifica el presupuesto del viaje',
'admin.addons.catalog.documents.name': 'Documentos',
'admin.addons.catalog.documents.description': 'Guarda y gestiona la documentación del viaje',
@@ -52,6 +52,7 @@ const es: NotificationLocale = {
title: 'Sesión de Synology cerrada',
body: 'Tu cuenta o URL de Synology ha cambiado. Has cerrado sesión en Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Restablecer tu contraseña',
+1 -1
View File
@@ -9,7 +9,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, rutas y agrupación',
'login.features.realtime': 'Sincronización en tiempo real',
'login.features.realtimeDesc': 'Planificad juntos mediante WebSocket',
'login.features.budget': 'Control de presupuesto',
'login.features.budget': 'Control de costes',
'login.features.budgetDesc': 'Categorías, gráficos y costes por persona',
'login.features.collab': 'Colaboración',
'login.features.collabDesc': 'Multiusuario con viajes compartidos',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Ver',
'notif.action.accept': 'Aceptar',
'notif.action.decline': 'Rechazar',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Notificación',
'notif.generic.text': 'Tienes una nueva notificación',
'notif.dev.unknown_event.title': '[DEV] Evento desconocido',
+6 -6
View File
@@ -22,7 +22,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Todas las sugerencias añadidas',
'packing.allPacked': '¡Todo preparado!',
'packing.addPlaceholder': 'Añadir nuevo elemento...',
'packing.categoryPlaceholder': 'Categoría...',
'packing.categoryPlaceholder': 'Lista...',
'packing.filterAll': 'Todo',
'packing.filterOpen': 'Pendientes',
'packing.filterDone': 'Hecho',
@@ -32,11 +32,11 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Renombrar',
'packing.menuCheckAll': 'Marcar todo',
'packing.menuUncheckAll': 'Desmarcar todo',
'packing.menuDeleteCat': 'Eliminar categoría',
'packing.menuDeleteCat': 'Eliminar lista',
'packing.addItem': 'Añadir artículo',
'packing.addItemPlaceholder': 'Nombre del artículo...',
'packing.addCategory': 'Añadir categoría',
'packing.newCategoryPlaceholder': 'Nombre de categoría (ej. Ropa)',
'packing.addCategory': 'Añadir lista',
'packing.newCategoryPlaceholder': 'Nombre de la lista (p. ej. Ropa)',
'packing.applyTemplate': 'Aplicar plantilla',
'packing.template': 'Plantilla',
'packing.templateApplied': '{count} artículos añadidos desde plantilla',
@@ -50,9 +50,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Peso total',
'packing.bagName': 'Nombre...',
'packing.addBag': 'Añadir equipaje',
'packing.changeCategory': 'Cambiar categoría',
'packing.changeCategory': 'Mover a lista',
'packing.confirm.clearChecked': '¿Seguro que quieres eliminar {count} elementos marcados?',
'packing.confirm.deleteCat': '¿Seguro que quieres eliminar la categoría "{name}" con {count} elementos?',
'packing.confirm.deleteCat': '¿Seguro que quieres eliminar la lista "{name}" con {count} elementos?',
'packing.defaultCategory': 'Otros',
'packing.toast.saveError': 'No se pudo guardar',
'packing.toast.deleteError': 'No se pudo eliminar',
+26
View File
@@ -7,6 +7,15 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Mapa',
'settings.tabs.notifications': 'Notificaciones',
'settings.tabs.integrations': 'Integraciones',
'settings.plugins.oauth.connected': 'Conectado',
'settings.plugins.oauth.notConnected': 'No conectado',
'settings.plugins.oauth.connect': 'Conectar',
'settings.plugins.oauth.disconnect': 'Desconectar',
'settings.tabs.plugins': 'Complementos',
'settings.plugins.title': 'Ajustes de complementos',
'settings.plugins.subtitle': 'Tus ajustes personales para los complementos que usas (claves de API, preferencias).',
'settings.plugins.empty': 'No hay complementos activos.',
'settings.plugins.saved': 'Ajustes guardados',
'settings.tabs.account': 'Cuenta',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'Acerca de',
@@ -282,6 +291,14 @@ const settings: TranslationStrings = {
'settings.ntfyUrl.testFailed': 'Error en la notificación de prueba de Ntfy',
'settings.ntfyUrl.tokenCleared': 'Token de acceso eliminado',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured': 'Aún no configurado — configúralo en los ajustes del plugin',
'settings.plugins.actions': 'Acciones',
'settings.plugins.actions.confirm': '¿Ejecutar esta acción?',
'settings.notificationPreferences.sendTest': 'Enviar prueba',
'settings.notificationPreferences.configure': 'Configurar',
'settings.notificationPreferences.testSuccess': 'Notificación de prueba enviada.',
'settings.notificationPreferences.testFailed': 'La prueba falló.',
'settings.notificationPreferences.pluginConfigured': 'Configurado. Gestiona tus credenciales en la página de ajustes del plugin.',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.ntfy': 'Ntfy',
@@ -468,6 +485,15 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'Sincronizando {count}…',
'settings.offline.banner.failed': 'Error al sincronizar: {count}',
'settings.offline.banner.conflicts': 'Conflictos: {count}',
'settings.pluginActivity.title': 'Actividad de plugins',
'settings.pluginActivity.description': 'Cada acción que un plugin realizó en tu nombre, las más recientes primero.',
'settings.pluginActivity.empty': 'Todavía no hay actividad de plugins.',
'settings.pluginActivity.refresh': 'Actualizar',
'settings.pluginActivity.columns.plugin': 'Plugin',
'settings.pluginActivity.columns.action': 'Acción',
'settings.pluginActivity.columns.resource': 'Recurso',
'settings.pluginActivity.columns.when': 'Cuándo',
'settings.pluginActivity.columns.status': 'Resultado',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Mapa y plan',
'share.permBookings': 'Reservas',
'share.permPacking': 'Equipaje',
'share.permBudget': 'Presupuesto',
'share.permBudget': 'Costes',
'share.permCollab': 'Chat',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Plan',
'shared.tabBookings': 'Reservas',
'shared.tabPacking': 'Equipaje',
'shared.tabBudget': 'Presupuesto',
'shared.tabBudget': 'Costes',
'shared.tabChat': 'Chat',
'shared.days': 'días',
'shared.places': 'lugares',
'shared.other': 'Otro',
'shared.totalBudget': 'Presupuesto total',
'shared.totalBudget': 'Costes totales',
'shared.messages': 'mensajes',
'shared.sharedVia': 'Compartido vía',
'shared.confirmed': 'Confirmado',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Todo',
'todo.filter.open': 'Abierto',
'todo.filter.done': 'Hecho',
'todo.uncategorized': 'Sin categoría',
'todo.uncategorized': 'Sin lista',
'todo.namePlaceholder': 'Nombre de la tarea',
'todo.descriptionPlaceholder': 'Descripción (opcional)',
'todo.unassigned': 'Sin asignar',
'todo.noCategory': 'Sin categoría',
'todo.noCategory': 'Sin lista',
'todo.hasDescription': 'Con descripción',
'todo.addItem': 'Nueva tarea',
'todo.sidebar.sortBy': 'Ordenar por',
'todo.priority': 'Prioridad',
'todo.newCategoryLabel': 'nueva',
'todo.newCategory': 'Nombre de la categoría',
'todo.addCategory': 'Añadir categoría',
'todo.newCategory': 'Nombre de la lista',
'todo.addCategory': 'Añadir lista',
'todo.newItem': 'Nueva tarea',
'todo.empty': 'Aún no hay tareas. ¡Añade una tarea para empezar!',
'todo.filter.my': 'Mis tareas',
'todo.filter.overdue': 'Vencida',
'todo.sidebar.tasks': 'Tareas',
'todo.sidebar.categories': 'Categorías',
'todo.sidebar.categories': 'Listas',
'todo.detail.title': 'Tarea',
'todo.detail.description': 'Descripción',
'todo.detail.category': 'Categoría',
'todo.detail.category': 'Lista',
'todo.detail.dueDate': 'Fecha límite',
'todo.detail.assignedTo': 'Asignado a',
'todo.detail.delete': 'Eliminar',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'Equipaje',
'trip.tabs.lists': 'Listas',
'trip.tabs.listsShort': 'Listas',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'Costes',
'trip.tabs.files': 'Archivos',
'trip.loading': 'Cargando viaje...',
'trip.loadingPhotos': 'Cargando fotos de los lugares...',
@@ -32,7 +32,8 @@ export type NotificationEventKey =
| 'collab_message'
| 'packing_tagged'
| 'version_available'
| 'synology_session_cleared';
| 'synology_session_cleared'
| 'plugin_notification';
export interface NotificationLocale {
email: EmailStrings;
+89 -1
View File
@@ -169,6 +169,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': "Déposez un .zip de plugin pour l'installer",
'admin.plugins.uploaded': "Plugin “{name}” téléversé — activez-le pour l'exécuter",
'admin.plugins.sideloaded': 'Chargé manuellement',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Chargé depuis un dossier de build local et rechargé à chaud avec des données réelles — dev uniquement',
'admin.plugins.devLinkTitle': 'Lier un plugin local',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Lier',
'admin.plugins.devLinkLinked': "{id} lié — activez-le pour l'exécuter",
'admin.plugins.sideloadedHint': 'Téléversé manuellement — pas depuis le registre, non signé et non vérifié',
'admin.plugins.browse': 'Parcourir les plugins',
'admin.plugins.installed': 'Installé',
@@ -181,6 +188,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin désactivé',
'admin.plugins.viewErrors': "Voir le journal d'erreurs",
'admin.plugins.errorLog': "Journal d'erreurs",
'admin.plugins.allowedHosts': 'Hôtes autorisés',
'admin.plugins.allowedHosts.hint': 'Ce plugin communique avec un service que vous seul pouvez nommer (un serveur auto-hébergé). Ajoutez les hôtes qu’il peut joindre — il ne joindra aucun autre.',
'admin.plugins.allowedHosts.none': 'Aucun hôte ajouté pour l’instant.',
'admin.plugins.allowedHosts.unsupported': 'Ce plugin n’utilise pas d’hôtes fournis par l’opérateur. Ses hôtes autorisés sont fixés dans son manifeste.',
'admin.plugins.allowedHosts.restartNote': 'L’enregistrement redémarre le plugin pour qu’il prenne en compte la nouvelle liste.',
'admin.plugins.allowedHosts.add': 'Ajouter un hôte autorisé',
'admin.plugins.allowedHosts.count': '{n} hôte(s) autorisé(s)',
'admin.plugins.operatorEgressPill': '+ hôtes que vous ajoutez',
'admin.plugins.operatorEgressHint': 'Ce plugin communique avec un service que vous seul pouvez nommer (un serveur auto-hébergé). Après installation, ajoutez les hôtes qu’il peut joindre via ⋯ → Hôtes autorisés. Il ne joindra aucun autre.',
'admin.plugins.noErrors': 'Aucune erreur enregistrée.',
'admin.plugins.uninstalled': 'Plugin désinstallé',
'admin.plugins.uninstallTitle': 'Désinstaller le plugin ?',
@@ -222,6 +238,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Par utilisateur',
'admin.plugins.fieldRequired': 'Requis',
'admin.plugins.sourceRepo': 'Dépôt source',
'admin.plugins.reportIssue': 'Signaler un problème',
'admin.plugins.homepage': 'Site web',
'admin.plugins.requiresTrek': 'Nécessite TREK {version}+',
'admin.plugins.reviewedOn': 'Vérifié le {date}',
@@ -230,7 +247,52 @@ const admin: TranslationStrings = {
'admin.plugins.perm.db:read:users': 'Lire les informations de profil de base (nom, avatar — jamais les identifiants)',
'admin.plugins.perm.db:read:costs': "Lire les dépenses (postes de budget) accessibles à l'utilisateur actif",
'admin.plugins.perm.db:read:packing': "Lire les listes de bagages des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:write:packing':
"Créer, modifier et supprimer des articles de la liste de bagages sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.weather:read': "Lire les prévisions météo en cache de l'hôte (par coordonnées)",
'admin.plugins.perm.db:read:categories': 'Lire la liste globale des catégories de lieux',
'admin.plugins.perm.db:read:tags': "Lire les étiquettes de l'utilisateur actif",
'admin.plugins.perm.db:write:tags': "Créer, modifier et supprimer les étiquettes de l'utilisateur actif",
'admin.plugins.perm.db:read:todos': "Lire les tâches des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:write:todos':
"Créer, modifier et supprimer des tâches sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:atlas':
"Marquer des pays et régions comme visités et gérer la liste d'envies de l'utilisateur actif (nécessite le module Atlas)",
'admin.plugins.perm.db:write:vacay':
"Basculer les jours de congé et les fermetures d'entreprise sur le plan actif de l'utilisateur actif (nécessite le module Vacay)",
'admin.plugins.perm.db:write:journal':
"Créer, modifier et supprimer des entrées de journal dans les journaux que l'utilisateur actif peut modifier (nécessite le module Journey)",
'admin.plugins.perm.db:write:collections':
"Créer et modifier des collections et y enregistrer des lieux, avec le rôle de collection de l'utilisateur actif (nécessite le module Collections)",
'admin.plugins.perm.db:write:files':
"Joindre des fichiers aux voyages que l'utilisateur actif peut modifier (limite de 10 Mo, extensions bloquées refusées) et gérer leurs liens",
'admin.plugins.perm.db:write:collab':
"Publier des notes, des sondages et des messages de chat sur les voyages que l'utilisateur actif peut modifier (nécessite le module Collab)",
'admin.plugins.perm.db:write:members':
"Ajouter des utilisateurs aux voyages — accorde l'accès au voyage ; protégé par le droit de gestion des membres de l'utilisateur actif",
'admin.plugins.perm.notify:send':
"Envoyer une notification (boîte de réception + e-mail/ntfy) à l'utilisateur actif ou à un voyage auquel il appartient — jamais à un destinataire arbitraire",
'admin.plugins.perm.ai:invoke':
"Utiliser le fournisseur d'IA configuré par l'administrateur (complétion de texte + extraction de documents) au nom de l'utilisateur actif — le module ne détient jamais de clé",
'admin.plugins.perm.oauth:client':
"Se connecter à un service tiers en votre nom via OAuth géré par l'hôte (l'hôte détient les jetons, le module ne les voit jamais)",
'admin.plugins.perm.db:read:files': "Lire les fichiers des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:write:reservations':
"Créer, modifier et supprimer des réservations sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:accommodations':
"Créer, modifier et supprimer des hébergements sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:read:journal':
"Lire les journaux de voyage de l'utilisateur actif (nécessite le module Journey)",
'admin.plugins.perm.db:read:atlas':
"Lire les pays et régions visités par l'utilisateur actif (nécessite le module Atlas)",
'admin.plugins.perm.db:read:vacay': "Lire le plan de vacances de l'utilisateur actif (nécessite le module Vacay)",
'admin.plugins.perm.db:read:daynotes': "Lire les notes de journée des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:read:collections':
"Lire les collections de lieux enregistrés de l'utilisateur actif (nécessite le module Collections)",
'admin.plugins.perm.db:write:daynotes':
"Créer, modifier et supprimer des notes de journée sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.jobs:run':
"Exécuter ses tâches d'arrière-plan selon une planification (sans contexte utilisateur — ne peut pas lire les données utilisateur)",
'admin.plugins.perm.db:write:costs':
"Créer des dépenses (postes de budget) sur les voyages que l'utilisateur actif peut modifier",
'admin.plugins.perm.db:write:places':
@@ -250,9 +312,29 @@ const admin: TranslationStrings = {
'admin.plugins.perm.hook:place-detail-provider': 'Ajouter des détails supplémentaires (avis, notes, liens) à un lieu',
'admin.plugins.perm.hook:trip-warning-provider':
'Émettre des avertissements de validation sur un voyage (affichés dans le planificateur)',
'admin.plugins.perm.hook:table-contributor':
'Ajouter des colonnes et des actions aux vues du voyage (réservations, lieux, jours)',
'admin.plugins.perm.hook:map-marker-provider':
'Ajouter des marqueurs à la carte du voyage (p. ex. afficher réservations ou POIs)',
'admin.plugins.perm.hook:pdf-section-provider': "Ajouter des sections de texte à l'export PDF du voyage",
'admin.plugins.perm.hook:atlas-layer-provider':
"Mettre en évidence des pays sur la carte du monde Atlas (p. ex. listes d'envies ou avis aux voyageurs)",
'admin.plugins.perm.hook:journal-entry-provider':
'Ajouter des lignes supplémentaires (liens, statistiques) aux entrées du journal',
'admin.plugins.perm.hook:user-data':
"Effacer ou exporter les données qu'il stocke sur un utilisateur (RGPD : suppression de compte et demandes d'accès)",
'admin.plugins.perm.hook:trip-card-provider':
'Ajouter de petits badges (statut, compteurs) aux cartes de voyage du tableau de bord',
'admin.plugins.perm.hook:notification-channel': 'Envoyer vos notifications via un canal supplémentaire',
'admin.plugins.perm.events:subscribe':
'Réagir aux événements d’activité principaux (nom de l’événement et voyage uniquement, jamais le contenu)',
'admin.plugins.perm.http:outbound': 'Effectuer des requêtes sortantes vers ses hôtes déclarés',
'admin.plugins.perm.db:read:collab':
"Lire les notes, sondages et messages de chat des voyages accessibles à l'utilisateur actif (nécessite le module Collab)",
'admin.plugins.perm.db:read:files:content':
"Lire le contenu (octets) des fichiers des voyages accessibles à l'utilisateur actif",
'admin.plugins.perm.db:create:trips': "Créer de nouveaux voyages appartenant à l'utilisateur actif",
'admin.plugins.perm.rates:read': "Lire les taux de change en cache de l'hôte",
'admin.plugins.updateConsentTitle': 'Cette mise à jour nécessite de nouvelles permissions',
'admin.plugins.updateConsentBody':
"{name} v{version} demande des droits que vous n'avez pas encore accordés. La nouvelle version est installée mais reste désactivée jusqu'à ce que vous l'approuviez.",
@@ -296,6 +378,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Nom',
'admin.plugins.sortRecent': 'Récemment mis à jour',
'admin.plugins.sortUpdates': 'Mises à jour en premier',
'admin.plugins.sortDownloads': 'Plus téléchargés',
'admin.plugins.updatesAvailable': '{count} mises à jour disponibles pour vos plugins.',
'admin.plugins.updateAll': 'Tout mettre à jour',
'admin.plugins.noMatchInstalled': 'Aucun plugin installé ne correspond à votre recherche.',
@@ -316,7 +399,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Widget de tableau de bord',
'admin.plugins.cap.heroWidget': "Widget carte d'embarquement",
'admin.plugins.cap.placeSlot': 'Détail du lieu',
'admin.plugins.cap.daySlot': 'Détail du jour',
'admin.plugins.cap.reservationSlot': 'Détail de la réservation',
'admin.plugins.cap.replacesTabs': 'Remplace des onglets du planificateur',
'admin.plugins.cap.realtime': 'Mises à jour en temps réel',
'admin.plugins.cap.notificationChannel': 'Canal de notification',
'admin.plugins.cap.photos': 'Fournit des photos',
'admin.plugins.cap.calendar': 'Fournit des événements de calendrier',
'admin.plugins.cap.placeDetails': 'Enrichit les lieux',
@@ -342,6 +429,7 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Taille',
'admin.plugins.metaRequires': 'Nécessite',
'admin.plugins.metaReviewed': 'Vérifié le',
'admin.plugins.downloads': 'Téléchargements',
'admin.addons.title': 'Extensions',
'admin.addons.subtitle': 'Activez ou désactivez des fonctionnalités pour personnaliser votre expérience TREK.',
'admin.addons.catalog.memories.name': 'Photos (Immich)',
@@ -350,7 +438,7 @@ const admin: TranslationStrings = {
'admin.addons.catalog.mcp.description': "Protocole de contexte de modèle pour l'intégration d'assistants IA",
'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': 'Budget',
'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.documents.name': 'Documents',
'admin.addons.catalog.documents.description': 'Stockez et gérez vos documents de voyage',
@@ -52,6 +52,7 @@ const fr: NotificationLocale = {
title: 'Session Synology effacée',
body: 'Votre compte ou URL Synology a changé. Vous avez été déconnecté de Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Réinitialisez votre mot de passe',
+1 -1
View File
@@ -9,7 +9,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, itinéraires et regroupement',
'login.features.realtime': 'Synchronisation en temps réel',
'login.features.realtimeDesc': 'Planifiez ensemble via WebSocket',
'login.features.budget': 'Suivi du budget',
'login.features.budget': 'Suivi des coûts',
'login.features.budgetDesc': 'Catégories, graphiques et coûts par personne',
'login.features.collab': 'Collaboration',
'login.features.collabDesc': 'Multi-utilisateurs avec voyages partagés',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Voir',
'notif.action.accept': 'Accepter',
'notif.action.decline': 'Refuser',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Notification',
'notif.generic.text': 'Vous avez une nouvelle notification',
'notif.dev.unknown_event.title': '[DEV] Événement inconnu',
+6 -6
View File
@@ -21,7 +21,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Toutes les suggestions ajoutées',
'packing.allPacked': 'Tout est emballé !',
'packing.addPlaceholder': 'Ajouter un nouvel article…',
'packing.categoryPlaceholder': 'Catégorie…',
'packing.categoryPlaceholder': 'Liste...',
'packing.filterAll': 'Tous',
'packing.filterOpen': 'À faire',
'packing.filterDone': 'Fait',
@@ -31,11 +31,11 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Renommer',
'packing.menuCheckAll': 'Tout cocher',
'packing.menuUncheckAll': 'Tout décocher',
'packing.menuDeleteCat': 'Supprimer la catégorie',
'packing.menuDeleteCat': 'Supprimer la liste',
'packing.addItem': 'Ajouter un article',
'packing.addItemPlaceholder': "Nom de l'article...",
'packing.addCategory': 'Ajouter une catégorie',
'packing.newCategoryPlaceholder': 'Nom de catégorie (ex. Vêtements)',
'packing.addCategory': 'Ajouter une liste',
'packing.newCategoryPlaceholder': 'Nom de la liste (ex. Vêtements)',
'packing.applyTemplate': 'Appliquer un modèle',
'packing.template': 'Modèle',
'packing.templateApplied': '{count} articles ajoutés depuis le modèle',
@@ -49,9 +49,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Poids total',
'packing.bagName': 'Nom...',
'packing.addBag': 'Ajouter un bagage',
'packing.changeCategory': 'Changer de catégorie',
'packing.changeCategory': 'Déplacer vers une liste',
'packing.confirm.clearChecked': 'Voulez-vous vraiment supprimer {count} articles cochés ?',
'packing.confirm.deleteCat': 'Voulez-vous vraiment supprimer la catégorie « {name} » avec {count} articles ?',
'packing.confirm.deleteCat': 'Voulez-vous vraiment supprimer la liste « {name} » contenant {count} éléments ?',
'packing.defaultCategory': 'Autre',
'packing.toast.saveError': "Échec de l'enregistrement",
'packing.toast.deleteError': 'Échec de la suppression',
+27
View File
@@ -7,6 +7,15 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Carte',
'settings.tabs.notifications': 'Notifications',
'settings.tabs.integrations': 'Intégrations',
'settings.plugins.oauth.connected': 'Connecté',
'settings.plugins.oauth.notConnected': 'Non connecté',
'settings.plugins.oauth.connect': 'Connecter',
'settings.plugins.oauth.disconnect': 'Déconnecter',
'settings.tabs.plugins': 'Modules',
'settings.plugins.title': 'Paramètres des modules',
'settings.plugins.subtitle': 'Vos paramètres personnels pour les modules que vous utilisez (clés API, préférences).',
'settings.plugins.empty': 'Aucun module actif.',
'settings.plugins.saved': 'Paramètres enregistrés',
'settings.tabs.account': 'Compte',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'À propos',
@@ -285,6 +294,14 @@ const settings: TranslationStrings = {
'settings.ntfyUrl.testFailed': 'Échec de la notification de test Ntfy',
'settings.ntfyUrl.tokenCleared': "Jeton d'accès effacé",
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured': 'Pas encore configuré — à paramétrer dans les réglages du plugin',
'settings.plugins.actions': 'Actions',
'settings.plugins.actions.confirm': 'Exécuter cette action ?',
'settings.notificationPreferences.sendTest': 'Envoyer un test',
'settings.notificationPreferences.configure': 'Configurer',
'settings.notificationPreferences.testSuccess': 'Notification de test envoyée.',
'settings.notificationPreferences.testFailed': 'Échec du test.',
'settings.notificationPreferences.pluginConfigured': 'Configuré. Gérez vos identifiants sur la page de réglages du plugin.',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.ntfy': 'Ntfy',
@@ -474,6 +491,16 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'Synchronisation de {count}…',
'settings.offline.banner.failed': 'Échec de la synchronisation : {count}',
'settings.offline.banner.conflicts': 'Conflits : {count}',
'settings.pluginActivity.title': 'Activité des plugins',
'settings.pluginActivity.description':
"Chaque action qu'un plugin a effectuée en votre nom, la plus récente en premier.",
'settings.pluginActivity.empty': 'Aucune activité de plugin pour le moment.',
'settings.pluginActivity.refresh': 'Actualiser',
'settings.pluginActivity.columns.plugin': 'Plugin',
'settings.pluginActivity.columns.action': 'Action',
'settings.pluginActivity.columns.resource': 'Ressource',
'settings.pluginActivity.columns.when': 'Quand',
'settings.pluginActivity.columns.status': 'Résultat',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Carte et plan',
'share.permBookings': 'Réservations',
'share.permPacking': 'Bagages',
'share.permBudget': 'Budget',
'share.permBudget': 'Coûts',
'share.permCollab': 'Chat',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Plan',
'shared.tabBookings': 'Réservations',
'shared.tabPacking': 'Bagages',
'shared.tabBudget': 'Budget',
'shared.tabBudget': 'Coûts',
'shared.tabChat': 'Chat',
'shared.days': 'jours',
'shared.places': 'lieux',
'shared.other': 'Autre',
'shared.totalBudget': 'Budget total',
'shared.totalBudget': 'Coûts totaux',
'shared.messages': 'messages',
'shared.sharedVia': 'Partagé via',
'shared.confirmed': 'Confirmé',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Tout',
'todo.filter.open': 'En cours',
'todo.filter.done': 'Terminé',
'todo.uncategorized': 'Sans catégorie',
'todo.uncategorized': 'Aucune liste',
'todo.namePlaceholder': 'Nom de la tâche',
'todo.descriptionPlaceholder': 'Description (facultative)',
'todo.unassigned': 'Non assigné',
'todo.noCategory': 'Aucune catégorie',
'todo.noCategory': 'Aucune liste',
'todo.hasDescription': 'Avec description',
'todo.addItem': 'Nouvelle tâche',
'todo.sidebar.sortBy': 'Trier par',
'todo.priority': 'Priorité',
'todo.newCategoryLabel': 'nouvelle',
'todo.newCategory': 'Nom de la catégorie',
'todo.addCategory': 'Ajouter une catégorie',
'todo.newCategory': 'Nom de la liste',
'todo.addCategory': 'Ajouter une liste',
'todo.newItem': 'Nouvelle tâche',
'todo.empty': "Aucune tâche pour l'instant. Ajoutez une tâche pour commencer !",
'todo.filter.my': 'Mes tâches',
'todo.filter.overdue': 'En retard',
'todo.sidebar.tasks': 'Tâches',
'todo.sidebar.categories': 'Catégories',
'todo.sidebar.categories': 'Listes',
'todo.detail.title': 'Tâche',
'todo.detail.description': 'Description',
'todo.detail.category': 'Catégorie',
'todo.detail.category': 'Liste',
'todo.detail.dueDate': "Date d'échéance",
'todo.detail.assignedTo': 'Assigné à',
'todo.detail.delete': 'Supprimer',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'Bagages',
'trip.tabs.lists': 'Listes',
'trip.tabs.listsShort': 'Listes',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'Coûts',
'trip.tabs.files': 'Fichiers',
'trip.loading': 'Chargement du voyage…',
'trip.loadingPhotos': 'Chargement des photos des lieux...',
+93 -1
View File
@@ -214,6 +214,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Αποθέστε ένα .zip plugin για εγκατάσταση',
'admin.plugins.uploaded': 'Το plugin “{name}” μεταφορτώθηκε — ενεργοποιήστε το για εκτέλεση',
'admin.plugins.sideloaded': 'Φορτώθηκε χειροκίνητα',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Φορτώνεται από τοπικό κατάλογο build και επαναφορτώνεται ζωντανά με πραγματικά δεδομένα — μόνο για ανάπτυξη',
'admin.plugins.devLinkTitle': 'Σύνδεση τοπικού πρόσθετου',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Σύνδεση',
'admin.plugins.devLinkLinked': '{id} συνδέθηκε — ενεργοποιήστε το για εκτέλεση',
'admin.plugins.sideloadedHint': 'Μεταφορτώθηκε χειροκίνητα — όχι από το μητρώο, χωρίς υπογραφή και έλεγχο',
'admin.plugins.browse': 'Περιήγηση σε plugins',
'admin.plugins.installed': 'Εγκατεστημένο',
@@ -226,6 +233,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Το plugin απενεργοποιήθηκε',
'admin.plugins.viewErrors': 'Προβολή αρχείου σφαλμάτων',
'admin.plugins.errorLog': 'Αρχείο σφαλμάτων',
'admin.plugins.allowedHosts': 'Επιτρεπόμενοι κόμβοι',
'admin.plugins.allowedHosts.hint': 'Αυτό το πρόσθετο επικοινωνεί με μια υπηρεσία που μόνο εσείς μπορείτε να ονομάσετε (αυτο-φιλοξενούμενος διακομιστής). Προσθέστε τους κόμβους που μπορεί να προσεγγίσει — κανέναν άλλον.',
'admin.plugins.allowedHosts.none': 'Δεν έχουν προστεθεί κόμβοι ακόμη.',
'admin.plugins.allowedHosts.unsupported': 'Αυτό το πρόσθετο δεν χρησιμοποιεί κόμβους που ορίζει ο διαχειριστής. Οι επιτρεπόμενοι κόμβοι του είναι σταθεροί στο manifest.',
'admin.plugins.allowedHosts.restartNote': 'Η αποθήκευση επανεκκινεί το πρόσθετο ώστε να λάβει τη νέα λίστα.',
'admin.plugins.allowedHosts.add': 'Προσθήκη επιτρεπόμενου κόμβου',
'admin.plugins.allowedHosts.count': '{n} επιτρεπόμενοι κόμβοι',
'admin.plugins.operatorEgressPill': '+ κόμβοι που προσθέτετε',
'admin.plugins.operatorEgressHint': 'Αυτό το πρόσθετο επικοινωνεί με μια υπηρεσία που μόνο εσείς μπορείτε να ονομάσετε (αυτο-φιλοξενούμενος διακομιστής). Μετά την εγκατάσταση προσθέστε τους κόμβους στο ⋯ → Επιτρεπόμενοι κόμβοι. Κανέναν άλλον.',
'admin.plugins.noErrors': 'Δεν έχουν καταγραφεί σφάλματα.',
'admin.plugins.uninstalled': 'Το plugin απεγκαταστάθηκε',
'admin.plugins.uninstallTitle': 'Απεγκατάσταση plugin;',
@@ -267,6 +283,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Ανά χρήστη',
'admin.plugins.fieldRequired': 'Υποχρεωτικό',
'admin.plugins.sourceRepo': 'Αποθετήριο πηγαίου κώδικα',
'admin.plugins.reportIssue': 'Αναφορά προβλήματος',
'admin.plugins.homepage': 'Αρχική σελίδα',
'admin.plugins.requiresTrek': 'Απαιτεί TREK {version}+',
'admin.plugins.reviewedOn': 'Ελέγχθηκε {date}',
@@ -276,7 +293,55 @@ const admin: TranslationStrings = {
'admin.plugins.perm.db:read:costs':
'Ανάγνωση εξόδων (στοιχεία προϋπολογισμού) στα οποία έχει πρόσβαση ο ενεργός χρήστης',
'admin.plugins.perm.db:read:packing': 'Ανάγνωση λιστών αποσκευών ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
'admin.plugins.perm.db:write:packing':
'Δημιουργία, επεξεργασία και διαγραφή αντικειμένων λίστας αποσκευών σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
'admin.plugins.perm.weather:read':
'Ανάγνωση της προσωρινά αποθηκευμένης πρόγνωσης καιρού του κεντρικού υπολογιστή (ανά συντεταγμένες)',
'admin.plugins.perm.db:read:categories': 'Ανάγνωση της γενικής λίστας κατηγοριών τοποθεσιών',
'admin.plugins.perm.db:read:tags': 'Ανάγνωση των ετικετών του ενεργού χρήστη',
'admin.plugins.perm.db:write:tags': 'Δημιουργία, επεξεργασία και διαγραφή των ετικετών του ενεργού χρήστη',
'admin.plugins.perm.db:read:todos': 'Ανάγνωση των εργασιών ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
'admin.plugins.perm.db:write:todos':
'Δημιουργία, επεξεργασία και διαγραφή εργασιών σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
'admin.plugins.perm.db:write:atlas':
'Επισήμανση χωρών και περιοχών ως επισκεφθέντων και διαχείριση της λίστας επιθυμιών του ενεργού χρήστη (απαιτεί το πρόσθετο Atlas)',
'admin.plugins.perm.db:write:vacay':
'Εναλλαγή ημερών άδειας και εταιρικών αργιών στο ενεργό πλάνο του ενεργού χρήστη (απαιτεί το πρόσθετο Vacay)',
'admin.plugins.perm.db:write:journal':
'Δημιουργία, επεξεργασία και διαγραφή καταχωρήσεων σε ημερολόγια που μπορεί να επεξεργαστεί ο ενεργός χρήστης (απαιτεί το πρόσθετο Journey)',
'admin.plugins.perm.db:write:collections':
'Δημιουργία και επεξεργασία συλλογών και αποθήκευση τοποθεσιών σε αυτές, με τον ρόλο συλλογής του ενεργού χρήστη (απαιτεί το πρόσθετο Collections)',
'admin.plugins.perm.db:write:files':
'Επισύναψη αρχείων σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης (όριο 10 MB, αποκλεισμένες επεκτάσεις απορρίπτονται) και διαχείριση των συνδέσμων τους',
'admin.plugins.perm.db:write:collab':
'Δημοσίευση σημειώσεων, ψηφοφοριών και μηνυμάτων συνομιλίας σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης (απαιτεί το πρόσθετο Collab)',
'admin.plugins.perm.db:write:members':
'Προσθήκη χρηστών σε ταξίδια — παρέχει πρόσβαση στο ταξίδι· προστατεύεται από το δικαίωμα διαχείρισης μελών του ενεργού χρήστη',
'admin.plugins.perm.notify:send':
'Αποστολή ειδοποίησης (εισερχόμενα κουδουνιού + email/ntfy) στον ενεργό χρήστη ή σε ταξίδι στο οποίο ανήκει — ποτέ σε αυθαίρετο παραλήπτη',
'admin.plugins.perm.ai:invoke':
'Χρήση του παρόχου AI που ρύθμισε ο διαχειριστής (συμπλήρωση κειμένου + εξαγωγή εγγράφων) εκ μέρους του ενεργού χρήστη — το πρόσθετο δεν κρατά ποτέ κλειδί',
'admin.plugins.perm.oauth:client':
'Σύνδεση σε υπηρεσία τρίτου εκ μέρους σας μέσω OAuth με διαμεσολάβηση του κεντρικού υπολογιστή (ο κεντρικός υπολογιστής κρατά τα διακριτικά, το πρόσθετο δεν τα βλέπει ποτέ)',
'admin.plugins.perm.db:read:files': 'Ανάγνωση αρχείων ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
'admin.plugins.perm.db:write:reservations':
'Δημιουργία, επεξεργασία και διαγραφή κρατήσεων σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
'admin.plugins.perm.db:write:accommodations':
'Δημιουργία, επεξεργασία και διαγραφή καταλυμάτων σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
'admin.plugins.perm.db:read:journal':
'Ανάγνωση των ημερολογίων ταξιδιού του ενεργού χρήστη (απαιτεί το πρόσθετο Journey)',
'admin.plugins.perm.db:read:atlas':
'Ανάγνωση των χωρών και περιοχών που έχει επισκεφθεί ο ενεργός χρήστης (απαιτεί το πρόσθετο Atlas)',
'admin.plugins.perm.db:read:vacay':
'Ανάγνωση του προγράμματος διακοπών του ενεργού χρήστη (απαιτεί το πρόσθετο Vacay)',
'admin.plugins.perm.db:read:daynotes':
'Ανάγνωση των σημειώσεων ημέρας ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
'admin.plugins.perm.db:read:collections':
'Ανάγνωση των συλλογών αποθηκευμένων τοποθεσιών του ενεργού χρήστη (απαιτεί το πρόσθετο Collections)',
'admin.plugins.perm.db:write:daynotes':
'Δημιουργία, επεξεργασία και διαγραφή σημειώσεων ημέρας σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
'admin.plugins.perm.jobs:run':
'Εκτέλεση των δηλωμένων εργασιών παρασκηνίου βάσει προγράμματος (χωρίς περιβάλλον χρήστη — δεν μπορεί να διαβάσει δεδομένα χρήστη)',
'admin.plugins.perm.db:write:costs':
'Δημιουργία εξόδων (στοιχεία προϋπολογισμού) σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
'admin.plugins.perm.db:write:places':
@@ -297,9 +362,30 @@ const admin: TranslationStrings = {
'Συνεισφορά επιπλέον λεπτομερειών (κριτικές, βαθμολογίες, σύνδεσμοι) σε ένα μέρος',
'admin.plugins.perm.hook:trip-warning-provider':
'Εμφάνιση προειδοποιήσεων επικύρωσης σε ένα ταξίδι (εμφανίζονται στον σχεδιαστή)',
'admin.plugins.perm.hook:table-contributor':
'Προσθήκη στηλών και ενεργειών στις προβολές του ταξιδιού (κρατήσεις, τοποθεσίες, ημέρες)',
'admin.plugins.perm.hook:map-marker-provider':
'Προσθήκη δεικτών στον χάρτη του ταξιδιού (π.χ. εμφάνιση κρατήσεων ή POI)',
'admin.plugins.perm.hook:pdf-section-provider': 'Προσθήκη ενοτήτων κειμένου στην εξαγωγή PDF του ταξιδιού',
'admin.plugins.perm.hook:atlas-layer-provider':
'Επισήμανση χωρών στον παγκόσμιο χάρτη του Άτλαντα (π.χ. λίστες επιθυμιών ή ταξιδιωτικές οδηγίες)',
'admin.plugins.perm.hook:journal-entry-provider':
'Προσθήκη επιπλέον γραμμών (σύνδεσμοι, στατιστικά) στις καταχωρίσεις του ημερολογίου',
'admin.plugins.perm.hook:user-data':
'Διαγραφή ή εξαγωγή των δεδομένων που αποθηκεύει για έναν χρήστη (GDPR: διαγραφή λογαριασμού και αιτήματα δεδομένων)',
'admin.plugins.perm.hook:trip-card-provider':
'Προσθήκη μικρών σημάτων (κατάσταση, μετρήσεις) στις κάρτες ταξιδιού του πίνακα',
'admin.plugins.perm.hook:notification-channel': 'Παράδοση των ειδοποιήσεών σας μέσω πρόσθετου καναλιού',
'admin.plugins.perm.events:subscribe':
'Αντιδρά σε βασικά συμβάντα δραστηριότητας (όνομα συμβάντος + ταξίδι μόνο, ποτέ το περιεχόμενο)',
'admin.plugins.perm.http:outbound': 'Εκτέλεση εξερχόμενων αιτημάτων προς τους δηλωμένους hosts του',
'admin.plugins.perm.db:read:collab':
'Ανάγνωση σημειώσεων, ψηφοφοριών και μηνυμάτων συνομιλίας ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης (απαιτεί το πρόσθετο Collab)',
'admin.plugins.perm.db:read:files:content':
'Ανάγνωση του περιεχομένου (bytes) των αρχείων ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
'admin.plugins.perm.db:create:trips': 'Δημιουργία νέων ταξιδιών που ανήκουν στον ενεργό χρήστη',
'admin.plugins.perm.rates:read':
'Ανάγνωση των προσωρινά αποθηκευμένων συναλλαγματικών ισοτιμιών του κεντρικού υπολογιστή',
'admin.plugins.updateConsentTitle': 'Αυτή η ενημέρωση χρειάζεται νέα δικαιώματα',
'admin.plugins.updateConsentBody':
'Το {name} v{version} ζητά δικαιώματα που δεν έχετε ακόμη παραχωρήσει. Η νέα έκδοση έχει εγκατασταθεί αλλά παραμένει ανενεργή μέχρι να την εγκρίνετε.',
@@ -342,6 +428,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Όνομα',
'admin.plugins.sortRecent': 'Πρόσφατα ενημερωμένα',
'admin.plugins.sortUpdates': 'Ενημερώσεις πρώτα',
'admin.plugins.sortDownloads': 'Περισσότερες λήψεις',
'admin.plugins.updatesAvailable': 'Διαθέσιμες {count} ενημερώσεις για τα plugins σας.',
'admin.plugins.updateAll': 'Ενημέρωση όλων',
'admin.plugins.noMatchInstalled': 'Κανένα εγκατεστημένο plugin δεν ταιριάζει με την αναζήτησή σας.',
@@ -362,7 +449,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Widget πίνακα',
'admin.plugins.cap.heroWidget': 'Widget κάρτας επιβίβασης',
'admin.plugins.cap.placeSlot': 'Λεπτομέρειες τόπου',
'admin.plugins.cap.daySlot': 'Λεπτομέρειες ημέρας',
'admin.plugins.cap.reservationSlot': 'Λεπτομέρειες κράτησης',
'admin.plugins.cap.replacesTabs': 'Αντικαθιστά καρτέλες του σχεδιαστή',
'admin.plugins.cap.realtime': 'Ενημερώσεις σε πραγματικό χρόνο',
'admin.plugins.cap.notificationChannel': 'Κανάλι ειδοποιήσεων',
'admin.plugins.cap.photos': 'Παρέχει φωτογραφίες',
'admin.plugins.cap.calendar': 'Παρέχει συμβάντα ημερολογίου',
'admin.plugins.cap.placeDetails': 'Εμπλουτίζει μέρη',
@@ -388,12 +479,13 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Μέγεθος',
'admin.plugins.metaRequires': 'Απαιτεί',
'admin.plugins.metaReviewed': 'Ελέγχθηκε στις',
'admin.plugins.downloads': 'Λήψεις',
'admin.addons.title': 'Πρόσθετα',
'admin.addons.subtitle':
'Ενεργοποιήστε ή απενεργοποιήστε λειτουργίες για να εξατομικεύσετε την εμπειρία σας στο TREK.',
'admin.addons.catalog.packing.name': 'Λίστες',
'admin.addons.catalog.packing.description': 'Λίστες πακεταρίσματος και εργασίες προς εκτέλεση για τα ταξίδια σας',
'admin.addons.catalog.budget.name': 'Προϋπολογισμός',
'admin.addons.catalog.budget.name': 'Κόστη',
'admin.addons.catalog.budget.description': 'Παρακολουθήστε τα έξοδα και σχεδιάστε τον προϋπολογισμό του ταξιδιού σας',
'admin.addons.catalog.documents.name': 'Έγγραφα',
'admin.addons.catalog.documents.description': 'Αποθηκεύστε και διαχειριστείτε ταξιδιωτικά έγγραφα',
@@ -52,6 +52,7 @@ const gr: NotificationLocale = {
title: 'Η σύνδεση Synology τερματίστηκε',
body: 'Ο λογαριασμός σας Synology ή το URL άλλαξε. Έχετε αποσυνδεθεί από το Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Επαναφορά κωδικού πρόσβασης',
+1 -1
View File
@@ -9,7 +9,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, διαδρομές & ομαδοποίηση',
'login.features.realtime': 'Συγχρονισμός Πραγματικού Χρόνου',
'login.features.realtimeDesc': 'Σχεδιάστε μαζί μέσω WebSocket',
'login.features.budget': 'Παρακολούθηση Προϋπολογισμού',
'login.features.budget': 'Παρακολούθηση Κόστους',
'login.features.budgetDesc': 'Κατηγορίες, γραφήματα & κόστος ανά άτομο',
'login.features.collab': 'Συνεργασία',
'login.features.collabDesc': 'Πολλαπλοί χρήστες με κοινόχρηστα ταξίδια',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Προβολή',
'notif.action.accept': 'Αποδοχή',
'notif.action.decline': 'Απόρριψη',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Ειδοποίηση',
'notif.generic.text': 'Έχετε μια νέα ειδοποίηση',
'notif.dev.unknown_event.title': '[DEV] Άγνωστο Συμβάν',
+6 -6
View File
@@ -22,7 +22,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Όλες οι προτάσεις προστέθηκαν',
'packing.allPacked': 'Όλα έτοιμα!',
'packing.addPlaceholder': 'Προσθήκη νέου αντικειμένου...',
'packing.categoryPlaceholder': 'Κατηγορία...',
'packing.categoryPlaceholder': 'Λίστα...',
'packing.filterAll': 'Όλα',
'packing.filterOpen': 'Ανοιχτά',
'packing.filterDone': 'Ολοκληρωμένα',
@@ -32,12 +32,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Μετονομασία',
'packing.menuCheckAll': 'Επιλογή Όλων',
'packing.menuUncheckAll': 'Αποεπιλογή Όλων',
'packing.menuDeleteCat': 'Διαγραφή Κατηγορίας',
'packing.menuDeleteCat': 'Διαγραφή λίστας',
'packing.noMembers': 'Δεν υπάρχουν μέλη ταξιδιού',
'packing.addItem': 'Προσθήκη αντικειμένου',
'packing.addItemPlaceholder': 'Όνομα αντικειμένου...',
'packing.addCategory': 'Προσθήκη κατηγορίας',
'packing.newCategoryPlaceholder': 'Όνομα κατηγορίας (π.χ. Ρούχα)',
'packing.addCategory': 'Προσθήκη λίστας',
'packing.newCategoryPlaceholder': 'Όνομα λίστας (π.χ. Ρούχα)',
'packing.applyTemplate': 'Εφαρμογή προτύπου',
'packing.template': 'Πρότυπο',
'packing.templateApplied': '{count} αντικείμενα προστέθηκαν από το πρότυπο',
@@ -50,9 +50,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Συνολικό βάρος',
'packing.bagName': 'Όνομα τσάντας...',
'packing.addBag': 'Προσθήκη τσάντας',
'packing.changeCategory': 'Αλλαγή Κατηγορίας',
'packing.changeCategory': 'Μετακίνηση σε λίστα',
'packing.confirm.clearChecked': 'Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count} επιλεγμένα αντικείμενα;',
'packing.confirm.deleteCat': 'Είστε σίγουροι ότι θέλετε να διαγράψετε την κατηγορία "{name}" με {count} αντικείμενα;',
'packing.confirm.deleteCat': 'Είστε βέβαιοι ότι θέλετε να διαγράψετε τη λίστα "{name}" με {count} στοιχεία;',
'packing.defaultCategory': 'Άλλα',
'packing.toast.saveError': 'Αποτυχία αποθήκευσης',
'packing.toast.deleteError': 'Αποτυχία διαγραφής',
+29
View File
@@ -7,6 +7,16 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Χάρτης',
'settings.tabs.notifications': 'Ειδοποιήσεις',
'settings.tabs.integrations': 'Ενσωματώσεις',
'settings.plugins.oauth.connected': 'Συνδεδεμένο',
'settings.plugins.oauth.notConnected': 'Μη συνδεδεμένο',
'settings.plugins.oauth.connect': 'Σύνδεση',
'settings.plugins.oauth.disconnect': 'Αποσύνδεση',
'settings.tabs.plugins': 'Πρόσθετα',
'settings.plugins.title': 'Ρυθμίσεις προσθέτων',
'settings.plugins.subtitle':
'Οι προσωπικές σας ρυθμίσεις για τα πρόσθετα που χρησιμοποιείτε (κλειδιά API, προτιμήσεις).',
'settings.plugins.empty': 'Δεν υπάρχουν ενεργά πρόσθετα.',
'settings.plugins.saved': 'Οι ρυθμίσεις αποθηκεύτηκαν',
'settings.tabs.account': 'Λογαριασμός',
'settings.tabs.offline': 'Εκτός σύνδεσης',
'settings.tabs.about': 'Σχετικά',
@@ -86,6 +96,15 @@ const settings: TranslationStrings = {
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.inapp': 'Εντός εφαρμογής',
'settings.notificationPreferences.notConfigured':
'Δεν έχει ρυθμιστεί ακόμη — ρυθμίστε το στις ρυθμίσεις του πρόσθετου',
'settings.plugins.actions': 'Ενέργειες',
'settings.plugins.actions.confirm': 'Εκτέλεση αυτής της ενέργειας;',
'settings.notificationPreferences.sendTest': 'Αποστολή δοκιμής',
'settings.notificationPreferences.configure': 'Ρύθμιση',
'settings.notificationPreferences.testSuccess': 'Στάλθηκε δοκιμαστική ειδοποίηση.',
'settings.notificationPreferences.testFailed': 'Η δοκιμή απέτυχε.',
'settings.notificationPreferences.pluginConfigured': 'Ρυθμίστηκε. Διαχειριστείτε τα διαπιστευτήρια στη σελίδα ρυθμίσεων του πρόσθετου.',
'settings.notificationPreferences.ntfy': 'Ntfy',
'settings.notificationPreferences.noChannels':
'Δεν έχουν διαμορφωθεί κανάλια ειδοποιήσεων. Ζητήστε από έναν διαχειριστή να ρυθμίσει ειδοποιήσεις email ή webhook.',
@@ -475,6 +494,16 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'Συγχρονισμός {count}…',
'settings.offline.banner.failed': 'Αποτυχία συγχρονισμού: {count}',
'settings.offline.banner.conflicts': 'Διενέξεις: {count}',
'settings.pluginActivity.title': 'Δραστηριότητα προσθέτων',
'settings.pluginActivity.description':
'Κάθε ενέργεια που εκτέλεσε ένα πρόσθετο εκ μέρους σας, με τις πιο πρόσφατες πρώτες.',
'settings.pluginActivity.empty': 'Δεν υπάρχει ακόμη δραστηριότητα προσθέτων.',
'settings.pluginActivity.refresh': 'Ανανέωση',
'settings.pluginActivity.columns.plugin': 'Πρόσθετο',
'settings.pluginActivity.columns.action': 'Ενέργεια',
'settings.pluginActivity.columns.resource': 'Πόρος',
'settings.pluginActivity.columns.when': 'Πότε',
'settings.pluginActivity.columns.status': 'Αποτέλεσμα',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Χάρτης & Σχέδιο',
'share.permBookings': 'Κρατήσεις',
'share.permPacking': 'Πακετάρισμα',
'share.permBudget': 'Προϋπολογισμός',
'share.permBudget': 'Κόστη',
'share.permCollab': 'Συνομιλία',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Σχέδιο',
'shared.tabBookings': 'Κρατήσεις',
'shared.tabPacking': 'Πακετάρισμα',
'shared.tabBudget': 'Προϋπολογισμός',
'shared.tabBudget': 'Κόστη',
'shared.tabChat': 'Συνομιλία',
'shared.days': 'ημέρες',
'shared.places': 'τόποι',
'shared.other': 'Άλλα',
'shared.totalBudget': 'Συνολικός Προϋπολογισμός',
'shared.totalBudget': 'Συνολικά Κόστη',
'shared.messages': 'μηνύματα',
'shared.sharedVia': 'Κοινοποιήθηκε μέσω',
'shared.confirmed': 'Επιβεβαιωμένο',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Όλα',
'todo.filter.open': 'Ανοιχτά',
'todo.filter.done': 'Ολοκληρωμένα',
'todo.uncategorized': 'Χωρίς κατηγορία',
'todo.uncategorized': 'Χωρίς λίστα',
'todo.namePlaceholder': 'Όνομα εργασίας',
'todo.descriptionPlaceholder': 'Περιγραφή (προαιρετικό)',
'todo.unassigned': 'Χωρίς ανάθεση',
'todo.noCategory': 'Χωρίς κατηγορία',
'todo.noCategory': 'Χωρίς λίστα',
'todo.hasDescription': 'Έχει περιγραφή',
'todo.addItem': 'Προσθήκη νέας εργασίας',
'todo.sidebar.sortBy': 'Ταξινόμηση κατά',
'todo.priority': 'Προτεραιότητα',
'todo.newCategoryLabel': 'νέο',
'todo.newCategory': 'Όνομα κατηγορίας',
'todo.addCategory': 'Προσθήκη κατηγορίας',
'todo.newCategory': 'Όνομα λίστας',
'todo.addCategory': 'Προσθήκη λίστας',
'todo.newItem': 'Νέα εργασία',
'todo.empty': 'Δεν υπάρχουν εργασίες ακόμα. Προσθέστε μία για να ξεκινήσετε!',
'todo.filter.my': 'Οι Εργασίες μου',
'todo.filter.overdue': 'Εκπρόθεσμα',
'todo.sidebar.tasks': 'Εργασίες',
'todo.sidebar.categories': 'Κατηγορίες',
'todo.sidebar.categories': 'Λίστες',
'todo.detail.title': 'Εργασία',
'todo.detail.description': 'Περιγραφή',
'todo.detail.category': 'Κατηγορία',
'todo.detail.category': 'Λίστα',
'todo.detail.dueDate': 'Ημερομηνία λήξης',
'todo.detail.assignedTo': 'Ανατεθειμένο σε',
'todo.detail.delete': 'Διαγραφή',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'Αποσκευές',
'trip.tabs.lists': 'Λίστες',
'trip.tabs.listsShort': 'Λίστες',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'Κόστη',
'trip.tabs.files': 'Αρχεία',
'trip.loading': 'Φόρτωση ταξιδιού...',
'trip.loadingPhotos': 'Φόρτωση φωτογραφιών μέρους...',
+91 -1
View File
@@ -168,6 +168,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Húzz ide egy plugin .zip fájlt a telepítéshez',
'admin.plugins.uploaded': 'A(z) “{name}” plugin feltöltve — aktiváld a futtatáshoz',
'admin.plugins.sideloaded': 'Manuálisan betöltve',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Helyi build könyvtárból betöltve és valós adatokkal élőben újratöltve — csak fejlesztéshez',
'admin.plugins.devLinkTitle': 'Helyi bővítmény csatolása',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Csatolás',
'admin.plugins.devLinkLinked': '{id} csatolva — aktiváld a futtatáshoz',
'admin.plugins.sideloadedHint': 'Manuálisan feltöltve — nem a tárból, aláíratlan és ellenőrizetlen',
'admin.plugins.browse': 'Pluginok böngészése',
'admin.plugins.installed': 'Telepítve',
@@ -180,6 +187,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin deaktiválva',
'admin.plugins.viewErrors': 'Hibanapló megtekintése',
'admin.plugins.errorLog': 'Hibanapló',
'admin.plugins.allowedHosts': 'Engedélyezett hosztok',
'admin.plugins.allowedHosts.hint': 'Ez a bővítmény olyan szolgáltatással kommunikál, amelyet csak te tudsz megnevezni (saját üzemeltetésű kiszolgáló). Add meg a hosztokat, amelyeket elérhet — másokat nem.',
'admin.plugins.allowedHosts.none': 'Még nincs hozzáadott hoszt.',
'admin.plugins.allowedHosts.unsupported': 'Ez a bővítmény nem használ üzemeltető által megadott hosztokat. Az engedélyezett hosztjai a manifestben rögzítettek.',
'admin.plugins.allowedHosts.restartNote': 'A mentés újraindítja a bővítményt, hogy átvegye az új listát.',
'admin.plugins.allowedHosts.add': 'Engedélyezett hoszt hozzáadása',
'admin.plugins.allowedHosts.count': '{n} engedélyezett hoszt',
'admin.plugins.operatorEgressPill': '+ általad hozzáadott hosztok',
'admin.plugins.operatorEgressHint': 'Ez a bővítmény olyan szolgáltatással kommunikál, amelyet csak te tudsz megnevezni (saját üzemeltetésű kiszolgáló). Telepítés után add meg a hosztokat a ⋯ → Engedélyezett hosztok alatt. Másokat nem ér el.',
'admin.plugins.noErrors': 'Nincsenek naplózott hibák.',
'admin.plugins.uninstalled': 'Plugin eltávolítva',
'admin.plugins.uninstallTitle': 'Plugin eltávolítása?',
@@ -221,6 +237,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Felhasználónként',
'admin.plugins.fieldRequired': 'Kötelező',
'admin.plugins.sourceRepo': 'Forráskód-tároló',
'admin.plugins.reportIssue': 'Probléma jelentése',
'admin.plugins.homepage': 'Honlap',
'admin.plugins.requiresTrek': 'TREK {version}+ szükséges',
'admin.plugins.reviewedOn': 'Ellenőrizve: {date}',
@@ -230,7 +247,54 @@ const admin: TranslationStrings = {
'admin.plugins.perm.db:read:costs': 'Az aktuális felhasználó számára elérhető költségek (költségtételek) olvasása',
'admin.plugins.perm.db:read:packing':
'Az aktuális felhasználó számára elérhető utazások csomagolási listáinak olvasása',
'admin.plugins.perm.db:write:packing':
'Csomagolási lista tételek létrehozása, szerkesztése és törlése az aktuális felhasználó által szerkeszthető utazásokon',
'admin.plugins.perm.weather:read':
'A gazdagép gyorsítótárazott időjárás-előrejelzésének olvasása (koordináták szerint)',
'admin.plugins.perm.db:read:categories': 'A globális helykategória-lista olvasása',
'admin.plugins.perm.db:read:tags': 'Az aktuális felhasználó címkéinek olvasása',
'admin.plugins.perm.db:write:tags': 'Az aktuális felhasználó címkéinek létrehozása, szerkesztése és törlése',
'admin.plugins.perm.db:read:todos': 'Az aktuális felhasználó számára elérhető utazások teendőinek olvasása',
'admin.plugins.perm.db:write:todos':
'Teendők létrehozása, szerkesztése és törlése az aktuális felhasználó által szerkeszthető utazásokon',
'admin.plugins.perm.db:write:atlas':
'Országok és régiók meglátogatottként jelölése és az aktuális felhasználó bakancslistájának kezelése (az Atlas bővítmény szükséges)',
'admin.plugins.perm.db:write:vacay':
'Szabadságnapok és céges ünnepek átkapcsolása az aktuális felhasználó aktív tervén (a Vacay bővítmény szükséges)',
'admin.plugins.perm.db:write:journal':
'Naplóbejegyzések létrehozása, szerkesztése és törlése az aktuális felhasználó által szerkeszthető naplókban (a Journey bővítmény szükséges)',
'admin.plugins.perm.db:write:collections':
'Gyűjtemények létrehozása és szerkesztése, helyek mentése beléjük, az aktuális felhasználó gyűjteményszerepével (a Collections bővítmény szükséges)',
'admin.plugins.perm.db:write:files':
'Fájlok csatolása az aktuális felhasználó által szerkeszthető utazásokhoz (10 MB-os korlát, tiltott kiterjesztések elutasítva), és hivatkozásaik kezelése',
'admin.plugins.perm.db:write:collab':
'Jegyzetek, szavazások és csevegőüzenetek közzététele az aktuális felhasználó által szerkeszthető utazásokon (a Collab bővítmény szükséges)',
'admin.plugins.perm.db:write:members':
'Felhasználók hozzáadása utazásokhoz — utazás-hozzáférést ad; az aktuális felhasználó tagkezelési jogával védve',
'admin.plugins.perm.notify:send':
'Értesítés küldése (csengő postaláda + e-mail/ntfy) az aktuális felhasználónak vagy egy utazásának — soha tetszőleges címzettnek',
'admin.plugins.perm.ai:invoke':
'Az adminisztrátor által beállított MI-szolgáltató (szövegkiegészítés + dokumentumkinyerés) használata az aktuális felhasználó nevében — a bővítmény soha nem tárol kulcsot',
'admin.plugins.perm.oauth:client':
'Csatlakozás egy harmadik féltől származó szolgáltatáshoz a nevedben, gazdagép által közvetített OAuth-tal (a tokeneket a gazdagép tárolja, a bővítmény sosem látja)',
'admin.plugins.perm.db:read:files': 'Az aktuális felhasználó számára elérhető utazások fájljainak olvasása',
'admin.plugins.perm.db:write:reservations':
'Foglalások létrehozása, szerkesztése és törlése az aktuális felhasználó által szerkeszthető utazásokon',
'admin.plugins.perm.db:write:accommodations':
'Szállások létrehozása, szerkesztése és törlése az aktuális felhasználó által szerkeszthető utazásokon',
'admin.plugins.perm.db:read:journal':
'Az aktuális felhasználó úti naplóinak olvasása (a Journey bővítmény szükséges)',
'admin.plugins.perm.db:read:atlas':
'Az aktuális felhasználó által meglátogatott országok és régiók olvasása (az Atlas bővítmény szükséges)',
'admin.plugins.perm.db:read:vacay':
'Az aktuális felhasználó nyaralási tervének olvasása (a Vacay bővítmény szükséges)',
'admin.plugins.perm.db:read:daynotes': 'Az aktuális felhasználó számára elérhető utazások napi jegyzeteinek olvasása',
'admin.plugins.perm.db:read:collections':
'Az aktuális felhasználó mentett helyeinek gyűjteményeit olvasása (a Collections bővítmény szükséges)',
'admin.plugins.perm.db:write:daynotes':
'Napi jegyzetek létrehozása, szerkesztése és törlése az aktuális felhasználó által szerkeszthető utazásokon',
'admin.plugins.perm.jobs:run':
'A deklarált háttérfeladatok ütemezett futtatása (felhasználói környezet nélkül — nem olvashat felhasználói adatokat)',
'admin.plugins.perm.db:write:costs':
'Költségek (költségtételek) létrehozása az aktuális felhasználó által szerkeszthető utazásokon',
'admin.plugins.perm.db:write:places':
@@ -251,9 +315,29 @@ const admin: TranslationStrings = {
'További részletek (értékelések, minősítések, hivatkozások) hozzáadása egy helyhez',
'admin.plugins.perm.hook:trip-warning-provider':
'Ellenőrzési figyelmeztetések megjelenítése egy utazáson (a tervezőben látható)',
'admin.plugins.perm.hook:table-contributor':
'Oszlopok és műveletek hozzáadása az utazási nézetekhez (foglalások, helyek, napok)',
'admin.plugins.perm.hook:map-marker-provider':
'Jelölők hozzáadása az utazás térképéhez (pl. foglalások vagy POI-k megjelenítése)',
'admin.plugins.perm.hook:pdf-section-provider': 'Szöveges szakaszok hozzáfűzése az utazás PDF-exportjához',
'admin.plugins.perm.hook:atlas-layer-provider':
'Országok kiemelése az Atlas világtérképén (pl. kívánságlisták vagy utazási figyelmeztetések)',
'admin.plugins.perm.hook:journal-entry-provider':
'További sorok (hivatkozások, statisztikák) hozzáadása a naplóbejegyzésekhez',
'admin.plugins.perm.hook:user-data':
'A felhasználóról tárolt adatok törlése vagy exportálása (GDPR: fióktörlés és adatkérések)',
'admin.plugins.perm.hook:trip-card-provider':
'Kis jelvények (állapot, számlálók) hozzáadása az irányítópult utazáskártyáihoz',
'admin.plugins.perm.hook:notification-channel': 'Értesítéseid kézbesítése egy további csatornán',
'admin.plugins.perm.events:subscribe':
'Reagál az alapvető tevékenységi eseményekre (csak eseménynév + utazás, soha nem a tartalom)',
'admin.plugins.perm.http:outbound': 'Kimenő kérések küldése a bejelentett kiszolgálók felé',
'admin.plugins.perm.db:read:collab':
'Jegyzetek, szavazások és csevegőüzenetek olvasása az aktuális felhasználó számára elérhető utazásokon (a Collab bővítmény szükséges)',
'admin.plugins.perm.db:read:files:content':
'Az aktuális felhasználó számára elérhető utazások fájltartalmának (bájtok) olvasása',
'admin.plugins.perm.db:create:trips': 'Új utazások létrehozása az aktuális felhasználó tulajdonában',
'admin.plugins.perm.rates:read': 'A gazdagép gyorsítótárazott devizaárfolyamainak olvasása',
'admin.plugins.updateConsentTitle': 'Ez a frissítés új engedélyeket igényel',
'admin.plugins.updateConsentBody':
'A(z) {name} v{version} olyan jogokat kér, amelyeket még nem adtál meg. Az új verzió telepítve van, de kikapcsolva marad, amíg jóvá nem hagyod.',
@@ -296,6 +380,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Név',
'admin.plugins.sortRecent': 'Nemrég frissítve',
'admin.plugins.sortUpdates': 'Frissítések elöl',
'admin.plugins.sortDownloads': 'Legtöbb letöltés',
'admin.plugins.updatesAvailable': '{count} frissítés érhető el a pluginjaidhoz.',
'admin.plugins.updateAll': 'Összes frissítése',
'admin.plugins.noMatchInstalled': 'Nincs a keresésnek megfelelő telepített plugin.',
@@ -316,7 +401,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Irányítópult-widget',
'admin.plugins.cap.heroWidget': 'Beszállókártya-widget',
'admin.plugins.cap.placeSlot': 'Hely részletei',
'admin.plugins.cap.daySlot': 'Nap részletei',
'admin.plugins.cap.reservationSlot': 'Foglalás részletei',
'admin.plugins.cap.replacesTabs': 'Tervező lapokat vált ki',
'admin.plugins.cap.realtime': 'Valós idejű frissítések',
'admin.plugins.cap.notificationChannel': 'Értesítési csatorna',
'admin.plugins.cap.photos': 'Fotókat biztosít',
'admin.plugins.cap.calendar': 'Naptáreseményeket biztosít',
'admin.plugins.cap.placeDetails': 'Helyeket gazdagít',
@@ -342,11 +431,12 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Méret',
'admin.plugins.metaRequires': 'Szükséges',
'admin.plugins.metaReviewed': 'Ellenőrizve',
'admin.plugins.downloads': 'Letöltések',
'admin.addons.title': 'Bővítmények',
'admin.addons.subtitle': 'Funkciók engedélyezése vagy letiltása a TREK testreszabásához.',
'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égvetés',
'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.documents.name': 'Dokumentumok',
'admin.addons.catalog.documents.description': 'Úti dokumentumok tárolása és kezelése',
@@ -52,6 +52,7 @@ const hu: NotificationLocale = {
title: 'Synology munkamenet törölve',
body: 'A Synology fiókja vagy URL-je megváltozott. Kijelentkeztek a Synology Photos-ból.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Jelszó visszaállítása',
+1 -1
View File
@@ -9,7 +9,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, útvonalak és csoportosítás',
'login.features.realtime': 'Valós idejű szinkron',
'login.features.realtimeDesc': 'Közös tervezés WebSocket-en keresztül',
'login.features.budget': 'Költségvetés-követés',
'login.features.budget': 'Költségkövetés',
'login.features.budgetDesc': 'Kategóriák, diagramok és személyenkénti költségek',
'login.features.collab': 'Együttműködés',
'login.features.collabDesc': 'Többfelhasználós, megosztott utazásokkal',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Megtekintés',
'notif.action.accept': 'Elfogadás',
'notif.action.decline': 'Elutasítás',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Értesítés',
'notif.generic.text': 'Új értesítésed érkezett',
'notif.dev.unknown_event.title': '[DEV] Ismeretlen esemény',
+6 -6
View File
@@ -22,7 +22,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Minden javaslat hozzáadva',
'packing.allPacked': 'Minden be van csomagolva!',
'packing.addPlaceholder': 'Új tárgy hozzáadása...',
'packing.categoryPlaceholder': 'Kategória...',
'packing.categoryPlaceholder': 'Lista...',
'packing.filterAll': 'Összes',
'packing.filterOpen': 'Nyitott',
'packing.filterDone': 'Kész',
@@ -32,12 +32,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Átnevezés',
'packing.menuCheckAll': 'Összes kipipálása',
'packing.menuUncheckAll': 'Összes jelölés törlése',
'packing.menuDeleteCat': 'Kategória törlése',
'packing.menuDeleteCat': 'Lista törlése',
'packing.noMembers': 'Nincsenek utazási tagok',
'packing.addItem': 'Tétel hozzáadása',
'packing.addItemPlaceholder': 'Tétel neve...',
'packing.addCategory': 'Kategória hozzáadása',
'packing.newCategoryPlaceholder': 'Kategória neve (pl. Ruházat)',
'packing.addCategory': 'Lista hozzáadása',
'packing.newCategoryPlaceholder': 'Lista neve (pl. Ruházat)',
'packing.applyTemplate': 'Sablon alkalmazása',
'packing.template': 'Sablon',
'packing.templateApplied': '{count} tétel hozzáadva a sablonból',
@@ -50,9 +50,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Összsúly',
'packing.bagName': 'Táska neve...',
'packing.addBag': 'Táska hozzáadása',
'packing.changeCategory': 'Kategória módosítása',
'packing.changeCategory': 'Áthelyezés listába',
'packing.confirm.clearChecked': 'Biztosan el szeretnéd távolítani a(z) {count} kipipált tárgyat?',
'packing.confirm.deleteCat': 'Biztosan törölni szeretnéd a(z) "{name}" kategóriát {count} tárggyal?',
'packing.confirm.deleteCat': 'Biztosan törölni szeretnéd a(z) „{name}” listát {count} elemmel?',
'packing.defaultCategory': 'Egyéb',
'packing.toast.saveError': 'Nem sikerült menteni',
'packing.toast.deleteError': 'Nem sikerült törölni',
+27
View File
@@ -7,6 +7,15 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Térkép',
'settings.tabs.notifications': 'Értesítések',
'settings.tabs.integrations': 'Integrációk',
'settings.plugins.oauth.connected': 'Csatlakoztatva',
'settings.plugins.oauth.notConnected': 'Nincs csatlakoztatva',
'settings.plugins.oauth.connect': 'Csatlakozás',
'settings.plugins.oauth.disconnect': 'Leválasztás',
'settings.tabs.plugins': 'Bővítmények',
'settings.plugins.title': 'Bővítménybeállítások',
'settings.plugins.subtitle': 'Személyes beállításaid az általad használt bővítményekhez (API-kulcsok, beállítások).',
'settings.plugins.empty': 'Nincsenek aktív bővítmények.',
'settings.plugins.saved': 'Beállítások mentve',
'settings.tabs.account': 'Fiók',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'Névjegy',
@@ -281,6 +290,14 @@ const settings: TranslationStrings = {
'settings.ntfyUrl.testFailed': 'Teszt Ntfy értesítés sikertelen',
'settings.ntfyUrl.tokenCleared': 'Hozzáférési token törölve',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured': 'Még nincs beállítva — állítsd be a bővítmény beállításaiban',
'settings.plugins.actions': 'Műveletek',
'settings.plugins.actions.confirm': 'Végrehajtod ezt a műveletet?',
'settings.notificationPreferences.sendTest': 'Teszt küldése',
'settings.notificationPreferences.configure': 'Beállítás',
'settings.notificationPreferences.testSuccess': 'Teszt értesítés elküldve.',
'settings.notificationPreferences.testFailed': 'A teszt sikertelen.',
'settings.notificationPreferences.pluginConfigured': 'Beállítva. A hitelesítő adatok a bővítmény beállításainál kezelhetők.',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.ntfy': 'Ntfy',
@@ -467,6 +484,16 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': '{count} szinkronizálása…',
'settings.offline.banner.failed': 'Sikertelen szinkronizálás: {count}',
'settings.offline.banner.conflicts': 'Ütközések: {count}',
'settings.pluginActivity.title': 'Bővítmény-tevékenység',
'settings.pluginActivity.description':
'Minden művelet, amelyet egy bővítmény a nevedben végzett, a legújabbtól kezdve.',
'settings.pluginActivity.empty': 'Még nincs bővítmény-tevékenység.',
'settings.pluginActivity.refresh': 'Frissítés',
'settings.pluginActivity.columns.plugin': 'Bővítmény',
'settings.pluginActivity.columns.action': 'Művelet',
'settings.pluginActivity.columns.resource': 'Erőforrás',
'settings.pluginActivity.columns.when': 'Mikor',
'settings.pluginActivity.columns.status': 'Eredmény',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Térkép és terv',
'share.permBookings': 'Foglalások',
'share.permPacking': 'Csomagolás',
'share.permBudget': 'Költségvetés',
'share.permBudget': 'Költségek',
'share.permCollab': 'Csevegés',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Terv',
'shared.tabBookings': 'Foglalások',
'shared.tabPacking': 'Csomagolás',
'shared.tabBudget': 'Költségvetés',
'shared.tabBudget': 'Költségek',
'shared.tabChat': 'Csevegés',
'shared.days': 'nap',
'shared.places': 'hely',
'shared.other': 'Egyéb',
'shared.totalBudget': 'Teljes költségvetés',
'shared.totalBudget': 'Összes költség',
'shared.messages': 'üzenet',
'shared.sharedVia': 'Megosztva:',
'shared.confirmed': 'Megerősítve',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Mind',
'todo.filter.open': 'Nyitott',
'todo.filter.done': 'Kész',
'todo.uncategorized': 'Kategória nélküli',
'todo.uncategorized': 'Nincs lista',
'todo.namePlaceholder': 'Feladat neve',
'todo.descriptionPlaceholder': 'Leírás (opcionális)',
'todo.unassigned': 'Nem hozzárendelt',
'todo.noCategory': 'Nincs kategória',
'todo.noCategory': 'Nincs lista',
'todo.hasDescription': 'Van leírás',
'todo.addItem': 'Új feladat',
'todo.sidebar.sortBy': 'Rendezés',
'todo.priority': 'Prioritás',
'todo.newCategoryLabel': 'új',
'todo.newCategory': 'Kategória neve',
'todo.addCategory': 'Kategória hozzáadása',
'todo.newCategory': 'Lista neve',
'todo.addCategory': 'Lista hozzáadása',
'todo.newItem': 'Új feladat',
'todo.empty': 'Még nincsenek feladatok. Adj hozzá egyet a kezdéshez!',
'todo.filter.my': 'Saját feladataim',
'todo.filter.overdue': 'Lejárt',
'todo.sidebar.tasks': 'Feladatok',
'todo.sidebar.categories': 'Kategóriák',
'todo.sidebar.categories': 'Listák',
'todo.detail.title': 'Feladat',
'todo.detail.description': 'Leírás',
'todo.detail.category': 'Kategória',
'todo.detail.category': 'Lista',
'todo.detail.dueDate': 'Határidő',
'todo.detail.assignedTo': 'Hozzárendelve',
'todo.detail.delete': 'Törlés',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'Csomag',
'trip.tabs.lists': 'Listák',
'trip.tabs.listsShort': 'Listák',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'Költségek',
'trip.tabs.files': 'Fájlok',
'trip.loading': 'Utazás betöltése...',
'trip.mobilePlan': 'Tervezés',
+89 -1
View File
@@ -210,6 +210,13 @@ const admin: TranslationStrings = {
'admin.plugins.dropToUpload': 'Lepaskan .zip plugin untuk memasang',
'admin.plugins.uploaded': 'Plugin “{name}” diunggah — aktifkan untuk menjalankan',
'admin.plugins.sideloaded': 'Dimuat manual',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Dimuat dari direktori build lokal dan dimuat ulang langsung dengan data nyata — hanya untuk pengembangan',
'admin.plugins.devLinkTitle': 'Tautkan plugin lokal',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Tautkan',
'admin.plugins.devLinkLinked': '{id} ditautkan — aktifkan untuk menjalankan',
'admin.plugins.sideloadedHint': 'Diunggah manual — bukan dari registri, tanpa tanda tangan dan tinjauan',
'admin.plugins.browse': 'Jelajahi plugin',
'admin.plugins.installed': 'Terpasang',
@@ -222,6 +229,15 @@ const admin: TranslationStrings = {
'admin.plugins.deactivated': 'Plugin dinonaktifkan',
'admin.plugins.viewErrors': 'Lihat log error',
'admin.plugins.errorLog': 'Log error',
'admin.plugins.allowedHosts': 'Host yang diizinkan',
'admin.plugins.allowedHosts.hint': 'Plugin ini berbicara dengan layanan yang hanya Anda yang tahu namanya (server swakelola). Tambahkan host yang boleh dijangkau — selain itu tidak bisa.',
'admin.plugins.allowedHosts.none': 'Belum ada host yang ditambahkan.',
'admin.plugins.allowedHosts.unsupported': 'Plugin ini tidak memakai host dari operator. Host yang diizinkan sudah tetap di manifesnya.',
'admin.plugins.allowedHosts.restartNote': 'Menyimpan akan memulai ulang plugin agar memakai daftar baru.',
'admin.plugins.allowedHosts.add': 'Tambah host yang diizinkan',
'admin.plugins.allowedHosts.count': '{n} host diizinkan',
'admin.plugins.operatorEgressPill': '+ host yang Anda tambahkan',
'admin.plugins.operatorEgressHint': 'Plugin ini berbicara dengan layanan yang hanya Anda yang tahu namanya (server swakelola). Setelah dipasang, tambahkan host di ⋯ → Host yang diizinkan. Selain itu tidak bisa.',
'admin.plugins.noErrors': 'Tidak ada error yang tercatat.',
'admin.plugins.uninstalled': 'Plugin dihapus',
'admin.plugins.uninstallTitle': 'Hapus plugin?',
@@ -263,6 +279,7 @@ const admin: TranslationStrings = {
'admin.plugins.scope.user': 'Per pengguna',
'admin.plugins.fieldRequired': 'Wajib diisi',
'admin.plugins.sourceRepo': 'Repositori sumber',
'admin.plugins.reportIssue': 'Laporkan masalah',
'admin.plugins.homepage': 'Beranda',
'admin.plugins.requiresTrek': 'Memerlukan TREK {version}+',
'admin.plugins.reviewedOn': 'Ditinjau {date}',
@@ -273,8 +290,55 @@ const admin: TranslationStrings = {
'Membaca pengeluaran (item anggaran) yang dapat diakses oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:read:packing':
'Membaca daftar barang bawaan dari perjalanan yang dapat diakses oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:write:packing':
'Membuat, mengedit, dan menghapus item daftar bawaan pada perjalanan yang dapat diedit oleh pengguna yang bersangkutan',
'admin.plugins.perm.weather:read': 'Membaca prakiraan cuaca host yang di-cache (berdasarkan koordinat)',
'admin.plugins.perm.db:read:categories': 'Membaca daftar kategori tempat global',
'admin.plugins.perm.db:read:tags': 'Membaca tag pengguna yang bersangkutan',
'admin.plugins.perm.db:write:tags': 'Membuat, mengedit, dan menghapus tag pengguna yang bersangkutan',
'admin.plugins.perm.db:read:todos':
'Membaca tugas dari perjalanan yang dapat diakses oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:write:todos':
'Membuat, mengedit, dan menghapus tugas pada perjalanan yang dapat diedit oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:write:atlas':
'Menandai negara dan wilayah sebagai sudah dikunjungi dan mengelola bucket list pengguna yang bersangkutan (memerlukan add-on Atlas)',
'admin.plugins.perm.db:write:vacay':
'Mengalihkan hari cuti dan libur perusahaan pada rencana aktif pengguna yang bersangkutan (memerlukan add-on Vacay)',
'admin.plugins.perm.db:write:journal':
'Membuat, mengedit, dan menghapus entri jurnal di jurnal yang dapat diedit oleh pengguna yang bersangkutan (memerlukan add-on Journey)',
'admin.plugins.perm.db:write:collections':
'Membuat dan mengedit koleksi serta menyimpan tempat ke dalamnya, dengan peran koleksi pengguna yang bersangkutan (memerlukan add-on Collections)',
'admin.plugins.perm.db:write:files':
'Melampirkan berkas ke perjalanan yang dapat diedit oleh pengguna yang bersangkutan (batas 10 MB, ekstensi terblokir ditolak) dan mengelola tautannya',
'admin.plugins.perm.db:write:collab':
'Memublikasikan catatan, jajak pendapat, dan pesan obrolan pada perjalanan yang dapat diedit oleh pengguna yang bersangkutan (memerlukan add-on Collab)',
'admin.plugins.perm.db:write:members':
'Menambahkan pengguna ke perjalanan — memberikan akses perjalanan; dilindungi oleh hak kelola anggota pengguna yang bersangkutan',
'admin.plugins.perm.notify:send':
'Mengirim notifikasi (kotak lonceng + email/ntfy) kepada pengguna yang bersangkutan atau perjalanan yang diikutinya — tidak pernah ke penerima sembarangan',
'admin.plugins.perm.ai:invoke':
'Menjalankan penyedia AI yang dikonfigurasi admin (penyelesaian teks + ekstraksi dokumen) atas nama pengguna yang bersangkutan — plugin tidak pernah menyimpan kunci',
'admin.plugins.perm.oauth:client':
'Terhubung ke layanan pihak ketiga atas namamu melalui OAuth yang diperantarai host (host menyimpan token, plugin tidak pernah melihatnya)',
'admin.plugins.perm.db:read:files':
'Membaca berkas dari perjalanan yang dapat diakses oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:write:reservations':
'Membuat, mengedit, dan menghapus pemesanan pada perjalanan yang dapat diedit oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:write:accommodations':
'Membuat, mengedit, dan menghapus akomodasi pada perjalanan yang dapat diedit oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:read:journal':
'Membaca jurnal perjalanan pengguna yang bersangkutan (memerlukan add-on Journey)',
'admin.plugins.perm.db:read:atlas':
'Membaca negara dan wilayah yang dikunjungi oleh pengguna yang bersangkutan (memerlukan add-on Atlas)',
'admin.plugins.perm.db:read:vacay': 'Membaca rencana liburan pengguna yang bersangkutan (memerlukan add-on Vacay)',
'admin.plugins.perm.db:read:daynotes':
'Membaca catatan harian dari perjalanan yang dapat diakses oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:read:collections':
'Membaca koleksi tempat tersimpan milik pengguna yang bersangkutan (memerlukan add-on Collections)',
'admin.plugins.perm.db:write:daynotes':
'Membuat, mengedit, dan menghapus catatan harian pada perjalanan yang dapat diedit oleh pengguna yang bersangkutan',
'admin.plugins.perm.jobs:run':
'Menjalankan tugas latar belakang yang dideklarasikan sesuai jadwal (tanpa konteks pengguna — tidak dapat membaca data pengguna)',
'admin.plugins.perm.db:write:costs':
'Membuat pengeluaran (item anggaran) pada perjalanan yang dapat diedit oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:write:places':
@@ -295,9 +359,27 @@ const admin: TranslationStrings = {
'Menyumbangkan detail tambahan (ulasan, peringkat, tautan) untuk suatu tempat',
'admin.plugins.perm.hook:trip-warning-provider':
'Munculkan peringatan validasi pada perjalanan (ditampilkan di perencana)',
'admin.plugins.perm.hook:table-contributor':
'Menambahkan kolom dan tindakan ke tampilan perjalanan (pemesanan, tempat, hari)',
'admin.plugins.perm.hook:map-marker-provider':
'Menambahkan penanda ke peta perjalanan (mis. menampilkan pemesanan atau POI)',
'admin.plugins.perm.hook:pdf-section-provider': 'Menambahkan bagian teks ke ekspor PDF perjalanan',
'admin.plugins.perm.hook:atlas-layer-provider':
'Menyorot negara di peta dunia Atlas (mis. daftar keinginan atau peringatan perjalanan)',
'admin.plugins.perm.hook:journal-entry-provider': 'Menambahkan baris tambahan (tautan, statistik) ke entri jurnal',
'admin.plugins.perm.hook:user-data':
'Menghapus atau mengekspor data yang disimpannya tentang pengguna (GDPR: penghapusan akun & permintaan data)',
'admin.plugins.perm.hook:trip-card-provider': 'Menambahkan lencana kecil (status, jumlah) ke kartu perjalanan dasbor',
'admin.plugins.perm.hook:notification-channel': 'Mengirim notifikasi Anda melalui saluran tambahan',
'admin.plugins.perm.events:subscribe':
'Bereaksi terhadap peristiwa aktivitas inti (hanya nama peristiwa + perjalanan, tidak pernah kontennya)',
'admin.plugins.perm.http:outbound': 'Membuat permintaan keluar ke host yang telah dideklarasikannya',
'admin.plugins.perm.db:read:collab':
'Membaca catatan, jajak pendapat, dan pesan obrolan dari perjalanan yang dapat diakses oleh pengguna yang bersangkutan (memerlukan add-on Collab)',
'admin.plugins.perm.db:read:files:content':
'Membaca isi (byte) berkas dari perjalanan yang dapat diakses oleh pengguna yang bersangkutan',
'admin.plugins.perm.db:create:trips': 'Membuat perjalanan baru milik pengguna yang bersangkutan',
'admin.plugins.perm.rates:read': 'Membaca kurs mata uang host yang di-cache',
'admin.plugins.updateConsentTitle': 'Pembaruan ini memerlukan izin baru',
'admin.plugins.updateConsentBody':
'{name} v{version} meminta hak akses yang belum kamu berikan. Versi baru sudah terpasang tetapi tetap nonaktif sampai kamu menyetujuinya.',
@@ -340,6 +422,7 @@ const admin: TranslationStrings = {
'admin.plugins.sortName': 'Nama',
'admin.plugins.sortRecent': 'Baru diperbarui',
'admin.plugins.sortUpdates': 'Pembaruan dulu',
'admin.plugins.sortDownloads': 'Paling banyak diunduh',
'admin.plugins.updatesAvailable': '{count} pembaruan tersedia untuk plugin kamu.',
'admin.plugins.updateAll': 'Perbarui semua',
'admin.plugins.noMatchInstalled': 'Tidak ada plugin terpasang yang cocok dengan pencarianmu.',
@@ -360,7 +443,11 @@ const admin: TranslationStrings = {
'admin.plugins.cap.widget': 'Widget dasbor',
'admin.plugins.cap.heroWidget': 'Widget boarding pass',
'admin.plugins.cap.placeSlot': 'Detail tempat',
'admin.plugins.cap.daySlot': 'Detail hari',
'admin.plugins.cap.reservationSlot': 'Detail reservasi',
'admin.plugins.cap.replacesTabs': 'Menggantikan tab perencana',
'admin.plugins.cap.realtime': 'Pembaruan real-time',
'admin.plugins.cap.notificationChannel': 'Saluran notifikasi',
'admin.plugins.cap.photos': 'Menyediakan foto',
'admin.plugins.cap.calendar': 'Menyediakan acara kalender',
'admin.plugins.cap.placeDetails': 'Memperkaya tempat',
@@ -386,11 +473,12 @@ const admin: TranslationStrings = {
'admin.plugins.metaSize': 'Ukuran',
'admin.plugins.metaRequires': 'Memerlukan',
'admin.plugins.metaReviewed': 'Ditinjau pada',
'admin.plugins.downloads': 'Unduhan',
'admin.addons.title': 'Addon',
'admin.addons.subtitle': 'Aktifkan atau nonaktifkan fitur untuk menyesuaikan pengalaman TREK kamu.',
'admin.addons.catalog.packing.name': 'Daftar',
'admin.addons.catalog.packing.description': 'Daftar packing dan tugas to-do untuk perjalananmu',
'admin.addons.catalog.budget.name': 'Anggaran',
'admin.addons.catalog.budget.name': 'Biaya',
'admin.addons.catalog.budget.description': 'Lacak pengeluaran dan rencanakan anggaran perjalananmu',
'admin.addons.catalog.documents.name': 'Dokumen',
'admin.addons.catalog.documents.description': 'Simpan dan kelola dokumen perjalanan',
@@ -52,6 +52,7 @@ const id: NotificationLocale = {
title: 'Sesi Synology dihapus',
body: 'Akun atau URL Synology Anda berubah. Anda telah keluar dari Synology Photos.',
}),
plugin_notification: (p) => ({ title: p.title ?? '', body: p.body ?? '' }),
},
passwordReset: {
subject: 'Setel ulang kata sandi Anda',
+1 -1
View File
@@ -8,7 +8,7 @@ const login: TranslationStrings = {
'login.features.mapsDesc': 'Google Places, rute & pengelompokan',
'login.features.realtime': 'Sinkronisasi Real-Time',
'login.features.realtimeDesc': 'Rencanakan bersama via WebSocket',
'login.features.budget': 'Pelacak Anggaran',
'login.features.budget': 'Pelacak Biaya',
'login.features.budgetDesc': 'Kategori, grafik & biaya per orang',
'login.features.collab': 'Kolaborasi',
'login.features.collabDesc': 'Multi-pengguna dengan perjalanan bersama',
+2
View File
@@ -35,6 +35,8 @@ const notif: TranslationStrings = {
'notif.action.view': 'Lihat',
'notif.action.accept': 'Terima',
'notif.action.decline': 'Tolak',
'notif.plugin.title': '{title}',
'notif.plugin.text': '{body}',
'notif.generic.title': 'Notifikasi',
'notif.generic.text': 'Kamu punya notifikasi baru',
'notif.dev.unknown_event.title': '[DEV] Event Tidak Dikenal',
+6 -6
View File
@@ -22,7 +22,7 @@ const packing: TranslationStrings = {
'packing.allSuggested': 'Semua saran sudah ditambahkan',
'packing.allPacked': 'Semua sudah dikemas!',
'packing.addPlaceholder': 'Tambah item baru...',
'packing.categoryPlaceholder': 'Kategori...',
'packing.categoryPlaceholder': 'Daftar...',
'packing.filterAll': 'Semua',
'packing.filterOpen': 'Belum',
'packing.filterDone': 'Selesai',
@@ -32,12 +32,12 @@ const packing: TranslationStrings = {
'packing.menuRename': 'Ganti Nama',
'packing.menuCheckAll': 'Centang Semua',
'packing.menuUncheckAll': 'Hapus Centang Semua',
'packing.menuDeleteCat': 'Hapus Kategori',
'packing.menuDeleteCat': 'Hapus daftar',
'packing.noMembers': 'Tidak ada anggota perjalanan',
'packing.addItem': 'Tambah item',
'packing.addItemPlaceholder': 'Nama item...',
'packing.addCategory': 'Tambah kategori',
'packing.newCategoryPlaceholder': 'Nama kategori (contoh: Pakaian)',
'packing.addCategory': 'Tambah daftar',
'packing.newCategoryPlaceholder': 'Nama daftar (mis. Pakaian)',
'packing.applyTemplate': 'Terapkan template',
'packing.template': 'Template',
'packing.templateApplied': '{count} item ditambahkan dari template',
@@ -50,9 +50,9 @@ const packing: TranslationStrings = {
'packing.totalWeight': 'Total berat',
'packing.bagName': 'Nama tas...',
'packing.addBag': 'Tambah tas',
'packing.changeCategory': 'Ganti Kategori',
'packing.changeCategory': 'Pindahkan ke daftar',
'packing.confirm.clearChecked': 'Yakin ingin menghapus {count} item yang dicentang?',
'packing.confirm.deleteCat': 'Yakin ingin menghapus kategori "{name}" beserta {count} item di dalamnya?',
'packing.confirm.deleteCat': 'Yakin ingin menghapus daftar "{name}" dengan {count} item?',
'packing.defaultCategory': 'Lainnya',
'packing.toast.saveError': 'Gagal menyimpan',
'packing.toast.deleteError': 'Gagal menghapus',
+26
View File
@@ -7,6 +7,15 @@ const settings: TranslationStrings = {
'settings.tabs.map': 'Peta',
'settings.tabs.notifications': 'Notifikasi',
'settings.tabs.integrations': 'Integrasi',
'settings.plugins.oauth.connected': 'Terhubung',
'settings.plugins.oauth.notConnected': 'Tidak terhubung',
'settings.plugins.oauth.connect': 'Hubungkan',
'settings.plugins.oauth.disconnect': 'Putuskan',
'settings.tabs.plugins': 'Plugin',
'settings.plugins.title': 'Pengaturan plugin',
'settings.plugins.subtitle': 'Pengaturan pribadimu untuk plugin yang kamu gunakan (kunci API, preferensi).',
'settings.plugins.empty': 'Tidak ada plugin yang aktif.',
'settings.plugins.saved': 'Pengaturan disimpan',
'settings.tabs.account': 'Akun',
'settings.tabs.offline': 'Offline',
'settings.tabs.about': 'Tentang',
@@ -79,6 +88,14 @@ const settings: TranslationStrings = {
'settings.notificationPreferences.email': 'Email',
'settings.notificationPreferences.webhook': 'Webhook',
'settings.notificationPreferences.inapp': 'In-App',
'settings.notificationPreferences.notConfigured': 'Belum dikonfigurasi — atur di pengaturan plugin',
'settings.plugins.actions': 'Tindakan',
'settings.plugins.actions.confirm': 'Jalankan tindakan ini?',
'settings.notificationPreferences.sendTest': 'Kirim tes',
'settings.notificationPreferences.configure': 'Konfigurasi',
'settings.notificationPreferences.testSuccess': 'Notifikasi tes terkirim.',
'settings.notificationPreferences.testFailed': 'Tes gagal.',
'settings.notificationPreferences.pluginConfigured': 'Sudah diatur. Kelola kredensial di halaman pengaturan plugin.',
'settings.notificationPreferences.ntfy': 'Ntfy',
'settings.notificationPreferences.noChannels':
'Belum ada saluran notifikasi yang dikonfigurasi. Minta admin untuk mengatur notifikasi email atau webhook.',
@@ -466,6 +483,15 @@ const settings: TranslationStrings = {
'settings.offline.banner.syncing': 'Menyinkronkan {count}…',
'settings.offline.banner.failed': 'Gagal menyinkronkan: {count}',
'settings.offline.banner.conflicts': 'Konflik: {count}',
'settings.pluginActivity.title': 'Aktivitas plugin',
'settings.pluginActivity.description': 'Setiap tindakan yang dilakukan plugin atas nama Anda, terbaru lebih dulu.',
'settings.pluginActivity.empty': 'Belum ada aktivitas plugin.',
'settings.pluginActivity.refresh': 'Segarkan',
'settings.pluginActivity.columns.plugin': 'Plugin',
'settings.pluginActivity.columns.action': 'Tindakan',
'settings.pluginActivity.columns.resource': 'Sumber daya',
'settings.pluginActivity.columns.when': 'Waktu',
'settings.pluginActivity.columns.status': 'Hasil',
};
export default settings;
+1 -1
View File
@@ -10,7 +10,7 @@ const share: TranslationStrings = {
'share.permMap': 'Peta & Rencana',
'share.permBookings': 'Pemesanan',
'share.permPacking': 'Bawaan',
'share.permBudget': 'Anggaran',
'share.permBudget': 'Biaya',
'share.permCollab': 'Chat',
};
export default share;
+2 -2
View File
@@ -7,12 +7,12 @@ const shared: TranslationStrings = {
'shared.tabPlan': 'Rencana',
'shared.tabBookings': 'Pemesanan',
'shared.tabPacking': 'Bawaan',
'shared.tabBudget': 'Anggaran',
'shared.tabBudget': 'Biaya',
'shared.tabChat': 'Chat',
'shared.days': 'hari',
'shared.places': 'tempat',
'shared.other': 'Lainnya',
'shared.totalBudget': 'Total Anggaran',
'shared.totalBudget': 'Total Biaya',
'shared.messages': 'pesan',
'shared.sharedVia': 'Dibagikan via',
'shared.confirmed': 'Dikonfirmasi',
+6 -6
View File
@@ -7,27 +7,27 @@ const todo: TranslationStrings = {
'todo.filter.all': 'Semua',
'todo.filter.open': 'Terbuka',
'todo.filter.done': 'Selesai',
'todo.uncategorized': 'Tanpa kategori',
'todo.uncategorized': 'Tanpa daftar',
'todo.namePlaceholder': 'Nama tugas',
'todo.descriptionPlaceholder': 'Deskripsi (opsional)',
'todo.unassigned': 'Belum ditugaskan',
'todo.noCategory': 'Tanpa kategori',
'todo.noCategory': 'Tanpa daftar',
'todo.hasDescription': 'Ada deskripsi',
'todo.addItem': 'Tugas baru',
'todo.sidebar.sortBy': 'Urutkan',
'todo.priority': 'Prioritas',
'todo.newCategoryLabel': 'baru',
'todo.newCategory': 'Nama kategori',
'todo.addCategory': 'Tambah kategori',
'todo.newCategory': 'Nama daftar',
'todo.addCategory': 'Tambah daftar',
'todo.newItem': 'Tugas baru',
'todo.empty': 'Belum ada tugas. Tambah tugas untuk memulai!',
'todo.filter.my': 'Tugasku',
'todo.filter.overdue': 'Terlambat',
'todo.sidebar.tasks': 'Tugas',
'todo.sidebar.categories': 'Kategori',
'todo.sidebar.categories': 'Daftar',
'todo.detail.title': 'Tugas',
'todo.detail.description': 'Deskripsi',
'todo.detail.category': 'Kategori',
'todo.detail.category': 'Daftar',
'todo.detail.dueDate': 'Tenggat waktu',
'todo.detail.assignedTo': 'Ditugaskan ke',
'todo.detail.delete': 'Hapus',
+1 -1
View File
@@ -9,7 +9,7 @@ const trip: TranslationStrings = {
'trip.tabs.packingShort': 'Perlengkapan',
'trip.tabs.lists': 'Daftar',
'trip.tabs.listsShort': 'Daftar',
'trip.tabs.budget': 'Costs',
'trip.tabs.budget': 'Biaya',
'trip.tabs.files': 'File',
'trip.loading': 'Memuat perjalanan...',
'trip.loadingPhotos': 'Memuat foto tempat...',

Some files were not shown because too many files have changed in this diff Show More