From a9ffcbc134a71af54abcc5dde6833f591355b997 Mon Sep 17 00:00:00 2001 From: Maurice Date: Thu, 5 Nov 2026 17:11:47 +0100 Subject: [PATCH] style: prettier-format the native and i18n changes The feature-branch CI does not run the formatting check, so the conflict-resolved files (AboutTab, useAccountSettings, useTripExport), the native shell and iap module, and the i18n files touched by the translation scripts were never run through Prettier. Format them all; no behaviour changes. --- client/native-shell/main.ts | 787 +++++++++--------- .../src/components/Planner/useTripExport.ts | 2 +- client/src/components/Settings/AboutTab.tsx | 2 +- .../components/Settings/useAccountSettings.ts | 2 +- client/src/native/iap.ts | 5 +- shared/scripts/i18n-parity.mjs | 20 +- shared/src/i18n/az/native.ts | 6 +- shared/src/i18n/br/native.ts | 6 +- shared/src/i18n/ca/native.ts | 6 +- shared/src/i18n/cs/native.ts | 6 +- shared/src/i18n/de/native.ts | 6 +- shared/src/i18n/en/native.ts | 6 +- shared/src/i18n/es/native.ts | 6 +- shared/src/i18n/et/native.ts | 6 +- shared/src/i18n/fr/native.ts | 6 +- shared/src/i18n/gr/native.ts | 6 +- shared/src/i18n/he/index.ts | 2 +- shared/src/i18n/hu/native.ts | 9 +- shared/src/i18n/id/native.ts | 9 +- shared/src/i18n/it/native.ts | 6 +- shared/src/i18n/ja/native.ts | 9 +- shared/src/i18n/nl/native.ts | 6 +- shared/src/i18n/pl/native.ts | 6 +- shared/src/i18n/ru/native.ts | 9 +- shared/src/i18n/sk/native.ts | 6 +- shared/src/i18n/sv/native.ts | 6 +- shared/src/i18n/tr/native.ts | 6 +- shared/src/i18n/uk/native.ts | 6 +- shared/src/i18n/vi/native.ts | 6 +- 29 files changed, 527 insertions(+), 437 deletions(-) diff --git a/client/native-shell/main.ts b/client/native-shell/main.ts index 65f5cbaef..fd8cf0413 100644 --- a/client/native-shell/main.ts +++ b/client/native-shell/main.ts @@ -1,14 +1,14 @@ -import { CapacitorHttp } from '@capacitor/core' -import { Haptics, ImpactStyle, NotificationType } from '@capacitor/haptics' -import { SplashScreen } from '@capacitor/splash-screen' -import { StatusBar, Style } from '@capacitor/status-bar' -import { TrekShell } from '../src/native/trekShell' -import { probeDemo, probeServer, type ProbeFailure, type ProbeGet } from '../src/native/serverAddress' -import { CLOUD_URL, PRIVACY_URL, SERVER_DOCS_URL } from '../src/native/links' -import { getIapClient, type Purchase, productIdFor } from '../src/native/iap' -import { pickLocale, translator } from './i18n' -import logoUrl from '../public/icons/icon-white.svg' -import './shell.css' +import { CapacitorHttp } from '@capacitor/core'; +import { Haptics, ImpactStyle, NotificationType } from '@capacitor/haptics'; +import { SplashScreen } from '@capacitor/splash-screen'; +import { StatusBar, Style } from '@capacitor/status-bar'; +import logoUrl from '../public/icons/icon-white.svg'; +import { getIapClient, productIdFor, type Purchase } from '../src/native/iap'; +import { CLOUD_URL, PRIVACY_URL, SERVER_DOCS_URL } from '../src/native/links'; +import { probeDemo, probeServer, type ProbeFailure, type ProbeGet } from '../src/native/serverAddress'; +import { TrekShell } from '../src/native/trekShell'; +import { pickLocale, translator } from './i18n'; +import './shell.css'; /** * The only page the app ships itself. It welcomes someone on first start, asks @@ -17,44 +17,44 @@ import './shell.css' * Everything past it is the UI that server delivers. */ -const locale = pickLocale(navigator.languages?.length ? navigator.languages : [navigator.language]) -const t = translator(locale) -document.documentElement.lang = locale -document.documentElement.dir = locale === 'ar' ? 'rtl' : 'ltr' +const locale = pickLocale(navigator.languages?.length ? navigator.languages : [navigator.language]); +const t = translator(locale); +document.documentElement.lang = locale; +document.documentElement.dir = locale === 'ar' ? 'rtl' : 'ltr'; -const root = document.getElementById('shell')! -const PROBE_TIMEOUT_MS = 8000 +const root = document.getElementById('shell')!; +const PROBE_TIMEOUT_MS = 8000; -const iap = getIapClient() +const iap = getIapClient(); /** Whether this build can take a payment in-app; decided once at start. Until then, and in a store build without the purchase plugin, the plan picker finishes on the web. */ -let iapAvailable = false +let iapAvailable = false; /** The subdomain suffix a cloud instance sits on, from the cloud address: `liketrek.com` → `.liketrek.com`. */ function cloudSuffix(): string { try { - return `.${new URL(CLOUD_URL).host}` + return `.${new URL(CLOUD_URL).host}`; } catch { - return '.liketrek.com' + return '.liketrek.com'; } } // Native HTTP rather than fetch: the probe goes to a server of the user's // choosing, which has no reason to answer this page's origin with CORS. const get: ProbeGet = async (url) => { - const response = await CapacitorHttp.get({ url, connectTimeout: PROBE_TIMEOUT_MS, readTimeout: PROBE_TIMEOUT_MS }) - return { status: response.status, data: response.data, url: response.url } -} + const response = await CapacitorHttp.get({ url, connectTimeout: PROBE_TIMEOUT_MS, readTimeout: PROBE_TIMEOUT_MS }); + return { status: response.status, data: response.data, url: response.url }; +}; function failureMessage(reason: ProbeFailure): string { switch (reason) { case 'invalid': - return t('native.connect.errorInvalid') + return t('native.connect.errorInvalid'); case 'unreachable': - return t('native.connect.errorUnreachable') + return t('native.connect.errorUnreachable'); case 'notTrek': - return t('native.connect.errorNotTrek') + return t('native.connect.errorNotTrek'); case 'tooOld': - return t('native.connect.errorTooOld') + return t('native.connect.errorTooOld'); } } @@ -64,29 +64,29 @@ function el( props: Partial & { dataset?: Record } = {}, ...children: (Node | string)[] ): HTMLElementTagNameMap[K] { - const { dataset, ...rest } = props - const node = document.createElement(tag) - Object.assign(node, rest) - if (dataset) for (const [k, v] of Object.entries(dataset)) node.dataset[k] = v - node.append(...children.filter((c) => c !== null && c !== undefined)) - return node + const { dataset, ...rest } = props; + const node = document.createElement(tag); + Object.assign(node, rest); + if (dataset) for (const [k, v] of Object.entries(dataset)) node.dataset[k] = v; + node.append(...children.filter((c) => c !== null && c !== undefined)); + return node; } function icon(path: string, size = 22, className?: string): SVGSVGElement { - const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') - svg.setAttribute('viewBox', '0 0 24 24') - svg.setAttribute('width', String(size)) - svg.setAttribute('height', String(size)) - svg.setAttribute('fill', 'none') - svg.setAttribute('stroke', 'currentColor') - svg.setAttribute('stroke-width', '2') - svg.setAttribute('stroke-linecap', 'round') - svg.setAttribute('stroke-linejoin', 'round') - if (className) svg.setAttribute('class', className) - const p = document.createElementNS('http://www.w3.org/2000/svg', 'path') - p.setAttribute('d', path) - svg.appendChild(p) - return svg + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('width', String(size)); + svg.setAttribute('height', String(size)); + svg.setAttribute('fill', 'none'); + svg.setAttribute('stroke', 'currentColor'); + svg.setAttribute('stroke-width', '2'); + svg.setAttribute('stroke-linecap', 'round'); + svg.setAttribute('stroke-linejoin', 'round'); + if (className) svg.setAttribute('class', className); + const p = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + p.setAttribute('d', path); + svg.appendChild(p); + return svg; } const I = { chevron: 'M9 18l6-6-6-6', @@ -95,84 +95,94 @@ const I = { bolt: 'M13 2 4 14h7l-1 8 9-12h-7z', shield: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10', check: 'M20 6 9 17l-5-5', -} +}; async function tap(style: ImpactStyle = ImpactStyle.Light): Promise { try { - await Haptics.impact({ style }) + await Haptics.impact({ style }); } catch { /* web / unavailable */ } } async function notify(type: NotificationType): Promise { try { - await Haptics.notification({ type }) + await Haptics.notification({ type }); } catch { /* web / unavailable */ } } // ── Screen transitions ────────────────────────────────────────────────────── -let current: HTMLElement | null = null +let current: HTMLElement | null = null; function show(screen: HTMLElement, opts: { back?: boolean } = {}): void { - screen.classList.add('screen', 'screen-enter') - if (opts.back) screen.classList.add('screen-back') - const previous = current + screen.classList.add('screen', 'screen-enter'); + if (opts.back) screen.classList.add('screen-back'); + const previous = current; if (previous) { - previous.classList.remove('screen-enter') - previous.classList.add('screen-leave') - if (opts.back) previous.classList.add('screen-back') - previous.addEventListener('animationend', () => previous.remove(), { once: true }) - setTimeout(() => previous.remove(), 400) + previous.classList.remove('screen-enter'); + previous.classList.add('screen-leave'); + if (opts.back) previous.classList.add('screen-back'); + previous.addEventListener('animationend', () => previous.remove(), { once: true }); + setTimeout(() => previous.remove(), 400); } - root.append(screen) - current = screen + root.append(screen); + current = screen; } function externalLink(text: string, href: string): HTMLAnchorElement { - return el('a', { href, target: '_blank', rel: 'noopener noreferrer' }, text) + return el('a', { href, target: '_blank', rel: 'noopener noreferrer' }, text); } function footLinks(...extra: Node[]): HTMLElement { - return el('div', { className: 'foot-links' }, ...extra, externalLink(t('native.privacy'), PRIVACY_URL)) + return el('div', { className: 'foot-links' }, ...extra, externalLink(t('native.privacy'), PRIVACY_URL)); } function topbar(onBack: () => void): HTMLElement { - const back = el('button', { type: 'button', className: 'back' }, icon('M15 18l-6-6 6-6', 18), t('native.back')) + const back = el('button', { type: 'button', className: 'back' }, icon('M15 18l-6-6 6-6', 18), t('native.back')); back.addEventListener('click', () => { - void tap() - onBack() - }) - return el('div', { className: 'topbar' }, back) + void tap(); + onBack(); + }); + return el('div', { className: 'topbar' }, back); } // ── Welcome ───────────────────────────────────────────────────────────────── function showWelcome(opts: { back?: boolean } = {}): void { - const hero = el('img', { src: logoUrl, alt: '', className: 'hero-mark' }) + const hero = el('img', { src: logoUrl, alt: '', className: 'hero-mark' }); const connect = el( 'button', { type: 'button', className: 'btn' }, - el('span', { className: 'btn-row' }, el('b', {}, t('native.welcome.connect')), el('small', {}, t('native.welcome.connectSub'))), - icon(I.chevron, 20, 'chev'), - ) + el( + 'span', + { className: 'btn-row' }, + el('b', {}, t('native.welcome.connect')), + el('small', {}, t('native.welcome.connectSub')) + ), + icon(I.chevron, 20, 'chev') + ); connect.addEventListener('click', () => { - void tap() - showConnect() - }) + void tap(); + showConnect(); + }); const cloud = el( 'button', { type: 'button', className: 'btn btn-ghost' }, - el('span', { className: 'btn-row' }, el('b', {}, t('native.welcome.getCloud')), el('small', {}, t('native.welcome.getCloudSub'))), - icon(I.chevron, 20, 'chev'), - ) + el( + 'span', + { className: 'btn-row' }, + el('b', {}, t('native.welcome.getCloud')), + el('small', {}, t('native.welcome.getCloudSub')) + ), + icon(I.chevron, 20, 'chev') + ); cloud.addEventListener('click', () => { - void tap() - showCloud() - }) + void tap(); + showCloud(); + }); - const demo = el('button', { type: 'button', className: 'linkbtn' }, t('native.connect.tryDemo')) - demo.addEventListener('click', () => runDemo(demo)) + const demo = el('button', { type: 'button', className: 'linkbtn' }, t('native.connect.tryDemo')); + demo.addEventListener('click', () => runDemo(demo)); show( el( @@ -184,10 +194,10 @@ function showWelcome(opts: { back?: boolean } = {}): void { connect, cloud, demo, - footLinks(externalLink(t('native.connect.whatIsServer'), SERVER_DOCS_URL)), + footLinks(externalLink(t('native.connect.whatIsServer'), SERVER_DOCS_URL)) ), - opts, - ) + opts + ); } // ── Connect (address) ─────────────────────────────────────────────────────── @@ -198,48 +208,48 @@ function showConnect(prefill = '', opts: { back?: boolean } = {}): void { value: prefill, autocapitalize: 'off', spellcheck: false, - }) - input.setAttribute('inputmode', 'url') - input.setAttribute('autocomplete', 'url') + }); + input.setAttribute('inputmode', 'url'); + input.setAttribute('autocomplete', 'url'); // The on-screen keyboard's return key submits the address; it reads "Go", not a newline. - input.setAttribute('enterkeyhint', 'go') - input.setAttribute('aria-label', t('native.connect.title')) - const field = el('div', { className: 'field' }, input) + input.setAttribute('enterkeyhint', 'go'); + input.setAttribute('aria-label', t('native.connect.title')); + const field = el('div', { className: 'field' }, input); - const label = el('span', {}, t('native.connect.submit')) - const submit = el('button', { type: 'submit', className: 'btn' }, label) - const error = el('p', { className: 'error', role: 'alert' }) - const form = el('form', { className: 'form', noValidate: true }, field, submit, error) + const label = el('span', {}, t('native.connect.submit')); + const submit = el('button', { type: 'submit', className: 'btn' }, label); + const error = el('p', { className: 'error', role: 'alert' }); + const form = el('form', { className: 'form', noValidate: true }, field, submit, error); const fail = (reason: ProbeFailure): void => { - void notify(NotificationType.Error) - field.classList.add('bad') - setTimeout(() => field.classList.remove('bad'), 350) - error.textContent = failureMessage(reason) - input.disabled = false - submit.disabled = false - label.textContent = t('native.connect.submit') - submit.replaceChildren(label) - input.focus() - } + void notify(NotificationType.Error); + field.classList.add('bad'); + setTimeout(() => field.classList.remove('bad'), 350); + error.textContent = failureMessage(reason); + input.disabled = false; + submit.disabled = false; + label.textContent = t('native.connect.submit'); + submit.replaceChildren(label); + input.focus(); + }; form.addEventListener('submit', async (event) => { - event.preventDefault() - void tap(ImpactStyle.Medium) - error.textContent = '' - input.disabled = true - submit.disabled = true - submit.replaceChildren(el('span', { className: 'spinner' }), t('native.connect.checking')) - const result = await probeServer(input.value, get) + event.preventDefault(); + void tap(ImpactStyle.Medium); + error.textContent = ''; + input.disabled = true; + submit.disabled = true; + submit.replaceChildren(el('span', { className: 'spinner' }), t('native.connect.checking')); + const result = await probeServer(input.value, get); if (result.ok) { - await connected(result.url) - return + await connected(result.url); + return; } - fail(result.reason) - }) + fail(result.reason); + }); - const demo = el('button', { type: 'button', className: 'linkbtn' }, t('native.connect.tryDemo')) - demo.addEventListener('click', () => runDemo(demo, error)) + const demo = el('button', { type: 'button', className: 'linkbtn' }, t('native.connect.tryDemo')); + demo.addEventListener('click', () => runDemo(demo, error)); show( el( @@ -250,30 +260,35 @@ function showConnect(prefill = '', opts: { back?: boolean } = {}): void { el('p', { className: 'sub' }, t('native.connect.hint')), form, demo, - footLinks(externalLink(t('native.connect.whatIsServer'), SERVER_DOCS_URL)), + footLinks(externalLink(t('native.connect.whatIsServer'), SERVER_DOCS_URL)) ), - opts, - ) - requestAnimationFrame(() => input.focus()) + opts + ); + requestAnimationFrame(() => input.focus()); } // ── Get TREK Cloud ────────────────────────────────────────────────────────── function feature(path: string, text: string): HTMLElement { - return el('div', { className: 'feature' }, el('span', { className: 'ic' }, icon(path, 18)), el('span', { className: 'tx' }, text)) + return el( + 'div', + { className: 'feature' }, + el('span', { className: 'ic' }, icon(path, 18)), + el('span', { className: 'tx' }, text) + ); } function showCloud(opts: { back?: boolean } = {}): void { - const cta = el('button', { type: 'button', className: 'btn' }, t('native.cloud.cta'), icon(I.chevron, 20, 'chev')) + const cta = el('button', { type: 'button', className: 'btn' }, t('native.cloud.cta'), icon(I.chevron, 20, 'chev')); cta.addEventListener('click', () => { - void tap() - showPlans() - }) + void tap(); + showPlans(); + }); - const have = el('button', { type: 'button', className: 'linkbtn' }, t('native.cloud.haveServer')) + const have = el('button', { type: 'button', className: 'linkbtn' }, t('native.cloud.haveServer')); have.addEventListener('click', () => { - void tap() - showConnect() - }) + void tap(); + showConnect(); + }); show( el( @@ -282,57 +297,63 @@ function showCloud(opts: { back?: boolean } = {}): void { topbar(() => showWelcome({ back: true })), el('h1', { className: 'title-sm' }, t('native.cloud.title')), el('p', { className: 'sub' }, t('native.cloud.sub')), - el('div', { className: 'features' }, feature(I.globe, t('native.cloud.f1')), feature(I.bolt, t('native.cloud.f2')), feature(I.shield, t('native.cloud.f3'))), + el( + 'div', + { className: 'features' }, + feature(I.globe, t('native.cloud.f1')), + feature(I.bolt, t('native.cloud.f2')), + feature(I.shield, t('native.cloud.f3')) + ), cta, have, - footLinks(), + footLinks() ), - opts, - ) + opts + ); } // ── Choose a plan (TREK Cloud) ────────────────────────────────────────────── interface CloudPlan { - id: string - name: string - monthlyCents: number - yearlyCents: number - aiDocuments: number - storageGb: number + id: string; + name: string; + monthlyCents: number; + yearlyCents: number; + aiDocuments: number; + storageGb: number; } function money(cents: number): string { - return new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(cents / 100) + return new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(cents / 100); } async function fetchPlans(): Promise { try { - const res = await get(`${CLOUD_URL}/api/plans`) - const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data - const plans = (data as { plans?: CloudPlan[] })?.plans - return Array.isArray(plans) && plans.length ? plans : null + const res = await get(`${CLOUD_URL}/api/plans`); + const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data; + const plans = (data as { plans?: CloudPlan[] })?.plans; + return Array.isArray(plans) && plans.length ? plans : null; } catch { - return null + return null; } } function subscribe(plan: CloudPlan, interval: 'month' | 'year'): void { - void tap(ImpactStyle.Medium) + void tap(ImpactStyle.Medium); // In-app purchase when this build can take one; otherwise finish on the web, so // nothing half-wired faces a customer until the store products exist. if (iapAvailable) { - showClaim(plan, interval) + showClaim(plan, interval); } else { - window.open(`${CLOUD_URL}/order?plan=${encodeURIComponent(plan.id)}`, '_blank', 'noopener') + window.open(`${CLOUD_URL}/order?plan=${encodeURIComponent(plan.id)}`, '_blank', 'noopener'); } } function planCard(plan: CloudPlan, interval: 'month' | 'year', featured: boolean): HTMLElement { - const price = interval === 'year' ? plan.yearlyCents : plan.monthlyCents - const per = interval === 'year' ? t('native.plans.perYear') : t('native.plans.perMonth') - const feat = (text: string) => el('span', {}, icon(I.check, 16), text) - const sub = el('button', { type: 'button', className: 'btn' }, t('native.plans.subscribe')) - sub.addEventListener('click', () => subscribe(plan, interval)) + const price = interval === 'year' ? plan.yearlyCents : plan.monthlyCents; + const per = interval === 'year' ? t('native.plans.perYear') : t('native.plans.perMonth'); + const feat = (text: string) => el('span', {}, icon(I.check, 16), text); + const sub = el('button', { type: 'button', className: 'btn' }, t('native.plans.subscribe')); + sub.addEventListener('click', () => subscribe(plan, interval)); return el( 'div', { className: featured ? 'plan featured' : 'plan' }, @@ -340,66 +361,71 @@ function planCard(plan: CloudPlan, interval: 'month' | 'year', featured: boolean 'div', { className: 'plan-top' }, el('span', { className: 'plan-name' }, plan.name), - el('span', { className: 'plan-price' }, money(price), el('small', {}, per)), + el('span', { className: 'plan-price' }, money(price), el('small', {}, per)) ), el( 'div', { className: 'plan-feats' }, feat(t('native.plans.unlimited')), feat(t('native.plans.storage', { n: String(plan.storageGb) })), - feat(plan.aiDocuments > 0 ? t('native.plans.aiDocs', { n: String(plan.aiDocuments) }) : t('native.plans.noAi')), + feat(plan.aiDocuments > 0 ? t('native.plans.aiDocs', { n: String(plan.aiDocuments) }) : t('native.plans.noAi')) ), - sub, - ) + sub + ); } function showPlans(opts: { back?: boolean } = {}): void { - let interval: 'month' | 'year' = 'month' - let plans: CloudPlan[] | null = null + let interval: 'month' | 'year' = 'month'; + let plans: CloudPlan[] | null = null; - const content = el('div', { className: 'loading' }, el('span', { className: 'spinner' })) + const content = el('div', { className: 'loading' }, el('span', { className: 'spinner' })); - const monthBtn = el('button', { type: 'button' }, t('native.plans.monthly')) - const yearBtn = el('button', { type: 'button' }, t('native.plans.yearly'), el('span', { className: 'save' }, t('native.plans.save'))) - const seg = el('div', { className: 'segmented' }, monthBtn, yearBtn) + const monthBtn = el('button', { type: 'button' }, t('native.plans.monthly')); + const yearBtn = el( + 'button', + { type: 'button' }, + t('native.plans.yearly'), + el('span', { className: 'save' }, t('native.plans.save')) + ); + const seg = el('div', { className: 'segmented' }, monthBtn, yearBtn); const paintSeg = () => { - monthBtn.setAttribute('aria-pressed', String(interval === 'month')) - yearBtn.setAttribute('aria-pressed', String(interval === 'year')) - } + monthBtn.setAttribute('aria-pressed', String(interval === 'month')); + yearBtn.setAttribute('aria-pressed', String(interval === 'year')); + }; const renderList = () => { - const list = plans - if (!list) return - content.className = 'plans' - content.replaceChildren(...list.map((p, i) => planCard(p, interval, i === list.length - 1))) - } + const list = plans; + if (!list) return; + content.className = 'plans'; + content.replaceChildren(...list.map((p, i) => planCard(p, interval, i === list.length - 1))); + }; const choose = (next: 'month' | 'year') => { - if (interval === next) return - void tap() - interval = next - paintSeg() - renderList() - } - monthBtn.addEventListener('click', () => choose('month')) - yearBtn.addEventListener('click', () => choose('year')) - paintSeg() + if (interval === next) return; + void tap(); + interval = next; + paintSeg(); + renderList(); + }; + monthBtn.addEventListener('click', () => choose('month')); + yearBtn.addEventListener('click', () => choose('year')); + paintSeg(); const load = async () => { - plans = await fetchPlans() + plans = await fetchPlans(); if (plans) { - renderList() - return + renderList(); + return; } - content.className = '' - const retry = el('button', { type: 'button', className: 'btn btn-ghost' }, t('native.plans.retry')) + content.className = ''; + const retry = el('button', { type: 'button', className: 'btn btn-ghost' }, t('native.plans.retry')); retry.addEventListener('click', () => { - void tap() - content.className = 'loading' - content.replaceChildren(el('span', { className: 'spinner' })) - void load() - }) - content.replaceChildren(el('p', { className: 'error' }, t('native.plans.loadError')), retry) - } + void tap(); + content.className = 'loading'; + content.replaceChildren(el('span', { className: 'spinner' })); + void load(); + }); + content.replaceChildren(el('p', { className: 'error' }, t('native.plans.loadError')), retry); + }; show( el( @@ -410,34 +436,34 @@ function showPlans(opts: { back?: boolean } = {}): void { el('p', { className: 'sub' }, t('native.plans.sub')), seg, content, - el('p', { className: 'note' }, t('native.plans.soon')), + el('p', { className: 'note' }, t('native.plans.soon')) ), - opts, - ) - void load() + opts + ); + void load(); } // ── Claim a TREK Cloud instance (choose address, buy, provision) ──────────── -type SlugStatus = 'available' | 'taken' | 'invalid' | 'reserved' | 'error' +type SlugStatus = 'available' | 'taken' | 'invalid' | 'reserved' | 'error'; async function slugStatus(slug: string): Promise { try { - const res = await get(`${CLOUD_URL}/api/slug/check?slug=${encodeURIComponent(slug)}`) - const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data - const status = (data as { status?: string })?.status - if (status === 'available' || status === 'taken' || status === 'invalid' || status === 'reserved') return status - return 'error' + const res = await get(`${CLOUD_URL}/api/slug/check?slug=${encodeURIComponent(slug)}`); + const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data; + const status = (data as { status?: string })?.status; + if (status === 'available' || status === 'taken' || status === 'invalid' || status === 'reserved') return status; + return 'error'; } catch { - return 'error' + return 'error'; } } interface RedeemResult { - ok: boolean + ok: boolean; /** Set when ok: the address the app loads. */ - instanceUrl?: string + instanceUrl?: string; /** Set when not ok: a stable refusal code. */ - code?: string + code?: string; } async function redeemPurchase(slug: string, email: string, purchase: Purchase): Promise { try { @@ -447,38 +473,53 @@ async function redeemPurchase(slug: string, email: string, purchase: Purchase): connectTimeout: PROBE_TIMEOUT_MS, readTimeout: PROBE_TIMEOUT_MS, data: { platform: purchase.platform, productId: purchase.productId, receipt: purchase.receipt, slug, email }, - }) - const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data - const body = (data ?? {}) as { instanceUrl?: string; code?: string } + }); + const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data; + const body = (data ?? {}) as { instanceUrl?: string; code?: string }; if ((res.status === 200 || res.status === 201) && typeof body.instanceUrl === 'string') { - return { ok: true, instanceUrl: body.instanceUrl } + return { ok: true, instanceUrl: body.instanceUrl }; } - return { ok: false, code: body.code ?? 'redeem-failed' } + return { ok: false, code: body.code ?? 'redeem-failed' }; } catch { - return { ok: false, code: 'redeem-failed' } + return { ok: false, code: 'redeem-failed' }; } } function showClaim(plan: CloudPlan, interval: 'month' | 'year', opts: { back?: boolean } = {}): void { - const price = interval === 'year' ? plan.yearlyCents : plan.monthlyCents + const price = interval === 'year' ? plan.yearlyCents : plan.monthlyCents; - const slugInput = el('input', { type: 'text', className: 'slug-input', autocapitalize: 'off', spellcheck: false, value: '' }) - slugInput.setAttribute('inputmode', 'url') - slugInput.setAttribute('autocomplete', 'off') - slugInput.setAttribute('maxlength', '40') - slugInput.setAttribute('aria-label', t('native.claim.addressLabel')) - const slugField = el('div', { className: 'field slug-field' }, slugInput, el('span', { className: 'slug-suffix' }, cloudSuffix())) - const slugNote = el('p', { className: 'hint' }) + const slugInput = el('input', { + type: 'text', + className: 'slug-input', + autocapitalize: 'off', + spellcheck: false, + value: '', + }); + slugInput.setAttribute('inputmode', 'url'); + slugInput.setAttribute('autocomplete', 'off'); + slugInput.setAttribute('maxlength', '40'); + slugInput.setAttribute('aria-label', t('native.claim.addressLabel')); + const slugField = el( + 'div', + { className: 'field slug-field' }, + slugInput, + el('span', { className: 'slug-suffix' }, cloudSuffix()) + ); + const slugNote = el('p', { className: 'hint' }); - const emailInput = el('input', { type: 'email', value: '', autocapitalize: 'off', spellcheck: false }) - emailInput.setAttribute('inputmode', 'email') - emailInput.setAttribute('autocomplete', 'email') - emailInput.setAttribute('enterkeyhint', 'go') - emailInput.setAttribute('aria-label', t('native.claim.emailLabel')) - const emailField = el('div', { className: 'field' }, emailInput) + const emailInput = el('input', { type: 'email', value: '', autocapitalize: 'off', spellcheck: false }); + emailInput.setAttribute('inputmode', 'email'); + emailInput.setAttribute('autocomplete', 'email'); + emailInput.setAttribute('enterkeyhint', 'go'); + emailInput.setAttribute('aria-label', t('native.claim.emailLabel')); + const emailField = el('div', { className: 'field' }, emailInput); - const submit = el('button', { type: 'submit', className: 'btn' }, t('native.claim.subscribe', { price: money(price) })) - const error = el('p', { className: 'error', role: 'alert' }) + const submit = el( + 'button', + { type: 'submit', className: 'btn' }, + t('native.claim.subscribe', { price: money(price) }) + ); + const error = el('p', { className: 'error', role: 'alert' }); const form = el( 'form', { className: 'form', noValidate: true }, @@ -487,97 +528,97 @@ function showClaim(plan: CloudPlan, interval: 'month' | 'year', opts: { back?: b emailField, el('p', { className: 'hint' }, t('native.claim.emailHint')), submit, - error, - ) + error + ); - let status: SlugStatus | null = null - let timer: ReturnType | undefined + let status: SlugStatus | null = null; + let timer: ReturnType | undefined; const noteFor: Partial> = { available: t('native.claim.free'), taken: t('native.claim.taken'), invalid: t('native.claim.invalid'), reserved: t('native.claim.reserved'), - } + }; slugInput.addEventListener('input', () => { - clearTimeout(timer) - status = null - slugNote.textContent = '' - slugNote.className = 'hint' - slugField.classList.remove('bad') - const value = slugInput.value.trim().toLowerCase() - if (value.length < 3) return + clearTimeout(timer); + status = null; + slugNote.textContent = ''; + slugNote.className = 'hint'; + slugField.classList.remove('bad'); + const value = slugInput.value.trim().toLowerCase(); + if (value.length < 3) return; timer = setTimeout(async () => { - const result = await slugStatus(value) - if (slugInput.value.trim().toLowerCase() !== value) return - status = result - slugNote.textContent = noteFor[result] ?? '' - slugNote.className = `hint ${result === 'available' ? 'ok' : result === 'error' ? '' : 'bad'}` - }, 350) - }) + const result = await slugStatus(value); + if (slugInput.value.trim().toLowerCase() !== value) return; + status = result; + slugNote.textContent = noteFor[result] ?? ''; + slugNote.className = `hint ${result === 'available' ? 'ok' : result === 'error' ? '' : 'bad'}`; + }, 350); + }); const shake = (field: HTMLElement): void => { - void notify(NotificationType.Error) - field.classList.add('bad') - setTimeout(() => field.classList.remove('bad'), 350) - } + void notify(NotificationType.Error); + field.classList.add('bad'); + setTimeout(() => field.classList.remove('bad'), 350); + }; form.addEventListener('submit', async (event) => { - event.preventDefault() - error.textContent = '' - const slug = slugInput.value.trim().toLowerCase() - const email = emailInput.value.trim() + event.preventDefault(); + error.textContent = ''; + const slug = slugInput.value.trim().toLowerCase(); + const email = emailInput.value.trim(); if (slug.length < 3 || status === 'taken' || status === 'invalid' || status === 'reserved') { - shake(slugField) - slugInput.focus() - return + shake(slugField); + slugInput.focus(); + return; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { - shake(emailField) - emailInput.focus() - return + shake(emailField); + emailInput.focus(); + return; } - void tap(ImpactStyle.Medium) + void tap(ImpactStyle.Medium); const lock = (on: boolean) => { - slugInput.disabled = on - emailInput.disabled = on - submit.disabled = on - } - lock(true) - submit.replaceChildren(el('span', { className: 'spinner' }), t('native.connect.checking')) + slugInput.disabled = on; + emailInput.disabled = on; + submit.disabled = on; + }; + lock(true); + submit.replaceChildren(el('span', { className: 'spinner' }), t('native.connect.checking')); const restore = () => { - lock(false) - submit.replaceChildren(t('native.claim.subscribe', { price: money(price) })) - } + lock(false); + submit.replaceChildren(t('native.claim.subscribe', { price: money(price) })); + }; - const outcome = await iap.purchase(productIdFor(plan.id, interval)) + const outcome = await iap.purchase(productIdFor(plan.id, interval)); if (outcome.kind === 'cancelled') { - restore() - error.textContent = t('native.claim.cancelled') - return + restore(); + error.textContent = t('native.claim.cancelled'); + return; } if (outcome.kind === 'failed') { - void notify(NotificationType.Error) - restore() - error.textContent = t('native.claim.failed') - return + void notify(NotificationType.Error); + restore(); + error.textContent = t('native.claim.failed'); + return; } - const redeemed = await redeemPurchase(slug, email, outcome.purchase) + const redeemed = await redeemPurchase(slug, email, outcome.purchase); if (redeemed.ok && redeemed.instanceUrl) { - showProvisioning(redeemed.instanceUrl) - return + showProvisioning(redeemed.instanceUrl); + return; } - void notify(NotificationType.Error) - restore() + void notify(NotificationType.Error); + restore(); if (redeemed.code === 'slug-taken') { - shake(slugField) - slugNote.textContent = t('native.claim.taken') - slugNote.className = 'hint bad' + shake(slugField); + slugNote.textContent = t('native.claim.taken'); + slugNote.className = 'hint bad'; } else { - error.textContent = t('native.claim.redeemFailed') + error.textContent = t('native.claim.redeemFailed'); } - }) + }); show( el( @@ -586,125 +627,127 @@ function showClaim(plan: CloudPlan, interval: 'month' | 'year', opts: { back?: b topbar(() => showPlans({ back: true })), el('h1', { className: 'title-sm' }, t('native.claim.title')), el('p', { className: 'sub' }, t('native.claim.sub')), - form, + form ), - opts, - ) - requestAnimationFrame(() => slugInput.focus()) + opts + ); + requestAnimationFrame(() => slugInput.focus()); } /** The build runs on the server; the app waits on the new address and loads it the moment it answers. */ function showProvisioning(instanceUrl: string): void { const host = (() => { try { - return new URL(instanceUrl).host + return new URL(instanceUrl).host; } catch { - return instanceUrl + return instanceUrl; } - })() + })(); - const spinner = el('div', { className: 'provision-mark' }, el('span', { className: 'spinner' })) + const spinner = el('div', { className: 'provision-mark' }, el('span', { className: 'spinner' })); show( el( 'div', { className: 'screen-center' }, spinner, el('h1', { className: 'title-sm' }, t('native.provisioning.title')), - el('p', { className: 'sub' }, t('native.provisioning.sub', { host })), - ), - ) + el('p', { className: 'sub' }, t('native.provisioning.sub', { host })) + ) + ); - const deadline = Date.now() + 180_000 + const deadline = Date.now() + 180_000; const poll = async (): Promise => { - const result = await probeServer(instanceUrl, get) + const result = await probeServer(instanceUrl, get); if (result.ok) { - await connected(result.url) - return + await connected(result.url); + return; } if (Date.now() > deadline) { - showProvisioningSlow(instanceUrl) - return + showProvisioningSlow(instanceUrl); + return; } - setTimeout(() => void poll(), 4000) - } - setTimeout(() => void poll(), 3000) + setTimeout(() => void poll(), 4000); + }; + setTimeout(() => void poll(), 3000); } function showProvisioningSlow(instanceUrl: string): void { - void notify(NotificationType.Warning) - const retry = el('button', { type: 'button', className: 'btn' }, t('native.provisioning.retry')) + void notify(NotificationType.Warning); + const retry = el('button', { type: 'button', className: 'btn' }, t('native.provisioning.retry')); retry.addEventListener('click', () => { - void tap() - showProvisioning(instanceUrl) - }) + void tap(); + showProvisioning(instanceUrl); + }); show( el( 'div', { className: 'screen-center' }, el('h1', { className: 'title-sm' }, t('native.provisioning.failedTitle')), el('p', { className: 'sub' }, t('native.provisioning.failedSub')), - retry, - ), - ) + retry + ) + ); } // ── Shared actions ────────────────────────────────────────────────────────── async function runDemo(button: HTMLButtonElement, error?: HTMLElement): Promise { - void tap() - if (error) error.textContent = '' - button.disabled = true - const result = await probeDemo(get) + void tap(); + if (error) error.textContent = ''; + button.disabled = true; + const result = await probeDemo(get); if (result.ok) { - await connected(result.url) - return + await connected(result.url); + return; } - button.disabled = false + button.disabled = false; if (error) { - error.textContent = failureMessage(result.reason) + error.textContent = failureMessage(result.reason); } else { - void notify(NotificationType.Error) + void notify(NotificationType.Error); } } /** The tick, then the server takes over. A beat so the success registers. */ async function connected(url: string): Promise { - void notify(NotificationType.Success) + void notify(NotificationType.Success); const host = (() => { try { - return new URL(url).host + return new URL(url).host; } catch { - return url + return url; } - })() - const tick = el('div', { className: 'tick' }) - const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') - svg.setAttribute('viewBox', '0 0 24 24') - svg.setAttribute('fill', 'none') - svg.setAttribute('stroke', 'currentColor') - svg.setAttribute('stroke-width', '2.5') - svg.setAttribute('stroke-linecap', 'round') - svg.setAttribute('stroke-linejoin', 'round') - const p = document.createElementNS('http://www.w3.org/2000/svg', 'path') - p.setAttribute('d', I.check) - svg.appendChild(p) - tick.appendChild(svg) - show(el('div', { className: 'success' }, tick, el('h1', { className: 'title-sm' }, t('native.connecting', { host })))) - await new Promise((r) => setTimeout(r, 700)) - await TrekShell.setServer({ url }) + })(); + const tick = el('div', { className: 'tick' }); + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('fill', 'none'); + svg.setAttribute('stroke', 'currentColor'); + svg.setAttribute('stroke-width', '2.5'); + svg.setAttribute('stroke-linecap', 'round'); + svg.setAttribute('stroke-linejoin', 'round'); + const p = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + p.setAttribute('d', I.check); + svg.appendChild(p); + tick.appendChild(svg); + show( + el('div', { className: 'success' }, tick, el('h1', { className: 'title-sm' }, t('native.connecting', { host }))) + ); + await new Promise((r) => setTimeout(r, 700)); + await TrekShell.setServer({ url }); } function showUnreachable(server: string): void { - const retry = el('button', { type: 'button', className: 'btn' }, t('native.offline.retry')) + const retry = el('button', { type: 'button', className: 'btn' }, t('native.offline.retry')); retry.addEventListener('click', () => { - void tap() - retry.disabled = true - void TrekShell.reload() - }) - const change = el('button', { type: 'button', className: 'linkbtn' }, t('native.changeServer')) + void tap(); + retry.disabled = true; + void TrekShell.reload(); + }); + const change = el('button', { type: 'button', className: 'linkbtn' }, t('native.changeServer')); change.addEventListener('click', () => { - void tap() - void TrekShell.resetServer() - }) + void tap(); + void TrekShell.resetServer(); + }); show( el( 'div', @@ -714,9 +757,9 @@ function showUnreachable(server: string): void { el('p', { className: 'sub' }, t('native.offline.hint', { server: new URL(server).host })), retry, change, - footLinks(), - ), - ) + footLinks() + ) + ); } // ── Start ─────────────────────────────────────────────────────────────────── @@ -728,28 +771,28 @@ function showUnreachable(server: string): void { */ async function paintStatusBar(): Promise { try { - await StatusBar.setStyle({ style: Style.Dark }) + await StatusBar.setStyle({ style: Style.Dark }); } catch { /* web / unavailable: no native status bar to style */ } } async function start(): Promise { - void paintStatusBar() + void paintStatusBar(); iap.isAvailable().then((ok) => { - iapAvailable = ok - }) - const { url } = await TrekShell.getServer() + iapAvailable = ok; + }); + const { url } = await TrekShell.getServer(); if (!url) { - showWelcome() + showWelcome(); } else { // Only reached when loading the server failed. On iOS the stored copy of // its start page brings the offline-capable UI up anyway. - const { opened } = await TrekShell.openOfflineCopy() - if (opened) return - showUnreachable(url) + const { opened } = await TrekShell.openOfflineCopy(); + if (opened) return; + showUnreachable(url); } - await SplashScreen.hide() + await SplashScreen.hide(); } -void start() +void start(); diff --git a/client/src/components/Planner/useTripExport.ts b/client/src/components/Planner/useTripExport.ts index 0fc9ea94a..caa587296 100644 --- a/client/src/components/Planner/useTripExport.ts +++ b/client/src/components/Planner/useTripExport.ts @@ -5,8 +5,8 @@ import { useAuthStore } from '../../store/authStore'; import { useSettingsStore } from '../../store/settingsStore'; import type { AssignmentsMap, Category, Day, DayNote, Place, Reservation, Trip } from '../../types'; import { importChunk } from '../../utils/chunkReload'; -import { downloadBlob } from '../../utils/fileDownload'; import type { ViasByDay } from '../../utils/dayRouteVias'; +import { downloadBlob } from '../../utils/fileDownload'; import type { TourStageStops } from '../Map/dayTourProjection'; import { hasPersonalPlan } from '../PDF/pdfScope'; diff --git a/client/src/components/Settings/AboutTab.tsx b/client/src/components/Settings/AboutTab.tsx index f38b54d37..e513120af 100644 --- a/client/src/components/Settings/AboutTab.tsx +++ b/client/src/components/Settings/AboutTab.tsx @@ -15,10 +15,10 @@ import { import React, { type CSSProperties, type ReactNode } from 'react'; import { useTranslation } from '../../i18n'; import { useAuthStore } from '../../store/authStore'; +import { donationsShown } from '../../utils/donations'; import { fs } from '../shared/DialogShell'; import Section from './Section'; import { StatusPill } from './settingsKit'; -import { donationsShown } from '../../utils/donations'; interface Props { appVersion: string; diff --git a/client/src/components/Settings/useAccountSettings.ts b/client/src/components/Settings/useAccountSettings.ts index f5ec03b00..e409e6bb4 100644 --- a/client/src/components/Settings/useAccountSettings.ts +++ b/client/src/components/Settings/useAccountSettings.ts @@ -6,8 +6,8 @@ import { useDateFormat } from '../../hooks/useDateFormat'; import { useTranslation } from '../../i18n'; import { useAuthStore } from '../../store/authStore'; import { getApiErrorMessage, type UserWithOidc } from '../../types'; -import { downloadBlob } from '../../utils/fileDownload'; import { formatDisplayDateTime } from '../../utils/dateFormat'; +import { downloadBlob } from '../../utils/fileDownload'; import { passwordErrorKey } from '../../utils/passwordError'; import { useToast } from '../shared/Toast'; diff --git a/client/src/native/iap.ts b/client/src/native/iap.ts index 37f5bc69b..0707f7548 100644 --- a/client/src/native/iap.ts +++ b/client/src/native/iap.ts @@ -19,10 +19,7 @@ export interface Purchase { } /** What a purchase attempt came to. `cancelled` is the customer backing out, not an error. */ -type PurchaseOutcome = - | { kind: 'bought'; purchase: Purchase } - | { kind: 'cancelled' } - | { kind: 'failed' }; +type PurchaseOutcome = { kind: 'bought'; purchase: Purchase } | { kind: 'cancelled' } | { kind: 'failed' }; export interface IapClient { /** Whether this build can take a payment: the plugin is present and the store products exist. */ diff --git a/shared/scripts/i18n-parity.mjs b/shared/scripts/i18n-parity.mjs index 0832a221c..cf04516e3 100644 --- a/shared/scripts/i18n-parity.mjs +++ b/shared/scripts/i18n-parity.mjs @@ -34,14 +34,14 @@ // first column of the file, matching the regex below). Nested objects, function // bodies, and inline comments are ignored. This matches how `t(key)` calls // resolve at runtime in TranslationContext. - -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; import { getIntlLanguage } from '../src/i18n/languages.ts'; import { allPluralCategories, integerPluralCategories, pluralFormOf, pluralGroups } from '../src/i18n/plural.ts'; import { I18N_ROOT, listDomainFiles, listLocales, readCatalog } from './i18n-catalog.mjs'; import { compare, countUntranslated, formatGrown, readBaseline } from './i18n-untranslated.mjs'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + // Match a top-level translation key declaration: leading whitespace, then a // quoted key (must start with a lowercase letter), then a colon. This is the // exact pattern every domain file uses. Keys may hold a colon themselves @@ -103,7 +103,10 @@ export const NOT_PLURAL = [ { key: 'dawarich.duration.hoursMinutes', because: 'a duration in unit symbols (h, min), the same for any number' }, { key: 'files.uploadErrorSize', because: 'a size limit in a unit symbol (MB), the same for any number' }, { key: 'native.plans.storage', because: 'a storage size in a unit symbol (GB), the same for any number' }, - { key: 'native.plans.aiDocs', because: 'a monthly document allowance, always in the tens, so no singular ever shows' }, + { + key: 'native.plans.aiDocs', + because: 'a monthly document allowance, always in the tens, so no singular ever shows', + }, ]; /** @@ -189,10 +192,7 @@ function checkParity() { if (locale === 'en') continue; const localeFiles = listDomainFiles(locale); - const { missing: missingFiles, extra: extraFiles } = diffSets( - new Set(enFiles), - new Set(localeFiles), - ); + const { missing: missingFiles, extra: extraFiles } = diffSets(new Set(enFiles), new Set(localeFiles)); if (missingFiles.length || extraFiles.length) { report.fileDrift.push({ locale, missing: missingFiles, extra: extraFiles }); @@ -280,7 +280,9 @@ function formatReport(report) { } else if (grown.length === 0) { lines.push( 'Untranslated strings: OK' + - (lowerable ? ` (${lowerable} baseline entries can come down: node scripts/i18n-untranslated.mjs --update)` : ''), + (lowerable + ? ` (${lowerable} baseline entries can come down: node scripts/i18n-untranslated.mjs --update)` + : ''), ); } else { lines.push('Untranslated strings: grew past scripts/i18n-untranslated-baseline.json'); diff --git a/shared/src/i18n/az/native.ts b/shared/src/i18n/az/native.ts index ee3c6a263..637381f36 100644 --- a/shared/src/i18n/az/native.ts +++ b/shared/src/i18n/az/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Bu tətbiq {server} serverinə qoşulub.', 'native.welcome.headline': 'Səyahətləri birlikdə planlayın.', - 'native.welcome.sub': 'TREK hər günü, yeri, rezervasiyanı və xərci bütün qrupun birlikdə redaktə etdiyi tək bir canlı planda saxlayır.', + 'native.welcome.sub': + 'TREK hər günü, yeri, rezervasiyanı və xərci bütün qrupun birlikdə redaktə etdiyi tək bir canlı planda saxlayır.', 'native.welcome.connect': 'TREK-inizi qoşun', 'native.welcome.connectSub': 'Artıq istifadə etdiyiniz ünvanı açın', 'native.welcome.getCloud': 'TREK Cloud əldə edin', 'native.welcome.getCloudSub': 'Sizin üçün hostlanır, quraşdırma yoxdur', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'TREK-i sizin üçün, öz ünvanınızda hostlayırıq. Quraşdırmaq lazım deyil, saxlamaq lazım deyil: bir neçə dəqiqəyə hazırdır.', + 'native.cloud.sub': + 'TREK-i sizin üçün, öz ünvanınızda hostlayırıq. Quraşdırmaq lazım deyil, saxlamaq lazım deyil: bir neçə dəqiqəyə hazırdır.', 'native.cloud.f1': 'Öz ünvanınız, Almaniyadakı serverlərdə', 'native.cloud.f2': 'Bir neçə dəqiqəyə hazır, yeniləmələr bizdən', 'native.cloud.f3': 'Məlumatlarınız sizin qalır: reklam yoxdur, izləmə yoxdur', diff --git a/shared/src/i18n/br/native.ts b/shared/src/i18n/br/native.ts index 71cefcf3b..3e376bcc3 100644 --- a/shared/src/i18n/br/native.ts +++ b/shared/src/i18n/br/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Servidor', 'native.settings.serverHint': 'Este app está conectado a {server}.', 'native.welcome.headline': 'Planejem viagens juntos.', - 'native.welcome.sub': 'O TREK reúne cada dia, lugar, reserva e gasto em um único plano ao vivo que todo o grupo edita junto.', + 'native.welcome.sub': + 'O TREK reúne cada dia, lugar, reserva e gasto em um único plano ao vivo que todo o grupo edita junto.', 'native.welcome.connect': 'Conecte seu TREK', 'native.welcome.connectSub': 'Abra o endereço que você já usa', 'native.welcome.getCloud': 'Obter o TREK Cloud', 'native.welcome.getCloudSub': 'Hospedado para você, sem instalação', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Nós hospedamos o TREK para você, no seu próprio endereço. Nada para instalar, nada para manter: pronto em minutos.', + 'native.cloud.sub': + 'Nós hospedamos o TREK para você, no seu próprio endereço. Nada para instalar, nada para manter: pronto em minutos.', 'native.cloud.f1': 'Seu próprio endereço, em servidores na Alemanha', 'native.cloud.f2': 'Pronto em minutos, com as atualizações por nossa conta', 'native.cloud.f3': 'Seus dados continuam seus: sem anúncios, sem rastreamento', diff --git a/shared/src/i18n/ca/native.ts b/shared/src/i18n/ca/native.ts index c2f534297..5a937923f 100644 --- a/shared/src/i18n/ca/native.ts +++ b/shared/src/i18n/ca/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Servidor', 'native.settings.serverHint': 'Aquesta app està connectada a {server}.', 'native.welcome.headline': 'Planifiqueu viatges junts.', - 'native.welcome.sub': 'El TREK recull cada dia, lloc, reserva i despesa en un únic pla en viu que tot el grup edita alhora.', + 'native.welcome.sub': + 'El TREK recull cada dia, lloc, reserva i despesa en un únic pla en viu que tot el grup edita alhora.', 'native.welcome.connect': 'Connecta el teu TREK', 'native.welcome.connectSub': 'Obre l’adreça que ja fas servir', 'native.welcome.getCloud': 'Obtén el TREK Cloud', 'native.welcome.getCloudSub': 'Allotjat per tu, sense instal·lació', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Allotgem el TREK per tu, a la teva pròpia adreça. Res a instal·lar, res a mantenir: a punt en minuts.', + 'native.cloud.sub': + 'Allotgem el TREK per tu, a la teva pròpia adreça. Res a instal·lar, res a mantenir: a punt en minuts.', 'native.cloud.f1': 'La teva pròpia adreça, en servidors a Alemanya', 'native.cloud.f2': 'A punt en minuts, amb les actualitzacions a càrrec nostre', 'native.cloud.f3': 'Les teves dades segueixen sent teves: sense anuncis, sense seguiment', diff --git a/shared/src/i18n/cs/native.ts b/shared/src/i18n/cs/native.ts index eea5d1736..f2755e174 100644 --- a/shared/src/i18n/cs/native.ts +++ b/shared/src/i18n/cs/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Tato aplikace je připojena k {server}.', 'native.welcome.headline': 'Plánujte cesty společně.', - 'native.welcome.sub': 'TREK drží každý den, místo, rezervaci i výdaj v jednom živém plánu, který celá skupina upravuje společně.', + 'native.welcome.sub': + 'TREK drží každý den, místo, rezervaci i výdaj v jednom živém plánu, který celá skupina upravuje společně.', 'native.welcome.connect': 'Připojte svůj TREK', 'native.welcome.connectSub': 'Otevřete adresu, kterou už používáte', 'native.welcome.getCloud': 'Získat TREK Cloud', 'native.welcome.getCloudSub': 'Hostováno za vás, bez instalace', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'TREK hostujeme za vás, na vaší vlastní adrese. Nic neinstalujete, nic neudržujete: hotovo za pár minut.', + 'native.cloud.sub': + 'TREK hostujeme za vás, na vaší vlastní adrese. Nic neinstalujete, nic neudržujete: hotovo za pár minut.', 'native.cloud.f1': 'Vlastní adresa, na serverech v Německu', 'native.cloud.f2': 'Hotovo za pár minut, aktualizace řešíme my', 'native.cloud.f3': 'Vaše data zůstávají vaše: žádné reklamy, žádné sledování', diff --git a/shared/src/i18n/de/native.ts b/shared/src/i18n/de/native.ts index 54dd2a1e1..a69129bf7 100644 --- a/shared/src/i18n/de/native.ts +++ b/shared/src/i18n/de/native.ts @@ -26,13 +26,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Diese App ist mit {server} verbunden.', 'native.welcome.headline': 'Reisen gemeinsam planen.', - 'native.welcome.sub': 'TREK hält jeden Tag, Ort, jede Buchung und Kosten in einem gemeinsamen Plan, den eure ganze Gruppe live bearbeitet.', + 'native.welcome.sub': + 'TREK hält jeden Tag, Ort, jede Buchung und Kosten in einem gemeinsamen Plan, den eure ganze Gruppe live bearbeitet.', 'native.welcome.connect': 'Mit deinem TREK verbinden', 'native.welcome.connectSub': 'Die Adresse öffnen, die du schon nutzt', 'native.welcome.getCloud': 'TREK Cloud holen', 'native.welcome.getCloudSub': 'Für dich gehostet, ohne Einrichtung', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Wir hosten TREK für dich, auf deiner eigenen Adresse. Nichts installieren, nichts warten — in Minuten bereit.', + 'native.cloud.sub': + 'Wir hosten TREK für dich, auf deiner eigenen Adresse. Nichts installieren, nichts warten — in Minuten bereit.', 'native.cloud.f1': 'Eigene Adresse, auf Servern in Deutschland', 'native.cloud.f2': 'In Minuten eingerichtet, Updates übernehmen wir', 'native.cloud.f3': 'Deine Daten bleiben deine — keine Werbung, kein Tracking', diff --git a/shared/src/i18n/en/native.ts b/shared/src/i18n/en/native.ts index 2f984dbce..0a2a82f73 100644 --- a/shared/src/i18n/en/native.ts +++ b/shared/src/i18n/en/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', 'native.settings.serverHint': 'This app is connected to {server}.', 'native.welcome.headline': 'Plan trips together.', - 'native.welcome.sub': 'TREK keeps every day, place, booking and cost in one live plan your whole group edits together.', + 'native.welcome.sub': + 'TREK keeps every day, place, booking and cost in one live plan your whole group edits together.', 'native.welcome.connect': 'Connect your TREK', 'native.welcome.connectSub': 'Open the address you already use', 'native.welcome.getCloud': 'Get TREK Cloud', 'native.welcome.getCloudSub': 'Hosted for you, no setup', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'We host TREK for you, on your own address. Nothing to install, nothing to maintain — ready in minutes.', + 'native.cloud.sub': + 'We host TREK for you, on your own address. Nothing to install, nothing to maintain — ready in minutes.', 'native.cloud.f1': 'Your own address, on servers in Germany', 'native.cloud.f2': 'Set up in minutes, updates handled for you', 'native.cloud.f3': 'Your data stays yours — no ads, no tracking', diff --git a/shared/src/i18n/es/native.ts b/shared/src/i18n/es/native.ts index 72217ccb7..abdb1a598 100644 --- a/shared/src/i18n/es/native.ts +++ b/shared/src/i18n/es/native.ts @@ -26,13 +26,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Servidor', 'native.settings.serverHint': 'Esta app está conectada a {server}.', 'native.welcome.headline': 'Planificar viajes juntos.', - 'native.welcome.sub': 'TREK reúne cada día, lugar, reserva y gasto en un único plan en vivo que todo el grupo edita a la vez.', + 'native.welcome.sub': + 'TREK reúne cada día, lugar, reserva y gasto en un único plan en vivo que todo el grupo edita a la vez.', 'native.welcome.connect': 'Conecta tu TREK', 'native.welcome.connectSub': 'Abre la dirección que ya usas', 'native.welcome.getCloud': 'Obtén TREK Cloud', 'native.welcome.getCloudSub': 'Alojado para ti, sin instalación', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Alojamos TREK por ti, en tu propia dirección. Nada que instalar, nada que mantener: listo en minutos.', + 'native.cloud.sub': + 'Alojamos TREK por ti, en tu propia dirección. Nada que instalar, nada que mantener: listo en minutos.', 'native.cloud.f1': 'Tu propia dirección, en servidores en Alemania', 'native.cloud.f2': 'Listo en minutos, con las actualizaciones a nuestro cargo', 'native.cloud.f3': 'Tus datos siguen siendo tuyos: sin anuncios, sin rastreo', diff --git a/shared/src/i18n/et/native.ts b/shared/src/i18n/et/native.ts index 724d8d95a..f276fe0f7 100644 --- a/shared/src/i18n/et/native.ts +++ b/shared/src/i18n/et/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'See rakendus on ühendatud serveriga {server}.', 'native.welcome.headline': 'Planeerige reise koos.', - 'native.welcome.sub': 'TREK hoiab iga päeva, koha, broneeringu ja kulu ühes elavas plaanis, mida kogu rühm koos muudab.', + 'native.welcome.sub': + 'TREK hoiab iga päeva, koha, broneeringu ja kulu ühes elavas plaanis, mida kogu rühm koos muudab.', 'native.welcome.connect': 'Ühenda oma TREK', 'native.welcome.connectSub': 'Ava aadress, mida juba kasutad', 'native.welcome.getCloud': 'Hangi TREK Cloud', 'native.welcome.getCloudSub': 'Majutatud sinu eest, seadistamiseta', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Majutame TREK-i sinu eest, sinu enda aadressil. Midagi pole vaja paigaldada ega hallata: valmis mõne minutiga.', + 'native.cloud.sub': + 'Majutame TREK-i sinu eest, sinu enda aadressil. Midagi pole vaja paigaldada ega hallata: valmis mõne minutiga.', 'native.cloud.f1': 'Sinu enda aadress, Saksamaa serverites', 'native.cloud.f2': 'Valmis mõne minutiga, uuendused teeme meie', 'native.cloud.f3': 'Sinu andmed jäävad sinu omaks: reklaamideta, jälgimiseta', diff --git a/shared/src/i18n/fr/native.ts b/shared/src/i18n/fr/native.ts index 7649f3594..d4d0c966f 100644 --- a/shared/src/i18n/fr/native.ts +++ b/shared/src/i18n/fr/native.ts @@ -26,13 +26,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Serveur', 'native.settings.serverHint': 'Cette application est connectée à {server}.', 'native.welcome.headline': 'Planifiez vos voyages ensemble.', - 'native.welcome.sub': 'TREK réunit chaque jour, lieu, réservation et dépense dans un plan commun que tout le groupe modifie en direct.', + 'native.welcome.sub': + 'TREK réunit chaque jour, lieu, réservation et dépense dans un plan commun que tout le groupe modifie en direct.', 'native.welcome.connect': 'Connecter votre TREK', 'native.welcome.connectSub': 'Ouvrez l’adresse que vous utilisez déjà', 'native.welcome.getCloud': 'Obtenir TREK Cloud', 'native.welcome.getCloudSub': 'Hébergé pour vous, sans installation', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Nous hébergeons TREK pour vous, sur votre propre adresse. Rien à installer, rien à maintenir : prêt en quelques minutes.', + 'native.cloud.sub': + 'Nous hébergeons TREK pour vous, sur votre propre adresse. Rien à installer, rien à maintenir : prêt en quelques minutes.', 'native.cloud.f1': 'Votre propre adresse, sur des serveurs en Allemagne', 'native.cloud.f2': 'Prêt en quelques minutes, mises à jour prises en charge', 'native.cloud.f3': 'Vos données restent les vôtres : sans publicité, sans pistage', diff --git a/shared/src/i18n/gr/native.ts b/shared/src/i18n/gr/native.ts index 179f1c7f9..6bb563f04 100644 --- a/shared/src/i18n/gr/native.ts +++ b/shared/src/i18n/gr/native.ts @@ -27,13 +27,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Διακομιστής', 'native.settings.serverHint': 'Αυτή η εφαρμογή είναι συνδεδεμένη στο {server}.', 'native.welcome.headline': 'Σχεδιάστε ταξίδια μαζί.', - 'native.welcome.sub': 'Το TREK κρατά κάθε μέρα, μέρος, κράτηση και έξοδο σε ένα ζωντανό πλάνο που όλη η ομάδα επεξεργάζεται μαζί.', + 'native.welcome.sub': + 'Το TREK κρατά κάθε μέρα, μέρος, κράτηση και έξοδο σε ένα ζωντανό πλάνο που όλη η ομάδα επεξεργάζεται μαζί.', 'native.welcome.connect': 'Συνδέστε το TREK σας', 'native.welcome.connectSub': 'Ανοίξτε τη διεύθυνση που ήδη χρησιμοποιείτε', 'native.welcome.getCloud': 'Αποκτήστε το TREK Cloud', 'native.welcome.getCloudSub': 'Φιλοξενείται για εσάς, χωρίς εγκατάσταση', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Φιλοξενούμε το TREK για εσάς, στη δική σας διεύθυνση. Τίποτα για εγκατάσταση, τίποτα για συντήρηση: έτοιμο σε λίγα λεπτά.', + 'native.cloud.sub': + 'Φιλοξενούμε το TREK για εσάς, στη δική σας διεύθυνση. Τίποτα για εγκατάσταση, τίποτα για συντήρηση: έτοιμο σε λίγα λεπτά.', 'native.cloud.f1': 'Η δική σας διεύθυνση, σε διακομιστές στη Γερμανία', 'native.cloud.f2': 'Έτοιμο σε λίγα λεπτά, τις ενημερώσεις τις αναλαμβάνουμε εμείς', 'native.cloud.f3': 'Τα δεδομένα σας παραμένουν δικά σας: χωρίς διαφημίσεις, χωρίς παρακολούθηση', diff --git a/shared/src/i18n/he/index.ts b/shared/src/i18n/he/index.ts index d5a2e196c..9e2a4afbd 100644 --- a/shared/src/i18n/he/index.ts +++ b/shared/src/i18n/he/index.ts @@ -46,8 +46,8 @@ import mobileNav from './mobileNav'; import mobileSettings from './mobileSettings'; import mobileTrip from './mobileTrip'; import mobileVacay from './mobileVacay'; -import nav from './nav'; import native from './native'; +import nav from './nav'; import notif from './notif'; import notifications from './notifications'; import oauth from './oauth'; diff --git a/shared/src/i18n/hu/native.ts b/shared/src/i18n/hu/native.ts index e82ec227e..4729b294c 100644 --- a/shared/src/i18n/hu/native.ts +++ b/shared/src/i18n/hu/native.ts @@ -26,13 +26,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Szerver', 'native.settings.serverHint': 'Ez az alkalmazás ehhez csatlakozik: {server}.', 'native.welcome.headline': 'Tervezzetek utakat együtt.', - 'native.welcome.sub': 'A TREK minden napot, helyet, foglalást és költséget egyetlen élő tervben tart, amelyet az egész csapat együtt szerkeszt.', + 'native.welcome.sub': + 'A TREK minden napot, helyet, foglalást és költséget egyetlen élő tervben tart, amelyet az egész csapat együtt szerkeszt.', 'native.welcome.connect': 'Csatlakoztasd a TREK-edet', 'native.welcome.connectSub': 'Nyisd meg a címet, amit már használsz', 'native.welcome.getCloud': 'TREK Cloud beszerzése', 'native.welcome.getCloudSub': 'Neked tárolva, telepítés nélkül', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'A TREK-et neked üzemeltetjük, a saját címeden. Semmit nem kell telepíteni, semmit karbantartani: percek alatt kész.', + 'native.cloud.sub': + 'A TREK-et neked üzemeltetjük, a saját címeden. Semmit nem kell telepíteni, semmit karbantartani: percek alatt kész.', 'native.cloud.f1': 'Saját cím, németországi szervereken', 'native.cloud.f2': 'Percek alatt kész, a frissítéseket mi intézzük', 'native.cloud.f3': 'Az adataid a tieid maradnak: nincs hirdetés, nincs követés', @@ -71,7 +73,8 @@ const native: TranslationStrings = { 'native.provisioning.title': 'A TREK-edet állítjuk be', 'native.provisioning.sub': 'A(z) {host} előkészítése. Csak egy percet vesz igénybe.', 'native.provisioning.failedTitle': 'Majdnem kész', - 'native.provisioning.failedSub': 'A TREK-ed úton van. Próbáld újra egy pillanat múlva, vagy nyisd meg az appot később.', + 'native.provisioning.failedSub': + 'A TREK-ed úton van. Próbáld újra egy pillanat múlva, vagy nyisd meg az appot később.', 'native.provisioning.retry': 'Próbáld most', }; diff --git a/shared/src/i18n/id/native.ts b/shared/src/i18n/id/native.ts index 72495beec..1c1950ee3 100644 --- a/shared/src/i18n/id/native.ts +++ b/shared/src/i18n/id/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Aplikasi ini terhubung ke {server}.', 'native.welcome.headline': 'Rencanakan perjalanan bersama.', - 'native.welcome.sub': 'TREK menyatukan setiap hari, tempat, pemesanan, dan biaya dalam satu rencana langsung yang diedit seluruh grup bersama.', + 'native.welcome.sub': + 'TREK menyatukan setiap hari, tempat, pemesanan, dan biaya dalam satu rencana langsung yang diedit seluruh grup bersama.', 'native.welcome.connect': 'Hubungkan TREK Anda', 'native.welcome.connectSub': 'Buka alamat yang sudah Anda pakai', 'native.welcome.getCloud': 'Dapatkan TREK Cloud', 'native.welcome.getCloudSub': 'Dihosting untuk Anda, tanpa penyiapan', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Kami menghosting TREK untuk Anda, di alamat Anda sendiri. Tidak ada yang perlu dipasang atau dirawat: siap dalam hitungan menit.', + 'native.cloud.sub': + 'Kami menghosting TREK untuk Anda, di alamat Anda sendiri. Tidak ada yang perlu dipasang atau dirawat: siap dalam hitungan menit.', 'native.cloud.f1': 'Alamat Anda sendiri, di server di Jerman', 'native.cloud.f2': 'Siap dalam hitungan menit, pembaruan kami yang urus', 'native.cloud.f3': 'Data Anda tetap milik Anda: tanpa iklan, tanpa pelacakan', @@ -70,7 +72,8 @@ const native: TranslationStrings = { 'native.provisioning.title': 'Menyiapkan TREK Anda', 'native.provisioning.sub': 'Menyiapkan {host}. Ini hanya perlu satu menit.', 'native.provisioning.failedTitle': 'Hampir siap', - 'native.provisioning.failedSub': 'TREK Anda sedang dalam perjalanan. Coba lagi sebentar lagi, atau buka aplikasi nanti.', + 'native.provisioning.failedSub': + 'TREK Anda sedang dalam perjalanan. Coba lagi sebentar lagi, atau buka aplikasi nanti.', 'native.provisioning.retry': 'Coba sekarang', }; diff --git a/shared/src/i18n/it/native.ts b/shared/src/i18n/it/native.ts index c3501b672..880bcbab8 100644 --- a/shared/src/i18n/it/native.ts +++ b/shared/src/i18n/it/native.ts @@ -26,13 +26,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Questa app è collegata a {server}.', 'native.welcome.headline': 'Pianificate i viaggi insieme.', - 'native.welcome.sub': 'TREK raccoglie ogni giorno, luogo, prenotazione e spesa in un unico piano dal vivo che tutto il gruppo modifica insieme.', + 'native.welcome.sub': + 'TREK raccoglie ogni giorno, luogo, prenotazione e spesa in un unico piano dal vivo che tutto il gruppo modifica insieme.', 'native.welcome.connect': 'Collega il tuo TREK', 'native.welcome.connectSub': 'Apri l’indirizzo che già usi', 'native.welcome.getCloud': 'Attiva TREK Cloud', 'native.welcome.getCloudSub': 'Ospitato per te, senza installazione', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Ospitiamo TREK per te, sul tuo indirizzo. Niente da installare, niente da mantenere: pronto in pochi minuti.', + 'native.cloud.sub': + 'Ospitiamo TREK per te, sul tuo indirizzo. Niente da installare, niente da mantenere: pronto in pochi minuti.', 'native.cloud.f1': 'Il tuo indirizzo, su server in Germania', 'native.cloud.f2': 'Pronto in pochi minuti, aggiornamenti inclusi', 'native.cloud.f3': 'I tuoi dati restano tuoi: niente pubblicità, niente tracciamento', diff --git a/shared/src/i18n/ja/native.ts b/shared/src/i18n/ja/native.ts index 2f4b17e91..6b7005b69 100644 --- a/shared/src/i18n/ja/native.ts +++ b/shared/src/i18n/ja/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'サーバー', 'native.settings.serverHint': 'このアプリは {server} に接続されています。', 'native.welcome.headline': 'みんなで旅を計画。', - 'native.welcome.sub': 'TREKは、毎日・場所・予約・費用を、グループ全員で一緒に編集できる1つのライブプランにまとめます。', + 'native.welcome.sub': + 'TREKは、毎日・場所・予約・費用を、グループ全員で一緒に編集できる1つのライブプランにまとめます。', 'native.welcome.connect': 'あなたのTREKに接続', 'native.welcome.connectSub': 'すでに使っているアドレスを開く', 'native.welcome.getCloud': 'TREK Cloudを始める', 'native.welcome.getCloudSub': 'あなたのために用意、設定不要', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'TREKをあなたのために、あなた専用のアドレスでホストします。インストールも管理も不要で、数分で使えます。', + 'native.cloud.sub': + 'TREKをあなたのために、あなた専用のアドレスでホストします。インストールも管理も不要で、数分で使えます。', 'native.cloud.f1': '専用アドレス、ドイツのサーバー上', 'native.cloud.f2': '数分で準備完了、アップデートはお任せ', 'native.cloud.f3': 'データはあなたのもの。広告なし、追跡なし', @@ -70,7 +72,8 @@ const native: TranslationStrings = { 'native.provisioning.title': 'TREKを準備中', 'native.provisioning.sub': '{host} を準備しています。1分ほどで完了します。', 'native.provisioning.failedTitle': 'もうすぐ完了', - 'native.provisioning.failedSub': 'TREKはまもなく準備できます。少ししてからもう一度試すか、後でアプリを開いてください。', + 'native.provisioning.failedSub': + 'TREKはまもなく準備できます。少ししてからもう一度試すか、後でアプリを開いてください。', 'native.provisioning.retry': '今すぐ試す', }; diff --git a/shared/src/i18n/nl/native.ts b/shared/src/i18n/nl/native.ts index 66410cef8..d42c4d482 100644 --- a/shared/src/i18n/nl/native.ts +++ b/shared/src/i18n/nl/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Deze app is verbonden met {server}.', 'native.welcome.headline': 'Plan reizen samen.', - 'native.welcome.sub': 'TREK houdt elke dag, plek, boeking en kostenpost bij in één live plan dat je hele groep samen bewerkt.', + 'native.welcome.sub': + 'TREK houdt elke dag, plek, boeking en kostenpost bij in één live plan dat je hele groep samen bewerkt.', 'native.welcome.connect': 'Verbind je TREK', 'native.welcome.connectSub': 'Open het adres dat je al gebruikt', 'native.welcome.getCloud': 'Neem TREK Cloud', 'native.welcome.getCloudSub': 'Voor je gehost, zonder installatie', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Wij hosten TREK voor je, op je eigen adres. Niets te installeren, niets te onderhouden: binnen enkele minuten klaar.', + 'native.cloud.sub': + 'Wij hosten TREK voor je, op je eigen adres. Niets te installeren, niets te onderhouden: binnen enkele minuten klaar.', 'native.cloud.f1': 'Je eigen adres, op servers in Duitsland', 'native.cloud.f2': 'In enkele minuten klaar, updates doen wij', 'native.cloud.f3': 'Je gegevens blijven van jou: geen advertenties, geen tracking', diff --git a/shared/src/i18n/pl/native.ts b/shared/src/i18n/pl/native.ts index 30323de8b..5143e9b20 100644 --- a/shared/src/i18n/pl/native.ts +++ b/shared/src/i18n/pl/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Serwer', 'native.settings.serverHint': 'Ta aplikacja jest połączona z {server}.', 'native.welcome.headline': 'Planujcie podróże razem.', - 'native.welcome.sub': 'TREK zbiera każdy dzień, miejsce, rezerwację i wydatek w jednym wspólnym planie, który cała grupa edytuje na żywo.', + 'native.welcome.sub': + 'TREK zbiera każdy dzień, miejsce, rezerwację i wydatek w jednym wspólnym planie, który cała grupa edytuje na żywo.', 'native.welcome.connect': 'Połącz swój TREK', 'native.welcome.connectSub': 'Otwórz adres, którego już używasz', 'native.welcome.getCloud': 'Weź TREK Cloud', 'native.welcome.getCloudSub': 'Hostowany dla Ciebie, bez instalacji', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Hostujemy TREK za Ciebie, pod Twoim własnym adresem. Nic do instalowania, nic do utrzymywania: gotowe w kilka minut.', + 'native.cloud.sub': + 'Hostujemy TREK za Ciebie, pod Twoim własnym adresem. Nic do instalowania, nic do utrzymywania: gotowe w kilka minut.', 'native.cloud.f1': 'Twój własny adres, na serwerach w Niemczech', 'native.cloud.f2': 'Gotowe w kilka minut, aktualizacje po naszej stronie', 'native.cloud.f3': 'Twoje dane zostają Twoje: bez reklam, bez śledzenia', diff --git a/shared/src/i18n/ru/native.ts b/shared/src/i18n/ru/native.ts index 812b64562..cf4dae78c 100644 --- a/shared/src/i18n/ru/native.ts +++ b/shared/src/i18n/ru/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Сервер', 'native.settings.serverHint': 'Приложение подключено к {server}.', 'native.welcome.headline': 'Планируйте поездки вместе.', - 'native.welcome.sub': 'TREK хранит каждый день, место, бронирование и расход в одном живом плане, который вся группа редактирует вместе.', + 'native.welcome.sub': + 'TREK хранит каждый день, место, бронирование и расход в одном живом плане, который вся группа редактирует вместе.', 'native.welcome.connect': 'Подключите свой TREK', 'native.welcome.connectSub': 'Откройте адрес, которым уже пользуетесь', 'native.welcome.getCloud': 'Получить TREK Cloud', 'native.welcome.getCloudSub': 'Размещён для вас, без настройки', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Мы разместим TREK для вас, на вашем собственном адресе. Ничего не нужно устанавливать и обслуживать: готово за минуты.', + 'native.cloud.sub': + 'Мы разместим TREK для вас, на вашем собственном адресе. Ничего не нужно устанавливать и обслуживать: готово за минуты.', 'native.cloud.f1': 'Собственный адрес, на серверах в Германии', 'native.cloud.f2': 'Готово за минуты, обновления берём на себя', 'native.cloud.f3': 'Ваши данные остаются вашими: без рекламы, без слежки', @@ -70,7 +72,8 @@ const native: TranslationStrings = { 'native.provisioning.title': 'Настраиваем ваш TREK', 'native.provisioning.sub': 'Готовим {host}. Это займёт всего минуту.', 'native.provisioning.failedTitle': 'Почти готово', - 'native.provisioning.failedSub': 'Ваш TREK уже в пути. Повторите попытку через мгновение или откройте приложение позже.', + 'native.provisioning.failedSub': + 'Ваш TREK уже в пути. Повторите попытку через мгновение или откройте приложение позже.', 'native.provisioning.retry': 'Повторить сейчас', }; diff --git a/shared/src/i18n/sk/native.ts b/shared/src/i18n/sk/native.ts index 77712d843..f9dd48585 100644 --- a/shared/src/i18n/sk/native.ts +++ b/shared/src/i18n/sk/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Táto aplikácia je pripojená k serveru {server}.', 'native.welcome.headline': 'Plánujte cesty spoločne.', - 'native.welcome.sub': 'TREK drží každý deň, miesto, rezerváciu aj výdavok v jednom živom pláne, ktorý celá skupina upravuje spoločne.', + 'native.welcome.sub': + 'TREK drží každý deň, miesto, rezerváciu aj výdavok v jednom živom pláne, ktorý celá skupina upravuje spoločne.', 'native.welcome.connect': 'Pripojte svoj TREK', 'native.welcome.connectSub': 'Otvorte adresu, ktorú už používate', 'native.welcome.getCloud': 'Získať TREK Cloud', 'native.welcome.getCloudSub': 'Hostované za vás, bez inštalácie', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'TREK hostujeme za vás, na vašej vlastnej adrese. Nič neinštalujete, nič neudržiavate: hotovo za pár minút.', + 'native.cloud.sub': + 'TREK hostujeme za vás, na vašej vlastnej adrese. Nič neinštalujete, nič neudržiavate: hotovo za pár minút.', 'native.cloud.f1': 'Vlastná adresa, na serveroch v Nemecku', 'native.cloud.f2': 'Hotovo za pár minút, aktualizácie riešime my', 'native.cloud.f3': 'Vaše údaje zostávajú vaše: žiadne reklamy, žiadne sledovanie', diff --git a/shared/src/i18n/sv/native.ts b/shared/src/i18n/sv/native.ts index 033c805e8..9bef2c7d6 100644 --- a/shared/src/i18n/sv/native.ts +++ b/shared/src/i18n/sv/native.ts @@ -26,13 +26,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Server', // same-as-en 'native.settings.serverHint': 'Den här appen är ansluten till {server}.', 'native.welcome.headline': 'Planera resor tillsammans.', - 'native.welcome.sub': 'TREK samlar varje dag, plats, bokning och kostnad i en levande plan som hela gruppen redigerar tillsammans.', + 'native.welcome.sub': + 'TREK samlar varje dag, plats, bokning och kostnad i en levande plan som hela gruppen redigerar tillsammans.', 'native.welcome.connect': 'Anslut din TREK', 'native.welcome.connectSub': 'Öppna adressen du redan använder', 'native.welcome.getCloud': 'Skaffa TREK Cloud', 'native.welcome.getCloudSub': 'Hostat åt dig, utan installation', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Vi hostar TREK åt dig, på din egen adress. Inget att installera, inget att underhålla: klart på några minuter.', + 'native.cloud.sub': + 'Vi hostar TREK åt dig, på din egen adress. Inget att installera, inget att underhålla: klart på några minuter.', 'native.cloud.f1': 'Din egen adress, på servrar i Tyskland', 'native.cloud.f2': 'Klart på några minuter, uppdateringar sköter vi', 'native.cloud.f3': 'Dina data förblir dina: inga annonser, ingen spårning', diff --git a/shared/src/i18n/tr/native.ts b/shared/src/i18n/tr/native.ts index 466ab03bf..3edea167f 100644 --- a/shared/src/i18n/tr/native.ts +++ b/shared/src/i18n/tr/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Sunucu', 'native.settings.serverHint': 'Bu uygulama {server} adresine bağlı.', 'native.welcome.headline': 'Gezileri birlikte planlayın.', - 'native.welcome.sub': 'TREK her günü, yeri, rezervasyonu ve masrafı tüm grubun birlikte düzenlediği tek bir canlı planda tutar.', + 'native.welcome.sub': + 'TREK her günü, yeri, rezervasyonu ve masrafı tüm grubun birlikte düzenlediği tek bir canlı planda tutar.', 'native.welcome.connect': 'TREK’inizi bağlayın', 'native.welcome.connectSub': 'Zaten kullandığınız adresi açın', 'native.welcome.getCloud': 'TREK Cloud’u edinin', 'native.welcome.getCloudSub': 'Sizin için barındırılır, kurulum yok', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'TREK’i sizin için, kendi adresinizde barındırırız. Kuracak bir şey yok, bakımı yok: dakikalar içinde hazır.', + 'native.cloud.sub': + 'TREK’i sizin için, kendi adresinizde barındırırız. Kuracak bir şey yok, bakımı yok: dakikalar içinde hazır.', 'native.cloud.f1': 'Kendi adresiniz, Almanya’daki sunucularda', 'native.cloud.f2': 'Dakikalar içinde hazır, güncellemeler bizden', 'native.cloud.f3': 'Verileriniz sizin kalır: reklam yok, takip yok', diff --git a/shared/src/i18n/uk/native.ts b/shared/src/i18n/uk/native.ts index 237beefed..04ce76bb8 100644 --- a/shared/src/i18n/uk/native.ts +++ b/shared/src/i18n/uk/native.ts @@ -25,13 +25,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Сервер', 'native.settings.serverHint': 'Застосунок підключено до {server}.', 'native.welcome.headline': 'Плануйте подорожі разом.', - 'native.welcome.sub': 'TREK зберігає кожен день, місце, бронювання та витрату в одному живому плані, який уся група редагує разом.', + 'native.welcome.sub': + 'TREK зберігає кожен день, місце, бронювання та витрату в одному живому плані, який уся група редагує разом.', 'native.welcome.connect': 'Підключіть свій TREK', 'native.welcome.connectSub': 'Відкрийте адресу, якою вже користуєтеся', 'native.welcome.getCloud': 'Отримати TREK Cloud', 'native.welcome.getCloudSub': 'Розміщено для вас, без налаштування', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Ми розміщуємо TREK для вас, на вашій власній адресі. Нічого не потрібно встановлювати чи обслуговувати: готово за кілька хвилин.', + 'native.cloud.sub': + 'Ми розміщуємо TREK для вас, на вашій власній адресі. Нічого не потрібно встановлювати чи обслуговувати: готово за кілька хвилин.', 'native.cloud.f1': 'Власна адреса, на серверах у Німеччині', 'native.cloud.f2': 'Готово за кілька хвилин, оновлення беремо на себе', 'native.cloud.f3': 'Ваші дані залишаються вашими: без реклами, без стеження', diff --git a/shared/src/i18n/vi/native.ts b/shared/src/i18n/vi/native.ts index c5e87f289..a5b570a20 100644 --- a/shared/src/i18n/vi/native.ts +++ b/shared/src/i18n/vi/native.ts @@ -26,13 +26,15 @@ const native: TranslationStrings = { 'native.settings.server': 'Máy chủ', 'native.settings.serverHint': 'Ứng dụng này đang kết nối với {server}.', 'native.welcome.headline': 'Cùng nhau lên kế hoạch chuyến đi.', - 'native.welcome.sub': 'TREK giữ mọi ngày, địa điểm, đặt chỗ và chi phí trong một kế hoạch trực tiếp mà cả nhóm cùng chỉnh sửa.', + 'native.welcome.sub': + 'TREK giữ mọi ngày, địa điểm, đặt chỗ và chi phí trong một kế hoạch trực tiếp mà cả nhóm cùng chỉnh sửa.', 'native.welcome.connect': 'Kết nối TREK của bạn', 'native.welcome.connectSub': 'Mở địa chỉ bạn vẫn dùng', 'native.welcome.getCloud': 'Nhận TREK Cloud', 'native.welcome.getCloudSub': 'Được lưu trữ sẵn cho bạn, không cần cài đặt', 'native.cloud.title': 'TREK Cloud', - 'native.cloud.sub': 'Chúng tôi lưu trữ TREK cho bạn, trên địa chỉ của riêng bạn. Không cần cài đặt, không cần bảo trì: sẵn sàng trong vài phút.', + 'native.cloud.sub': + 'Chúng tôi lưu trữ TREK cho bạn, trên địa chỉ của riêng bạn. Không cần cài đặt, không cần bảo trì: sẵn sàng trong vài phút.', 'native.cloud.f1': 'Địa chỉ riêng của bạn, trên máy chủ ở Đức', 'native.cloud.f2': 'Sẵn sàng trong vài phút, bản cập nhật do chúng tôi lo', 'native.cloud.f3': 'Dữ liệu của bạn vẫn là của bạn: không quảng cáo, không theo dõi',