diff --git a/client/native-shell/main.ts b/client/native-shell/main.ts index 6f99b96a5..f4f7bb4af 100644 --- a/client/native-shell/main.ts +++ b/client/native-shell/main.ts @@ -246,8 +246,11 @@ function feature(path: string, text: string): HTMLElement { } function showCloud(opts: { back?: boolean } = {}): void { - const cta = el('a', { className: 'btn', href: CLOUD_URL, target: '_blank', rel: 'noopener noreferrer' }, t('native.cloud.cta'), icon(I.chevron, 20, 'chev')) - cta.addEventListener('click', () => void tap()) + const cta = el('button', { type: 'button', className: 'btn' }, t('native.cloud.cta'), icon(I.chevron, 20, 'chev')) + cta.addEventListener('click', () => { + void tap() + showPlans() + }) const have = el('button', { type: 'button', className: 'linkbtn' }, t('native.cloud.haveServer')) have.addEventListener('click', () => { @@ -271,6 +274,128 @@ function showCloud(opts: { back?: boolean } = {}): void { ) } +// ── Choose a plan (TREK Cloud) ────────────────────────────────────────────── +interface CloudPlan { + 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) +} + +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 + } catch { + return null + } +} + +function subscribe(plan: CloudPlan): void { + void tap(ImpactStyle.Medium) + // Phase 2: the subscription is an Apple/Google in-app purchase. Until the + // store products exist, the order is finished on the web for this plan. + 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)) + return el( + 'div', + { className: featured ? 'plan featured' : 'plan' }, + el( + 'div', + { className: 'plan-top' }, + el('span', { className: 'plan-name' }, plan.name), + 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')), + ), + sub, + ) +} + +function showPlans(opts: { back?: boolean } = {}): void { + let interval: 'month' | 'year' = 'month' + let plans: CloudPlan[] | null = null + + 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 paintSeg = () => { + 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 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() + + const load = async () => { + plans = await fetchPlans() + if (plans) { + renderList() + return + } + 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) + } + + show( + el( + 'div', + {}, + topbar(() => showCloud({ back: true })), + el('h1', { className: 'title-sm' }, t('native.plans.title')), + el('p', { className: 'sub' }, t('native.plans.sub')), + seg, + content, + el('p', { className: 'note' }, t('native.plans.soon')), + ), + opts, + ) + void load() +} + // ── Shared actions ────────────────────────────────────────────────────────── async function runDemo(button: HTMLButtonElement, error?: HTMLElement): Promise { void tap() diff --git a/client/native-shell/shell.css b/client/native-shell/shell.css index 8793c757f..f5d38906a 100644 --- a/client/native-shell/shell.css +++ b/client/native-shell/shell.css @@ -517,3 +517,138 @@ a.linkbtn { transition-duration: 0.001ms !important; } } + +/* ── Plan picker (cloud subscription) ────────────────────────────────────── */ +.segmented { + display: flex; + gap: 4px; + padding: 4px; + background: var(--surface); + border: 1px solid var(--edge); + border-radius: 13px; + align-self: stretch; +} +.segmented button { + flex: 1; + font: inherit; + font-weight: 600; + font-size: 0.95rem; + padding: 9px; + border: 0; + border-radius: 9px; + background: none; + color: var(--muted); + cursor: pointer; + transition: + background 0.2s, + color 0.2s; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 7px; +} +.segmented button[aria-pressed='true'] { + background: var(--accent); + color: var(--accent-ink); +} +.segmented .save { + font-size: 0.72rem; + font-weight: 600; + padding: 1px 6px; + border-radius: 999px; + background: color-mix(in srgb, var(--ok) 22%, transparent); + color: var(--ok); +} +.segmented button[aria-pressed='true'] .save { + background: color-mix(in srgb, var(--accent-ink) 16%, transparent); + color: var(--accent-ink); +} + +.plans { + display: flex; + flex-direction: column; + gap: 12px; +} +.plan { + border: 1px solid var(--edge); + border-radius: var(--radius); + padding: 16px; + background: var(--surface); + transition: + border-color 0.2s, + transform 0.14s var(--ease); +} +.plan:active { + transform: scale(0.99); +} +.plan.featured { + border-color: var(--edge-strong); + background: var(--surface-2); +} +.plan-top { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 10px; +} +.plan-name { + font-weight: 700; + font-size: 1.12rem; +} +.plan-price { + font-weight: 700; + font-size: 1.3rem; + letter-spacing: -0.01em; +} +.plan-price small { + font-weight: 500; + font-size: 0.8rem; + color: var(--muted); + margin-left: 3px; +} +.plan-feats { + margin: 10px 0 14px; + display: flex; + flex-direction: column; + gap: 6px; + color: var(--muted); + font-size: 0.92rem; +} +.plan-feats span { + display: flex; + align-items: center; + gap: 8px; +} +.plan-feats svg { + flex: none; + color: var(--ok); +} +.plan .btn { + width: 100%; + min-height: 48px; + padding: 13px; +} +.plan:not(.featured) .btn { + background: var(--surface-2); + color: var(--ink); + border-color: var(--edge); +} + +.loading { + display: flex; + justify-content: center; + padding: 40px 0; +} +.loading .spinner { + width: 26px; + height: 26px; + border-color: var(--edge-strong); + border-top-color: var(--ink); +} +.note { + text-align: center; + color: var(--faint); + font-size: 0.84rem; + margin: 2px 0 0; + line-height: 1.45; +} diff --git a/shared/src/i18n/ar/native.ts b/shared/src/i18n/ar/native.ts index 07649ce9c..9990786cc 100644 --- a/shared/src/i18n/ar/native.ts +++ b/shared/src/i18n/ar/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/az/native.ts b/shared/src/i18n/az/native.ts index 87038e4ba..8b2f499fc 100644 --- a/shared/src/i18n/az/native.ts +++ b/shared/src/i18n/az/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/br/native.ts b/shared/src/i18n/br/native.ts index 7621cefba..2e6745d02 100644 --- a/shared/src/i18n/br/native.ts +++ b/shared/src/i18n/br/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/ca/native.ts b/shared/src/i18n/ca/native.ts index b3aff0bcb..392c624b5 100644 --- a/shared/src/i18n/ca/native.ts +++ b/shared/src/i18n/ca/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/cs/native.ts b/shared/src/i18n/cs/native.ts index 854f0382d..3d733422a 100644 --- a/shared/src/i18n/cs/native.ts +++ b/shared/src/i18n/cs/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/de/native.ts b/shared/src/i18n/de/native.ts index c0a851f97..4422fd73c 100644 --- a/shared/src/i18n/de/native.ts +++ b/shared/src/i18n/de/native.ts @@ -40,6 +40,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'Ich habe schon ein TREK', 'native.connecting': 'Verbinde mit {host}', 'native.back': 'Zurück', + 'native.plans.title': 'Tarif wählen', + 'native.plans.sub': 'Gehostetes TREK auf deiner eigenen Adresse. Jederzeit änder- oder kündbar.', + 'native.plans.monthly': 'Monatlich', + 'native.plans.yearly': 'Jährlich', + 'native.plans.save': '2 Monate gratis', + 'native.plans.perMonth': 'pro Monat', + 'native.plans.perYear': 'pro Jahr', + 'native.plans.aiDocs': '{n} KI-Dokumente im Monat', + 'native.plans.noAi': 'Keine KI-Dokumente', + 'native.plans.storage': '{n} GB Speicher', + 'native.plans.unlimited': 'Unbegrenzt Orte, Reisen und Personen', + 'native.plans.subscribe': 'Abonnieren', + 'native.plans.loadError': 'Die Tarife konnten nicht geladen werden. Prüfe deine Verbindung und versuch es erneut.', + 'native.plans.retry': 'Erneut versuchen', + 'native.plans.soon': 'In-App-Abos kommen bald. Vorerst auf der Website abschließen.', }; export default native; diff --git a/shared/src/i18n/en/native.ts b/shared/src/i18n/en/native.ts index fe8da4971..cbd9163c9 100644 --- a/shared/src/i18n/en/native.ts +++ b/shared/src/i18n/en/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/es/native.ts b/shared/src/i18n/es/native.ts index b0b7af1cf..1a7768d8c 100644 --- a/shared/src/i18n/es/native.ts +++ b/shared/src/i18n/es/native.ts @@ -40,6 +40,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/et/native.ts b/shared/src/i18n/et/native.ts index 649be95de..7f7f0b5ce 100644 --- a/shared/src/i18n/et/native.ts +++ b/shared/src/i18n/et/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/fr/native.ts b/shared/src/i18n/fr/native.ts index fb3a98150..81dc25789 100644 --- a/shared/src/i18n/fr/native.ts +++ b/shared/src/i18n/fr/native.ts @@ -40,6 +40,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/gr/native.ts b/shared/src/i18n/gr/native.ts index ab66261ee..71f4389b4 100644 --- a/shared/src/i18n/gr/native.ts +++ b/shared/src/i18n/gr/native.ts @@ -41,6 +41,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/hu/native.ts b/shared/src/i18n/hu/native.ts index 4e4a64bd4..2bcb3e2b0 100644 --- a/shared/src/i18n/hu/native.ts +++ b/shared/src/i18n/hu/native.ts @@ -40,6 +40,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/id/native.ts b/shared/src/i18n/id/native.ts index 2abf64497..00a4cfc40 100644 --- a/shared/src/i18n/id/native.ts +++ b/shared/src/i18n/id/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/it/native.ts b/shared/src/i18n/it/native.ts index a858aafb4..9f08f0d74 100644 --- a/shared/src/i18n/it/native.ts +++ b/shared/src/i18n/it/native.ts @@ -40,6 +40,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/ja/native.ts b/shared/src/i18n/ja/native.ts index 22b89231a..34e3392de 100644 --- a/shared/src/i18n/ja/native.ts +++ b/shared/src/i18n/ja/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/ko/native.ts b/shared/src/i18n/ko/native.ts index bdbe07eb2..d7ce6ce37 100644 --- a/shared/src/i18n/ko/native.ts +++ b/shared/src/i18n/ko/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/nl/native.ts b/shared/src/i18n/nl/native.ts index e2cdbd579..a3b5af9b6 100644 --- a/shared/src/i18n/nl/native.ts +++ b/shared/src/i18n/nl/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/pl/native.ts b/shared/src/i18n/pl/native.ts index 2a82571da..2f74452b0 100644 --- a/shared/src/i18n/pl/native.ts +++ b/shared/src/i18n/pl/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/ru/native.ts b/shared/src/i18n/ru/native.ts index 1b25e3d39..4b1015948 100644 --- a/shared/src/i18n/ru/native.ts +++ b/shared/src/i18n/ru/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/sk/native.ts b/shared/src/i18n/sk/native.ts index 8a9f05161..4c1856eea 100644 --- a/shared/src/i18n/sk/native.ts +++ b/shared/src/i18n/sk/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/sv/native.ts b/shared/src/i18n/sv/native.ts index 974e07111..07e7b0576 100644 --- a/shared/src/i18n/sv/native.ts +++ b/shared/src/i18n/sv/native.ts @@ -40,6 +40,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/th/native.ts b/shared/src/i18n/th/native.ts index 018915996..1a0782623 100644 --- a/shared/src/i18n/th/native.ts +++ b/shared/src/i18n/th/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/tr/native.ts b/shared/src/i18n/tr/native.ts index 603b9e03a..50fa837b7 100644 --- a/shared/src/i18n/tr/native.ts +++ b/shared/src/i18n/tr/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/uk/native.ts b/shared/src/i18n/uk/native.ts index df7e5d499..6a07a1d2d 100644 --- a/shared/src/i18n/uk/native.ts +++ b/shared/src/i18n/uk/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/vi/native.ts b/shared/src/i18n/vi/native.ts index 575009ece..140d0a7d8 100644 --- a/shared/src/i18n/vi/native.ts +++ b/shared/src/i18n/vi/native.ts @@ -40,6 +40,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/zh-TW/native.ts b/shared/src/i18n/zh-TW/native.ts index 2a2f837f4..254a969e5 100644 --- a/shared/src/i18n/zh-TW/native.ts +++ b/shared/src/i18n/zh-TW/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native; diff --git a/shared/src/i18n/zh/native.ts b/shared/src/i18n/zh/native.ts index ced5e69ad..583de31f5 100644 --- a/shared/src/i18n/zh/native.ts +++ b/shared/src/i18n/zh/native.ts @@ -39,6 +39,21 @@ const native: TranslationStrings = { 'native.cloud.haveServer': 'I already have a TREK', 'native.connecting': 'Connecting to {host}', 'native.back': 'Back', + 'native.plans.title': 'Choose your plan', + 'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.', + 'native.plans.monthly': 'Monthly', + 'native.plans.yearly': 'Yearly', + 'native.plans.save': '2 months free', + 'native.plans.perMonth': 'per month', + 'native.plans.perYear': 'per year', + 'native.plans.aiDocs': '{n} AI documents a month', + 'native.plans.noAi': 'No AI documents', + 'native.plans.storage': '{n} GB storage', + 'native.plans.unlimited': 'Unlimited places, trips and people', + 'native.plans.subscribe': 'Subscribe', + 'native.plans.loadError': 'Could not load the plans. Check your connection and try again.', + 'native.plans.retry': 'Try again', + 'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.', }; export default native;