mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
3.2.1 (#1433)
* fix(plugins): prevent arbitrary api access * fix(planner): disable drag & drop on mobile so the places list scrolls (#1432) On touch devices the draggable rows hijack the scroll gesture, so dragging to scroll started an HTML5 drag and popped up the file-import overlay instead of scrolling. Gate the draggable rows and the sidebar file-drop handlers on !isMobile across the places sidebar and the day plan (places, transports, notes), and hide the grip handle — the arrow reorder buttons take over there. * fix(inspector): make the remove-from-day button icon-only on mobile * fix(collections): show the save picker above the mobile place detail * fix(plugins): seal IPC parent/child for good * test(inspector): match the icon-only remove-from-day button * feat(sdk): switch plain ts for clack/prompts interactive session * feat(plugins): force-refresh the registry from the rescan button The registry is cached for 30 min server-side and GitHub serves it with a 5-min CDN cache, so a freshly published plugin could take up to ~35 min to appear. The rescan/reload button now force-pulls the registry: it bypasses the in-memory cache and appends a cache-buster + no-cache headers to beat the CDN, and refreshes the browse grid immediately. * feat(sdk): bump plugin version * fix(stored settings): prevent local storage drop when update not successful * feat(plugins): sideload plugins by uploading a .zip Adds an admin "Upload plugin" button + drag-and-drop to the plugins panel for installing a plugin archive directly — handy for testing a build before it goes to the registry. It reuses the registry install pipeline (slip/bomb-safe extract, strict manifest validation, native-binary scan) via a new POST /admin/plugins/upload, and only skips the registry sha256/signature checks that a sideload can't have. Sideloaded plugins are flagged (source "local:upload", a "Sideloaded" badge, no GitHub link, no auto-update) and always land INACTIVE — replacing a running or active plugin stops it and clears the active flag first, so new code never runs without a fresh activation + permission consent. * fix(planner): keep the day-plan collapse state after fully closing the page The expanded/collapsed days were stored in sessionStorage, which survives a reload but is wiped when the tab or window is closed — so every fresh open re-expanded all days, which is tedious to re-collapse on long (10+ day) trips. Store it in localStorage instead so a collapsed layout sticks until it's changed. * fix(i18n): correct Vietnamese translation of 'Disabled' (#1438) 'Tàn tật' means physically handicapped/disabled-person, not the off/disabled state of a toggle. Replace with 'Tắt' (off), matching the existing 'admin.plugins.stateOff' translation. Affects admin.notifications.none and admin.addons.disabled. * add the code of conduct * fix(plugins): let widgets follow the in-app dark-mode toggle The plugin frame is sandboxed at an opaque origin (no parent DOM access) and we only sent the context — including the theme — once, on trek:ready. So toggling dark mode in TREK left already-mounted widgets on the old theme until a reload. Watch the <html> `dark` class and re-post the context when it flips; plugins already re-apply the theme on trek:context. * fix(plugins): deliver widget context on load so the theme is right on first paint * fix(plugins): give widget cards the native glassy look and auto-height Widget plugins rendered in a plain card with a fixed 180px body, so they looked foreign next to the glassy dashboard tools and taller widgets had their controls clipped. Mirror the native `.tool` surface (glass background/border/blur, uppercase title) and let the body grow to the height the widget reports over trek:resize. * feat(plugins): add read/rwite costs * feat(plugin): better readme/index.js * feat(plugin): better readme/index.js * feat(plugins): hand widgets TREK's theme tokens, formats and display identity Extends trek:context with a non-secret `tokens` map (TREK's resolved CSS design tokens for the current theme), `formats` (currency/date/units/timezone) and a `user` display object (name/avatar/isAdmin — never the email, role only as a boolean). Re-sent on every theme toggle. A widget can now apply the tokens and match the host exactly, in both themes and under a custom appearance, instead of hard-coding a palette that drifts — so plugins feel native, not bolted-on. * feat(plugins): hand plugins the full palette and appearance state The theme context only carried a ~19-token subset read off <html> and only followed the dark-mode toggle. Widen it to the whole global (:root/.dark) palette — surfaces, text, borders, the accent family, semantic + soft fills, shadows, radii and fonts — so a plugin tracks the user's chosen accent scheme, custom accent and high-contrast live, not just light/dark. Also send an `appearance` block (scheme, density, reduced-motion, no-transparency) mirrored from the attributes applyAppearance writes on <html>, and re-post the context whenever any of those actually change (a small signature dedupes unrelated mutations) so plugins restyle in step with the app. * feat(plugins): ship a design kit so plugin UIs look native A plugin's UI is a sandboxed, opaque-origin iframe that can't load TREK's stylesheet — so authors had to re-derive the whole look by hand, and most didn't. Ship it instead: a token-driven stylesheet (glass, hover, buttons, inputs, chips, rows) that consumes the tokens the host already sends and swaps light/dark, plus a small bootstrap that applies those tokens, mirrors the appearance flags, auto-reports the frame height and exposes a `window.trek` helper over the existing bridge. Both are plain strings meant to be inlined (the CSP forbids external assets for an opaque frame); `injectTrekUi` expands a `<!-- trek:ui -->` marker. No new capability — only a native look. * feat(plugins): deliver the design kit — native scaffold + inline on dev/pack A new page/widget scaffolds a native, glassy starter that talks over window.trek. The source keeps a single `<!-- trek:ui -->` line; `dev` (when it serves /ui) and `pack` (as the file enters the archive) expand it into the inlined kit — so the file stays a one-line opt-in and a rebuild always ships the current kit. Existing plugins opt in the same way, by dropping the marker. * feat(plugins): faithful themed host preview in dev `dev` served the plugin UI raw at /ui — top-level, with no host — so the theme, context and bridge never fired and authors couldn't see the design kit render. Add /preview: it embeds /ui in a sandboxed opaque-origin iframe (exactly TREK's isolation) and plays the host — posts trek:context with a theme/accent/appearance toggle, proxies trek:invoke to your /api routes as the dev user, and surfaces resize/notify/navigate. /ui stays as the raw doc for debugging. * docs(plugins): document the design kit, window.trek and the token contract Rewrite the client section of the Plugin Development wiki kit-first: the `<!-- trek:ui -->` marker, the component classes, the `window.trek` bridge, the `/preview` host preview, the full `trek:context` payload (now the whole palette plus an `appearance` block) and how to apply tokens by hand. Add a "Build a native UI" section + the new exports to the SDK README. * feat(budget): add 'Outstanding amount' card * fix(translations): finish translating new keys * feat(plugins): trip-page plugins — a plugin tab inside every trip Adds a `trip-page` plugin type whose sandboxed iframe mounts as a tab in the trip planner (Plan / Transports / … / <plugin>), scoped to the open trip, with no dashboard nav entry. This is the most-asked planner-extension request from discussion #1429 (a plugin that lives in the trip, e.g. SimMesg20's budget planner). It reuses PluginFrame and the existing tab system — the frame already receives the current tripId over trek:context — so there is no bridge or security change: only the manifest type enum (server + SDK), the client feed classification (pluginStore.tripPages), and one render branch in the planner. The SDK scaffolds it with `create --type trip-page`. * fix(apple wallet): support for .pkpasses * feat(plugins): permission-gated write APIs for the planner (#1429) Plugins can now WRITE core planner data, not just read it, through curated, membership-checked methods — so downstream features can live in plugins instead of long-lived core patches. Four new scopes: db:write:places (create/update/delete places), db:write:days (days), db:write:itinerary (assign/unassign a place on a day) and db:write:trips (update trip fields). Each ctx method mirrors costs.create: it validates the input against the SAME @trek/shared schema the web app uses, binds the acting user host-side (a job/onLoad has none, so its writes are refused), checks trip access AND the app's edit permission (place_edit / day_edit / trip_edit), delegates to the real services, broadcasts the same events so open sessions update live, and records the write in the tamper-evident capability audit. No new route, no sandbox or CSP change — the isolation boundary is unchanged; a plugin can only change what its user could change by hand. Consent UI + permission labels in all 22 locales, SDK types + mock host, and the wikis are updated. * docs(plugin): ensure wiki correctness * feat(plugins): plugin metadata on core entities — db:meta (#1429) Plugins can now attach their OWN namespaced key/value data to a trip, place or day without forking the core schema (#1429, request 2). New `db:meta` scope + `ctx.meta.get/set/list/delete`. Storage is one plugin_entity_metadata table (migration 161) keyed (plugin_id, entity_type, entity_id, key) — a plugin only ever sees its own rows. Every call is membership-checked: the entity must belong to a trip the host-bound acting user can access. Quotas guard the shared volume (≤64KB per value, ≤100 keys per entity); rows are purged on uninstall-with-delete-data and recorded in the capability audit. SDK types + mock host, a consent chip + labels in all 22 locales, and the wikis. No new route, no sandbox change. * feat(plugins): place-detail plugin slot in the trip planner (#1429) A widget plugin can declare `capabilities.widget.slot: 'place-detail'` to mount its sandboxed frame inside the trip planner's place-detail panel, scoped to the open place — the frame receives the `placeId` in trek:context alongside the tripId. This is the UI half of the place-detail-providers ask (reviews/ratings/popular times shown on a place). It reuses the existing widget mechanism: PluginFrame gains an optional placeId, the feed/store learn the new slot, and PlaceInspector renders the slot at the foot of its body in trip mode. Admin chip + label in all 22 locales, wiki updated. No sandbox or permission change. * fix(plugins): green the server tests + harden the new capability surface The in-memory uninstall fixture was missing the new plugin_entity_metadata table, so uninstall's DELETE threw "no such table" and failed the server test job. Add the table to the fixture schema. Self-review hardening of the write/metadata surface: - trips.update now reproduces the web UI's per-field gate: is_archived needs trip_archive and cover_image needs trip_cover_upload, not just trip_edit — so a member who may only edit can't archive or re-cover a trip. - Plugin metadata WRITES now also require the entity's edit permission (place_edit/day_edit/trip_edit), not just trip access, so a read-only member can't overwrite or delete metadata another user created. Reads stay access-gated. - Cap the metadata key length (<=256 chars) alongside the value/count quotas — the key was attacker-controlled and uncapped, defeating the disk-DoS guard. * test(plugins): cover the new write/metadata deps to hold the coverage gate The new create-rpc-host write + metadata deps were untested, dropping the src/nest branch coverage below the 80% gate. Add a seeded in-memory core db plus mocked core services to exercise every dep end-to-end: places/days/itinerary create/update/delete + not-found paths, trips.update with the archive/cover per-field gates and the Validation/NotFound/unknown-error mapping, metadata CRUD + key/value/count caps + access checks, the costs deps, and users.getById scoping. Plus rpc-host cases for meta writes on place/day and a no-acting-user refusal. Tests only — no production code change. * fix(costs): freeze FX on every cost + settlement write path (#1445) Settled foreign-currency costs kept re-opening with a few-cent residual when live rates drifted. The #1335 freeze only ran on the REST create/ update path, so two gaps remained: - Foreign-currency items created via MCP create_budget_item or booking- import bypassed the freeze and stored exchange_rate = 1, so settlement re-converted them with live rates. Promote freezeForeignRate into the shared budgetService and call it from every write path. - Settle-up transfers were stored currency-less and re-converted with live rates on each recompute. Add currency + exchange_rate to budget_settlements (migration), freeze the display-currency rate at settle time, and convert with it in calculateSettlement. Legacy rows (currency = NULL / rate = 1) keep live-rate behaviour until re-edited. Also expose guarded cost update/delete to plugins: costs.update and costs.delete under db:write:costs, gated exactly like costs.create (addon + trip access + the acting user's budget_edit permission). updateCost reuses BudgetService.update so a plugin write re-freezes the FX rate too; both broadcast the same budget:updated / budget:deleted events the REST controller emits. Wired through the host, the runtime SDK context and the published trek-plugin-sdk (types + mock host). * feat(plugins): provider hooks — placeDetailProvider, wired (#1429) Turn "hooks" from a declared-but-dead surface into a real host→plugin capability. Add an invoke.hook branch to the child + a supervisor hook registry (providersOf) + PluginRuntimeService.invokeHook, reusing the existing invoke transport and its timeout (a short 5s deadline so a slow provider can't delay a response; a job/onLoad has no user, host-bound as ever). Also fixes a real bug: the in-repo runtime SDK copy was missing the `hooks` field entirely and could not even parse a plugin that declared one — synced it with the published SDK. The first wired hook is placeDetailProvider: a plugin returns extra rows ({label,value?,url?}) for a place, and TREK renders them natively at the foot of the place-detail panel. Consumer is a new, additive, fail-safe endpoint GET /api/place-details/:placeId (membership-checked; any provider that errors or times out is simply skipped — it never breaks the panel). New hook:place-detail- provider scope + consent chip in all 22 locales. SDK types (both copies), a controller test, and the wiki. photoProvider/calendarSource stay reserved but the transport now exists for them. No sandbox or CSP change. * fix(files): handle pkpass in booking uploads and files-tab open (#1447, #1448) Both bugs were client-only; the server already allows .pkpass and serves it as application/vnd.apple.pkpass. #1448: the reservation/transport attachment inputs hard-coded an accept list that omitted pkpass, so macOS grayed it out. Add .pkpass/.pkpasses (+ wallet MIME types) to the accept attribute in both modals. #1447: the files-tab open path routed every non-media/non-markdown file into the in-app PDF preview object. Add isWalletPass() and route wallet passes through the shared blob openFile helper (as bookings already do), which downloads them so the OS hands them to Apple Wallet. * feat(plugins): validation/warning contributions via warningProvider hook (#1429) Second wired provider hook, reusing the invoke.hook infra from the last commit. A plugin implements warningProvider.getWarnings(tripId, ctx) → {level, message, dayId?, placeId?}[] to flag problems on a trip (overpacked day, place closed on its planned date, missing booking, …). TREK surfaces them as a non-blocking overlay banner at the top of the trip planner (the wrapper ignores pointer events so it never covers the map/panels; only the pills are interactive). Consumer is a new additive, fail-safe endpoint GET /api/trip-warnings/:tripId (membership-checked; a provider that errors or times out contributes nothing and never blocks the planner). New hook:trip-warning-provider scope + consent chip in all 22 locales, SDK types (both copies), a controller test, and the wiki. This is the validation half of the scheduling+validation block; feeding durations/travel times back into core recalculation stays out (it would touch core planner computation — deliberately deferred to keep the no-breaking-changes guarantee). * fix(plugins): enforce the hook:* grant on provider dispatch (#1429 audit) The adversarial audit of the #1429 additions found one real (medium) gap: the hook:* permission was never enforced at runtime. providersOf() selected provider plugins purely by the hooks their CODE declares (sup.hooks, reported by the child as Object.keys(def.hooks)) and never intersected that with sup.granted — so a plugin that merely implemented placeDetailProvider/warningProvider got wired in as a provider even when the admin never consented to hook:place-detail-provider / hook:trip-warning-provider. The downstream capability router still held (the hook's ctx can only do what the plugin's OTHER grants allow), but a plugin could obtain an auto-triggered, user-bound execution context on a passive UI browse without the hook being consented — a consent-integrity gap that contradicts the documented invariant. Gate it host-side: a hookName→permission map, and providersOf now returns a plugin only if it is active, implements the hook, AND holds the matching hook:* grant. An unmapped hook resolves to nobody. invokeHook additionally re-checks membership in providersOf (defense-in-depth against a direct caller). Unit test proves the grant/implements/active intersection. * docs(plugins): add the Plugin Cookbook + a trip-doctor example (#1429 eco) Fosters plugin authoring by turning the new #1429 capabilities into copy-paste recipes. New wiki page Plugin-Cookbook (read a trip, write to the itinerary, tag an entity with metadata, contribute native place details, raise trip warnings, broadcast, match the TREK look) linked in the sidebar, plus a complete runnable example — trip-doctor — a hooks-only plugin that showcases warningProvider + placeDetailProvider + ctx.meta with zero UI of its own. Manifest validates against the SDK. Docs/example only; no product code. * fix(collections): don't reset saved-place status to 'idea' on edit (#1437) The update schema reused collectionStatusSchema, whose .default('idea') survives .optional() — so a PATCH that omits status had 'idea' injected by the validation pipe and written to the DB, clobbering 'want'/'visited'. Strip the default on the update field with .removeDefault(), keeping the .catch guard. Add a shared schema regression test and an e2e round-trip. * docs(plugin): ensure plugin scopes are the same everywhere * feat(plugins): read scopes for packing + files (#1429 eco) Extend the read side of the capability model beyond trips/costs: db:read:packing → ctx.packing.list(tripId) and db:read:files → ctx.files.list(tripId). Both mirror the existing trip reads exactly — the host membership-checks the trip against the invocation's user (tripRead) before delegating to the same packingService/ fileService the REST paths use (so bags/assignees hydrate and trash is excluded), and each is a separate scope (packing doesn't unlock files). ctx types in both SDK copies + mock-host, consent labels + cap chips in all 22 locales, rpc-host + create-rpc-host tests, and the wiki (perm table + cookbook recipe). * feat(plugins): core event subscriptions (#1429 eco) A plugin can react to core activity by declaring events: [{ on, handler }] + the events:subscribe grant. websocket.broadcast announces every CORE trip event (name + tripId ONLY, never the payload) through a tiny dependency-free relay (plugin-event-sink); the runtime registers a sink in onModuleInit and the supervisor fans each event out to subscribed, granted, active plugins via a fire-and-forget invoke.event on a short timeout — so a slow subscriber can never block a core write. Safety by construction: handlers run with NO user (like a job) so trip reads are refused — they react to the fact, using the plugin's own ctx.db/ws/outbound; the grant is enforced host-side (deliverEvent checks events:subscribe); plugin:* re- broadcasts are never delivered back, so handlers can't loop; and only the event name + tripId cross the boundary. SDK types (both copies), consent label + cap chip in all 22 locales, supervisor gating + broadcast-tap tests, and the wiki + cookbook. The relay lives in its own module (not websocket) so it doesn't drag `ws` into the runtime and tests that mock ./websocket don't strip the sink. * feat(plugin-sdk): typed ctx returns + native trek.ui DOM helpers (#1429 eco) Two author-DX wins, SDK-only. Typed reads/writes: ctx.trips.getById/getPlaces/getReservations, packing.list, files.list, costs.*, places/days/itinerary writes and users.getById now return proper entity types (Trip, Place, Day, Reservation, PackingItem, TripFile, BudgetItem, Assignment, User) instead of unknown — real autocomplete for authors. Only `id` is guaranteed and every shape keeps an index signature, so it mirrors the raw DB row honestly (no column hidden, no false guarantees). mock-host matches. Native UI helpers: window.trek now carries `trek.ui` — a tiny bundler-free DOM builder (el/button/card/chip/input/mount) that emits the kit's trek-* classes, so a widget builds themed UI with no CSS and no build step. Ships inlined via the same <!-- trek:ui --> marker. Wiki updated. * fix(plugins): scope packing.list to the acting user's #858 visibility (eco audit) The final eco audit found one real (medium) gap: the db:read:packing delegate called packingService.listItems(tripId) with NO userId, which takes the UNFILTERED branch and returns every member's private (is_private=1) packing items — leaking another member's personal/surprise-gift items to a plugin the normal UI/REST hides them from. The handler had the host-bound acting user but dropped it when delegating. Thread it through: tripRead now hands the membership-checked userId to the read callback, packing.list forwards it to listPackingItems(tripId, userId), and the service applies its three-tier #858 filter — a plugin now sees exactly what its user sees. files.list is unaffected (no per-user file visibility). Tests assert the user is passed. The other three audited surfaces (event subscriptions, trek.ui, and the regression sweep of the capability boundary) were clean. * security(plugins): prevent open redirect * fix(plugins): resolve PR #1433 full-audit findings (code + tests) The comprehensive PR audit confirmed 21 findings; this fixes the code/test ones I own: - ctx.users.getById was DEAD: the runtime SDK omitted the _inv tag, so actingUser never bound and every call hit RESOURCE_FORBIDDEN. Add _inv (the test had codified the bug — corrected). - Plugin place writes bypassed the REST STRING_LIMITS (a 100k-char name the web app rejects). Mirror the caps (name 200 / description 2000 / address 500 / notes 2000). - packing.list / files.list were missing from the capability audit log while every other core read is audited — add them to isAuditable + auditResource. - SDK lockstep: CalendarSource.getEvents drifted (published Date vs runtime string); the host->plugin boundary is JSON, so align both to string. - Admin panel didn't know the new trip-page plugin type (unlocalised badge, missing filter) — add it to KNOWN_TYPES + the type filter + a 22-locale label. - Tests for previously-uncovered paths: the child-side invoke.hook/invoke.event dispatch (real fork, hook + event + non-matching-subscription), and invokeHook's defense-in-depth grant re-check. Julien's settlement-FX-refreeze finding is his budget code (flagged, not touched). * docs(plugins): correct the wiki against the shipped capability surface (#1433 audit) Fixes the 11 doc findings from the PR audit — every corrected claim was cross-checked against the code: - Plugin-Development: CSP connect-src is built from granted http:outbound:<host>, not egress[]; dropped the stale "costs.create is the first and only core mutation"; documented costs.update/delete + ctx.packing/ctx.files; the manifest permission table gained the six missing scopes (db:write:places/days/itinerary/trips, db:meta, hook:trip-warning-provider); the widget slot table gained place-detail. - Plugin-Cookbook: days.create no longer passes a title the schema drops; broadcastToUser uses the real (userId, event, data) signature; fixed the broken #the-trek-ui-design-kit anchor and noted window.trek.ui. - Plugin-Permissions: added db:read:packing, db:read:files and events:subscribe; the provider hooks are implemented in `hooks: {...}` on the definition, not on ctx. * fix(unsplash) allow api key usage * fix(guests): scope guest display names per-trip, not globally (#1446) A guest is a per-trip person, but their name lived in the globally UNIQUE users.username, so uniqueGuestUsername() auto-renamed a second "Jake" (on any other trip) to "Jake 2". Add a non-unique users.display_name: a guest now stores the human name there and gets a uuid-based username that is never shown, and every member view (members list, day-assignment participants, budget members/payers, packing recipients/contributors/bags/assignees) COALESCEs display_name over username. Rename updates display_name with no dedup. Real users are unchanged (display_name NULL → COALESCE falls through to username). Migration adds the nullable column; existing guests keep their current username via the COALESCE fallback. This also unblocks ctx.users.getById (the audit's #4 fix), whose projection selects display_name. Tests: two "Jake" guests on two trips both keep the name; the two codified-the-old-behaviour guest tests corrected. * fix(costs): don't re-freeze a settlement's FX rate on an unrelated edit (#1445) The full audit found that updateSettlement called freezeForeignRate without the "currency unchanged" guard the item path has, so any edit of a foreign-currency settlement (e.g. correcting from/to) re-fetched the LIVE rate and overwrote the frozen one — re-opening an already-balanced position with a small residual, the exact drift #1445 was meant to prevent. freezeForeignRate's unchanged-check was item-centric (it queried budget_items), which a settlement (a different table) can't use. Give it an explicit existingCurrency param; updateSettlement now reads the settlement's stored currency and passes it, so an edit that doesn't change the currency keeps the frozen rate (the service UPDATE already preserves exchange_rate when it's left unset). Tests cover both: unchanged currency keeps the rate, a real currency change re-freezes. * feat(plugins): add inter plugin dependency support and addon dependency support * feat(plugins): add inter plugin dependency support and addon dependency support * docs(plugins) inter dependencies --------- Co-authored-by: Maurice <mauriceboe@icloud.com> Co-authored-by: trongbinhnguyen <43725147+trongbinh15@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
APPEARANCE_SCALE_MAX,
|
||||
APPEARANCE_SCALE_MIN,
|
||||
@@ -6,6 +5,8 @@ import {
|
||||
normalizeAppearance,
|
||||
} from './appearance.schema';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('normalizeAppearance', () => {
|
||||
it('returns the neutral default for undefined / null / non-objects', () => {
|
||||
expect(normalizeAppearance(undefined)).toEqual(DEFAULT_APPEARANCE);
|
||||
|
||||
@@ -179,8 +179,11 @@ export type BudgetUpdatePayersRequest = z.infer<typeof budgetUpdatePayersRequest
|
||||
|
||||
/**
|
||||
* A persisted settle-up transfer (budget_settlements row): "from paid to" a
|
||||
* given amount in the trip base currency. Creating one marks a suggested flow as
|
||||
* paid; deleting it (undo) brings the flow back. Names joined for display.
|
||||
* given amount, entered in the payer's display `currency`. `exchange_rate` is the
|
||||
* live rate frozen at settle time (units of that currency per 1 trip currency), so
|
||||
* a settled position stays balanced when live rates drift (#1445). Legacy rows
|
||||
* have currency = null / exchange_rate = 1 and convert with live rates. Creating
|
||||
* one marks a suggested flow as paid; deleting it (undo) brings the flow back.
|
||||
*/
|
||||
export const budgetSettlementSchema = z.object({
|
||||
id: z.number(),
|
||||
@@ -188,6 +191,8 @@ export const budgetSettlementSchema = z.object({
|
||||
from_user_id: z.number(),
|
||||
to_user_id: z.number(),
|
||||
amount: z.number(),
|
||||
currency: z.string().nullable().optional(),
|
||||
exchange_rate: z.number().optional(),
|
||||
created_at: z.string().optional(),
|
||||
created_by_user_id: z.number().nullable().optional(),
|
||||
from_username: z.string().optional(),
|
||||
@@ -201,6 +206,8 @@ export const budgetCreateSettlementRequestSchema = z.object({
|
||||
from_user_id: z.number(),
|
||||
to_user_id: z.number(),
|
||||
amount: z.number(),
|
||||
// The display currency the amount was entered in; the server freezes its FX rate.
|
||||
currency: z.string().nullable().optional(),
|
||||
});
|
||||
export type BudgetCreateSettlementRequest = z.infer<typeof budgetCreateSettlementRequestSchema>;
|
||||
|
||||
@@ -209,6 +216,7 @@ export const budgetUpdateSettlementRequestSchema = z.object({
|
||||
from_user_id: z.number(),
|
||||
to_user_id: z.number(),
|
||||
amount: z.number(),
|
||||
currency: z.string().nullable().optional(),
|
||||
});
|
||||
export type BudgetUpdateSettlementRequest = z.infer<typeof budgetUpdateSettlementRequestSchema>;
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { collectionPlaceUpdateRequestSchema } from './collection.schema';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('collectionPlaceUpdateRequestSchema', () => {
|
||||
// Regression for #1437: an update that omits `status` must NOT inject a default
|
||||
// of 'idea', otherwise the server overwrites the stored status on every edit-save.
|
||||
it('does not inject a status when the field is absent', () => {
|
||||
const parsed = collectionPlaceUpdateRequestSchema.parse({ name: 'Trevi Fountain' });
|
||||
expect('status' in parsed).toBe(false);
|
||||
expect(parsed.status).toBeUndefined();
|
||||
});
|
||||
|
||||
it('passes through an explicitly provided status', () => {
|
||||
expect(collectionPlaceUpdateRequestSchema.parse({ status: 'want' }).status).toBe('want');
|
||||
expect(collectionPlaceUpdateRequestSchema.parse({ status: 'visited' }).status).toBe('visited');
|
||||
});
|
||||
|
||||
it('catches an invalid status back to idea rather than throwing', () => {
|
||||
expect(collectionPlaceUpdateRequestSchema.parse({ status: 'bogus' as never }).status).toBe('idea');
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
import { placeCategorySchema } from '../place/place.schema';
|
||||
import { tagSchema } from '../tag/tag.schema';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
export const COLLECTION_STATUSES = ['idea', 'want', 'visited'] as const;
|
||||
export const collectionStatusSchema = z.enum(COLLECTION_STATUSES).catch('idea').default('idea');
|
||||
export type CollectionStatus = (typeof COLLECTION_STATUSES)[number];
|
||||
@@ -17,7 +18,11 @@ export type CollectionRole = (typeof COLLECTION_ROLES)[number];
|
||||
export const collectionLinkSchema = z.object({
|
||||
label: z.string().max(120).optional(),
|
||||
// http/https only — blocks javascript:/data: hrefs and forces an absolute link.
|
||||
url: z.string().trim().max(2000).regex(/^https?:\/\/.+/i),
|
||||
url: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(2000)
|
||||
.regex(/^https?:\/\/.+/i),
|
||||
});
|
||||
export type CollectionLink = z.infer<typeof collectionLinkSchema>;
|
||||
export const collectionLinksSchema = z.array(collectionLinkSchema).max(30);
|
||||
@@ -104,7 +109,10 @@ export type Collection = z.infer<typeof collectionSchema>;
|
||||
export const collectionCreateRequestSchema = z.object({
|
||||
name: z.string().min(1).max(120),
|
||||
description: z.string().max(2000).nullable().optional(),
|
||||
color: z.string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/).optional(),
|
||||
color: z
|
||||
.string()
|
||||
.regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/)
|
||||
.optional(),
|
||||
icon: z.string().max(40).optional(),
|
||||
cover_image: z.string().max(500).nullable().optional(),
|
||||
links: collectionLinksSchema.optional(),
|
||||
@@ -165,7 +173,10 @@ export const collectionPlaceUpdateRequestSchema = z.object({
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().nullable().optional(),
|
||||
notes: z.string().nullable().optional(),
|
||||
status: collectionStatusSchema.optional(),
|
||||
// .removeDefault() strips the inner .default('idea') so an ABSENT status parses to
|
||||
// undefined (left unchanged) instead of being injected as 'idea' (see #1437). The
|
||||
// .catch('idea') guard against invalid values is preserved.
|
||||
status: collectionStatusSchema.removeDefault().optional(),
|
||||
category_id: z.number().nullable().optional(),
|
||||
collection_id: z.number().optional(), // move to another list
|
||||
links: collectionLinksSchema.optional(),
|
||||
|
||||
+70
-12
@@ -116,6 +116,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'مُوصى به',
|
||||
'admin.weatherKey': 'مفتاح OpenWeatherMap API',
|
||||
'admin.weatherKeyHint': 'لبيانات الطقس. مجاني من openweathermap.org',
|
||||
'admin.unsplashKey': 'مفتاح واجهة برمجة تطبيقات Unsplash',
|
||||
'admin.unsplashKeyHint': 'للبحث عن الصور. مجاني من unsplash.com/developers',
|
||||
'admin.validateKey': 'اختبار',
|
||||
'admin.keyValid': 'متصل',
|
||||
'admin.keyInvalid': 'غير صالح',
|
||||
@@ -180,6 +182,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'الإضافات الخارجية',
|
||||
'admin.plugins.rescan': 'إعادة الفحص',
|
||||
'admin.plugins.rescanned': 'تمت إعادة فحص مجلد الإضافات',
|
||||
'admin.plugins.upload': 'رفع إضافة',
|
||||
'admin.plugins.dropToUpload': 'أفلت ملف .zip لتثبيت الإضافة',
|
||||
'admin.plugins.uploaded': 'تم رفع الإضافة “{name}” — فعّلها لتشغيلها',
|
||||
'admin.plugins.sideloaded': 'محمّلة يدويًا',
|
||||
'admin.plugins.sideloadedHint': 'تم رفعها يدويًا — ليست من السجل، غير موقّعة وغير مراجَعة',
|
||||
'admin.plugins.browse': 'تصفح الإضافات',
|
||||
'admin.plugins.installed': 'مثبّتة',
|
||||
'admin.plugins.install': 'تثبيت',
|
||||
@@ -194,11 +201,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'لا توجد أخطاء مسجّلة.',
|
||||
'admin.plugins.uninstalled': 'تمت إزالة الإضافة',
|
||||
'admin.plugins.uninstallTitle': 'إزالة الإضافة؟',
|
||||
'admin.plugins.uninstallBody': 'سيؤدي هذا إلى إيقاف الإضافة وإزالة تعليماتها البرمجية وحذف جميع بياناتها. لا يمكن التراجع عن هذا الإجراء.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'سيؤدي هذا إلى إيقاف الإضافة وإزالة تعليماتها البرمجية وحذف جميع بياناتها. لا يمكن التراجع عن هذا الإجراء.',
|
||||
'admin.plugins.status.starting': 'جارٍ البدء…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'صفحة',
|
||||
'admin.plugins.type.integration': 'تكامل',
|
||||
'admin.plugins.type.trip-page': 'صفحة الرحلة',
|
||||
'admin.plugins.reviewed': 'تمت مراجعتها',
|
||||
'admin.plugins.unreviewed': 'لم تتم مراجعتها',
|
||||
'admin.plugins.updated': 'تم تحديث الإضافة',
|
||||
@@ -208,10 +217,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'الإضافات الخارجية',
|
||||
'admin.plugins.subtitle': 'ثبّت وأدر إضافات الجهات الخارجية على خادمك.',
|
||||
'admin.plugins.disabledTitle': 'الإضافات الخارجية معطّلة',
|
||||
'admin.plugins.disabledBody': 'بيئة تشغيل الإضافات متوقفة (TREK_PLUGINS_ENABLED). لا يمكن تشغيل أي إضافة حتى يقوم مسؤول بتفعيلها في إعدادات الخادم.',
|
||||
'admin.plugins.disabledBody':
|
||||
'بيئة تشغيل الإضافات متوقفة (TREK_PLUGINS_ENABLED). لا يمكن تشغيل أي إضافة حتى يقوم مسؤول بتفعيلها في إعدادات الخادم.',
|
||||
'admin.plugins.empty': 'لم يتم تثبيت أي إضافات بعد.',
|
||||
'admin.plugins.loadError': 'تعذّر تحميل الإضافات.',
|
||||
'admin.plugins.scaffoldNote': 'سيتوفر تثبيت الإضافات وتفعيلها في تحديث لاحق. تعرض هذه اللوحة حاليًا الإضافات المثبّتة فقط.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'سيتوفر تثبيت الإضافات وتفعيلها في تحديث لاحق. تعرض هذه اللوحة حاليًا الإضافات المثبّتة فقط.',
|
||||
'admin.plugins.status.active': 'نشطة',
|
||||
'admin.plugins.status.inactive': 'غير نشطة',
|
||||
'admin.plugins.status.disabled': 'معطّلة',
|
||||
@@ -233,33 +244,55 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.reviewedOn': 'تمت المراجعة {date}',
|
||||
'admin.plugins.perm.db:own': 'تخزين بياناتها الخاصة في قاعدة بيانات معزولة',
|
||||
'admin.plugins.perm.db:read:trips': 'قراءة الرحلات التي يمكن للمستخدم الحالي الوصول إليها',
|
||||
'admin.plugins.perm.db:read:users': 'قراءة معلومات الملف الشخصي الأساسية (الاسم والصورة الرمزية — وليس بيانات الاعتماد أبدًا)',
|
||||
'admin.plugins.perm.db:read:users':
|
||||
'قراءة معلومات الملف الشخصي الأساسية (الاسم والصورة الرمزية — وليس بيانات الاعتماد أبدًا)',
|
||||
'admin.plugins.perm.db:read:costs': 'قراءة المصروفات (بنود الميزانية) التي يمكن للمستخدم الحالي الوصول إليها',
|
||||
'admin.plugins.perm.db:read:packing': 'قراءة قوائم الأمتعة للرحلات التي يمكن للمستخدم الحالي الوصول إليها',
|
||||
'admin.plugins.perm.db:read:files': 'قراءة ملفات الرحلات التي يمكن للمستخدم الحالي الوصول إليها',
|
||||
'admin.plugins.perm.db:write:costs': 'إنشاء مصروفات (بنود الميزانية) على الرحلات التي يمكن للمستخدم الحالي تعديلها',
|
||||
'admin.plugins.perm.db:write:places': 'إضافة الأماكن وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
|
||||
'admin.plugins.perm.db:write:days': 'إضافة الأيام وتعديلها وحذفها في الرحلات التي يمكن للمستخدم الحالي تعديلها',
|
||||
'admin.plugins.perm.db:write:itinerary': 'إسناد الأماكن وإزالتها من أيام الرحلات التي يمكن للمستخدم الحالي تعديلها',
|
||||
'admin.plugins.perm.db:write:trips':
|
||||
'تعديل تفاصيل الرحلة (العنوان، التواريخ، العملة…) في الرحلات التي يمكن للمستخدم الحالي تعديلها',
|
||||
'admin.plugins.perm.db:meta': 'إرفاق بياناته الخاصة بالرحلات والأماكن والأيام التي يمكن للمستخدم الحالي الوصول إليها',
|
||||
'admin.plugins.perm.ws:broadcast:trip': 'إرسال تحديثات فورية إلى أعضاء الرحلة',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'إرسال تحديثات فورية إلى مستخدمين بعينهم',
|
||||
'admin.plugins.perm.hook:photo-provider': 'توفير الصور لميزة الذكريات',
|
||||
'admin.plugins.perm.hook:calendar-source': 'توفير الأحداث للتقويم',
|
||||
'admin.plugins.perm.hook:place-detail-provider': 'المساهمة بتفاصيل إضافية (مراجعات، تقييمات، روابط) لمكان ما',
|
||||
'admin.plugins.perm.hook:trip-warning-provider': 'إظهار تحذيرات التحقق على الرحلة (تظهر في المخطط)',
|
||||
'admin.plugins.perm.events:subscribe': 'التفاعل مع أحداث النشاط الأساسية (اسم الحدث والرحلة فقط، دون المحتوى مطلقًا)',
|
||||
'admin.plugins.perm.http:outbound': 'إجراء طلبات صادرة إلى المضيفات المُعلنة الخاصة بها',
|
||||
'admin.plugins.updateConsentTitle': 'يتطلب هذا التحديث أذونات جديدة',
|
||||
'admin.plugins.updateConsentBody': '{name} v{version} يطلب أذونات لم تمنحها بعد. تم تثبيت الإصدار الجديد لكنه يبقى متوقفًا حتى توافق عليه.',
|
||||
'admin.plugins.updateConsentBody':
|
||||
'{name} v{version} يطلب أذونات لم تمنحها بعد. تم تثبيت الإصدار الجديد لكنه يبقى متوقفًا حتى توافق عليه.',
|
||||
'admin.plugins.updateNewPermissions': 'الأذونات المطلوبة حديثًا',
|
||||
'admin.plugins.updateNewEgress': 'اتصالات صادرة جديدة',
|
||||
'admin.plugins.updateApprove': 'الموافقة والتفعيل',
|
||||
'admin.plugins.updateLater': 'إبقاؤه متوقفًا الآن',
|
||||
'admin.plugins.updateKeptOff': 'تم تثبيت التحديث — يبقى متوقفًا حتى توافق على الأذونات الجديدة',
|
||||
'admin.plugins.reviewedMeaning': '"تمت مراجعتها" تعني أن أحد القائمين على TREK فحص هذه الإضافة بحثًا عن البرمجيات الخبيثة في كل إصدار — وليس من حيث الجودة أو ما إذا كانت تعمل. وهي ليست ضمانًا بأن الإضافة غير ضارة.',
|
||||
'admin.plugins.reviewedMeaning':
|
||||
'"تمت مراجعتها" تعني أن أحد القائمين على TREK فحص هذه الإضافة بحثًا عن البرمجيات الخبيثة في كل إصدار — وليس من حيث الجودة أو ما إذا كانت تعمل. وهي ليست ضمانًا بأن الإضافة غير ضارة.',
|
||||
'admin.plugins.security.title': 'كيف يتم احتواء الإضافات — وحدود ذلك',
|
||||
'admin.plugins.security.isolationTitle': 'كل إضافة تعمل في بيئة معزولة',
|
||||
'admin.plugins.security.isolationBody': 'تعمل الإضافة كعملية معزولة خاصة بها لا يمكنها قراءة سوى ملفاتها الخاصة. لا يمكنها قراءة قاعدة بياناتك أو سر تسجيل دخولك أو مفتاح التشفير الخاص بك، ولا يمكنها تشغيل برامج أخرى، ولا يمكنها كتابة ملفات في أي مكان. تعمل واجهتها داخل إطار متصفح محكم لا يمكنه قراءة ملف تعريف ارتباط جلستك أو المساس بصفحة TREK المحيطة به.',
|
||||
'admin.plugins.security.isolationBody':
|
||||
'تعمل الإضافة كعملية معزولة خاصة بها لا يمكنها قراءة سوى ملفاتها الخاصة. لا يمكنها قراءة قاعدة بياناتك أو سر تسجيل دخولك أو مفتاح التشفير الخاص بك، ولا يمكنها تشغيل برامج أخرى، ولا يمكنها كتابة ملفات في أي مكان. تعمل واجهتها داخل إطار متصفح محكم لا يمكنه قراءة ملف تعريف ارتباط جلستك أو المساس بصفحة TREK المحيطة به.',
|
||||
'admin.plugins.security.permsTitle': 'ماذا تعني الأذونات',
|
||||
'admin.plugins.security.permsBody': 'الأذونات المعروضة قبل التثبيت هي حد صارم يفرضه TREK أثناء تشغيل الإضافة — فهي لا تستطيع فعليًا القيام بأي شيء ليس على القائمة. لكنها تخبرك بما يمكن للإضافة فعله، لا بما تفعله بالفعل. الإضافة المسموح لها بقراءة رحلاتك والوصول إلى خادم يمكنها إرسال رحلاتك إلى ذلك الخادم، لذا اقرأ الأذونات والمضيفات الصادرة، وليس الوصف فقط.',
|
||||
'admin.plugins.security.permsBody':
|
||||
'الأذونات المعروضة قبل التثبيت هي حد صارم يفرضه TREK أثناء تشغيل الإضافة — فهي لا تستطيع فعليًا القيام بأي شيء ليس على القائمة. لكنها تخبرك بما يمكن للإضافة فعله، لا بما تفعله بالفعل. الإضافة المسموح لها بقراءة رحلاتك والوصول إلى خادم يمكنها إرسال رحلاتك إلى ذلك الخادم، لذا اقرأ الأذونات والمضيفات الصادرة، وليس الوصف فقط.',
|
||||
'admin.plugins.security.limitsTitle': 'ما لا يمكننا ضمانه',
|
||||
'admin.plugins.security.limitsBody': 'العزل هو حدّ برمجي قوي، لكنه ليس مطلقًا. تعمل الإضافة بالأذونات التي توافق عليها تمامًا، لذا يمكنها ضمن تلك الأذونات أن تتصرف بشكل مختلف عمّا يوحي به وصفها، ويمكنها إرسال البيانات التي تملكها بشكل مشروع إلى المضيفات التي أعلنتها. لا يقرأ TREK ولا يحكم على ما تفعله شيفرة الإضافة فعليًا.',
|
||||
'admin.plugins.security.limitsBody':
|
||||
'العزل هو حدّ برمجي قوي، لكنه ليس مطلقًا. تعمل الإضافة بالأذونات التي توافق عليها تمامًا، لذا يمكنها ضمن تلك الأذونات أن تتصرف بشكل مختلف عمّا يوحي به وصفها، ويمكنها إرسال البيانات التي تملكها بشكل مشروع إلى المضيفات التي أعلنتها. لا يقرأ TREK ولا يحكم على ما تفعله شيفرة الإضافة فعليًا.',
|
||||
'admin.plugins.security.worstTitle': 'أسوأ الحالات',
|
||||
'admin.plugins.security.worstBody': 'الإضافة الضارة التي تُفعّلها يمكنها إساءة استخدام البيانات والاتصالات التي منحتها إياها — على سبيل المثال، تسريب الرحلات المسموح لها بقراءتها. لا يمكنها سرقة كلمات المرور، أو تزوير تسجيل دخول مسؤول، أو تنفيذ أوامر على خادمك، أو الوصول إلى بيانات لم تمنحها. يبقى الضرر ضمن ما وافقت عليه، وإيقاف الإضافة يوقفه.',
|
||||
'admin.plugins.security.worstBody':
|
||||
'الإضافة الضارة التي تُفعّلها يمكنها إساءة استخدام البيانات والاتصالات التي منحتها إياها — على سبيل المثال، تسريب الرحلات المسموح لها بقراءتها. لا يمكنها سرقة كلمات المرور، أو تزوير تسجيل دخول مسؤول، أو تنفيذ أوامر على خادمك، أو الوصول إلى بيانات لم تمنحها. يبقى الضرر ضمن ما وافقت عليه، وإيقاف الإضافة يوقفه.',
|
||||
'admin.plugins.security.reviewedTitle': 'ماذا تعني "تمت مراجعتها"',
|
||||
'admin.plugins.security.reviewedBody': 'الإضافة التي تمت مراجعتها فُحصت يدويًا بحثًا عن البرمجيات الخبيثة بواسطة أحد القائمين على TREK في كل إصدار — جرى فحصها بحثًا عن شيفرة ضارة، لا لمعرفة ما إذا كانت تعمل جيدًا. وهي ليست وعدًا بأن الإضافة غير ضارة.',
|
||||
'admin.plugins.security.reviewedBody':
|
||||
'الإضافة التي تمت مراجعتها فُحصت يدويًا بحثًا عن البرمجيات الخبيثة بواسطة أحد القائمين على TREK في كل إصدار — جرى فحصها بحثًا عن شيفرة ضارة، لا لمعرفة ما إذا كانت تعمل جيدًا. وهي ليست وعدًا بأن الإضافة غير ضارة.',
|
||||
'admin.plugins.security.trustTitle': 'الخلاصة',
|
||||
'admin.plugins.security.trustBody': 'تثبيت إضافة يشبه تثبيت أي تطبيق من جهة خارجية: لا تُضِف سوى شيفرة من مطورين تثق بهم، وعند الشك، افحصها بنفسك أولًا. لا يتحمل TREK أي مسؤولية عن إضافات الجهات الخارجية.',
|
||||
'admin.plugins.security.trustBody':
|
||||
'تثبيت إضافة يشبه تثبيت أي تطبيق من جهة خارجية: لا تُضِف سوى شيفرة من مطورين تثق بهم، وعند الشك، افحصها بنفسك أولًا. لا يتحمل TREK أي مسؤولية عن إضافات الجهات الخارجية.',
|
||||
'admin.plugins.runtimeOn': 'بيئة التشغيل مفعّلة',
|
||||
'admin.plugins.tabDiscover': 'اكتشاف',
|
||||
'admin.plugins.searchPlaceholder': 'ابحث عن الإضافات…',
|
||||
@@ -281,11 +314,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'تمت إعادة تشغيل الإضافة',
|
||||
'admin.plugins.cap.readsTrips': 'يقرأ رحلاتك',
|
||||
'admin.plugins.cap.readsUsers': 'يقرأ الملفات الشخصية الأساسية',
|
||||
'admin.plugins.cap.readsCosts': 'يقرأ مصروفاتك',
|
||||
'admin.plugins.cap.readsPacking': 'يقرأ قوائم الأمتعة',
|
||||
'admin.plugins.cap.readsFiles': 'يقرأ ملفات الرحلة',
|
||||
'admin.plugins.cap.writesCosts': 'يضيف مصروفات',
|
||||
'admin.plugins.cap.writesPlaces': 'يعدّل الأماكن',
|
||||
'admin.plugins.cap.writesDays': 'يعدّل الأيام',
|
||||
'admin.plugins.cap.writesItinerary': 'يعدّل خط السير',
|
||||
'admin.plugins.cap.writesTrips': 'يعدّل الرحلات',
|
||||
'admin.plugins.cap.metadata': 'يضيف بيانات وصفية',
|
||||
'admin.plugins.cap.widget': 'أداة لوحة التحكم',
|
||||
'admin.plugins.cap.heroWidget': 'أداة بطاقة الصعود',
|
||||
'admin.plugins.cap.placeSlot': 'تفاصيل المكان',
|
||||
'admin.plugins.cap.realtime': 'تحديثات فورية',
|
||||
'admin.plugins.cap.photos': 'يوفّر الصور',
|
||||
'admin.plugins.cap.calendar': 'يوفّر أحداث التقويم',
|
||||
'admin.plugins.cap.placeDetails': 'يُثري الأماكن',
|
||||
'admin.plugins.cap.warnings': 'يرصد المشكلات',
|
||||
'admin.plugins.cap.events': 'يتفاعل مع النشاط',
|
||||
'admin.plugins.cap.requiresAddon': 'يتطلب {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'يحتاج إلى {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'فعّل الإضافات المطلوبة أولاً: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'تم تفعيل الإضافات المطلوبة أولاً: {plugins}',
|
||||
'admin.plugins.dep.downloaded': 'تم تنزيل {id}',
|
||||
'admin.plugins.dep.resolveTitle': 'تبعيات مفقودة',
|
||||
'admin.plugins.dep.resolveBody': 'يحتاج “{name}” إلى تثبيت هذه الإضافات قبل تفعيله.',
|
||||
'admin.plugins.dep.requires': 'يتطلب {version}',
|
||||
'admin.plugins.dep.mismatch': 'يحتاج إلى {wanted} — المثبَّت هو {installed}',
|
||||
'admin.plugins.dep.download': 'تنزيل',
|
||||
'admin.plugins.dep.update': 'تحديث',
|
||||
'admin.plugins.dep.resolveHint': 'ينزّل أحدث إصدار متوافق، بما في ذلك تبعياته الخاصة.',
|
||||
'admin.plugins.accessTitle': 'ما الذي يمكنها الوصول إليه',
|
||||
'admin.plugins.connectsTitle': 'يتصل بـ',
|
||||
'admin.plugins.detailsTitle': 'التفاصيل',
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'لا شيء مستحق لك',
|
||||
'costs.yourShare': 'حصتك',
|
||||
'costs.youPaid': 'أنت دفعت',
|
||||
'costs.outstanding': 'مبلغ غير مسدَّد',
|
||||
'costs.outstandingSub': 'مصاريف بلا دافع بعد',
|
||||
'costs.outstandingItems': 'مصاريف بحاجة إلى دافع',
|
||||
'costs.expenses': 'المصروفات',
|
||||
'costs.entries': '{count} إدخالات',
|
||||
'costs.searchPlaceholder': 'ابحث في المصروفات…',
|
||||
@@ -116,10 +119,10 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'في الإجمالي فقط — لم تتم التسوية بعد',
|
||||
'costs.tapToInclude': 'اضغط للتضمين',
|
||||
'costs.amount': 'المبلغ',
|
||||
'costs.split': "Split",
|
||||
'costs.splitEqually': "Equally",
|
||||
'costs.splitCustom': "Custom",
|
||||
'costs.splitTicket': "Ticket",
|
||||
'costs.split': 'Split',
|
||||
'costs.splitEqually': 'Equally',
|
||||
'costs.splitCustom': 'Custom',
|
||||
'costs.splitTicket': 'Ticket',
|
||||
'costs.filter.allCategories': 'كل الفئات',
|
||||
'costs.filter.allDays': 'كل الأيام',
|
||||
'costs.expensesCount': '{count} مصروف',
|
||||
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'إعادة تسمية',
|
||||
'collections.listMenu': 'خيارات القائمة',
|
||||
'collections.deleteList': 'حذف القائمة',
|
||||
'collections.deleteListConfirm': 'سيؤدي هذا إلى حذف القائمة وجميع الأماكن المحفوظة فيها نهائيًا. لا يمكن التراجع عن ذلك.',
|
||||
'collections.deleteListConfirm':
|
||||
'سيؤدي هذا إلى حذف القائمة وجميع الأماكن المحفوظة فيها نهائيًا. لا يمكن التراجع عن ذلك.',
|
||||
'collections.listName': 'اسم القائمة',
|
||||
'collections.listNamePlaceholder': 'مثال: طوكيو 2025',
|
||||
'collections.listColor': 'اللون',
|
||||
@@ -127,7 +128,8 @@ const collection: TranslationStrings = {
|
||||
'collections.share.memberHint': 'يمكن لمالك القائمة فقط دعوة الأشخاص أو إزالتهم.',
|
||||
'collections.share.cancel': 'إلغاء',
|
||||
'collections.share.leave': 'مغادرة القائمة',
|
||||
'collections.share.leaveConfirm': 'هل تريد مغادرة هذه القائمة المشتركة؟ ستفقد إمكانية الوصول إليها حتى تتم دعوتك مرة أخرى.',
|
||||
'collections.share.leaveConfirm':
|
||||
'هل تريد مغادرة هذه القائمة المشتركة؟ ستفقد إمكانية الوصول إليها حتى تتم دعوتك مرة أخرى.',
|
||||
'collections.share.left': 'لقد غادرت القائمة',
|
||||
'collections.share.remove': 'إزالة',
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { TranslationStrings } from '../types';
|
||||
|
||||
// English fallback until 'ar' is translated.
|
||||
const help: TranslationStrings = {
|
||||
'help.title': "Help & Docs",
|
||||
'help.search': "Search docs…",
|
||||
'help.contents': "Contents",
|
||||
'help.noResults': "No matching pages.",
|
||||
'help.title': 'Help & Docs',
|
||||
'help.search': 'Search docs…',
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody': "The help content is fetched from the TREK wiki. Check your connection and try again.",
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -86,7 +86,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'تسجيل الدخول باستخدام مفتاح المرور',
|
||||
'login.passkey.failed': 'فشل تسجيل الدخول بمفتاح المرور. يرجى المحاولة مرة أخرى.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'اختر نوعًا واحدًا على الأقل.',
|
||||
'places.selectionCount': '{count} محدد',
|
||||
'places.deleteSelected': 'حذف المحدد',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': 'تم استيراد {count} مكان من KMZ/KML',
|
||||
'places.urlResolved': 'تم استيراد المكان من الرابط',
|
||||
'places.importList': 'استيراد قائمة',
|
||||
|
||||
@@ -318,7 +318,8 @@ const settings: TranslationStrings = {
|
||||
'settings.airtrail.test.success': 'متصل — تم العثور على {count} رحلة/رحلات',
|
||||
'settings.airtrail.test.failed': 'فشل الاتصال',
|
||||
'settings.aiParsing.title': 'التحليل بالذكاء الاصطناعي',
|
||||
'settings.aiParsing.hint': 'استخدم نموذج الذكاء الاصطناعي الخاص بك لاستخراج الحجوزات من الملفات المرفوعة. لا يسري هذا إلا عندما لا يكون المسؤول قد أعدّ نموذجًا للنظام بأكمله.',
|
||||
'settings.aiParsing.hint':
|
||||
'استخدم نموذج الذكاء الاصطناعي الخاص بك لاستخراج الحجوزات من الملفات المرفوعة. لا يسري هذا إلا عندما لا يكون المسؤول قد أعدّ نموذجًا للنظام بأكمله.',
|
||||
'settings.aiParsing.provider': 'المزوّد',
|
||||
'settings.aiParsing.providerLocal': 'محلي (Ollama)',
|
||||
'settings.aiParsing.providerOpenai': 'OpenAI',
|
||||
@@ -348,7 +349,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -378,7 +380,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -401,10 +404,12 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'ذاكرة التخزين المؤقت دون اتصال',
|
||||
'settings.offline.mode.title': 'وضع عدم الاتصال',
|
||||
'settings.offline.mode.force': 'فرض وضع عدم الاتصال',
|
||||
'settings.offline.mode.forceHint': 'اعمل من البيانات المخزّنة مؤقتًا فقط وضع تغييراتك في قائمة الانتظار. أوقفه لإعادة الاتصال والمزامنة.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'اعمل من البيانات المخزّنة مؤقتًا فقط وضع تغييراتك في قائمة الانتظار. أوقفه لإعادة الاتصال والمزامنة.',
|
||||
'settings.offline.mode.active': 'وضع عدم الاتصال مفعّل — تغييراتك في قائمة الانتظار وستتم مزامنتها عند إيقافه.',
|
||||
'settings.offline.prepare.title': 'التحضير للعمل دون اتصال',
|
||||
'settings.offline.prepare.hint': 'نزّل كل ما تحتاجه — بيانات الرحلة والمستندات وبلاطات الخريطة — قبل أن تفقد الاتصال.',
|
||||
'settings.offline.prepare.hint':
|
||||
'نزّل كل ما تحتاجه — بيانات الرحلة والمستندات وبلاطات الخريطة — قبل أن تفقد الاتصال.',
|
||||
'settings.offline.prepare.button': 'تنزيل للاستخدام دون اتصال',
|
||||
'settings.offline.prepare.running': 'جارٍ التنزيل…',
|
||||
'settings.offline.prepare.done': 'جاهز للاستخدام دون اتصال',
|
||||
@@ -415,12 +420,14 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'جارٍ المزامنة…',
|
||||
'settings.offline.storage.title': 'ما يتم تخزينه دون اتصال',
|
||||
'settings.offline.storage.tiles': 'تخزين بلاطات الخريطة دون اتصال',
|
||||
'settings.offline.storage.tilesHint': 'تستهلك بلاطات الخريطة أكبر مساحة تخزين. أوقف هذا للاحتفاظ ببيانات الرحلة والمستندات فقط دون اتصال.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'تستهلك بلاطات الخريطة أكبر مساحة تخزين. أوقف هذا للاحتفاظ ببيانات الرحلة والمستندات فقط دون اتصال.',
|
||||
'settings.offline.storage.tripsTitle': 'الرحلات',
|
||||
'settings.offline.storage.tripOn': 'مخزّن دون اتصال',
|
||||
'settings.offline.storage.tripOff': 'غير مخزّن',
|
||||
'settings.offline.clear': 'مسح ذاكرة التخزين المؤقت',
|
||||
'settings.offline.clearConfirm': 'هل تريد مسح جميع بيانات الرحلة المخزّنة دون اتصال؟ يمكنك إعادة المزامنة في أي وقت أثناء الاتصال.',
|
||||
'settings.offline.clearConfirm':
|
||||
'هل تريد مسح جميع بيانات الرحلة المخزّنة دون اتصال؟ يمكنك إعادة المزامنة في أي وقت أثناء الاتصال.',
|
||||
'settings.offline.stats.trips': 'الرحلات المخزّنة مؤقتًا',
|
||||
'settings.offline.stats.pending': 'تغييرات معلّقة',
|
||||
'settings.offline.stats.failed': 'تغييرات فاشلة',
|
||||
@@ -428,7 +435,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': 'لا توجد رحلات مخزّنة بعد. اتصل بالإنترنت للمزامنة.',
|
||||
'settings.offline.loading': 'جارٍ التحميل…',
|
||||
'settings.offline.conflicts.title': 'تعارضات المزامنة',
|
||||
'settings.offline.conflicts.hint': 'تتعارض التغييرات التي أجريتها دون اتصال مع تغييرات أحدث على الخادم. اختر الإصدار الذي تريد الاحتفاظ به.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'تتعارض التغييرات التي أجريتها دون اتصال مع تغييرات أحدث على الخادم. اختر الإصدار الذي تريد الاحتفاظ به.',
|
||||
'settings.offline.conflicts.keepMine': 'الاحتفاظ بنسختي',
|
||||
'settings.offline.conflicts.keepServer': 'الاحتفاظ بنسخة الخادم',
|
||||
'settings.offline.conflicts.mine': 'نسختك',
|
||||
|
||||
@@ -28,7 +28,8 @@ const trip: TranslationStrings = {
|
||||
'trip.confirm.deletePlaces': 'حذف {count} أماكن؟',
|
||||
'trip.toast.placesDeleted': 'تم حذف {count} أماكن',
|
||||
'trip.invite.linkTitle': 'رابط دعوة الرحلة',
|
||||
'trip.invite.linkHint': 'أي شخص لديه حساب TREK ويفتح هذا الرابط ينضم إلى الرحلة كعضو. أعد الإنشاء لإبطال الرابط القديم.',
|
||||
'trip.invite.linkHint':
|
||||
'أي شخص لديه حساب TREK ويفتح هذا الرابط ينضم إلى الرحلة كعضو. أعد الإنشاء لإبطال الرابط القديم.',
|
||||
'trip.invite.create': 'إنشاء رابط دعوة',
|
||||
'trip.invite.regenerate': 'إعادة الإنشاء',
|
||||
'trip.invite.disable': 'تعطيل',
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'تم تحسين المسار',
|
||||
'undo.deletePlace': 'تم حذف المكان',
|
||||
'undo.deletePlaces': 'تم حذف الأماكن',
|
||||
'undo.changeCategory': "Category changed",
|
||||
'undo.changeCategory': 'Category changed',
|
||||
'undo.moveDay': 'تم نقل المكان إلى يوم آخر',
|
||||
'undo.lock': 'تم تبديل قفل المكان',
|
||||
'undo.importGpx': 'استيراد GPX',
|
||||
|
||||
@@ -94,6 +94,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Recomendado',
|
||||
'admin.weatherKey': 'Chave OpenWeatherMap',
|
||||
'admin.weatherKeyHint': 'Para dados meteorológicos. Grátis em openweathermap.org',
|
||||
'admin.unsplashKey': 'Chave de API do Unsplash',
|
||||
'admin.unsplashKeyHint': 'Para busca de imagens. Grátis em unsplash.com/developers',
|
||||
'admin.validateKey': 'Testar',
|
||||
'admin.keyValid': 'Conectado',
|
||||
'admin.keyInvalid': 'Inválida',
|
||||
@@ -159,6 +161,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Plugins',
|
||||
'admin.plugins.rescan': 'Reescanear',
|
||||
'admin.plugins.rescanned': 'Pasta de plugins reescaneada',
|
||||
'admin.plugins.upload': 'Enviar plugin',
|
||||
'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.sideloadedHint': 'Enviado manualmente — não é do registro, sem assinatura e sem revisão',
|
||||
'admin.plugins.browse': 'Explorar plugins',
|
||||
'admin.plugins.installed': 'Instalado',
|
||||
'admin.plugins.install': 'Instalar',
|
||||
@@ -173,11 +180,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'Nenhum erro registrado.',
|
||||
'admin.plugins.uninstalled': 'Plugin desinstalado',
|
||||
'admin.plugins.uninstallTitle': 'Desinstalar plugin?',
|
||||
'admin.plugins.uninstallBody': 'Isso interrompe o plugin, remove seu código e exclui todos os dados dele. Esta ação não pode ser desfeita.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'Isso interrompe o plugin, remove seu código e exclui todos os dados dele. Esta ação não pode ser desfeita.',
|
||||
'admin.plugins.status.starting': 'Iniciando…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Página',
|
||||
'admin.plugins.type.integration': 'Integração',
|
||||
'admin.plugins.type.trip-page': 'Página da viagem',
|
||||
'admin.plugins.reviewed': 'Revisado',
|
||||
'admin.plugins.unreviewed': 'Não revisado',
|
||||
'admin.plugins.updated': 'Plugin atualizado',
|
||||
@@ -187,10 +196,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Plugins',
|
||||
'admin.plugins.subtitle': 'Instale e gerencie plugins de terceiros na sua instância.',
|
||||
'admin.plugins.disabledTitle': 'Os plugins estão desativados',
|
||||
'admin.plugins.disabledBody': 'O runtime de plugins está desligado (TREK_PLUGINS_ENABLED). Nenhum plugin pode ser executado até que um admin o ative na configuração do servidor.',
|
||||
'admin.plugins.disabledBody':
|
||||
'O runtime de plugins está desligado (TREK_PLUGINS_ENABLED). Nenhum plugin pode ser executado até que um admin o ative na configuração do servidor.',
|
||||
'admin.plugins.empty': 'Nenhum plugin instalado ainda.',
|
||||
'admin.plugins.loadError': 'Não foi possível carregar os plugins.',
|
||||
'admin.plugins.scaffoldNote': 'A instalação e ativação de plugins chegarão em uma atualização futura. Este painel atualmente lista apenas os plugins instalados.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'A instalação e ativação de plugins chegarão em uma atualização futura. Este painel atualmente lista apenas os plugins instalados.',
|
||||
'admin.plugins.status.active': 'Ativo',
|
||||
'admin.plugins.status.inactive': 'Inativo',
|
||||
'admin.plugins.status.disabled': 'Desativado',
|
||||
@@ -213,10 +224,27 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Armazenar seus próprios dados em um banco de dados isolado',
|
||||
'admin.plugins.perm.db:read:trips': 'Ler viagens às quais o usuário atual tem acesso',
|
||||
'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:read:files': 'Ler arquivos de viagens às quais o usuário atual tem acesso',
|
||||
'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',
|
||||
'admin.plugins.perm.db:write:itinerary':
|
||||
'Atribuir e remover locais em dias de viagens que o usuário atual pode editar',
|
||||
'admin.plugins.perm.db:write:trips':
|
||||
'Editar detalhes da viagem (título, datas, moeda…) em viagens que o usuário atual pode editar',
|
||||
'admin.plugins.perm.db:meta':
|
||||
'Anexar seus próprios dados privados a viagens, locais e dias que o usuário atual pode acessar',
|
||||
'admin.plugins.perm.ws:broadcast:trip': 'Enviar atualizações em tempo real aos membros da viagem',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'Enviar atualizações em tempo real a usuários individuais',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Fornecer fotos para as Memórias',
|
||||
'admin.plugins.perm.hook:calendar-source': 'Fornecer eventos para o calendário',
|
||||
'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.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.updateConsentTitle': 'Esta atualização precisa de novas permissões',
|
||||
'admin.plugins.updateConsentBody':
|
||||
@@ -268,11 +296,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Plugin reiniciado',
|
||||
'admin.plugins.cap.readsTrips': 'Lê suas viagens',
|
||||
'admin.plugins.cap.readsUsers': 'Lê perfis básicos',
|
||||
'admin.plugins.cap.readsCosts': 'Lê suas despesas',
|
||||
'admin.plugins.cap.readsPacking': 'Lê listas de bagagem',
|
||||
'admin.plugins.cap.readsFiles': 'Lê arquivos de viagem',
|
||||
'admin.plugins.cap.writesCosts': 'Adiciona despesas',
|
||||
'admin.plugins.cap.writesPlaces': 'Edita locais',
|
||||
'admin.plugins.cap.writesDays': 'Edita dias',
|
||||
'admin.plugins.cap.writesItinerary': 'Edita o itinerário',
|
||||
'admin.plugins.cap.writesTrips': 'Edita viagens',
|
||||
'admin.plugins.cap.metadata': 'Adiciona metadados',
|
||||
'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.realtime': 'Atualizações em tempo real',
|
||||
'admin.plugins.cap.photos': 'Fornece fotos',
|
||||
'admin.plugins.cap.calendar': 'Fornece eventos de calendário',
|
||||
'admin.plugins.cap.placeDetails': 'Enriquece locais',
|
||||
'admin.plugins.cap.warnings': 'Sinaliza problemas',
|
||||
'admin.plugins.cap.events': 'Reage a atividades',
|
||||
'admin.plugins.cap.requiresAddon': 'Requer {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'Requer {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Ative primeiro os complementos necessários: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Plugins necessários ativados primeiro: {plugins}',
|
||||
'admin.plugins.dep.downloaded': '{id} baixado',
|
||||
'admin.plugins.dep.resolveTitle': 'Dependências ausentes',
|
||||
'admin.plugins.dep.resolveBody': '“{name}” precisa que estes plugins estejam instalados antes de ser ativado.',
|
||||
'admin.plugins.dep.requires': 'Requer {version}',
|
||||
'admin.plugins.dep.mismatch': 'Requer {wanted} — {installed} está instalado',
|
||||
'admin.plugins.dep.download': 'Baixar',
|
||||
'admin.plugins.dep.update': 'Atualizar',
|
||||
'admin.plugins.dep.resolveHint': 'Baixa a versão compatível mais recente, incluindo suas próprias dependências.',
|
||||
'admin.plugins.accessTitle': 'O que ele pode acessar',
|
||||
'admin.plugins.connectsTitle': 'Conecta-se a',
|
||||
'admin.plugins.detailsTitle': 'Detalhes',
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'Ninguém deve nada a você',
|
||||
'costs.yourShare': 'Sua parte',
|
||||
'costs.youPaid': 'Você pagou',
|
||||
'costs.outstanding': 'Valor pendente',
|
||||
'costs.outstandingSub': 'Despesas sem pagador',
|
||||
'costs.outstandingItems': 'despesas sem pagador',
|
||||
'costs.expenses': 'Despesas',
|
||||
'costs.entries': '{count} lançamentos',
|
||||
'costs.searchPlaceholder': 'Buscar despesas…',
|
||||
@@ -116,10 +119,10 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'Apenas no total — ainda não acertado',
|
||||
'costs.tapToInclude': 'Toque para incluir',
|
||||
'costs.amount': 'Valor',
|
||||
'costs.split': "Split",
|
||||
'costs.splitEqually': "Equally",
|
||||
'costs.splitCustom': "Custom",
|
||||
'costs.splitTicket': "Ticket",
|
||||
'costs.split': 'Split',
|
||||
'costs.splitEqually': 'Equally',
|
||||
'costs.splitCustom': 'Custom',
|
||||
'costs.splitTicket': 'Ticket',
|
||||
'costs.filter.allCategories': 'Todas as categorias',
|
||||
'costs.filter.allDays': 'Todos os dias',
|
||||
'costs.expensesCount': '{count} despesas',
|
||||
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'Renomear',
|
||||
'collections.listMenu': 'Opções da lista',
|
||||
'collections.deleteList': 'Excluir lista',
|
||||
'collections.deleteListConfirm': 'Isso exclui permanentemente a lista e todos os lugares salvos nela. Não é possível desfazer.',
|
||||
'collections.deleteListConfirm':
|
||||
'Isso exclui permanentemente a lista e todos os lugares salvos nela. Não é possível desfazer.',
|
||||
'collections.listName': 'Nome da lista',
|
||||
'collections.listNamePlaceholder': 'ex.: Tóquio 2025',
|
||||
'collections.listColor': 'Cor',
|
||||
@@ -127,7 +128,8 @@ const collection: TranslationStrings = {
|
||||
'collections.share.memberHint': 'Apenas o proprietário da lista pode convidar ou remover pessoas.',
|
||||
'collections.share.cancel': 'Cancelar',
|
||||
'collections.share.leave': 'Sair da lista',
|
||||
'collections.share.leaveConfirm': 'Sair desta lista compartilhada? Você perderá o acesso até ser convidado novamente.',
|
||||
'collections.share.leaveConfirm':
|
||||
'Sair desta lista compartilhada? Você perderá o acesso até ser convidado novamente.',
|
||||
'collections.share.left': 'Você saiu da lista',
|
||||
'collections.share.remove': 'Remover',
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { TranslationStrings } from '../types';
|
||||
|
||||
// English fallback until 'br' is translated.
|
||||
const help: TranslationStrings = {
|
||||
'help.title': "Help & Docs",
|
||||
'help.search': "Search docs…",
|
||||
'help.contents': "Contents",
|
||||
'help.noResults': "No matching pages.",
|
||||
'help.title': 'Help & Docs',
|
||||
'help.search': 'Search docs…',
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody': "The help content is fetched from the TREK wiki. Check your connection and try again.",
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -85,7 +85,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'Entrar com uma passkey',
|
||||
'login.passkey.failed': 'Falha ao entrar com passkey. Tente novamente.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Selecione pelo menos um tipo.',
|
||||
'places.selectionCount': '{count} selecionado(s)',
|
||||
'places.deleteSelected': 'Excluir seleção',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': '{count} lugares importados de KMZ/KML',
|
||||
'places.urlResolved': 'Lugar importado da URL',
|
||||
'places.importList': 'Importar lista',
|
||||
|
||||
@@ -29,7 +29,8 @@ const settings: TranslationStrings = {
|
||||
'settings.mapStylePlaceholder': 'Selecionar um estilo Mapbox',
|
||||
'settings.mapStyleHint': 'Preset ou sua própria URL mapbox://styles/USER/ID',
|
||||
'settings.mapOpenFreeMapStylePlaceholder': 'Selecionar um estilo OpenFreeMap',
|
||||
'settings.mapOpenFreeMapStyleHint': 'Preset ou URL de estilo OpenFreeMap. Os estilos OpenFreeMap funcionam sem token.',
|
||||
'settings.mapOpenFreeMapStyleHint':
|
||||
'Preset ou URL de estilo OpenFreeMap. Os estilos OpenFreeMap funcionam sem token.',
|
||||
'settings.map3dBuildings': 'Prédios 3D & terreno',
|
||||
'settings.map3dHint': 'Inclinação + extrusões 3D reais de prédios — funciona em todo estilo, incluindo satélite.',
|
||||
'settings.mapHighQuality': 'Modo alta qualidade',
|
||||
@@ -328,18 +329,21 @@ const settings: TranslationStrings = {
|
||||
'settings.airtrail.test.success': 'Conectado — {count} voo(s) encontrado(s)',
|
||||
'settings.airtrail.test.failed': 'Falha na conexão',
|
||||
'settings.aiParsing.title': 'Análise por IA',
|
||||
'settings.aiParsing.hint': 'Use seu próprio modelo de IA para extrair reservas dos arquivos enviados. Isso se aplica apenas quando o administrador não configurou um modelo para toda a instância.',
|
||||
'settings.aiParsing.hint':
|
||||
'Use seu próprio modelo de IA para extrair reservas dos arquivos enviados. Isso se aplica apenas quando o administrador não configurou um modelo para toda a instância.',
|
||||
'settings.aiParsing.provider': 'Provedor',
|
||||
'settings.aiParsing.providerLocal': 'Local (Ollama)',
|
||||
'settings.aiParsing.providerOpenai': 'OpenAI',
|
||||
'settings.aiParsing.providerAnthropic': 'Anthropic',
|
||||
'settings.aiParsing.model': 'Modelo',
|
||||
'settings.aiParsing.baseUrl': 'URL base',
|
||||
'settings.aiParsing.baseUrlHint': 'Onde o modelo é executado — um servidor Ollama local ou um endpoint compatível com OpenAI.',
|
||||
'settings.aiParsing.baseUrlHint':
|
||||
'Onde o modelo é executado — um servidor Ollama local ou um endpoint compatível com OpenAI.',
|
||||
'settings.aiParsing.apiKey': 'Chave de API',
|
||||
'settings.aiParsing.apiKeyHint': 'Armazenada de forma criptografada. Deixe em branco para manter a chave atual.',
|
||||
'settings.aiParsing.multimodal': 'Enviar documentos como imagens',
|
||||
'settings.aiParsing.multimodalHint': 'Para modelos com capacidade de visão — envia o PDF original em vez do texto extraído.',
|
||||
'settings.aiParsing.multimodalHint':
|
||||
'Para modelos com capacidade de visão — envia o PDF original em vez do texto extraído.',
|
||||
'settings.aiParsing.toast.saved': 'Configurações de IA salvas',
|
||||
'settings.aiParsing.toast.saveError': 'Não foi possível salvar as configurações de IA',
|
||||
'settings.tabs.appearance': 'Appearance',
|
||||
@@ -358,7 +362,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -388,7 +393,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -411,10 +417,13 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Cache offline',
|
||||
'settings.offline.mode.title': 'Modo offline',
|
||||
'settings.offline.mode.force': 'Forçar modo offline',
|
||||
'settings.offline.mode.forceHint': 'Trabalhe apenas com os dados em cache e enfileire suas alterações. Desative para reconectar e sincronizar.',
|
||||
'settings.offline.mode.active': 'O modo offline está ligado — suas alterações estão na fila e serão sincronizadas quando você o desativar.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'Trabalhe apenas com os dados em cache e enfileire suas alterações. Desative para reconectar e sincronizar.',
|
||||
'settings.offline.mode.active':
|
||||
'O modo offline está ligado — suas alterações estão na fila e serão sincronizadas quando você o desativar.',
|
||||
'settings.offline.prepare.title': 'Preparar para uso offline',
|
||||
'settings.offline.prepare.hint': 'Baixe tudo o que você precisa — dados da viagem, documentos e blocos do mapa — antes de perder a conexão.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Baixe tudo o que você precisa — dados da viagem, documentos e blocos do mapa — antes de perder a conexão.',
|
||||
'settings.offline.prepare.button': 'Baixar para uso offline',
|
||||
'settings.offline.prepare.running': 'Baixando…',
|
||||
'settings.offline.prepare.done': 'Pronto para uso offline',
|
||||
@@ -425,12 +434,14 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Sincronizando…',
|
||||
'settings.offline.storage.title': 'O que armazenar offline',
|
||||
'settings.offline.storage.tiles': 'Armazenar blocos do mapa offline',
|
||||
'settings.offline.storage.tilesHint': 'Os blocos do mapa usam mais armazenamento. Desative para manter offline apenas os dados da viagem e os documentos.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'Os blocos do mapa usam mais armazenamento. Desative para manter offline apenas os dados da viagem e os documentos.',
|
||||
'settings.offline.storage.tripsTitle': 'Viagens',
|
||||
'settings.offline.storage.tripOn': 'Armazenada offline',
|
||||
'settings.offline.storage.tripOff': 'Não armazenada',
|
||||
'settings.offline.clear': 'Limpar cache',
|
||||
'settings.offline.clearConfirm': 'Limpar todos os dados de viagem offline? Você pode sincronizar novamente a qualquer momento quando estiver online.',
|
||||
'settings.offline.clearConfirm':
|
||||
'Limpar todos os dados de viagem offline? Você pode sincronizar novamente a qualquer momento quando estiver online.',
|
||||
'settings.offline.stats.trips': 'Viagens em cache',
|
||||
'settings.offline.stats.pending': 'Alterações pendentes',
|
||||
'settings.offline.stats.failed': 'Alterações com falha',
|
||||
@@ -438,7 +449,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': 'Nenhuma viagem em cache ainda. Conecte-se à internet para sincronizar.',
|
||||
'settings.offline.loading': 'Carregando…',
|
||||
'settings.offline.conflicts.title': 'Conflitos de sincronização',
|
||||
'settings.offline.conflicts.hint': 'As alterações que você fez offline conflitam com alterações mais recentes no servidor. Escolha qual versão manter.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'As alterações que você fez offline conflitam com alterações mais recentes no servidor. Escolha qual versão manter.',
|
||||
'settings.offline.conflicts.keepMine': 'Manter a minha',
|
||||
'settings.offline.conflicts.keepServer': 'Manter a do servidor',
|
||||
'settings.offline.conflicts.mine': 'Sua versão',
|
||||
|
||||
@@ -28,7 +28,8 @@ const trip: TranslationStrings = {
|
||||
'trip.toast.placesDeleted': '{count} lugares excluídos',
|
||||
'trip.loadingPhotos': 'Carregando fotos dos lugares...',
|
||||
'trip.invite.linkTitle': 'Link de convite da viagem',
|
||||
'trip.invite.linkHint': 'Qualquer pessoa com uma conta TREK que abrir este link entra na viagem como membro. Gere novamente para invalidar o link antigo.',
|
||||
'trip.invite.linkHint':
|
||||
'Qualquer pessoa com uma conta TREK que abrir este link entra na viagem como membro. Gere novamente para invalidar o link antigo.',
|
||||
'trip.invite.create': 'Criar link de convite',
|
||||
'trip.invite.regenerate': 'Gerar novamente',
|
||||
'trip.invite.disable': 'Desativar',
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'Rota otimizada',
|
||||
'undo.deletePlace': 'Local excluído',
|
||||
'undo.deletePlaces': 'Lugares excluídos',
|
||||
'undo.changeCategory': "Category changed",
|
||||
'undo.changeCategory': 'Category changed',
|
||||
'undo.moveDay': 'Local movido para outro dia',
|
||||
'undo.lock': 'Bloqueio do local alternado',
|
||||
'undo.importGpx': 'Importação de GPX',
|
||||
|
||||
@@ -94,6 +94,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Doporučeno',
|
||||
'admin.weatherKey': 'OpenWeatherMap API klíč',
|
||||
'admin.weatherKeyHint': 'Pro data o počasí. Zdarma na openweathermap.org',
|
||||
'admin.unsplashKey': 'Klíč API Unsplash',
|
||||
'admin.unsplashKeyHint': 'Pro vyhledávání obrázků. Zdarma na unsplash.com/developers',
|
||||
'admin.validateKey': 'Testovat',
|
||||
'admin.keyValid': 'Připojeno',
|
||||
'admin.keyInvalid': 'Neplatný',
|
||||
@@ -160,6 +162,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Pluginy',
|
||||
'admin.plugins.rescan': 'Znovu prohledat',
|
||||
'admin.plugins.rescanned': 'Složka pluginů byla znovu prohledána',
|
||||
'admin.plugins.upload': 'Nahrát plugin',
|
||||
'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.sideloadedHint': 'Nahráno ručně — není z registru, nepodepsané a nezkontrolované',
|
||||
'admin.plugins.browse': 'Procházet pluginy',
|
||||
'admin.plugins.installed': 'Nainstalováno',
|
||||
'admin.plugins.install': 'Instalovat',
|
||||
@@ -174,11 +181,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'Žádné zaznamenané chyby.',
|
||||
'admin.plugins.uninstalled': 'Plugin odinstalován',
|
||||
'admin.plugins.uninstallTitle': 'Odinstalovat plugin?',
|
||||
'admin.plugins.uninstallBody': 'Tím se plugin zastaví, odstraní se jeho kód a smažou se všechna jeho data. Tuto akci nelze vrátit zpět.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'Tím se plugin zastaví, odstraní se jeho kód a smažou se všechna jeho data. Tuto akci nelze vrátit zpět.',
|
||||
'admin.plugins.status.starting': 'Spouští se…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Stránka',
|
||||
'admin.plugins.type.integration': 'Integrace',
|
||||
'admin.plugins.type.trip-page': 'Stránka cesty',
|
||||
'admin.plugins.reviewed': 'Zkontrolováno',
|
||||
'admin.plugins.unreviewed': 'Nezkontrolováno',
|
||||
'admin.plugins.updated': 'Plugin aktualizován',
|
||||
@@ -188,10 +197,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Pluginy',
|
||||
'admin.plugins.subtitle': 'Instalujte a spravujte pluginy třetích stran na své instanci.',
|
||||
'admin.plugins.disabledTitle': 'Pluginy jsou zakázány',
|
||||
'admin.plugins.disabledBody': 'Běhové prostředí pluginů je vypnuté (TREK_PLUGINS_ENABLED). Dokud jej administrátor nepovolí v konfiguraci serveru, nemůže běžet žádný plugin.',
|
||||
'admin.plugins.disabledBody':
|
||||
'Běhové prostředí pluginů je vypnuté (TREK_PLUGINS_ENABLED). Dokud jej administrátor nepovolí v konfiguraci serveru, nemůže běžet žádný plugin.',
|
||||
'admin.plugins.empty': 'Zatím nejsou nainstalovány žádné pluginy.',
|
||||
'admin.plugins.loadError': 'Nepodařilo se načíst pluginy.',
|
||||
'admin.plugins.scaffoldNote': 'Instalace a aktivace pluginů přijdou v pozdější aktualizaci. Tento panel zatím pouze zobrazuje nainstalované pluginy.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'Instalace a aktivace pluginů přijdou v pozdější aktualizaci. Tento panel zatím pouze zobrazuje nainstalované pluginy.',
|
||||
'admin.plugins.status.active': 'Aktivní',
|
||||
'admin.plugins.status.inactive': 'Neaktivní',
|
||||
'admin.plugins.status.disabled': 'Zakázán',
|
||||
@@ -214,10 +225,29 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Ukládat vlastní data v izolované databázi',
|
||||
'admin.plugins.perm.db:read:trips': 'Číst cesty, ke kterým má aktuální uživatel přístup',
|
||||
'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:read:files': 'Číst soubory cest, ke kterým má aktuální uživatel přístup',
|
||||
'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':
|
||||
'Přidávat, upravovat a odebírat místa na cestách, které může aktuální uživatel upravovat',
|
||||
'admin.plugins.perm.db:write:days':
|
||||
'Přidávat, upravovat a odebírat dny na cestách, které může aktuální uživatel upravovat',
|
||||
'admin.plugins.perm.db:write:itinerary':
|
||||
'Přiřazovat a odebírat místa ke dnům cest, které může aktuální uživatel upravovat',
|
||||
'admin.plugins.perm.db:write:trips':
|
||||
'Upravovat podrobnosti cesty (název, data, měnu…) na cestách, které může aktuální uživatel upravovat',
|
||||
'admin.plugins.perm.db:meta':
|
||||
'Připojovat vlastní soukromá data k cestám, místům a dnům, ke kterým má aktuální uživatel přístup',
|
||||
'admin.plugins.perm.ws:broadcast:trip': 'Odesílat aktualizace v reálném čase členům cesty',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'Odesílat aktualizace v reálném čase jednotlivým uživatelům',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Poskytovat fotky do sekce Fotky',
|
||||
'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.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.updateConsentTitle': 'Tato aktualizace vyžaduje nová oprávnění',
|
||||
'admin.plugins.updateConsentBody':
|
||||
@@ -269,11 +299,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Plugin restartován',
|
||||
'admin.plugins.cap.readsTrips': 'Čte vaše cesty',
|
||||
'admin.plugins.cap.readsUsers': 'Čte základní profily',
|
||||
'admin.plugins.cap.readsCosts': 'Čte vaše výdaje',
|
||||
'admin.plugins.cap.readsPacking': 'Čte seznamy věcí k zabalení',
|
||||
'admin.plugins.cap.readsFiles': 'Čte soubory cest',
|
||||
'admin.plugins.cap.writesCosts': 'Přidává výdaje',
|
||||
'admin.plugins.cap.writesPlaces': 'Upravuje místa',
|
||||
'admin.plugins.cap.writesDays': 'Upravuje dny',
|
||||
'admin.plugins.cap.writesItinerary': 'Upravuje itinerář',
|
||||
'admin.plugins.cap.writesTrips': 'Upravuje cesty',
|
||||
'admin.plugins.cap.metadata': 'Přidává metadata',
|
||||
'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.realtime': 'Aktualizace v reálném čase',
|
||||
'admin.plugins.cap.photos': 'Poskytuje fotky',
|
||||
'admin.plugins.cap.calendar': 'Poskytuje události kalendáře',
|
||||
'admin.plugins.cap.placeDetails': 'Obohacuje místa',
|
||||
'admin.plugins.cap.warnings': 'Označuje problémy',
|
||||
'admin.plugins.cap.events': 'Reaguje na aktivitu',
|
||||
'admin.plugins.cap.requiresAddon': 'Vyžaduje {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'Vyžaduje {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Nejprve povolte potřebné doplňky: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Nejprve povoleny potřebné pluginy: {plugins}',
|
||||
'admin.plugins.dep.downloaded': 'Staženo {id}',
|
||||
'admin.plugins.dep.resolveTitle': 'Chybějící závislosti',
|
||||
'admin.plugins.dep.resolveBody': '„{name}“ před povolením potřebuje nainstalovat tyto pluginy.',
|
||||
'admin.plugins.dep.requires': 'Vyžaduje {version}',
|
||||
'admin.plugins.dep.mismatch': 'Vyžaduje {wanted} — nainstalováno je {installed}',
|
||||
'admin.plugins.dep.download': 'Stáhnout',
|
||||
'admin.plugins.dep.update': 'Aktualizovat',
|
||||
'admin.plugins.dep.resolveHint': 'Stáhne nejnovější kompatibilní verzi včetně jejích vlastních závislostí.',
|
||||
'admin.plugins.accessTitle': 'K čemu má přístup',
|
||||
'admin.plugins.connectsTitle': 'Připojuje se k',
|
||||
'admin.plugins.detailsTitle': 'Podrobnosti',
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'Nikdo vám nic nedluží',
|
||||
'costs.yourShare': 'Váš podíl',
|
||||
'costs.youPaid': 'Zaplatili jste',
|
||||
'costs.outstanding': 'Nevyřízená částka',
|
||||
'costs.outstandingSub': 'Výdaje zatím bez plátce',
|
||||
'costs.outstandingItems': 'výdajů potřebuje plátce',
|
||||
'costs.expenses': 'Výdaje',
|
||||
'costs.entries': '{count} položek',
|
||||
'costs.searchPlaceholder': 'Hledat výdaje…',
|
||||
@@ -116,10 +119,10 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'Jen v součtu — zatím nevyrovnáno',
|
||||
'costs.tapToInclude': 'Klepnutím zahrnout',
|
||||
'costs.amount': 'Částka',
|
||||
'costs.split': "Split",
|
||||
'costs.splitEqually': "Equally",
|
||||
'costs.splitCustom': "Custom",
|
||||
'costs.splitTicket': "Ticket",
|
||||
'costs.split': 'Split',
|
||||
'costs.splitEqually': 'Equally',
|
||||
'costs.splitCustom': 'Custom',
|
||||
'costs.splitTicket': 'Ticket',
|
||||
'costs.filter.allCategories': 'Všechny kategorie',
|
||||
'costs.filter.allDays': 'Všechny dny',
|
||||
'costs.expensesCount': '{count} výdajů',
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { TranslationStrings } from '../types';
|
||||
|
||||
// English fallback until 'cs' is translated.
|
||||
const help: TranslationStrings = {
|
||||
'help.title': "Help & Docs",
|
||||
'help.search': "Search docs…",
|
||||
'help.contents': "Contents",
|
||||
'help.noResults': "No matching pages.",
|
||||
'help.title': 'Help & Docs',
|
||||
'help.search': 'Search docs…',
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody': "The help content is fetched from the TREK wiki. Check your connection and try again.",
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -85,7 +85,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'Přihlásit se pomocí přístupového klíče',
|
||||
'login.passkey.failed': 'Přihlášení přístupovým klíčem se nezdařilo. Zkuste to prosím znovu.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Vyberte alespoň jeden typ.',
|
||||
'places.selectionCount': '{count} vybráno',
|
||||
'places.deleteSelected': 'Smazat vybrané',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': 'Importováno {count} míst z KMZ/KML',
|
||||
'places.urlResolved': 'Místo importováno z URL',
|
||||
'places.importList': 'Import seznamu',
|
||||
|
||||
@@ -325,7 +325,8 @@ const settings: TranslationStrings = {
|
||||
'settings.airtrail.test.success': 'Připojeno – nalezeno letů: {count}',
|
||||
'settings.airtrail.test.failed': 'Připojení selhalo',
|
||||
'settings.aiParsing.title': 'Zpracování pomocí AI',
|
||||
'settings.aiParsing.hint': 'Použijte vlastní model AI k získání rezervací z nahraných souborů. Platí pouze tehdy, když správce nenastavil model pro celou instanci.',
|
||||
'settings.aiParsing.hint':
|
||||
'Použijte vlastní model AI k získání rezervací z nahraných souborů. Platí pouze tehdy, když správce nenastavil model pro celou instanci.',
|
||||
'settings.aiParsing.provider': 'Poskytovatel',
|
||||
'settings.aiParsing.providerLocal': 'Místní (Ollama)',
|
||||
'settings.aiParsing.providerOpenai': 'OpenAI',
|
||||
@@ -336,7 +337,8 @@ const settings: TranslationStrings = {
|
||||
'settings.aiParsing.apiKey': 'Klíč API',
|
||||
'settings.aiParsing.apiKeyHint': 'Ukládá se šifrovaně. Ponechte prázdné pro zachování aktuálního klíče.',
|
||||
'settings.aiParsing.multimodal': 'Odesílat dokumenty jako obrázky',
|
||||
'settings.aiParsing.multimodalHint': 'Pro modely se schopností zpracovat obraz — odešle původní PDF místo extrahovaného textu.',
|
||||
'settings.aiParsing.multimodalHint':
|
||||
'Pro modely se schopností zpracovat obraz — odešle původní PDF místo extrahovaného textu.',
|
||||
'settings.aiParsing.toast.saved': 'Nastavení AI uloženo',
|
||||
'settings.aiParsing.toast.saveError': 'Nastavení AI se nepodařilo uložit',
|
||||
'settings.tabs.appearance': 'Appearance',
|
||||
@@ -355,7 +357,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -385,7 +388,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -408,10 +412,13 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Offline mezipaměť',
|
||||
'settings.offline.mode.title': 'Offline režim',
|
||||
'settings.offline.mode.force': 'Vynutit offline režim',
|
||||
'settings.offline.mode.forceHint': 'Pracujte pouze s daty z mezipaměti a změny se zařadí do fronty. Vypnutím se znovu připojíte a synchronizujete.',
|
||||
'settings.offline.mode.active': 'Offline režim je zapnutý — vaše změny jsou ve frontě a synchronizují se, jakmile jej vypnete.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'Pracujte pouze s daty z mezipaměti a změny se zařadí do fronty. Vypnutím se znovu připojíte a synchronizujete.',
|
||||
'settings.offline.mode.active':
|
||||
'Offline režim je zapnutý — vaše změny jsou ve frontě a synchronizují se, jakmile jej vypnete.',
|
||||
'settings.offline.prepare.title': 'Příprava na offline',
|
||||
'settings.offline.prepare.hint': 'Stáhněte si vše potřebné — data cest, dokumenty a mapové dlaždice — než ztratíte připojení.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Stáhněte si vše potřebné — data cest, dokumenty a mapové dlaždice — než ztratíte připojení.',
|
||||
'settings.offline.prepare.button': 'Stáhnout pro offline použití',
|
||||
'settings.offline.prepare.running': 'Stahování…',
|
||||
'settings.offline.prepare.done': 'Připraveno pro offline použití',
|
||||
@@ -422,7 +429,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Synchronizace…',
|
||||
'settings.offline.storage.title': 'Co ukládat offline',
|
||||
'settings.offline.storage.tiles': 'Ukládat mapové dlaždice offline',
|
||||
'settings.offline.storage.tilesHint': 'Mapové dlaždice zabírají nejvíce místa. Vypnutím ponecháte offline pouze data cest a dokumenty.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'Mapové dlaždice zabírají nejvíce místa. Vypnutím ponecháte offline pouze data cest a dokumenty.',
|
||||
'settings.offline.storage.tripsTitle': 'Cesty',
|
||||
'settings.offline.storage.tripOn': 'Uloženo offline',
|
||||
'settings.offline.storage.tripOff': 'Neuloženo',
|
||||
@@ -435,7 +443,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': 'Zatím žádné cesty v mezipaměti. Pro synchronizaci se připojte k internetu.',
|
||||
'settings.offline.loading': 'Načítání…',
|
||||
'settings.offline.conflicts.title': 'Konflikty synchronizace',
|
||||
'settings.offline.conflicts.hint': 'Změny, které jste provedli offline, se střetávají s novějšími změnami na serveru. Vyberte, kterou verzi zachovat.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'Změny, které jste provedli offline, se střetávají s novějšími změnami na serveru. Vyberte, kterou verzi zachovat.',
|
||||
'settings.offline.conflicts.keepMine': 'Zachovat moji',
|
||||
'settings.offline.conflicts.keepServer': 'Zachovat serverovou',
|
||||
'settings.offline.conflicts.mine': 'Vaše verze',
|
||||
|
||||
@@ -28,7 +28,8 @@ const trip: TranslationStrings = {
|
||||
'trip.confirm.deletePlaces': 'Smazat {count} míst?',
|
||||
'trip.toast.placesDeleted': '{count} míst smazáno',
|
||||
'trip.invite.linkTitle': 'Odkaz pro pozvání na cestu',
|
||||
'trip.invite.linkHint': 'Kdokoli s účtem TREK, kdo otevře tento odkaz, se připojí k cestě jako člen. Vygenerováním nového odkazu zneplatníte ten starý.',
|
||||
'trip.invite.linkHint':
|
||||
'Kdokoli s účtem TREK, kdo otevře tento odkaz, se připojí k cestě jako člen. Vygenerováním nového odkazu zneplatníte ten starý.',
|
||||
'trip.invite.create': 'Vytvořit odkaz pro pozvání',
|
||||
'trip.invite.regenerate': 'Vygenerovat znovu',
|
||||
'trip.invite.disable': 'Deaktivovat',
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'Trasa optimalizována',
|
||||
'undo.deletePlace': 'Místo smazáno',
|
||||
'undo.deletePlaces': 'Místa smazána',
|
||||
'undo.changeCategory': "Category changed",
|
||||
'undo.changeCategory': 'Category changed',
|
||||
'undo.moveDay': 'Místo přesunuto na jiný den',
|
||||
'undo.lock': 'Zámek místa přepnut',
|
||||
'undo.importGpx': 'Import GPX',
|
||||
|
||||
+80
-13
@@ -96,6 +96,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Empfohlen',
|
||||
'admin.weatherKey': 'OpenWeatherMap API-Schlüssel',
|
||||
'admin.weatherKeyHint': 'Für Wetterdaten. Kostenlos unter openweathermap.org',
|
||||
'admin.unsplashKey': 'Unsplash-API-Schlüssel',
|
||||
'admin.unsplashKeyHint': 'Für die Bildsuche. Kostenlos unter unsplash.com/developers',
|
||||
'admin.validateKey': 'Test',
|
||||
'admin.keyValid': 'Verbunden',
|
||||
'admin.keyInvalid': 'Ungültig',
|
||||
@@ -162,6 +164,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Plugins',
|
||||
'admin.plugins.rescan': 'Neu scannen',
|
||||
'admin.plugins.rescanned': 'Plugin-Ordner neu gescannt',
|
||||
'admin.plugins.upload': 'Plugin hochladen',
|
||||
'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.sideloadedHint': 'Manuell hochgeladen — nicht aus der Registry, unsigniert und ungeprüft',
|
||||
'admin.plugins.browse': 'Plugins durchsuchen',
|
||||
'admin.plugins.installed': 'Installiert',
|
||||
'admin.plugins.install': 'Installieren',
|
||||
@@ -176,11 +183,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'Keine Fehler protokolliert.',
|
||||
'admin.plugins.uninstalled': 'Plugin deinstalliert',
|
||||
'admin.plugins.uninstallTitle': 'Plugin deinstallieren?',
|
||||
'admin.plugins.uninstallBody': 'Das stoppt das Plugin, entfernt seinen Code und löscht alle seine Daten. Das kann nicht rückgängig gemacht werden.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'Das stoppt das Plugin, entfernt seinen Code und löscht alle seine Daten. Das kann nicht rückgängig gemacht werden.',
|
||||
'admin.plugins.status.starting': 'Startet…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Seite',
|
||||
'admin.plugins.type.integration': 'Integration',
|
||||
'admin.plugins.type.trip-page': 'Reise-Seite',
|
||||
'admin.plugins.reviewed': 'Geprüft',
|
||||
'admin.plugins.unreviewed': 'Ungeprüft',
|
||||
'admin.plugins.updated': 'Plugin aktualisiert',
|
||||
@@ -190,10 +199,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Plugins',
|
||||
'admin.plugins.subtitle': 'Plugins von Drittanbietern auf deiner Instanz installieren und verwalten.',
|
||||
'admin.plugins.disabledTitle': 'Plugins sind deaktiviert',
|
||||
'admin.plugins.disabledBody': 'Die Plugin-Laufzeit ist ausgeschaltet (TREK_PLUGINS_ENABLED). Es kann kein Plugin laufen, bis ein Admin sie in der Serverkonfiguration aktiviert.',
|
||||
'admin.plugins.disabledBody':
|
||||
'Die Plugin-Laufzeit ist ausgeschaltet (TREK_PLUGINS_ENABLED). Es kann kein Plugin laufen, bis ein Admin sie in der Serverkonfiguration aktiviert.',
|
||||
'admin.plugins.empty': 'Noch keine Plugins installiert.',
|
||||
'admin.plugins.loadError': 'Plugins konnten nicht geladen werden.',
|
||||
'admin.plugins.scaffoldNote': 'Installation und Aktivierung von Plugins folgen in einem späteren Update. Dieses Panel zeigt derzeit nur installierte Plugins an.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'Installation und Aktivierung von Plugins folgen in einem späteren Update. Dieses Panel zeigt derzeit nur installierte Plugins an.',
|
||||
'admin.plugins.status.active': 'Aktiv',
|
||||
'admin.plugins.status.inactive': 'Inaktiv',
|
||||
'admin.plugins.status.disabled': 'Deaktiviert',
|
||||
@@ -216,32 +227,61 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Eigene Daten in einer isolierten Datenbank speichern',
|
||||
'admin.plugins.perm.db:read:trips': 'Reisen lesen, auf die der handelnde Nutzer Zugriff hat',
|
||||
'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:read:files': 'Dateien von Reisen lesen, auf die der handelnde Nutzer Zugriff hat',
|
||||
'admin.plugins.perm.db:write:costs':
|
||||
'Ausgaben (Budgetposten) auf Reisen erstellen, die der handelnde Nutzer bearbeiten kann',
|
||||
'admin.plugins.perm.db:write:places':
|
||||
'Orte auf Reisen anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten kann',
|
||||
'admin.plugins.perm.db:write:days':
|
||||
'Tage auf Reisen anlegen, bearbeiten und löschen, die der handelnde Nutzer bearbeiten kann',
|
||||
'admin.plugins.perm.db:write:itinerary':
|
||||
'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: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',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'Echtzeit-Updates an einzelne Nutzer senden',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Fotos für Erinnerungen bereitstellen',
|
||||
'admin.plugins.perm.hook:calendar-source': 'Termine für den Kalender bereitstellen',
|
||||
'admin.plugins.perm.hook:place-detail-provider':
|
||||
'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.events:subscribe':
|
||||
'Auf Kern-Aktivitäts-Events reagieren (nur Event-Name + Reise, nie der Inhalt)',
|
||||
'admin.plugins.perm.http:outbound': 'Ausgehende Anfragen an deklarierte Hosts stellen',
|
||||
'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.',
|
||||
'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.',
|
||||
'admin.plugins.updateNewPermissions': 'Neu verlangte Berechtigungen',
|
||||
'admin.plugins.updateNewEgress': 'Neue ausgehende Verbindungen',
|
||||
'admin.plugins.updateApprove': 'Freigeben & einschalten',
|
||||
'admin.plugins.updateLater': 'Vorerst aus lassen',
|
||||
'admin.plugins.updateKeptOff': 'Update installiert — bleibt aus, bis du die neuen Berechtigungen freigibst',
|
||||
'admin.plugins.reviewedMeaning': '„Geprüft" heißt: Ein TREK-Maintainer hat den Code dieses Plugins bei jeder Version manuell auf Schadsoftware gescannt — nicht auf Qualität oder Funktion. Es ist keine Garantie, dass ein Plugin harmlos ist.',
|
||||
'admin.plugins.reviewedMeaning':
|
||||
'„Geprüft" heißt: Ein TREK-Maintainer hat den Code dieses Plugins bei jeder Version manuell auf Schadsoftware gescannt — nicht auf Qualität oder Funktion. Es ist keine Garantie, dass ein Plugin harmlos ist.',
|
||||
'admin.plugins.security.title': 'Wie Plugins eingesperrt werden — und wo die Grenzen liegen',
|
||||
'admin.plugins.security.isolationTitle': 'Jedes Plugin läuft abgeschottet',
|
||||
'admin.plugins.security.isolationBody': 'Ein Plugin läuft als eigener, abgeriegelter Prozess, der nur seine eigenen Dateien lesen darf. Es kann weder deine Datenbank noch deinen Login-Schlüssel oder deinen Verschlüsselungsschlüssel lesen, keine anderen Programme starten und nirgends Dateien schreiben. Seine Oberfläche läuft in einem versiegelten Browser-Rahmen, der dein Sitzungs-Cookie nicht lesen und die TREK-Seite drumherum nicht anfassen kann.',
|
||||
'admin.plugins.security.isolationBody':
|
||||
'Ein Plugin läuft als eigener, abgeriegelter Prozess, der nur seine eigenen Dateien lesen darf. Es kann weder deine Datenbank noch deinen Login-Schlüssel oder deinen Verschlüsselungsschlüssel lesen, keine anderen Programme starten und nirgends Dateien schreiben. Seine Oberfläche läuft in einem versiegelten Browser-Rahmen, der dein Sitzungs-Cookie nicht lesen und die TREK-Seite drumherum nicht anfassen kann.',
|
||||
'admin.plugins.security.permsTitle': 'Was die Berechtigungen bedeuten',
|
||||
'admin.plugins.security.permsBody': 'Die Berechtigungen, die dir vor der Installation angezeigt werden, sind eine harte Obergrenze, die TREK zur Laufzeit durchsetzt — ein Plugin kann buchstäblich nichts tun, was nicht auf der Liste steht. Aber sie sagen dir, was ein Plugin kann, nicht was es tatsächlich tut. Ein Plugin, das deine Reisen lesen und einen Server erreichen darf, kann deine Reisen an diesen Server schicken — lies also die Berechtigungen und die Zielhosts, nicht nur die Beschreibung.',
|
||||
'admin.plugins.security.permsBody':
|
||||
'Die Berechtigungen, die dir vor der Installation angezeigt werden, sind eine harte Obergrenze, die TREK zur Laufzeit durchsetzt — ein Plugin kann buchstäblich nichts tun, was nicht auf der Liste steht. Aber sie sagen dir, was ein Plugin kann, nicht was es tatsächlich tut. Ein Plugin, das deine Reisen lesen und einen Server erreichen darf, kann deine Reisen an diesen Server schicken — lies also die Berechtigungen und die Zielhosts, nicht nur die Beschreibung.',
|
||||
'admin.plugins.security.limitsTitle': 'Was wir nicht garantieren können',
|
||||
'admin.plugins.security.limitsBody': 'Die Isolation ist eine starke Software-Grenze, keine absolute. Ein Plugin handelt mit genau den Rechten, die du freigibst — innerhalb dieser Rechte kann es sich anders verhalten als seine Beschreibung nahelegt, und es kann Daten, die es legitim hat, an die von ihm deklarierten Hosts senden. TREK liest oder bewertet nicht, was der Code eines Plugins tatsächlich tut.',
|
||||
'admin.plugins.security.limitsBody':
|
||||
'Die Isolation ist eine starke Software-Grenze, keine absolute. Ein Plugin handelt mit genau den Rechten, die du freigibst — innerhalb dieser Rechte kann es sich anders verhalten als seine Beschreibung nahelegt, und es kann Daten, die es legitim hat, an die von ihm deklarierten Hosts senden. TREK liest oder bewertet nicht, was der Code eines Plugins tatsächlich tut.',
|
||||
'admin.plugins.security.worstTitle': 'Der schlimmste Fall',
|
||||
'admin.plugins.security.worstBody': 'Ein bösartiges Plugin, das du einschaltest, kann die Daten und Verbindungen missbrauchen, die du ihm gegeben hast — etwa die Reisen abgreifen, die es lesen darf. Es kann keine Passwörter stehlen, keinen Admin-Login fälschen, keine Befehle auf deinem Server ausführen und keine Daten erreichen, die du nicht freigegeben hast. Der Schaden bleibt in dem, was du freigegeben hast, und das Ausschalten des Plugins stoppt ihn.',
|
||||
'admin.plugins.security.worstBody':
|
||||
'Ein bösartiges Plugin, das du einschaltest, kann die Daten und Verbindungen missbrauchen, die du ihm gegeben hast — etwa die Reisen abgreifen, die es lesen darf. Es kann keine Passwörter stehlen, keinen Admin-Login fälschen, keine Befehle auf deinem Server ausführen und keine Daten erreichen, die du nicht freigegeben hast. Der Schaden bleibt in dem, was du freigegeben hast, und das Ausschalten des Plugins stoppt ihn.',
|
||||
'admin.plugins.security.reviewedTitle': 'Was „Geprüft" bedeutet',
|
||||
'admin.plugins.security.reviewedBody': 'Ein geprüftes Plugin wurde bei jeder Version von einem TREK-Maintainer manuell auf Schadsoftware gescannt — geprüft auf feindlichen Code, nicht darauf, ob es gut funktioniert. Es ist kein Versprechen, dass das Plugin harmlos ist.',
|
||||
'admin.plugins.security.reviewedBody':
|
||||
'Ein geprüftes Plugin wurde bei jeder Version von einem TREK-Maintainer manuell auf Schadsoftware gescannt — geprüft auf feindlichen Code, nicht darauf, ob es gut funktioniert. Es ist kein Versprechen, dass das Plugin harmlos ist.',
|
||||
'admin.plugins.security.trustTitle': 'Das Fazit',
|
||||
'admin.plugins.security.trustBody': 'Ein Plugin zu installieren ist wie jede fremde App zu installieren: Füge nur Code von Autoren hinzu, denen du vertraust, und prüfe ihn im Zweifel zuerst selbst. TREK übernimmt keine Verantwortung für Plugins von Drittanbietern.',
|
||||
'admin.plugins.security.trustBody':
|
||||
'Ein Plugin zu installieren ist wie jede fremde App zu installieren: Füge nur Code von Autoren hinzu, denen du vertraust, und prüfe ihn im Zweifel zuerst selbst. TREK übernimmt keine Verantwortung für Plugins von Drittanbietern.',
|
||||
'admin.plugins.runtimeOn': 'Laufzeit aktiv',
|
||||
'admin.plugins.tabDiscover': 'Entdecken',
|
||||
'admin.plugins.searchPlaceholder': 'Plugins durchsuchen…',
|
||||
@@ -263,11 +303,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Plugin neu gestartet',
|
||||
'admin.plugins.cap.readsTrips': 'Liest deine Reisen',
|
||||
'admin.plugins.cap.readsUsers': 'Liest Basis-Profile',
|
||||
'admin.plugins.cap.readsCosts': 'Liest deine Ausgaben',
|
||||
'admin.plugins.cap.readsPacking': 'Liest Packlisten',
|
||||
'admin.plugins.cap.readsFiles': 'Liest Reise-Dateien',
|
||||
'admin.plugins.cap.writesCosts': 'Fügt Ausgaben hinzu',
|
||||
'admin.plugins.cap.writesPlaces': 'Bearbeitet Orte',
|
||||
'admin.plugins.cap.writesDays': 'Bearbeitet Tage',
|
||||
'admin.plugins.cap.writesItinerary': 'Bearbeitet Reiseplan',
|
||||
'admin.plugins.cap.writesTrips': 'Bearbeitet Reisen',
|
||||
'admin.plugins.cap.metadata': 'Ergänzt Metadaten',
|
||||
'admin.plugins.cap.widget': 'Dashboard-Widget',
|
||||
'admin.plugins.cap.heroWidget': 'Boarding-Pass-Widget',
|
||||
'admin.plugins.cap.placeSlot': 'Ortsdetails',
|
||||
'admin.plugins.cap.realtime': 'Echtzeit-Updates',
|
||||
'admin.plugins.cap.photos': 'Liefert Fotos',
|
||||
'admin.plugins.cap.calendar': 'Liefert Kalender-Events',
|
||||
'admin.plugins.cap.placeDetails': 'Reichert Orte an',
|
||||
'admin.plugins.cap.warnings': 'Meldet Probleme',
|
||||
'admin.plugins.cap.events': 'Reagiert auf Aktivität',
|
||||
'admin.plugins.cap.requiresAddon': 'Benötigt {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'Benötigt {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Aktiviere zuerst die benötigten Addons: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Benötigte Plugins zuerst aktiviert: {plugins}',
|
||||
'admin.plugins.dep.downloaded': '{id} heruntergeladen',
|
||||
'admin.plugins.dep.resolveTitle': 'Fehlende Abhängigkeiten',
|
||||
'admin.plugins.dep.resolveBody': '„{name}“ benötigt diese Plugins, bevor es aktiviert werden kann.',
|
||||
'admin.plugins.dep.requires': 'Benötigt {version}',
|
||||
'admin.plugins.dep.mismatch': 'Benötigt {wanted} — installiert ist {installed}',
|
||||
'admin.plugins.dep.download': 'Herunterladen',
|
||||
'admin.plugins.dep.update': 'Aktualisieren',
|
||||
'admin.plugins.dep.resolveHint': 'Lädt die neueste kompatible Version samt eigener Abhängigkeiten herunter.',
|
||||
'admin.plugins.accessTitle': 'Worauf es zugreift',
|
||||
'admin.plugins.connectsTitle': 'Verbindet sich mit',
|
||||
'admin.plugins.detailsTitle': 'Details',
|
||||
@@ -440,7 +505,8 @@ const admin: TranslationStrings = {
|
||||
'admin.addons.catalog.journey.name': 'Journey',
|
||||
'admin.addons.catalog.journey.description': 'Reise-Tracking & Tagebuch mit Check-ins, Fotos und Tagesberichten',
|
||||
'admin.addons.catalog.collections.name': 'Sammlungen',
|
||||
'admin.addons.catalog.collections.description': 'Persönliche Orte-Bibliothek — speichere Orte aus Reisen in benannten Listen, kopiere sie in jede Reise, teile sie mit anderen',
|
||||
'admin.addons.catalog.collections.description':
|
||||
'Persönliche Orte-Bibliothek — speichere Orte aus Reisen in benannten Listen, kopiere sie in jede Reise, teile sie mit anderen',
|
||||
'admin.passkey.title': 'Passkey-Anmeldung',
|
||||
'admin.passkey.cardHint': 'Erlaube Benutzern die Anmeldung mit Passkeys (WebAuthn). Standardmäßig deaktiviert.',
|
||||
'admin.passkey.login': 'Passkey-Anmeldung aktivieren',
|
||||
@@ -478,7 +544,8 @@ const admin: TranslationStrings = {
|
||||
'admin.group.maintenance': 'Wartung',
|
||||
'admin.invite.tripLabel': 'Zu Trip hinzufügen (optional)',
|
||||
'admin.invite.tripNone': 'Kein Trip',
|
||||
'admin.invite.tripHint': 'Der neue Nutzer wird automatisch zu diesem Trip hinzugefügt, wenn er sich über den Link registriert.',
|
||||
'admin.invite.tripHint':
|
||||
'Der neue Nutzer wird automatisch zu diesem Trip hinzugefügt, wenn er sich über den Link registriert.',
|
||||
'admin.invite.boundTo': 'fügt zu {trip} hinzu',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'Dir wird nichts geschuldet',
|
||||
'costs.yourShare': 'Dein Anteil',
|
||||
'costs.youPaid': 'Du zahltest',
|
||||
'costs.outstanding': 'Ausstehender Betrag',
|
||||
'costs.outstandingSub': 'Ausgaben noch ohne Zahler',
|
||||
'costs.outstandingItems': 'Ausgaben ohne Zahler',
|
||||
'costs.expenses': 'Ausgaben',
|
||||
'costs.entries': '{count} Einträge',
|
||||
'costs.searchPlaceholder': 'Ausgaben suchen…',
|
||||
@@ -119,9 +122,9 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'Nur Gesamtsumme — noch nicht abgerechnet',
|
||||
'costs.tapToInclude': 'Zum Einbeziehen tippen',
|
||||
'costs.amount': 'Betrag',
|
||||
'costs.split': "Aufteilen",
|
||||
'costs.splitEqually': "Gleichmäßig",
|
||||
'costs.splitCustom': "Individuell",
|
||||
'costs.splitTicket': "Beleg",
|
||||
'costs.split': 'Aufteilen',
|
||||
'costs.splitEqually': 'Gleichmäßig',
|
||||
'costs.splitCustom': 'Individuell',
|
||||
'costs.splitTicket': 'Beleg',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'Umbenennen',
|
||||
'collections.listMenu': 'Listenoptionen',
|
||||
'collections.deleteList': 'Liste löschen',
|
||||
'collections.deleteListConfirm': 'Dies löscht die Liste und alle gespeicherten Orte endgültig. Das kann nicht rückgängig gemacht werden.',
|
||||
'collections.deleteListConfirm':
|
||||
'Dies löscht die Liste und alle gespeicherten Orte endgültig. Das kann nicht rückgängig gemacht werden.',
|
||||
'collections.listName': 'Listenname',
|
||||
'collections.listNamePlaceholder': 'z. B. Tokio 2025',
|
||||
'collections.listColor': 'Farbe',
|
||||
@@ -61,7 +62,8 @@ const collection: TranslationStrings = {
|
||||
'collections.empty.noMatchTitle': 'Keine Treffer',
|
||||
'collections.empty.noMatchText': 'Keine gespeicherten Orte passen zu deinem Filter oder deiner Suche.',
|
||||
'collections.empty.firstTitle': 'Starte deine erste Sammlung',
|
||||
'collections.empty.firstText': 'Erstelle eine Liste, um Orte zu speichern, die du dir über Reisen hinweg merken willst.',
|
||||
'collections.empty.firstText':
|
||||
'Erstelle eine Liste, um Orte zu speichern, die du dir über Reisen hinweg merken willst.',
|
||||
|
||||
'collections.view.grid': 'Raster',
|
||||
'collections.view.list': 'Liste',
|
||||
@@ -127,7 +129,8 @@ const collection: TranslationStrings = {
|
||||
'collections.share.memberHint': 'Nur der Listenbesitzer kann Personen einladen oder entfernen.',
|
||||
'collections.share.cancel': 'Abbrechen',
|
||||
'collections.share.leave': 'Liste verlassen',
|
||||
'collections.share.leaveConfirm': 'Diese geteilte Liste verlassen? Du verlierst den Zugriff, bis du erneut eingeladen wirst.',
|
||||
'collections.share.leaveConfirm':
|
||||
'Diese geteilte Liste verlassen? Du verlierst den Zugriff, bis du erneut eingeladen wirst.',
|
||||
'collections.share.left': 'Du hast die Liste verlassen',
|
||||
'collections.share.remove': 'Entfernen',
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ const help: TranslationStrings = {
|
||||
'help.contents': 'Inhalt',
|
||||
'help.noResults': 'Keine passenden Seiten.',
|
||||
'help.errorTitle': 'Seite konnte nicht geladen werden',
|
||||
'help.errorBody':
|
||||
'Die Hilfe-Inhalte kommen aus dem TREK-Wiki. Prüfe deine Verbindung und versuch es erneut.',
|
||||
'help.errorBody': 'Die Hilfe-Inhalte kommen aus dem TREK-Wiki. Prüfe deine Verbindung und versuch es erneut.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -86,8 +86,9 @@ const login: TranslationStrings = {
|
||||
'login.resetPasswordFailed': 'Zurücksetzen fehlgeschlagen. Der Link ist möglicherweise abgelaufen.',
|
||||
'login.passkey.signIn': 'Mit Passkey anmelden',
|
||||
'login.passkey.failed': 'Anmeldung mit Passkey fehlgeschlagen. Bitte erneut versuchen.',
|
||||
'login.insecureCookie.title': "Login hält über HTTP nicht",
|
||||
'login.insecureCookie.body': "Du verbindest dich über reines HTTP, daher verwirft dein Browser TREKs sicheren Session-Cookie — die nächste Anfrage scheitert mit „Access token required\". Lösung: HTTPS nutzen, oder für ein Heim-Setup COOKIE_SECURE=false setzen.",
|
||||
'login.insecureCookie.link': "Zur Troubleshooting-Anleitung",
|
||||
'login.insecureCookie.title': 'Login hält über HTTP nicht',
|
||||
'login.insecureCookie.body':
|
||||
'Du verbindest dich über reines HTTP, daher verwirft dein Browser TREKs sicheren Session-Cookie — die nächste Anfrage scheitert mit „Access token required". Lösung: HTTPS nutzen, oder für ein Heim-Setup COOKIE_SECURE=false setzen.',
|
||||
'login.insecureCookie.link': 'Zur Troubleshooting-Anleitung',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -25,7 +25,8 @@ const members: TranslationStrings = {
|
||||
'members.transferError': 'Übertragung fehlgeschlagen',
|
||||
'members.guests': 'Gäste',
|
||||
'members.guest': 'Gast',
|
||||
'members.guestsHint': 'Personen ohne Account. Sie können Kosten, Gepäck und Aufgaben zugewiesen bekommen, sich aber nicht anmelden.',
|
||||
'members.guestsHint':
|
||||
'Personen ohne Account. Sie können Kosten, Gepäck und Aufgaben zugewiesen bekommen, sich aber nicht anmelden.',
|
||||
'members.addGuest': 'Gast hinzufügen',
|
||||
'members.guestNamePlaceholder': 'Name des Gasts',
|
||||
'members.guestAdded': 'Gast hinzugefügt',
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Wähle mindestens einen Typ aus.',
|
||||
'places.selectionCount': '{count} ausgewählt',
|
||||
'places.deleteSelected': 'Auswahl löschen',
|
||||
'places.changeCategory': "Kategorie ändern",
|
||||
'places.categoryChanged': "{count} Orte aktualisiert",
|
||||
'places.changeCategory': 'Kategorie ändern',
|
||||
'places.categoryChanged': '{count} Orte aktualisiert',
|
||||
'places.kmlKmzImported': '{count} Orte aus KMZ/KML importiert',
|
||||
'places.urlResolved': 'Ort aus URL importiert',
|
||||
'places.importList': 'Listenimport',
|
||||
|
||||
@@ -339,11 +339,13 @@ const settings: TranslationStrings = {
|
||||
'settings.aiParsing.providerAnthropic': 'Anthropic',
|
||||
'settings.aiParsing.model': 'Modell',
|
||||
'settings.aiParsing.baseUrl': 'Basis-URL',
|
||||
'settings.aiParsing.baseUrlHint': 'Wo das Modell läuft — ein lokaler Ollama-Server oder ein OpenAI-kompatibler Endpunkt.',
|
||||
'settings.aiParsing.baseUrlHint':
|
||||
'Wo das Modell läuft — ein lokaler Ollama-Server oder ein OpenAI-kompatibler Endpunkt.',
|
||||
'settings.aiParsing.apiKey': 'API-Schlüssel',
|
||||
'settings.aiParsing.apiKeyHint': 'Verschlüsselt gespeichert. Leer lassen, um den aktuellen Schlüssel zu behalten.',
|
||||
'settings.aiParsing.multimodal': 'Dokumente als Bilder senden',
|
||||
'settings.aiParsing.multimodalHint': 'Für Modelle mit Bildverständnis — sendet das Original-PDF statt extrahiertem Text.',
|
||||
'settings.aiParsing.multimodalHint':
|
||||
'Für Modelle mit Bildverständnis — sendet das Original-PDF statt extrahiertem Text.',
|
||||
'settings.aiParsing.toast.saved': 'KI-Einstellungen gespeichert',
|
||||
'settings.aiParsing.toast.saveError': 'KI-Einstellungen konnten nicht gespeichert werden',
|
||||
'settings.tabs.appearance': 'Erscheinungsbild',
|
||||
@@ -362,7 +364,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Niedriger Kontrast',
|
||||
'settings.appearance.readability': 'Lesbarkeit',
|
||||
'settings.appearance.transparency': 'Transparenz',
|
||||
'settings.appearance.transparencyHint': 'Glasige, durchscheinende Flächen. Aus = solide, kontrastreichere Hintergründe.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glasige, durchscheinende Flächen. Aus = solide, kontrastreichere Hintergründe.',
|
||||
'settings.appearance.reduceMotion': 'Bewegung reduzieren',
|
||||
'settings.appearance.reduceMotionHint': 'Animationen und Übergänge minimieren.',
|
||||
'settings.appearance.density': 'Dichte',
|
||||
@@ -415,10 +418,13 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Offline-Cache',
|
||||
'settings.offline.mode.title': 'Offline-Modus',
|
||||
'settings.offline.mode.force': 'Offline-Modus erzwingen',
|
||||
'settings.offline.mode.forceHint': 'Nur mit zwischengespeicherten Daten arbeiten und Änderungen in die Warteschlange legen. Zum Verbinden und Synchronisieren ausschalten.',
|
||||
'settings.offline.mode.active': 'Offline-Modus ist an — deine Änderungen werden gesammelt und beim Ausschalten synchronisiert.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'Nur mit zwischengespeicherten Daten arbeiten und Änderungen in die Warteschlange legen. Zum Verbinden und Synchronisieren ausschalten.',
|
||||
'settings.offline.mode.active':
|
||||
'Offline-Modus ist an — deine Änderungen werden gesammelt und beim Ausschalten synchronisiert.',
|
||||
'settings.offline.prepare.title': 'Für Offline vorbereiten',
|
||||
'settings.offline.prepare.hint': 'Lade alles Nötige herunter — Reisedaten, Dokumente und Kartenkacheln — bevor du die Verbindung verlierst.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Lade alles Nötige herunter — Reisedaten, Dokumente und Kartenkacheln — bevor du die Verbindung verlierst.',
|
||||
'settings.offline.prepare.button': 'Für Offline-Nutzung herunterladen',
|
||||
'settings.offline.prepare.running': 'Lädt herunter…',
|
||||
'settings.offline.prepare.done': 'Bereit für die Offline-Nutzung',
|
||||
@@ -429,20 +435,24 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Synchronisiert…',
|
||||
'settings.offline.storage.title': 'Was offline gespeichert wird',
|
||||
'settings.offline.storage.tiles': 'Kartenkacheln offline speichern',
|
||||
'settings.offline.storage.tilesHint': 'Kartenkacheln brauchen den meisten Speicher. Schalte dies aus, um nur Reisedaten und Dokumente offline zu behalten.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'Kartenkacheln brauchen den meisten Speicher. Schalte dies aus, um nur Reisedaten und Dokumente offline zu behalten.',
|
||||
'settings.offline.storage.tripsTitle': 'Reisen',
|
||||
'settings.offline.storage.tripOn': 'Offline gespeichert',
|
||||
'settings.offline.storage.tripOff': 'Nicht gespeichert',
|
||||
'settings.offline.clear': 'Cache leeren',
|
||||
'settings.offline.clearConfirm': 'Alle offline gespeicherten Reisedaten löschen? Du kannst jederzeit online neu synchronisieren.',
|
||||
'settings.offline.clearConfirm':
|
||||
'Alle offline gespeicherten Reisedaten löschen? Du kannst jederzeit online neu synchronisieren.',
|
||||
'settings.offline.stats.trips': 'Zwischengespeicherte Reisen',
|
||||
'settings.offline.stats.pending': 'Ausstehende Änderungen',
|
||||
'settings.offline.stats.failed': 'Fehlgeschlagene Änderungen',
|
||||
'settings.offline.stats.conflicts': 'Konflikte',
|
||||
'settings.offline.empty': 'Noch keine Reisen zwischengespeichert. Verbinde dich mit dem Internet zum Synchronisieren.',
|
||||
'settings.offline.empty':
|
||||
'Noch keine Reisen zwischengespeichert. Verbinde dich mit dem Internet zum Synchronisieren.',
|
||||
'settings.offline.loading': 'Lädt…',
|
||||
'settings.offline.conflicts.title': 'Synchronisierungskonflikte',
|
||||
'settings.offline.conflicts.hint': 'Offline vorgenommene Änderungen kollidieren mit neueren Änderungen auf dem Server. Wähle, welche Version behalten werden soll.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'Offline vorgenommene Änderungen kollidieren mit neueren Änderungen auf dem Server. Wähle, welche Version behalten werden soll.',
|
||||
'settings.offline.conflicts.keepMine': 'Meine behalten',
|
||||
'settings.offline.conflicts.keepServer': 'Server behalten',
|
||||
'settings.offline.conflicts.mine': 'Deine Version',
|
||||
|
||||
@@ -28,7 +28,8 @@ const trip: TranslationStrings = {
|
||||
'trip.confirm.deletePlaces': '{count} Orte löschen?',
|
||||
'trip.toast.placesDeleted': '{count} Orte gelöscht',
|
||||
'trip.invite.linkTitle': 'Trip-Einladungslink',
|
||||
'trip.invite.linkHint': 'Wer einen TREK-Account hat und diesen Link öffnet, tritt dem Trip als Mitglied bei. Neu generieren macht den alten Link ungültig.',
|
||||
'trip.invite.linkHint':
|
||||
'Wer einen TREK-Account hat und diesen Link öffnet, tritt dem Trip als Mitglied bei. Neu generieren macht den alten Link ungültig.',
|
||||
'trip.invite.create': 'Einladungslink erstellen',
|
||||
'trip.invite.regenerate': 'Neu generieren',
|
||||
'trip.invite.disable': 'Deaktivieren',
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'Route optimiert',
|
||||
'undo.deletePlace': 'Ort gelöscht',
|
||||
'undo.deletePlaces': 'Orte gelöscht',
|
||||
'undo.changeCategory': "Kategorie geändert",
|
||||
'undo.changeCategory': 'Kategorie geändert',
|
||||
'undo.moveDay': 'Ort zu anderem Tag verschoben',
|
||||
'undo.lock': 'Ortssperre umgeschaltet',
|
||||
'undo.importGpx': 'GPX-Import',
|
||||
|
||||
+68
-12
@@ -136,6 +136,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Recommended',
|
||||
'admin.weatherKey': 'OpenWeatherMap API Key',
|
||||
'admin.weatherKeyHint': 'For weather data. Free at openweathermap.org',
|
||||
'admin.unsplashKey': 'Unsplash API Key',
|
||||
'admin.unsplashKeyHint': 'For image search. Free at unsplash.com/developers',
|
||||
'admin.validateKey': 'Test',
|
||||
'admin.keyValid': 'Connected',
|
||||
'admin.keyInvalid': 'Invalid',
|
||||
@@ -214,6 +216,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Plugins',
|
||||
'admin.plugins.rescan': 'Rescan',
|
||||
'admin.plugins.rescanned': 'Rescanned the plugins folder',
|
||||
'admin.plugins.upload': 'Upload plugin',
|
||||
'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.sideloadedHint': 'Uploaded manually — not from the registry, unsigned and unreviewed',
|
||||
'admin.plugins.browse': 'Browse plugins',
|
||||
'admin.plugins.installed': 'Installed',
|
||||
'admin.plugins.install': 'Install',
|
||||
@@ -228,11 +235,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'No errors logged.',
|
||||
'admin.plugins.uninstalled': 'Plugin uninstalled',
|
||||
'admin.plugins.uninstallTitle': 'Uninstall plugin?',
|
||||
'admin.plugins.uninstallBody': 'This stops the plugin, removes its code, and deletes all of its data. This cannot be undone.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'This stops the plugin, removes its code, and deletes all of its data. This cannot be undone.',
|
||||
'admin.plugins.status.starting': 'Starting…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Page',
|
||||
'admin.plugins.type.integration': 'Integration',
|
||||
'admin.plugins.type.trip-page': 'Trip page',
|
||||
'admin.plugins.reviewed': 'Reviewed',
|
||||
'admin.plugins.unreviewed': 'Not reviewed',
|
||||
'admin.plugins.updated': 'Plugin updated',
|
||||
@@ -242,10 +251,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Plugins',
|
||||
'admin.plugins.subtitle': 'Install and manage third-party plugins on your instance.',
|
||||
'admin.plugins.disabledTitle': 'Plugins are disabled',
|
||||
'admin.plugins.disabledBody': 'The plugin runtime is turned off (TREK_PLUGINS_ENABLED). No plugin can run until an admin enables it in the server configuration.',
|
||||
'admin.plugins.disabledBody':
|
||||
'The plugin runtime is turned off (TREK_PLUGINS_ENABLED). No plugin can run until an admin enables it in the server configuration.',
|
||||
'admin.plugins.empty': 'No plugins installed yet.',
|
||||
'admin.plugins.loadError': 'Could not load plugins.',
|
||||
'admin.plugins.scaffoldNote': 'Plugin installation and activation are coming in a later update. This panel currently lists installed plugins only.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'Plugin installation and activation are coming in a later update. This panel currently lists installed plugins only.',
|
||||
'admin.plugins.status.active': 'Active',
|
||||
'admin.plugins.status.inactive': 'Inactive',
|
||||
'admin.plugins.status.disabled': 'Disabled',
|
||||
@@ -268,32 +279,52 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Store its own data in an isolated database',
|
||||
'admin.plugins.perm.db:read:trips': 'Read trips the acting user can access',
|
||||
'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:read:files': 'Read files of trips the acting user can access',
|
||||
'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: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',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Provide photos to Memories',
|
||||
'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.events:subscribe': 'React to core activity events (event name + trip only, never the content)',
|
||||
'admin.plugins.perm.http:outbound': 'Make outbound requests to its declared hosts',
|
||||
'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.',
|
||||
'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.",
|
||||
'admin.plugins.updateNewPermissions': 'Newly requested permissions',
|
||||
'admin.plugins.updateNewEgress': 'New outbound connections',
|
||||
'admin.plugins.updateApprove': 'Approve & turn on',
|
||||
'admin.plugins.updateLater': 'Keep off for now',
|
||||
'admin.plugins.updateKeptOff': 'Update installed — left off until you approve the new permissions',
|
||||
'admin.plugins.reviewedMeaning': '"Reviewed" means a TREK maintainer scanned this plugin for malware on each version — not for quality or whether it works. It is not a guarantee that a plugin is harmless.',
|
||||
'admin.plugins.reviewedMeaning':
|
||||
'"Reviewed" means a TREK maintainer scanned this plugin for malware on each version — not for quality or whether it works. It is not a guarantee that a plugin is harmless.',
|
||||
'admin.plugins.security.title': 'How plugins are contained — and the limits',
|
||||
'admin.plugins.security.isolationTitle': 'Every plugin runs boxed in',
|
||||
'admin.plugins.security.isolationBody': 'A plugin runs as its own locked-down process that can only read its own files. It cannot read your database, your login secret or your encryption key, cannot start other programs, and cannot write files anywhere. Its interface runs in a sealed browser frame that can\'t read your session cookie or touch the TREK page around it.',
|
||||
'admin.plugins.security.isolationBody':
|
||||
"A plugin runs as its own locked-down process that can only read its own files. It cannot read your database, your login secret or your encryption key, cannot start other programs, and cannot write files anywhere. Its interface runs in a sealed browser frame that can't read your session cookie or touch the TREK page around it.",
|
||||
'admin.plugins.security.permsTitle': 'What the permissions mean',
|
||||
'admin.plugins.security.permsBody': 'The permissions shown before you install are a hard limit that TREK enforces while the plugin runs — it literally cannot do anything that isn\'t on the list. But they tell you what a plugin can do, not what it actually does. A plugin allowed to read your trips and reach a server can send your trips to that server, so read the permissions and the outbound hosts, not just the description.',
|
||||
'admin.plugins.security.limitsTitle': 'What we can\'t promise',
|
||||
'admin.plugins.security.limitsBody': 'The isolation is a strong software boundary, not an absolute one. A plugin acts with exactly the rights you approve, so within those rights it can behave differently than its description suggests, and it can send data it legitimately holds to the hosts it declared. TREK does not read or judge what a plugin\'s code actually does.',
|
||||
'admin.plugins.security.permsBody':
|
||||
"The permissions shown before you install are a hard limit that TREK enforces while the plugin runs — it literally cannot do anything that isn't on the list. But they tell you what a plugin can do, not what it actually does. A plugin allowed to read your trips and reach a server can send your trips to that server, so read the permissions and the outbound hosts, not just the description.",
|
||||
'admin.plugins.security.limitsTitle': "What we can't promise",
|
||||
'admin.plugins.security.limitsBody':
|
||||
"The isolation is a strong software boundary, not an absolute one. A plugin acts with exactly the rights you approve, so within those rights it can behave differently than its description suggests, and it can send data it legitimately holds to the hosts it declared. TREK does not read or judge what a plugin's code actually does.",
|
||||
'admin.plugins.security.worstTitle': 'The worst case',
|
||||
'admin.plugins.security.worstBody': 'A hostile plugin you turn on can misuse the data and connections you granted it — for example, leak the trips it is allowed to read. It cannot steal passwords, forge an admin login, run commands on your server, or reach data you didn\'t grant. The damage stays inside what you approved, and turning the plugin off stops it.',
|
||||
'admin.plugins.security.worstBody':
|
||||
"A hostile plugin you turn on can misuse the data and connections you granted it — for example, leak the trips it is allowed to read. It cannot steal passwords, forge an admin login, run commands on your server, or reach data you didn't grant. The damage stays inside what you approved, and turning the plugin off stops it.",
|
||||
'admin.plugins.security.reviewedTitle': 'What "Reviewed" means',
|
||||
'admin.plugins.security.reviewedBody': 'A reviewed plugin was manually scanned for malware by a TREK maintainer on each version — checked for hostile code, not for whether it works well. It is not a promise that the plugin is harmless.',
|
||||
'admin.plugins.security.reviewedBody':
|
||||
'A reviewed plugin was manually scanned for malware by a TREK maintainer on each version — checked for hostile code, not for whether it works well. It is not a promise that the plugin is harmless.',
|
||||
'admin.plugins.security.trustTitle': 'The bottom line',
|
||||
'admin.plugins.security.trustBody': 'Installing a plugin is like installing any third-party app: only add code from authors you trust, and when in doubt, inspect it yourself first. TREK takes no responsibility for third-party plugins.',
|
||||
'admin.plugins.security.trustBody':
|
||||
'Installing a plugin is like installing any third-party app: only add code from authors you trust, and when in doubt, inspect it yourself first. TREK takes no responsibility for third-party plugins.',
|
||||
'admin.plugins.runtimeOn': 'Runtime on',
|
||||
'admin.plugins.tabDiscover': 'Discover',
|
||||
'admin.plugins.searchPlaceholder': 'Search plugins…',
|
||||
@@ -315,11 +346,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Plugin restarted',
|
||||
'admin.plugins.cap.readsTrips': 'Reads your trips',
|
||||
'admin.plugins.cap.readsUsers': 'Reads basic profiles',
|
||||
'admin.plugins.cap.readsCosts': 'Reads your costs',
|
||||
'admin.plugins.cap.readsPacking': 'Reads packing lists',
|
||||
'admin.plugins.cap.readsFiles': 'Reads trip files',
|
||||
'admin.plugins.cap.writesCosts': 'Adds costs',
|
||||
'admin.plugins.cap.writesPlaces': 'Edits places',
|
||||
'admin.plugins.cap.writesDays': 'Edits days',
|
||||
'admin.plugins.cap.writesItinerary': 'Edits itinerary',
|
||||
'admin.plugins.cap.writesTrips': 'Edits trips',
|
||||
'admin.plugins.cap.metadata': 'Adds metadata',
|
||||
'admin.plugins.cap.widget': 'Dashboard widget',
|
||||
'admin.plugins.cap.heroWidget': 'Boarding-pass widget',
|
||||
'admin.plugins.cap.placeSlot': 'Place detail',
|
||||
'admin.plugins.cap.realtime': 'Real-time updates',
|
||||
'admin.plugins.cap.photos': 'Provides photos',
|
||||
'admin.plugins.cap.calendar': 'Provides calendar events',
|
||||
'admin.plugins.cap.placeDetails': 'Enriches places',
|
||||
'admin.plugins.cap.warnings': 'Flags issues',
|
||||
'admin.plugins.cap.events': 'Reacts to activity',
|
||||
'admin.plugins.cap.requiresAddon': 'Requires {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'Needs {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Enable the required addon(s) first: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Enabled required plugin(s) first: {plugins}',
|
||||
'admin.plugins.dep.downloaded': 'Downloaded {id}',
|
||||
'admin.plugins.dep.resolveTitle': 'Missing dependencies',
|
||||
'admin.plugins.dep.resolveBody': '“{name}” needs these plugins installed before it can be enabled.',
|
||||
'admin.plugins.dep.requires': 'Requires {version}',
|
||||
'admin.plugins.dep.mismatch': 'Needs {wanted} — {installed} is installed',
|
||||
'admin.plugins.dep.download': 'Download',
|
||||
'admin.plugins.dep.update': 'Update',
|
||||
'admin.plugins.dep.resolveHint': 'Downloads the latest compatible version, including its own dependencies.',
|
||||
'admin.plugins.accessTitle': 'What it can access',
|
||||
'admin.plugins.connectsTitle': 'Connects to',
|
||||
'admin.plugins.detailsTitle': 'Details',
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'Nothing owed to you',
|
||||
'costs.yourShare': 'Your share',
|
||||
'costs.youPaid': 'You paid',
|
||||
'costs.outstanding': 'Outstanding amount',
|
||||
'costs.outstandingSub': 'Expenses with no payer yet',
|
||||
'costs.outstandingItems': 'expenses need a payer',
|
||||
'costs.expenses': 'Expenses',
|
||||
'costs.entries': '{count} entries',
|
||||
'costs.searchPlaceholder': 'Search expenses…',
|
||||
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'Rename',
|
||||
'collections.listMenu': 'List options',
|
||||
'collections.deleteList': 'Delete list',
|
||||
'collections.deleteListConfirm': 'This permanently deletes the list and all of its saved places. This cannot be undone.',
|
||||
'collections.deleteListConfirm':
|
||||
'This permanently deletes the list and all of its saved places. This cannot be undone.',
|
||||
'collections.listName': 'List name',
|
||||
'collections.listNamePlaceholder': 'e.g. Tokyo 2025',
|
||||
'collections.listColor': 'Colour',
|
||||
|
||||
@@ -6,8 +6,7 @@ const help: TranslationStrings = {
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody':
|
||||
'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -86,7 +86,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'Sign in with a passkey',
|
||||
'login.passkey.failed': 'Passkey sign-in failed. Please try again.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -25,7 +25,8 @@ const members: TranslationStrings = {
|
||||
'members.transferError': 'Failed to transfer ownership',
|
||||
'members.guests': 'Guests',
|
||||
'members.guest': 'Guest',
|
||||
'members.guestsHint': 'People without an account. They can be assigned to costs, packing and tasks, but cannot sign in.',
|
||||
'members.guestsHint':
|
||||
'People without an account. They can be assigned to costs, packing and tasks, but cannot sign in.',
|
||||
'members.addGuest': 'Add guest',
|
||||
'members.guestNamePlaceholder': 'Guest name',
|
||||
'members.guestAdded': 'Guest added',
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Select at least one type to import.',
|
||||
'places.selectionCount': '{count} selected',
|
||||
'places.deleteSelected': 'Delete selected',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': '{count} places imported from KMZ/KML',
|
||||
'places.urlResolved': 'Place imported from URL',
|
||||
'places.importList': 'List Import',
|
||||
|
||||
@@ -356,7 +356,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -386,7 +387,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -409,10 +411,12 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Offline cache',
|
||||
'settings.offline.mode.title': 'Offline mode',
|
||||
'settings.offline.mode.force': 'Force offline mode',
|
||||
'settings.offline.mode.forceHint': 'Work from cached data only and queue your changes. Turn off to reconnect and sync.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'Work from cached data only and queue your changes. Turn off to reconnect and sync.',
|
||||
'settings.offline.mode.active': 'Offline mode is on — your changes are queued and will sync when you turn it off.',
|
||||
'settings.offline.prepare.title': 'Prepare for offline',
|
||||
'settings.offline.prepare.hint': 'Download everything you need — trip data, documents and map tiles — before you lose connection.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Download everything you need — trip data, documents and map tiles — before you lose connection.',
|
||||
'settings.offline.prepare.button': 'Download for offline use',
|
||||
'settings.offline.prepare.running': 'Downloading…',
|
||||
'settings.offline.prepare.done': 'Ready for offline use',
|
||||
@@ -423,7 +427,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Syncing…',
|
||||
'settings.offline.storage.title': 'What to store offline',
|
||||
'settings.offline.storage.tiles': 'Store map tiles offline',
|
||||
'settings.offline.storage.tilesHint': 'Map tiles use the most storage. Turn this off to keep only trip data and documents offline.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'Map tiles use the most storage. Turn this off to keep only trip data and documents offline.',
|
||||
'settings.offline.storage.tripsTitle': 'Trips',
|
||||
'settings.offline.storage.tripOn': 'Stored offline',
|
||||
'settings.offline.storage.tripOff': 'Not stored',
|
||||
@@ -436,7 +441,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': 'No trips cached yet. Connect to the internet to sync.',
|
||||
'settings.offline.loading': 'Loading…',
|
||||
'settings.offline.conflicts.title': 'Sync conflicts',
|
||||
'settings.offline.conflicts.hint': 'Changes you made offline clash with newer changes on the server. Choose which version to keep.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'Changes you made offline clash with newer changes on the server. Choose which version to keep.',
|
||||
'settings.offline.conflicts.keepMine': 'Keep mine',
|
||||
'settings.offline.conflicts.keepServer': 'Keep theirs',
|
||||
'settings.offline.conflicts.mine': 'Your version',
|
||||
|
||||
@@ -28,7 +28,8 @@ const trip: TranslationStrings = {
|
||||
'trip.confirm.deletePlaces': 'Delete {count} places?',
|
||||
'trip.toast.placesDeleted': '{count} places deleted',
|
||||
'trip.invite.linkTitle': 'Trip invite link',
|
||||
'trip.invite.linkHint': 'Anyone with a TREK account who opens this link joins the trip as a member. Regenerate to invalidate the old link.',
|
||||
'trip.invite.linkHint':
|
||||
'Anyone with a TREK account who opens this link joins the trip as a member. Regenerate to invalidate the old link.',
|
||||
'trip.invite.create': 'Create invite link',
|
||||
'trip.invite.regenerate': 'Regenerate',
|
||||
'trip.invite.disable': 'Disable',
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'Route optimized',
|
||||
'undo.deletePlace': 'Place deleted',
|
||||
'undo.deletePlaces': 'Places deleted',
|
||||
'undo.changeCategory': "Category changed",
|
||||
'undo.changeCategory': 'Category changed',
|
||||
'undo.moveDay': 'Place moved to another day',
|
||||
'undo.lock': 'Place lock toggled',
|
||||
'undo.importGpx': 'GPX import',
|
||||
|
||||
@@ -96,6 +96,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Recomendado',
|
||||
'admin.weatherKey': 'Clave API de OpenWeatherMap',
|
||||
'admin.weatherKeyHint': 'Para datos meteorológicos. Gratis en openweathermap.org',
|
||||
'admin.unsplashKey': 'Clave de API de Unsplash',
|
||||
'admin.unsplashKeyHint': 'Para la búsqueda de imágenes. Gratis en unsplash.com/developers',
|
||||
'admin.validateKey': 'Probar',
|
||||
'admin.keyValid': 'Conectado',
|
||||
'admin.keyInvalid': 'No válida',
|
||||
@@ -161,6 +163,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Plugins',
|
||||
'admin.plugins.rescan': 'Volver a escanear',
|
||||
'admin.plugins.rescanned': 'Carpeta de plugins reescaneada',
|
||||
'admin.plugins.upload': 'Subir plugin',
|
||||
'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.sideloadedHint': 'Subido manualmente — no proviene del registro, sin firmar ni revisar',
|
||||
'admin.plugins.browse': 'Explorar plugins',
|
||||
'admin.plugins.installed': 'Instalado',
|
||||
'admin.plugins.install': 'Instalar',
|
||||
@@ -175,11 +182,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'No hay errores registrados.',
|
||||
'admin.plugins.uninstalled': 'Plugin desinstalado',
|
||||
'admin.plugins.uninstallTitle': '¿Desinstalar el plugin?',
|
||||
'admin.plugins.uninstallBody': 'Esto detiene el plugin, elimina su código y borra todos sus datos. No se puede deshacer.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'Esto detiene el plugin, elimina su código y borra todos sus datos. No se puede deshacer.',
|
||||
'admin.plugins.status.starting': 'Iniciando…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Página',
|
||||
'admin.plugins.type.integration': 'Integración',
|
||||
'admin.plugins.type.trip-page': 'Página de viaje',
|
||||
'admin.plugins.reviewed': 'Revisado',
|
||||
'admin.plugins.unreviewed': 'No revisado',
|
||||
'admin.plugins.updated': 'Plugin actualizado',
|
||||
@@ -189,10 +198,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Plugins',
|
||||
'admin.plugins.subtitle': 'Instala y gestiona plugins de terceros en tu instancia.',
|
||||
'admin.plugins.disabledTitle': 'Los plugins están desactivados',
|
||||
'admin.plugins.disabledBody': 'El entorno de ejecución de plugins está apagado (TREK_PLUGINS_ENABLED). Ningún plugin puede ejecutarse hasta que un administrador lo active en la configuración del servidor.',
|
||||
'admin.plugins.disabledBody':
|
||||
'El entorno de ejecución de plugins está apagado (TREK_PLUGINS_ENABLED). Ningún plugin puede ejecutarse hasta que un administrador lo active en la configuración del servidor.',
|
||||
'admin.plugins.empty': 'Aún no hay plugins instalados.',
|
||||
'admin.plugins.loadError': 'No se pudieron cargar los plugins.',
|
||||
'admin.plugins.scaffoldNote': 'La instalación y activación de plugins llegarán en una actualización posterior. Por ahora este panel solo muestra los plugins instalados.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'La instalación y activación de plugins llegarán en una actualización posterior. Por ahora este panel solo muestra los plugins instalados.',
|
||||
'admin.plugins.status.active': 'Activo',
|
||||
'admin.plugins.status.inactive': 'Inactivo',
|
||||
'admin.plugins.status.disabled': 'Desactivado',
|
||||
@@ -215,10 +226,32 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Almacenar sus propios datos en una base de datos aislada',
|
||||
'admin.plugins.perm.db:read:trips': 'Leer los viajes a los que el usuario activo tiene acceso',
|
||||
'admin.plugins.perm.db:read:users': 'Leer información básica del perfil (nombre, avatar — nunca credenciales)',
|
||||
'admin.plugins.perm.db:read:costs':
|
||||
'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:read:files': 'Leer los archivos de los viajes a los que el usuario activo tiene acceso',
|
||||
'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':
|
||||
'Añadir, editar y eliminar lugares en los viajes que el usuario activo puede editar',
|
||||
'admin.plugins.perm.db:write:days': 'Añadir, editar y eliminar días en los viajes que el usuario activo puede editar',
|
||||
'admin.plugins.perm.db:write:itinerary':
|
||||
'Asignar y quitar lugares en los días de los viajes que el usuario activo puede editar',
|
||||
'admin.plugins.perm.db:write:trips':
|
||||
'Editar los detalles del viaje (título, fechas, moneda…) en los viajes que el usuario activo puede editar',
|
||||
'admin.plugins.perm.db:meta':
|
||||
'Adjuntar sus propios datos privados a los viajes, lugares y días a los que el usuario activo tiene acceso',
|
||||
'admin.plugins.perm.ws:broadcast:trip': 'Enviar actualizaciones en tiempo real a los miembros del viaje',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'Enviar actualizaciones en tiempo real a usuarios individuales',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Proporcionar fotos a la sección Fotos',
|
||||
'admin.plugins.perm.hook:calendar-source': 'Proporcionar eventos al calendario',
|
||||
'admin.plugins.perm.hook:place-detail-provider':
|
||||
'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.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.updateConsentTitle': 'Esta actualización necesita nuevos permisos',
|
||||
'admin.plugins.updateConsentBody':
|
||||
@@ -270,11 +303,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Plugin reiniciado',
|
||||
'admin.plugins.cap.readsTrips': 'Lee tus viajes',
|
||||
'admin.plugins.cap.readsUsers': 'Lee perfiles básicos',
|
||||
'admin.plugins.cap.readsCosts': 'Lee tus gastos',
|
||||
'admin.plugins.cap.readsPacking': 'Lee las listas de equipaje',
|
||||
'admin.plugins.cap.readsFiles': 'Lee los archivos del viaje',
|
||||
'admin.plugins.cap.writesCosts': 'Añade gastos',
|
||||
'admin.plugins.cap.writesPlaces': 'Edita lugares',
|
||||
'admin.plugins.cap.writesDays': 'Edita días',
|
||||
'admin.plugins.cap.writesItinerary': 'Edita itinerario',
|
||||
'admin.plugins.cap.writesTrips': 'Edita viajes',
|
||||
'admin.plugins.cap.metadata': 'Añade metadatos',
|
||||
'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.realtime': 'Actualizaciones en tiempo real',
|
||||
'admin.plugins.cap.photos': 'Proporciona fotos',
|
||||
'admin.plugins.cap.calendar': 'Proporciona eventos de calendario',
|
||||
'admin.plugins.cap.placeDetails': 'Enriquece lugares',
|
||||
'admin.plugins.cap.warnings': 'Marca problemas',
|
||||
'admin.plugins.cap.events': 'Reacciona a la actividad',
|
||||
'admin.plugins.cap.requiresAddon': 'Requiere {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'Necesita {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Activa primero los complementos necesarios: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Se activaron primero los plugins necesarios: {plugins}',
|
||||
'admin.plugins.dep.downloaded': '{id} descargado',
|
||||
'admin.plugins.dep.resolveTitle': 'Dependencias faltantes',
|
||||
'admin.plugins.dep.resolveBody': '«{name}» necesita que estos plugins estén instalados antes de poder activarse.',
|
||||
'admin.plugins.dep.requires': 'Requiere {version}',
|
||||
'admin.plugins.dep.mismatch': 'Necesita {wanted}: {installed} está instalado',
|
||||
'admin.plugins.dep.download': 'Descargar',
|
||||
'admin.plugins.dep.update': 'Actualizar',
|
||||
'admin.plugins.dep.resolveHint': 'Descarga la última versión compatible, incluidas sus propias dependencias.',
|
||||
'admin.plugins.accessTitle': 'A qué puede acceder',
|
||||
'admin.plugins.connectsTitle': 'Se conecta a',
|
||||
'admin.plugins.detailsTitle': 'Detalles',
|
||||
@@ -493,7 +551,8 @@ const admin: TranslationStrings = {
|
||||
'admin.group.maintenance': 'Maintenance',
|
||||
'admin.invite.tripLabel': 'Añadir a un viaje (opcional)',
|
||||
'admin.invite.tripNone': 'Sin viaje',
|
||||
'admin.invite.tripHint': 'El nuevo usuario se añade automáticamente a este viaje cuando se registra mediante el enlace.',
|
||||
'admin.invite.tripHint':
|
||||
'El nuevo usuario se añade automáticamente a este viaje cuando se registra mediante el enlace.',
|
||||
'admin.invite.boundTo': 'se añade a {trip}',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'Nadie te debe nada',
|
||||
'costs.yourShare': 'Tu parte',
|
||||
'costs.youPaid': 'Pagaste',
|
||||
'costs.outstanding': 'Importe pendiente',
|
||||
'costs.outstandingSub': 'Gastos sin pagador aún',
|
||||
'costs.outstandingItems': 'gastos sin pagador',
|
||||
'costs.expenses': 'Gastos',
|
||||
'costs.entries': '{count} entradas',
|
||||
'costs.searchPlaceholder': 'Buscar gastos…',
|
||||
@@ -116,10 +119,10 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'Solo en el total — aún sin saldar',
|
||||
'costs.tapToInclude': 'Toca para incluir',
|
||||
'costs.amount': 'Importe',
|
||||
'costs.split': "Split",
|
||||
'costs.splitEqually': "Equally",
|
||||
'costs.splitCustom': "Custom",
|
||||
'costs.splitTicket': "Ticket",
|
||||
'costs.split': 'Split',
|
||||
'costs.splitEqually': 'Equally',
|
||||
'costs.splitCustom': 'Custom',
|
||||
'costs.splitTicket': 'Ticket',
|
||||
'costs.filter.allCategories': 'Todas las categorías',
|
||||
'costs.filter.allDays': 'Todos los días',
|
||||
'costs.expensesCount': '{count} gastos',
|
||||
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'Renombrar',
|
||||
'collections.listMenu': 'Opciones de la lista',
|
||||
'collections.deleteList': 'Eliminar lista',
|
||||
'collections.deleteListConfirm': 'Esto elimina de forma permanente la lista y todos sus lugares guardados. No se puede deshacer.',
|
||||
'collections.deleteListConfirm':
|
||||
'Esto elimina de forma permanente la lista y todos sus lugares guardados. No se puede deshacer.',
|
||||
'collections.listName': 'Nombre de la lista',
|
||||
'collections.listNamePlaceholder': 'p. ej. Tokio 2025',
|
||||
'collections.listColor': 'Color',
|
||||
@@ -127,7 +128,8 @@ const collection: TranslationStrings = {
|
||||
'collections.share.memberHint': 'Solo el propietario de la lista puede invitar o quitar personas.',
|
||||
'collections.share.cancel': 'Cancelar',
|
||||
'collections.share.leave': 'Salir de la lista',
|
||||
'collections.share.leaveConfirm': '¿Salir de esta lista compartida? Perderás el acceso hasta que vuelvan a invitarte.',
|
||||
'collections.share.leaveConfirm':
|
||||
'¿Salir de esta lista compartida? Perderás el acceso hasta que vuelvan a invitarte.',
|
||||
'collections.share.left': 'Has salido de la lista',
|
||||
'collections.share.remove': 'Quitar',
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { TranslationStrings } from '../types';
|
||||
|
||||
// English fallback until 'es' is translated.
|
||||
const help: TranslationStrings = {
|
||||
'help.title': "Help & Docs",
|
||||
'help.search': "Search docs…",
|
||||
'help.contents': "Contents",
|
||||
'help.noResults': "No matching pages.",
|
||||
'help.title': 'Help & Docs',
|
||||
'help.search': 'Search docs…',
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody': "The help content is fetched from the TREK wiki. Check your connection and try again.",
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -87,7 +87,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'Iniciar sesión con una passkey',
|
||||
'login.passkey.failed': 'Error al iniciar sesión con la passkey. Inténtalo de nuevo.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -25,7 +25,8 @@ const members: TranslationStrings = {
|
||||
'members.transferError': 'Error al transferir la propiedad',
|
||||
'members.guests': 'Invitados',
|
||||
'members.guest': 'Invitado',
|
||||
'members.guestsHint': 'Personas sin cuenta. Se les pueden asignar gastos, equipaje y tareas, pero no pueden iniciar sesión.',
|
||||
'members.guestsHint':
|
||||
'Personas sin cuenta. Se les pueden asignar gastos, equipaje y tareas, pero no pueden iniciar sesión.',
|
||||
'members.addGuest': 'Añadir invitado',
|
||||
'members.guestNamePlaceholder': 'Nombre del invitado',
|
||||
'members.guestAdded': 'Invitado añadido',
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Selecciona al menos un tipo.',
|
||||
'places.selectionCount': '{count} seleccionado(s)',
|
||||
'places.deleteSelected': 'Eliminar selección',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': '{count} lugares importados desde KMZ/KML',
|
||||
'places.urlResolved': 'Lugar importado desde URL',
|
||||
'places.importList': 'Importar lista',
|
||||
|
||||
@@ -29,7 +29,8 @@ const settings: TranslationStrings = {
|
||||
'settings.mapStylePlaceholder': 'Seleccionar un estilo de Mapbox',
|
||||
'settings.mapStyleHint': 'Preset o tu propia URL mapbox://styles/USER/ID',
|
||||
'settings.mapOpenFreeMapStylePlaceholder': 'Seleccionar un estilo de OpenFreeMap',
|
||||
'settings.mapOpenFreeMapStyleHint': 'Preset o URL de estilo de OpenFreeMap. Los estilos de OpenFreeMap funcionan sin token.',
|
||||
'settings.mapOpenFreeMapStyleHint':
|
||||
'Preset o URL de estilo de OpenFreeMap. Los estilos de OpenFreeMap funcionan sin token.',
|
||||
'settings.map3dBuildings': 'Edificios 3D y terreno',
|
||||
'settings.map3dHint':
|
||||
'Inclinación + extrusiones 3D reales de edificios — funciona con todos los estilos, incluyendo satélite.',
|
||||
@@ -331,14 +332,16 @@ const settings: TranslationStrings = {
|
||||
'settings.airtrail.test.success': 'Conectado: {count} vuelo(s) encontrado(s)',
|
||||
'settings.airtrail.test.failed': 'Error de conexión',
|
||||
'settings.aiParsing.title': 'Análisis con IA',
|
||||
'settings.aiParsing.hint': 'Usa tu propio modelo de IA para extraer reservas de los archivos subidos. Esto solo se aplica cuando tu administrador no ha configurado un modelo para toda la instancia.',
|
||||
'settings.aiParsing.hint':
|
||||
'Usa tu propio modelo de IA para extraer reservas de los archivos subidos. Esto solo se aplica cuando tu administrador no ha configurado un modelo para toda la instancia.',
|
||||
'settings.aiParsing.provider': 'Proveedor',
|
||||
'settings.aiParsing.providerLocal': 'Local (Ollama)',
|
||||
'settings.aiParsing.providerOpenai': 'OpenAI',
|
||||
'settings.aiParsing.providerAnthropic': 'Anthropic',
|
||||
'settings.aiParsing.model': 'Modelo',
|
||||
'settings.aiParsing.baseUrl': 'URL base',
|
||||
'settings.aiParsing.baseUrlHint': 'Donde se ejecuta el modelo: un servidor Ollama local o un endpoint compatible con OpenAI.',
|
||||
'settings.aiParsing.baseUrlHint':
|
||||
'Donde se ejecuta el modelo: un servidor Ollama local o un endpoint compatible con OpenAI.',
|
||||
'settings.aiParsing.apiKey': 'Clave de API',
|
||||
'settings.aiParsing.apiKeyHint': 'Se almacena cifrada. Déjalo en blanco para mantener la clave actual.',
|
||||
'settings.aiParsing.multimodal': 'Enviar documentos como imágenes',
|
||||
@@ -361,7 +364,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -391,7 +395,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -414,10 +419,13 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Caché offline',
|
||||
'settings.offline.mode.title': 'Modo offline',
|
||||
'settings.offline.mode.force': 'Forzar el modo offline',
|
||||
'settings.offline.mode.forceHint': 'Trabaja solo con los datos en caché y pon tus cambios en cola. Desactívalo para reconectar y sincronizar.',
|
||||
'settings.offline.mode.active': 'El modo offline está activado: tus cambios están en cola y se sincronizarán cuando lo desactives.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'Trabaja solo con los datos en caché y pon tus cambios en cola. Desactívalo para reconectar y sincronizar.',
|
||||
'settings.offline.mode.active':
|
||||
'El modo offline está activado: tus cambios están en cola y se sincronizarán cuando lo desactives.',
|
||||
'settings.offline.prepare.title': 'Prepárate para estar offline',
|
||||
'settings.offline.prepare.hint': 'Descarga todo lo que necesitas —datos del viaje, documentos y mosaicos del mapa— antes de perder la conexión.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Descarga todo lo que necesitas —datos del viaje, documentos y mosaicos del mapa— antes de perder la conexión.',
|
||||
'settings.offline.prepare.button': 'Descargar para uso offline',
|
||||
'settings.offline.prepare.running': 'Descargando…',
|
||||
'settings.offline.prepare.done': 'Listo para usar offline',
|
||||
@@ -428,12 +436,14 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Sincronizando…',
|
||||
'settings.offline.storage.title': 'Qué almacenar offline',
|
||||
'settings.offline.storage.tiles': 'Almacenar mosaicos del mapa offline',
|
||||
'settings.offline.storage.tilesHint': 'Los mosaicos del mapa ocupan más almacenamiento. Desactívalo para conservar offline solo los datos del viaje y los documentos.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'Los mosaicos del mapa ocupan más almacenamiento. Desactívalo para conservar offline solo los datos del viaje y los documentos.',
|
||||
'settings.offline.storage.tripsTitle': 'Viajes',
|
||||
'settings.offline.storage.tripOn': 'Almacenado offline',
|
||||
'settings.offline.storage.tripOff': 'No almacenado',
|
||||
'settings.offline.clear': 'Vaciar caché',
|
||||
'settings.offline.clearConfirm': '¿Vaciar todos los datos de viaje offline? Puedes volver a sincronizar en cualquier momento con conexión.',
|
||||
'settings.offline.clearConfirm':
|
||||
'¿Vaciar todos los datos de viaje offline? Puedes volver a sincronizar en cualquier momento con conexión.',
|
||||
'settings.offline.stats.trips': 'Viajes en caché',
|
||||
'settings.offline.stats.pending': 'Cambios pendientes',
|
||||
'settings.offline.stats.failed': 'Cambios fallidos',
|
||||
@@ -441,7 +451,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': 'Aún no hay viajes en caché. Conéctate a internet para sincronizar.',
|
||||
'settings.offline.loading': 'Cargando…',
|
||||
'settings.offline.conflicts.title': 'Conflictos de sincronización',
|
||||
'settings.offline.conflicts.hint': 'Los cambios que hiciste offline chocan con cambios más recientes en el servidor. Elige qué versión conservar.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'Los cambios que hiciste offline chocan con cambios más recientes en el servidor. Elige qué versión conservar.',
|
||||
'settings.offline.conflicts.keepMine': 'Conservar la mía',
|
||||
'settings.offline.conflicts.keepServer': 'Conservar la del servidor',
|
||||
'settings.offline.conflicts.mine': 'Tu versión',
|
||||
|
||||
@@ -28,7 +28,8 @@ const trip: TranslationStrings = {
|
||||
'trip.confirm.deletePlaces': '¿Eliminar {count} lugares?',
|
||||
'trip.toast.placesDeleted': '{count} lugares eliminados',
|
||||
'trip.invite.linkTitle': 'Enlace de invitación al viaje',
|
||||
'trip.invite.linkHint': 'Cualquier persona con una cuenta de TREK que abra este enlace se unirá al viaje como miembro. Regenéralo para invalidar el enlace anterior.',
|
||||
'trip.invite.linkHint':
|
||||
'Cualquier persona con una cuenta de TREK que abra este enlace se unirá al viaje como miembro. Regenéralo para invalidar el enlace anterior.',
|
||||
'trip.invite.create': 'Crear enlace de invitación',
|
||||
'trip.invite.regenerate': 'Regenerar',
|
||||
'trip.invite.disable': 'Desactivar',
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'Ruta optimizada',
|
||||
'undo.deletePlace': 'Lugar eliminado',
|
||||
'undo.deletePlaces': 'Lugares eliminados',
|
||||
'undo.changeCategory': "Category changed",
|
||||
'undo.changeCategory': 'Category changed',
|
||||
'undo.moveDay': 'Lugar movido a otro día',
|
||||
'undo.lock': 'Bloqueo de lugar activado/desactivado',
|
||||
'undo.importGpx': 'Importación GPX',
|
||||
|
||||
@@ -95,6 +95,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Recommandé',
|
||||
'admin.weatherKey': 'Clé API OpenWeatherMap',
|
||||
'admin.weatherKeyHint': 'Pour les données météo. Gratuit sur openweathermap.org',
|
||||
'admin.unsplashKey': 'Clé API Unsplash',
|
||||
'admin.unsplashKeyHint': 'Pour la recherche d’images. Gratuit sur unsplash.com/developers',
|
||||
'admin.validateKey': 'Tester',
|
||||
'admin.keyValid': 'Connecté',
|
||||
'admin.keyInvalid': 'Invalide',
|
||||
@@ -163,6 +165,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Plugins',
|
||||
'admin.plugins.rescan': 'Réanalyser',
|
||||
'admin.plugins.rescanned': 'Dossier des plugins réanalysé',
|
||||
'admin.plugins.upload': 'Téléverser un plugin',
|
||||
'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.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é',
|
||||
'admin.plugins.install': 'Installer',
|
||||
@@ -177,11 +184,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'Aucune erreur enregistrée.',
|
||||
'admin.plugins.uninstalled': 'Plugin désinstallé',
|
||||
'admin.plugins.uninstallTitle': 'Désinstaller le plugin ?',
|
||||
'admin.plugins.uninstallBody': 'Cela arrête le plugin, supprime son code et efface toutes ses données. Cette action est irréversible.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'Cela arrête le plugin, supprime son code et efface toutes ses données. Cette action est irréversible.',
|
||||
'admin.plugins.status.starting': 'Démarrage…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Page',
|
||||
'admin.plugins.type.integration': 'Intégration',
|
||||
'admin.plugins.type.trip-page': 'Page de voyage',
|
||||
'admin.plugins.reviewed': 'Vérifié',
|
||||
'admin.plugins.unreviewed': 'Non vérifié',
|
||||
'admin.plugins.updated': 'Plugin mis à jour',
|
||||
@@ -191,10 +200,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Plugins',
|
||||
'admin.plugins.subtitle': 'Installez et gérez des plugins tiers sur votre instance.',
|
||||
'admin.plugins.disabledTitle': 'Les plugins sont désactivés',
|
||||
'admin.plugins.disabledBody': "L'environnement d'exécution des plugins est désactivé (TREK_PLUGINS_ENABLED). Aucun plugin ne peut fonctionner tant qu'un administrateur ne l'active pas dans la configuration du serveur.",
|
||||
'admin.plugins.disabledBody':
|
||||
"L'environnement d'exécution des plugins est désactivé (TREK_PLUGINS_ENABLED). Aucun plugin ne peut fonctionner tant qu'un administrateur ne l'active pas dans la configuration du serveur.",
|
||||
'admin.plugins.empty': 'Aucun plugin installé pour le moment.',
|
||||
'admin.plugins.loadError': 'Impossible de charger les plugins.',
|
||||
'admin.plugins.scaffoldNote': "L'installation et l'activation des plugins arriveront dans une mise à jour ultérieure. Ce panneau liste actuellement uniquement les plugins installés.",
|
||||
'admin.plugins.scaffoldNote':
|
||||
"L'installation et l'activation des plugins arriveront dans une mise à jour ultérieure. Ce panneau liste actuellement uniquement les plugins installés.",
|
||||
'admin.plugins.status.active': 'Actif',
|
||||
'admin.plugins.status.inactive': 'Inactif',
|
||||
'admin.plugins.status.disabled': 'Désactivé',
|
||||
@@ -217,10 +228,30 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Stocker ses propres données dans une base de données isolée',
|
||||
'admin.plugins.perm.db:read:trips': "Lire les voyages accessibles à l'utilisateur actif",
|
||||
'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:read:files': "Lire les fichiers des voyages accessibles à l'utilisateur actif",
|
||||
'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':
|
||||
"Ajouter, modifier et supprimer des lieux sur les voyages que l'utilisateur actif peut modifier",
|
||||
'admin.plugins.perm.db:write:days':
|
||||
"Ajouter, modifier et supprimer des jours sur les voyages que l'utilisateur actif peut modifier",
|
||||
'admin.plugins.perm.db:write:itinerary':
|
||||
"Attribuer et retirer des lieux aux jours des voyages que l'utilisateur actif peut modifier",
|
||||
'admin.plugins.perm.db:write:trips':
|
||||
"Modifier les détails du voyage (titre, dates, devise…) sur les voyages que l'utilisateur actif peut modifier",
|
||||
'admin.plugins.perm.db:meta':
|
||||
"Associer ses propres données privées aux voyages, lieux et jours auxquels l'utilisateur actif a accès",
|
||||
'admin.plugins.perm.ws:broadcast:trip': 'Envoyer des mises à jour en temps réel aux membres du voyage',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'Envoyer des mises à jour en temps réel à des utilisateurs individuels',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Fournir des photos à Photos',
|
||||
'admin.plugins.perm.hook:calendar-source': 'Fournir des événements au calendrier',
|
||||
'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.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.updateConsentTitle': 'Cette mise à jour nécessite de nouvelles permissions',
|
||||
'admin.plugins.updateConsentBody':
|
||||
@@ -273,11 +304,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Plugin redémarré',
|
||||
'admin.plugins.cap.readsTrips': 'Lit vos voyages',
|
||||
'admin.plugins.cap.readsUsers': 'Lit les profils de base',
|
||||
'admin.plugins.cap.readsCosts': 'Lit vos dépenses',
|
||||
'admin.plugins.cap.readsPacking': 'Lit les listes de bagages',
|
||||
'admin.plugins.cap.readsFiles': 'Lit les fichiers du voyage',
|
||||
'admin.plugins.cap.writesCosts': 'Ajoute des dépenses',
|
||||
'admin.plugins.cap.writesPlaces': 'Modifie les lieux',
|
||||
'admin.plugins.cap.writesDays': 'Modifie les jours',
|
||||
'admin.plugins.cap.writesItinerary': "Modifie l'itinéraire",
|
||||
'admin.plugins.cap.writesTrips': 'Modifie les voyages',
|
||||
'admin.plugins.cap.metadata': 'Ajoute des métadonnées',
|
||||
'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.realtime': 'Mises à jour en temps réel',
|
||||
'admin.plugins.cap.photos': 'Fournit des photos',
|
||||
'admin.plugins.cap.calendar': 'Fournit des événements de calendrier',
|
||||
'admin.plugins.cap.placeDetails': 'Enrichit les lieux',
|
||||
'admin.plugins.cap.warnings': 'Signale les problèmes',
|
||||
'admin.plugins.cap.events': 'Réagit à l’activité',
|
||||
'admin.plugins.cap.requiresAddon': 'Nécessite {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'Requiert {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Activez d’abord le ou les modules requis : {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Plugin(s) requis activé(s) au préalable : {plugins}',
|
||||
'admin.plugins.dep.downloaded': '{id} téléchargé',
|
||||
'admin.plugins.dep.resolveTitle': 'Dépendances manquantes',
|
||||
'admin.plugins.dep.resolveBody': '« {name} » nécessite l’installation de ces plugins avant de pouvoir être activé.',
|
||||
'admin.plugins.dep.requires': 'Nécessite {version}',
|
||||
'admin.plugins.dep.mismatch': 'Nécessite {wanted} — {installed} est installé',
|
||||
'admin.plugins.dep.download': 'Télécharger',
|
||||
'admin.plugins.dep.update': 'Mettre à jour',
|
||||
'admin.plugins.dep.resolveHint': 'Télécharge la dernière version compatible, y compris ses propres dépendances.',
|
||||
'admin.plugins.accessTitle': 'Ce à quoi il peut accéder',
|
||||
'admin.plugins.connectsTitle': 'Se connecte à',
|
||||
'admin.plugins.detailsTitle': 'Détails',
|
||||
@@ -491,7 +547,8 @@ const admin: TranslationStrings = {
|
||||
'admin.group.maintenance': 'Maintenance',
|
||||
'admin.invite.tripLabel': 'Ajouter à un voyage (facultatif)',
|
||||
'admin.invite.tripNone': 'Aucun voyage',
|
||||
'admin.invite.tripHint': 'Le nouvel utilisateur est automatiquement ajouté à ce voyage lorsqu\'il s\'inscrit via le lien.',
|
||||
'admin.invite.tripHint':
|
||||
"Le nouvel utilisateur est automatiquement ajouté à ce voyage lorsqu'il s'inscrit via le lien.",
|
||||
'admin.invite.boundTo': 'ajoute à {trip}',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'On ne vous doit rien',
|
||||
'costs.yourShare': 'Votre part',
|
||||
'costs.youPaid': 'Vous avez payé',
|
||||
'costs.outstanding': 'Montant en attente',
|
||||
'costs.outstandingSub': 'Dépenses sans payeur',
|
||||
'costs.outstandingItems': 'dépenses sans payeur',
|
||||
'costs.expenses': 'Dépenses',
|
||||
'costs.entries': '{count} entrées',
|
||||
'costs.searchPlaceholder': 'Rechercher des dépenses…',
|
||||
@@ -116,10 +119,10 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'Total seulement — pas encore réglé',
|
||||
'costs.tapToInclude': 'Toucher pour inclure',
|
||||
'costs.amount': 'Montant',
|
||||
'costs.split': "Split",
|
||||
'costs.splitEqually': "Equally",
|
||||
'costs.splitCustom': "Custom",
|
||||
'costs.splitTicket': "Ticket",
|
||||
'costs.split': 'Split',
|
||||
'costs.splitEqually': 'Equally',
|
||||
'costs.splitCustom': 'Custom',
|
||||
'costs.splitTicket': 'Ticket',
|
||||
'costs.filter.allCategories': 'Toutes les catégories',
|
||||
'costs.filter.allDays': 'Tous les jours',
|
||||
'costs.expensesCount': '{count} dépenses',
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { TranslationStrings } from '../types';
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Enregistrer {count} dans une liste',
|
||||
'collections.addedNToList': '{count} enregistrés dans {name}',
|
||||
'collections.noOwnLists': 'Vous n\'avez pas encore de listes',
|
||||
'collections.noOwnLists': "Vous n'avez pas encore de listes",
|
||||
'collections.saveToListHint': 'Les doublons sont ignorés automatiquement',
|
||||
'collections.role.label': 'Rôle',
|
||||
'collections.role.viewer': 'Lecteur',
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'Renommer',
|
||||
'collections.listMenu': 'Options de la liste',
|
||||
'collections.deleteList': 'Supprimer la liste',
|
||||
'collections.deleteListConfirm': 'Cela supprime définitivement la liste et tous ses lieux enregistrés. Cette action est irréversible.',
|
||||
'collections.deleteListConfirm':
|
||||
'Cela supprime définitivement la liste et tous ses lieux enregistrés. Cette action est irréversible.',
|
||||
'collections.listName': 'Nom de la liste',
|
||||
'collections.listNamePlaceholder': 'p. ex. Tokyo 2025',
|
||||
'collections.listColor': 'Couleur',
|
||||
@@ -127,7 +128,8 @@ const collection: TranslationStrings = {
|
||||
'collections.share.memberHint': 'Seul le propriétaire de la liste peut inviter ou retirer des personnes.',
|
||||
'collections.share.cancel': 'Annuler',
|
||||
'collections.share.leave': 'Quitter la liste',
|
||||
'collections.share.leaveConfirm': 'Quitter cette liste partagée ? Vous perdrez l’accès jusqu’à ce que vous soyez réinvité.',
|
||||
'collections.share.leaveConfirm':
|
||||
'Quitter cette liste partagée ? Vous perdrez l’accès jusqu’à ce que vous soyez réinvité.',
|
||||
'collections.share.left': 'Vous avez quitté la liste',
|
||||
'collections.share.remove': 'Retirer',
|
||||
|
||||
|
||||
@@ -42,8 +42,7 @@ const dashboard: TranslationStrings = {
|
||||
'dashboard.status.past': 'Passé',
|
||||
'dashboard.status.daysLeft': '{count} jours restants',
|
||||
'dashboard.toast.loadError': 'Impossible de charger les voyages',
|
||||
'dashboard.loadErrorBanner':
|
||||
"Impossible de joindre le serveur. Vos voyages sont en sécurité — veuillez réessayer.",
|
||||
'dashboard.loadErrorBanner': 'Impossible de joindre le serveur. Vos voyages sont en sécurité — veuillez réessayer.',
|
||||
'dashboard.retry': 'Réessayer',
|
||||
'dashboard.toast.created': 'Voyage créé avec succès !',
|
||||
'dashboard.toast.createError': 'Impossible de créer le voyage',
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { TranslationStrings } from '../types';
|
||||
|
||||
// English fallback until 'fr' is translated.
|
||||
const help: TranslationStrings = {
|
||||
'help.title': "Help & Docs",
|
||||
'help.search': "Search docs…",
|
||||
'help.contents': "Contents",
|
||||
'help.noResults': "No matching pages.",
|
||||
'help.title': 'Help & Docs',
|
||||
'help.search': 'Search docs…',
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody': "The help content is fetched from the TREK wiki. Check your connection and try again.",
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -89,7 +89,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'Se connecter avec une passkey',
|
||||
'login.passkey.failed': 'Échec de la connexion par passkey. Veuillez réessayer.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -25,12 +25,13 @@ const members: TranslationStrings = {
|
||||
'members.transferError': 'Échec du transfert de propriété',
|
||||
'members.guests': 'Invités',
|
||||
'members.guest': 'Invité',
|
||||
'members.guestsHint': 'Personnes sans compte. On peut leur attribuer des dépenses, des bagages et des tâches, mais elles ne peuvent pas se connecter.',
|
||||
'members.guestsHint':
|
||||
'Personnes sans compte. On peut leur attribuer des dépenses, des bagages et des tâches, mais elles ne peuvent pas se connecter.',
|
||||
'members.addGuest': 'Ajouter un invité',
|
||||
'members.guestNamePlaceholder': 'Nom de l\'invité',
|
||||
'members.guestNamePlaceholder': "Nom de l'invité",
|
||||
'members.guestAdded': 'Invité ajouté',
|
||||
'members.guestAddError': 'Échec de l\'ajout de l\'invité',
|
||||
'members.guestRenameError': 'Échec du renommage de l\'invité',
|
||||
'members.guestAddError': "Échec de l'ajout de l'invité",
|
||||
'members.guestRenameError': "Échec du renommage de l'invité",
|
||||
'members.guestRemoved': 'Invité supprimé',
|
||||
'members.confirmRemoveGuest': 'Supprimer cet invité ? Ses affectations et parts de dépenses seront aussi supprimées.',
|
||||
};
|
||||
|
||||
@@ -189,12 +189,12 @@ const packing: TranslationStrings = {
|
||||
'packing.tierPersonal': 'Personnel',
|
||||
'packing.tierPersonalHint': 'Privé — vous seul le voyez',
|
||||
'packing.tierShared': 'Partager avec…',
|
||||
'packing.noOneToShare': 'Personne d\'autre sur ce voyage pour l\'instant',
|
||||
'packing.noOneToShare': "Personne d'autre sur ce voyage pour l'instant",
|
||||
'packing.takenCareOf': 'par {name}',
|
||||
'packing.sharedWithCount': 'partagé avec {count}',
|
||||
'packing.broughtBy': 'apporté par {name}',
|
||||
'packing.alsoBring': 'Je peux l\'apporter aussi',
|
||||
'packing.alsoBringingStop': 'Je ne l\'apporte pas',
|
||||
'packing.alsoBring': "Je peux l'apporter aussi",
|
||||
'packing.alsoBringingStop': "Je ne l'apporte pas",
|
||||
'packing.cloneToMine': 'Copier dans ma liste',
|
||||
};
|
||||
export default packing;
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Sélectionnez au moins un type.',
|
||||
'places.selectionCount': '{count} sélectionné(s)',
|
||||
'places.deleteSelected': 'Supprimer la sélection',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': '{count} lieux importés depuis KMZ/KML',
|
||||
'places.urlResolved': "Lieu importé depuis l'URL",
|
||||
'places.importList': 'Import de liste',
|
||||
|
||||
@@ -29,7 +29,8 @@ const settings: TranslationStrings = {
|
||||
'settings.mapStylePlaceholder': 'Sélectionner un style Mapbox',
|
||||
'settings.mapStyleHint': 'Preset ou votre propre URL mapbox://styles/USER/ID',
|
||||
'settings.mapOpenFreeMapStylePlaceholder': 'Sélectionner un style OpenFreeMap',
|
||||
'settings.mapOpenFreeMapStyleHint': 'Preset ou URL de style OpenFreeMap. Les styles OpenFreeMap fonctionnent sans jeton.',
|
||||
'settings.mapOpenFreeMapStyleHint':
|
||||
'Preset ou URL de style OpenFreeMap. Les styles OpenFreeMap fonctionnent sans jeton.',
|
||||
'settings.map3dBuildings': 'Bâtiments 3D & terrain',
|
||||
'settings.map3dHint':
|
||||
'Inclinaison + extrusions 3D réelles des bâtiments — fonctionne avec tous les styles, y compris satellite.',
|
||||
@@ -336,20 +337,23 @@ const settings: TranslationStrings = {
|
||||
'settings.airtrail.test.success': 'Connecté — {count} vol(s) trouvé(s)',
|
||||
'settings.airtrail.test.failed': 'Échec de la connexion',
|
||||
'settings.aiParsing.title': 'Analyse par IA',
|
||||
'settings.aiParsing.hint': 'Utilisez votre propre modèle d\'IA pour extraire les réservations des fichiers importés. Cela ne s\'applique que si votre administrateur n\'a pas configuré de modèle pour l\'ensemble de l\'instance.',
|
||||
'settings.aiParsing.hint':
|
||||
"Utilisez votre propre modèle d'IA pour extraire les réservations des fichiers importés. Cela ne s'applique que si votre administrateur n'a pas configuré de modèle pour l'ensemble de l'instance.",
|
||||
'settings.aiParsing.provider': 'Fournisseur',
|
||||
'settings.aiParsing.providerLocal': 'Local (Ollama)',
|
||||
'settings.aiParsing.providerOpenai': 'OpenAI',
|
||||
'settings.aiParsing.providerAnthropic': 'Anthropic',
|
||||
'settings.aiParsing.model': 'Modèle',
|
||||
'settings.aiParsing.baseUrl': 'URL de base',
|
||||
'settings.aiParsing.baseUrlHint': 'Emplacement d\'exécution du modèle — un serveur Ollama local ou un point de terminaison compatible OpenAI.',
|
||||
'settings.aiParsing.baseUrlHint':
|
||||
"Emplacement d'exécution du modèle — un serveur Ollama local ou un point de terminaison compatible OpenAI.",
|
||||
'settings.aiParsing.apiKey': 'Clé API',
|
||||
'settings.aiParsing.apiKeyHint': 'Stockée de façon chiffrée. Laissez vide pour conserver la clé actuelle.',
|
||||
'settings.aiParsing.multimodal': 'Envoyer les documents sous forme d\'images',
|
||||
'settings.aiParsing.multimodalHint': 'Pour les modèles capables d\'analyser des images — envoie le PDF d\'origine au lieu du texte extrait.',
|
||||
'settings.aiParsing.toast.saved': 'Paramètres d\'IA enregistrés',
|
||||
'settings.aiParsing.toast.saveError': 'Impossible d\'enregistrer les paramètres d\'IA',
|
||||
'settings.aiParsing.multimodal': "Envoyer les documents sous forme d'images",
|
||||
'settings.aiParsing.multimodalHint':
|
||||
"Pour les modèles capables d'analyser des images — envoie le PDF d'origine au lieu du texte extrait.",
|
||||
'settings.aiParsing.toast.saved': "Paramètres d'IA enregistrés",
|
||||
'settings.aiParsing.toast.saveError': "Impossible d'enregistrer les paramètres d'IA",
|
||||
'settings.tabs.appearance': 'Appearance',
|
||||
'settings.appearance.theme': 'Theme',
|
||||
'settings.appearance.scheme': 'Color scheme',
|
||||
@@ -366,7 +370,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -396,7 +401,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -419,10 +425,13 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Cache hors ligne',
|
||||
'settings.offline.mode.title': 'Mode hors ligne',
|
||||
'settings.offline.mode.force': 'Forcer le mode hors ligne',
|
||||
'settings.offline.mode.forceHint': "Travaillez uniquement à partir des données en cache et mettez vos modifications en file d'attente. Désactivez pour vous reconnecter et synchroniser.",
|
||||
'settings.offline.mode.active': "Le mode hors ligne est activé — vos modifications sont en file d'attente et seront synchronisées lorsque vous le désactiverez.",
|
||||
'settings.offline.mode.forceHint':
|
||||
"Travaillez uniquement à partir des données en cache et mettez vos modifications en file d'attente. Désactivez pour vous reconnecter et synchroniser.",
|
||||
'settings.offline.mode.active':
|
||||
"Le mode hors ligne est activé — vos modifications sont en file d'attente et seront synchronisées lorsque vous le désactiverez.",
|
||||
'settings.offline.prepare.title': 'Préparer le mode hors ligne',
|
||||
'settings.offline.prepare.hint': 'Téléchargez tout ce dont vous avez besoin — données de voyage, documents et tuiles de carte — avant de perdre la connexion.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Téléchargez tout ce dont vous avez besoin — données de voyage, documents et tuiles de carte — avant de perdre la connexion.',
|
||||
'settings.offline.prepare.button': 'Télécharger pour une utilisation hors ligne',
|
||||
'settings.offline.prepare.running': 'Téléchargement…',
|
||||
'settings.offline.prepare.done': 'Prêt pour une utilisation hors ligne',
|
||||
@@ -433,12 +442,14 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Synchronisation…',
|
||||
'settings.offline.storage.title': 'Que stocker hors ligne',
|
||||
'settings.offline.storage.tiles': 'Stocker les tuiles de carte hors ligne',
|
||||
'settings.offline.storage.tilesHint': 'Les tuiles de carte utilisent le plus de stockage. Désactivez cette option pour ne conserver hors ligne que les données de voyage et les documents.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'Les tuiles de carte utilisent le plus de stockage. Désactivez cette option pour ne conserver hors ligne que les données de voyage et les documents.',
|
||||
'settings.offline.storage.tripsTitle': 'Voyages',
|
||||
'settings.offline.storage.tripOn': 'Stocké hors ligne',
|
||||
'settings.offline.storage.tripOff': 'Non stocké',
|
||||
'settings.offline.clear': 'Vider le cache',
|
||||
'settings.offline.clearConfirm': 'Vider toutes les données de voyage hors ligne ? Vous pourrez resynchroniser à tout moment lorsque vous serez en ligne.',
|
||||
'settings.offline.clearConfirm':
|
||||
'Vider toutes les données de voyage hors ligne ? Vous pourrez resynchroniser à tout moment lorsque vous serez en ligne.',
|
||||
'settings.offline.stats.trips': 'Voyages en cache',
|
||||
'settings.offline.stats.pending': 'Modifications en attente',
|
||||
'settings.offline.stats.failed': 'Modifications échouées',
|
||||
@@ -446,7 +457,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': "Aucun voyage en cache pour l'instant. Connectez-vous à Internet pour synchroniser.",
|
||||
'settings.offline.loading': 'Chargement…',
|
||||
'settings.offline.conflicts.title': 'Conflits de synchronisation',
|
||||
'settings.offline.conflicts.hint': 'Les modifications que vous avez faites hors ligne entrent en conflit avec des modifications plus récentes sur le serveur. Choisissez la version à conserver.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'Les modifications que vous avez faites hors ligne entrent en conflit avec des modifications plus récentes sur le serveur. Choisissez la version à conserver.',
|
||||
'settings.offline.conflicts.keepMine': 'Conserver la mienne',
|
||||
'settings.offline.conflicts.keepServer': 'Conserver la leur',
|
||||
'settings.offline.conflicts.mine': 'Votre version',
|
||||
|
||||
+12
-10
@@ -27,9 +27,10 @@ const trip: TranslationStrings = {
|
||||
'trip.confirm.deletePlace': 'Voulez-vous vraiment supprimer ce lieu ?',
|
||||
'trip.confirm.deletePlaces': 'Supprimer {count} lieux?',
|
||||
'trip.toast.placesDeleted': '{count} lieux supprimés',
|
||||
'trip.invite.linkTitle': 'Lien d\'invitation au voyage',
|
||||
'trip.invite.linkHint': 'Toute personne disposant d\'un compte TREK qui ouvre ce lien rejoint le voyage en tant que membre. Régénérez le lien pour invalider l\'ancien.',
|
||||
'trip.invite.create': 'Créer un lien d\'invitation',
|
||||
'trip.invite.linkTitle': "Lien d'invitation au voyage",
|
||||
'trip.invite.linkHint':
|
||||
"Toute personne disposant d'un compte TREK qui ouvre ce lien rejoint le voyage en tant que membre. Régénérez le lien pour invalider l'ancien.",
|
||||
'trip.invite.create': "Créer un lien d'invitation",
|
||||
'trip.invite.regenerate': 'Régénérer',
|
||||
'trip.invite.disable': 'Désactiver',
|
||||
'trip.invite.joinHeading': 'Rejoindre ce voyage',
|
||||
@@ -37,7 +38,7 @@ const trip: TranslationStrings = {
|
||||
'trip.invite.joinCta': 'Rejoindre le voyage',
|
||||
'trip.invite.joining': 'Ajout en cours…',
|
||||
'trip.invite.invalidTitle': 'Invitation indisponible',
|
||||
'trip.invite.invalid': 'Ce lien d\'invitation est invalide ou a expiré.',
|
||||
'trip.invite.invalid': "Ce lien d'invitation est invalide ou a expiré.",
|
||||
'trip.invite.backToDashboard': 'Retour au tableau de bord',
|
||||
'transit.title': 'Transports en commun',
|
||||
'transit.from': 'Départ',
|
||||
@@ -57,25 +58,26 @@ const trip: TranslationStrings = {
|
||||
'transit.pref.walking': 'Moins de marche',
|
||||
'transit.search': 'Rechercher',
|
||||
'transit.searching': 'Recherche…',
|
||||
'transit.searchError': 'Échec de la recherche d\'itinéraire. Veuillez réessayer.',
|
||||
'transit.noResults': 'Aucune connexion trouvée. Essayez un autre horaire ou d\'autres filtres.',
|
||||
'transit.searchError': "Échec de la recherche d'itinéraire. Veuillez réessayer.",
|
||||
'transit.noResults': "Aucune connexion trouvée. Essayez un autre horaire ou d'autres filtres.",
|
||||
'transit.direct': 'Direct',
|
||||
'transit.transfers': '{count} correspondances',
|
||||
'transit.min': '{count} min',
|
||||
'transit.stops': '{count} arrêts',
|
||||
'transit.walkTo': 'Marcher jusqu\'à {name}',
|
||||
'transit.walkTo': "Marcher jusqu'à {name}",
|
||||
'transit.platform': 'Voie {track}',
|
||||
'transit.adding': 'Ajout…',
|
||||
'transit.addToDay': 'Ajouter au jour',
|
||||
'transit.itinerary': 'Itinéraire',
|
||||
'transit.attribution': 'Données d\'itinéraire via',
|
||||
'transit.attribution': "Données d'itinéraire via",
|
||||
'transport.modeManual': 'Transport manuel',
|
||||
'transport.modeAutomated': 'Transport automatisé',
|
||||
'transit.sectionTitle': 'Transports en commun automatisés',
|
||||
'transit.changeRoute': 'Modifier l\'itinéraire',
|
||||
'transit.changeRoute': "Modifier l'itinéraire",
|
||||
'transit.journey': 'Trajet en transports en commun',
|
||||
'transit.pickDay': 'Choisissez un jour pour rechercher des connexions.',
|
||||
'transit.noItinerary': 'Cette entrée n\'a aucun itinéraire enregistré — utilisez « Modifier l\'itinéraire » pour en rechercher un.',
|
||||
'transit.noItinerary':
|
||||
"Cette entrée n'a aucun itinéraire enregistré — utilisez « Modifier l'itinéraire » pour en rechercher un.",
|
||||
'transit.durationLabel': 'Durée',
|
||||
'transit.transfersLabel': 'Correspondances',
|
||||
'transit.walkLabel': 'Marche',
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'Itinéraire optimisé',
|
||||
'undo.deletePlace': 'Lieu supprimé',
|
||||
'undo.deletePlaces': 'Lieux supprimés',
|
||||
'undo.changeCategory': "Category changed",
|
||||
'undo.changeCategory': 'Category changed',
|
||||
'undo.moveDay': 'Lieu déplacé vers un autre jour',
|
||||
'undo.lock': 'Verrouillage du lieu modifié',
|
||||
'undo.importGpx': 'Import GPX',
|
||||
|
||||
@@ -141,6 +141,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Συνιστάται',
|
||||
'admin.weatherKey': 'OpenWeatherMap API Key',
|
||||
'admin.weatherKeyHint': 'Για δεδομένα καιρού. Δωρεάν στο openweathermap.org',
|
||||
'admin.unsplashKey': 'Κλειδί API Unsplash',
|
||||
'admin.unsplashKeyHint': 'Για αναζήτηση εικόνων. Δωρεάν στο unsplash.com/developers',
|
||||
'admin.validateKey': 'Δοκιμή',
|
||||
'admin.keyValid': 'Συνδέθηκε',
|
||||
'admin.keyInvalid': 'Μη έγκυρο',
|
||||
@@ -208,6 +210,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Plugins',
|
||||
'admin.plugins.rescan': 'Επανασάρωση',
|
||||
'admin.plugins.rescanned': 'Ο φάκελος plugins σαρώθηκε ξανά',
|
||||
'admin.plugins.upload': 'Μεταφόρτωση plugin',
|
||||
'admin.plugins.dropToUpload': 'Αποθέστε ένα .zip plugin για εγκατάσταση',
|
||||
'admin.plugins.uploaded': 'Το plugin “{name}” μεταφορτώθηκε — ενεργοποιήστε το για εκτέλεση',
|
||||
'admin.plugins.sideloaded': 'Φορτώθηκε χειροκίνητα',
|
||||
'admin.plugins.sideloadedHint': 'Μεταφορτώθηκε χειροκίνητα — όχι από το μητρώο, χωρίς υπογραφή και έλεγχο',
|
||||
'admin.plugins.browse': 'Περιήγηση σε plugins',
|
||||
'admin.plugins.installed': 'Εγκατεστημένο',
|
||||
'admin.plugins.install': 'Εγκατάσταση',
|
||||
@@ -222,11 +229,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'Δεν έχουν καταγραφεί σφάλματα.',
|
||||
'admin.plugins.uninstalled': 'Το plugin απεγκαταστάθηκε',
|
||||
'admin.plugins.uninstallTitle': 'Απεγκατάσταση plugin;',
|
||||
'admin.plugins.uninstallBody': 'Αυτό σταματά το plugin, αφαιρεί τον κώδικά του και διαγράφει όλα τα δεδομένα του. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'Αυτό σταματά το plugin, αφαιρεί τον κώδικά του και διαγράφει όλα τα δεδομένα του. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.',
|
||||
'admin.plugins.status.starting': 'Εκκίνηση…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Σελίδα',
|
||||
'admin.plugins.type.integration': 'Ενσωμάτωση',
|
||||
'admin.plugins.type.trip-page': 'Σελίδα ταξιδιού',
|
||||
'admin.plugins.reviewed': 'Ελεγμένο',
|
||||
'admin.plugins.unreviewed': 'Μη ελεγμένο',
|
||||
'admin.plugins.updated': 'Το plugin ενημερώθηκε',
|
||||
@@ -236,10 +245,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Plugins',
|
||||
'admin.plugins.subtitle': 'Εγκαταστήστε και διαχειριστείτε plugins τρίτων στην εγκατάστασή σας.',
|
||||
'admin.plugins.disabledTitle': 'Τα plugins είναι απενεργοποιημένα',
|
||||
'admin.plugins.disabledBody': 'Το περιβάλλον εκτέλεσης plugins είναι απενεργοποιημένο (TREK_PLUGINS_ENABLED). Κανένα plugin δεν μπορεί να εκτελεστεί μέχρι ένας διαχειριστής να το ενεργοποιήσει στη διαμόρφωση του server.',
|
||||
'admin.plugins.disabledBody':
|
||||
'Το περιβάλλον εκτέλεσης plugins είναι απενεργοποιημένο (TREK_PLUGINS_ENABLED). Κανένα plugin δεν μπορεί να εκτελεστεί μέχρι ένας διαχειριστής να το ενεργοποιήσει στη διαμόρφωση του server.',
|
||||
'admin.plugins.empty': 'Δεν έχουν εγκατασταθεί plugins ακόμη.',
|
||||
'admin.plugins.loadError': 'Δεν ήταν δυνατή η φόρτωση των plugins.',
|
||||
'admin.plugins.scaffoldNote': 'Η εγκατάσταση και ενεργοποίηση plugins έρχονται σε μελλοντική ενημέρωση. Αυτός ο πίνακας εμφανίζει προς το παρόν μόνο τα εγκατεστημένα plugins.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'Η εγκατάσταση και ενεργοποίηση plugins έρχονται σε μελλοντική ενημέρωση. Αυτός ο πίνακας εμφανίζει προς το παρόν μόνο τα εγκατεστημένα plugins.',
|
||||
'admin.plugins.status.active': 'Ενεργό',
|
||||
'admin.plugins.status.inactive': 'Ανενεργό',
|
||||
'admin.plugins.status.disabled': 'Απενεργοποιημένο',
|
||||
@@ -262,10 +273,32 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Αποθήκευση των δικών του δεδομένων σε απομονωμένη βάση δεδομένων',
|
||||
'admin.plugins.perm.db:read:trips': 'Ανάγνωση ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:read:users': 'Ανάγνωση βασικών στοιχείων προφίλ (όνομα, avatar — ποτέ διαπιστευτήρια)',
|
||||
'admin.plugins.perm.db:read:costs':
|
||||
'Ανάγνωση εξόδων (στοιχεία προϋπολογισμού) στα οποία έχει πρόσβαση ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:read:packing': 'Ανάγνωση λιστών αποσκευών ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:read:files': 'Ανάγνωση αρχείων ταξιδιών στα οποία έχει πρόσβαση ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:write:costs':
|
||||
'Δημιουργία εξόδων (στοιχεία προϋπολογισμού) σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:write:places':
|
||||
'Προσθήκη, επεξεργασία και αφαίρεση τοποθεσιών σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:write:days':
|
||||
'Προσθήκη, επεξεργασία και αφαίρεση ημερών σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:write:itinerary':
|
||||
'Ανάθεση και αφαίρεση τοποθεσιών σε ημέρες ταξιδιών που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:write:trips':
|
||||
'Επεξεργασία στοιχείων ταξιδιού (τίτλος, ημερομηνίες, νόμισμα…) σε ταξίδια που μπορεί να επεξεργαστεί ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.db:meta':
|
||||
'Επισύναψη των δικών του ιδιωτικών δεδομένων σε ταξίδια, τοποθεσίες και ημέρες στις οποίες έχει πρόσβαση ο ενεργός χρήστης',
|
||||
'admin.plugins.perm.ws:broadcast:trip': 'Αποστολή ενημερώσεων σε πραγματικό χρόνο στα μέλη του ταξιδιού',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'Αποστολή ενημερώσεων σε πραγματικό χρόνο σε μεμονωμένους χρήστες',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Παροχή φωτογραφιών στις Αναμνήσεις',
|
||||
'admin.plugins.perm.hook:calendar-source': 'Παροχή συμβάντων στο ημερολόγιο',
|
||||
'admin.plugins.perm.hook:place-detail-provider':
|
||||
'Συνεισφορά επιπλέον λεπτομερειών (κριτικές, βαθμολογίες, σύνδεσμοι) σε ένα μέρος',
|
||||
'admin.plugins.perm.hook:trip-warning-provider':
|
||||
'Εμφάνιση προειδοποιήσεων επικύρωσης σε ένα ταξίδι (εμφανίζονται στον σχεδιαστή)',
|
||||
'admin.plugins.perm.events:subscribe':
|
||||
'Αντιδρά σε βασικά συμβάντα δραστηριότητας (όνομα συμβάντος + ταξίδι μόνο, ποτέ το περιεχόμενο)',
|
||||
'admin.plugins.perm.http:outbound': 'Εκτέλεση εξερχόμενων αιτημάτων προς τους δηλωμένους hosts του',
|
||||
'admin.plugins.updateConsentTitle': 'Αυτή η ενημέρωση χρειάζεται νέα δικαιώματα',
|
||||
'admin.plugins.updateConsentBody':
|
||||
@@ -317,11 +350,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Το plugin επανεκκινήθηκε',
|
||||
'admin.plugins.cap.readsTrips': 'Διαβάζει τα ταξίδια σας',
|
||||
'admin.plugins.cap.readsUsers': 'Διαβάζει βασικά προφίλ',
|
||||
'admin.plugins.cap.readsCosts': 'Διαβάζει τα έξοδά σας',
|
||||
'admin.plugins.cap.readsPacking': 'Διαβάζει λίστες αποσκευών',
|
||||
'admin.plugins.cap.readsFiles': 'Διαβάζει αρχεία ταξιδιού',
|
||||
'admin.plugins.cap.writesCosts': 'Προσθέτει έξοδα',
|
||||
'admin.plugins.cap.writesPlaces': 'Επεξεργάζεται τοποθεσίες',
|
||||
'admin.plugins.cap.writesDays': 'Επεξεργάζεται ημέρες',
|
||||
'admin.plugins.cap.writesItinerary': 'Επεξεργάζεται δρομολόγιο',
|
||||
'admin.plugins.cap.writesTrips': 'Επεξεργάζεται ταξίδια',
|
||||
'admin.plugins.cap.metadata': 'Προσθέτει μεταδεδομένα',
|
||||
'admin.plugins.cap.widget': 'Widget πίνακα',
|
||||
'admin.plugins.cap.heroWidget': 'Widget κάρτας επιβίβασης',
|
||||
'admin.plugins.cap.placeSlot': 'Λεπτομέρειες τόπου',
|
||||
'admin.plugins.cap.realtime': 'Ενημερώσεις σε πραγματικό χρόνο',
|
||||
'admin.plugins.cap.photos': 'Παρέχει φωτογραφίες',
|
||||
'admin.plugins.cap.calendar': 'Παρέχει συμβάντα ημερολογίου',
|
||||
'admin.plugins.cap.placeDetails': 'Εμπλουτίζει μέρη',
|
||||
'admin.plugins.cap.warnings': 'Επισημαίνει ζητήματα',
|
||||
'admin.plugins.cap.events': 'Αντιδρά σε δραστηριότητα',
|
||||
'admin.plugins.cap.requiresAddon': 'Απαιτεί {addon}',
|
||||
'admin.plugins.cap.dependsOn': 'Χρειάζεται {id} {version}',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Ενεργοποιήστε πρώτα τα απαιτούμενα πρόσθετα: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Ενεργοποιήθηκαν πρώτα τα απαιτούμενα πρόσθετα: {plugins}',
|
||||
'admin.plugins.dep.downloaded': 'Λήφθηκε το {id}',
|
||||
'admin.plugins.dep.resolveTitle': 'Λείπουν εξαρτήσεις',
|
||||
'admin.plugins.dep.resolveBody': 'Το «{name}» χρειάζεται να εγκατασταθούν αυτά τα πρόσθετα πριν ενεργοποιηθεί.',
|
||||
'admin.plugins.dep.requires': 'Απαιτεί {version}',
|
||||
'admin.plugins.dep.mismatch': 'Χρειάζεται {wanted} — είναι εγκατεστημένο το {installed}',
|
||||
'admin.plugins.dep.download': 'Λήψη',
|
||||
'admin.plugins.dep.update': 'Ενημέρωση',
|
||||
'admin.plugins.dep.resolveHint': 'Λαμβάνει την πιο πρόσφατη συμβατή έκδοση, μαζί με τις δικές της εξαρτήσεις.',
|
||||
'admin.plugins.accessTitle': 'Σε τι έχει πρόσβαση',
|
||||
'admin.plugins.connectsTitle': 'Συνδέεται σε',
|
||||
'admin.plugins.detailsTitle': 'Λεπτομέρειες',
|
||||
|
||||
@@ -53,6 +53,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'Δεν σας χρωστάει κανείς',
|
||||
'costs.yourShare': 'Το μερίδιό σας',
|
||||
'costs.youPaid': 'Πληρώσατε',
|
||||
'costs.outstanding': 'Εκκρεμές ποσό',
|
||||
'costs.outstandingSub': 'Έξοδα χωρίς πληρωτή ακόμη',
|
||||
'costs.outstandingItems': 'έξοδα χρειάζονται πληρωτή',
|
||||
'costs.expenses': 'Έξοδα',
|
||||
'costs.entries': '{count} εγγραφές',
|
||||
'costs.searchPlaceholder': 'Αναζήτηση εξόδων…',
|
||||
@@ -117,10 +120,10 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'Μόνο στο σύνολο — δεν έχει διακανονιστεί',
|
||||
'costs.tapToInclude': 'Πατήστε για προσθήκη',
|
||||
'costs.amount': 'Ποσό',
|
||||
'costs.split': "Split",
|
||||
'costs.splitEqually': "Equally",
|
||||
'costs.splitCustom': "Custom",
|
||||
'costs.splitTicket': "Ticket",
|
||||
'costs.split': 'Split',
|
||||
'costs.splitEqually': 'Equally',
|
||||
'costs.splitCustom': 'Custom',
|
||||
'costs.splitTicket': 'Ticket',
|
||||
'costs.filter.allCategories': 'Όλες οι κατηγορίες',
|
||||
'costs.filter.allDays': 'Όλες οι ημέρες',
|
||||
'costs.expensesCount': '{count} έξοδα',
|
||||
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'Μετονομασία',
|
||||
'collections.listMenu': 'Επιλογές λίστας',
|
||||
'collections.deleteList': 'Διαγραφή λίστας',
|
||||
'collections.deleteListConfirm': 'Αυτό διαγράφει οριστικά τη λίστα και όλα τα αποθηκευμένα μέρη της. Δεν είναι δυνατή η αναίρεση.',
|
||||
'collections.deleteListConfirm':
|
||||
'Αυτό διαγράφει οριστικά τη λίστα και όλα τα αποθηκευμένα μέρη της. Δεν είναι δυνατή η αναίρεση.',
|
||||
'collections.listName': 'Όνομα λίστας',
|
||||
'collections.listNamePlaceholder': 'π.χ. Τόκιο 2025',
|
||||
'collections.listColor': 'Χρώμα',
|
||||
@@ -127,7 +128,8 @@ const collection: TranslationStrings = {
|
||||
'collections.share.memberHint': 'Μόνο ο ιδιοκτήτης της λίστας μπορεί να προσκαλεί ή να αφαιρεί άτομα.',
|
||||
'collections.share.cancel': 'Άκυρο',
|
||||
'collections.share.leave': 'Αποχώρηση από τη λίστα',
|
||||
'collections.share.leaveConfirm': 'Αποχώρηση από αυτή την κοινόχρηστη λίστα; Θα χάσεις την πρόσβαση μέχρι να προσκληθείς ξανά.',
|
||||
'collections.share.leaveConfirm':
|
||||
'Αποχώρηση από αυτή την κοινόχρηστη λίστα; Θα χάσεις την πρόσβαση μέχρι να προσκληθείς ξανά.',
|
||||
'collections.share.left': 'Αποχώρησες από τη λίστα',
|
||||
'collections.share.remove': 'Αφαίρεση',
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { TranslationStrings } from '../types';
|
||||
|
||||
// English fallback until 'gr' is translated.
|
||||
const help: TranslationStrings = {
|
||||
'help.title': "Help & Docs",
|
||||
'help.search': "Search docs…",
|
||||
'help.contents': "Contents",
|
||||
'help.noResults': "No matching pages.",
|
||||
'help.title': 'Help & Docs',
|
||||
'help.search': 'Search docs…',
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody': "The help content is fetched from the TREK wiki. Check your connection and try again.",
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -89,7 +89,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'Σύνδεση με passkey',
|
||||
'login.passkey.failed': 'Η σύνδεση με passkey απέτυχε. Παρακαλώ δοκιμάστε ξανά.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -25,13 +25,15 @@ const members: TranslationStrings = {
|
||||
'members.transferError': 'Αποτυχία μεταβίβασης ιδιοκτησίας',
|
||||
'members.guests': 'Επισκέπτες',
|
||||
'members.guest': 'Επισκέπτης',
|
||||
'members.guestsHint': 'Άτομα χωρίς λογαριασμό. Μπορούν να ανατεθούν σε έξοδα, αποσκευές και εργασίες, αλλά δεν μπορούν να συνδεθούν.',
|
||||
'members.guestsHint':
|
||||
'Άτομα χωρίς λογαριασμό. Μπορούν να ανατεθούν σε έξοδα, αποσκευές και εργασίες, αλλά δεν μπορούν να συνδεθούν.',
|
||||
'members.addGuest': 'Προσθήκη επισκέπτη',
|
||||
'members.guestNamePlaceholder': 'Όνομα επισκέπτη',
|
||||
'members.guestAdded': 'Ο επισκέπτης προστέθηκε',
|
||||
'members.guestAddError': 'Αποτυχία προσθήκης επισκέπτη',
|
||||
'members.guestRenameError': 'Αποτυχία μετονομασίας επισκέπτη',
|
||||
'members.guestRemoved': 'Ο επισκέπτης αφαιρέθηκε',
|
||||
'members.confirmRemoveGuest': 'Αφαίρεση αυτού του επισκέπτη; Θα αφαιρεθούν επίσης οι αναθέσεις και τα μερίδια εξόδων του.',
|
||||
'members.confirmRemoveGuest':
|
||||
'Αφαίρεση αυτού του επισκέπτη; Θα αφαιρεθούν επίσης οι αναθέσεις και τα μερίδια εξόδων του.',
|
||||
};
|
||||
export default members;
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Επιλέξτε τουλάχιστον έναν τύπο για εισαγωγή.',
|
||||
'places.selectionCount': '{count} επιλεγμένα',
|
||||
'places.deleteSelected': 'Διαγραφή επιλεγμένων',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': '{count} μέρη εισήχθησαν από KMZ/KML',
|
||||
'places.urlResolved': 'Το μέρος εισήχθη από URL',
|
||||
'places.importList': 'Εισαγωγή Λίστας',
|
||||
|
||||
@@ -30,7 +30,8 @@ const settings: TranslationStrings = {
|
||||
'settings.mapStylePlaceholder': 'Επιλέξτε ένα στυλ Mapbox',
|
||||
'settings.mapStyleHint': 'Προκαθορισμένο ή δικό σας mapbox://styles/USER/ID URL',
|
||||
'settings.mapOpenFreeMapStylePlaceholder': 'Επιλέξτε ένα στυλ OpenFreeMap',
|
||||
'settings.mapOpenFreeMapStyleHint': 'Προκαθορισμένο ή URL στυλ OpenFreeMap. Τα στυλ OpenFreeMap λειτουργούν χωρίς token.',
|
||||
'settings.mapOpenFreeMapStyleHint':
|
||||
'Προκαθορισμένο ή URL στυλ OpenFreeMap. Τα στυλ OpenFreeMap λειτουργούν χωρίς token.',
|
||||
'settings.map3dBuildings': '3D Κτίρια & Ανάγλυφο',
|
||||
'settings.map3dHint':
|
||||
'Κλίση + πραγματικές 3D προεξοχές κτιρίων — λειτουργεί σε κάθε στυλ, συμπεριλαμβανομένου του δορυφορικού.',
|
||||
@@ -337,18 +338,21 @@ const settings: TranslationStrings = {
|
||||
'settings.airtrail.test.success': 'Συνδέθηκε — βρέθηκαν {count} πτήση/πτήσεις',
|
||||
'settings.airtrail.test.failed': 'Η σύνδεση απέτυχε',
|
||||
'settings.aiParsing.title': 'Ανάλυση με AI',
|
||||
'settings.aiParsing.hint': 'Χρησιμοποιήστε το δικό σας μοντέλο AI για την εξαγωγή κρατήσεων από τα αρχεία που ανεβάζετε. Ισχύει μόνο όταν ο διαχειριστής σας δεν έχει ρυθμίσει μοντέλο για ολόκληρη την εγκατάσταση.',
|
||||
'settings.aiParsing.hint':
|
||||
'Χρησιμοποιήστε το δικό σας μοντέλο AI για την εξαγωγή κρατήσεων από τα αρχεία που ανεβάζετε. Ισχύει μόνο όταν ο διαχειριστής σας δεν έχει ρυθμίσει μοντέλο για ολόκληρη την εγκατάσταση.',
|
||||
'settings.aiParsing.provider': 'Πάροχος',
|
||||
'settings.aiParsing.providerLocal': 'Τοπικό (Ollama)',
|
||||
'settings.aiParsing.providerOpenai': 'OpenAI',
|
||||
'settings.aiParsing.providerAnthropic': 'Anthropic',
|
||||
'settings.aiParsing.model': 'Μοντέλο',
|
||||
'settings.aiParsing.baseUrl': 'Βασικό URL',
|
||||
'settings.aiParsing.baseUrlHint': 'Πού εκτελείται το μοντέλο — ένας τοπικός διακομιστής Ollama ή ένα τελικό σημείο συμβατό με OpenAI.',
|
||||
'settings.aiParsing.baseUrlHint':
|
||||
'Πού εκτελείται το μοντέλο — ένας τοπικός διακομιστής Ollama ή ένα τελικό σημείο συμβατό με OpenAI.',
|
||||
'settings.aiParsing.apiKey': 'Κλειδί API',
|
||||
'settings.aiParsing.apiKeyHint': 'Αποθηκεύεται κρυπτογραφημένο. Αφήστε το κενό για να διατηρήσετε το τρέχον κλειδί.',
|
||||
'settings.aiParsing.multimodal': 'Αποστολή εγγράφων ως εικόνες',
|
||||
'settings.aiParsing.multimodalHint': 'Για μοντέλα με δυνατότητα όρασης — στέλνει το αρχικό PDF αντί για το εξαγόμενο κείμενο.',
|
||||
'settings.aiParsing.multimodalHint':
|
||||
'Για μοντέλα με δυνατότητα όρασης — στέλνει το αρχικό PDF αντί για το εξαγόμενο κείμενο.',
|
||||
'settings.aiParsing.toast.saved': 'Οι ρυθμίσεις AI αποθηκεύτηκαν',
|
||||
'settings.aiParsing.toast.saveError': 'Δεν ήταν δυνατή η αποθήκευση των ρυθμίσεων AI',
|
||||
'settings.tabs.appearance': 'Appearance',
|
||||
@@ -367,7 +371,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -397,7 +402,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -420,10 +426,13 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Προσωρινή μνήμη εκτός σύνδεσης',
|
||||
'settings.offline.mode.title': 'Λειτουργία εκτός σύνδεσης',
|
||||
'settings.offline.mode.force': 'Εξαναγκασμός λειτουργίας εκτός σύνδεσης',
|
||||
'settings.offline.mode.forceHint': 'Εργαστείτε μόνο με τα αποθηκευμένα δεδομένα και τοποθετήστε τις αλλαγές σας σε ουρά. Απενεργοποιήστε το για επανασύνδεση και συγχρονισμό.',
|
||||
'settings.offline.mode.active': 'Η λειτουργία εκτός σύνδεσης είναι ενεργή — οι αλλαγές σας βρίσκονται σε ουρά και θα συγχρονιστούν όταν την απενεργοποιήσετε.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'Εργαστείτε μόνο με τα αποθηκευμένα δεδομένα και τοποθετήστε τις αλλαγές σας σε ουρά. Απενεργοποιήστε το για επανασύνδεση και συγχρονισμό.',
|
||||
'settings.offline.mode.active':
|
||||
'Η λειτουργία εκτός σύνδεσης είναι ενεργή — οι αλλαγές σας βρίσκονται σε ουρά και θα συγχρονιστούν όταν την απενεργοποιήσετε.',
|
||||
'settings.offline.prepare.title': 'Προετοιμασία για εκτός σύνδεσης',
|
||||
'settings.offline.prepare.hint': 'Κατεβάστε όλα όσα χρειάζεστε — δεδομένα ταξιδιού, έγγραφα και πλακίδια χάρτη — πριν χάσετε τη σύνδεση.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Κατεβάστε όλα όσα χρειάζεστε — δεδομένα ταξιδιού, έγγραφα και πλακίδια χάρτη — πριν χάσετε τη σύνδεση.',
|
||||
'settings.offline.prepare.button': 'Λήψη για χρήση εκτός σύνδεσης',
|
||||
'settings.offline.prepare.running': 'Λήψη…',
|
||||
'settings.offline.prepare.done': 'Έτοιμο για χρήση εκτός σύνδεσης',
|
||||
@@ -434,12 +443,14 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Συγχρονισμός…',
|
||||
'settings.offline.storage.title': 'Τι να αποθηκεύεται εκτός σύνδεσης',
|
||||
'settings.offline.storage.tiles': 'Αποθήκευση πλακιδίων χάρτη εκτός σύνδεσης',
|
||||
'settings.offline.storage.tilesHint': 'Τα πλακίδια χάρτη καταναλώνουν τον περισσότερο χώρο αποθήκευσης. Απενεργοποιήστε το για να διατηρείτε εκτός σύνδεσης μόνο δεδομένα ταξιδιού και έγγραφα.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'Τα πλακίδια χάρτη καταναλώνουν τον περισσότερο χώρο αποθήκευσης. Απενεργοποιήστε το για να διατηρείτε εκτός σύνδεσης μόνο δεδομένα ταξιδιού και έγγραφα.',
|
||||
'settings.offline.storage.tripsTitle': 'Ταξίδια',
|
||||
'settings.offline.storage.tripOn': 'Αποθηκευμένο εκτός σύνδεσης',
|
||||
'settings.offline.storage.tripOff': 'Μη αποθηκευμένο',
|
||||
'settings.offline.clear': 'Εκκαθάριση προσωρινής μνήμης',
|
||||
'settings.offline.clearConfirm': 'Εκκαθάριση όλων των δεδομένων ταξιδιού εκτός σύνδεσης; Μπορείτε να επανασυγχρονίσετε ανά πάσα στιγμή όσο είστε συνδεδεμένοι.',
|
||||
'settings.offline.clearConfirm':
|
||||
'Εκκαθάριση όλων των δεδομένων ταξιδιού εκτός σύνδεσης; Μπορείτε να επανασυγχρονίσετε ανά πάσα στιγμή όσο είστε συνδεδεμένοι.',
|
||||
'settings.offline.stats.trips': 'Ταξίδια σε προσωρινή μνήμη',
|
||||
'settings.offline.stats.pending': 'Εκκρεμείς αλλαγές',
|
||||
'settings.offline.stats.failed': 'Αποτυχημένες αλλαγές',
|
||||
@@ -447,7 +458,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': 'Δεν υπάρχουν ταξίδια σε προσωρινή μνήμη ακόμη. Συνδεθείτε στο διαδίκτυο για συγχρονισμό.',
|
||||
'settings.offline.loading': 'Φόρτωση…',
|
||||
'settings.offline.conflicts.title': 'Διενέξεις συγχρονισμού',
|
||||
'settings.offline.conflicts.hint': 'Οι αλλαγές που κάνατε εκτός σύνδεσης συγκρούονται με νεότερες αλλαγές στον διακομιστή. Επιλέξτε ποια έκδοση να διατηρήσετε.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'Οι αλλαγές που κάνατε εκτός σύνδεσης συγκρούονται με νεότερες αλλαγές στον διακομιστή. Επιλέξτε ποια έκδοση να διατηρήσετε.',
|
||||
'settings.offline.conflicts.keepMine': 'Διατήρηση της δικής μου',
|
||||
'settings.offline.conflicts.keepServer': 'Διατήρηση του διακομιστή',
|
||||
'settings.offline.conflicts.mine': 'Η έκδοσή σας',
|
||||
|
||||
@@ -28,7 +28,8 @@ const trip: TranslationStrings = {
|
||||
'trip.confirm.deletePlaces': 'Διαγραφή {count} μερών;',
|
||||
'trip.toast.placesDeleted': '{count} μέρη διαγράφηκαν',
|
||||
'trip.invite.linkTitle': 'Σύνδεσμος πρόσκλησης ταξιδιού',
|
||||
'trip.invite.linkHint': 'Όποιος έχει λογαριασμό TREK και ανοίξει αυτόν τον σύνδεσμο, εντάσσεται στο ταξίδι ως μέλος. Δημιουργήστε ξανά τον σύνδεσμο για να ακυρώσετε τον παλιό.',
|
||||
'trip.invite.linkHint':
|
||||
'Όποιος έχει λογαριασμό TREK και ανοίξει αυτόν τον σύνδεσμο, εντάσσεται στο ταξίδι ως μέλος. Δημιουργήστε ξανά τον σύνδεσμο για να ακυρώσετε τον παλιό.',
|
||||
'trip.invite.create': 'Δημιουργία συνδέσμου πρόσκλησης',
|
||||
'trip.invite.regenerate': 'Δημιουργία ξανά',
|
||||
'trip.invite.disable': 'Απενεργοποίηση',
|
||||
@@ -75,10 +76,12 @@ const trip: TranslationStrings = {
|
||||
'transit.changeRoute': 'Αλλαγή διαδρομής',
|
||||
'transit.journey': 'Διαδρομή με δημόσιες συγκοινωνίες',
|
||||
'transit.pickDay': 'Επιλέξτε μια ημέρα για αναζήτηση συνδέσεων.',
|
||||
'transit.noItinerary': 'Αυτή η καταχώριση δεν έχει αποθηκευμένο δρομολόγιο — χρησιμοποιήστε την «Αλλαγή διαδρομής» για να αναζητήσετε ένα.',
|
||||
'transit.noItinerary':
|
||||
'Αυτή η καταχώριση δεν έχει αποθηκευμένο δρομολόγιο — χρησιμοποιήστε την «Αλλαγή διαδρομής» για να αναζητήσετε ένα.',
|
||||
'transit.durationLabel': 'Διάρκεια',
|
||||
'transit.transfersLabel': 'Μετεπιβιβάσεις',
|
||||
'transit.walkLabel': 'Περπάτημα',
|
||||
'transit.searchHint': 'Αναζητήστε πραγματικές συνδέσεις και προσθέστε τις απευθείας στην ημέρα — δεδομένα μέσω Transitous.',
|
||||
'transit.searchHint':
|
||||
'Αναζητήστε πραγματικές συνδέσεις και προσθέστε τις απευθείας στην ημέρα — δεδομένα μέσω Transitous.',
|
||||
};
|
||||
export default trip;
|
||||
|
||||
@@ -9,7 +9,7 @@ const undo: TranslationStrings = {
|
||||
'undo.optimize': 'Η διαδρομή βελτιστοποιήθηκε',
|
||||
'undo.deletePlace': 'Η τοποθεσία διαγράφηκε',
|
||||
'undo.deletePlaces': 'Οι τοποθεσίες διαγράφηκαν',
|
||||
'undo.changeCategory': "Category changed",
|
||||
'undo.changeCategory': 'Category changed',
|
||||
'undo.moveDay': 'Η τοποθεσία μεταφέρθηκε σε άλλη ημέρα',
|
||||
'undo.lock': 'Το κλείδωμα τοποθεσίας εναλλάχθηκε',
|
||||
'undo.importGpx': 'Εισαγωγή GPX',
|
||||
|
||||
@@ -95,6 +95,8 @@ const admin: TranslationStrings = {
|
||||
'admin.recommended': 'Ajánlott',
|
||||
'admin.weatherKey': 'OpenWeatherMap API kulcs',
|
||||
'admin.weatherKeyHint': 'Időjárás adatokhoz. Ingyenes: openweathermap.org',
|
||||
'admin.unsplashKey': 'Unsplash API-kulcs',
|
||||
'admin.unsplashKeyHint': 'Képkereséshez. Ingyenes: unsplash.com/developers',
|
||||
'admin.validateKey': 'Teszt',
|
||||
'admin.keyValid': 'Csatlakozva',
|
||||
'admin.keyInvalid': 'Érvénytelen',
|
||||
@@ -162,6 +164,11 @@ const admin: TranslationStrings = {
|
||||
'admin.tabs.plugins': 'Pluginok',
|
||||
'admin.plugins.rescan': 'Újraszkennelés',
|
||||
'admin.plugins.rescanned': 'Plugin mappa újraszkennelve',
|
||||
'admin.plugins.upload': 'Plugin feltöltése',
|
||||
'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.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',
|
||||
'admin.plugins.install': 'Telepítés',
|
||||
@@ -176,11 +183,13 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.noErrors': 'Nincsenek naplózott hibák.',
|
||||
'admin.plugins.uninstalled': 'Plugin eltávolítva',
|
||||
'admin.plugins.uninstallTitle': 'Plugin eltávolítása?',
|
||||
'admin.plugins.uninstallBody': 'Ez leállítja a plugint, eltávolítja a kódját, és törli az összes adatát. Ez nem vonható vissza.',
|
||||
'admin.plugins.uninstallBody':
|
||||
'Ez leállítja a plugint, eltávolítja a kódját, és törli az összes adatát. Ez nem vonható vissza.',
|
||||
'admin.plugins.status.starting': 'Indítás…',
|
||||
'admin.plugins.type.widget': 'Widget',
|
||||
'admin.plugins.type.page': 'Oldal',
|
||||
'admin.plugins.type.integration': 'Integráció',
|
||||
'admin.plugins.type.trip-page': 'Útvonal-oldal',
|
||||
'admin.plugins.reviewed': 'Ellenőrizve',
|
||||
'admin.plugins.unreviewed': 'Nem ellenőrzött',
|
||||
'admin.plugins.updated': 'Plugin frissítve',
|
||||
@@ -190,10 +199,12 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.title': 'Pluginok',
|
||||
'admin.plugins.subtitle': 'Harmadik féltől származó pluginok telepítése és kezelése a példányodon.',
|
||||
'admin.plugins.disabledTitle': 'A pluginok le vannak tiltva',
|
||||
'admin.plugins.disabledBody': 'A plugin futtatókörnyezet ki van kapcsolva (TREK_PLUGINS_ENABLED). Egyetlen plugin sem futhat, amíg egy admin nem engedélyezi a szerverkonfigurációban.',
|
||||
'admin.plugins.disabledBody':
|
||||
'A plugin futtatókörnyezet ki van kapcsolva (TREK_PLUGINS_ENABLED). Egyetlen plugin sem futhat, amíg egy admin nem engedélyezi a szerverkonfigurációban.',
|
||||
'admin.plugins.empty': 'Még nincsenek telepített pluginok.',
|
||||
'admin.plugins.loadError': 'Nem sikerült betölteni a pluginokat.',
|
||||
'admin.plugins.scaffoldNote': 'A pluginok telepítése és aktiválása egy későbbi frissítésben érkezik. Ez a panel jelenleg csak a telepített pluginokat listázza.',
|
||||
'admin.plugins.scaffoldNote':
|
||||
'A pluginok telepítése és aktiválása egy későbbi frissítésben érkezik. Ez a panel jelenleg csak a telepített pluginokat listázza.',
|
||||
'admin.plugins.status.active': 'Aktív',
|
||||
'admin.plugins.status.inactive': 'Inaktív',
|
||||
'admin.plugins.status.disabled': 'Letiltva',
|
||||
@@ -216,10 +227,32 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.perm.db:own': 'Saját adatok tárolása elkülönített adatbázisban',
|
||||
'admin.plugins.perm.db:read:trips': 'Az aktuális felhasználó számára elérhető utazások olvasása',
|
||||
'admin.plugins.perm.db:read:users': 'Alapvető profiladatok olvasása (név, profilkép — hitelesítő adatok soha)',
|
||||
'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:read:files': 'Az aktuális felhasználó számára elérhető utazások fájljainak olvasása',
|
||||
'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':
|
||||
'Helyek hozzáadása, szerkesztése és eltávolítása az aktuális felhasználó által szerkeszthető utazásokon',
|
||||
'admin.plugins.perm.db:write:days':
|
||||
'Napok hozzáadása, szerkesztése és eltávolítása az aktuális felhasználó által szerkeszthető utazásokon',
|
||||
'admin.plugins.perm.db:write:itinerary':
|
||||
'Helyek hozzárendelése és eltávolítása az aktuális felhasználó által szerkeszthető utazások napjain',
|
||||
'admin.plugins.perm.db:write:trips':
|
||||
'Utazás adatainak szerkesztése (cím, dátumok, pénznem…) az aktuális felhasználó által szerkeszthető utazásokon',
|
||||
'admin.plugins.perm.db:meta':
|
||||
'Saját privát adatok csatolása az aktuális felhasználó által elérhető utazásokhoz, helyekhez és napokhoz',
|
||||
'admin.plugins.perm.ws:broadcast:trip': 'Valós idejű frissítések küldése az utazás tagjainak',
|
||||
'admin.plugins.perm.ws:broadcast:user': 'Valós idejű frissítések küldése egyes felhasználóknak',
|
||||
'admin.plugins.perm.hook:photo-provider': 'Fotók biztosítása az Emlékek funkcióhoz',
|
||||
'admin.plugins.perm.hook:calendar-source': 'Események biztosítása a naptárhoz',
|
||||
'admin.plugins.perm.hook:place-detail-provider':
|
||||
'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.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.updateConsentTitle': 'Ez a frissítés új engedélyeket igényel',
|
||||
'admin.plugins.updateConsentBody':
|
||||
@@ -271,11 +304,36 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.restarted': 'Plugin újraindítva',
|
||||
'admin.plugins.cap.readsTrips': 'Olvassa az utazásaidat',
|
||||
'admin.plugins.cap.readsUsers': 'Olvassa az alapprofilokat',
|
||||
'admin.plugins.cap.readsCosts': 'Olvassa a költségeidet',
|
||||
'admin.plugins.cap.readsPacking': 'Csomagolási listákat olvas',
|
||||
'admin.plugins.cap.readsFiles': 'Utazási fájlokat olvas',
|
||||
'admin.plugins.cap.writesCosts': 'Költségeket ad hozzá',
|
||||
'admin.plugins.cap.writesPlaces': 'Helyeket szerkeszt',
|
||||
'admin.plugins.cap.writesDays': 'Napokat szerkeszt',
|
||||
'admin.plugins.cap.writesItinerary': 'Útitervet szerkeszt',
|
||||
'admin.plugins.cap.writesTrips': 'Utazásokat szerkeszt',
|
||||
'admin.plugins.cap.metadata': 'Metaadatot ad',
|
||||
'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.realtime': 'Valós idejű frissítések',
|
||||
'admin.plugins.cap.photos': 'Fotókat biztosít',
|
||||
'admin.plugins.cap.calendar': 'Naptáreseményeket biztosít',
|
||||
'admin.plugins.cap.placeDetails': 'Helyeket gazdagít',
|
||||
'admin.plugins.cap.warnings': 'Problémákat jelez',
|
||||
'admin.plugins.cap.events': 'Reagál a tevékenységre',
|
||||
'admin.plugins.cap.requiresAddon': '{addon} szükséges',
|
||||
'admin.plugins.cap.dependsOn': '{id} {version} szükséges',
|
||||
'admin.plugins.dep.addonDisabledToast': 'Előbb engedélyezze a szükséges bővítmény(eke)t: {addons}',
|
||||
'admin.plugins.dep.autoEnabled': 'Előbb engedélyezett szükséges pluginek: {plugins}',
|
||||
'admin.plugins.dep.downloaded': '{id} letöltve',
|
||||
'admin.plugins.dep.resolveTitle': 'Hiányzó függőségek',
|
||||
'admin.plugins.dep.resolveBody': 'A(z) „{name}” engedélyezéséhez előbb telepíteni kell ezeket a plugineket.',
|
||||
'admin.plugins.dep.requires': '{version} szükséges',
|
||||
'admin.plugins.dep.mismatch': '{wanted} szükséges — {installed} van telepítve',
|
||||
'admin.plugins.dep.download': 'Letöltés',
|
||||
'admin.plugins.dep.update': 'Frissítés',
|
||||
'admin.plugins.dep.resolveHint': 'Letölti a legújabb kompatibilis verziót a saját függőségeivel együtt.',
|
||||
'admin.plugins.accessTitle': 'Mihez fér hozzá',
|
||||
'admin.plugins.connectsTitle': 'Kapcsolódik ehhez',
|
||||
'admin.plugins.detailsTitle': 'Részletek',
|
||||
@@ -489,7 +547,8 @@ const admin: TranslationStrings = {
|
||||
'admin.group.maintenance': 'Maintenance',
|
||||
'admin.invite.tripLabel': 'Hozzáadás utazáshoz (opcionális)',
|
||||
'admin.invite.tripNone': 'Nincs utazás',
|
||||
'admin.invite.tripHint': 'Az új felhasználó automatikusan hozzáadódik ehhez az utazáshoz, amikor a linken keresztül regisztrál.',
|
||||
'admin.invite.tripHint':
|
||||
'Az új felhasználó automatikusan hozzáadódik ehhez az utazáshoz, amikor a linken keresztül regisztrál.',
|
||||
'admin.invite.boundTo': 'hozzáadja a következőhöz: {trip}',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -52,6 +52,9 @@ const budget: TranslationStrings = {
|
||||
'costs.nothingOwed': 'Senki sem tartozik neked',
|
||||
'costs.yourShare': 'A te részed',
|
||||
'costs.youPaid': 'Te fizettél',
|
||||
'costs.outstanding': 'Rendezetlen összeg',
|
||||
'costs.outstandingSub': 'Fizető nélküli költségek',
|
||||
'costs.outstandingItems': 'költség fizetőre vár',
|
||||
'costs.expenses': 'Költségek',
|
||||
'costs.entries': '{count} bejegyzés',
|
||||
'costs.searchPlaceholder': 'Költségek keresése…',
|
||||
@@ -116,10 +119,10 @@ const budget: TranslationStrings = {
|
||||
'costs.unfinishedHint': 'Csak az összegben — még nincs rendezve',
|
||||
'costs.tapToInclude': 'Koppintson a hozzáadáshoz',
|
||||
'costs.amount': 'Összeg',
|
||||
'costs.split': "Split",
|
||||
'costs.splitEqually': "Equally",
|
||||
'costs.splitCustom': "Custom",
|
||||
'costs.splitTicket': "Ticket",
|
||||
'costs.split': 'Split',
|
||||
'costs.splitEqually': 'Equally',
|
||||
'costs.splitCustom': 'Custom',
|
||||
'costs.splitTicket': 'Ticket',
|
||||
'costs.filter.allCategories': 'Összes kategória',
|
||||
'costs.filter.allDays': 'Összes nap',
|
||||
'costs.expensesCount': '{count} kiadás',
|
||||
|
||||
@@ -49,7 +49,8 @@ const collection: TranslationStrings = {
|
||||
'collections.editList': 'Átnevezés',
|
||||
'collections.listMenu': 'Lista beállításai',
|
||||
'collections.deleteList': 'Lista törlése',
|
||||
'collections.deleteListConfirm': 'Ez véglegesen törli a listát és az összes benne mentett helyet. Ez nem vonható vissza.',
|
||||
'collections.deleteListConfirm':
|
||||
'Ez véglegesen törli a listát és az összes benne mentett helyet. Ez nem vonható vissza.',
|
||||
'collections.listName': 'Lista neve',
|
||||
'collections.listNamePlaceholder': 'pl. Tokió 2025',
|
||||
'collections.listColor': 'Szín',
|
||||
@@ -61,7 +62,8 @@ const collection: TranslationStrings = {
|
||||
'collections.empty.noMatchTitle': 'Nincs találat',
|
||||
'collections.empty.noMatchText': 'Egyetlen mentett hely sem felel meg a szűrőnek vagy a keresésnek.',
|
||||
'collections.empty.firstTitle': 'Kezdd el az első gyűjteményed',
|
||||
'collections.empty.firstText': 'Hozz létre egy listát, hogy elmenthesd a helyeket, amelyekre utazásokon átívelően emlékezni szeretnél.',
|
||||
'collections.empty.firstText':
|
||||
'Hozz létre egy listát, hogy elmenthesd a helyeket, amelyekre utazásokon átívelően emlékezni szeretnél.',
|
||||
|
||||
'collections.view.grid': 'Rács',
|
||||
'collections.view.list': 'Lista',
|
||||
@@ -127,7 +129,8 @@ const collection: TranslationStrings = {
|
||||
'collections.share.memberHint': 'Csak a lista tulajdonosa hívhat meg vagy távolíthat el embereket.',
|
||||
'collections.share.cancel': 'Mégse',
|
||||
'collections.share.leave': 'Lista elhagyása',
|
||||
'collections.share.leaveConfirm': 'Elhagyod ezt a megosztott listát? Elveszíted a hozzáférést, amíg újra meg nem hívnak.',
|
||||
'collections.share.leaveConfirm':
|
||||
'Elhagyod ezt a megosztott listát? Elveszíted a hozzáférést, amíg újra meg nem hívnak.',
|
||||
'collections.share.left': 'Elhagytad a listát',
|
||||
'collections.share.remove': 'Eltávolítás',
|
||||
|
||||
|
||||
@@ -43,7 +43,8 @@ const dashboard: TranslationStrings = {
|
||||
'dashboard.status.past': 'Múlt',
|
||||
'dashboard.status.daysLeft': 'Még {count} nap',
|
||||
'dashboard.toast.loadError': 'Nem sikerült betölteni az utazásokat',
|
||||
'dashboard.loadErrorBanner': 'Nem sikerült elérni a kiszolgálót. Az utazásaid biztonságban vannak — kérlek, próbáld újra.',
|
||||
'dashboard.loadErrorBanner':
|
||||
'Nem sikerült elérni a kiszolgálót. Az utazásaid biztonságban vannak — kérlek, próbáld újra.',
|
||||
'dashboard.retry': 'Újra',
|
||||
'dashboard.toast.created': 'Utazás sikeresen létrehozva!',
|
||||
'dashboard.toast.createError': 'Nem sikerült létrehozni',
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { TranslationStrings } from '../types';
|
||||
|
||||
// English fallback until 'hu' is translated.
|
||||
const help: TranslationStrings = {
|
||||
'help.title': "Help & Docs",
|
||||
'help.search': "Search docs…",
|
||||
'help.contents': "Contents",
|
||||
'help.noResults': "No matching pages.",
|
||||
'help.title': 'Help & Docs',
|
||||
'help.search': 'Search docs…',
|
||||
'help.contents': 'Contents',
|
||||
'help.noResults': 'No matching pages.',
|
||||
'help.errorTitle': "Couldn't load this page",
|
||||
'help.errorBody': "The help content is fetched from the TREK wiki. Check your connection and try again.",
|
||||
'help.errorBody': 'The help content is fetched from the TREK wiki. Check your connection and try again.',
|
||||
};
|
||||
|
||||
export default help;
|
||||
|
||||
@@ -5,11 +5,13 @@ import backup from './backup';
|
||||
import budget from './budget';
|
||||
import categories from './categories';
|
||||
import collab from './collab';
|
||||
import collection from './collection';
|
||||
import common from './common';
|
||||
import dashboard from './dashboard';
|
||||
import day from './day';
|
||||
import dayplan from './dayplan';
|
||||
import files from './files';
|
||||
import help from './help';
|
||||
import inspector from './inspector';
|
||||
import journey from './journey';
|
||||
import login from './login';
|
||||
@@ -39,8 +41,6 @@ import trip from './trip';
|
||||
import trips from './trips';
|
||||
import undo from './undo';
|
||||
import vacay from './vacay';
|
||||
import collection from './collection';
|
||||
import help from './help';
|
||||
|
||||
const locale = {
|
||||
...common,
|
||||
|
||||
@@ -87,7 +87,8 @@ const login: TranslationStrings = {
|
||||
'login.passkey.signIn': 'Bejelentkezés passkey-jel',
|
||||
'login.passkey.failed': 'A passkey-bejelentkezés sikertelen. Kérjük, próbáld újra.',
|
||||
'login.insecureCookie.title': "Login won't stick over HTTP",
|
||||
'login.insecureCookie.body': "You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with \"Access token required\". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.",
|
||||
'login.insecureCookie.link': "Open the Troubleshooting guide",
|
||||
'login.insecureCookie.body':
|
||||
'You’re connecting over plain HTTP, so your browser drops TREK’s secure session cookie — the next request fails with "Access token required". Fix: use HTTPS, or for a home-lab set COOKIE_SECURE=false.',
|
||||
'login.insecureCookie.link': 'Open the Troubleshooting guide',
|
||||
};
|
||||
export default login;
|
||||
|
||||
@@ -25,7 +25,8 @@ const members: TranslationStrings = {
|
||||
'members.transferError': 'A tulajdonjog átruházása sikertelen',
|
||||
'members.guests': 'Vendégek',
|
||||
'members.guest': 'Vendég',
|
||||
'members.guestsHint': 'Fiók nélküli személyek. Hozzárendelhetők költségekhez, csomagoláshoz és feladatokhoz, de nem tudnak bejelentkezni.',
|
||||
'members.guestsHint':
|
||||
'Fiók nélküli személyek. Hozzárendelhetők költségekhez, csomagoláshoz és feladatokhoz, de nem tudnak bejelentkezni.',
|
||||
'members.addGuest': 'Vendég hozzáadása',
|
||||
'members.guestNamePlaceholder': 'Vendég neve',
|
||||
'members.guestAdded': 'Vendég hozzáadva',
|
||||
|
||||
@@ -24,8 +24,8 @@ const places: TranslationStrings = {
|
||||
'places.kmlImportNoneSelected': 'Válassz legalább egy típust.',
|
||||
'places.selectionCount': '{count} kiválasztva',
|
||||
'places.deleteSelected': 'Kijelöltek törlése',
|
||||
'places.changeCategory': "Change category",
|
||||
'places.categoryChanged': "{count} places updated",
|
||||
'places.changeCategory': 'Change category',
|
||||
'places.categoryChanged': '{count} places updated',
|
||||
'places.kmlKmzImported': '{count} hely importálva KMZ/KML-ből',
|
||||
'places.urlResolved': 'Hely importálva URL-ből',
|
||||
'places.importList': 'Lista importálás',
|
||||
|
||||
@@ -29,7 +29,8 @@ const settings: TranslationStrings = {
|
||||
'settings.mapStylePlaceholder': 'Válassz Mapbox stílust',
|
||||
'settings.mapStyleHint': 'Preset vagy saját mapbox://styles/USER/ID URL',
|
||||
'settings.mapOpenFreeMapStylePlaceholder': 'Válassz OpenFreeMap stílust',
|
||||
'settings.mapOpenFreeMapStyleHint': 'Preset vagy OpenFreeMap stílus URL. Az OpenFreeMap stílusok token nélkül működnek.',
|
||||
'settings.mapOpenFreeMapStyleHint':
|
||||
'Preset vagy OpenFreeMap stílus URL. Az OpenFreeMap stílusok token nélkül működnek.',
|
||||
'settings.map3dBuildings': '3D épületek és terep',
|
||||
'settings.map3dHint': 'Dőlés + valódi 3D épület-kiemelés — minden stílussal működik, beleértve a műholdast.',
|
||||
'settings.mapHighQuality': 'Magas minőség mód',
|
||||
@@ -330,7 +331,8 @@ const settings: TranslationStrings = {
|
||||
'settings.airtrail.test.success': 'Csatlakoztatva — {count} járat található',
|
||||
'settings.airtrail.test.failed': 'A kapcsolat sikertelen',
|
||||
'settings.aiParsing.title': 'AI-feldolgozás',
|
||||
'settings.aiParsing.hint': 'Használd a saját AI-modelledet a foglalások kinyeréséhez a feltöltött fájlokból. Ez csak akkor érvényes, ha a rendszergazda nem állított be modellt az egész példányhoz.',
|
||||
'settings.aiParsing.hint':
|
||||
'Használd a saját AI-modelledet a foglalások kinyeréséhez a feltöltött fájlokból. Ez csak akkor érvényes, ha a rendszergazda nem állított be modellt az egész példányhoz.',
|
||||
'settings.aiParsing.provider': 'Szolgáltató',
|
||||
'settings.aiParsing.providerLocal': 'Helyi (Ollama)',
|
||||
'settings.aiParsing.providerOpenai': 'OpenAI',
|
||||
@@ -341,7 +343,8 @@ const settings: TranslationStrings = {
|
||||
'settings.aiParsing.apiKey': 'API-kulcs',
|
||||
'settings.aiParsing.apiKeyHint': 'Titkosítva tárolva. Hagyd üresen a jelenlegi kulcs megtartásához.',
|
||||
'settings.aiParsing.multimodal': 'Dokumentumok küldése képként',
|
||||
'settings.aiParsing.multimodalHint': 'Képfelismerésre képes modellekhez — az eredeti PDF-et küldi a kinyert szöveg helyett.',
|
||||
'settings.aiParsing.multimodalHint':
|
||||
'Képfelismerésre képes modellekhez — az eredeti PDF-et küldi a kinyert szöveg helyett.',
|
||||
'settings.aiParsing.toast.saved': 'AI-beállítások elmentve',
|
||||
'settings.aiParsing.toast.saveError': 'Az AI-beállítások mentése nem sikerült',
|
||||
'settings.tabs.appearance': 'Appearance',
|
||||
@@ -360,7 +363,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.contrastLow': 'Low contrast',
|
||||
'settings.appearance.readability': 'Readability',
|
||||
'settings.appearance.transparency': 'Transparency',
|
||||
'settings.appearance.transparencyHint': 'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.transparencyHint':
|
||||
'Glassy translucent surfaces. Turn off for solid, higher-contrast backgrounds.',
|
||||
'settings.appearance.reduceMotion': 'Reduce motion',
|
||||
'settings.appearance.reduceMotionHint': 'Minimize animations and transitions.',
|
||||
'settings.appearance.density': 'Density',
|
||||
@@ -390,7 +394,8 @@ const settings: TranslationStrings = {
|
||||
'settings.appearance.group.belowHero': 'Below the hero',
|
||||
'settings.appearance.group.bottomOfPage': 'Bottom of page',
|
||||
'settings.appearance.sidebarHint': 'The whole right column. Turn off and the dashboard centers.',
|
||||
'settings.appearance.densityHint': 'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.densityHint':
|
||||
'Compact tightens spacing and padding for a denser layout that fits more on screen.',
|
||||
'settings.appearance.textSizeAll': 'Everything',
|
||||
'settings.appearance.perSize': 'Adjust each size separately',
|
||||
'settings.appearance.size.large': 'Large',
|
||||
@@ -413,10 +418,13 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.cache.title': 'Offline gyorsítótár',
|
||||
'settings.offline.mode.title': 'Offline mód',
|
||||
'settings.offline.mode.force': 'Offline mód kényszerítése',
|
||||
'settings.offline.mode.forceHint': 'Csak a gyorsítótárazott adatokból dolgozik, a módosításaidat pedig sorba állítja. Kapcsold ki az újracsatlakozáshoz és szinkronizáláshoz.',
|
||||
'settings.offline.mode.active': 'Az offline mód be van kapcsolva — a módosításaid sorban állnak, és szinkronizálódnak, amikor kikapcsolod.',
|
||||
'settings.offline.mode.forceHint':
|
||||
'Csak a gyorsítótárazott adatokból dolgozik, a módosításaidat pedig sorba állítja. Kapcsold ki az újracsatlakozáshoz és szinkronizáláshoz.',
|
||||
'settings.offline.mode.active':
|
||||
'Az offline mód be van kapcsolva — a módosításaid sorban állnak, és szinkronizálódnak, amikor kikapcsolod.',
|
||||
'settings.offline.prepare.title': 'Felkészülés offline használatra',
|
||||
'settings.offline.prepare.hint': 'Tölts le mindent, amire szükséged van — utazási adatok, dokumentumok és térképcsempék —, mielőtt megszakad a kapcsolat.',
|
||||
'settings.offline.prepare.hint':
|
||||
'Tölts le mindent, amire szükséged van — utazási adatok, dokumentumok és térképcsempék —, mielőtt megszakad a kapcsolat.',
|
||||
'settings.offline.prepare.button': 'Letöltés offline használatra',
|
||||
'settings.offline.prepare.running': 'Letöltés…',
|
||||
'settings.offline.prepare.done': 'Kész az offline használatra',
|
||||
@@ -427,12 +435,14 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.resyncing': 'Szinkronizálás…',
|
||||
'settings.offline.storage.title': 'Mit tároljon offline',
|
||||
'settings.offline.storage.tiles': 'Térképcsempék offline tárolása',
|
||||
'settings.offline.storage.tilesHint': 'A térképcsempék foglalják a legtöbb tárhelyet. Kapcsold ki, hogy csak az utazási adatokat és a dokumentumokat tárold offline.',
|
||||
'settings.offline.storage.tilesHint':
|
||||
'A térképcsempék foglalják a legtöbb tárhelyet. Kapcsold ki, hogy csak az utazási adatokat és a dokumentumokat tárold offline.',
|
||||
'settings.offline.storage.tripsTitle': 'Utazások',
|
||||
'settings.offline.storage.tripOn': 'Offline tárolva',
|
||||
'settings.offline.storage.tripOff': 'Nincs tárolva',
|
||||
'settings.offline.clear': 'Gyorsítótár törlése',
|
||||
'settings.offline.clearConfirm': 'Törlöd az összes offline utazási adatot? Online állapotban bármikor újraszinkronizálhatsz.',
|
||||
'settings.offline.clearConfirm':
|
||||
'Törlöd az összes offline utazási adatot? Online állapotban bármikor újraszinkronizálhatsz.',
|
||||
'settings.offline.stats.trips': 'Gyorsítótárazott utazások',
|
||||
'settings.offline.stats.pending': 'Függőben lévő módosítások',
|
||||
'settings.offline.stats.failed': 'Sikertelen módosítások',
|
||||
@@ -440,7 +450,8 @@ const settings: TranslationStrings = {
|
||||
'settings.offline.empty': 'Még nincsenek gyorsítótárazott utazások. Csatlakozz az internethez a szinkronizáláshoz.',
|
||||
'settings.offline.loading': 'Betöltés…',
|
||||
'settings.offline.conflicts.title': 'Szinkronizálási ütközések',
|
||||
'settings.offline.conflicts.hint': 'Az offline végzett módosításaid ütköznek a szerveren lévő újabb módosításokkal. Válaszd ki, melyik verziót szeretnéd megtartani.',
|
||||
'settings.offline.conflicts.hint':
|
||||
'Az offline végzett módosításaid ütköznek a szerveren lévő újabb módosításokkal. Válaszd ki, melyik verziót szeretnéd megtartani.',
|
||||
'settings.offline.conflicts.keepMine': 'Az enyém megtartása',
|
||||
'settings.offline.conflicts.keepServer': 'A szerveré megtartása',
|
||||
'settings.offline.conflicts.mine': 'A te verziód',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user