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;