@modainteract/moda-interact-shared 5.5.0 → 5.6.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/dist/chunk-KIHGDO35.js +148 -0
- package/dist/chunk-KIHGDO35.js.map +1 -0
- package/dist/{chunk-BDGLKOCW.js → chunk-S42O2OCG.js} +62 -2
- package/dist/chunk-S42O2OCG.js.map +1 -0
- package/dist/{chunk-XB5NIOKJ.js → chunk-WONND3OK.js} +3 -1
- package/dist/chunk-WONND3OK.js.map +1 -0
- package/dist/commerce/index.d.ts +5 -3
- package/dist/commerce/index.js +16 -2
- package/dist/commerce/index.js.map +1 -1
- package/dist/commerce/model/node.d.ts +3 -2
- package/dist/commerce/model/node.js +2 -2
- package/dist/commerce/presentation/index.d.ts +323 -0
- package/dist/commerce/presentation/index.js +594 -0
- package/dist/commerce/presentation/index.js.map +1 -0
- package/dist/commerce/runner/index.d.ts +4 -3
- package/dist/commerce/runner/index.js +62 -20
- package/dist/commerce/runner/index.js.map +1 -1
- package/dist/product-context-D3FuI6ah.d.ts +746 -0
- package/dist/{response-C1JsiFoO.d.ts → response-Cs4zC3d3.d.ts} +140 -707
- package/dist/{types-Ds5QJ621.d.ts → types-ByoKuP90.d.ts} +6 -2
- package/package.json +7 -2
- package/dist/chunk-BDGLKOCW.js.map +0 -1
- package/dist/chunk-BYAI6W7T.js +0 -97
- package/dist/chunk-BYAI6W7T.js.map +0 -1
- package/dist/chunk-XB5NIOKJ.js.map +0 -1
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommerceFinalResponseSchema,
|
|
3
|
+
CommerceProductOptionsSchema,
|
|
4
|
+
CommerceProductSchema,
|
|
5
|
+
CommerceProductSelectedOptionsSchema
|
|
6
|
+
} from "../../chunk-S42O2OCG.js";
|
|
7
|
+
import "../../chunk-QVPEX7KY.js";
|
|
8
|
+
import "../../chunk-AUS3WQIW.js";
|
|
9
|
+
import {
|
|
10
|
+
MODA_SUPPORTED_LANGUAGE_TAGS,
|
|
11
|
+
ModaSupportedLanguageTagSchema,
|
|
12
|
+
canonicaliseLanguageTag,
|
|
13
|
+
createInternationalizationRuntime,
|
|
14
|
+
resolveModaConfigurationLocale,
|
|
15
|
+
validateIcuCatalogue
|
|
16
|
+
} from "../../chunk-4KSODB2R.js";
|
|
17
|
+
|
|
18
|
+
// src/commerce/presentation/schemas.ts
|
|
19
|
+
import { z as z2 } from "zod";
|
|
20
|
+
|
|
21
|
+
// src/commerce/presentation/option-messages.ts
|
|
22
|
+
var labels = {
|
|
23
|
+
"zh-Hans": ["\u9009\u9879\u8BE6\u60C5\u4E0D\u53EF\u7528\u3002", "\u9009\u9879\u5217\u8868\u4E0D\u5B8C\u6574\u3002", "\u6240\u9009\u9009\u9879"],
|
|
24
|
+
"zh-Hant": ["\u9078\u9805\u8A73\u60C5\u7121\u6CD5\u53D6\u5F97\u3002", "\u9078\u9805\u6E05\u55AE\u4E0D\u5B8C\u6574\u3002", "\u6240\u9078\u9078\u9805"],
|
|
25
|
+
cs: ["Podrobnosti mo\u017Enost\xED nejsou dostupn\xE9.", "Seznam mo\u017Enost\xED nen\xED \xFApln\xFD.", "Vybran\xE9 mo\u017Enosti"],
|
|
26
|
+
da: ["Oplysninger om valgmuligheder er ikke tilg\xE6ngelige.", "Listen over valgmuligheder er ufuldst\xE6ndig.", "Valgte muligheder"],
|
|
27
|
+
nl: ["Optiedetails zijn niet beschikbaar.", "De optielijst is onvolledig.", "Geselecteerde opties"],
|
|
28
|
+
en: ["Option details are unavailable.", "The option list is incomplete.", "Selected options"],
|
|
29
|
+
fi: ["Vaihtoehtojen tiedot eiv\xE4t ole saatavilla.", "Vaihtoehtoluettelo on puutteellinen.", "Valitut vaihtoehdot"],
|
|
30
|
+
fr: ["Les d\xE9tails des options sont indisponibles.", "La liste des options est incompl\xE8te.", "Options s\xE9lectionn\xE9es"],
|
|
31
|
+
de: ["Optionsdetails sind nicht verf\xFCgbar.", "Die Optionsliste ist unvollst\xE4ndig.", "Ausgew\xE4hlte Optionen"],
|
|
32
|
+
it: ["I dettagli delle opzioni non sono disponibili.", "L\u2019elenco delle opzioni \xE8 incompleto.", "Opzioni selezionate"],
|
|
33
|
+
ja: ["\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\u8A73\u7D30\u306F\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002", "\u30AA\u30D7\u30B7\u30E7\u30F3\u4E00\u89A7\u306F\u5B8C\u5168\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002", "\u9078\u629E\u3057\u305F\u30AA\u30D7\u30B7\u30E7\u30F3"],
|
|
34
|
+
ko: ["\uC635\uC158 \uC138\uBD80 \uC815\uBCF4\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "\uC635\uC158 \uBAA9\uB85D\uC774 \uC644\uC804\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "\uC120\uD0DD\uD55C \uC635\uC158"],
|
|
35
|
+
nb: ["Alternativdetaljer er ikke tilgjengelige.", "Listen over alternativer er ufullstendig.", "Valgte alternativer"],
|
|
36
|
+
pl: ["Szczeg\xF3\u0142y opcji s\u0105 niedost\u0119pne.", "Lista opcji jest niepe\u0142na.", "Wybrane opcje"],
|
|
37
|
+
"pt-BR": ["Os detalhes das op\xE7\xF5es est\xE3o indispon\xEDveis.", "A lista de op\xE7\xF5es est\xE1 incompleta.", "Op\xE7\xF5es selecionadas"],
|
|
38
|
+
"pt-PT": ["Os detalhes das op\xE7\xF5es est\xE3o indispon\xEDveis.", "A lista de op\xE7\xF5es est\xE1 incompleta.", "Op\xE7\xF5es selecionadas"],
|
|
39
|
+
es: ["Los detalles de las opciones no est\xE1n disponibles.", "La lista de opciones est\xE1 incompleta.", "Opciones seleccionadas"],
|
|
40
|
+
sv: ["Alternativinformation \xE4r inte tillg\xE4nglig.", "Listan \xF6ver alternativ \xE4r ofullst\xE4ndig.", "Valda alternativ"],
|
|
41
|
+
th: ["\u0E44\u0E21\u0E48\u0E21\u0E35\u0E23\u0E32\u0E22\u0E25\u0E30\u0E40\u0E2D\u0E35\u0E22\u0E14\u0E15\u0E31\u0E27\u0E40\u0E25\u0E37\u0E2D\u0E01", "\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E15\u0E31\u0E27\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E21\u0E48\u0E04\u0E23\u0E1A\u0E16\u0E49\u0E27\u0E19", "\u0E15\u0E31\u0E27\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E17\u0E35\u0E48\u0E40\u0E25\u0E37\u0E2D\u0E01"],
|
|
42
|
+
tr: ["Se\xE7enek ayr\u0131nt\u0131lar\u0131 mevcut de\u011Fil.", "Se\xE7enek listesi eksik.", "Se\xE7ilen se\xE7enekler"]
|
|
43
|
+
};
|
|
44
|
+
var productOptionMessages = Object.freeze(labels);
|
|
45
|
+
|
|
46
|
+
// src/commerce/presentation/messages.ts
|
|
47
|
+
var recommendationMessageKeys = [
|
|
48
|
+
"scopeLimit",
|
|
49
|
+
"shippingUnverified",
|
|
50
|
+
"shippingRequired",
|
|
51
|
+
"shippingNotRequired",
|
|
52
|
+
"policyLink",
|
|
53
|
+
"storeLink",
|
|
54
|
+
"productOption",
|
|
55
|
+
"proposedQuantity",
|
|
56
|
+
"addProposal",
|
|
57
|
+
"replaceProposal",
|
|
58
|
+
"currentPrice",
|
|
59
|
+
"regularPrice",
|
|
60
|
+
"percentTerms",
|
|
61
|
+
"amountTerms",
|
|
62
|
+
"freeShippingTerms",
|
|
63
|
+
"code",
|
|
64
|
+
"automatic",
|
|
65
|
+
"checkoutQualification",
|
|
66
|
+
"productLink",
|
|
67
|
+
"noVerifiedOffer",
|
|
68
|
+
"optionsUnavailable",
|
|
69
|
+
"optionsPartial",
|
|
70
|
+
"optionsSelected"
|
|
71
|
+
];
|
|
72
|
+
var rows = {
|
|
73
|
+
"zh-Hans": ["\u6211\u53EF\u4EE5\u5E2E\u52A9\u60A8\u4E86\u89E3\u672C\u5E97\u5546\u54C1\uFF0C\u4F46\u65E0\u6CD5\u56DE\u7B54\u8FD9\u4E2A\u95EE\u9898\u3002", "\u6211\u65E0\u6CD5\u786E\u8BA4\u60A8\u8BE2\u95EE\u7684\u914D\u9001\u8BE6\u60C5\u3002\u8BF7\u5411\u5546\u5E97\u67E5\u8BE2\u3002", "\u6B64\u5546\u54C1\u9700\u8981\u914D\u9001\u3002", "\u6B64\u5546\u54C1\u65E0\u9700\u914D\u9001\u3002", "\u914D\u9001\u653F\u7B56\uFF1A{url}", "\u5546\u5E97\uFF1A{url}", "{title}", "\u62DF\u8D2D\u4E70\u6570\u91CF\uFF1A{quantity}\uFF1A", "\u5982\u679C\u60A8\u5C06\u6B64\u5546\u54C1\u7684 {quantity} \u4EF6\u52A0\u5165\u8D2D\u7269\u7BEE\uFF1A", "\u5982\u679C\u60A8\u7528\u6B64\u5546\u54C1\u7684 {quantity} \u4EF6\u66FF\u6362\u6240\u9009\u8D2D\u7269\u7BEE\u5546\u54C1\uFF1A", "\u5F53\u524D\u6807\u4EF7\uFF1A{price}", "\u5E38\u89C4\u6807\u4EF7\uFF1A{price}", "\u4F18\u60E0\u6761\u6B3E\uFF1A{percentage} \u6298\u6263\uFF0C\u5177\u4F53\u53D6\u51B3\u4E8E\u5DF2\u6838\u5B9E\u7684\u6761\u4EF6\u3002", "\u4F18\u60E0\u6761\u6B3E\uFF1A\u51CF\u514D {amount}\uFF0C\u5177\u4F53\u53D6\u51B3\u4E8E\u5DF2\u6838\u5B9E\u7684\u6761\u4EF6\u3002", "\u4F18\u60E0\u6761\u6B3E\uFF1A\u514D\u8D39\u914D\u9001\uFF0C\u4EE5\u7ED3\u8D26\u9A8C\u8BC1\u4E3A\u51C6\u3002", "\u4F18\u60E0\u7801\uFF1A{code}", "\u81EA\u52A8\u4F18\u60E0\uFF1B\u6700\u7EC8\u5E94\u7528\u4EE5\u7ED3\u8D26\u786E\u8BA4\u4E3A\u51C6\u3002", "\u8D44\u683C\u548C\u6700\u7EC8\u4EF7\u683C\u5C06\u5728\u7ED3\u8D26\u65F6\u786E\u8BA4\u3002", "\u67E5\u770B\u5546\u54C1\uFF1A{url}", "\u6211\u65E0\u6CD5\u6838\u5B9E\u6B64\u63A8\u8350\u9002\u7528\u7684\u4F18\u60E0\u3002"],
|
|
74
|
+
"zh-Hant": ["\u6211\u53EF\u4EE5\u5354\u52A9\u60A8\u4E86\u89E3\u672C\u5E97\u5546\u54C1\uFF0C\u4F46\u7121\u6CD5\u56DE\u7B54\u9019\u500B\u554F\u984C\u3002", "\u6211\u7121\u6CD5\u78BA\u8A8D\u60A8\u8A62\u554F\u7684\u914D\u9001\u8A73\u60C5\u3002\u8ACB\u5411\u5546\u5E97\u67E5\u8A62\u3002", "\u6B64\u5546\u54C1\u9700\u8981\u914D\u9001\u3002", "\u6B64\u5546\u54C1\u4E0D\u9700\u8981\u914D\u9001\u3002", "\u914D\u9001\u653F\u7B56\uFF1A{url}", "\u5546\u5E97\uFF1A{url}", "{title}", "\u64EC\u8CFC\u8CB7\u6578\u91CF\uFF1A{quantity}\uFF1A", "\u5982\u679C\u60A8\u5C07\u6B64\u5546\u54C1\u7684 {quantity} \u4EF6\u52A0\u5165\u8CFC\u7269\u7C43\uFF1A", "\u5982\u679C\u60A8\u4EE5\u6B64\u5546\u54C1\u7684 {quantity} \u4EF6\u53D6\u4EE3\u6240\u9078\u8CFC\u7269\u7C43\u5546\u54C1\uFF1A", "\u76EE\u524D\u6A19\u50F9\uFF1A{price}", "\u4E00\u822C\u6A19\u50F9\uFF1A{price}", "\u512A\u60E0\u689D\u6B3E\uFF1A{percentage} \u6298\u6263\uFF0C\u9808\u7B26\u5408\u5DF2\u6838\u5BE6\u7684\u689D\u4EF6\u3002", "\u512A\u60E0\u689D\u6B3E\uFF1A\u6298\u62B5 {amount}\uFF0C\u9808\u7B26\u5408\u5DF2\u6838\u5BE6\u7684\u689D\u4EF6\u3002", "\u512A\u60E0\u689D\u6B3E\uFF1A\u514D\u8CBB\u914D\u9001\uFF0C\u4EE5\u7D50\u5E33\u9A57\u8B49\u70BA\u6E96\u3002", "\u512A\u60E0\u78BC\uFF1A{code}", "\u81EA\u52D5\u512A\u60E0\uFF1B\u6700\u7D42\u5957\u7528\u60C5\u6CC1\u4EE5\u7D50\u5E33\u78BA\u8A8D\u70BA\u6E96\u3002", "\u8CC7\u683C\u548C\u6700\u7D42\u50F9\u683C\u5C07\u65BC\u7D50\u5E33\u6642\u78BA\u8A8D\u3002", "\u67E5\u770B\u5546\u54C1\uFF1A{url}", "\u6211\u7121\u6CD5\u6838\u5BE6\u6B64\u63A8\u85A6\u9069\u7528\u7684\u512A\u60E0\u3002"],
|
|
75
|
+
cs: ["S produkty tohoto obchodu v\xE1m pomohu, na tuto ot\xE1zku ale odpov\u011Bd\u011Bt nemohu.", "Po\u017Eadovan\xE9 \xFAdaje o doprav\u011B nemohu potvrdit. Ov\u011B\u0159te je pros\xEDm u obchodu.", "Tuto polo\u017Eku je nutn\xE9 odeslat.", "Tuto polo\u017Eku nen\xED nutn\xE9 odes\xEDlat.", "Podm\xEDnky dopravy: {url}", "Obchod: {url}", "{title}", "Pro navrhovan\xFD n\xE1kup v mno\u017Estv\xED {quantity}:", "Pokud do ko\u0161\xEDku p\u0159id\xE1te {quantity} kus\u016F t\xE9to polo\u017Eky:", "Pokud vybranou polo\u017Eku v ko\u0161\xEDku nahrad\xEDte {quantity} kusy tohoto zbo\u017E\xED:", "Aktu\xE1ln\xED uveden\xE1 cena: {price}", "B\u011B\u017En\xE1 uveden\xE1 cena: {price}", "Podm\xEDnky nab\xEDdky: sleva {percentage}, podle ov\u011B\u0159en\xFDch podm\xEDnek.", "Podm\xEDnky nab\xEDdky: sleva {amount}, podle ov\u011B\u0159en\xFDch podm\xEDnek.", "Podm\xEDnky nab\xEDdky: doprava zdarma, podl\xE9h\xE1 ov\u011B\u0159en\xED p\u0159i placen\xED.", "K\xF3d: {code}", "Automatick\xE1 nab\xEDdka; kone\u010Dn\xE9 uplatn\u011Bn\xED potvrd\xED pokladna.", "N\xE1rok a kone\u010Dnou cenu potvrd\xED pokladna.", "Zobrazit produkt: {url}", "U tohoto n\xE1vrhu se nepoda\u0159ilo ov\u011B\u0159it pou\u017Eitelnou nab\xEDdku."],
|
|
76
|
+
da: ["Jeg kan hj\xE6lpe med butikkens produkter, men jeg kan ikke besvare det sp\xF8rgsm\xE5l.", "Jeg kan ikke bekr\xE6fte de \xF8nskede leveringsoplysninger. Sp\xF8rg venligst butikken.", "Denne vare kr\xE6ver levering.", "Denne vare kr\xE6ver ikke levering.", "Leveringspolitik: {url}", "Butik: {url}", "{title}", "For et foresl\xE5et k\xF8b af {quantity}:", "Hvis du l\xE6gger {quantity} af denne vare i kurven:", "Hvis du erstatter den valgte vare i kurven med {quantity} af denne vare:", "Aktuel angivet pris: {price}", "Normal angivet pris: {price}", "Tilbudsvilk\xE5r: {percentage} rabat, afh\xE6ngigt af de bekr\xE6ftede vilk\xE5r.", "Tilbudsvilk\xE5r: {amount} i rabat, afh\xE6ngigt af de bekr\xE6ftede vilk\xE5r.", "Tilbudsvilk\xE5r: gratis levering, bekr\xE6ftes ved betaling.", "Kode: {code}", "Automatisk tilbud; betaling bekr\xE6fter den endelige anvendelse.", "Berettigelse og endelig pris bekr\xE6ftes ved betaling.", "Se produkt: {url}", "Jeg kunne ikke bekr\xE6fte et g\xE6ldende tilbud for dette forslag."],
|
|
77
|
+
nl: ["Ik kan u helpen met producten van deze winkel, maar ik kan die vraag niet beantwoorden.", "Ik kan de gevraagde verzendgegevens niet bevestigen. Vraag het aan de winkel.", "Dit artikel moet worden verzonden.", "Dit artikel hoeft niet te worden verzonden.", "Verzendbeleid: {url}", "Winkel: {url}", "{title}", "Voor een voorgestelde aankoop van {quantity}:", "Als u {quantity} van dit artikel aan uw mandje toevoegt:", "Als u het gekozen artikel in uw mandje vervangt door {quantity} van dit artikel:", "Huidige vermelde prijs: {price}", "Normale vermelde prijs: {price}", "Aanbiedingsvoorwaarden: {percentage} korting, afhankelijk van de geverifieerde voorwaarden.", "Aanbiedingsvoorwaarden: {amount} korting, afhankelijk van de geverifieerde voorwaarden.", "Aanbiedingsvoorwaarden: gratis verzending, bevestiging bij het afrekenen.", "Code: {code}", "Automatische aanbieding; de kassa bevestigt de uiteindelijke toepassing.", "Voorwaarden en eindprijs worden bij het afrekenen bevestigd.", "Product bekijken: {url}", "Ik kon geen toepasbare aanbieding voor dit voorstel verifi\xEBren."],
|
|
78
|
+
en: ["I can help with this store's products, but I can't answer that question.", "I can't confirm the shipping details for your request. Please check with the store.", "This item requires shipping.", "This item does not require shipping.", "Shipping policy: {url}", "Store: {url}", "{title}", "For a proposed purchase of {quantity}:", "If you add {quantity} of this item to your basket:", "If you replace the selected basket item with {quantity} of this item:", "Current listed price: {price}", "Regular listed price: {price}", "Offer terms: a {percentage} discount, subject to its verified conditions.", "Offer terms: a {amount} discount, subject to its verified conditions.", "Offer terms: free shipping, subject to checkout validation.", "Code: {code}", "Automatic offer; checkout confirms the final application.", "Eligibility and the final price are confirmed at checkout.", "View product: {url}", "I couldn't verify an applicable offer for this suggestion."],
|
|
79
|
+
fi: ["Voin auttaa t\xE4m\xE4n kaupan tuotteissa, mutta en voi vastata t\xE4h\xE4n kysymykseen.", "En voi vahvistaa pyyt\xE4mi\xE4si toimitustietoja. Tarkista asia kaupasta.", "T\xE4m\xE4 tuote on toimitettava.", "T\xE4t\xE4 tuotetta ei tarvitse toimittaa.", "Toimitusehdot: {url}", "Kauppa: {url}", "{title}", "Ehdotetulle {quantity} tuotteen ostolle:", "Jos lis\xE4\xE4t {quantity} t\xE4t\xE4 tuotetta koriisi:", "Jos korvaat valitun korituotteen {quantity} t\xE4ll\xE4 tuotteella:", "Nykyinen ilmoitettu hinta: {price}", "Normaali ilmoitettu hinta: {price}", "Tarjouksen ehdot: {percentage} alennus vahvistettujen ehtojen mukaisesti.", "Tarjouksen ehdot: {amount} alennus vahvistettujen ehtojen mukaisesti.", "Tarjouksen ehdot: ilmainen toimitus, vahvistetaan kassalla.", "Koodi: {code}", "Automaattinen tarjous; kassa vahvistaa lopullisen k\xE4yt\xF6n.", "Kelpoisuus ja lopullinen hinta vahvistetaan kassalla.", "N\xE4yt\xE4 tuote: {url}", "T\xE4lle ehdotukselle ei voitu vahvistaa soveltuvaa tarjousta."],
|
|
80
|
+
fr: ["Je peux vous aider avec les produits de cette boutique, mais pas r\xE9pondre \xE0 cette question.", "Je ne peux pas confirmer les d\xE9tails de livraison demand\xE9s. Veuillez consulter la boutique.", "Cet article n\xE9cessite une livraison.", "Cet article ne n\xE9cessite pas de livraison.", "Politique de livraison : {url}", "Boutique : {url}", "{title}", "Pour un achat propos\xE9 de {quantity} :", "Si vous ajoutez {quantity} de cet article \xE0 votre panier :", "Si vous remplacez l'article s\xE9lectionn\xE9 par {quantity} de cet article :", "Prix affich\xE9 actuel : {price}", "Prix affich\xE9 habituel : {price}", "Conditions de l'offre : remise de {percentage}, selon les conditions v\xE9rifi\xE9es.", "Conditions de l'offre : remise de {amount}, selon les conditions v\xE9rifi\xE9es.", "Conditions de l'offre : livraison gratuite, \xE0 confirmer lors du paiement.", "Code : {code}", "Offre automatique ; le paiement confirme son application finale.", "L'\xE9ligibilit\xE9 et le prix final sont confirm\xE9s au paiement.", "Voir le produit : {url}", "Je n'ai pas pu v\xE9rifier une offre applicable \xE0 cette suggestion."],
|
|
81
|
+
de: ["Ich helfe Ihnen gern mit den Produkten dieses Shops, kann diese Frage aber nicht beantworten.", "Ich kann die gew\xFCnschten Versanddetails nicht best\xE4tigen. Bitte fragen Sie beim Shop nach.", "Dieser Artikel muss versendet werden.", "Dieser Artikel muss nicht versendet werden.", "Versandbedingungen: {url}", "Shop: {url}", "{title}", "F\xFCr einen vorgeschlagenen Kauf von {quantity}:", "Wenn Sie {quantity} dieses Artikels in den Warenkorb legen:", "Wenn Sie den ausgew\xE4hlten Warenkorbartikel durch {quantity} dieses Artikels ersetzen:", "Aktueller Listenpreis: {price}", "Regul\xE4rer Listenpreis: {price}", "Angebotsbedingungen: {percentage} Rabatt, vorbehaltlich der gepr\xFCften Bedingungen.", "Angebotsbedingungen: {amount} Rabatt, vorbehaltlich der gepr\xFCften Bedingungen.", "Angebotsbedingungen: kostenloser Versand, vorbehaltlich der Best\xE4tigung beim Bezahlen.", "Code: {code}", "Automatisches Angebot; die Kasse best\xE4tigt die endg\xFCltige Anwendung.", "Berechtigung und Endpreis werden beim Bezahlen best\xE4tigt.", "Produkt ansehen: {url}", "Ich konnte kein passendes Angebot f\xFCr diesen Vorschlag best\xE4tigen."],
|
|
82
|
+
it: ["Posso aiutarti con i prodotti di questo negozio, ma non posso rispondere a questa domanda.", "Non posso confermare i dettagli di spedizione richiesti. Contatta il negozio.", "Questo articolo richiede la spedizione.", "Questo articolo non richiede la spedizione.", "Politica di spedizione: {url}", "Negozio: {url}", "{title}", "Per un acquisto proposto di {quantity}:", "Se aggiungi {quantity} di questo articolo al carrello:", "Se sostituisci l'articolo selezionato nel carrello con {quantity} di questo articolo:", "Prezzo di listino attuale: {price}", "Prezzo di listino normale: {price}", "Termini dell'offerta: sconto del {percentage}, secondo le condizioni verificate.", "Termini dell'offerta: sconto di {amount}, secondo le condizioni verificate.", "Termini dell'offerta: spedizione gratuita, da verificare al pagamento.", "Codice: {code}", "Offerta automatica; il pagamento conferma l'applicazione finale.", "Idoneit\xE0 e prezzo finale sono confermati al pagamento.", "Visualizza prodotto: {url}", "Non ho potuto verificare un'offerta applicabile a questo suggerimento."],
|
|
83
|
+
ja: ["\u3053\u306E\u30B9\u30C8\u30A2\u306E\u5546\u54C1\u306B\u3064\u3044\u3066\u306F\u304A\u624B\u4F1D\u3044\u3067\u304D\u307E\u3059\u304C\u3001\u305D\u306E\u8CEA\u554F\u306B\u306F\u56DE\u7B54\u3067\u304D\u307E\u305B\u3093\u3002", "\u3054\u5E0C\u671B\u306E\u914D\u9001\u60C5\u5831\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u30B9\u30C8\u30A2\u306B\u3054\u78BA\u8A8D\u304F\u3060\u3055\u3044\u3002", "\u3053\u306E\u5546\u54C1\u306F\u914D\u9001\u304C\u5FC5\u8981\u3067\u3059\u3002", "\u3053\u306E\u5546\u54C1\u306F\u914D\u9001\u4E0D\u8981\u3067\u3059\u3002", "\u914D\u9001\u30DD\u30EA\u30B7\u30FC\uFF1A{url}", "\u30B9\u30C8\u30A2\uFF1A{url}", "{title}", "\u8CFC\u5165\u5019\u88DC\u6570 {quantity}\uFF1A", "\u3053\u306E\u5546\u54C1\u3092 {quantity} \u500B\u30AB\u30FC\u30C8\u306B\u8FFD\u52A0\u3059\u308B\u5834\u5408\uFF1A", "\u9078\u629E\u3057\u305F\u30AB\u30FC\u30C8\u5546\u54C1\u3092\u3053\u306E\u5546\u54C1 {quantity} \u500B\u306B\u7F6E\u304D\u63DB\u3048\u308B\u5834\u5408\uFF1A", "\u73FE\u5728\u306E\u8868\u793A\u4FA1\u683C\uFF1A{price}", "\u901A\u5E38\u306E\u8868\u793A\u4FA1\u683C\uFF1A{price}", "\u30AA\u30D5\u30A1\u30FC\u6761\u4EF6\uFF1A\u78BA\u8A8D\u6E08\u307F\u306E\u6761\u4EF6\u306B\u5F93\u3044 {percentage} \u5272\u5F15\u3002", "\u30AA\u30D5\u30A1\u30FC\u6761\u4EF6\uFF1A\u78BA\u8A8D\u6E08\u307F\u306E\u6761\u4EF6\u306B\u5F93\u3044 {amount} \u5272\u5F15\u3002", "\u30AA\u30D5\u30A1\u30FC\u6761\u4EF6\uFF1A\u9001\u6599\u7121\u6599\u3002\u9069\u7528\u306F\u30C1\u30A7\u30C3\u30AF\u30A2\u30A6\u30C8\u3067\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002", "\u30B3\u30FC\u30C9\uFF1A{code}", "\u81EA\u52D5\u30AA\u30D5\u30A1\u30FC\u3067\u3059\u3002\u6700\u7D42\u7684\u306A\u9069\u7528\u306F\u30C1\u30A7\u30C3\u30AF\u30A2\u30A6\u30C8\u3067\u78BA\u8A8D\u3055\u308C\u307E\u3059\u3002", "\u9069\u7528\u6761\u4EF6\u3068\u6700\u7D42\u4FA1\u683C\u306F\u30C1\u30A7\u30C3\u30AF\u30A2\u30A6\u30C8\u3067\u78BA\u8A8D\u3055\u308C\u307E\u3059\u3002", "\u5546\u54C1\u3092\u898B\u308B\uFF1A{url}", "\u3053\u306E\u63D0\u6848\u306B\u9069\u7528\u3067\u304D\u308B\u30AA\u30D5\u30A1\u30FC\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002"],
|
|
84
|
+
ko: ["\uC774 \uC2A4\uD1A0\uC5B4\uC758 \uC0C1\uD488\uC740 \uB3C4\uC640\uB4DC\uB9B4 \uC218 \uC788\uC9C0\uB9CC \uD574\uB2F9 \uC9C8\uBB38\uC5D0\uB294 \uB2F5\uBCC0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "\uC694\uCCAD\uD558\uC2E0 \uBC30\uC1A1 \uC815\uBCF4\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC2A4\uD1A0\uC5B4\uC5D0 \uBB38\uC758\uD574 \uC8FC\uC138\uC694.", "\uC774 \uC0C1\uD488\uC740 \uBC30\uC1A1\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", "\uC774 \uC0C1\uD488\uC740 \uBC30\uC1A1\uC774 \uD544\uC694\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "\uBC30\uC1A1 \uC815\uCC45: {url}", "\uC2A4\uD1A0\uC5B4: {url}", "{title}", "\uC81C\uC548 \uAD6C\uB9E4 \uC218\uB7C9 {quantity}\uAC1C:", "\uC774 \uC0C1\uD488 {quantity}\uAC1C\uB97C \uC7A5\uBC14\uAD6C\uB2C8\uC5D0 \uCD94\uAC00\uD558\uBA74:", "\uC120\uD0DD\uD55C \uC7A5\uBC14\uAD6C\uB2C8 \uC0C1\uD488\uC744 \uC774 \uC0C1\uD488 {quantity}\uAC1C\uB85C \uAD50\uCCB4\uD558\uBA74:", "\uD604\uC7AC \uD45C\uC2DC \uAC00\uACA9: {price}", "\uC815\uC0C1 \uD45C\uC2DC \uAC00\uACA9: {price}", "\uD61C\uD0DD \uC870\uAC74: \uD655\uC778\uB41C \uC870\uAC74\uC5D0 \uB530\uB77C {percentage} \uD560\uC778.", "\uD61C\uD0DD \uC870\uAC74: \uD655\uC778\uB41C \uC870\uAC74\uC5D0 \uB530\uB77C {amount} \uD560\uC778.", "\uD61C\uD0DD \uC870\uAC74: \uBB34\uB8CC \uBC30\uC1A1. \uACB0\uC81C \uB2E8\uACC4\uC5D0\uC11C \uD655\uC778\uB429\uB2C8\uB2E4.", "\uCF54\uB4DC: {code}", "\uC790\uB3D9 \uD61C\uD0DD\uC774\uBA70 \uCD5C\uC885 \uC801\uC6A9\uC740 \uACB0\uC81C \uB2E8\uACC4\uC5D0\uC11C \uD655\uC778\uB429\uB2C8\uB2E4.", "\uC790\uACA9\uACFC \uCD5C\uC885 \uAC00\uACA9\uC740 \uACB0\uC81C \uB2E8\uACC4\uC5D0\uC11C \uD655\uC778\uB429\uB2C8\uB2E4.", "\uC0C1\uD488 \uBCF4\uAE30: {url}", "\uC774 \uC81C\uC548\uC5D0 \uC801\uC6A9 \uAC00\uB2A5\uD55C \uD61C\uD0DD\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."],
|
|
85
|
+
nb: ["Jeg kan hjelpe med produktene i denne butikken, men ikke svare p\xE5 det sp\xF8rsm\xE5let.", "Jeg kan ikke bekrefte de \xF8nskede fraktopplysningene. Kontakt butikken.", "Denne varen m\xE5 sendes.", "Denne varen trenger ikke \xE5 sendes.", "Fraktvilk\xE5r: {url}", "Butikk: {url}", "{title}", "For et foresl\xE5tt kj\xF8p p\xE5 {quantity}:", "Hvis du legger {quantity} av denne varen i handlekurven:", "Hvis du erstatter den valgte handlekurvvaren med {quantity} av denne varen:", "Gjeldende oppf\xF8rt pris: {price}", "Ordin\xE6r oppf\xF8rt pris: {price}", "Tilbudsvilk\xE5r: {percentage} rabatt, med forbehold om bekreftede vilk\xE5r.", "Tilbudsvilk\xE5r: {amount} rabatt, med forbehold om bekreftede vilk\xE5r.", "Tilbudsvilk\xE5r: gratis frakt, bekreftes i kassen.", "Kode: {code}", "Automatisk tilbud; kassen bekrefter endelig bruk.", "Kvalifikasjon og sluttpris bekreftes i kassen.", "Vis produkt: {url}", "Jeg kunne ikke bekrefte et gjeldende tilbud for dette forslaget."],
|
|
86
|
+
pl: ["Mog\u0119 pom\xF3c w sprawie produkt\xF3w tego sklepu, ale nie mog\u0119 odpowiedzie\u0107 na to pytanie.", "Nie mog\u0119 potwierdzi\u0107 \u017C\u0105danych informacji o wysy\u0142ce. Sprawd\u017A je w sklepie.", "Ten produkt wymaga wysy\u0142ki.", "Ten produkt nie wymaga wysy\u0142ki.", "Zasady wysy\u0142ki: {url}", "Sklep: {url}", "{title}", "Dla proponowanego zakupu w liczbie {quantity}:", "Je\u015Bli dodasz {quantity} szt. tego produktu do koszyka:", "Je\u015Bli zast\u0105pisz wybrany produkt w koszyku {quantity} szt. tego produktu:", "Aktualna cena katalogowa: {price}", "Regularna cena katalogowa: {price}", "Warunki oferty: rabat {percentage}, zgodnie ze zweryfikowanymi warunkami.", "Warunki oferty: rabat {amount}, zgodnie ze zweryfikowanymi warunkami.", "Warunki oferty: bezp\u0142atna wysy\u0142ka, do potwierdzenia przy kasie.", "Kod: {code}", "Oferta automatyczna; kasa potwierdzi ostateczne zastosowanie.", "Uprawnienie i cena ko\u0144cowa zostan\u0105 potwierdzone przy kasie.", "Zobacz produkt: {url}", "Nie uda\u0142o si\u0119 potwierdzi\u0107 oferty dla tej propozycji."],
|
|
87
|
+
"pt-BR": ["Posso ajudar com os produtos desta loja, mas n\xE3o posso responder a essa pergunta.", "N\xE3o consigo confirmar os detalhes de envio solicitados. Consulte a loja.", "Este item precisa ser enviado.", "Este item n\xE3o precisa ser enviado.", "Pol\xEDtica de envio: {url}", "Loja: {url}", "{title}", "Para uma compra sugerida de {quantity}:", "Se voc\xEA adicionar {quantity} deste item \xE0 sua cesta:", "Se voc\xEA substituir o item selecionado da cesta por {quantity} deste item:", "Pre\xE7o listado atual: {price}", "Pre\xE7o listado normal: {price}", "Termos da oferta: desconto de {percentage}, sujeito \xE0s condi\xE7\xF5es verificadas.", "Termos da oferta: desconto de {amount}, sujeito \xE0s condi\xE7\xF5es verificadas.", "Termos da oferta: frete gr\xE1tis, sujeito \xE0 valida\xE7\xE3o no checkout.", "C\xF3digo: {code}", "Oferta autom\xE1tica; o checkout confirma a aplica\xE7\xE3o final.", "A elegibilidade e o pre\xE7o final s\xE3o confirmados no checkout.", "Ver produto: {url}", "N\xE3o foi poss\xEDvel verificar uma oferta aplic\xE1vel para esta sugest\xE3o."],
|
|
88
|
+
"pt-PT": ["Posso ajudar com os produtos desta loja, mas n\xE3o posso responder a essa pergunta.", "N\xE3o consigo confirmar os detalhes de entrega pedidos. Consulte a loja.", "Este artigo requer entrega.", "Este artigo n\xE3o requer entrega.", "Pol\xEDtica de entrega: {url}", "Loja: {url}", "{title}", "Para uma compra proposta de {quantity}:", "Se adicionar {quantity} deste artigo ao cesto:", "Se substituir o artigo selecionado no cesto por {quantity} deste artigo:", "Pre\xE7o atual anunciado: {price}", "Pre\xE7o normal anunciado: {price}", "Condi\xE7\xF5es da oferta: desconto de {percentage}, sujeito \xE0s condi\xE7\xF5es verificadas.", "Condi\xE7\xF5es da oferta: desconto de {amount}, sujeito \xE0s condi\xE7\xF5es verificadas.", "Condi\xE7\xF5es da oferta: entrega gratuita, sujeita a valida\xE7\xE3o no pagamento.", "C\xF3digo: {code}", "Oferta autom\xE1tica; o pagamento confirma a aplica\xE7\xE3o final.", "A elegibilidade e o pre\xE7o final s\xE3o confirmados no pagamento.", "Ver produto: {url}", "N\xE3o foi poss\xEDvel confirmar uma oferta aplic\xE1vel a esta sugest\xE3o."],
|
|
89
|
+
es: ["Puedo ayudarle con los productos de esta tienda, pero no puedo responder a esa pregunta.", "No puedo confirmar los detalles de env\xEDo solicitados. Consulte con la tienda.", "Este art\xEDculo requiere env\xEDo.", "Este art\xEDculo no requiere env\xEDo.", "Pol\xEDtica de env\xEDo: {url}", "Tienda: {url}", "{title}", "Para una compra propuesta de {quantity}:", "Si a\xF1ade {quantity} de este art\xEDculo a la cesta:", "Si sustituye el art\xEDculo seleccionado de la cesta por {quantity} de este art\xEDculo:", "Precio actual indicado: {price}", "Precio habitual indicado: {price}", "Condiciones de la oferta: descuento del {percentage}, sujeto a las condiciones verificadas.", "Condiciones de la oferta: descuento de {amount}, sujeto a las condiciones verificadas.", "Condiciones de la oferta: env\xEDo gratis, sujeto a validaci\xF3n al pagar.", "C\xF3digo: {code}", "Oferta autom\xE1tica; el pago confirma la aplicaci\xF3n final.", "La elegibilidad y el precio final se confirman al pagar.", "Ver producto: {url}", "No pude verificar una oferta aplicable a esta sugerencia."],
|
|
90
|
+
sv: ["Jag kan hj\xE4lpa till med butikens produkter, men jag kan inte svara p\xE5 den fr\xE5gan.", "Jag kan inte bekr\xE4fta de efterfr\xE5gade leveransuppgifterna. Fr\xE5ga butiken.", "Den h\xE4r varan m\xE5ste skickas.", "Den h\xE4r varan beh\xF6ver inte skickas.", "Leveranspolicy: {url}", "Butik: {url}", "{title}", "F\xF6r ett f\xF6reslaget k\xF6p av {quantity}:", "Om du l\xE4gger {quantity} av den h\xE4r varan i varukorgen:", "Om du ers\xE4tter den valda varukorgsvaran med {quantity} av den h\xE4r varan:", "Aktuellt listpris: {price}", "Ordinarie listpris: {price}", "Erbjudandevillkor: {percentage} rabatt, enligt verifierade villkor.", "Erbjudandevillkor: {amount} rabatt, enligt verifierade villkor.", "Erbjudandevillkor: fri frakt, bekr\xE4ftas i kassan.", "Kod: {code}", "Automatiskt erbjudande; kassan bekr\xE4ftar den slutliga till\xE4mpningen.", "Beh\xF6righet och slutpris bekr\xE4ftas i kassan.", "Visa produkt: {url}", "Jag kunde inte verifiera ett till\xE4mpligt erbjudande f\xF6r det h\xE4r f\xF6rslaget."],
|
|
91
|
+
th: ["\u0E09\u0E31\u0E19\u0E0A\u0E48\u0E27\u0E22\u0E41\u0E19\u0E30\u0E19\u0E33\u0E2A\u0E34\u0E19\u0E04\u0E49\u0E32\u0E43\u0E19\u0E23\u0E49\u0E32\u0E19\u0E19\u0E35\u0E49\u0E44\u0E14\u0E49 \u0E41\u0E15\u0E48\u0E44\u0E21\u0E48\u0E2A\u0E32\u0E21\u0E32\u0E23\u0E16\u0E15\u0E2D\u0E1A\u0E04\u0E33\u0E16\u0E32\u0E21\u0E19\u0E31\u0E49\u0E19\u0E44\u0E14\u0E49", "\u0E09\u0E31\u0E19\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E23\u0E32\u0E22\u0E25\u0E30\u0E40\u0E2D\u0E35\u0E22\u0E14\u0E01\u0E32\u0E23\u0E08\u0E31\u0E14\u0E2A\u0E48\u0E07\u0E15\u0E32\u0E21\u0E17\u0E35\u0E48\u0E02\u0E2D\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49 \u0E42\u0E1B\u0E23\u0E14\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E01\u0E31\u0E1A\u0E23\u0E49\u0E32\u0E19\u0E04\u0E49\u0E32", "\u0E2A\u0E34\u0E19\u0E04\u0E49\u0E32\u0E19\u0E35\u0E49\u0E15\u0E49\u0E2D\u0E07\u0E08\u0E31\u0E14\u0E2A\u0E48\u0E07", "\u0E2A\u0E34\u0E19\u0E04\u0E49\u0E32\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E15\u0E49\u0E2D\u0E07\u0E08\u0E31\u0E14\u0E2A\u0E48\u0E07", "\u0E19\u0E42\u0E22\u0E1A\u0E32\u0E22\u0E01\u0E32\u0E23\u0E08\u0E31\u0E14\u0E2A\u0E48\u0E07: {url}", "\u0E23\u0E49\u0E32\u0E19\u0E04\u0E49\u0E32: {url}", "{title}", "\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A\u0E02\u0E49\u0E2D\u0E40\u0E2A\u0E19\u0E2D\u0E0B\u0E37\u0E49\u0E2D\u0E08\u0E33\u0E19\u0E27\u0E19 {quantity}:", "\u0E2B\u0E32\u0E01\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E2A\u0E34\u0E19\u0E04\u0E49\u0E32\u0E19\u0E35\u0E49 {quantity} \u0E0A\u0E34\u0E49\u0E19\u0E25\u0E07\u0E43\u0E19\u0E15\u0E30\u0E01\u0E23\u0E49\u0E32:", "\u0E2B\u0E32\u0E01\u0E41\u0E17\u0E19\u0E17\u0E35\u0E48\u0E2A\u0E34\u0E19\u0E04\u0E49\u0E32\u0E43\u0E19\u0E15\u0E30\u0E01\u0E23\u0E49\u0E32\u0E17\u0E35\u0E48\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E14\u0E49\u0E27\u0E22\u0E2A\u0E34\u0E19\u0E04\u0E49\u0E32\u0E19\u0E35\u0E49 {quantity} \u0E0A\u0E34\u0E49\u0E19:", "\u0E23\u0E32\u0E04\u0E32\u0E1B\u0E31\u0E08\u0E08\u0E38\u0E1A\u0E31\u0E19\u0E17\u0E35\u0E48\u0E41\u0E2A\u0E14\u0E07: {price}", "\u0E23\u0E32\u0E04\u0E32\u0E1B\u0E01\u0E15\u0E34\u0E17\u0E35\u0E48\u0E41\u0E2A\u0E14\u0E07: {price}", "\u0E40\u0E07\u0E37\u0E48\u0E2D\u0E19\u0E44\u0E02\u0E02\u0E49\u0E2D\u0E40\u0E2A\u0E19\u0E2D: \u0E2A\u0E48\u0E27\u0E19\u0E25\u0E14 {percentage} \u0E15\u0E32\u0E21\u0E40\u0E07\u0E37\u0E48\u0E2D\u0E19\u0E44\u0E02\u0E17\u0E35\u0E48\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E41\u0E25\u0E49\u0E27", "\u0E40\u0E07\u0E37\u0E48\u0E2D\u0E19\u0E44\u0E02\u0E02\u0E49\u0E2D\u0E40\u0E2A\u0E19\u0E2D: \u0E2A\u0E48\u0E27\u0E19\u0E25\u0E14 {amount} \u0E15\u0E32\u0E21\u0E40\u0E07\u0E37\u0E48\u0E2D\u0E19\u0E44\u0E02\u0E17\u0E35\u0E48\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E41\u0E25\u0E49\u0E27", "\u0E40\u0E07\u0E37\u0E48\u0E2D\u0E19\u0E44\u0E02\u0E02\u0E49\u0E2D\u0E40\u0E2A\u0E19\u0E2D: \u0E08\u0E31\u0E14\u0E2A\u0E48\u0E07\u0E1F\u0E23\u0E35 \u0E42\u0E14\u0E22\u0E15\u0E49\u0E2D\u0E07\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E17\u0E35\u0E48\u0E2B\u0E19\u0E49\u0E32\u0E0A\u0E33\u0E23\u0E30\u0E40\u0E07\u0E34\u0E19", "\u0E23\u0E2B\u0E31\u0E2A: {code}", "\u0E02\u0E49\u0E2D\u0E40\u0E2A\u0E19\u0E2D\u0E2D\u0E31\u0E15\u0E42\u0E19\u0E21\u0E31\u0E15\u0E34 \u0E23\u0E30\u0E1A\u0E1A\u0E0A\u0E33\u0E23\u0E30\u0E40\u0E07\u0E34\u0E19\u0E08\u0E30\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E01\u0E32\u0E23\u0E43\u0E0A\u0E49\u0E02\u0E31\u0E49\u0E19\u0E2A\u0E38\u0E14\u0E17\u0E49\u0E32\u0E22", "\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E41\u0E25\u0E30\u0E23\u0E32\u0E04\u0E32\u0E2A\u0E38\u0E14\u0E17\u0E49\u0E32\u0E22\u0E17\u0E35\u0E48\u0E2B\u0E19\u0E49\u0E32\u0E0A\u0E33\u0E23\u0E30\u0E40\u0E07\u0E34\u0E19", "\u0E14\u0E39\u0E2A\u0E34\u0E19\u0E04\u0E49\u0E32: {url}", "\u0E09\u0E31\u0E19\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E02\u0E49\u0E2D\u0E40\u0E2A\u0E19\u0E2D\u0E17\u0E35\u0E48\u0E43\u0E0A\u0E49\u0E44\u0E14\u0E49\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A\u0E04\u0E33\u0E41\u0E19\u0E30\u0E19\u0E33\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49"],
|
|
92
|
+
tr: ["Bu ma\u011Fazan\u0131n \xFCr\xFCnleri konusunda yard\u0131mc\u0131 olabilirim ancak bu soruyu yan\u0131tlayamam.", "\u0130stedi\u011Finiz g\xF6nderim ayr\u0131nt\u0131lar\u0131n\u0131 do\u011Frulayam\u0131yorum. L\xFCtfen ma\u011Fazaya dan\u0131\u015F\u0131n.", "Bu \xFCr\xFCn\xFCn g\xF6nderilmesi gerekir.", "Bu \xFCr\xFCn\xFCn g\xF6nderilmesi gerekmez.", "G\xF6nderim politikas\u0131: {url}", "Ma\u011Faza: {url}", "{title}", "\xD6nerilen {quantity} adetlik sat\u0131n alma i\xE7in:", "Bu \xFCr\xFCnden {quantity} adet sepetinize eklerseniz:", "Se\xE7ili sepet \xFCr\xFCn\xFCn\xFC bu \xFCr\xFCnden {quantity} adetle de\u011Fi\u015Ftirirseniz:", "G\xFCncel liste fiyat\u0131: {price}", "Normal liste fiyat\u0131: {price}", "Teklif ko\u015Fullar\u0131: do\u011Frulanm\u0131\u015F ko\u015Fullara tabi {percentage} indirim.", "Teklif ko\u015Fullar\u0131: do\u011Frulanm\u0131\u015F ko\u015Fullara tabi {amount} indirim.", "Teklif ko\u015Fullar\u0131: \xFCcretsiz g\xF6nderim; \xF6deme s\u0131ras\u0131nda do\u011Frulan\u0131r.", "Kod: {code}", "Otomatik teklif; son uygulamay\u0131 \xF6deme ekran\u0131 do\u011Frular.", "Uygunluk ve son fiyat \xF6deme s\u0131ras\u0131nda do\u011Frulan\u0131r.", "\xDCr\xFCn\xFC g\xF6r\xFCnt\xFCle: {url}", "Bu \xF6neri i\xE7in ge\xE7erli bir teklifi do\u011Frulayamad\u0131m."]
|
|
93
|
+
};
|
|
94
|
+
var recommendationMessages = Object.fromEntries(MODA_SUPPORTED_LANGUAGE_TAGS.map((locale) => {
|
|
95
|
+
const values = [...rows[locale], ...productOptionMessages[locale]];
|
|
96
|
+
if (values.length !== recommendationMessageKeys.length) throw new Error(`INVALID_RECOMMENDATION_CATALOGUE:${locale}`);
|
|
97
|
+
return [locale, Object.freeze(Object.fromEntries(recommendationMessageKeys.map((key, index) => [key, values[index]])))];
|
|
98
|
+
}));
|
|
99
|
+
for (const locale of MODA_SUPPORTED_LANGUAGE_TAGS) validateIcuCatalogue(recommendationMessages[locale], recommendationMessageKeys, { locale });
|
|
100
|
+
function recommendationMessage(locale, key, values) {
|
|
101
|
+
return createInternationalizationRuntime({ locale, catalogue: recommendationMessages[locale] }).t(key, values);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/commerce/presentation/options.ts
|
|
105
|
+
import { z } from "zod";
|
|
106
|
+
var CommerceOptionFactSchema = z.strictObject({ productId: z.string().min(1).max(128), variantId: z.string().min(1).max(128).nullable(), title: z.string().min(1).max(512), options: CommerceProductOptionsSchema, selectedOptions: CommerceProductSelectedOptionsSchema });
|
|
107
|
+
function verifyCommerceOptionFacts(selection, currentProducts) {
|
|
108
|
+
const parsed = z.array(z.strictObject({ productId: z.string().min(1).max(128), variantId: z.string().min(1).max(128).nullable() })).max(3).safeParse(selection);
|
|
109
|
+
if (!parsed.success || new Set(selection.map((s) => JSON.stringify([s.productId, s.variantId]))).size !== selection.length) return { kind: "limitation", reason: "INVALID_SELECTION" };
|
|
110
|
+
const facts = [];
|
|
111
|
+
for (const selected of parsed.data) {
|
|
112
|
+
const matches = currentProducts.filter((p) => p && typeof p === "object" && p.productId === selected.productId && p.variantId === selected.variantId);
|
|
113
|
+
if (matches.length !== 1) return { kind: "limitation", reason: "PRODUCT_NOT_FOUND" };
|
|
114
|
+
const product2 = CommerceProductSchema.safeParse(matches[0]);
|
|
115
|
+
if (!product2.success) return { kind: "limitation", reason: "PRODUCT_INVALID" };
|
|
116
|
+
facts.push({ productId: product2.data.productId, variantId: product2.data.variantId, title: product2.data.title, options: product2.data.options ?? { status: "PARTIAL", items: [] }, selectedOptions: product2.data.selectedOptions ?? [] });
|
|
117
|
+
}
|
|
118
|
+
return { kind: "verified", optionFacts: facts };
|
|
119
|
+
}
|
|
120
|
+
function renderProductOptionFacts(facts, locale) {
|
|
121
|
+
const unavailable = recommendationMessage(locale, "optionsUnavailable"), partial = recommendationMessage(locale, "optionsPartial"), selected = recommendationMessage(locale, "optionsSelected");
|
|
122
|
+
if (!facts.length) return unavailable;
|
|
123
|
+
return facts.map((f) => [f.title, ...f.options.status === "PARTIAL" ? [partial] : [], ...f.options.items.length ? f.options.items.map((o) => `${o.name}: ${o.values.length ? o.values.join(", ") : unavailable}`) : [unavailable], ...f.selectedOptions.length ? [`${selected}:`, ...f.selectedOptions.map((o) => `${o.name}: ${o.value}`)] : []].join("\n")).join("\n\n");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/commerce/presentation/schemas.ts
|
|
127
|
+
var safeUrl = z2.string().max(2048).refine((value) => {
|
|
128
|
+
try {
|
|
129
|
+
const u = new URL(value);
|
|
130
|
+
return !/[\x00-\x1f\x7f]/.test(value) && !u.hostname.startsWith("[") && !/^[0-9.]+$/.test(u.hostname) && u.hostname !== "localhost" && !u.hostname.endsWith(".localhost") && u.protocol === "https:" && !u.username && !u.password && !u.port && !u.hash;
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
var product = z2.strictObject({ title: z2.string().min(1).max(512), url: safeUrl, quantity: z2.number().int().min(1).max(Number.MAX_SAFE_INTEGER), scope: z2.enum(["PRODUCT_ONLY", "ADD_TO_BASKET", "REPLACE_BASKET_LINE"]), currentPrice: z2.string().max(128).nullable(), currency: z2.string().regex(/^[A-Z]{3}$/).nullable(), regularPrice: z2.string().max(128).nullable().optional(), offerTerms: z2.array(z2.strictObject({ kind: z2.enum(["percent", "amount", "free-shipping"]), value: z2.string().max(128).optional(), currency: z2.string().regex(/^[A-Z]{3}$/).optional(), code: z2.string().max(128).nullable().optional(), automatic: z2.boolean().optional() })).max(32).optional() });
|
|
136
|
+
var CommerceVerifiedPresentationSchema = z2.strictObject({ schemaVersion: z2.literal(1), brandName: z2.string().min(1).max(512).nullable(), locale: ModaSupportedLanguageTagSchema, disposition: z2.enum(["STANDARD", "OUT_OF_SCOPE_REDIRECT", "SHIPPING_INFORMATION_UNVERIFIED", "PRODUCT_OPTIONS"]), storeUrl: safeUrl.refine((value) => !new URL(value).search), products: z2.array(product).max(3), optionFacts: z2.array(CommerceOptionFactSchema).max(3), requiresShipping: z2.boolean().nullable(), policyUrl: safeUrl.refine((value) => !new URL(value).search).nullable(), includeNoVerifiedOffer: z2.boolean() }).superRefine((v, c) => {
|
|
137
|
+
try {
|
|
138
|
+
const origin = new URL(v.storeUrl).origin;
|
|
139
|
+
if (v.products.some((p) => new URL(p.url).origin !== origin) || v.policyUrl && new URL(v.policyUrl).origin !== origin) c.addIssue({ code: "custom", message: "Presentation URL is outside current shop" });
|
|
140
|
+
} catch {
|
|
141
|
+
c.addIssue({ code: "custom", message: "Invalid presentation URL" });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// src/commerce/presentation/format.ts
|
|
146
|
+
function normalizeCommerceReplyText(value) {
|
|
147
|
+
const linked = value.replace(/\[([^\]\r\n]+)\]\((https:\/\/[^\s)]+)\)/g, (original, label, raw) => {
|
|
148
|
+
try {
|
|
149
|
+
const url = new URL(raw);
|
|
150
|
+
return url.protocol === "https:" && !url.username && !url.password ? `${label}: ${raw}` : original;
|
|
151
|
+
} catch {
|
|
152
|
+
return original;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const strip = (text, pattern) => text.replace(pattern, (original, inner, offset, source) => /https?:\/\/[^\s]*$/.test(source.slice(0, offset)) ? original : inner);
|
|
156
|
+
return strip(strip(linked, /\*\*([^*]+)\*\*/g), /~~([^~]+)~~/g);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/commerce/presentation/money.ts
|
|
160
|
+
var DECIMAL = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
|
|
161
|
+
function isValidDecimal(value) {
|
|
162
|
+
return DECIMAL.test(value);
|
|
163
|
+
}
|
|
164
|
+
function currencyExponent(currency) {
|
|
165
|
+
try {
|
|
166
|
+
const options = new Intl.NumberFormat("en", { style: "currency", currency }).resolvedOptions();
|
|
167
|
+
const exponent = options.maximumFractionDigits;
|
|
168
|
+
return typeof exponent === "number" && Number.isInteger(exponent) && exponent >= 0 && exponent <= 4 && options.minimumFractionDigits === exponent ? exponent : null;
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function minorUnitsToDecimal(value, currency) {
|
|
174
|
+
const exponent = currencyExponent(currency);
|
|
175
|
+
if (exponent === null || value < 0n) return null;
|
|
176
|
+
if (exponent === 0) return value.toString();
|
|
177
|
+
const padded = value.toString().padStart(exponent + 1, "0");
|
|
178
|
+
return `${padded.slice(0, -exponent)}.${padded.slice(-exponent)}`;
|
|
179
|
+
}
|
|
180
|
+
function minorUnits(value, exponent) {
|
|
181
|
+
if (!DECIMAL.test(value)) return null;
|
|
182
|
+
const [whole = "", fraction = ""] = value.split(".");
|
|
183
|
+
if (fraction.length > exponent) return null;
|
|
184
|
+
try {
|
|
185
|
+
return BigInt(`${whole}${fraction.padEnd(exponent, "0")}`);
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function decimalToMinorUnits(value, currency) {
|
|
191
|
+
const exponent = currencyExponent(currency);
|
|
192
|
+
return exponent === null ? null : minorUnits(value, exponent);
|
|
193
|
+
}
|
|
194
|
+
function sumPurchaseSubtotal(lines, currency) {
|
|
195
|
+
let total = 0n;
|
|
196
|
+
for (const line of lines) {
|
|
197
|
+
if (!Number.isSafeInteger(line.quantity) || line.quantity < 1) return null;
|
|
198
|
+
const amount = decimalToMinorUnits(line.amountDecimal, currency);
|
|
199
|
+
if (amount === null) return null;
|
|
200
|
+
total += amount * BigInt(line.quantity);
|
|
201
|
+
}
|
|
202
|
+
return total;
|
|
203
|
+
}
|
|
204
|
+
function formatObservedMoney(value, currency, runtime) {
|
|
205
|
+
const minor = decimalToMinorUnits(value, currency);
|
|
206
|
+
const exponent = currencyExponent(currency);
|
|
207
|
+
if (minor === null || exponent === null || minor > BigInt(Number.MAX_SAFE_INTEGER)) return null;
|
|
208
|
+
const numeric = Number(minor) / 10 ** exponent;
|
|
209
|
+
if (!Number.isFinite(numeric) || decimalToMinorUnits(numeric.toFixed(exponent), currency) !== minor) return null;
|
|
210
|
+
try {
|
|
211
|
+
return runtime.formatMoney(numeric, currency);
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/commerce/presentation/render.ts
|
|
218
|
+
function renderRecommendationReply(input) {
|
|
219
|
+
const t = (key, values) => recommendationMessage(input.locale, key, values);
|
|
220
|
+
const lines = [];
|
|
221
|
+
if (input.disposition === "OUT_OF_SCOPE_REDIRECT") lines.push(t("scopeLimit"));
|
|
222
|
+
if (input.disposition === "SHIPPING_INFORMATION_UNVERIFIED") lines.push(t("shippingUnverified"));
|
|
223
|
+
if (input.requiresShipping !== null && input.requiresShipping !== void 0) {
|
|
224
|
+
lines.push(t(input.requiresShipping ? "shippingRequired" : "shippingNotRequired"));
|
|
225
|
+
}
|
|
226
|
+
if (input.policyUrl) lines.push(t("policyLink", { url: input.policyUrl }));
|
|
227
|
+
for (const product2 of input.products ?? []) {
|
|
228
|
+
if (product2.scope === "ADD_TO_BASKET") lines.push(t("addProposal", { quantity: product2.quantity }));
|
|
229
|
+
else if (product2.scope === "REPLACE_BASKET_LINE") lines.push(t("replaceProposal", { quantity: product2.quantity }));
|
|
230
|
+
else lines.push(t("proposedQuantity", { quantity: product2.quantity }));
|
|
231
|
+
lines.push(t("productOption", { title: product2.title }));
|
|
232
|
+
if (product2.currentPrice && product2.currency) {
|
|
233
|
+
const price = formatObservedMoney(
|
|
234
|
+
product2.currentPrice,
|
|
235
|
+
product2.currency,
|
|
236
|
+
// Formatting is provided by the published Shared internationalization runtime.
|
|
237
|
+
awaitlessRuntime(input.locale)
|
|
238
|
+
);
|
|
239
|
+
if (price) lines.push(t("currentPrice", { price }));
|
|
240
|
+
}
|
|
241
|
+
if (product2.regularPrice && product2.currency && product2.currentPrice && product2.regularPrice !== product2.currentPrice) {
|
|
242
|
+
const regular = formatObservedMoney(product2.regularPrice, product2.currency, awaitlessRuntime(input.locale));
|
|
243
|
+
if (regular) lines.push(t("regularPrice", { price: regular }));
|
|
244
|
+
}
|
|
245
|
+
for (const offer of product2.offerTerms ?? []) {
|
|
246
|
+
if (offer.kind === "percent" && offer.value) {
|
|
247
|
+
const raw = Number(offer.value);
|
|
248
|
+
if (Number.isFinite(raw) && raw > 0 && raw <= 100) {
|
|
249
|
+
const percentage = awaitlessRuntime(input.locale).formatPercent(raw / 100);
|
|
250
|
+
lines.push(t("percentTerms", { percentage }));
|
|
251
|
+
}
|
|
252
|
+
} else if (offer.kind === "amount" && offer.value && offer.currency) {
|
|
253
|
+
const amount = formatObservedMoney(offer.value, offer.currency, awaitlessRuntime(input.locale));
|
|
254
|
+
if (amount) lines.push(t("amountTerms", { amount }));
|
|
255
|
+
} else if (offer.kind === "free-shipping") lines.push(t("freeShippingTerms"));
|
|
256
|
+
if (offer.code) lines.push(t("code", { code: offer.code }));
|
|
257
|
+
if (offer.automatic) lines.push(t("automatic"));
|
|
258
|
+
if (offer.kind !== "free-shipping") lines.push(t("checkoutQualification"));
|
|
259
|
+
}
|
|
260
|
+
lines.push(t("productLink", { url: product2.url }));
|
|
261
|
+
}
|
|
262
|
+
if (input.includeNoVerifiedOffer && (input.products?.length ?? 0) > 0) lines.push(t("noVerifiedOffer"));
|
|
263
|
+
if ((input.products?.length ?? 0) === 0 && !input.policyUrl && input.requiresShipping == null) {
|
|
264
|
+
lines.push(t("storeLink", { url: input.storeUrl }));
|
|
265
|
+
}
|
|
266
|
+
return lines.join("\n");
|
|
267
|
+
}
|
|
268
|
+
function awaitlessRuntime(locale) {
|
|
269
|
+
return createRuntime(locale);
|
|
270
|
+
}
|
|
271
|
+
function createRuntime(locale) {
|
|
272
|
+
return createInternationalizationRuntime({ locale, catalogue: recommendationMessages[locale] });
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/commerce/presentation/candidates.ts
|
|
276
|
+
function buildReplyCandidates(input) {
|
|
277
|
+
const candidates = [];
|
|
278
|
+
for (const body of [input.fullBody, input.firstProductBody ?? null, input.minimalBody]) {
|
|
279
|
+
if (typeof body !== "string" || !body.trim() || body.length > 4096 || candidates.includes(body)) continue;
|
|
280
|
+
candidates.push(body);
|
|
281
|
+
}
|
|
282
|
+
if (!candidates.includes(input.minimalBody) && input.minimalBody.trim() && input.minimalBody.length <= 4096) {
|
|
283
|
+
candidates.push(input.minimalBody);
|
|
284
|
+
}
|
|
285
|
+
return Object.freeze(candidates);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/commerce/presentation/shipping.ts
|
|
289
|
+
function safeAvailablePolicyUrl(source) {
|
|
290
|
+
if (source.policy.status !== "AVAILABLE" || !source.policy.url) return null;
|
|
291
|
+
try {
|
|
292
|
+
const url = new URL(source.policy.url);
|
|
293
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.port && !url.search && !url.hash ? source.policy.url : null;
|
|
294
|
+
} catch {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function selectShippingPresentation(input) {
|
|
299
|
+
const source = input.refreshed;
|
|
300
|
+
if (!source || source.product.productId !== input.selection.productId || source.product.variantId !== input.selection.variantId || source.provider !== input.observation?.provider) {
|
|
301
|
+
return Object.freeze({ requiresShipping: null, policyUrl: null, limitation: true });
|
|
302
|
+
}
|
|
303
|
+
const policyUrl = safeAvailablePolicyUrl(source);
|
|
304
|
+
return Object.freeze({
|
|
305
|
+
requiresShipping: source.product.status === "AVAILABLE" ? source.product.requiresShipping : null,
|
|
306
|
+
policyUrl,
|
|
307
|
+
limitation: source.product.status !== "AVAILABLE" || source.product.requiresShipping === null || source.policy.status !== "AVAILABLE" || source.policy.trust === "UNTRUSTED_REFERENCE"
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/commerce/presentation/wrapper-projection.ts
|
|
312
|
+
var VARIABLES = /* @__PURE__ */ new Set([
|
|
313
|
+
"customer.firstName",
|
|
314
|
+
"shop.name",
|
|
315
|
+
"checkout.url",
|
|
316
|
+
"shopBrand.brandName",
|
|
317
|
+
"shopBrand.logoUrl",
|
|
318
|
+
"shopBrand.logoAltText",
|
|
319
|
+
"shopBrand.logoWidth",
|
|
320
|
+
"shopBrand.logoHeight",
|
|
321
|
+
"shopBrand.squareLogoUrl",
|
|
322
|
+
"shopBrand.squareLogoAltText",
|
|
323
|
+
"shopBrand.coverImageUrl"
|
|
324
|
+
]);
|
|
325
|
+
var SLOT = "response.body";
|
|
326
|
+
var TOKEN_MARKER = /\{\{[^{}]*\}\}|\{\{|\}\}/;
|
|
327
|
+
function object(value) {
|
|
328
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
329
|
+
}
|
|
330
|
+
function onlyKeys(value, allowed) {
|
|
331
|
+
return Object.keys(value).every((key) => allowed.includes(key));
|
|
332
|
+
}
|
|
333
|
+
function stableJson(value) {
|
|
334
|
+
if (value === null || typeof value !== "object") {
|
|
335
|
+
const result = JSON.stringify(value);
|
|
336
|
+
if (result === void 0) throw new Error("Non-JSON prepared projection value");
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
340
|
+
return `{${Object.entries(value).filter(([, child]) => child !== void 0).sort(([a], [b]) => a.localeCompare(b, "en")).map(([key, child]) => `${JSON.stringify(key)}:${stableJson(child)}`).join(",")}}`;
|
|
341
|
+
}
|
|
342
|
+
function digest(value, sha256) {
|
|
343
|
+
return sha256(value);
|
|
344
|
+
}
|
|
345
|
+
function readTokens(value, where) {
|
|
346
|
+
if (!Array.isArray(value) || value.length > 80) return null;
|
|
347
|
+
const limit = where === "body" ? 4e3 : 1500;
|
|
348
|
+
let length = 0;
|
|
349
|
+
const result = [];
|
|
350
|
+
for (const node of value) {
|
|
351
|
+
if (!object(node)) return null;
|
|
352
|
+
if (node.kind === "text" && onlyKeys(node, ["kind", "value"]) && typeof node.value === "string" && !TOKEN_MARKER.test(node.value)) {
|
|
353
|
+
length += node.value.length;
|
|
354
|
+
result.push({ kind: "text", value: node.value });
|
|
355
|
+
} else if (node.kind === "variable" && onlyKeys(node, ["kind", "name"]) && typeof node.name === "string" && VARIABLES.has(node.name) && (node.name !== "checkout.url" || where === "conditional")) {
|
|
356
|
+
length += 5;
|
|
357
|
+
result.push({ kind: "variable", name: node.name });
|
|
358
|
+
} else if (node.kind === "responseSlot" && where === "body" && onlyKeys(node, ["kind", "name"]) && node.name === SLOT) {
|
|
359
|
+
length += 5;
|
|
360
|
+
result.push({ kind: "responseSlot", name: SLOT });
|
|
361
|
+
} else return null;
|
|
362
|
+
if (length > limit) return null;
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
function readPreparedWrapper(localeAst, contentHash, sha256) {
|
|
367
|
+
if (!object(localeAst) || localeAst.schemaVersion !== 1 || !object(localeAst.prepared)) {
|
|
368
|
+
return { kind: "missing" };
|
|
369
|
+
}
|
|
370
|
+
const prepared = localeAst.prepared;
|
|
371
|
+
if (!onlyKeys(prepared, ["schemaVersion", "family", "responseSlot", "header", "body", "footer", "conditionals"]) || prepared.schemaVersion !== 1 || prepared.family !== "MODA_CONVERSATION_WRAPPER" || prepared.responseSlot !== SLOT || !Array.isArray(prepared.conditionals) || prepared.conditionals.length > 1) return { kind: "invalid" };
|
|
372
|
+
const header = readTokens(prepared.header, "header");
|
|
373
|
+
const body = readTokens(prepared.body, "body");
|
|
374
|
+
const footer = readTokens(prepared.footer, "footer");
|
|
375
|
+
if (!header || !body || !footer || body.filter((part) => part.kind === "responseSlot").length !== 1) {
|
|
376
|
+
return { kind: "invalid" };
|
|
377
|
+
}
|
|
378
|
+
const conditionals = [];
|
|
379
|
+
for (const conditional of prepared.conditionals) {
|
|
380
|
+
if (!object(conditional) || !onlyKeys(conditional, ["when", "tokens"]) || conditional.when !== "checkout.available") return { kind: "invalid" };
|
|
381
|
+
const tokens = readTokens(conditional.tokens, "conditional");
|
|
382
|
+
if (!tokens) return { kind: "invalid" };
|
|
383
|
+
conditionals.push({ when: "checkout.available", tokens });
|
|
384
|
+
}
|
|
385
|
+
if (!contentHash || !/^[a-f0-9]{64}$/.test(contentHash) || digest(stableJson(prepared), sha256) !== contentHash) return { kind: "invalid" };
|
|
386
|
+
return { kind: "ok", prepared: {
|
|
387
|
+
schemaVersion: 1,
|
|
388
|
+
family: "MODA_CONVERSATION_WRAPPER",
|
|
389
|
+
responseSlot: SLOT,
|
|
390
|
+
header,
|
|
391
|
+
body,
|
|
392
|
+
footer,
|
|
393
|
+
conditionals
|
|
394
|
+
} };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/commerce/presentation/wrapper-render.ts
|
|
398
|
+
var MAX_WHATSAPP_TEXT_LENGTH = 4096;
|
|
399
|
+
var MAX_VARIABLE_LENGTH = 2048;
|
|
400
|
+
function hasDisallowedTextControl(value) {
|
|
401
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
402
|
+
const code = value.charCodeAt(index);
|
|
403
|
+
if (code < 32 && code !== 9 && code !== 10 && code !== 13) return true;
|
|
404
|
+
}
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
function valueFor(token, context) {
|
|
408
|
+
if (token.kind !== "variable") return null;
|
|
409
|
+
const value = context.variables[token.name];
|
|
410
|
+
if (typeof value !== "string" || !value.trim() || value.length > MAX_VARIABLE_LENGTH || hasDisallowedTextControl(value)) return null;
|
|
411
|
+
return value;
|
|
412
|
+
}
|
|
413
|
+
function renderTokens(tokens, context, response) {
|
|
414
|
+
const parts = [];
|
|
415
|
+
for (const token of tokens) {
|
|
416
|
+
if (token.kind === "text") parts.push(token.value);
|
|
417
|
+
else if (token.kind === "responseSlot") parts.push(response);
|
|
418
|
+
else {
|
|
419
|
+
const variable = valueFor(token, context);
|
|
420
|
+
if (variable === null) return null;
|
|
421
|
+
parts.push(variable);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return parts.join("");
|
|
425
|
+
}
|
|
426
|
+
function renderSections(prepared, context, response) {
|
|
427
|
+
const header = renderTokens(prepared.header, context, response);
|
|
428
|
+
const body = renderTokens(prepared.body, context, response);
|
|
429
|
+
const footer = renderTokens(prepared.footer, context, response);
|
|
430
|
+
if (header === null || body === null || footer === null) return null;
|
|
431
|
+
const parts = [header, body];
|
|
432
|
+
if (context.checkoutAvailable) {
|
|
433
|
+
for (const conditional of prepared.conditionals) {
|
|
434
|
+
const content = renderTokens(conditional.tokens, context, response);
|
|
435
|
+
if (content !== null) parts.push(content);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
parts.push(footer);
|
|
439
|
+
return parts.filter((part) => part.length > 0).join("\n");
|
|
440
|
+
}
|
|
441
|
+
function renderPreparedWrapper(input) {
|
|
442
|
+
const { prepared, context, responseBody } = input;
|
|
443
|
+
if (prepared.body.filter((token) => token.kind === "responseSlot").length !== 1 || [...prepared.header, ...prepared.footer, ...prepared.conditionals.flatMap((c) => c.tokens)].some((token) => token.kind === "responseSlot")) return { kind: "rejected", reason: "PREPARED_INVALID" };
|
|
444
|
+
if (typeof responseBody !== "string" || !responseBody.trim() || hasDisallowedTextControl(responseBody)) {
|
|
445
|
+
return { kind: "rejected", reason: "INVALID_INPUT" };
|
|
446
|
+
}
|
|
447
|
+
if (responseBody.length > MAX_WHATSAPP_TEXT_LENGTH) {
|
|
448
|
+
return { kind: "rejected", reason: "OUTPUT_TOO_LONG" };
|
|
449
|
+
}
|
|
450
|
+
const probe = "\0__MODA_RESPONSE_SLOT__\0";
|
|
451
|
+
const envelope = renderSections(prepared, context, probe);
|
|
452
|
+
if (envelope === null) return { kind: "rejected", reason: "VARIABLE_UNAVAILABLE" };
|
|
453
|
+
const position = envelope.indexOf(probe);
|
|
454
|
+
const prefix = envelope.slice(0, position);
|
|
455
|
+
const suffix = envelope.slice(position + probe.length);
|
|
456
|
+
if ((prefix || suffix) && responseBody.length > prefix.length + suffix.length && responseBody.startsWith(prefix) && responseBody.endsWith(suffix)) {
|
|
457
|
+
return { kind: "rejected", reason: "ALREADY_WRAPPED" };
|
|
458
|
+
}
|
|
459
|
+
const rendered = renderSections(prepared, context, responseBody);
|
|
460
|
+
if (rendered === null) return { kind: "rejected", reason: "VARIABLE_UNAVAILABLE" };
|
|
461
|
+
if (hasDisallowedTextControl(rendered)) return { kind: "rejected", reason: "PREPARED_INVALID" };
|
|
462
|
+
if (rendered.length > MAX_WHATSAPP_TEXT_LENGTH) {
|
|
463
|
+
return { kind: "rejected", reason: "OUTPUT_TOO_LONG" };
|
|
464
|
+
}
|
|
465
|
+
return { kind: "rendered", body: rendered };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/commerce/presentation/wrapper-selection.ts
|
|
469
|
+
function canonicalTag(value) {
|
|
470
|
+
if (!value?.trim()) return null;
|
|
471
|
+
return canonicaliseLanguageTag(value);
|
|
472
|
+
}
|
|
473
|
+
function chooseSlot(slots, source) {
|
|
474
|
+
const own = slots.filter((slot) => slot.audience === source);
|
|
475
|
+
const shared = slots.filter((slot) => slot.audience === "SHARED");
|
|
476
|
+
if (own.length > 1 || shared.length > 1 || slots.some((slot) => !["SHARED", "AI", "HUMAN"].includes(slot.audience))) return "invalid";
|
|
477
|
+
return own[0] ?? shared[0] ?? null;
|
|
478
|
+
}
|
|
479
|
+
function validDefinition(def, slot) {
|
|
480
|
+
return def.id === slot.definitionId && slot.definition.id === slot.definitionId && Number.isSafeInteger(slot.editVersion) && slot.editVersion > 0 && def.kind === "MODA_CONVERSATION_WRAPPER" && def.audience === slot.audience && ["SHARED", "AI", "HUMAN"].includes(slot.audience) && def.status === "PUBLISHED" && def.publishedAt instanceof Date && Number.isFinite(def.publishedAt.getTime()) && def.retiredAt === null && Boolean(def.createdByAdminId) && def.sourceLanguageTag === "en" && def.astSchemaVersion === 1 && /^[a-f0-9]{64}$/.test(def.sourceHash);
|
|
481
|
+
}
|
|
482
|
+
function languageChoices(input, onInvalid) {
|
|
483
|
+
let customer;
|
|
484
|
+
let merchant;
|
|
485
|
+
let caller;
|
|
486
|
+
let platform;
|
|
487
|
+
try {
|
|
488
|
+
customer = canonicalTag(input.conversationTag);
|
|
489
|
+
merchant = canonicalTag(input.merchantTag);
|
|
490
|
+
caller = canonicalTag(input.preferredTag);
|
|
491
|
+
platform = canonicalTag(input.platformFallbackTag);
|
|
492
|
+
} catch (error) {
|
|
493
|
+
onInvalid?.(error);
|
|
494
|
+
return "invalid";
|
|
495
|
+
}
|
|
496
|
+
if (input.conversationTag && !customer || input.merchantTag && !merchant || input.preferredTag && !caller || input.platformFallbackTag && !platform) return "invalid";
|
|
497
|
+
if (caller && customer && caller !== customer) return "invalid";
|
|
498
|
+
const choices = [];
|
|
499
|
+
const add = (tag) => {
|
|
500
|
+
if (tag && !choices.includes(tag)) choices.push(tag);
|
|
501
|
+
};
|
|
502
|
+
add(customer ?? caller);
|
|
503
|
+
const base = customer ?? caller;
|
|
504
|
+
if (base) {
|
|
505
|
+
try {
|
|
506
|
+
add(new Intl.Locale(base).language);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
onInvalid?.(error);
|
|
509
|
+
return "invalid";
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
add(merchant);
|
|
513
|
+
add(platform);
|
|
514
|
+
return choices;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/commerce/presentation/compose.ts
|
|
518
|
+
function candidatesFor(envelope, p) {
|
|
519
|
+
if (envelope.answerKind === "REFER_TO_STORE") return buildReplyCandidates({ fullBody: normalizeCommerceReplyText(envelope.replyText), minimalBody: normalizeCommerceReplyText(envelope.replyText) });
|
|
520
|
+
if (p.disposition === "PRODUCT_OPTIONS") return buildReplyCandidates({ fullBody: renderProductOptionFacts(p.optionFacts, p.locale), firstProductBody: p.optionFacts.length > 1 ? renderProductOptionFacts(p.optionFacts.slice(0, 1), p.locale) : null, minimalBody: renderProductOptionFacts([], p.locale) });
|
|
521
|
+
if (p.products.length || p.disposition !== "STANDARD" || p.requiresShipping !== null || p.policyUrl) {
|
|
522
|
+
const shared = { locale: p.locale, disposition: p.disposition, storeUrl: p.storeUrl, policyUrl: p.policyUrl, requiresShipping: p.requiresShipping };
|
|
523
|
+
return buildReplyCandidates({ fullBody: renderRecommendationReply({ ...shared, products: p.products, includeNoVerifiedOffer: p.includeNoVerifiedOffer }), firstProductBody: p.products.length ? renderRecommendationReply({ ...shared, products: p.products.slice(0, 1), includeNoVerifiedOffer: p.includeNoVerifiedOffer }) : null, minimalBody: renderRecommendationReply({ ...shared, products: [] }) });
|
|
524
|
+
}
|
|
525
|
+
const body = normalizeCommerceReplyText(envelope.replyText);
|
|
526
|
+
return buildReplyCandidates({ fullBody: body, minimalBody: body });
|
|
527
|
+
}
|
|
528
|
+
function composeCommerceWhatsAppPreview(input) {
|
|
529
|
+
try {
|
|
530
|
+
const envelope = CommerceFinalResponseSchema.parse(input.envelope), p = CommerceVerifiedPresentationSchema.parse(input.presentation);
|
|
531
|
+
if (input.locale !== void 0 && resolveModaConfigurationLocale(input.locale) !== p.locale || input.brandName !== void 0 && input.brandName !== p.brandName) return { kind: "refused", reason: "INVALID_INPUT" };
|
|
532
|
+
const candidates = candidatesFor(envelope, p);
|
|
533
|
+
if (candidates[0] && /[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(candidates[0])) return { kind: "refused", reason: "INVALID_INPUT" };
|
|
534
|
+
if (!candidates.length) return { kind: "refused", reason: "OUTPUT_TOO_LONG" };
|
|
535
|
+
const w = input.wrapper;
|
|
536
|
+
if (!w || !w.slots.length) {
|
|
537
|
+
const body = candidates[0];
|
|
538
|
+
if (/[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(body)) return { kind: "refused", reason: "INVALID_INPUT" };
|
|
539
|
+
return { kind: "ready", body, candidates, wrapper: null };
|
|
540
|
+
}
|
|
541
|
+
const slot = chooseSlot(w.slots, "AI");
|
|
542
|
+
if (!slot) return { kind: "ready", body: candidates[0], candidates, wrapper: null };
|
|
543
|
+
if (slot === "invalid" || !validDefinition(slot.definition, slot)) return { kind: "refused", reason: "DEFINITION_UNAVAILABLE" };
|
|
544
|
+
const choices = languageChoices({ conversationTag: w.context.conversationLanguageTag, merchantTag: w.context.merchantLanguageTag, preferredTag: w.languageTag, platformFallbackTag: w.platformFallbackLanguageTag ?? null });
|
|
545
|
+
if (choices === "invalid") return { kind: "refused", reason: "INVALID_LANGUAGE" };
|
|
546
|
+
const effectiveTag = canonicalTag(w.context.conversationLanguageTag), bodyLocale = resolveModaConfigurationLocale(effectiveTag);
|
|
547
|
+
if (bodyLocale !== p.locale) return { kind: "refused", reason: "INVALID_LANGUAGE" };
|
|
548
|
+
const language = new Intl.Locale(bodyLocale).language;
|
|
549
|
+
const lookup = [...new Set([effectiveTag, bodyLocale, ...choices].filter((tag) => tag !== null))].filter((tag) => new Intl.Locale(tag).language === language && resolveModaConfigurationLocale(tag) === bodyLocale);
|
|
550
|
+
const locale = lookup.map((tag) => slot.definition.locales.find((row) => row.languageTag === tag)).find((row) => row !== void 0);
|
|
551
|
+
if (!locale || locale.status !== (locale.languageTag === "en" ? "SOURCE_SAVED" : "REVIEWED") || locale.providerLanguageCode !== null || locale.languageTag !== "en" && (!locale.reviewedAt || !locale.reviewedByAdminId)) return { kind: "refused", reason: "LOCALE_UNAVAILABLE" };
|
|
552
|
+
const projection = readPreparedWrapper(locale.localeAst, locale.contentHash, w.digest);
|
|
553
|
+
if (projection.kind !== "ok") return { kind: "refused", reason: projection.kind === "missing" ? "PREPARED_MISSING" : "PREPARED_INVALID" };
|
|
554
|
+
for (const candidate of candidates) {
|
|
555
|
+
const rendered = renderPreparedWrapper({ prepared: projection.prepared, context: w.context, responseBody: candidate });
|
|
556
|
+
if (rendered.kind === "rendered") return { kind: "ready", body: rendered.body, candidates, wrapper: { definitionId: slot.definitionId, localeId: locale.id, contentHash: locale.contentHash, slotEditVersion: slot.editVersion } };
|
|
557
|
+
if (rendered.reason !== "OUTPUT_TOO_LONG") return { kind: "refused", reason: rendered.reason };
|
|
558
|
+
}
|
|
559
|
+
return { kind: "refused", reason: "OUTPUT_TOO_LONG" };
|
|
560
|
+
} catch {
|
|
561
|
+
return { kind: "refused", reason: "INVALID_INPUT" };
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
export {
|
|
565
|
+
CommerceOptionFactSchema,
|
|
566
|
+
CommerceVerifiedPresentationSchema,
|
|
567
|
+
MAX_WHATSAPP_TEXT_LENGTH,
|
|
568
|
+
buildReplyCandidates,
|
|
569
|
+
canonicalTag,
|
|
570
|
+
chooseSlot,
|
|
571
|
+
composeCommerceWhatsAppPreview,
|
|
572
|
+
currencyExponent,
|
|
573
|
+
decimalToMinorUnits,
|
|
574
|
+
digest,
|
|
575
|
+
formatObservedMoney,
|
|
576
|
+
isValidDecimal,
|
|
577
|
+
languageChoices,
|
|
578
|
+
minorUnitsToDecimal,
|
|
579
|
+
normalizeCommerceReplyText,
|
|
580
|
+
productOptionMessages,
|
|
581
|
+
readPreparedWrapper,
|
|
582
|
+
recommendationMessage,
|
|
583
|
+
recommendationMessageKeys,
|
|
584
|
+
recommendationMessages,
|
|
585
|
+
renderPreparedWrapper,
|
|
586
|
+
renderProductOptionFacts,
|
|
587
|
+
renderRecommendationReply,
|
|
588
|
+
selectShippingPresentation,
|
|
589
|
+
stableJson,
|
|
590
|
+
sumPurchaseSubtotal,
|
|
591
|
+
validDefinition,
|
|
592
|
+
verifyCommerceOptionFacts
|
|
593
|
+
};
|
|
594
|
+
//# sourceMappingURL=index.js.map
|