@base44/app-plugin-commerce 0.9.5 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/base44/functions/commerce/storefront-catalog/entry.ts +5 -0
- package/package.json +1 -1
- package/scripts/install.js +3 -0
- package/skills/commerce/SKILL.md +36 -39
- package/skills/commerce/docs/api-storefront.md +1 -1
- package/skills/commerce/installation/install.md +58 -152
- package/skills/commerce/references/admin-localization.md +6 -4
- package/skills/commerce/references/storefront-ui.md +154 -0
- package/src/commerce/storefront/index.js +6 -3
- package/src/commerce/storefront-ui/CartButton.jsx +33 -0
- package/src/commerce/storefront-ui/CartPage.jsx +140 -0
- package/src/commerce/storefront-ui/CheckoutPage.jsx +287 -0
- package/src/commerce/storefront-ui/MiniCart.jsx +128 -0
- package/src/commerce/storefront-ui/OrderReceivedPage.jsx +159 -0
- package/src/commerce/storefront-ui/i18n/index.js +51 -0
- package/src/commerce/storefront-ui/i18n/locales/de.js +89 -0
- package/src/commerce/storefront-ui/i18n/locales/en.js +100 -0
- package/src/commerce/storefront-ui/i18n/locales/es.js +89 -0
- package/src/commerce/storefront-ui/i18n/locales/fr.js +89 -0
- package/src/commerce/storefront-ui/i18n/locales/ja.js +89 -0
- package/src/commerce/storefront-ui/i18n/locales/pt.js +89 -0
- package/src/commerce/storefront-ui/index.js +16 -0
- package/src/commerce/storefront-ui/internal.jsx +218 -0
- package/src/commerce/storefront-ui/storefront-ui.css +448 -0
- package/src/commerce/storefront-ui/theme.js +76 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Link } from "react-router-dom";
|
|
3
|
+
import { useFormatMoney, useOrderReturn } from "@/commerce/storefront";
|
|
4
|
+
import { orderTotalsLines } from "@/commerce/utils";
|
|
5
|
+
import { makeT, t } from "./i18n/index.js";
|
|
6
|
+
import { ErrorState, LoadingState, SfuiRoot } from "./internal.jsx";
|
|
7
|
+
|
|
8
|
+
const BRAND_KEYS = {
|
|
9
|
+
paidTitle: "order.paid.title",
|
|
10
|
+
paidBody: "order.paid.body",
|
|
11
|
+
unpaidTitle: "order.unpaid.title",
|
|
12
|
+
unpaidBody: "order.unpaid.body",
|
|
13
|
+
backToStore: "order.backToStore",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The mandatory /order-received page, complete: every payment link returns
|
|
18
|
+
* here, and this page is how a manual (offline) order tells the customer how
|
|
19
|
+
* to pay. Renders all of useOrderReturn's states — paid, unpaid (payment
|
|
20
|
+
* instructions + pay-now link), cancelled, error — plus the order's lines and
|
|
21
|
+
* totals. Mount it on the `/order-received` route.
|
|
22
|
+
*
|
|
23
|
+
* @param {object} props
|
|
24
|
+
* @param {Record<string,string>} [props.brand] wording overrides (paidTitle,
|
|
25
|
+
* paidBody, unpaidTitle, unpaidBody, backToStore).
|
|
26
|
+
* @param {string} [props.homeHref="/"]
|
|
27
|
+
*/
|
|
28
|
+
export function OrderReceivedPage({ brand, homeHref = "/" }) {
|
|
29
|
+
const tt = makeT(brand, BRAND_KEYS);
|
|
30
|
+
const { status, order, lines, paymentLink, paymentInstructions, error, reload } = useOrderReturn();
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<SfuiRoot className="sfui-order">
|
|
34
|
+
<div className="sfui-order-inner">
|
|
35
|
+
{status === "loading" && <LoadingState />}
|
|
36
|
+
|
|
37
|
+
{status === "error" && (
|
|
38
|
+
<ErrorState title={tt("order.error.title")} body={error?.message ?? tt("order.error.body")} onRetry={reload} />
|
|
39
|
+
)}
|
|
40
|
+
|
|
41
|
+
{status === "cancelled" && (
|
|
42
|
+
<div className="sfui-order-head">
|
|
43
|
+
<h1 className="sfui-heading sfui-h1">{tt("order.cancelled.title")}</h1>
|
|
44
|
+
<p className="sfui-muted">{tt("order.cancelled.body")}</p>
|
|
45
|
+
{paymentLink?.url && (
|
|
46
|
+
<a className="sfui-btn sfui-btn-inline" href={paymentLink.url}>
|
|
47
|
+
{tt("order.retryPayment")}
|
|
48
|
+
</a>
|
|
49
|
+
)}
|
|
50
|
+
<Link className="sfui-link" to={homeHref}>
|
|
51
|
+
{tt("order.backToStore")}
|
|
52
|
+
</Link>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
{(status === "paid" || status === "unpaid") && (
|
|
57
|
+
<>
|
|
58
|
+
<div className="sfui-order-head">
|
|
59
|
+
<div className="sfui-order-badge" aria-hidden="true">
|
|
60
|
+
{status === "paid" ? "✓" : "…"}
|
|
61
|
+
</div>
|
|
62
|
+
<h1 className="sfui-heading sfui-h1">
|
|
63
|
+
{status === "paid" ? tt("order.paid.title") : tt("order.unpaid.title")}
|
|
64
|
+
</h1>
|
|
65
|
+
<p className="sfui-muted">
|
|
66
|
+
{status === "paid" ? tt("order.paid.body") : tt("order.unpaid.body")}
|
|
67
|
+
</p>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
{status === "unpaid" && (paymentInstructions || paymentLink?.url) && (
|
|
71
|
+
<div className="sfui-instructions">
|
|
72
|
+
<h2 className="sfui-heading sfui-h2">{tt("order.instructionsTitle")}</h2>
|
|
73
|
+
{paymentInstructions?.description && <p className="sfui-muted">{paymentInstructions.description}</p>}
|
|
74
|
+
{paymentInstructions?.account_details && (
|
|
75
|
+
<dl>
|
|
76
|
+
{Object.entries(paymentInstructions.account_details).map(([k, v]) => (
|
|
77
|
+
<div key={k}>
|
|
78
|
+
<dt>{k}</dt>
|
|
79
|
+
<dd>{String(v)}</dd>
|
|
80
|
+
</div>
|
|
81
|
+
))}
|
|
82
|
+
</dl>
|
|
83
|
+
)}
|
|
84
|
+
{paymentLink?.url && (
|
|
85
|
+
<a className="sfui-btn" href={paymentLink.url}>
|
|
86
|
+
{tt("order.payNow")}
|
|
87
|
+
</a>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
{order && (
|
|
93
|
+
<div className="sfui-panel sfui-order-card">
|
|
94
|
+
<div className="sfui-order-meta">
|
|
95
|
+
<div>
|
|
96
|
+
<dt className="sfui-label">{tt("order.orderLabel")}</dt>
|
|
97
|
+
<dd>{order.number ?? order.id}</dd>
|
|
98
|
+
</div>
|
|
99
|
+
<div style={{ textAlign: "end" }}>
|
|
100
|
+
<dt className="sfui-label">{tt("order.dateLabel")}</dt>
|
|
101
|
+
<dd>{order.created_date ? new Date(order.created_date).toLocaleDateString() : ""}</dd>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
<ul className="sfui-lines">
|
|
105
|
+
{lines.map((l, i) => (
|
|
106
|
+
<li className="sfui-line" key={i}>
|
|
107
|
+
<div className="sfui-line-media" aria-hidden="true">
|
|
108
|
+
{l.image?.src && <img src={l.image.src} alt="" loading="lazy" />}
|
|
109
|
+
</div>
|
|
110
|
+
<div className="sfui-line-main">
|
|
111
|
+
<p className="sfui-line-name">{l.name}</p>
|
|
112
|
+
{l.attributesLabel && <p className="sfui-line-attrs">{l.attributesLabel}</p>}
|
|
113
|
+
<p className="sfui-line-attrs">{tt("order.qty", { count: l.quantity })}</p>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="sfui-line-end">
|
|
116
|
+
<span className="sfui-line-price">{l.totalLabel}</span>
|
|
117
|
+
</div>
|
|
118
|
+
</li>
|
|
119
|
+
))}
|
|
120
|
+
</ul>
|
|
121
|
+
<OrderTotals order={order} />
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
<p style={{ textAlign: "center", margin: 0 }}>
|
|
126
|
+
<Link className="sfui-link" to={homeHref}>
|
|
127
|
+
{tt("order.backToStore")}
|
|
128
|
+
</Link>
|
|
129
|
+
</p>
|
|
130
|
+
</>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
</SfuiRoot>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function OrderTotals({ order }) {
|
|
138
|
+
const formatMoney = useFormatMoney();
|
|
139
|
+
const lines = orderTotalsLines(order, {
|
|
140
|
+
formatMoney,
|
|
141
|
+
labels: {
|
|
142
|
+
subtotal: t("common.totals.subtotal"),
|
|
143
|
+
discount: t("common.totals.discount"),
|
|
144
|
+
shipping: t("common.totals.shipping"),
|
|
145
|
+
tax: t("common.totals.tax"),
|
|
146
|
+
total: t("common.totals.total"),
|
|
147
|
+
},
|
|
148
|
+
}).filter((l) => !l.hidden);
|
|
149
|
+
return (
|
|
150
|
+
<dl className="sfui-totals">
|
|
151
|
+
{lines.map((l) => (
|
|
152
|
+
<div key={l.key} data-emphasis={l.emphasis || undefined}>
|
|
153
|
+
<dt>{l.label}</dt>
|
|
154
|
+
<dd>{l.formatted}</dd>
|
|
155
|
+
</div>
|
|
156
|
+
))}
|
|
157
|
+
</dl>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storefront-ui localization — the admin i18n pattern, for the shipped
|
|
3
|
+
* customer-facing surfaces (CartPage, MiniCart, CheckoutPage,
|
|
4
|
+
* OrderReceivedPage). Every functional label renders through `t()`; the
|
|
5
|
+
* wording lives in `./locales/<lang>.js` (en, de, es, fr, ja, pt ship with the
|
|
6
|
+
* plugin; en is the default and the fallback).
|
|
7
|
+
*
|
|
8
|
+
* Switching the storefront's language is a one-line change: point the `active`
|
|
9
|
+
* import below at another locale file. A language outside the six: copy
|
|
10
|
+
* `./locales/en.js` to `./locales/<code>.js`, set `$locale`, translate every
|
|
11
|
+
* value (keep `{placeholders}` verbatim), point the import at it. Missing keys
|
|
12
|
+
* fall back to English, so a partial file renders mixed rather than broken.
|
|
13
|
+
*
|
|
14
|
+
* Brand wording (titles, CTAs, trust notes — the store's voice) does NOT live
|
|
15
|
+
* here: it comes in per-component via the `brand` prop, which overrides the
|
|
16
|
+
* matching keys. Precedence: brand prop → active locale → English.
|
|
17
|
+
*/
|
|
18
|
+
import en from "./locales/en.js";
|
|
19
|
+
import active from "./locales/en.js"; // ← the storefront's language: "./locales/de.js", "./locales/es.js", "./locales/fr.js", "./locales/ja.js", "./locales/pt.js"
|
|
20
|
+
|
|
21
|
+
/** BCP-47 tag of the active language — set it on the page's <html lang> too. */
|
|
22
|
+
export const locale = active.$locale || "en";
|
|
23
|
+
|
|
24
|
+
function lookup(overrides, key, vars) {
|
|
25
|
+
let s = overrides?.[key] ?? active[key] ?? en[key] ?? key;
|
|
26
|
+
if (vars) {
|
|
27
|
+
for (const k of Object.keys(vars)) s = s.split(`{${k}}`).join(String(vars[k] ?? ""));
|
|
28
|
+
}
|
|
29
|
+
return s;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Message lookup with `{name}` interpolation and English fallback. */
|
|
33
|
+
export function t(key, vars) {
|
|
34
|
+
return lookup(null, key, vars);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A `t` bound to one component's brand overrides. `brand` maps SHORT names to
|
|
39
|
+
* strings; `keymap` maps those short names to the full i18n keys, so
|
|
40
|
+
* `brand={{ title: "Your bag" }}` overrides exactly `cart.title` and nothing
|
|
41
|
+
* else. Unknown short names are ignored.
|
|
42
|
+
*/
|
|
43
|
+
export function makeT(brand, keymap) {
|
|
44
|
+
if (!brand) return t;
|
|
45
|
+
const overrides = {};
|
|
46
|
+
for (const short of Object.keys(brand)) {
|
|
47
|
+
const full = keymap[short];
|
|
48
|
+
if (full && brand[short] != null) overrides[full] = brand[short];
|
|
49
|
+
}
|
|
50
|
+
return (key, vars) => lookup(overrides, key, vars);
|
|
51
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Deutsch — formelle Anrede (Sie) durchgehend. Schlüssel wie in en.js. */
|
|
2
|
+
export default {
|
|
3
|
+
$locale: "de",
|
|
4
|
+
|
|
5
|
+
"common.loading": "Wird geladen…",
|
|
6
|
+
"common.error.title": "Etwas ist schiefgelaufen",
|
|
7
|
+
"common.error.retry": "Erneut versuchen",
|
|
8
|
+
"common.remove": "Entfernen",
|
|
9
|
+
"common.quantity": "Menge",
|
|
10
|
+
"common.decrease": "Menge verringern",
|
|
11
|
+
"common.increase": "Menge erhöhen",
|
|
12
|
+
"common.free": "Kostenlos",
|
|
13
|
+
"common.totals.subtotal": "Zwischensumme",
|
|
14
|
+
"common.totals.discount": "Rabatt",
|
|
15
|
+
"common.totals.shipping": "Versand",
|
|
16
|
+
"common.totals.tax": "Steuern",
|
|
17
|
+
"common.totals.total": "Gesamtsumme",
|
|
18
|
+
"common.coupon.label": "Rabattcode",
|
|
19
|
+
"common.coupon.placeholder": "Code eingeben",
|
|
20
|
+
"common.coupon.apply": "Einlösen",
|
|
21
|
+
"common.coupon.applied": "Code {code} eingelöst",
|
|
22
|
+
"common.coupon.remove": "Code entfernen",
|
|
23
|
+
|
|
24
|
+
"cart.title": "Ihr Warenkorb",
|
|
25
|
+
"cart.summaryTitle": "Bestellübersicht",
|
|
26
|
+
"cart.checkoutCta": "Zur Kasse",
|
|
27
|
+
"cart.continueShopping": "Weiter einkaufen",
|
|
28
|
+
"cart.empty.title": "Ihr Warenkorb ist leer",
|
|
29
|
+
"cart.empty.body": "Alles, was Sie hinzufügen, erscheint hier.",
|
|
30
|
+
"cart.empty.cta": "Zum Shop",
|
|
31
|
+
"cart.notes.label": "Anmerkungen zur Bestellung",
|
|
32
|
+
"cart.notes.placeholder": "Gibt es etwas, das wir wissen sollten?",
|
|
33
|
+
"cart.taxNote": "Versandkosten und Steuern werden an der Kasse berechnet.",
|
|
34
|
+
|
|
35
|
+
"minicart.title": "Ihr Warenkorb",
|
|
36
|
+
"minicart.close": "Warenkorb schließen",
|
|
37
|
+
"minicart.subtotal": "Zwischensumme",
|
|
38
|
+
"minicart.checkout": "Zur Kasse",
|
|
39
|
+
"minicart.viewCart": "Warenkorb ansehen",
|
|
40
|
+
"minicart.empty.title": "Ihr Warenkorb ist leer",
|
|
41
|
+
"minicart.empty.cta": "Weiter stöbern",
|
|
42
|
+
"minicart.button": "Warenkorb öffnen",
|
|
43
|
+
|
|
44
|
+
"checkout.title": "Kasse",
|
|
45
|
+
"checkout.contactTitle": "Kontakt & Rechnungsadresse",
|
|
46
|
+
"checkout.shipToDifferent": "An eine andere Adresse liefern",
|
|
47
|
+
"checkout.shippingAddressTitle": "Lieferadresse",
|
|
48
|
+
"checkout.shippingTitle": "Versandart",
|
|
49
|
+
"checkout.paymentTitle": "Zahlung",
|
|
50
|
+
"checkout.summaryTitle": "Bestellübersicht",
|
|
51
|
+
"checkout.notes.label": "Anmerkungen zur Bestellung",
|
|
52
|
+
"checkout.notes.placeholder": "Gibt es etwas, das wir wissen sollten?",
|
|
53
|
+
"checkout.terms": "Ich stimme den Verkaufsbedingungen zu",
|
|
54
|
+
"checkout.placeOrder": "Jetzt bestellen",
|
|
55
|
+
"checkout.placing": "Ihre Bestellung wird aufgegeben…",
|
|
56
|
+
"checkout.submitted": "Bestellung aufgegeben — Sie werden zur Bestätigung weitergeleitet…",
|
|
57
|
+
"checkout.empty.title": "Es gibt noch nichts zu bestellen",
|
|
58
|
+
"checkout.empty.cta": "Zurück zum Shop",
|
|
59
|
+
"checkout.hint.missing_address": "Geben Sie Ihre Adresse ein, um Lieferoptionen zu sehen.",
|
|
60
|
+
"checkout.hint.none_available": "An diese Adresse können wir leider noch nicht liefern.",
|
|
61
|
+
"checkout.hint.syncing": "Lieferoptionen werden aktualisiert…",
|
|
62
|
+
"checkout.hint.payment_none": "Die Kasse ist vorübergehend nicht verfügbar — keine Zahlungsart aktiviert.",
|
|
63
|
+
"checkout.blocker.cart_loading": "Ihr Warenkorb wird geladen…",
|
|
64
|
+
"checkout.blocker.empty_cart": "Ihr Warenkorb ist leer.",
|
|
65
|
+
"checkout.blocker.billing_incomplete": "Füllen Sie die erforderlichen Kontaktfelder aus.",
|
|
66
|
+
"checkout.blocker.shipping_address_incomplete": "Füllen Sie die Lieferadresse aus.",
|
|
67
|
+
"checkout.blocker.shipping_recalculating": "Versandkosten werden aktualisiert…",
|
|
68
|
+
"checkout.blocker.shipping_address_required": "Geben Sie Ihre Adresse ein, um Lieferoptionen zu sehen.",
|
|
69
|
+
"checkout.blocker.shipping_method_required": "Wählen Sie eine Versandart.",
|
|
70
|
+
"checkout.blocker.shipping_not_available": "An diese Adresse können wir leider noch nicht liefern.",
|
|
71
|
+
"checkout.blocker.payment_method_required": "Wählen Sie eine Zahlungsart.",
|
|
72
|
+
"checkout.error.card_payment_in_preview": "Kartenzahlung ist in der Vorschau nicht möglich — öffnen Sie den veröffentlichten Shop.",
|
|
73
|
+
|
|
74
|
+
"order.orderLabel": "Bestellung",
|
|
75
|
+
"order.dateLabel": "Datum",
|
|
76
|
+
"order.qty": "Menge {count}",
|
|
77
|
+
"order.backToStore": "Zurück zum Shop",
|
|
78
|
+
"order.paid.title": "Vielen Dank — Ihre Bestellung ist bestätigt",
|
|
79
|
+
"order.paid.body": "Eine Bestätigung ist auf dem Weg in Ihr Postfach.",
|
|
80
|
+
"order.unpaid.title": "Bestellung eingegangen — Zahlung ausstehend",
|
|
81
|
+
"order.unpaid.body": "Ihre Bestellung wird zurückgehalten, bis die Zahlung eingeht. Nutzen Sie die folgenden Angaben.",
|
|
82
|
+
"order.instructionsTitle": "Zahlungshinweise",
|
|
83
|
+
"order.payNow": "Zahlung abschließen",
|
|
84
|
+
"order.cancelled.title": "Diese Bestellung wurde storniert",
|
|
85
|
+
"order.cancelled.body": "Falls das nicht beabsichtigt war, können Sie die Zahlung erneut versuchen.",
|
|
86
|
+
"order.retryPayment": "Zahlung erneut versuchen",
|
|
87
|
+
"order.error.title": "Wir konnten diese Bestellung nicht verifizieren",
|
|
88
|
+
"order.error.body": "Der Link ist möglicherweise unvollständig — verwenden Sie den Link aus Ihrer Bestätigungs-E-Mail.",
|
|
89
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* English — the storefront-ui default language and the fallback for every
|
|
3
|
+
* other locale file. Keys are flat and dot-namespaced by surface; `{name}`
|
|
4
|
+
* marks an interpolated value and must survive translation verbatim.
|
|
5
|
+
*
|
|
6
|
+
* Adding a key: add it here first, then to every other file in this folder.
|
|
7
|
+
*/
|
|
8
|
+
export default {
|
|
9
|
+
$locale: "en",
|
|
10
|
+
|
|
11
|
+
// ── shared ──────────────────────────────────────────────────────
|
|
12
|
+
"common.loading": "Loading…",
|
|
13
|
+
"common.error.title": "Something went wrong",
|
|
14
|
+
"common.error.retry": "Try again",
|
|
15
|
+
"common.remove": "Remove",
|
|
16
|
+
"common.quantity": "Quantity",
|
|
17
|
+
"common.decrease": "Decrease quantity",
|
|
18
|
+
"common.increase": "Increase quantity",
|
|
19
|
+
"common.free": "Free",
|
|
20
|
+
"common.totals.subtotal": "Subtotal",
|
|
21
|
+
"common.totals.discount": "Discount",
|
|
22
|
+
"common.totals.shipping": "Shipping",
|
|
23
|
+
"common.totals.tax": "Tax",
|
|
24
|
+
"common.totals.total": "Total",
|
|
25
|
+
"common.coupon.label": "Discount code",
|
|
26
|
+
"common.coupon.placeholder": "Enter code",
|
|
27
|
+
"common.coupon.apply": "Apply",
|
|
28
|
+
"common.coupon.applied": "Code {code} applied",
|
|
29
|
+
"common.coupon.remove": "Remove code",
|
|
30
|
+
|
|
31
|
+
// ── cart page ───────────────────────────────────────────────────
|
|
32
|
+
"cart.title": "Your cart",
|
|
33
|
+
"cart.summaryTitle": "Order summary",
|
|
34
|
+
"cart.checkoutCta": "Proceed to checkout",
|
|
35
|
+
"cart.continueShopping": "Continue shopping",
|
|
36
|
+
"cart.empty.title": "Your cart is empty",
|
|
37
|
+
"cart.empty.body": "Everything you add will show up here.",
|
|
38
|
+
"cart.empty.cta": "Browse the store",
|
|
39
|
+
"cart.notes.label": "Order notes",
|
|
40
|
+
"cart.notes.placeholder": "Anything we should know about your order?",
|
|
41
|
+
"cart.taxNote": "Shipping and taxes are calculated at checkout.",
|
|
42
|
+
|
|
43
|
+
// ── minicart ────────────────────────────────────────────────────
|
|
44
|
+
"minicart.title": "Your cart",
|
|
45
|
+
"minicart.close": "Close cart",
|
|
46
|
+
"minicart.subtotal": "Subtotal",
|
|
47
|
+
"minicart.checkout": "Checkout",
|
|
48
|
+
"minicart.viewCart": "View cart",
|
|
49
|
+
"minicart.empty.title": "Your cart is empty",
|
|
50
|
+
"minicart.empty.cta": "Keep browsing",
|
|
51
|
+
"minicart.button": "Open cart",
|
|
52
|
+
|
|
53
|
+
// ── checkout ────────────────────────────────────────────────────
|
|
54
|
+
"checkout.title": "Checkout",
|
|
55
|
+
"checkout.contactTitle": "Contact & billing address",
|
|
56
|
+
"checkout.shipToDifferent": "Deliver to a different address",
|
|
57
|
+
"checkout.shippingAddressTitle": "Delivery address",
|
|
58
|
+
"checkout.shippingTitle": "Delivery method",
|
|
59
|
+
"checkout.paymentTitle": "Payment",
|
|
60
|
+
"checkout.summaryTitle": "Order summary",
|
|
61
|
+
"checkout.notes.label": "Order notes",
|
|
62
|
+
"checkout.notes.placeholder": "Anything we should know about your order?",
|
|
63
|
+
"checkout.terms": "I agree to the terms of sale",
|
|
64
|
+
"checkout.placeOrder": "Place order",
|
|
65
|
+
"checkout.placing": "Placing your order…",
|
|
66
|
+
"checkout.submitted": "Order placed — taking you to your confirmation…",
|
|
67
|
+
"checkout.empty.title": "There's nothing to check out yet",
|
|
68
|
+
"checkout.empty.cta": "Back to the store",
|
|
69
|
+
"checkout.hint.missing_address": "Enter your address to see delivery options.",
|
|
70
|
+
"checkout.hint.none_available": "We can't deliver to that address yet.",
|
|
71
|
+
"checkout.hint.syncing": "Updating delivery options…",
|
|
72
|
+
"checkout.hint.payment_none": "Checkout is temporarily unavailable — no payment method is enabled.",
|
|
73
|
+
"checkout.blocker.cart_loading": "Loading your cart…",
|
|
74
|
+
"checkout.blocker.empty_cart": "Your cart is empty.",
|
|
75
|
+
"checkout.blocker.billing_incomplete": "Fill in the required contact fields.",
|
|
76
|
+
"checkout.blocker.shipping_address_incomplete": "Fill in the delivery address.",
|
|
77
|
+
"checkout.blocker.shipping_recalculating": "Updating delivery costs…",
|
|
78
|
+
"checkout.blocker.shipping_address_required": "Enter your address to see delivery options.",
|
|
79
|
+
"checkout.blocker.shipping_method_required": "Choose a delivery method.",
|
|
80
|
+
"checkout.blocker.shipping_not_available": "We can't deliver to that address yet.",
|
|
81
|
+
"checkout.blocker.payment_method_required": "Choose how you'd like to pay.",
|
|
82
|
+
"checkout.error.card_payment_in_preview": "Card payment can't be completed in preview — open the published store to pay.",
|
|
83
|
+
|
|
84
|
+
// ── order received ──────────────────────────────────────────────
|
|
85
|
+
"order.orderLabel": "Order",
|
|
86
|
+
"order.dateLabel": "Date",
|
|
87
|
+
"order.qty": "Qty {count}",
|
|
88
|
+
"order.backToStore": "Back to the store",
|
|
89
|
+
"order.paid.title": "Thank you — your order is confirmed",
|
|
90
|
+
"order.paid.body": "A confirmation is on its way to your inbox.",
|
|
91
|
+
"order.unpaid.title": "Order received — awaiting payment",
|
|
92
|
+
"order.unpaid.body": "Your order is on hold until payment is received. Use the details below to complete it.",
|
|
93
|
+
"order.instructionsTitle": "Payment instructions",
|
|
94
|
+
"order.payNow": "Complete payment",
|
|
95
|
+
"order.cancelled.title": "This order was cancelled",
|
|
96
|
+
"order.cancelled.body": "If that wasn't intended, you can retry the payment.",
|
|
97
|
+
"order.retryPayment": "Retry payment",
|
|
98
|
+
"order.error.title": "We couldn't verify this order",
|
|
99
|
+
"order.error.body": "The link may be incomplete — try the link from your confirmation email.",
|
|
100
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Español — tono cercano y claro, neutro latinoamericano. Claves como en en.js. */
|
|
2
|
+
export default {
|
|
3
|
+
$locale: "es",
|
|
4
|
+
|
|
5
|
+
"common.loading": "Cargando…",
|
|
6
|
+
"common.error.title": "Algo salió mal",
|
|
7
|
+
"common.error.retry": "Intentar de nuevo",
|
|
8
|
+
"common.remove": "Eliminar",
|
|
9
|
+
"common.quantity": "Cantidad",
|
|
10
|
+
"common.decrease": "Disminuir cantidad",
|
|
11
|
+
"common.increase": "Aumentar cantidad",
|
|
12
|
+
"common.free": "Gratis",
|
|
13
|
+
"common.totals.subtotal": "Subtotal",
|
|
14
|
+
"common.totals.discount": "Descuento",
|
|
15
|
+
"common.totals.shipping": "Envío",
|
|
16
|
+
"common.totals.tax": "Impuestos",
|
|
17
|
+
"common.totals.total": "Total",
|
|
18
|
+
"common.coupon.label": "Código de descuento",
|
|
19
|
+
"common.coupon.placeholder": "Ingresa el código",
|
|
20
|
+
"common.coupon.apply": "Aplicar",
|
|
21
|
+
"common.coupon.applied": "Código {code} aplicado",
|
|
22
|
+
"common.coupon.remove": "Quitar código",
|
|
23
|
+
|
|
24
|
+
"cart.title": "Tu carrito",
|
|
25
|
+
"cart.summaryTitle": "Resumen del pedido",
|
|
26
|
+
"cart.checkoutCta": "Ir a pagar",
|
|
27
|
+
"cart.continueShopping": "Seguir comprando",
|
|
28
|
+
"cart.empty.title": "Tu carrito está vacío",
|
|
29
|
+
"cart.empty.body": "Todo lo que agregues aparecerá aquí.",
|
|
30
|
+
"cart.empty.cta": "Explorar la tienda",
|
|
31
|
+
"cart.notes.label": "Notas del pedido",
|
|
32
|
+
"cart.notes.placeholder": "¿Algo que debamos saber sobre tu pedido?",
|
|
33
|
+
"cart.taxNote": "El envío y los impuestos se calculan al pagar.",
|
|
34
|
+
|
|
35
|
+
"minicart.title": "Tu carrito",
|
|
36
|
+
"minicart.close": "Cerrar carrito",
|
|
37
|
+
"minicart.subtotal": "Subtotal",
|
|
38
|
+
"minicart.checkout": "Pagar",
|
|
39
|
+
"minicart.viewCart": "Ver carrito",
|
|
40
|
+
"minicart.empty.title": "Tu carrito está vacío",
|
|
41
|
+
"minicart.empty.cta": "Seguir explorando",
|
|
42
|
+
"minicart.button": "Abrir carrito",
|
|
43
|
+
|
|
44
|
+
"checkout.title": "Pago",
|
|
45
|
+
"checkout.contactTitle": "Contacto y dirección de facturación",
|
|
46
|
+
"checkout.shipToDifferent": "Enviar a una dirección diferente",
|
|
47
|
+
"checkout.shippingAddressTitle": "Dirección de entrega",
|
|
48
|
+
"checkout.shippingTitle": "Método de envío",
|
|
49
|
+
"checkout.paymentTitle": "Pago",
|
|
50
|
+
"checkout.summaryTitle": "Resumen del pedido",
|
|
51
|
+
"checkout.notes.label": "Notas del pedido",
|
|
52
|
+
"checkout.notes.placeholder": "¿Algo que debamos saber sobre tu pedido?",
|
|
53
|
+
"checkout.terms": "Acepto las condiciones de venta",
|
|
54
|
+
"checkout.placeOrder": "Realizar pedido",
|
|
55
|
+
"checkout.placing": "Realizando tu pedido…",
|
|
56
|
+
"checkout.submitted": "Pedido realizado — te llevamos a tu confirmación…",
|
|
57
|
+
"checkout.empty.title": "Todavía no hay nada para pagar",
|
|
58
|
+
"checkout.empty.cta": "Volver a la tienda",
|
|
59
|
+
"checkout.hint.missing_address": "Ingresa tu dirección para ver las opciones de entrega.",
|
|
60
|
+
"checkout.hint.none_available": "Aún no podemos hacer entregas en esa dirección.",
|
|
61
|
+
"checkout.hint.syncing": "Actualizando opciones de entrega…",
|
|
62
|
+
"checkout.hint.payment_none": "El pago no está disponible por el momento — no hay ningún método de pago habilitado.",
|
|
63
|
+
"checkout.blocker.cart_loading": "Cargando tu carrito…",
|
|
64
|
+
"checkout.blocker.empty_cart": "Tu carrito está vacío.",
|
|
65
|
+
"checkout.blocker.billing_incomplete": "Completa los campos de contacto obligatorios.",
|
|
66
|
+
"checkout.blocker.shipping_address_incomplete": "Completa la dirección de entrega.",
|
|
67
|
+
"checkout.blocker.shipping_recalculating": "Actualizando costos de envío…",
|
|
68
|
+
"checkout.blocker.shipping_address_required": "Ingresa tu dirección para ver las opciones de entrega.",
|
|
69
|
+
"checkout.blocker.shipping_method_required": "Elige un método de envío.",
|
|
70
|
+
"checkout.blocker.shipping_not_available": "Aún no podemos hacer entregas en esa dirección.",
|
|
71
|
+
"checkout.blocker.payment_method_required": "Elige cómo quieres pagar.",
|
|
72
|
+
"checkout.error.card_payment_in_preview": "El pago con tarjeta no puede completarse en la vista previa — abre la tienda publicada para pagar.",
|
|
73
|
+
|
|
74
|
+
"order.orderLabel": "Pedido",
|
|
75
|
+
"order.dateLabel": "Fecha",
|
|
76
|
+
"order.qty": "Cant. {count}",
|
|
77
|
+
"order.backToStore": "Volver a la tienda",
|
|
78
|
+
"order.paid.title": "Gracias — tu pedido está confirmado",
|
|
79
|
+
"order.paid.body": "Una confirmación va en camino a tu correo.",
|
|
80
|
+
"order.unpaid.title": "Pedido recibido — pago pendiente",
|
|
81
|
+
"order.unpaid.body": "Tu pedido queda en espera hasta recibir el pago. Usa los datos de abajo para completarlo.",
|
|
82
|
+
"order.instructionsTitle": "Instrucciones de pago",
|
|
83
|
+
"order.payNow": "Completar el pago",
|
|
84
|
+
"order.cancelled.title": "Este pedido fue cancelado",
|
|
85
|
+
"order.cancelled.body": "Si no fue tu intención, puedes reintentar el pago.",
|
|
86
|
+
"order.retryPayment": "Reintentar el pago",
|
|
87
|
+
"order.error.title": "No pudimos verificar este pedido",
|
|
88
|
+
"order.error.body": "Puede que el enlace esté incompleto — usa el enlace de tu correo de confirmación.",
|
|
89
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Français — vouvoiement systématique. Clés identiques à en.js. */
|
|
2
|
+
export default {
|
|
3
|
+
$locale: "fr",
|
|
4
|
+
|
|
5
|
+
"common.loading": "Chargement…",
|
|
6
|
+
"common.error.title": "Une erreur est survenue",
|
|
7
|
+
"common.error.retry": "Réessayer",
|
|
8
|
+
"common.remove": "Retirer",
|
|
9
|
+
"common.quantity": "Quantité",
|
|
10
|
+
"common.decrease": "Diminuer la quantité",
|
|
11
|
+
"common.increase": "Augmenter la quantité",
|
|
12
|
+
"common.free": "Offert",
|
|
13
|
+
"common.totals.subtotal": "Sous-total",
|
|
14
|
+
"common.totals.discount": "Remise",
|
|
15
|
+
"common.totals.shipping": "Livraison",
|
|
16
|
+
"common.totals.tax": "Taxes",
|
|
17
|
+
"common.totals.total": "Total",
|
|
18
|
+
"common.coupon.label": "Code de réduction",
|
|
19
|
+
"common.coupon.placeholder": "Saisissez le code",
|
|
20
|
+
"common.coupon.apply": "Appliquer",
|
|
21
|
+
"common.coupon.applied": "Code {code} appliqué",
|
|
22
|
+
"common.coupon.remove": "Retirer le code",
|
|
23
|
+
|
|
24
|
+
"cart.title": "Votre panier",
|
|
25
|
+
"cart.summaryTitle": "Récapitulatif de commande",
|
|
26
|
+
"cart.checkoutCta": "Passer au paiement",
|
|
27
|
+
"cart.continueShopping": "Continuer vos achats",
|
|
28
|
+
"cart.empty.title": "Votre panier est vide",
|
|
29
|
+
"cart.empty.body": "Tout ce que vous ajoutez apparaîtra ici.",
|
|
30
|
+
"cart.empty.cta": "Découvrir la boutique",
|
|
31
|
+
"cart.notes.label": "Notes de commande",
|
|
32
|
+
"cart.notes.placeholder": "Un détail à nous signaler concernant votre commande ?",
|
|
33
|
+
"cart.taxNote": "Les frais de livraison et les taxes sont calculés au paiement.",
|
|
34
|
+
|
|
35
|
+
"minicart.title": "Votre panier",
|
|
36
|
+
"minicart.close": "Fermer le panier",
|
|
37
|
+
"minicart.subtotal": "Sous-total",
|
|
38
|
+
"minicart.checkout": "Paiement",
|
|
39
|
+
"minicart.viewCart": "Voir le panier",
|
|
40
|
+
"minicart.empty.title": "Votre panier est vide",
|
|
41
|
+
"minicart.empty.cta": "Continuer à explorer",
|
|
42
|
+
"minicart.button": "Ouvrir le panier",
|
|
43
|
+
|
|
44
|
+
"checkout.title": "Paiement",
|
|
45
|
+
"checkout.contactTitle": "Contact et adresse de facturation",
|
|
46
|
+
"checkout.shipToDifferent": "Livrer à une autre adresse",
|
|
47
|
+
"checkout.shippingAddressTitle": "Adresse de livraison",
|
|
48
|
+
"checkout.shippingTitle": "Mode de livraison",
|
|
49
|
+
"checkout.paymentTitle": "Paiement",
|
|
50
|
+
"checkout.summaryTitle": "Récapitulatif de commande",
|
|
51
|
+
"checkout.notes.label": "Notes de commande",
|
|
52
|
+
"checkout.notes.placeholder": "Un détail à nous signaler concernant votre commande ?",
|
|
53
|
+
"checkout.terms": "J'accepte les conditions de vente",
|
|
54
|
+
"checkout.placeOrder": "Commander",
|
|
55
|
+
"checkout.placing": "Votre commande est en cours…",
|
|
56
|
+
"checkout.submitted": "Commande passée — redirection vers votre confirmation…",
|
|
57
|
+
"checkout.empty.title": "Il n'y a encore rien à régler",
|
|
58
|
+
"checkout.empty.cta": "Retour à la boutique",
|
|
59
|
+
"checkout.hint.missing_address": "Saisissez votre adresse pour voir les options de livraison.",
|
|
60
|
+
"checkout.hint.none_available": "Nous ne livrons pas encore à cette adresse.",
|
|
61
|
+
"checkout.hint.syncing": "Mise à jour des options de livraison…",
|
|
62
|
+
"checkout.hint.payment_none": "Le paiement est temporairement indisponible — aucun moyen de paiement n'est activé.",
|
|
63
|
+
"checkout.blocker.cart_loading": "Chargement de votre panier…",
|
|
64
|
+
"checkout.blocker.empty_cart": "Votre panier est vide.",
|
|
65
|
+
"checkout.blocker.billing_incomplete": "Renseignez les champs de contact obligatoires.",
|
|
66
|
+
"checkout.blocker.shipping_address_incomplete": "Renseignez l'adresse de livraison.",
|
|
67
|
+
"checkout.blocker.shipping_recalculating": "Mise à jour des frais de livraison…",
|
|
68
|
+
"checkout.blocker.shipping_address_required": "Saisissez votre adresse pour voir les options de livraison.",
|
|
69
|
+
"checkout.blocker.shipping_method_required": "Choisissez un mode de livraison.",
|
|
70
|
+
"checkout.blocker.shipping_not_available": "Nous ne livrons pas encore à cette adresse.",
|
|
71
|
+
"checkout.blocker.payment_method_required": "Choisissez votre moyen de paiement.",
|
|
72
|
+
"checkout.error.card_payment_in_preview": "Le paiement par carte est impossible dans l'aperçu — ouvrez la boutique publiée pour payer.",
|
|
73
|
+
|
|
74
|
+
"order.orderLabel": "Commande",
|
|
75
|
+
"order.dateLabel": "Date",
|
|
76
|
+
"order.qty": "Qté {count}",
|
|
77
|
+
"order.backToStore": "Retour à la boutique",
|
|
78
|
+
"order.paid.title": "Merci — votre commande est confirmée",
|
|
79
|
+
"order.paid.body": "Une confirmation est en route vers votre boîte mail.",
|
|
80
|
+
"order.unpaid.title": "Commande reçue — en attente de paiement",
|
|
81
|
+
"order.unpaid.body": "Votre commande est en attente jusqu'à réception du paiement. Utilisez les informations ci-dessous.",
|
|
82
|
+
"order.instructionsTitle": "Instructions de paiement",
|
|
83
|
+
"order.payNow": "Finaliser le paiement",
|
|
84
|
+
"order.cancelled.title": "Cette commande a été annulée",
|
|
85
|
+
"order.cancelled.body": "Si ce n'était pas votre intention, vous pouvez retenter le paiement.",
|
|
86
|
+
"order.retryPayment": "Retenter le paiement",
|
|
87
|
+
"order.error.title": "Impossible de vérifier cette commande",
|
|
88
|
+
"order.error.body": "Le lien est peut-être incomplet — utilisez celui de votre e-mail de confirmation.",
|
|
89
|
+
};
|