mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-14 00:01:03 +00:00
fix(native): App Review findings on buying TREK Cloud in the app
- Orders closed at the cloud: the app shows the plans and that TREK Cloud opens soon, and sells only in a test build (TestFlight, App Review), whose sandbox purchases the cloud still sets up; a store purchase it would refuse is never offered. The plans arrive without prices then, the store's own are shown where it gives them. - A purchase paid but never set up (Ask to Buy approved later, the app closed mid-purchase) is picked up by Restore purchases and goes on to the claim with that purchase, instead of failing; nothing is bought twice. - The buy button names the period with the price (guideline 3.1.2). - The privacy link opens the policy without the site's sales header. - The privacy manifest declares what TREK Cloud keeps: email, name, the store purchase, photos, other user content and saved places, all for the app to work, linked to the account, never for tracking. - The email hint promises messages about the contract, not receipts, which the store sends.
This commit is contained in:
@@ -32,13 +32,95 @@
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<!-- TREK sends no data to third parties and runs no trackers: all traffic
|
||||
goes to the server the user chose. -->
|
||||
<!-- No trackers and no advertising. Trip data goes to the server the user
|
||||
connects; with TREK Cloud that server is ours, and buying TREK Cloud in
|
||||
the app sends the email, the chosen address and the store's purchase to
|
||||
liketrek.com. That is what is declared below, all for the app to work,
|
||||
linked to the account, never for tracking. -->
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<array>
|
||||
<!-- The account and contract email, given when buying TREK Cloud and when signing up. -->
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeEmailAddress</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- The name on a TREK Cloud account and of the people a trip is shared with. -->
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeName</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- The App Store purchase of TREK Cloud, verified and kept with the contract. -->
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePurchaseHistory</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- Photos the user adds to trips and journeys on a TREK Cloud instance. -->
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePhotosorVideos</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- Trips, places, bookings, notes and files on a TREK Cloud instance. -->
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeOtherUserContent</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- Places the user saves with their coordinates; the live location only when searching nearby. -->
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePreciseLocation</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -443,12 +443,14 @@ extension TrekShellPlugin {
|
||||
/// know are left out, so the shell offers only what can really be bought.
|
||||
@objc func storeProducts(_ call: CAPPluginCall) {
|
||||
let ids = call.getArray("ids", String.self) ?? []
|
||||
// TestFlight, App Review and Xcode builds buy in the sandbox; their receipt says so.
|
||||
let sandbox = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
|
||||
Task {
|
||||
guard AppStore.canMakePayments, let products = try? await Product.products(for: ids) else {
|
||||
call.resolve(["products": []])
|
||||
call.resolve(["products": [], "sandbox": sandbox])
|
||||
return
|
||||
}
|
||||
call.resolve(["products": products.map { ["id": $0.id, "displayPrice": $0.displayPrice] }])
|
||||
call.resolve(["products": products.map { ["id": $0.id, "displayPrice": $0.displayPrice] }, "sandbox": sandbox])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+53
-21
@@ -330,8 +330,9 @@ function showCloud(opts: { back?: boolean } = {}): void {
|
||||
interface CloudPlan {
|
||||
id: string;
|
||||
name: string;
|
||||
monthlyCents: number;
|
||||
yearlyCents: number;
|
||||
/** Absent while orders are closed: the cloud names its plans but no price leaves it then. */
|
||||
monthlyCents?: number;
|
||||
yearlyCents?: number;
|
||||
aiDocuments: number;
|
||||
storageGb: number;
|
||||
}
|
||||
@@ -350,16 +351,22 @@ const SAMPLE_PLANS: CloudPlan[] = [
|
||||
{ id: 'studio', name: 'Studio', monthlyCents: 2399, yearlyCents: 23990, aiDocuments: 2000, storageGb: 1000 },
|
||||
];
|
||||
|
||||
async function fetchPlans(): Promise<CloudPlan[] | null> {
|
||||
interface CloudPlans {
|
||||
plans: CloudPlan[];
|
||||
/** Orders are closed: only a test build may buy, and the store charges it nothing. */
|
||||
closed: boolean;
|
||||
}
|
||||
|
||||
async function fetchPlans(): Promise<CloudPlans | null> {
|
||||
try {
|
||||
const res = await get(`${CLOUD_URL}/api/plans`);
|
||||
const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
|
||||
const plans = (data as { plans?: CloudPlan[] })?.plans;
|
||||
if (Array.isArray(plans) && plans.length) return plans;
|
||||
const body = (data ?? {}) as { plans?: CloudPlan[]; ordering?: string };
|
||||
if (Array.isArray(body.plans) && body.plans.length) return { plans: body.plans, closed: body.ordering === 'closed' };
|
||||
} catch {
|
||||
// Fall through to the preview fallback.
|
||||
}
|
||||
if (import.meta.env?.DEV) return SAMPLE_PLANS;
|
||||
if (import.meta.env?.DEV) return { plans: SAMPLE_PLANS, closed: false };
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -372,20 +379,26 @@ async function fetchPlans(): Promise<CloudPlan[] | null> {
|
||||
interface Offer {
|
||||
canBuy: boolean;
|
||||
storePrices: Map<string, string> | null;
|
||||
/** Orders are closed and this is no test build: the plans show, nothing is for sale yet. */
|
||||
opensSoon: boolean;
|
||||
}
|
||||
|
||||
function offerFor(offer: Offer, plan: CloudPlan, interval: 'month' | 'year'): { label: string; buyable: boolean } {
|
||||
const cents = interval === 'year' ? plan.yearlyCents : plan.monthlyCents;
|
||||
const ours = typeof cents === 'number' ? money(cents) : '';
|
||||
const storePrice = offer.storePrices?.get(productIdFor(plan.id, interval));
|
||||
if (offer.storePrices) return { label: storePrice ?? money(cents), buyable: offer.canBuy && !!storePrice };
|
||||
return { label: money(cents), buyable: offer.canBuy };
|
||||
if (offer.storePrices) return { label: storePrice ?? ours, buyable: offer.canBuy && !!storePrice };
|
||||
return { label: ours, buyable: offer.canBuy && ours !== '' };
|
||||
}
|
||||
|
||||
async function loadOffer(plans: CloudPlan[]): Promise<Offer> {
|
||||
const canBuy = await iapReady;
|
||||
if (!canBuy || iap === fakeIapClient) return { canBuy, storePrices: null };
|
||||
const ids = plans.flatMap((p) => [productIdFor(p.id, 'month'), productIdFor(p.id, 'year')]);
|
||||
return { canBuy, storePrices: await iap.prices(ids) };
|
||||
async function loadOffer(list: CloudPlans): Promise<Offer> {
|
||||
const available = await iapReady;
|
||||
// Closed: a store purchase would be refused at the cloud, so only a test build may buy.
|
||||
const opensSoon = available && list.closed && !(await iap.isSandbox().catch(() => false));
|
||||
const canBuy = available && !opensSoon;
|
||||
if (!canBuy || iap === fakeIapClient) return { canBuy, storePrices: null, opensSoon };
|
||||
const ids = list.plans.flatMap((p) => [productIdFor(p.id, 'month'), productIdFor(p.id, 'year')]);
|
||||
return { canBuy, storePrices: await iap.prices(ids), opensSoon };
|
||||
}
|
||||
|
||||
function planCard(plan: CloudPlan, interval: 'month' | 'year', offer: Offer): HTMLElement {
|
||||
@@ -400,7 +413,7 @@ function planCard(plan: CloudPlan, interval: 'month' | 'year', offer: Offer): HT
|
||||
sub = el('button', { type: 'button', className: 'btn' }, t('native.plans.subscribe'));
|
||||
sub.addEventListener('click', () => {
|
||||
void tap(ImpactStyle.Medium);
|
||||
showClaim(plan, interval, label);
|
||||
showClaim(plan, interval, `${label} ${per}`);
|
||||
});
|
||||
}
|
||||
return el(
|
||||
@@ -410,7 +423,8 @@ function planCard(plan: CloudPlan, interval: 'month' | 'year', offer: Offer): HT
|
||||
'div',
|
||||
{ className: 'plan-top' },
|
||||
el('span', { className: 'plan-name' }, plan.name),
|
||||
el('span', { className: 'plan-price' }, label, el('small', {}, per))
|
||||
// No price yet (orders not open): the plan shows without one rather than with an empty one.
|
||||
...(label ? [el('span', { className: 'plan-price' }, label, el('small', {}, per))] : [])
|
||||
),
|
||||
el(
|
||||
'div',
|
||||
@@ -431,7 +445,8 @@ function planCard(plan: CloudPlan, interval: 'month' | 'year', offer: Offer): HT
|
||||
* holds (a reinstall, a new phone). Without one: only that buying in the app is
|
||||
* not available, and no pointer elsewhere.
|
||||
*/
|
||||
function planFooter(offer: Offer): Node[] {
|
||||
function planFooter(offer: Offer, plans: CloudPlan[]): Node[] {
|
||||
if (offer.opensSoon) return [el('p', { className: 'note' }, t('native.plans.closed'))];
|
||||
if (!offer.canBuy) return [el('p', { className: 'note' }, t('native.plans.soon'))];
|
||||
|
||||
const status = el('p', { className: 'hint', role: 'status' });
|
||||
@@ -454,6 +469,18 @@ function planFooter(offer: Offer): Node[] {
|
||||
showProvisioning(redeemed.instanceUrl);
|
||||
return;
|
||||
}
|
||||
// Paid but never set up (Ask to Buy approved later, the app closed mid-purchase):
|
||||
// the claim screen takes the purchase as it is, so nothing is bought twice.
|
||||
const unclaimed = redeemed.code === 'email-invalid' || (redeemed.code ?? '').startsWith('slug-');
|
||||
const match = /^cloud\.([a-z0-9-]+)\.(monthly|yearly)$/.exec(purchase.productId);
|
||||
const plan = match ? plans.find((p) => p.id === match[1]) : undefined;
|
||||
if (unclaimed && match && plan) {
|
||||
const interval = match[2] === 'yearly' ? 'year' : 'month';
|
||||
const { label } = offerFor(offer, plan, interval);
|
||||
const per = interval === 'year' ? t('native.plans.perYear') : t('native.plans.perMonth');
|
||||
showClaim(plan, interval, `${label} ${per}`, { bought: purchase });
|
||||
return;
|
||||
}
|
||||
void notify(NotificationType.Error);
|
||||
restore.disabled = false;
|
||||
status.className = 'hint bad';
|
||||
@@ -476,7 +503,7 @@ function planFooter(offer: Offer): Node[] {
|
||||
function showPlans(opts: { back?: boolean } = {}): void {
|
||||
let interval: 'month' | 'year' = 'month';
|
||||
let plans: CloudPlan[] | null = null;
|
||||
let offer: Offer = { canBuy: false, storePrices: null };
|
||||
let offer: Offer = { canBuy: false, storePrices: null, opensSoon: false };
|
||||
|
||||
const content = el('div', { className: 'loading' }, el('span', { className: 'spinner' }));
|
||||
const footer = el('div', { className: 'plans-foot' });
|
||||
@@ -510,9 +537,9 @@ function showPlans(opts: { back?: boolean } = {}): void {
|
||||
const list = await fetchPlans();
|
||||
if (list) {
|
||||
offer = await loadOffer(list);
|
||||
plans = list;
|
||||
plans = list.plans;
|
||||
renderList();
|
||||
footer.replaceChildren(...planFooter(offer));
|
||||
footer.replaceChildren(...planFooter(offer, list.plans));
|
||||
return;
|
||||
}
|
||||
content.className = '';
|
||||
@@ -584,9 +611,14 @@ async function redeemPurchase(slug: string, email: string, purchase: Purchase):
|
||||
}
|
||||
}
|
||||
|
||||
function showClaim(plan: CloudPlan, interval: 'month' | 'year', price: string, opts: { back?: boolean } = {}): void {
|
||||
function showClaim(
|
||||
plan: CloudPlan,
|
||||
interval: 'month' | 'year',
|
||||
price: string,
|
||||
opts: { back?: boolean; bought?: Purchase } = {},
|
||||
): void {
|
||||
/** A purchase that went through but whose redeem did not: a retry redeems it again instead of buying twice. */
|
||||
let bought: Purchase | null = null;
|
||||
let bought: Purchase | null = opts.bought ?? null;
|
||||
|
||||
const slugInput = el('input', {
|
||||
type: 'text',
|
||||
|
||||
@@ -58,6 +58,17 @@ describe('native client', () => {
|
||||
expect(await nativeIapClient.isAvailable()).toBe(false);
|
||||
});
|
||||
|
||||
it('knows a test build from a store one, and takes an error for a store build', async () => {
|
||||
shell.storeProducts.mockResolvedValue({ products: [], sandbox: true });
|
||||
expect(await nativeIapClient.isSandbox()).toBe(true);
|
||||
expect(shell.storeProducts).toHaveBeenCalledWith({ ids: [] });
|
||||
shell.storeProducts.mockResolvedValue({ products: [] });
|
||||
expect(await nativeIapClient.isSandbox()).toBe(false);
|
||||
shell.storeProducts.mockRejectedValue(new Error('no store'));
|
||||
expect(await nativeIapClient.isSandbox()).toBe(false);
|
||||
expect(await fakeIapClient.isSandbox()).toBe(true);
|
||||
});
|
||||
|
||||
it('maps the store price strings by product', async () => {
|
||||
shell.storeProducts.mockResolvedValue({ products: [{ id: 'cloud.plus.monthly', displayPrice: '9,99 €' }] });
|
||||
const prices = await nativeIapClient.prices(['cloud.plus.monthly', 'cloud.pro.monthly']);
|
||||
|
||||
@@ -41,6 +41,8 @@ export interface IapClient {
|
||||
* prices come from the plan list.
|
||||
*/
|
||||
prices(productIds: string[]): Promise<Map<string, string>>;
|
||||
/** A test build (TestFlight, App Review), whose purchases cost nothing and may go ahead before the opening. */
|
||||
isSandbox(): Promise<boolean>;
|
||||
/** Runs the platform's purchase sheet for a product and resolves with what became of it. */
|
||||
purchase(productId: string): Promise<PurchaseOutcome>;
|
||||
/** Tells the store a purchase was delivered, once its instance exists. */
|
||||
@@ -73,6 +75,9 @@ export const fakeIapClient: IapClient = {
|
||||
async prices() {
|
||||
return new Map();
|
||||
},
|
||||
async isSandbox() {
|
||||
return true;
|
||||
},
|
||||
async purchase(productId) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 900));
|
||||
const nonce = String(Date.now());
|
||||
@@ -110,6 +115,13 @@ export const nativeIapClient: IapClient = {
|
||||
return new Map();
|
||||
}
|
||||
},
|
||||
async isSandbox() {
|
||||
try {
|
||||
return (await TrekShell.storeProducts({ ids: [] })).sandbox === true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async purchase(productId) {
|
||||
try {
|
||||
const result = await TrekShell.storePurchase({ productId });
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
/** The privacy policy the store listings name too. */
|
||||
export const PRIVACY_URL = 'https://liketrek.com/privacy';
|
||||
export const PRIVACY_URL = 'https://liketrek.com/privacy?app=1';
|
||||
|
||||
/** What a TREK server is and how to run one, for someone who has none yet. */
|
||||
export const SERVER_DOCS_URL = 'https://github.com/liketrek/TREK#readme';
|
||||
|
||||
@@ -74,7 +74,9 @@ export interface TrekShellPlugin {
|
||||
}): Promise<{ running: boolean }>;
|
||||
endTripDay(): Promise<void>;
|
||||
/** iOS, feature storeKit: the App Store's price strings for the products it knows. */
|
||||
storeProducts(options: { ids: string[] }): Promise<{ products: { id: string; displayPrice: string }[] }>;
|
||||
storeProducts(options: {
|
||||
ids: string[];
|
||||
}): Promise<{ products: { id: string; displayPrice: string }[]; sandbox?: boolean }>;
|
||||
/** iOS, feature storeKit: the purchase sheet; `jws` is the signed transaction, set when bought. */
|
||||
storePurchase(options: {
|
||||
productId: string;
|
||||
|
||||
@@ -53,6 +53,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'تعذّر تحميل الخطط. تحقق من اتصالك وحاول مرة أخرى.',
|
||||
'native.plans.retry': 'حاول مرة أخرى',
|
||||
'native.plans.soon': 'الاشتراك داخل التطبيق غير متاح بعد.',
|
||||
'native.plans.closed': 'يفتح TREK Cloud قريبًا. يمكنك الاشتراك هنا اعتبارًا من يوم الافتتاح.',
|
||||
'native.claim.title': 'اقتربنا من الانتهاء',
|
||||
'native.claim.sub': 'اختر عنوانك. سنُعدّه على الفور.',
|
||||
'native.claim.addressLabel': 'عنوانك',
|
||||
@@ -61,7 +62,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'هذا العنوان محجوز.',
|
||||
'native.claim.free': 'متاح',
|
||||
'native.claim.emailLabel': 'البريد الإلكتروني',
|
||||
'native.claim.emailHint': 'لحسابك وإيصالاتك.',
|
||||
'native.claim.emailHint': 'لحسابك والرسائل المتعلقة بعقدك.',
|
||||
'native.claim.subscribe': 'اشترك مقابل {price}',
|
||||
'native.claim.cancelled': 'تم إلغاء الشراء.',
|
||||
'native.claim.failed': 'لم تتم عملية الشراء. حاول مرة أخرى.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Planları yükləmək mümkün olmadı. Bağlantınızı yoxlayın və yenidən cəhd edin.',
|
||||
'native.plans.retry': 'Yenidən cəhd edin',
|
||||
'native.plans.soon': 'Tətbiqdaxili abunə hələ mövcud deyil.',
|
||||
'native.plans.closed': 'TREK Cloud tezliklə açılır. Açılış günündən etibarən burada abunə ola bilərsiniz.',
|
||||
'native.claim.title': 'Az qaldı',
|
||||
'native.claim.sub': 'Ünvanınızı seçin. Dərhal hazırlayırıq.',
|
||||
'native.claim.addressLabel': 'Ünvanınız',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Bu ünvan ehtiyat saxlanılıb.',
|
||||
'native.claim.free': 'Əlçatandır',
|
||||
'native.claim.emailLabel': 'E-poçt',
|
||||
'native.claim.emailHint': 'Hesabınız və qəbzləriniz üçün.',
|
||||
'native.claim.emailHint': 'Hesabınız və müqavilənizlə bağlı bildirişlər üçün.',
|
||||
'native.claim.subscribe': '{price} qarşılığında abunə ol',
|
||||
'native.claim.cancelled': 'Alış ləğv edildi.',
|
||||
'native.claim.failed': 'Alış baş tutmadı. Zəhmət olmasa yenidən cəhd edin.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Não foi possível carregar os planos. Verifique sua conexão e tente novamente.',
|
||||
'native.plans.retry': 'Tentar de novo',
|
||||
'native.plans.soon': 'A assinatura no app ainda não está disponível.',
|
||||
'native.plans.closed': 'O TREK Cloud abre em breve. Você poderá assinar aqui a partir do dia da abertura.',
|
||||
'native.claim.title': 'Quase lá',
|
||||
'native.claim.sub': 'Escolha seu endereço. Preparamos tudo na hora.',
|
||||
'native.claim.addressLabel': 'Seu endereço',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Esse endereço está reservado.',
|
||||
'native.claim.free': 'Disponível',
|
||||
'native.claim.emailLabel': 'E-mail',
|
||||
'native.claim.emailHint': 'Para sua conta e os recibos.',
|
||||
'native.claim.emailHint': 'Para sua conta e avisos sobre seu contrato.',
|
||||
'native.claim.subscribe': 'Assinar por {price}',
|
||||
'native.claim.cancelled': 'Compra cancelada.',
|
||||
'native.claim.failed': 'A compra não foi concluída. Tente novamente.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'No s’han pogut carregar els plans. Comprova la connexió i torna-ho a provar.',
|
||||
'native.plans.retry': 'Torna-ho a provar',
|
||||
'native.plans.soon': 'La subscripció a l’app encara no està disponible.',
|
||||
'native.plans.closed': 'TREK Cloud obre aviat. Podràs subscriure’t aquí des del dia de l’obertura.',
|
||||
'native.claim.title': 'Gairebé a punt',
|
||||
'native.claim.sub': 'Tria la teva adreça. Ho preparem de seguida.',
|
||||
'native.claim.addressLabel': 'La teva adreça',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Aquesta adreça està reservada.',
|
||||
'native.claim.free': 'Disponible',
|
||||
'native.claim.emailLabel': 'Correu',
|
||||
'native.claim.emailHint': 'Per al teu compte i els rebuts.',
|
||||
'native.claim.emailHint': 'Per al teu compte i els avisos sobre el teu contracte.',
|
||||
'native.claim.subscribe': 'Subscriu-te per {price}',
|
||||
'native.claim.cancelled': 'Compra cancel·lada.',
|
||||
'native.claim.failed': 'La compra no s’ha completat. Torna-ho a provar.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Tarify se nepodařilo načíst. Zkontrolujte připojení a zkuste to znovu.',
|
||||
'native.plans.retry': 'Zkusit znovu',
|
||||
'native.plans.soon': 'Předplatné v aplikaci zatím není k dispozici.',
|
||||
'native.plans.closed': 'TREK Cloud brzy otevírá. Ode dne otevření si zde můžete předplatné sjednat.',
|
||||
'native.claim.title': 'Skoro hotovo',
|
||||
'native.claim.sub': 'Vyberte si adresu. Hned ji připravíme.',
|
||||
'native.claim.addressLabel': 'Vaše adresa',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Tato adresa je rezervovaná.',
|
||||
'native.claim.free': 'Dostupné',
|
||||
'native.claim.emailLabel': 'E-mail',
|
||||
'native.claim.emailHint': 'Pro váš účet a účtenky.',
|
||||
'native.claim.emailHint': 'Pro váš účet a zprávy ke smlouvě.',
|
||||
'native.claim.subscribe': 'Předplatit za {price}',
|
||||
'native.claim.cancelled': 'Nákup zrušen.',
|
||||
'native.claim.failed': 'Nákup se nezdařil. Zkuste to znovu.',
|
||||
|
||||
@@ -55,6 +55,7 @@ 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': 'Abschließen in der App ist noch nicht verfügbar.',
|
||||
'native.plans.closed': 'TREK Cloud öffnet bald. Ab dem Eröffnungstag kannst du hier abonnieren.',
|
||||
'native.claim.title': 'Fast geschafft',
|
||||
'native.claim.sub': 'Wähle deine Adresse. Wir richten sie sofort ein.',
|
||||
'native.claim.addressLabel': 'Deine Adresse',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'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.emailHint': 'Für dein Konto und Mitteilungen zu deinem Vertrag.',
|
||||
'native.claim.subscribe': 'Abonnieren für {price}',
|
||||
'native.claim.cancelled': 'Kauf abgebrochen.',
|
||||
'native.claim.failed': 'Der Kauf hat nicht geklappt. Bitte versuch es erneut.',
|
||||
|
||||
@@ -54,6 +54,7 @@ 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': 'Subscribing in the app is not available yet.',
|
||||
'native.plans.closed': 'TREK Cloud opens soon. You can subscribe here from opening day.',
|
||||
'native.claim.title': 'Almost there',
|
||||
'native.claim.sub': 'Choose your address. We set it up right away.',
|
||||
'native.claim.addressLabel': 'Your address',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'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.emailHint': 'For your account and messages about your contract.',
|
||||
'native.claim.subscribe': 'Subscribe for {price}',
|
||||
'native.claim.cancelled': 'Purchase cancelled.',
|
||||
'native.claim.failed': 'The purchase did not go through. Please try again.',
|
||||
|
||||
@@ -55,6 +55,7 @@ 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': 'La suscripción dentro de la app aún no está disponible.',
|
||||
'native.plans.closed': 'TREK Cloud abre pronto. Podrás suscribirte aquí desde el día de la apertura.',
|
||||
'native.claim.title': 'Casi listo',
|
||||
'native.claim.sub': 'Elige tu dirección. La preparamos al instante.',
|
||||
'native.claim.addressLabel': 'Tu dirección',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'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.emailHint': 'Para tu cuenta y los avisos sobre tu contrato.',
|
||||
'native.claim.subscribe': 'Suscribirse por {price}',
|
||||
'native.claim.cancelled': 'Compra cancelada.',
|
||||
'native.claim.failed': 'La compra no se completó. Inténtalo de nuevo.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Pakette ei õnnestunud laadida. Kontrolli ühendust ja proovi uuesti.',
|
||||
'native.plans.retry': 'Proovi uuesti',
|
||||
'native.plans.soon': 'Rakendusesisene tellimine pole veel saadaval.',
|
||||
'native.plans.closed': 'TREK Cloud avatakse peagi. Avamispäevast saad siin tellida.',
|
||||
'native.claim.title': 'Peaaegu valmis',
|
||||
'native.claim.sub': 'Vali oma aadress. Seadistame selle kohe.',
|
||||
'native.claim.addressLabel': 'Sinu aadress',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'See aadress on reserveeritud.',
|
||||
'native.claim.free': 'Saadaval',
|
||||
'native.claim.emailLabel': 'E-post',
|
||||
'native.claim.emailHint': 'Sinu konto ja kviitungite jaoks.',
|
||||
'native.claim.emailHint': 'Sinu konto ja lepinguga seotud teadete jaoks.',
|
||||
'native.claim.subscribe': 'Telli hinnaga {price}',
|
||||
'native.claim.cancelled': 'Ost tühistatud.',
|
||||
'native.claim.failed': 'Ost ei õnnestunud. Proovi uuesti.',
|
||||
|
||||
@@ -55,6 +55,7 @@ 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': 'L’abonnement dans l’app n’est pas encore disponible.',
|
||||
'native.plans.closed': 'TREK Cloud ouvre bientôt. Vous pourrez vous abonner ici dès l’ouverture.',
|
||||
'native.claim.title': 'Presque terminé',
|
||||
'native.claim.sub': 'Choisissez votre adresse. Nous la préparons aussitôt.',
|
||||
'native.claim.addressLabel': 'Votre adresse',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'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.emailHint': 'Pour votre compte et les messages concernant votre contrat.',
|
||||
'native.claim.subscribe': 'S’abonner pour {price}',
|
||||
'native.claim.cancelled': 'Achat annulé.',
|
||||
'native.claim.failed': 'L’achat n’a pas abouti. Réessayez.',
|
||||
|
||||
@@ -57,6 +57,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Δεν ήταν δυνατή η φόρτωση των πλάνων. Ελέγξτε τη σύνδεσή σας και δοκιμάστε ξανά.',
|
||||
'native.plans.retry': 'Δοκιμάστε ξανά',
|
||||
'native.plans.soon': 'Η συνδρομή μέσα από την εφαρμογή δεν είναι ακόμη διαθέσιμη.',
|
||||
'native.plans.closed': 'Το TREK Cloud ανοίγει σύντομα. Από την ημέρα έναρξης θα μπορείτε να εγγραφείτε εδώ.',
|
||||
'native.claim.title': 'Σχεδόν έτοιμο',
|
||||
'native.claim.sub': 'Επιλέξτε τη διεύθυνσή σας. Το ετοιμάζουμε αμέσως.',
|
||||
'native.claim.addressLabel': 'Η διεύθυνσή σας',
|
||||
@@ -65,7 +66,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Αυτή η διεύθυνση είναι δεσμευμένη.',
|
||||
'native.claim.free': 'Διαθέσιμη',
|
||||
'native.claim.emailLabel': 'Ηλ. ταχυδρομείο',
|
||||
'native.claim.emailHint': 'Για τον λογαριασμό και τις αποδείξεις σας.',
|
||||
'native.claim.emailHint': 'Για τον λογαριασμό σας και ενημερώσεις για τη σύμβασή σας.',
|
||||
'native.claim.subscribe': 'Εγγραφή για {price}',
|
||||
'native.claim.cancelled': 'Η αγορά ακυρώθηκε.',
|
||||
'native.claim.failed': 'Η αγορά δεν ολοκληρώθηκε. Δοκιμάστε ξανά.',
|
||||
|
||||
@@ -53,6 +53,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'לא ניתן לטעון את התוכניות. בדקו את החיבור ונסו שוב.',
|
||||
'native.plans.retry': 'נסו שוב',
|
||||
'native.plans.soon': 'הרשמה בתוך האפליקציה עדיין לא זמינה.',
|
||||
'native.plans.closed': 'TREK Cloud ייפתח בקרוב. מיום הפתיחה תוכלו להירשם כאן.',
|
||||
'native.claim.title': 'כמעט סיימנו',
|
||||
'native.claim.sub': 'בחרו את הכתובת שלכם. אנחנו מכינים אותה מיד.',
|
||||
'native.claim.addressLabel': 'הכתובת שלכם',
|
||||
@@ -61,7 +62,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'הכתובת הזו שמורה.',
|
||||
'native.claim.free': 'זמין',
|
||||
'native.claim.emailLabel': 'אימייל',
|
||||
'native.claim.emailHint': 'עבור החשבון והקבלות שלכם.',
|
||||
'native.claim.emailHint': 'לחשבון שלכם ולהודעות על החוזה.',
|
||||
'native.claim.subscribe': 'הרשמה תמורת {price}',
|
||||
'native.claim.cancelled': 'הרכישה בוטלה.',
|
||||
'native.claim.failed': 'הרכישה לא הושלמה. אנא נסו שוב.',
|
||||
|
||||
@@ -55,6 +55,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'A csomagokat nem sikerült betölteni. Ellenőrizd a kapcsolatot, és próbáld újra.',
|
||||
'native.plans.retry': 'Újra',
|
||||
'native.plans.soon': 'Az appon belüli előfizetés még nem érhető el.',
|
||||
'native.plans.closed': 'A TREK Cloud hamarosan nyit. A nyitás napjától itt tudsz előfizetni.',
|
||||
'native.claim.title': 'Mindjárt kész',
|
||||
'native.claim.sub': 'Válaszd ki a címed. Azonnal beállítjuk.',
|
||||
'native.claim.addressLabel': 'A címed',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Ez a cím fenntartott.',
|
||||
'native.claim.free': 'Szabad',
|
||||
'native.claim.emailLabel': 'E-mail',
|
||||
'native.claim.emailHint': 'A fiókodhoz és a számlákhoz.',
|
||||
'native.claim.emailHint': 'A fiókodhoz és a szerződéssel kapcsolatos üzenetekhez.',
|
||||
'native.claim.subscribe': 'Előfizetés ennyiért: {price}',
|
||||
'native.claim.cancelled': 'Vásárlás megszakítva.',
|
||||
'native.claim.failed': 'A vásárlás nem sikerült. Próbáld újra.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Tidak dapat memuat paket. Periksa koneksi Anda dan coba lagi.',
|
||||
'native.plans.retry': 'Coba lagi',
|
||||
'native.plans.soon': 'Berlangganan di aplikasi belum tersedia.',
|
||||
'native.plans.closed': 'TREK Cloud segera dibuka. Mulai hari pembukaan, Anda bisa berlangganan di sini.',
|
||||
'native.claim.title': 'Hampir selesai',
|
||||
'native.claim.sub': 'Pilih alamat Anda. Kami langsung menyiapkannya.',
|
||||
'native.claim.addressLabel': 'Alamat Anda',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Alamat itu dicadangkan.',
|
||||
'native.claim.free': 'Tersedia',
|
||||
'native.claim.emailLabel': 'Email', // same-as-en
|
||||
'native.claim.emailHint': 'Untuk akun dan tanda terima Anda.',
|
||||
'native.claim.emailHint': 'Untuk akun Anda dan pemberitahuan tentang kontrak Anda.',
|
||||
'native.claim.subscribe': 'Berlangganan seharga {price}',
|
||||
'native.claim.cancelled': 'Pembelian dibatalkan.',
|
||||
'native.claim.failed': 'Pembelian tidak berhasil. Silakan coba lagi.',
|
||||
|
||||
@@ -55,6 +55,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Impossibile caricare i piani. Controlla la connessione e riprova.',
|
||||
'native.plans.retry': 'Riprova',
|
||||
'native.plans.soon': 'L’abbonamento nell’app non è ancora disponibile.',
|
||||
'native.plans.closed': 'TREK Cloud apre presto. Potrai abbonarti qui dal giorno dell’apertura.',
|
||||
'native.claim.title': 'Quasi fatto',
|
||||
'native.claim.sub': 'Scegli il tuo indirizzo. Lo prepariamo subito.',
|
||||
'native.claim.addressLabel': 'Il tuo indirizzo',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Questo indirizzo è riservato.',
|
||||
'native.claim.free': 'Disponibile',
|
||||
'native.claim.emailLabel': 'Email', // same-as-en
|
||||
'native.claim.emailHint': 'Per il tuo account e le ricevute.',
|
||||
'native.claim.emailHint': 'Per il tuo account e le comunicazioni sul contratto.',
|
||||
'native.claim.subscribe': 'Abbonati per {price}',
|
||||
'native.claim.cancelled': 'Acquisto annullato.',
|
||||
'native.claim.failed': 'L’acquisto non è andato a buon fine. Riprova.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'プランを読み込めませんでした。接続を確認してもう一度お試しください。',
|
||||
'native.plans.retry': 'もう一度試す',
|
||||
'native.plans.soon': 'アプリ内での登録はまだご利用いただけません。',
|
||||
'native.plans.closed': 'TREK Cloudはまもなく開始します。開始日からここで登録できます。',
|
||||
'native.claim.title': 'もう少しです',
|
||||
'native.claim.sub': 'アドレスを選んでください。すぐに用意します。',
|
||||
'native.claim.addressLabel': 'あなたのアドレス',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'このアドレスは予約済みです。',
|
||||
'native.claim.free': '利用可能',
|
||||
'native.claim.emailLabel': 'メール',
|
||||
'native.claim.emailHint': 'アカウントと領収書のため。',
|
||||
'native.claim.emailHint': 'アカウントと契約に関するお知らせ用です。',
|
||||
'native.claim.subscribe': '{price} で登録',
|
||||
'native.claim.cancelled': '購入をキャンセルしました。',
|
||||
'native.claim.failed': '購入できませんでした。もう一度お試しください。',
|
||||
|
||||
@@ -53,6 +53,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': '플랜을 불러오지 못했습니다. 연결을 확인하고 다시 시도하세요.',
|
||||
'native.plans.retry': '다시 시도',
|
||||
'native.plans.soon': '앱 내 구독은 아직 이용할 수 없습니다.',
|
||||
'native.plans.closed': 'TREK Cloud가 곧 문을 엽니다. 오픈일부터 여기에서 구독할 수 있습니다.',
|
||||
'native.claim.title': '거의 다 됐어요',
|
||||
'native.claim.sub': '주소를 선택하세요. 바로 준비해 드립니다.',
|
||||
'native.claim.addressLabel': '내 주소',
|
||||
@@ -61,7 +62,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': '예약된 주소예요.',
|
||||
'native.claim.free': '사용 가능',
|
||||
'native.claim.emailLabel': '이메일',
|
||||
'native.claim.emailHint': '계정과 영수증용입니다.',
|
||||
'native.claim.emailHint': '계정과 계약 관련 안내에 사용됩니다.',
|
||||
'native.claim.subscribe': '{price}에 구독',
|
||||
'native.claim.cancelled': '구매가 취소되었습니다.',
|
||||
'native.claim.failed': '구매가 완료되지 않았습니다. 다시 시도하세요.',
|
||||
|
||||
@@ -54,6 +54,7 @@ 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 is nog niet beschikbaar.',
|
||||
'native.plans.closed': 'TREK Cloud opent binnenkort. Vanaf de openingsdag kun je hier abonneren.',
|
||||
'native.claim.title': 'Bijna klaar',
|
||||
'native.claim.sub': 'Kies je adres. We zetten het meteen klaar.',
|
||||
'native.claim.addressLabel': 'Je adres',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'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.emailHint': 'Voor je account en berichten over je contract.',
|
||||
'native.claim.subscribe': 'Abonneren voor {price}',
|
||||
'native.claim.cancelled': 'Aankoop geannuleerd.',
|
||||
'native.claim.failed': 'De aankoop is niet gelukt. Probeer het opnieuw.',
|
||||
|
||||
@@ -54,6 +54,7 @@ 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': 'Subskrypcja w aplikacji nie jest jeszcze dostępna.',
|
||||
'native.plans.closed': 'TREK Cloud wkrótce startuje. Od dnia otwarcia możesz tu wykupić subskrypcję.',
|
||||
'native.claim.title': 'Prawie gotowe',
|
||||
'native.claim.sub': 'Wybierz swój adres. Przygotujemy go od razu.',
|
||||
'native.claim.addressLabel': 'Twój adres',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'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.emailHint': 'Do konta i wiadomości dotyczących umowy.',
|
||||
'native.claim.subscribe': 'Subskrybuj za {price}',
|
||||
'native.claim.cancelled': 'Zakup anulowany.',
|
||||
'native.claim.failed': 'Zakup się nie powiódł. Spróbuj ponownie.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Не удалось загрузить тарифы. Проверьте подключение и повторите попытку.',
|
||||
'native.plans.retry': 'Повторить',
|
||||
'native.plans.soon': 'Оформление подписки в приложении пока недоступно.',
|
||||
'native.plans.closed': 'TREK Cloud скоро откроется. Оформить подписку здесь можно будет со дня открытия.',
|
||||
'native.claim.title': 'Почти готово',
|
||||
'native.claim.sub': 'Выберите адрес. Мы сразу всё настроим.',
|
||||
'native.claim.addressLabel': 'Ваш адрес',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Этот адрес зарезервирован.',
|
||||
'native.claim.free': 'Доступно',
|
||||
'native.claim.emailLabel': 'Эл. почта',
|
||||
'native.claim.emailHint': 'Для вашего аккаунта и чеков.',
|
||||
'native.claim.emailHint': 'Для вашей учётной записи и сообщений о договоре.',
|
||||
'native.claim.subscribe': 'Подписаться за {price}',
|
||||
'native.claim.cancelled': 'Покупка отменена.',
|
||||
'native.claim.failed': 'Покупка не прошла. Повторите попытку.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Tarify sa nepodarilo načítať. Skontrolujte pripojenie a skúste to znova.',
|
||||
'native.plans.retry': 'Skúsiť znova',
|
||||
'native.plans.soon': 'Predplatné v aplikácii zatiaľ nie je dostupné.',
|
||||
'native.plans.closed': 'TREK Cloud čoskoro otvára. Odo dňa otvorenia si tu môžete predplatné objednať.',
|
||||
'native.claim.title': 'Takmer hotovo',
|
||||
'native.claim.sub': 'Vyberte si adresu. Hneď ju pripravíme.',
|
||||
'native.claim.addressLabel': 'Vaša adresa',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Táto adresa je rezervovaná.',
|
||||
'native.claim.free': 'Dostupné',
|
||||
'native.claim.emailLabel': 'E-mail',
|
||||
'native.claim.emailHint': 'Pre váš účet a účtenky.',
|
||||
'native.claim.emailHint': 'Pre váš účet a správy k zmluve.',
|
||||
'native.claim.subscribe': 'Predplatiť za {price}',
|
||||
'native.claim.cancelled': 'Nákup zrušený.',
|
||||
'native.claim.failed': 'Nákup sa nepodaril. Skúste to znova.',
|
||||
|
||||
@@ -55,6 +55,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Det gick inte att läsa in abonnemangen. Kontrollera anslutningen och försök igen.',
|
||||
'native.plans.retry': 'Försök igen',
|
||||
'native.plans.soon': 'Prenumeration i appen är inte tillgänglig ännu.',
|
||||
'native.plans.closed': 'TREK Cloud öppnar snart. Från öppningsdagen kan du prenumerera här.',
|
||||
'native.claim.title': 'Nästan klart',
|
||||
'native.claim.sub': 'Välj din adress. Vi gör i ordning den direkt.',
|
||||
'native.claim.addressLabel': 'Din adress',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Den adressen är reserverad.',
|
||||
'native.claim.free': 'Ledig',
|
||||
'native.claim.emailLabel': 'E-post',
|
||||
'native.claim.emailHint': 'För ditt konto och dina kvitton.',
|
||||
'native.claim.emailHint': 'För ditt konto och meddelanden om ditt avtal.',
|
||||
'native.claim.subscribe': 'Prenumerera för {price}',
|
||||
'native.claim.cancelled': 'Köpet avbröts.',
|
||||
'native.claim.failed': 'Köpet gick inte igenom. Försök igen.',
|
||||
|
||||
@@ -53,6 +53,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'โหลดแพ็กเกจไม่สำเร็จ ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง',
|
||||
'native.plans.retry': 'ลองอีกครั้ง',
|
||||
'native.plans.soon': 'ยังไม่สามารถสมัครสมาชิกในแอปได้',
|
||||
'native.plans.closed': 'TREK Cloud จะเปิดเร็วๆ นี้ ตั้งแต่วันเปิดตัวคุณสามารถสมัครสมาชิกได้ที่นี่',
|
||||
'native.claim.title': 'เกือบเสร็จแล้ว',
|
||||
'native.claim.sub': 'เลือกที่อยู่ของคุณ เราจะตั้งค่าให้ทันที',
|
||||
'native.claim.addressLabel': 'ที่อยู่ของคุณ',
|
||||
@@ -61,7 +62,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'ที่อยู่นี้ถูกสงวนไว้',
|
||||
'native.claim.free': 'ว่าง',
|
||||
'native.claim.emailLabel': 'อีเมล',
|
||||
'native.claim.emailHint': 'สำหรับบัญชีและใบเสร็จของคุณ',
|
||||
'native.claim.emailHint': 'สำหรับบัญชีของคุณและการแจ้งเกี่ยวกับสัญญา',
|
||||
'native.claim.subscribe': 'สมัครสมาชิกในราคา {price}',
|
||||
'native.claim.cancelled': 'ยกเลิกการซื้อแล้ว',
|
||||
'native.claim.failed': 'การซื้อไม่สำเร็จ โปรดลองอีกครั้ง',
|
||||
|
||||
@@ -55,6 +55,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Planlar yüklenemedi. Bağlantınızı kontrol edip tekrar deneyin.',
|
||||
'native.plans.retry': 'Tekrar dene',
|
||||
'native.plans.soon': 'Uygulama içi abonelik henüz kullanılamıyor.',
|
||||
'native.plans.closed': 'TREK Cloud yakında açılıyor. Açılış gününden itibaren buradan abone olabilirsiniz.',
|
||||
'native.claim.title': 'Neredeyse hazır',
|
||||
'native.claim.sub': 'Adresinizi seçin. Hemen hazırlıyoruz.',
|
||||
'native.claim.addressLabel': 'Adresiniz',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Bu adres ayrılmış.',
|
||||
'native.claim.free': 'Uygun',
|
||||
'native.claim.emailLabel': 'E-posta',
|
||||
'native.claim.emailHint': 'Hesabınız ve makbuzlarınız için.',
|
||||
'native.claim.emailHint': 'Hesabınız ve sözleşmenizle ilgili bildirimler için.',
|
||||
'native.claim.subscribe': '{price} karşılığında abone ol',
|
||||
'native.claim.cancelled': 'Satın alma iptal edildi.',
|
||||
'native.claim.failed': 'Satın alma tamamlanamadı. Lütfen tekrar deneyin.',
|
||||
|
||||
@@ -54,6 +54,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Не вдалося завантажити тарифи. Перевірте з’єднання та повторіть спробу.',
|
||||
'native.plans.retry': 'Повторити',
|
||||
'native.plans.soon': 'Оформлення підписки в застосунку поки недоступне.',
|
||||
'native.plans.closed': 'TREK Cloud скоро відкриється. Оформити підписку тут можна буде з дня відкриття.',
|
||||
'native.claim.title': 'Майже готово',
|
||||
'native.claim.sub': 'Виберіть адресу. Ми одразу все налаштуємо.',
|
||||
'native.claim.addressLabel': 'Ваша адреса',
|
||||
@@ -62,7 +63,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Ця адреса зарезервована.',
|
||||
'native.claim.free': 'Доступно',
|
||||
'native.claim.emailLabel': 'Ел. пошта',
|
||||
'native.claim.emailHint': 'Для вашого облікового запису та чеків.',
|
||||
'native.claim.emailHint': 'Для вашого облікового запису та повідомлень щодо договору.',
|
||||
'native.claim.subscribe': 'Підписатися за {price}',
|
||||
'native.claim.cancelled': 'Покупку скасовано.',
|
||||
'native.claim.failed': 'Покупка не пройшла. Повторіть спробу.',
|
||||
|
||||
@@ -55,6 +55,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': 'Không thể tải các gói. Hãy kiểm tra kết nối và thử lại.',
|
||||
'native.plans.retry': 'Thử lại',
|
||||
'native.plans.soon': 'Chưa thể đăng ký trong ứng dụng.',
|
||||
'native.plans.closed': 'TREK Cloud sắp ra mắt. Từ ngày ra mắt, bạn có thể đăng ký tại đây.',
|
||||
'native.claim.title': 'Sắp xong rồi',
|
||||
'native.claim.sub': 'Chọn địa chỉ của bạn. Chúng tôi thiết lập ngay.',
|
||||
'native.claim.addressLabel': 'Địa chỉ của bạn',
|
||||
@@ -63,7 +64,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': 'Địa chỉ đó đã được giữ chỗ.',
|
||||
'native.claim.free': 'Có sẵn',
|
||||
'native.claim.emailLabel': 'Email', // same-as-en
|
||||
'native.claim.emailHint': 'Cho tài khoản và biên nhận của bạn.',
|
||||
'native.claim.emailHint': 'Dùng cho tài khoản và thông báo về hợp đồng của bạn.',
|
||||
'native.claim.subscribe': 'Đăng ký với giá {price}',
|
||||
'native.claim.cancelled': 'Đã hủy mua.',
|
||||
'native.claim.failed': 'Giao dịch mua không thành công. Vui lòng thử lại.',
|
||||
|
||||
@@ -53,6 +53,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': '無法載入方案。請檢查網路連線後重試。',
|
||||
'native.plans.retry': '重試',
|
||||
'native.plans.soon': '目前尚不支援在 App 內訂閱。',
|
||||
'native.plans.closed': 'TREK Cloud 即將開放。開放當天起即可在此訂閱。',
|
||||
'native.claim.title': '快完成了',
|
||||
'native.claim.sub': '選擇你的網址。我們會立即為你準備好。',
|
||||
'native.claim.addressLabel': '你的網址',
|
||||
@@ -61,7 +62,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': '此網址已被保留。',
|
||||
'native.claim.free': '可用',
|
||||
'native.claim.emailLabel': '電子郵件',
|
||||
'native.claim.emailHint': '用於你的帳戶和收據。',
|
||||
'native.claim.emailHint': '用於你的帳號與合約相關通知。',
|
||||
'native.claim.subscribe': '以 {price} 訂閱',
|
||||
'native.claim.cancelled': '已取消購買。',
|
||||
'native.claim.failed': '購買未成功。請重試。',
|
||||
|
||||
@@ -53,6 +53,7 @@ const native: TranslationStrings = {
|
||||
'native.plans.loadError': '无法加载方案。请检查网络连接后重试。',
|
||||
'native.plans.retry': '重试',
|
||||
'native.plans.soon': '暂不支持在应用内订阅。',
|
||||
'native.plans.closed': 'TREK Cloud 即将开放。开放当天起即可在此订阅。',
|
||||
'native.claim.title': '就快好了',
|
||||
'native.claim.sub': '选择你的地址。我们会立即为你准备好。',
|
||||
'native.claim.addressLabel': '你的地址',
|
||||
@@ -61,7 +62,7 @@ const native: TranslationStrings = {
|
||||
'native.claim.reserved': '该地址已被保留。',
|
||||
'native.claim.free': '可用',
|
||||
'native.claim.emailLabel': '邮箱',
|
||||
'native.claim.emailHint': '用于你的账户和收据。',
|
||||
'native.claim.emailHint': '用于你的账户和合同相关通知。',
|
||||
'native.claim.subscribe': '以 {price} 订阅',
|
||||
'native.claim.cancelled': '购买已取消。',
|
||||
'native.claim.failed': '购买未成功。请重试。',
|
||||
|
||||
Reference in New Issue
Block a user