diff --git a/client/src/mobile/screens/vacay/MVacay.tsx b/client/src/mobile/screens/vacay/MVacay.tsx index ccff1b1e9..9d4074f1a 100644 --- a/client/src/mobile/screens/vacay/MVacay.tsx +++ b/client/src/mobile/screens/vacay/MVacay.tsx @@ -11,6 +11,9 @@ import MVacaySettingsSheet from './MVacaySettingsSheet' import MVacayShareSheet from './MVacayShareSheet' import { FALLBACK_PERSON_COLOR } from './vacayDayModel' +/** Half days (#552) make the balance fractional; one decimal is exact and never drifts. */ +const fmtDays = (n: number) => (Number.isInteger(n) ? String(n) : n.toFixed(1)) + const WEEKDAY_KEYS_MONDAY = ['vacay.mon', 'vacay.tue', 'vacay.wed', 'vacay.thu', 'vacay.fri', 'vacay.sat', 'vacay.sun'] as const const WEEKDAY_KEYS_SUNDAY = ['vacay.sun', 'vacay.mon', 'vacay.tue', 'vacay.wed', 'vacay.thu', 'vacay.fri', 'vacay.sat'] as const @@ -78,23 +81,16 @@ export default function MVacay() {
{v.selectedUser.username}
-
- - {t('mobileVacay.usedCount', { count: stat.used })} - - {stat.carried_over > 0 && ( - - +{stat.carried_over} {v.isShiftedYear ? t('vacay.carriedOverPrevPeriod') : t('vacay.carriedOver', { year: v.selectedYear - 1 })} - - )} - {/* Comp / flex days (#1074) sit outside used/left — they cost nothing. */} - {(stat.comp_used ?? 0) > 0 && ( - - {t('vacay.compUsedCount', { count: stat.comp_used })} - - )} - - {t('mobileVacay.leftCount', { count: stat.remaining })} + {/* Used, carried over and comp days all fit on a desktop sidebar; on a + phone they turn into four scraps of 9px text. Only the number you + act on stays — the rest is a tap away in the entitlement view. */} +
+ + {fmtDays(stat.remaining)} + {t('mobileVacay.left')}
@@ -110,7 +106,7 @@ export default function MVacay() {
{stat.vacation_days}
- {t('mobileVacay.daysPerYear')} + {t('vacay.entitlementDays')}
)} {/* Divider — comp/flex and half-day modify the selected person's logging, they aren't modes. */} diff --git a/client/src/pages/LoginPage.tsx b/client/src/pages/LoginPage.tsx index 93308e803..ebb0abf69 100644 --- a/client/src/pages/LoginPage.tsx +++ b/client/src/pages/LoginPage.tsx @@ -1,28 +1,41 @@ import { - BookMarked, - CheckSquare, ChevronDown, Eye, EyeOff, Fingerprint, - FolderOpen, Globe, KeyRound, Lock, Mail, - Map, Plane, - Route, Shield, User, - Users, - Wallet, - Zap, } from 'lucide-react'; import React from 'react'; import ToggleSwitch from '../components/Settings/ToggleSwitch'; import { SUPPORTED_LANGUAGES, useTranslation } from '../i18n'; import { useLogin } from './login/useLogin'; +import LoginWorld from './login/LoginWorld'; + +/** Fixed so the sky does not reshuffle on every render. */ +const STARFIELD = [ + { top: 6, left: 12, size: 1, opacity: 0.3, delay: 0 }, + { top: 11, left: 68, size: 2, opacity: 0.22, delay: 1.4 }, + { top: 17, left: 34, size: 1, opacity: 0.35, delay: 2.6 }, + { top: 22, left: 84, size: 1, opacity: 0.25, delay: 0.7 }, + { top: 28, left: 19, size: 2, opacity: 0.2, delay: 3.1 }, + { top: 33, left: 57, size: 1, opacity: 0.32, delay: 1.9 }, + { top: 39, left: 91, size: 1, opacity: 0.24, delay: 2.2 }, + { top: 46, left: 8, size: 1, opacity: 0.28, delay: 0.4 }, + { top: 52, left: 73, size: 2, opacity: 0.18, delay: 3.6 }, + { top: 58, left: 42, size: 1, opacity: 0.3, delay: 1.1 }, + { top: 64, left: 88, size: 1, opacity: 0.26, delay: 2.9 }, + { top: 71, left: 26, size: 1, opacity: 0.22, delay: 0.9 }, + { top: 77, left: 63, size: 1, opacity: 0.3, delay: 3.3 }, + { top: 83, left: 15, size: 2, opacity: 0.2, delay: 1.7 }, + { top: 88, left: 79, size: 1, opacity: 0.27, delay: 2.4 }, + { top: 93, left: 48, size: 1, opacity: 0.23, delay: 0.2 }, +]; export default function LoginPage(): React.ReactElement { const { t, language } = useTranslation(); @@ -96,190 +109,110 @@ export default function LoginPage(): React.ReactElement { if (showTakeoff) { return ( -
- {/* Sky gradient */} -
- - {/* Stars */} - {Array.from({ length: 60 }, (_, i) => ( -
0.7 ? 3 : 1.5, - height: Math.random() > 0.7 ? 3 : 1.5, - borderRadius: '50%', - background: 'white', - top: `${Math.random() * 100}%`, - left: `${Math.random() * 100}%`, - animationDelay: `${0.3 + Math.random() * 0.5}s, ${Math.random() * 1}s`, - }} - /> - ))} - - {/* Clouds rushing past */} - {[0, 1, 2, 3, 4].map((i) => ( -
- ))} - - {/* Speed lines */} - {Array.from({ length: 12 }, (_, i) => ( -
- ))} - - {/* Plane */} -
- - - - - - - - - - +
+ {/* Signing in picks up exactly where the login panel left off: the same dot + map, except now every route departs at once. The network finishing is + the moment — no separate imagery, no plane flying off alone. */} +
+
- {/* Contrail */} -
+ {/* The colour rises with the departures rather than sitting there from the start. */} +
+
- {/* Logo fade in + burst */} -
- TREK +
+ TREK

{t('login.tagline')}

+ {/* Hands over to the app instead of cutting to it. */} +
+
@@ -389,7 +322,7 @@ export default function LoginPage(): React.ReactElement { style={{ display: 'none', width: '55%', - background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%)', + background: '#070c1a', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', @@ -401,240 +334,87 @@ export default function LoginPage(): React.ReactElement { > - {/* Stars */} + {/* Aurora — three drifting colour fields. They are the whole background: + deep indigo, a cold cyan and a warm violet, blurred past recognition so + what is left is the gradient, not the shapes. */} +
+
+
+ + {/* Depth: a handful of pinpricks, far enough back to read as distance. */}
- {Array.from({ length: 40 }, (_, i) => ( + {STARFIELD.map((s, i) => (
0.7 ? 2 : 1, - height: Math.random() > 0.7 ? 2 : 1, + width: s.size, + height: s.size, borderRadius: '50%', background: 'white', - opacity: 0.15 + Math.random() * 0.25, - top: `${Math.random() * 70}%`, - left: `${Math.random() * 100}%`, - animationDelay: `${Math.random() * 4}s`, + opacity: s.opacity, + top: `${s.top}%`, + left: `${s.left}%`, + animationDelay: `${s.delay}s`, }} /> ))}
- {/* Animated glow orbs */} -
-
+ {/* Coastlines as a dot map, with routes lighting up between cities across + it. The geometry is TREK's own Atlas bundle, baked in at build time + because this screen is unauthenticated. */} + - {/* Animated planes — realistic silhouettes at different sizes/speeds */} -
- {/* Plane 1 — large, slow, foreground */} - - - - - - - - - - - - {/* Plane 2 — small, faster, higher */} - - - - - - - - - - {/* Plane 3 — medium, mid-speed */} - - - - - - - - - - - - {/* Plane 4 — tiny, fast, high */} - - - - - - - - - - {/* Plane 5 — medium, right to left, lower */} - - - - - - - - - - - - {/* Plane 6 — tiny distant */} - - - - - - - - -
- -
- {/* Logo */} -
- TREK + {/* No max-width: the tagline stays on one line, so the block is allowed to + use the whole panel rather than wrapping inside it. */} +
+ {/* Logo. The soft dark halo is what separates it from the dot map behind — + wide and low-opacity rather than a hard drop shadow, so it reads as the + map dimming around the mark instead of an outline. */} +
+ TREK

{t('login.tagline')}

{t('login.description')}

- -
- {[ - { Icon: Map, label: t('login.features.maps'), desc: t('login.features.mapsDesc') }, - { Icon: Zap, label: t('login.features.realtime'), desc: t('login.features.realtimeDesc') }, - { Icon: Wallet, label: t('login.features.budget'), desc: t('login.features.budgetDesc') }, - { Icon: Users, label: t('login.features.collab'), desc: t('login.features.collabDesc') }, - { Icon: CheckSquare, label: t('login.features.packing'), desc: t('login.features.packingDesc') }, - { Icon: BookMarked, label: t('login.features.bookings'), desc: t('login.features.bookingsDesc') }, - { Icon: FolderOpen, label: t('login.features.files'), desc: t('login.features.filesDesc') }, - { Icon: Route, label: t('login.features.routes'), desc: t('login.features.routesDesc') }, - ].map(({ Icon, label, desc }) => ( -
) => { - e.currentTarget.style.background = 'rgba(255,255,255,0.08)'; - e.currentTarget.style.borderColor = 'rgba(255,255,255,0.12)'; - }} - onMouseLeave={(e: React.MouseEvent) => { - e.currentTarget.style.background = 'rgba(255,255,255,0.04)'; - e.currentTarget.style.borderColor = 'rgba(255,255,255,0.06)'; - }} - > - -
- {label} -
-
- {desc} -
-
- ))} -
- -

- {t('login.selfHosted')} -

@@ -1441,20 +1221,45 @@ export default function LoginPage(): React.ReactElement {
diff --git a/client/src/pages/login/LoginWorld.tsx b/client/src/pages/login/LoginWorld.tsx new file mode 100644 index 000000000..8a684f1a3 --- /dev/null +++ b/client/src/pages/login/LoginWorld.tsx @@ -0,0 +1,287 @@ +import React from 'react'; +import { WORLD_GRID_H, WORLD_GRID_W, WORLD_LAT_BOT, WORLD_LAT_TOP, worldDots } from './worldDots'; + +/** + * The login backdrop: TREK's own coastlines as a dot map, with routes lighting up + * between cities across it. + * + * The dots come from the Atlas admin-0 bundle, baked into worldDots.ts — the login + * screen is unauthenticated, so it cannot fetch Atlas at runtime. + * + * Perf: the map itself is drawn once into an offscreen canvas and blitted each + * frame; only the handful of live arcs and their city glows are drawn per frame. + * No shadowBlur in the frame path. + */ + +/** Landmark cities, the anchors every route runs between. */ +const CITIES: [number, number][] = [ + [52.52, 13.4], [48.85, 2.35], [51.5, -0.12], [40.4, -3.7], [41.9, 12.5], [59.33, 18.07], + [41.0, 28.98], [55.75, 37.62], [52.37, 4.9], [64.15, -21.9], [38.72, -9.14], [50.08, 14.44], + [30.04, 31.24], [6.52, 3.38], [-1.29, 36.82], [-33.92, 18.42], [33.57, -7.59], + [25.2, 55.27], [19.08, 72.88], [28.61, 77.21], [13.76, 100.5], [1.35, 103.82], + [-6.21, 106.85], [35.68, 139.69], [37.57, 126.98], [39.9, 116.4], [31.23, 121.47], + [22.32, 114.17], [-33.87, 151.21], [-36.85, 174.76], [-27.47, 153.03], + [49.28, -123.12], [37.77, -122.42], [40.71, -74.01], [41.88, -87.63], [43.65, -79.38], + [19.43, -99.13], [25.76, -80.19], [4.71, -74.07], [-12.05, -77.04], [-23.55, -46.63], + [-34.6, -58.38], [-33.45, -70.67], +]; + +interface Arc { + from: number; + to: number; + /** 0 → 1 progress of the light travelling the arc. */ + t: number; + speed: number; + /** Stays lit briefly after arrival, then fades. */ + hold: number; + lift: number; + /** Milliseconds still to wait before this one starts (takeoff cascade). */ + delay: number; +} + +const MAX_ARCS = 7; +const DOT_COLOUR = 'rgba(148, 163, 214, 0.55)'; + +interface LoginWorldProps { + /** + * `ambient` is the login backdrop: a few routes at a time, forever. + * `takeoff` is the sign-in moment: the whole network fires at once and stays lit. + */ + variant?: 'ambient' | 'takeoff'; +} + +export default function LoginWorld({ variant = 'ambient' }: LoginWorldProps): React.ReactElement { + const canvasRef = React.useRef(null); + const takeoff = variant === 'takeoff'; + + React.useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false; + const dots = worldDots(); + + let raf = 0; + let width = 0; + let height = 0; + let dpr = 1; + let mapLayer: HTMLCanvasElement | null = null; + /** Map placement in CSS pixels, shared by the layer and the city projection. */ + let originX = 0; + let originY = 0; + let cell = 1; + + const project = (lat: number, lon: number) => ({ + x: originX + ((lon + 180) / 360) * (WORLD_GRID_W - 1) * cell, + y: originY + ((WORLD_LAT_TOP - lat) / (WORLD_LAT_TOP - WORLD_LAT_BOT)) * (WORLD_GRID_H - 1) * cell, + }); + + /** Redraw the static dot map. Called on mount and on resize only. */ + function buildMap() { + // Cover the panel, biased up a little so the busier northern half sits behind + // the copy rather than under it. + cell = Math.max(width / (WORLD_GRID_W - 1), height / (WORLD_GRID_H - 1)) * 1.08; + originX = (width - (WORLD_GRID_W - 1) * cell) / 2; + originY = (height - (WORLD_GRID_H - 1) * cell) / 2; + + const layer = document.createElement('canvas'); + layer.width = Math.max(1, Math.floor(width * dpr)); + layer.height = Math.max(1, Math.floor(height * dpr)); + const lc = layer.getContext('2d'); + if (!lc) return; + lc.scale(dpr, dpr); + lc.fillStyle = DOT_COLOUR; + const r = Math.max(0.6, cell * 0.19); + for (const d of dots) { + const x = originX + d.x * cell; + const y = originY + d.y * cell; + if (x < -4 || x > width + 4 || y < -4 || y > height + 4) continue; + lc.beginPath(); + lc.arc(x, y, r, 0, Math.PI * 2); + lc.fill(); + } + mapLayer = layer; + } + + function resize() { + const rect = canvas!.getBoundingClientRect(); + dpr = Math.min(window.devicePixelRatio || 1, 2); + width = rect.width; + height = rect.height; + canvas!.width = Math.max(1, Math.floor(width * dpr)); + canvas!.height = Math.max(1, Math.floor(height * dpr)); + ctx!.setTransform(dpr, 0, 0, dpr, 0, 0); + buildMap(); + } + + const arcs: Arc[] = []; + + function spawnArc(opts?: { delay?: number; speed?: number }) { + if (!takeoff && arcs.length >= MAX_ARCS) return; + const from = Math.floor(Math.random() * CITIES.length); + let to = Math.floor(Math.random() * CITIES.length); + if (to === from) to = (to + 1 + Math.floor(Math.random() * 3)) % CITIES.length; + arcs.push({ + from, + to, + t: 0, + // Per millisecond, so 1/speed is the flight time: roughly 3–5s. Long enough + // to read as a journey, short enough that the map keeps moving. + speed: opts?.speed ?? 0.0002 + Math.random() * 0.00013, + hold: 0, + // Longer hops bow higher, the way a route reads on a map. + lift: 0.16 + Math.random() * 0.16, + delay: opts?.delay ?? 0, + }); + } + + /** + * Sign-in: the whole network lights up at once. Routes leave in a staggered + * cascade so it reads as departures rather than one flash, and nothing fades — + * the finished web is the last thing on screen before the app takes over. + */ + function burst() { + for (let i = 0; i < 11; i++) { + spawnArc({ delay: i * 105 + Math.random() * 70, speed: 0.0013 + Math.random() * 0.0009 }); + } + } + + /** Quadratic arc between two projected cities, bowed away from the straight line. */ + function arcPoints(a: Arc) { + const p1 = project(CITIES[a.from][0], CITIES[a.from][1]); + const p2 = project(CITIES[a.to][0], CITIES[a.to][1]); + const mx = (p1.x + p2.x) / 2; + const my = (p1.y + p2.y) / 2; + const dx = p2.x - p1.x; + const dy = p2.y - p1.y; + const len = Math.hypot(dx, dy) || 1; + // Perpendicular offset, always bowing "up" so arcs never dive off-screen. + const cx = mx - (dy / len) * len * a.lift * Math.sign(dx || 1); + const cy = my + (dx / len) * len * a.lift * Math.sign(dx || 1) - len * 0.08; + return { p1, p2, cx, cy }; + } + + function pointOn(p1: { x: number; y: number }, cx: number, cy: number, p2: { x: number; y: number }, t: number) { + const u = 1 - t; + return { + x: u * u * p1.x + 2 * u * t * cx + t * t * p2.x, + y: u * u * p1.y + 2 * u * t * cy + t * t * p2.y, + }; + } + + function cityGlow(x: number, y: number, alpha: number, radius: number) { + const g = ctx!.createRadialGradient(x, y, 0, x, y, radius); + g.addColorStop(0, `rgba(199, 210, 254, ${alpha})`); + g.addColorStop(0.35, `rgba(129, 140, 248, ${alpha * 0.5})`); + g.addColorStop(1, 'rgba(99, 102, 241, 0)'); + ctx!.fillStyle = g; + ctx!.beginPath(); + ctx!.arc(x, y, radius, 0, Math.PI * 2); + ctx!.fill(); + } + + let last = 0; + let spawnTimer = 0; + + function frame(now: number) { + const dt = last ? Math.min(now - last, 48) : 16; + last = now; + + ctx!.clearRect(0, 0, width, height); + if (mapLayer) ctx!.drawImage(mapLayer, 0, 0, width, height); + + if (!takeoff) { + spawnTimer -= dt; + if (spawnTimer <= 0) { + spawnArc(); + spawnTimer = 1600 + Math.random() * 2200; + } + } + + for (let i = arcs.length - 1; i >= 0; i--) { + const a = arcs[i]; + + if (a.delay > 0) { + a.delay -= dt; + continue; + } + + const { p1, p2, cx, cy } = arcPoints(a); + + if (a.t < 1) a.t = Math.min(1, a.t + a.speed * dt); + else a.hold += dt; + + // Ambient routes retire to make room for the next; the takeoff web stays lit. + const fade = takeoff ? 1 : a.hold > 2200 ? Math.max(0, 1 - (a.hold - 2200) / 2400) : 1; + if (fade <= 0) { + arcs.splice(i, 1); + continue; + } + + // The drawn portion, with the leading edge brightest. + ctx!.strokeStyle = `rgba(129, 140, 248, ${0.5 * fade})`; + ctx!.lineWidth = 1.4; + ctx!.beginPath(); + const segs = Math.max(2, Math.round(34 * a.t)); + for (let s = 0; s <= segs; s++) { + const pt = pointOn(p1, cx, cy, p2, (a.t * s) / segs); + if (s === 0) ctx!.moveTo(pt.x, pt.y); + else ctx!.lineTo(pt.x, pt.y); + } + ctx!.stroke(); + + // Origin lights the moment the route leaves it, destination on arrival. + cityGlow(p1.x, p1.y, 0.55 * fade, Math.max(9, cell * 2.6)); + if (a.t >= 1) cityGlow(p2.x, p2.y, 0.6 * fade, Math.max(11, cell * 3)); + + // The travelling head. + if (a.t < 1) { + const head = pointOn(p1, cx, cy, p2, a.t); + cityGlow(head.x, head.y, 0.75, Math.max(7, cell * 2)); + ctx!.fillStyle = 'rgba(224, 242, 254, 0.95)'; + ctx!.beginPath(); + ctx!.arc(head.x, head.y, 1.8, 0, Math.PI * 2); + ctx!.fill(); + } + } + + raf = window.requestAnimationFrame(frame); + } + + resize(); + window.addEventListener('resize', resize); + + if (reduced) { + // Still frame: the map, no routes firing. + ctx.clearRect(0, 0, width, height); + if (mapLayer) ctx.drawImage(mapLayer, 0, 0, width, height); + } else if (takeoff) { + burst(); + raf = window.requestAnimationFrame(frame); + } else { + // Open with a few routes already in flight instead of an empty map. + for (let i = 0; i < 3; i++) { + spawnArc(); + const a = arcs[arcs.length - 1]; + if (a) a.t = Math.random() * 0.6; + } + raf = window.requestAnimationFrame(frame); + } + + return () => { + window.cancelAnimationFrame(raf); + window.removeEventListener('resize', resize); + }; + }, [takeoff]); + + return ( + + ); +} diff --git a/client/src/pages/login/worldDots.ts b/client/src/pages/login/worldDots.ts new file mode 100644 index 000000000..4e542e7d7 --- /dev/null +++ b/client/src/pages/login/worldDots.ts @@ -0,0 +1,23 @@ +// Generated from server/assets/atlas/admin0.geojson.gz — TREK's own Atlas bundle. +// The login screen is unauthenticated and cannot fetch Atlas, so the coastlines ship +// as a 300x150 dot grid: sorted cell indices, delta-encoded in base36. +// Regenerate with the script in the design notes if the bundle ever changes. + +export const WORLD_GRID_W = 300; +export const WORLD_GRID_H = 150; +export const WORLD_LAT_TOP = 80; +export const WORLD_LAT_BOT = -58; + +const PACKED = + '1u.1.1.1.1.1.6.1.1.2.1.1.2.1.1.1.1.1.1.4.1.10.1.1.1.n.1.1.1.1.1.1.1.1.1.1.3.1.j.1.6.1.e.1.b.1.1.1.3.1.1.3g.1.4.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.10.1.1.1.n.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.12.1.b.1.1.1.1.1.1.1.1.1.1.1.1.39.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.2.11.1.1.1.1.o.1.1.1.1.1.1.1.1.1.1.1.1l.5.1.1.1.1.1.1.1.32.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.3.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.4.1.1.1.1.1.1.10.1.1.1.1.q.1.1.1.1.2.1.1.1.y.1.1.1.b.5.1.5.1.3.1.1.1.1.1.1.1.1.1.w.21.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.6.1.1.1.1.1.1.1.1.1.v.1.1.1.t.1.6.1.r.1.1.1.1.1.1.1.1.1.a.1.1.8.1.1.1.1.1.1.1.4.1.1.1.1.1.1.i.2.1.1.1.1.1.4.1.23.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.g.1.1.t.1.1.1.1.1p.1.1.1.1.1.1.1.1.d.1.3.1.1.1.1.1.1.2.1.d.1.i.1.1.1.1.1.1.1.1.1.1.1.1.1.1z.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.i.1.s.1.1.1.v.1.t.1.1.1.1.1.f.1.2.1.1.1.1.i.1.1.1.1.2.g.1.1.1.1.1.1.1.3.1.1.1.1z.1.5.1.1.1.5.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.g.1.1.m.1.1.1.1.1.1.1.1p.1.1.1.1.a.1.2.1.1.2.1.1.1.1.1.1.1.f.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.5.1.3.1.1.1.24.1.4.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.2.2.1.1.1.2.1.1.1.1.1.g.1.1.m.1.1.1.1.1p.1.1.1.1.b.1.1.1.1.1.1.1.1.1.1.1.j.1.b.1.1.1.4.1.8.1.1.1.1.1.1.1.1.1.q.1.1.h.1.p.1.1.1.1.1.1.1.1.1.5.1.2.1.3.1.1.1.1.1.1.3.1.1.1.1.1.2.1.1.1.1.1.1.1.1.d.1.1.1.1.j.1.1.1.1.1.1.1o.1.1.1.1.9.1.1.2.1.1.1.1.1.1.1.1.1.1.11.1.2.1.1.1.1.1.1.1.4.1.2.1.1.1.m.1.1.1.1.d.1.1.1.1.1.1.1.1.1.1.1.1.g.1.3.1.3.1.1.1.1.1.1.6.1.1.1.3.1.2.1.2.1.1.1.1.2.2.1.3.1.1.1.1.1.1.b.1.1.1.1.h.1.1.1.1.1.1.1.a.1.n.1.1.1.1.1.1.1.1.1.i.1.1.1.1.1.1.6.5.1.2.1.1.3.1.12.1.1.5.b.1.1.1.1.1.1.1.1.1.e.1.f.1.1.1.a.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.4.3.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.4.1.1.1.1.a.1.1.1.1.i.1.1.1.1.1.x.1.1.1.1.1.1.1.1.1.1.1.1.1.1.c.1.7.1.1.1.1.1.2.1.4.1.7.1.1r.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.a.1.1.1.i.2.1.1.1.1.3.4.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.2.1.1.1.1.4.1.1.b.1.1.1.e.1.2.1.1.1.1.w.1.1.1.1.1.1.1.1.1.1.2.1.3.1.1.1.1.4.1.1.1.2.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.3.1.1.1.1.1x.1.5.1.1.1.6.1.1.1.1.1.1.7.1.1.j.l.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.3.1.2.1.1.1.1.1.4.1.1.1.1.8.1.1.1.e.1.1.1.1.y.1.1.1.1.1.1.2.1.4.1.7.1.1.1.2.1.1.1.1.1.1.1.1.1.4.7.1.3.1.1.2.1.2c.1.4.1.1.1.6.1.1.1.g.r.1.1.8.1.7.1.1.1.1.1.3.1.1.2.1.3.1.2.1.1.1.6.1.1.1.d.1.12.1.1.1.6.5.2.1.1.1.4.1.1.1.1.1.1.i.1.1.1.1.4.2c.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.g.s.1.d.1.1.1.1.1.1.1.7.1.4.1.1.1.1.1.1.6.1.1.1.9.1.1.1.1.8.1.1.1.1.1.1.1.1.1.l.1.1.1.6.1.1.4.1.2.1.1.1.1.1.1.1.1.1.l.1.1.1.2h.1.1.1.1.1.2.1.1.2.1.1.3.1.g.17.1.1.1.1.1.1.4.1.1.1.5.1.1.1.1.1.7.1.1.1.7.1.1.1.1.a.1.1.1.1.4.1.k.1.1.1.1.5.1.2.1.3.4.1.1.1.1.1.34.1.1.1.1.5.1.1.5.1.1.1.1.1.g.13.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.1.4.1.1.a.1.1.7.1.f.1.3.1.1.1.j.1.1.1.1.6.1.1.1.4.1.3.1.1.1.1.1.34.1.1.1.9.1.1.4.1.1.1.g.15.1.1.2.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.b.1.6.1.1.g.1.1.1.1.j.1.1.1.1.1.4.1.1.1.1.1.5.1.1.5.39.1.d.1.j.14.1.1.6.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.c.1.5.1.s.1.a.1.1.1.3.4.1.2.8.1.2z.2.1.1.8.1.1.1.1.c.1.1.b.1.1.1.1.4.13.1.b.1.1.4.1.1.1.2.1.1.c.1.1.1.1.1.1.1.t.9.1.1.1.4.4.3.1.5.1.1.2w.1.1.1.1.1.1.1.6.1.1.1.b.4.1.1.1.1.1.7.1.1.1.1.1.1.1.1.1.1.1.1.11.d.1.6.1.1.2.1.d.1.1.1.1.1.y.1.4.1.1.5.4.1.1.1.1.1.1.1.1.1.1.2w.1.3.1.1.1.1.1.1.1.1.1.1.1.i.1.1.1.1.6.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.x.c.2.6.1.3.1.1.f.1.1.x.1.1.5.1.3.1.1.4.1.1.1.1.1.1.1.1.1.1.1.2l.1.1.1.1.1.1.1.1.1.1.4.1.1.1.1.1.1.3.o.1.1.1.1.2.1.1.1.a.1.1.1.1.1.w.1.c.1.6.1.1.1.1.1.1.1.1a.1.1.1.7.1.1.1.1.1.4.1.1.1.2.1.1.2.1.2l.1.1.3.3.1.1.1.1.3.1.2.1.1.w.1.1.1.1.1.d.1.1.1.1.w.1.1.9.2.1.6.1.4.1.18.1.1.1.1.1.1.7.1.2.1.1.3.1.1.1.1.1.1.1.1.1.1.2k.1.e.1.3.1.n.1.7.1.1.1.1.1.1.1.e.1.1.1.1.v.1.1.1.1.7.1.2.c.1.18.1.1.2.1.8.1.1.1.1.3.1.1.2.1.1.1.2.1.2j.1.e.1.5.t.1.1.1.1.2.h.1.1.1.1.1.y.1.1.5.1.1.1.c.1.19.1.1.1.1.8.1.1.1.1.1.1.1.4.1.1.1.1.1.1.1.1.2g.1.f.5.1.r.1.1.1.1.1.l.1.1.1.1.10.1.1.1.2.1.1.e.1.1.1.14.1.1.1.1.1.1.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.3.t.1.1.1.1.1.1h.1.1.f.4.1.3.1.1.m.1.1.1.1.o.1.1.1.1.12.2.1.g.1.1.1.12.1.1.1.1.1.1.2.1.5.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.4.1.o.1.1.1.1.4.1.1.1.1.1.1.1d.1.1.1.2.b.3.1.1.5.j.1.1.1.s.1.1.1.1.12.1.1.1.f.1.2.1.12.1.2.1.1.1.2.1.1.2.1.1.1.1.1.3.1.7.6.1.1.n.1.9.1.3.1.y.1.1.1.1.1.9.1.1.1.1.1.1.a.2.1.b.1.7.1.1.1.1.1.1.1.w.1.1.1.1.11.1.1.1.j.1.11.1.1.1.1.1.1.1.2.1.2.1.1.7.7.1.1.1.3.1.1.1.e.1.6.1.f.g.1.1.g.1.4.1.c.1.1.a.2.c.1.2.1.1.1.2.1.1.14.1.1.1.11.1.g.1.1.1.12.1.1.2.1.1.1.1.1.1.1.1.1.1.6.1.7.1.2.1.1.1.3.1.b.1.1.1.1.1.1.1.1.1.1.1.e.1.1.1.1.7.5.1.1.1.f.6.b.1.1.1.a.1.1.h.1.18.1.1.1.1.1b.1.1.1.1.1.1.1.1.1.15.1.1.1.1.1.1.1.1.1.1.1.5.1.1.1.1.1.4.1.9.1.1.1.7.1.5.1.2.1.1.1.f.1.1.1.1.4.1.1.1.1.1.1.1.3.1.1.1.1.1.5.1.1.2.1.6.b.1.1.1.9.1.1t.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.l.1.1.1.1.1.1.3.1.1.1.1.16.1.1.1.4.1.1.4.1.1.2.1.1.1.1.1.d.1.5.1.1.u.1.1.1.1.6.1.8.1.1.1.1.1.2.1.1.7.1.1.9.1.1.1.1.8.1v.1.1.1.n.1.1.1.1.1.1.e.1.1.1.1.1.4.1.3.1.1.13.1.1.1.8.4.1.1.1.1.1.1.1.1.1.2.1.1.9.1.5.1.w.1.1.m.1.2.a.2.1.4.1.1.1.1.1.7.1.1w.1.1.p.2.2.1.d.1.1.1.1.1.2.1.1.1.1.1.1.1.1.14.1.1.7.1.1.1.1.1.3.1.1.3.1.1.1.1.1.1.7.1.1.6.1.s.1.2.2.1.1.k.1.1.1.1.9.1.1.1.4.2.1.8.1.1x.1.w.1.a.1.1.1.2.1.1.1.1.1.3.1.1.1.16.1.5.1.1.1.1.1.1.1.1.1.1.3.1.5.1.1.1.1.1.1.1.1.1.8.1.1.1.1.o.1.1.1.4.l.1.1.1.c.3.1.2.1.1.5.1.1.1z.x.1.9.1.2.1.1.1.1.1.1.1.1c.1.5.1.1.1.3.1.1.1.1.1.1.1.6.1.1.1.1.1.1.1.1.1.7.1.2.1.4.1.1.i.1.7.k.1.d.1.1.2.1.3.1.1.3.1.1.1.1z.1.y.5.1.1.1.1.1.1.1.1.1.1.1.1.1.1d.1.6.2.3.1.1.1.1.1.1.1.1.5.1.3.1.1.1.1.1.6.1.2.1.4.1.2.1.g.1.1.7.1.1.1.e.1.1.1.d.1.1.2.1.4.1.1.1.1.1.1.21.z.3.1.1.5.1.1.1.1.1.4.17.1.1.1.1.1.5.1.1.1.1.1.2.1.1.1.1.1.1.2.1.1.1.1.1.9.1.1.4.1.2.1.4.4.1.1.1.1.c.1.b.1.d.g.1.3.5.2.1.1.1.22.z.2.1.7.4.1b.1.3.1.1.1.1.1.1.1.1.1.2.1.1.2.2.1.1.1.1.1.1.3.1.a.1.1.1.1.1.1.3.1.3.2.1.5.4.1.1.1.1.1.1.1.1.1.c.1.1.1.1.1.7.1.f.1.1.1.1.1.4.1.2.1.1.23.y.1.1.1.7.1.1f.1.1.7.1.5.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.3.1.1.1.1.4.1.2.1.1.1.3.1.1.1.1.1.1.1.4.1.2.1.1.1.1.4.1.i.1.1.1.1.1.1.f.1.1.1.7.1.1.1.1.25.16.1.1.1.1f.2.6.1.1.4.1.1.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.2.2.1.1.2.1.1.4.1.1.1.1.1.2.1.1.1.z.1.3.1.1.1.9.1.27.1.14.1.10.i.2.5.1.2.1.3.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.c.1.1.3.1.1.1.7.1.4.1.1.1.1.1.1.1.10.1.1.1.1.1.1.1.1.9.1.28.13.1.1.10.2.1.f.1.1.5.1.1.1.4.1.1.4.1.1.2.1.1.1.1.1.1.e.1.1.1.1.1.3.8.1.2.1.1.1.1.1.1.10.1.1.1.1.1.1.1.1.1.a.1.28.1.11.1.1.13.1.1.1.d.1.1.5.1.9.1.1.1.1.3.1.1.1.1.1.1.e.3.1.4.1.1.1.1.5.1.1.1.2.1.2.1.z.1.2.3.1.1.1.1.7.1.1.1.29.12.1.15.1.d.1.1.4.1.6.1.1.1.1.1.1.1.5.1.1.1.1.1.1.a.1.1.1.1.3.1.1.2.1.3.1.4.1.1.1.1.1.1.1.1.1.10.1.1.1.3.1.2.1.5.1.1.2.29.1.11.1.16.f.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.6.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.5.1.1.5.1.3.3.3.1.1.1.1.10.1.1.3.1.2.3.2.1.1.2.1.2a.11.1.1l.1.1.1.1.1.1.6.1.1.1.2.1.7.1.1.1.1.3.1.1.1.1.4.3.1.c.1.1.1.6.4.1.1.1.x.5.1.1.1.2.1.1.1.1.2.1.1.2a.1.z.1.1m.3.1.8.2.1.a.1.1.5.1.1.1.1.3.1.3.1.c.8.1.2.1.1.1.1.1.w.1.5.1.1.1.1.1.1.1.1.1.1.1.1.2b.1.1.1.w.1.1l.1.1.4.8.2.1.k.1.1.1.1.4.1.c.8.1.2.4.1.x.1.4.1.2.1.1.1.1.1.1.2.2d.1.1.1.1.1.r.1.1.c.13.1.6.6.1.7.3.1.1.1.4.1.1.1.9.1.1.1.1.6.1.b.7.1.3.1.3.1.y.4.2.1.1.1.1.6.2e.1.1.1.1.1.3.1.l.1.d.19.1.4.1.1.1.8.1.1.3.1.3.3.1.1.1.3.1.2.1.1.1.1.1.1.5.b.1.5.1.4.1.3.y.1.1.6.1.1.2m.1.1.1.1.1.1.1.1.1.e.1.1.4.1n.5.1.a.1.5.1.1.1.4.2.1.1.1.1.1.1.1.1.1.3.1.4.1.b.4.1.5.1.3.1.1.x.1.7.1.7.2f.1.1.1.6.1.1.1.6.1.1.1.1.1.1.1.1.1.2.1n.1.2.1.1.b.1.7.1.4.6.1.1.1.1.1.4.1.2.1.1.1.9.1.2.1.1.5.1.5.1.1.1.u.1.1.6.1.2l.3.1.1.1.6.1.1.1.3.1.1.2.1.1.1.4.1.1.1.1g.3.2.1.1.1.1.1.d.c.6.1.1.1.1.6.1.1.1.1.1.1.8.1.1.1.7.6.1.2.1.7.1.1.1.k.1.1f.1g.1.1.1.1.8.1.1.1.1.b.1.1.1f.1.1.1.1.1.1.2.g.c.7.1.1.a.1.2.1.8.1.6.1.1.6.1.1.2.1.1.1.1.1.1.1.3.1.j.1.6.1.a.2e.1.1.1.1.1.8.1.1.c.2.1.1.1e.3.1.1.1.1.1.f.c.7.1.1.b.1.2.1.1.1.1.5.5.1.a.1.1.1.2.1.1.1.3.1.1.1.h.1.6.1.b.11.1e.1.1.1.1.8.1.1.c.1.1.1.1.1.1f.1.2.1.1.1.1.d.1.c.8.1.1.b.1.2.1.1.1.4.1.5.1.c.1.1.1.1.4.1.2.1.h.1.5.1.2.1.9.13.1d.1.1.1.1.8.1.d.1.1.1.1.1f.3.4.1.1.c.c.8.1.1.b.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.f.1.1.1.2.3.h.1.1.1.1.2.4.1.8.2h.1.1.2.1.7.d.1.2.1.1.1.1.1c.1.3.4.2.1.b.1.b.9.1.1.b.1.1.1.1.1.8.1.1.1.1.e.2.1.1.1.3.1.f.1.1.1.1.1.1.1.4.9.a.10.2.16.1.1.2.7.d.1.1.2.1.1.1.1c.3.1.4.3.1.b.1.1.1.1.1.6.9.2.1.a.1.2.1.1.1.1.7.1.1.f.1.1.1.1.1.3.1.5.7.2.1.2.1.1.1u.15.3.1.6.b.1.1.1.1.1.1.2.1.1.1b.3.5.4.1.8.1.1.2.1.2.1.5.6.2.1.1.2.b.1.1.1.3.8.1.2.d.1.1.1.1.5.2.1.1.1.1.5.1.1.1.3.1.1u.1.1.1.16.1.6.1.6.1.1.2.1.1.1.1.1.1.1.2.1.1.19.1.1.1.1.5.5.1.6.1.5.3.1.1.3.1.1.1.1.1.1.1.1.2.2.e.2.1.8.1.1.1.c.1.1.1.1.1.5.1.1.1.3.1.1.1.1.1.1.6.1x.1.16.7.5.1.2.7.1.1.1.1.1.1a.1.8.6.1.4.1.6.1.4.1.2.a.2.1.c.3.a.1.1.b.1.4.1.5.1.1.1.1.2.1.1.1.5.5.j.1e.1.10.5.7.1.4.3.5.1.2.1.1.1.1.1.1.1.18.9.6.1.2.1.8.5.1.1.a.3.1.a.1.2.1.c.a.1.6.1.3.1.1.1.2.1.1.1.1.1.1.8.1.j.1e.1.10.5.1.7.1.1.1.1.2.1.5.2.1.1.1.1.1.1.1.1.1.1.1.1.14.9.7.1.1.8.1.6.b.1.3.6.1.1.1.1.3.1.c.9.1.7.1.2.1.2.1.1.1.1.1.2.1.9.1.j.1.h.1u.9.1.1.6.1.2.1.1.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.12.9.8.9.7.c.1.2.1.1.3.1.3.2.1.1.d.8.1.9.2.1.2.1.1.1.1.1.2.1.8.1.1.k.12.1k.1.1.7.1.1.1.3.h.1.u.1.1.1.5.1.1.7.8.9.7.b.1.1.2.1.1.1.1.1.4.1.1.1.e.8.a.1.1.1.2.1.2.1.1.1.4.6.1.1.k.2o.1.1.1.1.1.1.1.1.1.1.1.1.1.i.1.u.1.1.5.1.1.1.1.2.3.8.8.1.7.b.2.1.2.8.h.1.5.1.1.a.1.1.1.1.4.1.1.1.1.9.1.1.j.1.2t.1.2.1.3.1.i.1.u.1.5.1.3.1.1.1.1.1.1.3.1.1.1.1.1.8.7.1.b.1.1.1.1.1.6.1.1.i.5.b.4.6.2.1.9.1.1.j.1.2u.1.1.1.1.1.1.1.1.i.10.1.1.2.1.7.1.2.4.7.7.c.1.1.1.1.1.4.1.1.k.5.b.4.1.3.1.1.1.1.1.9.1.1.1.1.h.k.2d.1.1.1.2.2.h.1.10.1.1.1.1.6.1.3.1.2.1.1.1.1.1.1.1.1.1.6.c.4.1.1.1.1.1.m.1.4.a.1.1.3.1.1.1.1.4.1.9.1.1.1.1.h.2z.1.2.1.1.8.1.1.1.5.1.11.1.1.1.1.1.1.3.1.5.1.1.4.1.3.1.6.8.1.2.1.4.1.1.1.5.1.1.1.h.4.a.1.4.1.1.1.1.3.1.1.9.1.1.1.1.1.11.2f.1.1.7.1.1.1.1.1.1.1.1.1.1.2.11.1.1.3.1.1.2.2.1.1.1.1.1.1.8.1.6.1.7.1.2.5.1.1.3.1.1.1.h.1.2.1.3.a.1.3.1.1.3.1.1.1.1.1.9.2.1.1.1.u.3.1.1.1.1.1w.j.1.1.7.1.1.1.3.1.1.1.1.1.1.12.1.4.1.1.1.2.1.1.1.2.1.8.1.5.1.1.1.1.2.1.1.1.1.1.1.5.1.1.1.1.1.3.h.1.1.2.2.1.a.5.1.3.1.1.1.1.1.8.1.1.1.1.1.1.x.3.1.2f.1.1.1.1.1.1.1.1.1.2.1.1.4.3.1.13.1.1.1.2.2.1.1.3.1.1.8.1.1.1.3.1.2.1.1.1.1.1.1.1.2.7.1.6.i.1.2.2.1.1.a.4.1.1.2.1.1.1.9.1.1.2.1.1.1.a.j.4.1.1.1.1.2h.1.1.1.1.1.1.3.1.9.1.12.1.2.1.1.4.3.1.1.8.1.1.3.1.3.1.7.1.8.1.1.3.1.i.3.1.1.1.1.a.4.1.1.3.1.1.9.1.4.1.1.1.j.e.1.2.1.2h.1.1.1.1.1.3.1.9.1.1.12.1.1.1.1.4.3.1.1.7.1.2.1.1.1.5.1.6.1.a.1.2.n.2.1.a.4.1.1.e.4.1.1.1.1.6.8.2.2.2.1.2.b.1.2.2i.1.2.5.1.1.7.2.1.11.1.1.1.1.4.3.1.1.1.4.1.1.3.9.7.1.9.1.1.1.j.6.1.1.9.1.4.1.1.c.1.1.2.1.1.1.1.1.6.d.7.9.2.n.1q.8.7.1.1.4.1.2.1.1.1.1.10.1.1.4.2.1.1.1.1.1.3.4.1.9.1.7.8.1.2.k.1.5.1.b.1.3.1.1.1.a.1.1.1.2.1.2.1.1.p.3.6.1.2.o.1x.8.6.1.1.1.2.1.1.10.1.1.1.1.1.1.1.5.1.1.1.5.3.1.1.1.1.1.1.1.1.1.4.1.6.1.1.2.1.k.1.i.1.1.2.2.4.5.1.1.2.1.5.1.4.i.8.5.r.1t.3.1.7.3.1.1.1.1.1.1.2.1.1.1.10.1.a.1.1.1.1.4.2.1.2.1.5.1.1.1.1.1.1.1.1.1.1.1.1.4.l.1.i.1.1.1.1.2.4.1.4.1.1.1.1.6.1.3m.8.1.2.4.2.2.1.1.1.1d.1.1.4.1.1.1.a.1.2.4.2.4.1.l.1.i.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.7.4.z.q.1t.1.7.1.1.2.1.3.2.1.1.1.1.1.1.1e.1.1.1.1.1.1.1.a.1.2.1.5.3.1.m.1.i.1.1.1.1.1.1.1.4.1.1.3.2.1.5.1.1.1.11.a.g.1s.1.1.6.1.1.1.1.1.4.1.1.6.1c.1.1.1.1.1.1.1.1.1.9.1.3.1.5.2.1.n.k.1.1.2.1.1.1.2.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.11.a.1y.1.9.2.1.1.4.1.2.1.c.1.1.1a.2.1.3.1.2.a.4.6.1.1.o.1.k.1.3.1.1.3.7.1.1.1.1.1.1.2.1.1.1.10.n.1k.1.1.7.1.4.1.3.1.f.1.1.1.1.1a.4.1.2.a.1.1.1.1.6.1.p.l.1.1.2.1.1.3.1.6.2.1.1.1.3.1.1.1.1.1.1.1.1.p.2.4.28.1.7.4.1.1.1.3.g.1.1.1.1.1.15.3.3.1.1.1.1.a.1.3.1.4.1.1b.1.1.3.1.1.2.1.5.1.2.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.3.2.1.3.l.1.b.23.1.2.1.3.1.1.1.g.1.3.1.1.1.1.15.1.1.1.1.2.a.1.1.4.1.1.2.1.1c.1.1.2.1.1.1.2.1.4.2.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.4.1.2.1.1.k.b.23.1.1.1.6.1.k.1.2.1.1.1.14.1.4.b.1.6.2.d.11.1.3.1.1.2.1.1.1.1.1.2.1.1.1.1.2.1.1.1.1.1.1.1.1.5.1.1.1.5.1.1.1.s.1.e.1p.1.1.6.1.r.1.4.10.1.1.1.1.b.1.6.1.1.c.1.11.1.1.2.1.7.1.3.1.1.1.3.2.1.1.1.1.1.1.4.2.1.1.3.1.1.1.1.4.12.1q.2.5.1.t.1.13.1.1.1.c.8.1.b.2.d.p.1.1.1.9.3.1.1.1.7.1.1.2.1.3.3.1.1.1.1.1.1.1.1.4.e.2e.1.6.v.14.1.1.1.b.8.r.r.1.1.1.1.1.1.1.6.1.1.1.1.7.1.1.2.1.2.4.1.1.1.1.3.1.h.1.2e.5.1.v.1.13.3.1.2.1.1.6.1.7.b.3.d.1.r.1.1.1.1.1.1.1.1.5.5.1.2.1.2.3.1.1.1.2.1.2.6.1.1.1.g.1.z.1e.1.4.1.v.1.h.m.1.3.1.1.2.6.1.7.1.1k.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.5.1.1.1.1.1.1.1.1.1.2.4.1.1.1.1.2.d.8.1.b.f.1.1e.5.v.14.1.7.5.1.1.1.1.5.6.4.1f.1.1.1.1.1.1.1.1.1.1.1.1.b.1.1.1.2.1.1.1.1.1.1.3.1.1.1.1.e.1.h.9.8.1.1e.1.4.1.1.3.1.o.1.14.1.7.5.4.1.4.1.5.1.3.1k.1.1.2.1.1.e.4.1.1.1.7.1.1.4.b.d.4.i.1f.6.1.1.1.1.o.16.7.1.1.1.2.4.1.4.1.d.1.14.d.1.1.6.1.1.3.5.5.1.1.1.1.6.1.3.1.o.b.1q.1.6.3.1.m.1.16.7.1.1.1.1.1.1.3.1.1.1.1.1.1.2.1.4.14.r.1.1.1.1.1.1.4.1.9.1.4.1.5.2q.6.4.1.1.j.1.16.1.7.1.3.1.1.3.1.5.3.1.3.1.13.m.6.1.1.1.1.1.4.1.1.j.9.6.3.1.27.6.6.1.i.17.8.5.1.3.1.1.4.6.1.1.1r.1.2.1.4.1.4.2.j.1.d.5.1.1.1.g.1.1.1.3.1i.1.5.7.i.17.8.7.1.1.1.1.4.6.2.1q.1.1.1.1.5.1.4.2.1.i.1.f.h.5.1.1.1.3.1.1i.1.4.7.i.16.1.8.6.1.3.1.4.4.1.1.2.1q.1.2.1.5.1.4.3.i.1.b.m.1.1.2.2.1.1.1.2.1j.1.3.8.1.g.s.e.9.5.1.1.1.1.1.1.3.1.3.1.1.3.8.1g.1.1.a.1.1.1.1.3.1.3.e.1.8.1.1.1.1.l.2.1.4.1.1.1.1.2.1i.1.2.9.1.f.16.1.1.1.1.1.1.3.1.1.2.1.4.2.1.1.1.3.1.5.8.1g.1.d.1.1.4.i.1.7.1.1.2.4.9.b.1.5.1.1.3.1i.1.1.a.e.1.16.1.5.1.1.1.1.1.1.1.5.3.6.4.1.1o.f.5.i.1.7.1.1.1.1.3.1.3.9.1.e.1.1.1l.1.1.6.1.1.1.e.1.17.7.4.6.2.1.6.4.1o.1.k.1.d.5.1.6.1.1.2.3.1.d.1.a.1.3.1m.1.1.5.1.2.f.9.1.y.1.6.4.1.5.1.1.7.4.7.1.4.19.1.1.1.m.1.c.1.2.2.1.a.3.d.h.2.1j.1.1.5.3.e.1.9.10.6.5.1.3.1.1.1.3.4.4.5.1.1.1c.1.1.q.1.c.1.1.1.f.e.f.1.2.1.1i.2.1.1.1.1.1.3.1.1.c.1a.5.1.6.1.1.1.3.3.1.2.1.3.1.1i.1.s.1.1.c.1.1.3.v.1.d.1i.2.1.2.2.5.9.1.1.1.1a.1.4.6.1.1.1.1.3.4.2.1.3.1j.1.t.1.d.1.1.2.x.2.1s.2.1.4.1.1.3.1.7.1.1.1c.1.4.6.4.3.7.3.1j.v.1.1t.2.3.19.2.7.1.3.6.1.1f.4.5.1.4.2.1.7.2.1.1i.1.w.1.36.2.8.1.2.1.4.1.1g.4.1.1.1.1.1.4.1.1.9.1.1.1j.1.w.1.2d.l.8.2.8.2.1.1.4.1h.4.1.1.7.1.1.1u.1.x.1h.t.v.1.1.1.8.1.1.1.1.4.1h.1.3.9.1.1.1v.x.35.2.b.1.5.1i.1.1.1.7.1.2.1.1v.1.w.p.2g.1.1.a.1.5.1.1i.1.1.1.6.1.1.1.1.1x.w.c.2t.1.1.9.1.1.4.1.1k.8.1.1.1.1y.w.o.2h.1.a.2.1.2.1.1.1k.1.8.1.1.1y.1.b.1.1.h.1.5.30.1.a.3.1.1.1.1m.8.1.20.8.1.1.1.1.1.1.1.3.c.36.1.1.9.4.1.1m.1.7.1.20.1.6.1.1.7.1.2.b.1.30.6.1.1.9.4.1o.2.1.1.1.1.1.21.4.1.1.1.9.1.1.1.b.h.2p.1.1.a.1.1.1.1.1o.1.1.1.25.1.1.1.1.d.1.1.1.9.1.i.1.2n.2.b.4h.1.1.1.8.j.1.1.2l.1.1.1.b.11.3i.1.8.k.1.1.2k.1.1.1.b.11.23.1g.1.2.1.2.1.1.l.1.1.1.2i.1.1.1.7.1.1.1.1.34.1h.1.1.1.1.1.m.1.2.1.2i.1.1.8.4r.2.1.1.l.1.1.1.2j.1.1.5.1.2.17.3k.1.2.1.j.1.1.1.1.2k.1.1.5.1.1.1.4s.1.1.1.j.1.1.1.1.2k.1.1.5.1.1.4t.2.1.j.2.2l.1.1.1.5.1.1.4t.1.1.1.h.1.1.1.1.9.2c.1.1.1.5.4w.i.1.2.1.2m.1.1.1.4.1.5d.1.2.1.2n.1.1.1.3.1.2p.1.1.2n.1.2.2o.1.2.3.1.1.2e.b.1.2n.1.1.1.2o.1.1.1.4.1.5d.a.2h.1.1.1.3.1.34.1.1.4z.1.1.3.1.36.1.2i.2h.1.1.3.1.5e.1.2r.1.1.1.2.7.1.1.1.7x.1.1.1.1.1.1.6.1.1.59.2q.1.1.1.1.1.39.1.4x.1.1.1.1.1.1.1.1.1.l.1.1.w.3l.1.30.1.1.1.1.1.1.1.m.1.4h.34.1.x.8c.8c.1'; + +/** Decoded grid cells as [gx, gy] pairs. */ +export function worldDots(): { x: number; y: number }[] { + const out: { x: number; y: number }[] = []; + let cursor = 0; + for (const part of PACKED.split('.')) { + cursor += parseInt(part, 36); + out.push({ x: cursor % WORLD_GRID_W, y: Math.floor(cursor / WORLD_GRID_W) }); + } + return out; +} diff --git a/shared/src/i18n/ar/login.ts b/shared/src/i18n/ar/login.ts index 83808f2e9..38655a091 100644 --- a/shared/src/i18n/ar/login.ts +++ b/shared/src/i18n/ar/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'فشل تسجيل الدخول. يرجى التحقق من بياناتك.', 'login.tagline': 'رحلاتك.\nخطتك.', 'login.description': 'خطط لرحلاتك بشكل تعاوني مع خرائط تفاعلية وميزانيات ومزامنة لحظية.', - 'login.features.maps': 'خرائط تفاعلية', - 'login.features.mapsDesc': 'Google Places ومسارات وتجميع', - 'login.features.realtime': 'مزامنة فورية', - 'login.features.realtimeDesc': 'خططوا معًا عبر WebSocket', - 'login.features.budget': 'تتبع التكاليف', - 'login.features.budgetDesc': 'فئات ورسوم وتقسيم لكل شخص', - 'login.features.collab': 'تعاون', - 'login.features.collabDesc': 'عدة مستخدمين مع رحلات مشتركة', - 'login.features.packing': 'قوائم تجهيز', - 'login.features.packingDesc': 'فئات وتقدم واقتراحات', - 'login.features.bookings': 'الحجوزات', - 'login.features.bookingsDesc': 'رحلات وفنادق ومطاعم وغير ذلك', - 'login.features.files': 'المستندات', - 'login.features.filesDesc': 'رفع الملفات وإدارتها', - 'login.features.routes': 'مسارات ذكية', - 'login.features.routesDesc': 'تحسين تلقائي وتصدير إلى Google Maps', - 'login.selfHosted': 'استضافة ذاتية · مفتوح المصدر · بياناتك تبقى ملكك', 'login.title': 'تسجيل الدخول', 'login.subtitle': 'مرحبًا بعودتك', 'login.signingIn': 'جارٍ تسجيل الدخول…', diff --git a/shared/src/i18n/ar/mobileVacay.ts b/shared/src/i18n/ar/mobileVacay.ts index d232bd0c3..2ac769367 100644 --- a/shared/src/i18n/ar/mobileVacay.ts +++ b/shared/src/i18n/ar/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'الشهر التالي', 'mobileVacay.usedCount': '{count} مستخدَمة', 'mobileVacay.leftCount': '{count} متبقية', - 'mobileVacay.daysPerYear': 'يوم / سنة', + 'mobileVacay.left': 'متبقية', + 'mobileVacay.modeCompany': 'الشركة', 'mobileVacay.decreaseAllowance': 'تقليل الرصيد', 'mobileVacay.increaseAllowance': 'زيادة الرصيد', 'mobileVacay.companyLegend': 'أيام الشركة', diff --git a/shared/src/i18n/br/login.ts b/shared/src/i18n/br/login.ts index 2a61c454b..194cac1ed 100644 --- a/shared/src/i18n/br/login.ts +++ b/shared/src/i18n/br/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Falha no login. Verifique suas credenciais.', 'login.tagline': 'Suas viagens.\nSeu plano.', 'login.description': 'Planeje viagens em equipe com mapas interativos, orçamento e sincronização em tempo real.', - 'login.features.maps': 'Mapas interativos', - 'login.features.mapsDesc': 'Google Places, rotas e agrupamento', - 'login.features.realtime': 'Sincronização em tempo real', - 'login.features.realtimeDesc': 'Planejem juntos via WebSocket', - 'login.features.budget': 'Controle de custos', - 'login.features.budgetDesc': 'Categorias, gráficos e custo por pessoa', - 'login.features.collab': 'Colaboração', - 'login.features.collabDesc': 'Vários usuários com viagens compartilhadas', - 'login.features.packing': 'Listas de malas', - 'login.features.packingDesc': 'Categorias, progresso e sugestões', - 'login.features.bookings': 'Reservas', - 'login.features.bookingsDesc': 'Voos, hotéis, restaurantes e mais', - 'login.features.files': 'Documentos', - 'login.features.filesDesc': 'Envie e gerencie documentos', - 'login.features.routes': 'Rotas inteligentes', - 'login.features.routesDesc': 'Otimize e exporte para o Google Maps', - 'login.selfHosted': 'Auto-hospedado · Código aberto · Seus dados são seus', 'login.title': 'Entrar', 'login.subtitle': 'Bem-vindo de volta', 'login.signingIn': 'Entrando…', diff --git a/shared/src/i18n/br/mobileVacay.ts b/shared/src/i18n/br/mobileVacay.ts index c97acf641..e35bdd9fa 100644 --- a/shared/src/i18n/br/mobileVacay.ts +++ b/shared/src/i18n/br/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Próximo mês', 'mobileVacay.usedCount': '{count} usados', 'mobileVacay.leftCount': '{count} restantes', - 'mobileVacay.daysPerYear': 'dias / ano', + 'mobileVacay.left': 'restantes', + 'mobileVacay.modeCompany': 'Empresa', 'mobileVacay.decreaseAllowance': 'Diminuir cota', 'mobileVacay.increaseAllowance': 'Aumentar cota', 'mobileVacay.companyLegend': 'Férias coletivas', diff --git a/shared/src/i18n/ca/login.ts b/shared/src/i18n/ca/login.ts index 3aabfa8b4..78bda4729 100644 --- a/shared/src/i18n/ca/login.ts +++ b/shared/src/i18n/ca/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Els teus viatges.\nEl teu pla.', 'login.description': 'Planifica viatges en col·laboració amb mapes interactius, pressupostos i sincronització en temps real.', - 'login.features.maps': 'Mapes interactius', - 'login.features.mapsDesc': 'Google Places, rutes i agrupació', - 'login.features.realtime': 'Sincronització en temps real', - 'login.features.realtimeDesc': 'Planifiqueu junts mitjançant WebSocket', - 'login.features.budget': 'Control de pressupost', - 'login.features.budgetDesc': 'Categories, gràfics i costos per persona', - 'login.features.collab': 'Col·laboració', - 'login.features.collabDesc': 'Multiusuari amb viatges compartits', - 'login.features.packing': "Llistes d'equipatge", - 'login.features.packingDesc': 'Categories, progrés i suggeriments', - 'login.features.bookings': 'Reserves', - 'login.features.bookingsDesc': 'Vols, hotels, restaurants i més', - 'login.features.files': 'Documents', - 'login.features.filesDesc': 'Puja i gestiona documents', - 'login.features.routes': 'Rutes intel·ligents', - 'login.features.routesDesc': 'Optimització automàtica i exportació a Google Maps', - 'login.selfHosted': 'Autoallotjat · Codi obert · Les teves dades segueixen sent teves', 'login.title': 'Inicia la sessió', 'login.subtitle': 'Benvingut de nou', 'login.signingIn': 'Iniciant la sessió…', diff --git a/shared/src/i18n/ca/mobileVacay.ts b/shared/src/i18n/ca/mobileVacay.ts index f8d6eda3c..8b11c010e 100644 --- a/shared/src/i18n/ca/mobileVacay.ts +++ b/shared/src/i18n/ca/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Mes següent', 'mobileVacay.usedCount': '{count} usats', 'mobileVacay.leftCount': '{count} restants', - 'mobileVacay.daysPerYear': 'dies / any', + 'mobileVacay.left': 'restants', + 'mobileVacay.modeCompany': 'Empresa', 'mobileVacay.decreaseAllowance': "Redueix l'assignació", 'mobileVacay.increaseAllowance': "Augmenta l'assignació", 'mobileVacay.companyLegend': "Dies d'empresa", diff --git a/shared/src/i18n/cs/login.ts b/shared/src/i18n/cs/login.ts index 97518ac52..e7c8a5fa8 100644 --- a/shared/src/i18n/cs/login.ts +++ b/shared/src/i18n/cs/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Přihlášení se nezdařilo. Zkontrolujte prosím své údaje.', 'login.tagline': 'Vaše cesty.\nVáš plán.', 'login.description': 'Plánujte cesty společně s interaktivními mapami, rozpočty a synchronizací v reálném čase.', - 'login.features.maps': 'Interaktivní mapy', - 'login.features.mapsDesc': 'Google Places, trasy a shlukování bodů', - 'login.features.realtime': 'Synchronizace v reálném čase', - 'login.features.realtimeDesc': 'Plánujte společně přes WebSocket', - 'login.features.budget': 'Sledování nákladů', - 'login.features.budgetDesc': 'Kategorie, grafy a náklady na osobu', - 'login.features.collab': 'Spolupráce', - 'login.features.collabDesc': 'Více uživatelů se sdílenými cestami', - 'login.features.packing': 'Seznamy věcí', - 'login.features.packingDesc': 'Kategorie, pokrok v balení a návrhy', - 'login.features.bookings': 'Rezervace', - 'login.features.bookingsDesc': 'Lety, hotely, restaurace a další', - 'login.features.files': 'Dokumenty', - 'login.features.filesDesc': 'Nahrávejte a spravujte dokumenty', - 'login.features.routes': 'Chytré trasy', - 'login.features.routesDesc': 'Automatická optimalizace a export do Google Maps', - 'login.selfHosted': 'Self-hosted · Open Source · Vaše data zůstávají u vás', 'login.title': 'Přihlásit se', 'login.subtitle': 'Vítejte zpět', 'login.signingIn': 'Přihlašování…', diff --git a/shared/src/i18n/cs/mobileVacay.ts b/shared/src/i18n/cs/mobileVacay.ts index f2e377d41..d1d94ec9e 100644 --- a/shared/src/i18n/cs/mobileVacay.ts +++ b/shared/src/i18n/cs/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Další měsíc', 'mobileVacay.usedCount': '{count} využito', 'mobileVacay.leftCount': 'Zbývá {count}', - 'mobileVacay.daysPerYear': 'dní / rok', + 'mobileVacay.left': 'zbývá', + 'mobileVacay.modeCompany': 'Firma', 'mobileVacay.decreaseAllowance': 'Snížit nárok', 'mobileVacay.increaseAllowance': 'Zvýšit nárok', 'mobileVacay.companyLegend': 'Firemní dny', diff --git a/shared/src/i18n/de/login.ts b/shared/src/i18n/de/login.ts index 4c43c8551..1ca952f97 100644 --- a/shared/src/i18n/de/login.ts +++ b/shared/src/i18n/de/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen.', 'login.tagline': 'Deine Reisen.\nDein Plan.', 'login.description': 'Plane Reisen gemeinsam mit interaktiven Karten, Budgets und Echtzeit-Sync.', - 'login.features.maps': 'Interaktive Karten', - 'login.features.mapsDesc': 'Google Places, Routen & Clustering', - 'login.features.realtime': 'Echtzeit-Sync', - 'login.features.realtimeDesc': 'Gemeinsam planen via WebSocket', - 'login.features.budget': 'Kosten-Tracking', - 'login.features.budgetDesc': 'Kategorien, Diagramme & Pro-Kopf', - 'login.features.collab': 'Zusammenarbeit', - 'login.features.collabDesc': 'Multi-User mit geteilten Reisen', - 'login.features.packing': 'Packlisten', - 'login.features.packingDesc': 'Kategorien & Fortschritt', - 'login.features.bookings': 'Buchungen', - 'login.features.bookingsDesc': 'Flüge, Hotels, Restaurants & mehr', - 'login.features.files': 'Dokumente', - 'login.features.filesDesc': 'Dateien hochladen & verwalten', - 'login.features.routes': 'Routenoptimierung', - 'login.features.routesDesc': 'Auto-Optimierung & Google Maps Export', - 'login.selfHosted': 'Self-hosted · Open Source · Deine Daten bleiben bei dir', 'login.title': 'Anmelden', 'login.subtitle': 'Willkommen zurück', 'login.signingIn': 'Anmelden…', diff --git a/shared/src/i18n/de/mobileVacay.ts b/shared/src/i18n/de/mobileVacay.ts index 3b37b3349..c0aaabe40 100644 --- a/shared/src/i18n/de/mobileVacay.ts +++ b/shared/src/i18n/de/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Nächster Monat', 'mobileVacay.usedCount': '{count} genommen', 'mobileVacay.leftCount': '{count} übrig', - 'mobileVacay.daysPerYear': 'Tage / Jahr', + 'mobileVacay.left': 'übrig', + 'mobileVacay.modeCompany': 'Betrieb', 'mobileVacay.decreaseAllowance': 'Anspruch verringern', 'mobileVacay.increaseAllowance': 'Anspruch erhöhen', 'mobileVacay.companyLegend': 'Betriebsferien', diff --git a/shared/src/i18n/en/login.ts b/shared/src/i18n/en/login.ts index f3631ea40..926eb1eb8 100644 --- a/shared/src/i18n/en/login.ts +++ b/shared/src/i18n/en/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Login failed. Please check your credentials.', 'login.tagline': 'Your Trips.\nYour Plan.', 'login.description': 'Plan trips collaboratively with interactive maps, budgets, and real-time sync.', - 'login.features.maps': 'Interactive Maps', - 'login.features.mapsDesc': 'Google Places, routes & clustering', - 'login.features.realtime': 'Real-Time Sync', - 'login.features.realtimeDesc': 'Plan together via WebSocket', - 'login.features.budget': 'Cost Tracking', - 'login.features.budgetDesc': 'Categories, charts & per-person costs', - 'login.features.collab': 'Collaboration', - 'login.features.collabDesc': 'Multi-user with shared trips', - 'login.features.packing': 'Packing Lists', - 'login.features.packingDesc': 'Categories, progress & suggestions', - 'login.features.bookings': 'Reservations', - 'login.features.bookingsDesc': 'Flights, hotels, restaurants & more', - 'login.features.files': 'Documents', - 'login.features.filesDesc': 'Upload & manage documents', - 'login.features.routes': 'Smart Routes', - 'login.features.routesDesc': 'Auto-optimize & Google Maps export', - 'login.selfHosted': 'Self-hosted · Open Source · Your data stays yours', 'login.title': 'Sign In', 'login.subtitle': 'Welcome back', 'login.signingIn': 'Signing in…', diff --git a/shared/src/i18n/en/mobileVacay.ts b/shared/src/i18n/en/mobileVacay.ts index 797300603..5702daf5f 100644 --- a/shared/src/i18n/en/mobileVacay.ts +++ b/shared/src/i18n/en/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Next month', 'mobileVacay.usedCount': '{count} used', 'mobileVacay.leftCount': '{count} left', - 'mobileVacay.daysPerYear': 'days / year', + 'mobileVacay.left': 'left', + 'mobileVacay.modeCompany': 'Company', 'mobileVacay.decreaseAllowance': 'Decrease allowance', 'mobileVacay.increaseAllowance': 'Increase allowance', 'mobileVacay.companyLegend': 'Company days', diff --git a/shared/src/i18n/es/login.ts b/shared/src/i18n/es/login.ts index f6992f17d..de96659c6 100644 --- a/shared/src/i18n/es/login.ts +++ b/shared/src/i18n/es/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Tus viajes.\nTu plan.', 'login.description': 'Planifica viajes en colaboración con mapas interactivos, presupuestos y sincronización en tiempo real.', - 'login.features.maps': 'Mapas interactivos', - 'login.features.mapsDesc': 'Google Places, rutas y agrupación', - 'login.features.realtime': 'Sincronización en tiempo real', - 'login.features.realtimeDesc': 'Planificad juntos mediante WebSocket', - 'login.features.budget': 'Control de costes', - 'login.features.budgetDesc': 'Categorías, gráficos y costes por persona', - 'login.features.collab': 'Colaboración', - 'login.features.collabDesc': 'Multiusuario con viajes compartidos', - 'login.features.packing': 'Listas de equipaje', - 'login.features.packingDesc': 'Categorías, progreso y sugerencias', - 'login.features.bookings': 'Reservas', - 'login.features.bookingsDesc': 'Vuelos, hoteles, restaurantes y más', - 'login.features.files': 'Documentos', - 'login.features.filesDesc': 'Sube y gestiona documentos', - 'login.features.routes': 'Rutas inteligentes', - 'login.features.routesDesc': 'Optimización automática y exportación a Google Maps', - 'login.selfHosted': 'Autoalojado · Código abierto · Tus datos siguen siendo tuyos', 'login.title': 'Iniciar sesión', 'login.subtitle': 'Bienvenido de nuevo', 'login.signingIn': 'Iniciando sesión…', diff --git a/shared/src/i18n/es/mobileVacay.ts b/shared/src/i18n/es/mobileVacay.ts index 3a331b4da..e0a4f3343 100644 --- a/shared/src/i18n/es/mobileVacay.ts +++ b/shared/src/i18n/es/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Mes siguiente', 'mobileVacay.usedCount': '{count} usados', 'mobileVacay.leftCount': '{count} restantes', - 'mobileVacay.daysPerYear': 'días / año', + 'mobileVacay.left': 'restantes', + 'mobileVacay.modeCompany': 'Empresa', 'mobileVacay.decreaseAllowance': 'Reducir asignación', 'mobileVacay.increaseAllowance': 'Aumentar asignación', 'mobileVacay.companyLegend': 'Días de empresa', diff --git a/shared/src/i18n/fr/login.ts b/shared/src/i18n/fr/login.ts index c8a907089..90f43a243 100644 --- a/shared/src/i18n/fr/login.ts +++ b/shared/src/i18n/fr/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Vos voyages.\nVotre organisation.', 'login.description': 'Planifiez vos voyages en collaboration avec des cartes interactives, des budgets et la synchronisation en temps réel.', - 'login.features.maps': 'Cartes interactives', - 'login.features.mapsDesc': 'Google Places, itinéraires et regroupement', - 'login.features.realtime': 'Synchronisation en temps réel', - 'login.features.realtimeDesc': 'Planifiez ensemble via WebSocket', - 'login.features.budget': 'Suivi des coûts', - 'login.features.budgetDesc': 'Catégories, graphiques et coûts par personne', - 'login.features.collab': 'Collaboration', - 'login.features.collabDesc': 'Multi-utilisateurs avec voyages partagés', - 'login.features.packing': 'Listes de bagages', - 'login.features.packingDesc': 'Catégories, progression et suggestions', - 'login.features.bookings': 'Réservations', - 'login.features.bookingsDesc': 'Vols, hôtels, restaurants et plus', - 'login.features.files': 'Documents', - 'login.features.filesDesc': 'Importez et gérez vos documents', - 'login.features.routes': 'Itinéraires intelligents', - 'login.features.routesDesc': 'Optimisation automatique et export Google Maps', - 'login.selfHosted': 'Auto-hébergé · Open Source · Vos données restent les vôtres', 'login.title': 'Connexion', 'login.subtitle': 'Bon retour', 'login.signingIn': 'Connexion en cours…', diff --git a/shared/src/i18n/fr/mobileVacay.ts b/shared/src/i18n/fr/mobileVacay.ts index ab38484a5..ade2173c5 100644 --- a/shared/src/i18n/fr/mobileVacay.ts +++ b/shared/src/i18n/fr/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Mois suivant', 'mobileVacay.usedCount': '{count} pris', 'mobileVacay.leftCount': '{count} restants', - 'mobileVacay.daysPerYear': 'jours / an', + 'mobileVacay.left': 'restants', + 'mobileVacay.modeCompany': 'Entreprise', 'mobileVacay.decreaseAllowance': 'Diminuer le quota', 'mobileVacay.increaseAllowance': 'Augmenter le quota', 'mobileVacay.companyLegend': 'Congés d\'entreprise', diff --git a/shared/src/i18n/gr/login.ts b/shared/src/i18n/gr/login.ts index 0f003c3cd..129f33be1 100644 --- a/shared/src/i18n/gr/login.ts +++ b/shared/src/i18n/gr/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Τα Ταξίδια σας.\nΤο Σχέδιό σας.', 'login.description': 'Σχεδιάστε ταξίδια συνεργατικά με διαδραστικούς χάρτες, προϋπολογισμούς και συγχρονισμό σε πραγματικό χρόνο.', - 'login.features.maps': 'Διαδραστικοί Χάρτες', - 'login.features.mapsDesc': 'Google Places, διαδρομές & ομαδοποίηση', - 'login.features.realtime': 'Συγχρονισμός Πραγματικού Χρόνου', - 'login.features.realtimeDesc': 'Σχεδιάστε μαζί μέσω WebSocket', - 'login.features.budget': 'Παρακολούθηση Κόστους', - 'login.features.budgetDesc': 'Κατηγορίες, γραφήματα & κόστος ανά άτομο', - 'login.features.collab': 'Συνεργασία', - 'login.features.collabDesc': 'Πολλαπλοί χρήστες με κοινόχρηστα ταξίδια', - 'login.features.packing': 'Λίστες Πακεταρίσματος', - 'login.features.packingDesc': 'Κατηγορίες, πρόοδος & προτάσεις', - 'login.features.bookings': 'Κρατήσεις', - 'login.features.bookingsDesc': 'Πτήσεις, ξενοδοχεία, εστιατόρια & άλλα', - 'login.features.files': 'Έγγραφα', - 'login.features.filesDesc': 'Μεταφόρτωση & διαχείριση εγγράφων', - 'login.features.routes': 'Έξυπνες Διαδρομές', - 'login.features.routesDesc': 'Αυτόματη βελτιστοποίηση & εξαγωγή στο Google Maps', - 'login.selfHosted': 'Self-hosted \u00B7 Open Source \u00B7 Τα δεδομένα σας παραμένουν δικά σας', 'login.title': 'Σύνδεση', 'login.subtitle': 'Καλώς ήρθατε πίσω', 'login.signingIn': 'Σύνδεση…', diff --git a/shared/src/i18n/gr/mobileVacay.ts b/shared/src/i18n/gr/mobileVacay.ts index 2f08844f1..f806593b2 100644 --- a/shared/src/i18n/gr/mobileVacay.ts +++ b/shared/src/i18n/gr/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Επόμενος μήνας', 'mobileVacay.usedCount': '{count} σε χρήση', 'mobileVacay.leftCount': '{count} απομένουν', - 'mobileVacay.daysPerYear': 'ημέρες / έτος', + 'mobileVacay.left': 'απομένουν', + 'mobileVacay.modeCompany': 'Εταιρεία', 'mobileVacay.decreaseAllowance': 'Μείωση δικαιώματος', 'mobileVacay.increaseAllowance': 'Αύξηση δικαιώματος', 'mobileVacay.companyLegend': 'Άδεια εταιρείας', diff --git a/shared/src/i18n/hu/login.ts b/shared/src/i18n/hu/login.ts index 7075050f4..1a69db77f 100644 --- a/shared/src/i18n/hu/login.ts +++ b/shared/src/i18n/hu/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Az utazásaid.\nA terved.', 'login.description': 'Tervezz utazásokat közösen interaktív térképekkel, költségvetéssel és valós idejű szinkronizálással.', - 'login.features.maps': 'Interaktív térképek', - 'login.features.mapsDesc': 'Google Places, útvonalak és csoportosítás', - 'login.features.realtime': 'Valós idejű szinkron', - 'login.features.realtimeDesc': 'Közös tervezés WebSocket-en keresztül', - 'login.features.budget': 'Költségkövetés', - 'login.features.budgetDesc': 'Kategóriák, diagramok és személyenkénti költségek', - 'login.features.collab': 'Együttműködés', - 'login.features.collabDesc': 'Többfelhasználós, megosztott utazásokkal', - 'login.features.packing': 'Csomagolási listák', - 'login.features.packingDesc': 'Kategóriák és haladás', - 'login.features.bookings': 'Foglalások', - 'login.features.bookingsDesc': 'Repülők, szállodák, éttermek és még több', - 'login.features.files': 'Dokumentumok', - 'login.features.filesDesc': 'Fájlok feltöltése és kezelése', - 'login.features.routes': 'Útvonal-optimalizálás', - 'login.features.routesDesc': 'Automatikus optimalizálás és Google Maps export', - 'login.selfHosted': 'Saját üzemeltetés · Nyílt forráskód · Az adataid nálad maradnak', 'login.title': 'Bejelentkezés', 'login.subtitle': 'Üdv újra', 'login.signingIn': 'Bejelentkezés…', diff --git a/shared/src/i18n/hu/mobileVacay.ts b/shared/src/i18n/hu/mobileVacay.ts index 09038cc6e..7d4d044d0 100644 --- a/shared/src/i18n/hu/mobileVacay.ts +++ b/shared/src/i18n/hu/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Következő hónap', 'mobileVacay.usedCount': '{count} felhasználva', 'mobileVacay.leftCount': '{count} maradt', - 'mobileVacay.daysPerYear': 'nap / év', + 'mobileVacay.left': 'maradt', + 'mobileVacay.modeCompany': 'Céges', 'mobileVacay.decreaseAllowance': 'Keret csökkentése', 'mobileVacay.increaseAllowance': 'Keret növelése', 'mobileVacay.companyLegend': 'Céges szabadság', diff --git a/shared/src/i18n/id/login.ts b/shared/src/i18n/id/login.ts index 692eda8d4..86ca7ce42 100644 --- a/shared/src/i18n/id/login.ts +++ b/shared/src/i18n/id/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Login gagal. Periksa kembali kredensial kamu.', 'login.tagline': 'Perjalananmu.\nRencanamu.', 'login.description': 'Rencanakan perjalanan bersama dengan peta interaktif, anggaran, dan sinkronisasi real-time.', - 'login.features.maps': 'Peta Interaktif', - 'login.features.mapsDesc': 'Google Places, rute & pengelompokan', - 'login.features.realtime': 'Sinkronisasi Real-Time', - 'login.features.realtimeDesc': 'Rencanakan bersama via WebSocket', - 'login.features.budget': 'Pelacak Biaya', - 'login.features.budgetDesc': 'Kategori, grafik & biaya per orang', - 'login.features.collab': 'Kolaborasi', - 'login.features.collabDesc': 'Multi-pengguna dengan perjalanan bersama', - 'login.features.packing': 'Daftar Packing', - 'login.features.packingDesc': 'Kategori, progres & saran', - 'login.features.bookings': 'Reservasi', - 'login.features.bookingsDesc': 'Penerbangan, hotel, restoran & lainnya', - 'login.features.files': 'Dokumen', - 'login.features.filesDesc': 'Unggah & kelola dokumen', - 'login.features.routes': 'Rute Cerdas', - 'login.features.routesDesc': 'Optimasi otomatis & ekspor ke Google Maps', - 'login.selfHosted': 'Self-hosted · Open Source · Datamu tetap milikmu', 'login.title': 'Masuk', 'login.subtitle': 'Selamat datang kembali', 'login.signingIn': 'Sedang masuk…', diff --git a/shared/src/i18n/id/mobileVacay.ts b/shared/src/i18n/id/mobileVacay.ts index 1acb914ed..42e2bc676 100644 --- a/shared/src/i18n/id/mobileVacay.ts +++ b/shared/src/i18n/id/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Bulan berikutnya', 'mobileVacay.usedCount': '{count} terpakai', 'mobileVacay.leftCount': '{count} tersisa', - 'mobileVacay.daysPerYear': 'hari / tahun', + 'mobileVacay.left': 'tersisa', + 'mobileVacay.modeCompany': 'Perusahaan', 'mobileVacay.decreaseAllowance': 'Kurangi jatah', 'mobileVacay.increaseAllowance': 'Tambah jatah', 'mobileVacay.companyLegend': 'Libur perusahaan', diff --git a/shared/src/i18n/it/login.ts b/shared/src/i18n/it/login.ts index 8a6ce0bd5..1804993f9 100644 --- a/shared/src/i18n/it/login.ts +++ b/shared/src/i18n/it/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'I tuoi viaggi.\nIl tuo programma.', 'login.description': 'Programma viaggi in collaborazione con mappe interattive, budget e sincronizzazione in tempo reale.', - 'login.features.maps': 'Mappe Interattive', - 'login.features.mapsDesc': 'Google Places, percorsi e clustering', - 'login.features.realtime': 'Sincronizzazione in tempo reale', - 'login.features.realtimeDesc': 'Programmate insieme tramite WebSocket', - 'login.features.budget': 'Tracciamento costi', - 'login.features.budgetDesc': 'Categorie, grafici e costi per persona', - 'login.features.collab': 'Collaborazione', - 'login.features.collabDesc': 'Multi-utente con viaggi condivisi', - 'login.features.packing': 'Lista Valigia', - 'login.features.packingDesc': 'Categorie, progressi e suggerimenti', - 'login.features.bookings': 'Prenotazioni', - 'login.features.bookingsDesc': 'Voli, alloggi, ristoranti e altro', - 'login.features.files': 'Documenti', - 'login.features.filesDesc': 'Carica e gestisci i documenti', - 'login.features.routes': 'Percorsi Intelligenti', - 'login.features.routesDesc': 'Ottimizzazione automatica ed esportazione su Google Maps', - 'login.selfHosted': 'Self-hosted · Open Source · Your data stays yours', 'login.title': 'Accedi', 'login.subtitle': 'Bentornato', 'login.signingIn': 'Accesso in corso…', diff --git a/shared/src/i18n/it/mobileVacay.ts b/shared/src/i18n/it/mobileVacay.ts index 720500eaa..d1dda1431 100644 --- a/shared/src/i18n/it/mobileVacay.ts +++ b/shared/src/i18n/it/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Mese successivo', 'mobileVacay.usedCount': '{count} usati', 'mobileVacay.leftCount': '{count} rimanenti', - 'mobileVacay.daysPerYear': 'giorni / anno', + 'mobileVacay.left': 'rimanenti', + 'mobileVacay.modeCompany': 'Azienda', 'mobileVacay.decreaseAllowance': 'Riduci giorni disponibili', 'mobileVacay.increaseAllowance': 'Aumenta giorni disponibili', 'mobileVacay.companyLegend': 'Ferie aziendali', diff --git a/shared/src/i18n/ja/login.ts b/shared/src/i18n/ja/login.ts index 238050912..6db123a19 100644 --- a/shared/src/i18n/ja/login.ts +++ b/shared/src/i18n/ja/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'ログインに失敗しました。認証情報を確認してください。', 'login.tagline': 'あなたの旅行。\nあなたの計画。', 'login.description': 'インタラクティブなマップ、予算、リアルタイム同期で、みんなで旅行を計画。', - 'login.features.maps': 'インタラクティブマップ', - 'login.features.mapsDesc': 'Google Places、経路、クラスタリング', - 'login.features.realtime': 'リアルタイム同期', - 'login.features.realtimeDesc': 'WebSocketで共同計画', - 'login.features.budget': '費用管理', - 'login.features.budgetDesc': 'カテゴリ、グラフ、人数別費用', - 'login.features.collab': 'コラボレーション', - 'login.features.collabDesc': '複数ユーザーで旅行を共有', - 'login.features.packing': '持ち物リスト', - 'login.features.packingDesc': 'カテゴリ、進捗、提案', - 'login.features.bookings': '予約', - 'login.features.bookingsDesc': '航空券、ホテル、レストランなど', - 'login.features.files': 'ドキュメント', - 'login.features.filesDesc': '書類のアップロードと管理', - 'login.features.routes': 'スマート経路', - 'login.features.routesDesc': '自動最適化&Google Maps書き出し', - 'login.selfHosted': 'セルフホスト · オープンソース · データはあなたのもの', 'login.title': 'サインイン', 'login.subtitle': 'おかえりなさい', 'login.signingIn': 'サインイン中…', diff --git a/shared/src/i18n/ja/mobileVacay.ts b/shared/src/i18n/ja/mobileVacay.ts index 634081423..62d5d1732 100644 --- a/shared/src/i18n/ja/mobileVacay.ts +++ b/shared/src/i18n/ja/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': '次の月', 'mobileVacay.usedCount': '{count} 日取得', 'mobileVacay.leftCount': '残り {count} 日', - 'mobileVacay.daysPerYear': '日 / 年', + 'mobileVacay.left': '残り', + 'mobileVacay.modeCompany': '会社', 'mobileVacay.decreaseAllowance': '付与日数を減らす', 'mobileVacay.increaseAllowance': '付与日数を増やす', 'mobileVacay.companyLegend': '会社の休業日', diff --git a/shared/src/i18n/ko/login.ts b/shared/src/i18n/ko/login.ts index e2ed8cf42..45cf3685a 100644 --- a/shared/src/i18n/ko/login.ts +++ b/shared/src/i18n/ko/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': '로그인 실패. 자격 증명을 확인하세요.', 'login.tagline': '나의 여행.\n나의 계획.', 'login.description': '인터랙티브 지도, 예산, 실시간 동기화로 함께 여행을 계획하세요.', - 'login.features.maps': '인터랙티브 지도', - 'login.features.mapsDesc': 'Google Places, 경로 및 클러스터링', - 'login.features.realtime': '실시간 동기화', - 'login.features.realtimeDesc': 'WebSocket으로 함께 계획', - 'login.features.budget': '비용 추적', - 'login.features.budgetDesc': '카테고리, 차트 및 1인당 비용', - 'login.features.collab': '협업', - 'login.features.collabDesc': '공유 여행으로 다중 사용자 지원', - 'login.features.packing': '짐 목록', - 'login.features.packingDesc': '카테고리, 진행 상황 및 제안', - 'login.features.bookings': '예약', - 'login.features.bookingsDesc': '항공, 호텔, 레스토랑 등', - 'login.features.files': '문서', - 'login.features.filesDesc': '문서 업로드 및 관리', - 'login.features.routes': '스마트 경로', - 'login.features.routesDesc': '자동 최적화 및 Google Maps 내보내기', - 'login.selfHosted': '자체 호스팅 · 오픈 소스 · 내 데이터는 내 것', 'login.title': '로그인', 'login.subtitle': '다시 오신 것을 환영합니다', 'login.signingIn': '로그인 중…', diff --git a/shared/src/i18n/ko/mobileVacay.ts b/shared/src/i18n/ko/mobileVacay.ts index b4ccb2008..6a48063a0 100644 --- a/shared/src/i18n/ko/mobileVacay.ts +++ b/shared/src/i18n/ko/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': '다음 달', 'mobileVacay.usedCount': '{count}일 사용', 'mobileVacay.leftCount': '{count}일 남음', - 'mobileVacay.daysPerYear': '일 / 년', + 'mobileVacay.left': '남음', + 'mobileVacay.modeCompany': '회사', 'mobileVacay.decreaseAllowance': '할당 줄이기', 'mobileVacay.increaseAllowance': '할당 늘리기', 'mobileVacay.companyLegend': '회사 지정 휴무', diff --git a/shared/src/i18n/nl/login.ts b/shared/src/i18n/nl/login.ts index 00ac863eb..083106fa0 100644 --- a/shared/src/i18n/nl/login.ts +++ b/shared/src/i18n/nl/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Inloggen mislukt. Controleer je inloggegevens.', 'login.tagline': 'Jouw reizen.\nJouw plan.', 'login.description': 'Plan reizen samen met interactieve kaarten, budgetten en realtime synchronisatie.', - 'login.features.maps': 'Interactieve kaarten', - 'login.features.mapsDesc': 'Google Places, routes en clustering', - 'login.features.realtime': 'Realtime synchronisatie', - 'login.features.realtimeDesc': 'Plan samen via WebSocket', - 'login.features.budget': 'Onkostenbeheer', - 'login.features.budgetDesc': 'Categorieën, grafieken en kosten per persoon', - 'login.features.collab': 'Samenwerking', - 'login.features.collabDesc': 'Meerdere gebruikers met gedeelde reizen', - 'login.features.packing': 'Paklijsten', - 'login.features.packingDesc': 'Categorieën, voortgang en suggesties', - 'login.features.bookings': 'Reserveringen', - 'login.features.bookingsDesc': 'Vluchten, hotels, restaurants en meer', - 'login.features.files': 'Documenten', - 'login.features.filesDesc': 'Upload en beheer documenten', - 'login.features.routes': 'Slimme routes', - 'login.features.routesDesc': 'Automatisch optimaliseren en exporteren naar Google Maps', - 'login.selfHosted': 'Zelf gehost · Open Source · Jouw gegevens blijven van jou', 'login.title': 'Inloggen', 'login.subtitle': 'Welkom terug', 'login.signingIn': 'Inloggen…', diff --git a/shared/src/i18n/nl/mobileVacay.ts b/shared/src/i18n/nl/mobileVacay.ts index 03ae86de9..b3bca586c 100644 --- a/shared/src/i18n/nl/mobileVacay.ts +++ b/shared/src/i18n/nl/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Volgende maand', 'mobileVacay.usedCount': '{count} gebruikt', 'mobileVacay.leftCount': '{count} over', - 'mobileVacay.daysPerYear': 'dagen / jaar', + 'mobileVacay.left': 'over', + 'mobileVacay.modeCompany': 'Bedrijf', 'mobileVacay.decreaseAllowance': 'Tegoed verlagen', 'mobileVacay.increaseAllowance': 'Tegoed verhogen', 'mobileVacay.companyLegend': 'Bedrijfsverlof', diff --git a/shared/src/i18n/pl/login.ts b/shared/src/i18n/pl/login.ts index e98b9d106..a26d647ad 100644 --- a/shared/src/i18n/pl/login.ts +++ b/shared/src/i18n/pl/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Twoje podróże.\nTwój plan.', 'login.description': 'Planuj wspólnie podróże z interaktywnymi mapami, budżetami i synchronizacją w czasie rzeczywistym.', - 'login.features.maps': 'Interaktywne mapy', - 'login.features.mapsDesc': 'Google Places, trasy i grupowanie', - 'login.features.realtime': 'Synchronizacja w czasie rzeczywistym', - 'login.features.realtimeDesc': 'Planuj wspólnie przez WebSocket', - 'login.features.budget': 'Śledzenie kosztów', - 'login.features.budgetDesc': 'Kategorie, wykresy i koszty w przeliczeniu na osobę', - 'login.features.collab': 'Współpraca', - 'login.features.collabDesc': 'Wielu użytkowników i wspólne podróże', - 'login.features.packing': 'Listy pakowania', - 'login.features.packingDesc': 'Kategorie, postęp i sugestie', - 'login.features.bookings': 'Rezerwacje', - 'login.features.bookingsDesc': 'Loty, hotele, restauracje i więcej', - 'login.features.files': 'Dokumenty', - 'login.features.filesDesc': 'Przesyłaj i zarządzaj dokumentami', - 'login.features.routes': 'Inteligentne trasy', - 'login.features.routesDesc': 'Automatyczna optymalizacja i eksport do Google Maps', - 'login.selfHosted': 'Własny hosting · Otwarty kod źródłowy · Twoje dane pozostają Twoje', 'login.title': 'Zaloguj się', 'login.subtitle': 'Witaj ponownie', 'login.signingIn': 'Logowanie...', diff --git a/shared/src/i18n/pl/mobileVacay.ts b/shared/src/i18n/pl/mobileVacay.ts index 9e252d784..77720119c 100644 --- a/shared/src/i18n/pl/mobileVacay.ts +++ b/shared/src/i18n/pl/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Następny miesiąc', 'mobileVacay.usedCount': 'Wykorzystano: {count}', 'mobileVacay.leftCount': 'Pozostało: {count}', - 'mobileVacay.daysPerYear': 'dni / rok', + 'mobileVacay.left': 'pozostało', + 'mobileVacay.modeCompany': 'Firma', 'mobileVacay.decreaseAllowance': 'Zmniejsz limit', 'mobileVacay.increaseAllowance': 'Zwiększ limit', 'mobileVacay.companyLegend': 'Dni firmowe', diff --git a/shared/src/i18n/ru/login.ts b/shared/src/i18n/ru/login.ts index cff0b25e2..48e7a1066 100644 --- a/shared/src/i18n/ru/login.ts +++ b/shared/src/i18n/ru/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Ваши поездки.\nВаш план.', 'login.description': 'Планируйте поездки совместно с интерактивными картами, бюджетами и синхронизацией в реальном времени.', - 'login.features.maps': 'Интерактивные карты', - 'login.features.mapsDesc': 'Google Places, маршруты и кластеризация', - 'login.features.realtime': 'Синхронизация в реальном времени', - 'login.features.realtimeDesc': 'Планируйте вместе через WebSocket', - 'login.features.budget': 'Контроль расходов', - 'login.features.budgetDesc': 'Категории, графики и расходы на человека', - 'login.features.collab': 'Совместная работа', - 'login.features.collabDesc': 'Многопользовательский режим с общими поездками', - 'login.features.packing': 'Списки вещей', - 'login.features.packingDesc': 'Категории, прогресс и подсказки', - 'login.features.bookings': 'Бронирования', - 'login.features.bookingsDesc': 'Авиабилеты, отели, рестораны и многое другое', - 'login.features.files': 'Документы', - 'login.features.filesDesc': 'Загружайте и управляйте документами', - 'login.features.routes': 'Умные маршруты', - 'login.features.routesDesc': 'Автооптимизация и экспорт в Google Maps', - 'login.selfHosted': 'Самостоятельный хостинг · Открытый код · Ваши данные остаются у вас', 'login.title': 'Вход', 'login.subtitle': 'С возвращением', 'login.signingIn': 'Вход…', diff --git a/shared/src/i18n/ru/mobileVacay.ts b/shared/src/i18n/ru/mobileVacay.ts index cf5228129..6c5395ff3 100644 --- a/shared/src/i18n/ru/mobileVacay.ts +++ b/shared/src/i18n/ru/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Следующий месяц', 'mobileVacay.usedCount': 'Использовано: {count}', 'mobileVacay.leftCount': 'Осталось: {count}', - 'mobileVacay.daysPerYear': 'дней / год', + 'mobileVacay.left': 'осталось', + 'mobileVacay.modeCompany': 'Компания', 'mobileVacay.decreaseAllowance': 'Уменьшить лимит', 'mobileVacay.increaseAllowance': 'Увеличить лимит', 'mobileVacay.companyLegend': 'Корпоративные дни', diff --git a/shared/src/i18n/sv/login.ts b/shared/src/i18n/sv/login.ts index 09045dde8..05e0ec172 100644 --- a/shared/src/i18n/sv/login.ts +++ b/shared/src/i18n/sv/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Dina resor.\nDin plan.', 'login.description': 'Planera resor tillsammans med hjälp av interaktiva kartor, budgetar och synkronisering i realtid.', - 'login.features.maps': 'Interaktiva kartor', - 'login.features.mapsDesc': 'Google Places, rutter och klusterbildning', - 'login.features.realtime': 'Synkronisering i realtid', - 'login.features.realtimeDesc': 'Planera tillsammans via WebSocket', - 'login.features.budget': 'Kostnadsuppföljning', - 'login.features.budgetDesc': 'Kategorier, diagram och kostnader per person', - 'login.features.collab': 'Samarbete', - 'login.features.collabDesc': 'Flera användare med delade resor', - 'login.features.packing': 'Packlistor', - 'login.features.packingDesc': 'Kategorier, framsteg och förslag', - 'login.features.bookings': 'Bokningar', - 'login.features.bookingsDesc': 'Flyg, hotell, restauranger och mycket mer', - 'login.features.files': 'Dokument', - 'login.features.filesDesc': 'Ladda upp och hantera dokument', - 'login.features.routes': 'Smarta rutter', - 'login.features.routesDesc': 'Automatisk optimering och export till Google Maps', - 'login.selfHosted': 'Egenhostad · Öppen källkod · Dina data förblir dina', 'login.title': 'Logga in', 'login.subtitle': 'Välkommen tillbaka', 'login.signingIn': 'Loggar in…', diff --git a/shared/src/i18n/sv/mobileVacay.ts b/shared/src/i18n/sv/mobileVacay.ts index dd210c106..f5a607f52 100644 --- a/shared/src/i18n/sv/mobileVacay.ts +++ b/shared/src/i18n/sv/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Nästa månad', 'mobileVacay.usedCount': '{count} använda', 'mobileVacay.leftCount': '{count} kvar', - 'mobileVacay.daysPerYear': 'dagar / år', + 'mobileVacay.left': 'kvar', + 'mobileVacay.modeCompany': 'Företag', 'mobileVacay.decreaseAllowance': 'Minska kvot', 'mobileVacay.increaseAllowance': 'Öka kvot', 'mobileVacay.companyLegend': 'Företagsledighet', diff --git a/shared/src/i18n/tr/login.ts b/shared/src/i18n/tr/login.ts index b6b11ec76..d851c51b9 100644 --- a/shared/src/i18n/tr/login.ts +++ b/shared/src/i18n/tr/login.ts @@ -5,23 +5,6 @@ const login: TranslationStrings = { 'login.tagline': 'Gezileriniz.\\nPlanınız.', 'login.description': 'Etkileşimli haritalar, bütçeler ve gerçek zamanlı senkronizasyonla gezileri işbirliği içinde planlayın.', - 'login.features.maps': 'İnteraktif Haritalar', - 'login.features.mapsDesc': 'Google Rehber, rotalar ve kümeleme', - 'login.features.realtime': 'Gerçek Zamanlı Senkronizasyon', - 'login.features.realtimeDesc': 'WebSocket aracılığıyla birlikte plan yapın', - 'login.features.budget': 'Maliyet Takibi', - 'login.features.budgetDesc': 'Kategoriler, grafikler ve kişi başı maliyetler', - 'login.features.collab': 'İşbirliği', - 'login.features.collabDesc': 'Paylaşılan gezilerle çok kullanıcılı', - 'login.features.packing': 'Paketleme Listeleri', - 'login.features.packingDesc': 'Kategoriler, ilerleme ve öneriler', - 'login.features.bookings': 'Rezervasyonlar', - 'login.features.bookingsDesc': 'Uçuşlar, oteller, restoranlar ve daha fazlası', - 'login.features.files': 'Belgeler', - 'login.features.filesDesc': 'Belgeleri yükleyin ve yönetin', - 'login.features.routes': 'Akıllı Rotalar', - 'login.features.routesDesc': "Otomatik optimize etme ve Google Haritalar'ı dışa aktarma", - 'login.selfHosted': 'Kendi kendine barındırılan \\u00B7 Açık Kaynak \\u00B7 Verileriniz size ait kalır', 'login.title': 'Oturum Aç', 'login.subtitle': 'Tekrar hoş geldiniz', 'login.signingIn': 'Oturum açılıyor…', diff --git a/shared/src/i18n/tr/mobileVacay.ts b/shared/src/i18n/tr/mobileVacay.ts index c566a3adb..dc1bac367 100644 --- a/shared/src/i18n/tr/mobileVacay.ts +++ b/shared/src/i18n/tr/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Sonraki ay', 'mobileVacay.usedCount': '{count} kullanıldı', 'mobileVacay.leftCount': '{count} kaldı', - 'mobileVacay.daysPerYear': 'gün / yıl', + 'mobileVacay.left': 'kaldı', + 'mobileVacay.modeCompany': 'Şirket', 'mobileVacay.decreaseAllowance': 'Hakkı azalt', 'mobileVacay.increaseAllowance': 'Hakkı artır', 'mobileVacay.companyLegend': 'Şirket tatili', diff --git a/shared/src/i18n/uk/login.ts b/shared/src/i18n/uk/login.ts index 741c5920f..ba502d03b 100644 --- a/shared/src/i18n/uk/login.ts +++ b/shared/src/i18n/uk/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Помилка входу. Перевірте свої облікові дані.', 'login.tagline': 'Ваші поїздки.\nВаш план.', 'login.description': 'Плануйте поїздки разом з інтерактивними картами, бюджетами та синхронізацією в реальному часі.', - 'login.features.maps': 'Інтерактивні карти', - 'login.features.mapsDesc': 'Google Places, маршрути та кластеризація', - 'login.features.realtime': 'Синхронізація в реальному часі', - 'login.features.realtimeDesc': 'Плануйте разом через WebSocket', - 'login.features.budget': 'Контроль витрат', - 'login.features.budgetDesc': 'Категорії, графіки та витрати на особу', - 'login.features.collab': 'Спільна робота', - 'login.features.collabDesc': 'Багатокористувацький режим зі спільними поїздками', - 'login.features.packing': 'Списки речей', - 'login.features.packingDesc': 'Категорії, прогрес та підказки', - 'login.features.bookings': 'Бронювання', - 'login.features.bookingsDesc': 'Авіаквитки, готелі, ресторани та багато іншого', - 'login.features.files': 'Документи', - 'login.features.filesDesc': 'Завантажуйте та керуйте документами', - 'login.features.routes': 'Розумні маршрути', - 'login.features.routesDesc': 'Автооптимізація та експорт з Google Maps', - 'login.selfHosted': 'Self-hosted · Відкритий код · Ваші дані залишаються у вас', 'login.title': 'Вхід', 'login.subtitle': 'З поверненням', 'login.signingIn': 'Вхід…', diff --git a/shared/src/i18n/uk/mobileVacay.ts b/shared/src/i18n/uk/mobileVacay.ts index 9f423eb42..57a95f9bb 100644 --- a/shared/src/i18n/uk/mobileVacay.ts +++ b/shared/src/i18n/uk/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Наступний місяць', 'mobileVacay.usedCount': 'Використано: {count}', 'mobileVacay.leftCount': 'Залишилось: {count}', - 'mobileVacay.daysPerYear': 'днів / рік', + 'mobileVacay.left': 'залишилось', + 'mobileVacay.modeCompany': 'Компанія', 'mobileVacay.decreaseAllowance': 'Зменшити ліміт', 'mobileVacay.increaseAllowance': 'Збільшити ліміт', 'mobileVacay.companyLegend': 'Корпоративні дні', diff --git a/shared/src/i18n/vi/login.ts b/shared/src/i18n/vi/login.ts index 2fbd29e7f..495906834 100644 --- a/shared/src/i18n/vi/login.ts +++ b/shared/src/i18n/vi/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': 'Đăng nhập không thành công. Vui lòng kiểm tra thông tin đăng nhập của bạn.', 'login.tagline': 'Chuyến đi của bạn.\nKế hoạch của bạn.', 'login.description': 'Lập kế hoạch chuyến đi cộng tác với bản đồ tương tác, ngân sách và đồng bộ hóa thời gian thực.', - 'login.features.maps': 'Bản đồ tương tác', - 'login.features.mapsDesc': 'Google Địa điểm, tuyến đường và phân cụm', - 'login.features.realtime': 'Đồng bộ hóa thời gian thực', - 'login.features.realtimeDesc': 'Lập kế hoạch cùng nhau thông qua WebSocket', - 'login.features.budget': 'Theo dõi chi phí', - 'login.features.budgetDesc': 'Danh mục, biểu đồ và chi phí mỗi người', - 'login.features.collab': 'Sự hợp tác', - 'login.features.collabDesc': 'Nhiều người dùng với các chuyến đi chung', - 'login.features.packing': 'Danh sách đóng gói', - 'login.features.packingDesc': 'Danh mục, tiến độ và đề xuất', - 'login.features.bookings': 'Đặt chỗ', - 'login.features.bookingsDesc': 'Chuyến bay, khách sạn, nhà hàng và hơn thế nữa', - 'login.features.files': 'Tài liệu', - 'login.features.filesDesc': 'Tải lên và quản lý tài liệu', - 'login.features.routes': 'Tuyến đường thông minh', - 'login.features.routesDesc': 'Tự động tối ưu hóa & xuất Google Maps', - 'login.selfHosted': 'Tự lưu trữ · Mã nguồn mở · Dữ liệu của bạn vẫn là của bạn', 'login.title': 'Đăng nhập', 'login.subtitle': 'Chào mừng trở lại', 'login.signingIn': 'Đang đăng nhập…', diff --git a/shared/src/i18n/vi/mobileVacay.ts b/shared/src/i18n/vi/mobileVacay.ts index 0b3c39faf..bde6b7f24 100644 --- a/shared/src/i18n/vi/mobileVacay.ts +++ b/shared/src/i18n/vi/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': 'Tháng sau', 'mobileVacay.usedCount': 'Đã dùng {count}', 'mobileVacay.leftCount': 'Còn {count}', - 'mobileVacay.daysPerYear': 'ngày / năm', + 'mobileVacay.left': 'còn', + 'mobileVacay.modeCompany': 'Công ty', 'mobileVacay.decreaseAllowance': 'Giảm hạn mức', 'mobileVacay.increaseAllowance': 'Tăng hạn mức', 'mobileVacay.companyLegend': 'Ngày nghỉ công ty', diff --git a/shared/src/i18n/zh-TW/login.ts b/shared/src/i18n/zh-TW/login.ts index 099dbfe9c..22451f2cf 100644 --- a/shared/src/i18n/zh-TW/login.ts +++ b/shared/src/i18n/zh-TW/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': '登入失敗,請檢查你的憑據。', 'login.tagline': '你的旅行。\n你的計劃。', 'login.description': '透過互動地圖、預算管理和即時同步,協同規劃旅行。', - 'login.features.maps': '互動地圖', - 'login.features.mapsDesc': 'Google Places、路線和聚類', - 'login.features.realtime': '即時同步', - 'login.features.realtimeDesc': '透過 WebSocket 協同規劃', - 'login.features.budget': '費用追蹤', - 'login.features.budgetDesc': '分類、圖表和人均費用', - 'login.features.collab': '協作', - 'login.features.collabDesc': '多使用者共享旅行', - 'login.features.packing': '行李清單', - 'login.features.packingDesc': '分類、進度和建議', - 'login.features.bookings': '預訂', - 'login.features.bookingsDesc': '航班、酒店、餐廳等', - 'login.features.files': '文件', - 'login.features.filesDesc': '上傳和管理文件', - 'login.features.routes': '智慧路線', - 'login.features.routesDesc': '自動最佳化和匯出到 Google Maps', - 'login.selfHosted': '自託管 · 開源 · 資料由你掌控', 'login.title': '登入', 'login.subtitle': '歡迎回來', 'login.signingIn': '登入中…', diff --git a/shared/src/i18n/zh-TW/mobileVacay.ts b/shared/src/i18n/zh-TW/mobileVacay.ts index 5b98eb94e..ed6327a9c 100644 --- a/shared/src/i18n/zh-TW/mobileVacay.ts +++ b/shared/src/i18n/zh-TW/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': '下個月', 'mobileVacay.usedCount': '已用 {count}', 'mobileVacay.leftCount': '剩餘 {count}', - 'mobileVacay.daysPerYear': '天 / 年', + 'mobileVacay.left': '剩餘', + 'mobileVacay.modeCompany': '公司', 'mobileVacay.decreaseAllowance': '減少額度', 'mobileVacay.increaseAllowance': '增加額度', 'mobileVacay.companyLegend': '公司假期', diff --git a/shared/src/i18n/zh/login.ts b/shared/src/i18n/zh/login.ts index aacb4a1c7..02d7226ed 100644 --- a/shared/src/i18n/zh/login.ts +++ b/shared/src/i18n/zh/login.ts @@ -4,23 +4,6 @@ const login: TranslationStrings = { 'login.error': '登录失败,请检查你的凭据。', 'login.tagline': '你的旅行。\n你的计划。', 'login.description': '通过互动地图、预算管理和实时同步,协同规划旅行。', - 'login.features.maps': '互动地图', - 'login.features.mapsDesc': 'Google Places、路线和聚类', - 'login.features.realtime': '实时同步', - 'login.features.realtimeDesc': '通过 WebSocket 协同规划', - 'login.features.budget': '费用跟踪', - 'login.features.budgetDesc': '分类、图表和人均费用', - 'login.features.collab': '协作', - 'login.features.collabDesc': '多用户共享旅行', - 'login.features.packing': '行李清单', - 'login.features.packingDesc': '分类、进度和建议', - 'login.features.bookings': '预订', - 'login.features.bookingsDesc': '航班、酒店、餐厅等', - 'login.features.files': '文档', - 'login.features.filesDesc': '上传和管理文档', - 'login.features.routes': '智能路线', - 'login.features.routesDesc': '自动优化和导出到 Google Maps', - 'login.selfHosted': '自托管 · 开源 · 数据由你掌控', 'login.title': '登录', 'login.subtitle': '欢迎回来', 'login.signingIn': '登录中…', diff --git a/shared/src/i18n/zh/mobileVacay.ts b/shared/src/i18n/zh/mobileVacay.ts index dd1a9645c..a8defcdbd 100644 --- a/shared/src/i18n/zh/mobileVacay.ts +++ b/shared/src/i18n/zh/mobileVacay.ts @@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = { 'mobileVacay.nextMonth': '下个月', 'mobileVacay.usedCount': '已用 {count}', 'mobileVacay.leftCount': '剩余 {count}', - 'mobileVacay.daysPerYear': '天 / 年', + 'mobileVacay.left': '剩余', + 'mobileVacay.modeCompany': '公司', 'mobileVacay.decreaseAllowance': '减少额度', 'mobileVacay.increaseAllowance': '增加额度', 'mobileVacay.companyLegend': '公司假期',