From 037e739807ab2e80ee0bffe95946b05585d4d9bb Mon Sep 17 00:00:00 2001 From: Maurice Date: Thu, 5 Nov 2026 14:36:21 +0100 Subject: [PATCH] feat(native): the in-app TREK Cloud subscription flow After picking a plan, the app now carries the whole purchase itself when the build can take one: choose your address (checked for free as you type, against the same endpoint the website uses), your email, and subscribe. The receipt goes to /api/iap/redeem, and a Setting up your TREK screen waits on the new address and loads it the moment it answers. The purchase sits behind an IapClient seam (src/native/iap.ts): a fake drives the whole flow in development and preview, the native client reports itself unavailable until the store plugin and products exist, and until then the plan picker still finishes on the web, so nothing half-wired faces a customer. New claim and provisioning strings across all locales (en, de and the five largest European languages translated). --- client/native-shell/main.ts | 259 +++++++++++++++++++++++++++++++- client/native-shell/shell.css | 58 +++++++ client/src/native/iap.test.ts | 24 +++ client/src/native/iap.ts | 92 ++++++++++++ shared/src/i18n/ar/native.ts | 18 +++ shared/src/i18n/az/native.ts | 18 +++ shared/src/i18n/br/native.ts | 18 +++ shared/src/i18n/ca/native.ts | 18 +++ shared/src/i18n/cs/native.ts | 18 +++ shared/src/i18n/de/native.ts | 18 +++ shared/src/i18n/en/native.ts | 18 +++ shared/src/i18n/es/native.ts | 18 +++ shared/src/i18n/et/native.ts | 18 +++ shared/src/i18n/fr/native.ts | 18 +++ shared/src/i18n/gr/native.ts | 18 +++ shared/src/i18n/hu/native.ts | 18 +++ shared/src/i18n/id/native.ts | 18 +++ shared/src/i18n/it/native.ts | 18 +++ shared/src/i18n/ja/native.ts | 18 +++ shared/src/i18n/ko/native.ts | 18 +++ shared/src/i18n/nl/native.ts | 18 +++ shared/src/i18n/pl/native.ts | 18 +++ shared/src/i18n/ru/native.ts | 18 +++ shared/src/i18n/sk/native.ts | 18 +++ shared/src/i18n/sv/native.ts | 18 +++ shared/src/i18n/th/native.ts | 18 +++ shared/src/i18n/tr/native.ts | 18 +++ shared/src/i18n/uk/native.ts | 18 +++ shared/src/i18n/vi/native.ts | 18 +++ shared/src/i18n/zh-TW/native.ts | 18 +++ shared/src/i18n/zh/native.ts | 18 +++ 31 files changed, 914 insertions(+), 5 deletions(-) create mode 100644 client/src/native/iap.test.ts create mode 100644 client/src/native/iap.ts diff --git a/client/native-shell/main.ts b/client/native-shell/main.ts index e9e359c67..a6248db0a 100644 --- a/client/native-shell/main.ts +++ b/client/native-shell/main.ts @@ -5,6 +5,7 @@ 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' @@ -24,6 +25,19 @@ document.documentElement.dir = locale === 'ar' ? 'rtl' : 'ltr' const root = document.getElementById('shell')! const PROBE_TIMEOUT_MS = 8000 +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 + +/** 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}` + } catch { + 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) => { @@ -302,11 +316,15 @@ async function fetchPlans(): Promise { } } -function subscribe(plan: CloudPlan): void { +function subscribe(plan: CloudPlan, interval: 'month' | 'year'): 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') + // 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) + } else { + window.open(`${CLOUD_URL}/order?plan=${encodeURIComponent(plan.id)}`, '_blank', 'noopener') + } } function planCard(plan: CloudPlan, interval: 'month' | 'year', featured: boolean): HTMLElement { @@ -314,7 +332,7 @@ function planCard(plan: CloudPlan, interval: 'month' | 'year', featured: boolean 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)) + sub.addEventListener('click', () => subscribe(plan, interval)) return el( 'div', { className: featured ? 'plan featured' : 'plan' }, @@ -399,6 +417,234 @@ function showPlans(opts: { back?: boolean } = {}): void { void load() } +// ── Claim a TREK Cloud instance (choose address, buy, provision) ──────────── +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' + } catch { + return 'error' + } +} + +interface RedeemOk { + ok: true + instanceUrl: string +} +async function redeemPurchase(slug: string, email: string, purchase: Purchase): Promise { + try { + const res = await CapacitorHttp.post({ + url: `${CLOUD_URL}/api/iap/redeem`, + headers: { 'content-type': 'application/json' }, + 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 } + if ((res.status === 200 || res.status === 201) && typeof body.instanceUrl === 'string') { + return { ok: true, instanceUrl: body.instanceUrl } + } + return { ok: false, code: body.code ?? 'redeem-failed' } + } catch { + 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 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 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 }, + slugField, + slugNote, + emailField, + el('p', { className: 'hint' }, t('native.claim.emailHint')), + submit, + error, + ) + + 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 + 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 shake = (field: HTMLElement): void => { + 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() + if (slug.length < 3 || status === 'taken' || status === 'invalid' || status === 'reserved') { + shake(slugField) + slugInput.focus() + return + } + if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + shake(emailField) + emailInput.focus() + return + } + + 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')) + const restore = () => { + lock(false) + submit.replaceChildren(t('native.claim.subscribe', { price: money(price) })) + } + + const outcome = await iap.purchase(productIdFor(plan.id, interval)) + if (outcome.kind === 'cancelled') { + restore() + error.textContent = t('native.claim.cancelled') + return + } + if (outcome.kind === 'failed') { + void notify(NotificationType.Error) + restore() + error.textContent = t('native.claim.failed') + return + } + + const redeemed = await redeemPurchase(slug, email, outcome.purchase) + if (redeemed.ok) { + showProvisioning(redeemed.instanceUrl) + return + } + void notify(NotificationType.Error) + restore() + if (redeemed.code === 'slug-taken') { + shake(slugField) + slugNote.textContent = t('native.claim.taken') + slugNote.className = 'hint bad' + } else { + error.textContent = t('native.claim.redeemFailed') + } + }) + + show( + el( + 'div', + {}, + topbar(() => showPlans({ back: true })), + el('h1', { className: 'title-sm' }, t('native.claim.title')), + el('p', { className: 'sub' }, t('native.claim.sub')), + form, + ), + 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 + } catch { + return instanceUrl + } + })() + + 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 })), + ), + ) + + const deadline = Date.now() + 180_000 + const poll = async (): Promise => { + const result = await probeServer(instanceUrl, get) + if (result.ok) { + await connected(result.url) + return + } + if (Date.now() > deadline) { + showProvisioningSlow(instanceUrl) + return + } + 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')) + retry.addEventListener('click', () => { + 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, + ), + ) +} + // ── Shared actions ────────────────────────────────────────────────────────── async function runDemo(button: HTMLButtonElement, error?: HTMLElement): Promise { void tap() @@ -487,6 +733,9 @@ async function paintStatusBar(): Promise { async function start(): Promise { void paintStatusBar() + iap.isAvailable().then((ok) => { + iapAvailable = ok + }) const { url } = await TrekShell.getServer() if (!url) { showWelcome() diff --git a/client/native-shell/shell.css b/client/native-shell/shell.css index f5d38906a..2946ffd45 100644 --- a/client/native-shell/shell.css +++ b/client/native-shell/shell.css @@ -652,3 +652,61 @@ a.linkbtn { margin: 2px 0 0; line-height: 1.45; } + +/* ── Claim: address + email, and the provisioning wait ───────────────────── */ +.slug-field { + display: flex; + align-items: center; + padding-right: 14px; +} +.slug-field .slug-input { + flex: 1; + min-width: 0; + text-align: right; +} +.slug-suffix { + flex: none; + color: var(--muted); + font-size: 0.98rem; + padding-left: 2px; + user-select: none; +} +.hint { + margin: -6px 2px 2px; + font-size: 0.84rem; + color: var(--faint); + min-height: 1.1em; + transition: color 0.2s; +} +.hint.ok { + color: var(--ok); +} +.hint.bad { + color: var(--danger); +} + +.screen-center { + justify-content: center; + text-align: center; + align-items: center; + gap: 18px; +} +.screen-center .title-sm, +.screen-center .sub { + text-align: center; +} +.provision-mark { + width: 56px; + height: 56px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 4px; +} +.provision-mark .spinner { + width: 40px; + height: 40px; + border-width: 3px; + border-color: var(--edge-strong); + border-top-color: var(--accent); +} diff --git a/client/src/native/iap.test.ts b/client/src/native/iap.test.ts new file mode 100644 index 000000000..fd7a01643 --- /dev/null +++ b/client/src/native/iap.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest'; +import { fakeIapClient, nativeIapClient, productIdFor } from './iap'; + +describe('in-app purchase client', () => { + it('names store products by the shared scheme', () => { + expect(productIdFor('start', 'month')).toBe('cloud.start.monthly'); + expect(productIdFor('pro', 'year')).toBe('cloud.pro.yearly'); + }); + + it('the native client is unavailable until the purchase plugin is wired', async () => { + expect(await nativeIapClient.isAvailable()).toBe(false); + expect(await nativeIapClient.purchase('cloud.plus.monthly')).toEqual({ kind: 'failed' }); + }); + + it('the fake client yields a receipt for the product asked for', async () => { + expect(await fakeIapClient.isAvailable()).toBe(true); + const outcome = await fakeIapClient.purchase('cloud.plus.monthly'); + expect(outcome.kind).toBe('bought'); + if (outcome.kind !== 'bought') return; + expect(outcome.purchase.productId).toBe('cloud.plus.monthly'); + expect(outcome.purchase.platform).toBe('apple'); + expect(outcome.purchase.receipt).toContain('cloud.plus.monthly'); + }); +}); diff --git a/client/src/native/iap.ts b/client/src/native/iap.ts new file mode 100644 index 000000000..47ec4b0ec --- /dev/null +++ b/client/src/native/iap.ts @@ -0,0 +1,92 @@ +/** + * In-app purchase, from the app's side. A seam, like {@link TrekShell}: the + * screens drive a plan to a receipt through this, and never touch the store + * plugin directly. Two implementations stand behind it — a fake one for + * development and preview, so the whole subscription flow can be walked without a + * store, and the native one that will call the platform plugin once the store + * products and accounts exist. Until then the native client reports itself + * unavailable and the plan picker falls back to finishing on the web, so nothing + * half-wired ever faces a customer. + */ + +export type IapPlatform = 'apple' | 'google'; + +/** The receipt a completed purchase yields, in the shape `/api/iap/redeem` takes. */ +export interface Purchase { + platform: IapPlatform; + productId: string; + receipt: string; +} + +/** What a purchase attempt came to. `cancelled` is the customer backing out, not an error. */ +export 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. */ + isAvailable(): Promise; + /** Runs the platform's purchase sheet for a product and resolves with what became of it. */ + purchase(productId: string): Promise; +} + +/** + * The store product id for a plan and interval. Must match the identifiers created + * in App Store Connect and the Play Console, and the control plane's catalog + * (`cloud..`); kept here as the one place the app names them. + */ +export function productIdFor(planId: string, interval: 'month' | 'year'): string { + return `cloud.${planId}.${interval === 'year' ? 'yearly' : 'monthly'}`; +} + +/** + * The fake used in development and preview: it waits a beat, as a real sheet + * would, then returns a receipt the control plane's fake verifier accepts. Never + * reached in a store build. + */ +export const fakeIapClient: IapClient = { + async isAvailable() { + return true; + }, + async purchase(productId) { + await new Promise((resolve) => setTimeout(resolve, 900)); + return { + kind: 'bought', + purchase: { platform: 'apple', productId, receipt: `fake-receipt:${productId}:${Date.now()}` }, + }; + }, +}; + +/** + * The store client. A stub until the purchase plugin is a dependency and the + * products exist: it reports itself unavailable, so the plan picker finishes on + * the web instead of offering a payment it cannot take. When the plugin lands, + * this is where it is wired; the screens do not change. + */ +export const nativeIapClient: IapClient = { + async isAvailable() { + return false; + }, + async purchase() { + return { kind: 'failed' }; + }, +}; + +/** + * Whether to use the fake. On in a development build, and switchable on in a + * preview build with `localStorage['trek-iap-fake'] = '1'`, so the flow can be + * shown without a store. Off in a store build, where the native client decides. + */ +function useFake(): boolean { + try { + if (import.meta.env?.DEV) return true; + return typeof localStorage !== 'undefined' && localStorage.getItem('trek-iap-fake') === '1'; + } catch { + return false; + } +} + +export function getIapClient(): IapClient { + return useFake() ? fakeIapClient : nativeIapClient; +} diff --git a/shared/src/i18n/ar/native.ts b/shared/src/i18n/ar/native.ts index 9990786cc..842cc3980 100644 --- a/shared/src/i18n/ar/native.ts +++ b/shared/src/i18n/ar/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/az/native.ts b/shared/src/i18n/az/native.ts index 8b2f499fc..2b070fcf2 100644 --- a/shared/src/i18n/az/native.ts +++ b/shared/src/i18n/az/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/br/native.ts b/shared/src/i18n/br/native.ts index 2e6745d02..906e2c897 100644 --- a/shared/src/i18n/br/native.ts +++ b/shared/src/i18n/br/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/ca/native.ts b/shared/src/i18n/ca/native.ts index 392c624b5..4e8024f4d 100644 --- a/shared/src/i18n/ca/native.ts +++ b/shared/src/i18n/ca/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/cs/native.ts b/shared/src/i18n/cs/native.ts index 3d733422a..b080f0b5b 100644 --- a/shared/src/i18n/cs/native.ts +++ b/shared/src/i18n/cs/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/de/native.ts b/shared/src/i18n/de/native.ts index 4422fd73c..54dd2a1e1 100644 --- a/shared/src/i18n/de/native.ts +++ b/shared/src/i18n/de/native.ts @@ -55,6 +55,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Deine TREK einrichten', + 'native.claim.sub': 'Wähle deine Adresse. Wir richten sie sofort ein.', + 'native.claim.addressLabel': 'Deine Adresse', + 'native.claim.taken': 'Diese Adresse ist vergeben.', + 'native.claim.invalid': 'Mindestens drei Buchstaben, Ziffern und Bindestriche.', + 'native.claim.reserved': 'Diese Adresse ist reserviert.', + 'native.claim.free': 'Verfügbar', + 'native.claim.emailLabel': 'E-Mail', + 'native.claim.emailHint': 'Für dein Konto und die Belege.', + 'native.claim.subscribe': 'Abonnieren für {price}', + 'native.claim.cancelled': 'Kauf abgebrochen.', + 'native.claim.failed': 'Der Kauf hat nicht geklappt. Bitte versuch es erneut.', + 'native.claim.redeemFailed': 'Wir konnten deine TREK nicht einrichten. Bitte versuch es erneut.', + 'native.provisioning.title': 'Deine TREK wird eingerichtet', + 'native.provisioning.sub': '{host} wird vorbereitet. Das dauert nur eine Minute.', + 'native.provisioning.failedTitle': 'Gleich fertig', + 'native.provisioning.failedSub': 'Deine TREK ist unterwegs. Versuch es gleich noch einmal oder öffne die App später.', + 'native.provisioning.retry': 'Jetzt versuchen', }; export default native; diff --git a/shared/src/i18n/en/native.ts b/shared/src/i18n/en/native.ts index cbd9163c9..2f984dbce 100644 --- a/shared/src/i18n/en/native.ts +++ b/shared/src/i18n/en/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/es/native.ts b/shared/src/i18n/es/native.ts index e8a8d3c60..72217ccb7 100644 --- a/shared/src/i18n/es/native.ts +++ b/shared/src/i18n/es/native.ts @@ -55,6 +55,24 @@ const native: TranslationStrings = { 'native.plans.loadError': 'No se han podido cargar los planes. Revisa tu conexión e inténtalo de nuevo.', 'native.plans.retry': 'Reintentar', 'native.plans.soon': 'Las suscripciones dentro de la app llegarán pronto. Por ahora, termina en la web.', + 'native.claim.title': 'Crea tu TREK', + 'native.claim.sub': 'Elige tu dirección. La preparamos al instante.', + 'native.claim.addressLabel': 'Tu dirección', + 'native.claim.taken': 'Esa dirección ya está ocupada.', + 'native.claim.invalid': 'Tres o más letras, números y guiones.', + 'native.claim.reserved': 'Esa dirección está reservada.', + 'native.claim.free': 'Disponible', + 'native.claim.emailLabel': 'Correo', + 'native.claim.emailHint': 'Para tu cuenta y los recibos.', + 'native.claim.subscribe': 'Suscribirse por {price}', + 'native.claim.cancelled': 'Compra cancelada.', + 'native.claim.failed': 'La compra no se completó. Inténtalo de nuevo.', + 'native.claim.redeemFailed': 'No pudimos preparar tu TREK. Inténtalo de nuevo.', + 'native.provisioning.title': 'Preparando tu TREK', + 'native.provisioning.sub': 'Preparando {host}. Solo tarda un minuto.', + 'native.provisioning.failedTitle': 'Casi listo', + 'native.provisioning.failedSub': 'Tu TREK está en camino. Inténtalo en un momento o abre la app más tarde.', + 'native.provisioning.retry': 'Probar ahora', }; export default native; diff --git a/shared/src/i18n/et/native.ts b/shared/src/i18n/et/native.ts index 7f7f0b5ce..657d10ff2 100644 --- a/shared/src/i18n/et/native.ts +++ b/shared/src/i18n/et/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/fr/native.ts b/shared/src/i18n/fr/native.ts index d6984c6af..7649f3594 100644 --- a/shared/src/i18n/fr/native.ts +++ b/shared/src/i18n/fr/native.ts @@ -55,6 +55,24 @@ const native: TranslationStrings = { 'native.plans.loadError': 'Impossible de charger les forfaits. Vérifiez votre connexion et réessayez.', 'native.plans.retry': 'Réessayer', 'native.plans.soon': 'Les abonnements dans l’app arrivent bientôt. Pour l’instant, terminez sur le site.', + 'native.claim.title': 'Créez votre TREK', + 'native.claim.sub': 'Choisissez votre adresse. Nous la préparons aussitôt.', + 'native.claim.addressLabel': 'Votre adresse', + 'native.claim.taken': 'Cette adresse est déjà prise.', + 'native.claim.invalid': 'Au moins trois lettres, chiffres ou traits d’union.', + 'native.claim.reserved': 'Cette adresse est réservée.', + 'native.claim.free': 'Disponible', + 'native.claim.emailLabel': 'E-mail', + 'native.claim.emailHint': 'Pour votre compte et vos reçus.', + 'native.claim.subscribe': 'S’abonner pour {price}', + 'native.claim.cancelled': 'Achat annulé.', + 'native.claim.failed': 'L’achat n’a pas abouti. Réessayez.', + 'native.claim.redeemFailed': 'Nous n’avons pas pu préparer votre TREK. Réessayez.', + 'native.provisioning.title': 'Préparation de votre TREK', + 'native.provisioning.sub': 'Préparation de {host}. Cela ne prend qu’une minute.', + 'native.provisioning.failedTitle': 'Presque prêt', + 'native.provisioning.failedSub': 'Votre TREK arrive. Réessayez dans un instant ou rouvrez l’app plus tard.', + 'native.provisioning.retry': 'Réessayer', }; export default native; diff --git a/shared/src/i18n/gr/native.ts b/shared/src/i18n/gr/native.ts index 71f4389b4..e7e654e8f 100644 --- a/shared/src/i18n/gr/native.ts +++ b/shared/src/i18n/gr/native.ts @@ -56,6 +56,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/hu/native.ts b/shared/src/i18n/hu/native.ts index 2bcb3e2b0..ea0d1d7c6 100644 --- a/shared/src/i18n/hu/native.ts +++ b/shared/src/i18n/hu/native.ts @@ -55,6 +55,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/id/native.ts b/shared/src/i18n/id/native.ts index 00a4cfc40..5cd4ae764 100644 --- a/shared/src/i18n/id/native.ts +++ b/shared/src/i18n/id/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/it/native.ts b/shared/src/i18n/it/native.ts index 67b633ab5..c677d3364 100644 --- a/shared/src/i18n/it/native.ts +++ b/shared/src/i18n/it/native.ts @@ -55,6 +55,24 @@ const native: TranslationStrings = { 'native.plans.loadError': 'Impossibile caricare i piani. Controlla la connessione e riprova.', 'native.plans.retry': 'Riprova', 'native.plans.soon': 'Gli abbonamenti nell’app arrivano presto. Per ora, completa sul sito.', + 'native.claim.title': 'Crea il tuo TREK', + 'native.claim.sub': 'Scegli il tuo indirizzo. Lo prepariamo subito.', + 'native.claim.addressLabel': 'Il tuo indirizzo', + 'native.claim.taken': 'Questo indirizzo è già occupato.', + 'native.claim.invalid': 'Almeno tre lettere, numeri o trattini.', + 'native.claim.reserved': 'Questo indirizzo è riservato.', + 'native.claim.free': 'Disponibile', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'Per il tuo account e le ricevute.', + 'native.claim.subscribe': 'Abbonati per {price}', + 'native.claim.cancelled': 'Acquisto annullato.', + 'native.claim.failed': 'L’acquisto non è andato a buon fine. Riprova.', + 'native.claim.redeemFailed': 'Non siamo riusciti a preparare il tuo TREK. Riprova.', + 'native.provisioning.title': 'Preparazione del tuo TREK', + 'native.provisioning.sub': 'Stiamo preparando {host}. Ci vuole solo un minuto.', + 'native.provisioning.failedTitle': 'Quasi pronto', + 'native.provisioning.failedSub': 'Il tuo TREK sta arrivando. Riprova tra poco o riapri l’app più tardi.', + 'native.provisioning.retry': 'Riprova', }; export default native; diff --git a/shared/src/i18n/ja/native.ts b/shared/src/i18n/ja/native.ts index 34e3392de..d0826f255 100644 --- a/shared/src/i18n/ja/native.ts +++ b/shared/src/i18n/ja/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/ko/native.ts b/shared/src/i18n/ko/native.ts index d7ce6ce37..2afbafa25 100644 --- a/shared/src/i18n/ko/native.ts +++ b/shared/src/i18n/ko/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/nl/native.ts b/shared/src/i18n/nl/native.ts index f8d0d2b6b..66410cef8 100644 --- a/shared/src/i18n/nl/native.ts +++ b/shared/src/i18n/nl/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { 'native.plans.loadError': 'Kan de abonnementen niet laden. Controleer je verbinding en probeer het opnieuw.', 'native.plans.retry': 'Opnieuw proberen', 'native.plans.soon': 'Abonneren in de app komt eraan. Rond het voorlopig af op de website.', + 'native.claim.title': 'Maak je TREK', + 'native.claim.sub': 'Kies je adres. We zetten het meteen klaar.', + 'native.claim.addressLabel': 'Je adres', + 'native.claim.taken': 'Dit adres is al bezet.', + 'native.claim.invalid': 'Minstens drie letters, cijfers of streepjes.', + 'native.claim.reserved': 'Dit adres is gereserveerd.', + 'native.claim.free': 'Beschikbaar', + 'native.claim.emailLabel': 'E-mail', + 'native.claim.emailHint': 'Voor je account en bonnen.', + 'native.claim.subscribe': 'Abonneren voor {price}', + 'native.claim.cancelled': 'Aankoop geannuleerd.', + 'native.claim.failed': 'De aankoop is niet gelukt. Probeer het opnieuw.', + 'native.claim.redeemFailed': 'We konden je TREK niet klaarzetten. Probeer het opnieuw.', + 'native.provisioning.title': 'Je TREK wordt klaargezet', + 'native.provisioning.sub': '{host} wordt klaargemaakt. Dit duurt maar een minuut.', + 'native.provisioning.failedTitle': 'Bijna klaar', + 'native.provisioning.failedSub': 'Je TREK is onderweg. Probeer het zo opnieuw of open de app later.', + 'native.provisioning.retry': 'Nu proberen', }; export default native; diff --git a/shared/src/i18n/pl/native.ts b/shared/src/i18n/pl/native.ts index ede31ad57..30323de8b 100644 --- a/shared/src/i18n/pl/native.ts +++ b/shared/src/i18n/pl/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { 'native.plans.loadError': 'Nie udało się wczytać planów. Sprawdź połączenie i spróbuj ponownie.', 'native.plans.retry': 'Spróbuj ponownie', 'native.plans.soon': 'Subskrypcje w aplikacji już wkrótce. Na razie dokończ na stronie.', + 'native.claim.title': 'Utwórz swój TREK', + 'native.claim.sub': 'Wybierz swój adres. Przygotujemy go od razu.', + 'native.claim.addressLabel': 'Twój adres', + 'native.claim.taken': 'Ten adres jest już zajęty.', + 'native.claim.invalid': 'Co najmniej trzy litery, cyfry lub myślniki.', + 'native.claim.reserved': 'Ten adres jest zarezerwowany.', + 'native.claim.free': 'Dostępny', + 'native.claim.emailLabel': 'E-mail', + 'native.claim.emailHint': 'Do Twojego konta i potwierdzeń.', + 'native.claim.subscribe': 'Subskrybuj za {price}', + 'native.claim.cancelled': 'Zakup anulowany.', + 'native.claim.failed': 'Zakup się nie powiódł. Spróbuj ponownie.', + 'native.claim.redeemFailed': 'Nie udało się przygotować Twojego TREK. Spróbuj ponownie.', + 'native.provisioning.title': 'Przygotowujemy Twój TREK', + 'native.provisioning.sub': 'Przygotowujemy {host}. To zajmie tylko minutę.', + 'native.provisioning.failedTitle': 'Już prawie', + 'native.provisioning.failedSub': 'Twój TREK jest w drodze. Spróbuj za chwilę lub otwórz aplikację później.', + 'native.provisioning.retry': 'Spróbuj teraz', }; export default native; diff --git a/shared/src/i18n/ru/native.ts b/shared/src/i18n/ru/native.ts index 4b1015948..68754db5f 100644 --- a/shared/src/i18n/ru/native.ts +++ b/shared/src/i18n/ru/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/sk/native.ts b/shared/src/i18n/sk/native.ts index 4c1856eea..4bc18151c 100644 --- a/shared/src/i18n/sk/native.ts +++ b/shared/src/i18n/sk/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/sv/native.ts b/shared/src/i18n/sv/native.ts index 07e7b0576..06b47104b 100644 --- a/shared/src/i18n/sv/native.ts +++ b/shared/src/i18n/sv/native.ts @@ -55,6 +55,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/th/native.ts b/shared/src/i18n/th/native.ts index 1a0782623..87d6d74fc 100644 --- a/shared/src/i18n/th/native.ts +++ b/shared/src/i18n/th/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/tr/native.ts b/shared/src/i18n/tr/native.ts index 50fa837b7..35fd19987 100644 --- a/shared/src/i18n/tr/native.ts +++ b/shared/src/i18n/tr/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/uk/native.ts b/shared/src/i18n/uk/native.ts index 6a07a1d2d..75d833c68 100644 --- a/shared/src/i18n/uk/native.ts +++ b/shared/src/i18n/uk/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/vi/native.ts b/shared/src/i18n/vi/native.ts index 140d0a7d8..f87ded7f8 100644 --- a/shared/src/i18n/vi/native.ts +++ b/shared/src/i18n/vi/native.ts @@ -55,6 +55,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/zh-TW/native.ts b/shared/src/i18n/zh-TW/native.ts index 254a969e5..47bca4465 100644 --- a/shared/src/i18n/zh-TW/native.ts +++ b/shared/src/i18n/zh-TW/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native; diff --git a/shared/src/i18n/zh/native.ts b/shared/src/i18n/zh/native.ts index 583de31f5..fa1f585eb 100644 --- a/shared/src/i18n/zh/native.ts +++ b/shared/src/i18n/zh/native.ts @@ -54,6 +54,24 @@ const native: TranslationStrings = { '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.', + 'native.claim.title': 'Create your TREK', + 'native.claim.sub': 'Choose your address. We set it up right away.', + 'native.claim.addressLabel': 'Your address', + 'native.claim.taken': 'That address is taken.', + 'native.claim.invalid': 'Three or more letters, numbers and hyphens.', + 'native.claim.reserved': 'That address is reserved.', + 'native.claim.free': 'Available', + 'native.claim.emailLabel': 'Email', + 'native.claim.emailHint': 'For your account and receipts.', + 'native.claim.subscribe': 'Subscribe for {price}', + 'native.claim.cancelled': 'Purchase cancelled.', + 'native.claim.failed': 'The purchase did not go through. Please try again.', + 'native.claim.redeemFailed': 'We could not set up your TREK. Please try again.', + 'native.provisioning.title': 'Setting up your TREK', + 'native.provisioning.sub': 'Getting {host} ready. This only takes a minute.', + 'native.provisioning.failedTitle': 'Almost ready', + 'native.provisioning.failedSub': 'Your TREK is on its way. Try again in a moment, or open the app later.', + 'native.provisioning.retry': 'Try now', }; export default native;