@talqui-oss/template-builder 0.1.3 → 0.1.4

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/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ EDITOR_LABELS: () => EDITOR_LABELS,
23
24
  TemplateValidationError: () => TemplateValidationError,
24
25
  UnsupportedByProviderError: () => UnsupportedByProviderError,
25
26
  applyEdit: () => applyEdit,
@@ -1341,43 +1342,93 @@ var ICONS = {
1341
1342
  };
1342
1343
 
1343
1344
  // src/editor/model.ts
1345
+ var EDITOR_LABELS = {
1346
+ basics: "Basics",
1347
+ name: "Name",
1348
+ nameHint: "lowercase, numbers and _",
1349
+ language: "Language",
1350
+ category: "Category",
1351
+ utility: "Utility",
1352
+ marketing: "Marketing",
1353
+ authentication: "Authentication",
1354
+ content: "Content",
1355
+ header: "Header",
1356
+ headerNone: "None",
1357
+ headerText: "Text",
1358
+ headerImage: "Image",
1359
+ headerVideo: "Video",
1360
+ headerDocument: "Document",
1361
+ headerTextField: "Header text",
1362
+ sampleFileUrl: "Sample file URL",
1363
+ body: "Body",
1364
+ footer: "Footer",
1365
+ formattingNote: "Use {{name}} for variables and *bold*, _italic_, ~strike~ for formatting.",
1366
+ buttons: "Buttons",
1367
+ buttonType: "Type",
1368
+ quickReply: "Quick reply",
1369
+ link: "Link",
1370
+ phone: "Phone",
1371
+ copyCode: "Copy code",
1372
+ buttonText: "Button text",
1373
+ url: "URL",
1374
+ phoneNumber: "Phone number",
1375
+ sampleCode: "Sample code",
1376
+ removeButton: "Remove button",
1377
+ addButton: "Add button",
1378
+ variables: "Variables",
1379
+ /** Followed by the variable's number, e.g. "position 1". */
1380
+ variablePosition: "position",
1381
+ exampleValue: "Example value",
1382
+ noVariables: "Type a placeholder such as {{first_name}} in the text to add one.",
1383
+ codeMessage: "Code message",
1384
+ securityRecommendation: "Add \u201Cdo not share this code\u201D",
1385
+ expiresAfter: "Expires after",
1386
+ expiresHint: "minutes, 1 to 90",
1387
+ optional: "optional",
1388
+ /** Default text of new buttons. */
1389
+ newQuickReply: "Reply",
1390
+ newLinkButton: "Open link",
1391
+ newPhoneButton: "Call us"
1392
+ };
1344
1393
  var LANGUAGES = [
1345
1394
  { value: "en_US", label: "English (US)" },
1346
1395
  { value: "pt_BR", label: "Portuguese (BR)" },
1347
1396
  { value: "es", label: "Spanish" }
1348
1397
  ];
1349
- var CATEGORIES2 = [
1350
- { value: "UTILITY", label: "Utility" },
1351
- { value: "MARKETING", label: "Marketing" },
1352
- { value: "AUTHENTICATION", label: "Authentication" }
1398
+ var categories = (l) => [
1399
+ { value: "UTILITY", label: l.utility },
1400
+ { value: "MARKETING", label: l.marketing },
1401
+ { value: "AUTHENTICATION", label: l.authentication }
1353
1402
  ];
1354
- var HEADER_KINDS = [
1355
- { value: "NONE", label: "None" },
1356
- { value: "TEXT", label: "Text" },
1357
- { value: "IMAGE", label: "Image" },
1358
- { value: "VIDEO", label: "Video" },
1359
- { value: "DOCUMENT", label: "Document" }
1403
+ var headerKinds = (l) => [
1404
+ { value: "NONE", label: l.headerNone },
1405
+ { value: "TEXT", label: l.headerText },
1406
+ { value: "IMAGE", label: l.headerImage },
1407
+ { value: "VIDEO", label: l.headerVideo },
1408
+ { value: "DOCUMENT", label: l.headerDocument }
1360
1409
  ];
1361
- var BUTTON_TYPES = [
1362
- { value: "QUICK_REPLY", label: "Quick reply" },
1363
- { value: "URL", label: "Link" },
1364
- { value: "PHONE_NUMBER", label: "Phone" },
1365
- { value: "COPY_CODE", label: "Copy code" }
1410
+ var BUTTON_TYPES = ["QUICK_REPLY", "URL", "PHONE_NUMBER", "COPY_CODE"];
1411
+ var buttonTypes = (l) => [
1412
+ { value: "QUICK_REPLY", label: l.quickReply },
1413
+ { value: "URL", label: l.link },
1414
+ { value: "PHONE_NUMBER", label: l.phone },
1415
+ { value: "COPY_CODE", label: l.copyCode }
1366
1416
  ];
1367
1417
  var MAX_BUTTONS = 10;
1368
1418
  var count = (text, max) => `${[...text].length}/${max}`;
1369
1419
  function editorSections(template, options = {}) {
1420
+ const l = { ...EDITOR_LABELS, ...options.labels };
1370
1421
  const basics = {
1371
1422
  id: "basics",
1372
- title: "Basics",
1423
+ title: l.basics,
1373
1424
  rows: [
1374
1425
  {
1375
1426
  fields: [
1376
1427
  {
1377
1428
  kind: "text",
1378
1429
  id: "name",
1379
- label: "Name",
1380
- hint: "lowercase, numbers and _",
1430
+ label: l.name,
1431
+ hint: l.nameHint,
1381
1432
  value: template.name,
1382
1433
  mono: true
1383
1434
  }
@@ -1388,47 +1439,48 @@ function editorSections(template, options = {}) {
1388
1439
  {
1389
1440
  kind: "select",
1390
1441
  id: "language",
1391
- label: "Language",
1442
+ label: l.language,
1392
1443
  value: template.language,
1393
1444
  options: options.languages ?? LANGUAGES
1394
1445
  },
1395
1446
  {
1396
1447
  kind: "segment",
1397
1448
  id: "category",
1398
- label: "Category",
1449
+ label: l.category,
1399
1450
  value: template.category,
1400
- options: CATEGORIES2
1451
+ options: categories(l)
1401
1452
  }
1402
1453
  ]
1403
1454
  }
1404
1455
  ]
1405
1456
  };
1406
1457
  if (template.category === "AUTHENTICATION")
1407
- return [basics, codeSection(template)];
1458
+ return [basics, codeSection(template, l)];
1408
1459
  return [
1409
1460
  basics,
1410
- contentSection(template),
1411
- buttonsSection(template),
1412
- variablesSection(template)
1461
+ contentSection(template, l),
1462
+ buttonsSection(template, l),
1463
+ variablesSection(template, l)
1413
1464
  ];
1414
1465
  }
1415
- function contentSection(template) {
1466
+ function contentSection(template, l) {
1416
1467
  const header2 = template.header;
1417
- const kind = HEADER_KINDS.some((option) => option.value === header2?.type) ? header2.type : "NONE";
1468
+ const kinds = headerKinds(l);
1469
+ const kind = kinds.some((option) => option.value === header2?.type) ? header2.type : "NONE";
1418
1470
  const headerRow = [
1419
1471
  {
1420
1472
  kind: "select",
1421
1473
  id: "header.kind",
1422
- label: "Header",
1474
+ label: l.header,
1423
1475
  value: kind,
1424
- options: HEADER_KINDS
1476
+ options: kinds
1425
1477
  }
1426
1478
  ];
1427
1479
  if (header2?.type === "TEXT")
1428
1480
  headerRow.push({
1429
1481
  kind: "text",
1430
1482
  id: "header.text",
1431
- label: "Header text",
1483
+ label: l.headerTextField,
1432
1484
  hint: count(header2.text, 60),
1433
1485
  value: header2.text
1434
1486
  });
@@ -1436,13 +1488,13 @@ function contentSection(template) {
1436
1488
  headerRow.push({
1437
1489
  kind: "text",
1438
1490
  id: "header.url",
1439
- label: "Sample file URL",
1491
+ label: l.sampleFileUrl,
1440
1492
  value: header2.url,
1441
1493
  mono: true
1442
1494
  });
1443
1495
  return {
1444
1496
  id: "content",
1445
- title: "Content",
1497
+ title: l.content,
1446
1498
  rows: [
1447
1499
  { fields: headerRow },
1448
1500
  {
@@ -1450,7 +1502,7 @@ function contentSection(template) {
1450
1502
  {
1451
1503
  kind: "textarea",
1452
1504
  id: "body",
1453
- label: "Body",
1505
+ label: l.body,
1454
1506
  hint: count(template.body.text, 1024),
1455
1507
  value: template.body.text
1456
1508
  }
@@ -1461,35 +1513,35 @@ function contentSection(template) {
1461
1513
  {
1462
1514
  kind: "text",
1463
1515
  id: "footer",
1464
- label: "Footer",
1516
+ label: l.footer,
1465
1517
  hint: count(template.footer?.text ?? "", 60),
1466
1518
  value: template.footer?.text ?? ""
1467
1519
  }
1468
1520
  ]
1469
1521
  }
1470
1522
  ],
1471
- note: "Use {{name}} for variables and *bold*, _italic_, ~strike~ for formatting."
1523
+ note: l.formattingNote
1472
1524
  };
1473
1525
  }
1474
- function buttonsSection(template) {
1526
+ function buttonsSection(template, l) {
1475
1527
  const buttons = template.buttons ?? [];
1476
1528
  const rows = buttons.map((button2, index) => {
1477
1529
  const at = `buttons.${index}`;
1478
- const type = BUTTON_TYPES.some((option) => option.value === button2.type) ? button2.type : "QUICK_REPLY";
1530
+ const type = BUTTON_TYPES.includes(button2.type) ? button2.type : "QUICK_REPLY";
1479
1531
  const fields = [
1480
1532
  {
1481
1533
  kind: "select",
1482
1534
  id: `${at}.type`,
1483
- label: "Type",
1535
+ label: l.buttonType,
1484
1536
  value: type,
1485
- options: BUTTON_TYPES
1537
+ options: buttonTypes(l)
1486
1538
  }
1487
1539
  ];
1488
1540
  if ("text" in button2 && button2.text !== void 0)
1489
1541
  fields.push({
1490
1542
  kind: "text",
1491
1543
  id: `${at}.text`,
1492
- label: "Button text",
1544
+ label: l.buttonText,
1493
1545
  hint: count(button2.text, 25),
1494
1546
  value: button2.text
1495
1547
  });
@@ -1497,7 +1549,7 @@ function buttonsSection(template) {
1497
1549
  fields.push({
1498
1550
  kind: "text",
1499
1551
  id: `${at}.url`,
1500
- label: "URL",
1552
+ label: l.url,
1501
1553
  value: button2.url,
1502
1554
  mono: true
1503
1555
  });
@@ -1505,7 +1557,7 @@ function buttonsSection(template) {
1505
1557
  fields.push({
1506
1558
  kind: "text",
1507
1559
  id: `${at}.phoneNumber`,
1508
- label: "Phone number",
1560
+ label: l.phoneNumber,
1509
1561
  value: button2.phoneNumber,
1510
1562
  mono: true
1511
1563
  });
@@ -1513,14 +1565,14 @@ function buttonsSection(template) {
1513
1565
  fields.push({
1514
1566
  kind: "text",
1515
1567
  id: `${at}.example`,
1516
- label: "Sample code",
1568
+ label: l.sampleCode,
1517
1569
  value: button2.example,
1518
1570
  mono: true
1519
1571
  });
1520
1572
  fields.push({
1521
1573
  kind: "action",
1522
1574
  id: `${at}.remove`,
1523
- label: "Remove button",
1575
+ label: l.removeButton,
1524
1576
  icon: "remove"
1525
1577
  });
1526
1578
  return { fields, list: true };
@@ -1530,7 +1582,7 @@ function buttonsSection(template) {
1530
1582
  {
1531
1583
  kind: "action",
1532
1584
  id: "buttons.add",
1533
- label: "Add button",
1585
+ label: l.addButton,
1534
1586
  icon: "add",
1535
1587
  disabled: buttons.length >= MAX_BUTTONS
1536
1588
  }
@@ -1538,42 +1590,42 @@ function buttonsSection(template) {
1538
1590
  });
1539
1591
  return {
1540
1592
  id: "buttons",
1541
- title: `Buttons ${buttons.length}/${MAX_BUTTONS}`,
1593
+ title: `${l.buttons} ${buttons.length}/${MAX_BUTTONS}`,
1542
1594
  rows
1543
1595
  };
1544
1596
  }
1545
- function variablesSection(template) {
1597
+ function variablesSection(template, l) {
1546
1598
  const variables = Object.entries(template.variables ?? {});
1547
1599
  return {
1548
1600
  id: "variables",
1549
- title: "Variables",
1601
+ title: l.variables,
1550
1602
  rows: variables.map(([name, variable], index) => ({
1551
1603
  fields: [
1552
1604
  {
1553
1605
  kind: "text",
1554
1606
  id: `variables.${name}`,
1555
1607
  label: `{{${name}}}`,
1556
- hint: `position ${index + 1}`,
1608
+ hint: `${l.variablePosition} ${index + 1}`,
1557
1609
  value: variable.example,
1558
- placeholder: "Example value"
1610
+ placeholder: l.exampleValue
1559
1611
  }
1560
1612
  ]
1561
1613
  })),
1562
- note: variables.length ? void 0 : "Type a placeholder such as {{first_name}} in the text to add one."
1614
+ note: variables.length ? void 0 : l.noVariables
1563
1615
  };
1564
1616
  }
1565
- function codeSection(template) {
1617
+ function codeSection(template, l) {
1566
1618
  const minutes = template.footer?.codeExpirationMinutes;
1567
1619
  return {
1568
1620
  id: "code",
1569
- title: "Code message",
1621
+ title: l.codeMessage,
1570
1622
  rows: [
1571
1623
  {
1572
1624
  fields: [
1573
1625
  {
1574
1626
  kind: "checkbox",
1575
1627
  id: "body.addSecurityRecommendation",
1576
- label: "Add \u201Cdo not share this code\u201D",
1628
+ label: l.securityRecommendation,
1577
1629
  checked: template.body?.addSecurityRecommendation ?? false
1578
1630
  }
1579
1631
  ]
@@ -1583,25 +1635,25 @@ function codeSection(template) {
1583
1635
  {
1584
1636
  kind: "text",
1585
1637
  id: "footer.codeExpirationMinutes",
1586
- label: "Expires after",
1587
- hint: "minutes, 1 to 90",
1638
+ label: l.expiresAfter,
1639
+ hint: l.expiresHint,
1588
1640
  value: minutes === void 0 ? "" : String(minutes),
1589
1641
  mono: true
1590
1642
  },
1591
1643
  {
1592
1644
  kind: "text",
1593
1645
  id: "button.text",
1594
- label: "Button text",
1595
- hint: "optional",
1646
+ label: l.buttonText,
1647
+ hint: l.optional,
1596
1648
  value: template.button.text ?? "",
1597
- placeholder: "Copy code"
1649
+ placeholder: l.copyCode
1598
1650
  }
1599
1651
  ]
1600
1652
  }
1601
1653
  ]
1602
1654
  };
1603
1655
  }
1604
- function applyEdit(template, id, value, remembered = {}) {
1656
+ function applyEdit(template, id, value, remembered = {}, labels = {}) {
1605
1657
  const text = typeof value === "string" ? value : "";
1606
1658
  if (id === "name") return { ...template, name: text };
1607
1659
  if (id === "language") return { ...template, language: text };
@@ -1634,9 +1686,12 @@ function applyEdit(template, id, value, remembered = {}) {
1634
1686
  [id.slice("variables.".length)]: { example: text }
1635
1687
  }
1636
1688
  };
1637
- return syncVariables(editStandard(template, id, text), remembered);
1689
+ return syncVariables(
1690
+ editStandard(template, id, text, { ...EDITOR_LABELS, ...labels }),
1691
+ remembered
1692
+ );
1638
1693
  }
1639
- function editStandard(template, id, text) {
1694
+ function editStandard(template, id, text, l) {
1640
1695
  if (id === "header.kind") return { ...template, header: newHeader(text) };
1641
1696
  if (id === "header.text")
1642
1697
  return { ...template, header: { type: "TEXT", text } };
@@ -1651,7 +1706,7 @@ function editStandard(template, id, text) {
1651
1706
  buttons: next2.length ? next2 : void 0
1652
1707
  });
1653
1708
  if (id === "buttons.add")
1654
- return buttons.length >= MAX_BUTTONS ? template : withButtons([...buttons, newButton("QUICK_REPLY")]);
1709
+ return buttons.length >= MAX_BUTTONS ? template : withButtons([...buttons, newButton(l, "QUICK_REPLY")]);
1655
1710
  const match = /^buttons\.(\d+)\.(\w+)$/.exec(id);
1656
1711
  if (!match) return template;
1657
1712
  const index = Number(match[1]);
@@ -1661,6 +1716,7 @@ function editStandard(template, id, text) {
1661
1716
  if (field === "remove")
1662
1717
  return withButtons(buttons.filter((_, i) => i !== index));
1663
1718
  const next = field === "type" ? newButton(
1719
+ l,
1664
1720
  text,
1665
1721
  "text" in button2 ? button2.text ?? "" : ""
1666
1722
  ) : { ...button2, [field]: text };
@@ -1702,13 +1758,13 @@ function newHeader(kind) {
1702
1758
  return { type: kind, url: "https://" };
1703
1759
  return void 0;
1704
1760
  }
1705
- function newButton(type, label = "") {
1761
+ function newButton(l, type, label = "") {
1706
1762
  if (type === "URL")
1707
- return { type, text: label || "Open link", url: "https://" };
1763
+ return { type, text: label || l.newLinkButton, url: "https://" };
1708
1764
  if (type === "PHONE_NUMBER")
1709
- return { type, text: label || "Call us", phoneNumber: "+" };
1765
+ return { type, text: label || l.newPhoneButton, phoneNumber: "+" };
1710
1766
  if (type === "COPY_CODE") return { type, example: "CODE10" };
1711
- return { type: "QUICK_REPLY", text: label || "Reply" };
1767
+ return { type: "QUICK_REPLY", text: label || l.newQuickReply };
1712
1768
  }
1713
1769
 
1714
1770
  // src/editor/tree.ts
@@ -1837,6 +1893,7 @@ function node2(tag, className, attrs, children = []) {
1837
1893
  }
1838
1894
  // Annotate the CommonJS export names for ESM import in node:
1839
1895
  0 && (module.exports = {
1896
+ EDITOR_LABELS,
1840
1897
  TemplateValidationError,
1841
1898
  UnsupportedByProviderError,
1842
1899
  applyEdit,
package/dist/index.d.cts CHANGED
@@ -277,9 +277,62 @@ type EditorSection = {
277
277
  /** Help text under the section, e.g. the formatting syntax. */
278
278
  note?: string;
279
279
  };
280
+ /** Every text the editor shows, so it can be translated. */
281
+ type EditorLabels = typeof EDITOR_LABELS;
282
+ /** The editor texts in English, the default. */
283
+ declare const EDITOR_LABELS: {
284
+ basics: string;
285
+ name: string;
286
+ nameHint: string;
287
+ language: string;
288
+ category: string;
289
+ utility: string;
290
+ marketing: string;
291
+ authentication: string;
292
+ content: string;
293
+ header: string;
294
+ headerNone: string;
295
+ headerText: string;
296
+ headerImage: string;
297
+ headerVideo: string;
298
+ headerDocument: string;
299
+ headerTextField: string;
300
+ sampleFileUrl: string;
301
+ body: string;
302
+ footer: string;
303
+ formattingNote: string;
304
+ buttons: string;
305
+ buttonType: string;
306
+ quickReply: string;
307
+ link: string;
308
+ phone: string;
309
+ copyCode: string;
310
+ buttonText: string;
311
+ url: string;
312
+ phoneNumber: string;
313
+ sampleCode: string;
314
+ removeButton: string;
315
+ addButton: string;
316
+ variables: string;
317
+ /** Followed by the variable's number, e.g. "position 1". */
318
+ variablePosition: string;
319
+ exampleValue: string;
320
+ noVariables: string;
321
+ codeMessage: string;
322
+ securityRecommendation: string;
323
+ expiresAfter: string;
324
+ expiresHint: string;
325
+ optional: string;
326
+ /** Default text of new buttons. */
327
+ newQuickReply: string;
328
+ newLinkButton: string;
329
+ newPhoneButton: string;
330
+ };
280
331
  type EditorOptions = {
281
332
  /** Languages offered in the language select. Default: English (US), Portuguese (BR), Spanish. */
282
333
  languages?: EditorOption[];
334
+ /** Texts to replace, e.g. to show the editor in another language. Default: English. */
335
+ labels?: Partial<EditorLabels>;
283
336
  };
284
337
  /** The sections of the editor for a template. */
285
338
  declare function editorSections(template: Template, options?: EditorOptions): EditorSection[];
@@ -290,8 +343,10 @@ declare function editorSections(template: Template, options?: EditorOptions): Ed
290
343
  *
291
344
  * `remembered` holds examples of variables seen earlier: while someone types `{{name}}` the
292
345
  * placeholder is briefly incomplete, and its example comes back from here once it is whole.
346
+ *
347
+ * `labels` gives new buttons their default text in the editor's language.
293
348
  */
294
- declare function applyEdit(template: Template, id: string, value?: string | boolean, remembered?: Variables): Template;
349
+ declare function applyEdit(template: Template, id: string, value?: string | boolean, remembered?: Variables, labels?: Partial<EditorLabels>): Template;
295
350
  /**
296
351
  * Switches category. Authentication has its own shape, so moving in or out of it keeps only
297
352
  * the name and language.
@@ -311,5 +366,5 @@ declare function editorTree(template: Template, options?: EditorOptions & {
311
366
  idPrefix?: string;
312
367
  }): PreviewNode;
313
368
 
314
- export { Adapter, Category, HttpRequest, StandardTemplate, Template, TemplateValidationError, Variables, applyEdit, changeCategory, codes, editorSections, editorTree, extractVariables, previewTree, syncVariables, toCurl, toPreviewModel, toRequests, validate };
315
- export type { ButtonIcon, Code, EditorField, EditorOption, EditorOptions, EditorRow, EditorSection, Issue, Line, Platform, PreviewButton, PreviewCard, PreviewHeader, PreviewModel, PreviewNode, PreviewOptions, Severity, Span, TreeOptions, ValidateOptions, ValidationResult };
369
+ export { Adapter, Category, EDITOR_LABELS, HttpRequest, StandardTemplate, Template, TemplateValidationError, Variables, applyEdit, changeCategory, codes, editorSections, editorTree, extractVariables, previewTree, syncVariables, toCurl, toPreviewModel, toRequests, validate };
370
+ export type { ButtonIcon, Code, EditorField, EditorLabels, EditorOption, EditorOptions, EditorRow, EditorSection, Issue, Line, Platform, PreviewButton, PreviewCard, PreviewHeader, PreviewModel, PreviewNode, PreviewOptions, Severity, Span, TreeOptions, ValidateOptions, ValidationResult };
package/dist/index.d.ts CHANGED
@@ -277,9 +277,62 @@ type EditorSection = {
277
277
  /** Help text under the section, e.g. the formatting syntax. */
278
278
  note?: string;
279
279
  };
280
+ /** Every text the editor shows, so it can be translated. */
281
+ type EditorLabels = typeof EDITOR_LABELS;
282
+ /** The editor texts in English, the default. */
283
+ declare const EDITOR_LABELS: {
284
+ basics: string;
285
+ name: string;
286
+ nameHint: string;
287
+ language: string;
288
+ category: string;
289
+ utility: string;
290
+ marketing: string;
291
+ authentication: string;
292
+ content: string;
293
+ header: string;
294
+ headerNone: string;
295
+ headerText: string;
296
+ headerImage: string;
297
+ headerVideo: string;
298
+ headerDocument: string;
299
+ headerTextField: string;
300
+ sampleFileUrl: string;
301
+ body: string;
302
+ footer: string;
303
+ formattingNote: string;
304
+ buttons: string;
305
+ buttonType: string;
306
+ quickReply: string;
307
+ link: string;
308
+ phone: string;
309
+ copyCode: string;
310
+ buttonText: string;
311
+ url: string;
312
+ phoneNumber: string;
313
+ sampleCode: string;
314
+ removeButton: string;
315
+ addButton: string;
316
+ variables: string;
317
+ /** Followed by the variable's number, e.g. "position 1". */
318
+ variablePosition: string;
319
+ exampleValue: string;
320
+ noVariables: string;
321
+ codeMessage: string;
322
+ securityRecommendation: string;
323
+ expiresAfter: string;
324
+ expiresHint: string;
325
+ optional: string;
326
+ /** Default text of new buttons. */
327
+ newQuickReply: string;
328
+ newLinkButton: string;
329
+ newPhoneButton: string;
330
+ };
280
331
  type EditorOptions = {
281
332
  /** Languages offered in the language select. Default: English (US), Portuguese (BR), Spanish. */
282
333
  languages?: EditorOption[];
334
+ /** Texts to replace, e.g. to show the editor in another language. Default: English. */
335
+ labels?: Partial<EditorLabels>;
283
336
  };
284
337
  /** The sections of the editor for a template. */
285
338
  declare function editorSections(template: Template, options?: EditorOptions): EditorSection[];
@@ -290,8 +343,10 @@ declare function editorSections(template: Template, options?: EditorOptions): Ed
290
343
  *
291
344
  * `remembered` holds examples of variables seen earlier: while someone types `{{name}}` the
292
345
  * placeholder is briefly incomplete, and its example comes back from here once it is whole.
346
+ *
347
+ * `labels` gives new buttons their default text in the editor's language.
293
348
  */
294
- declare function applyEdit(template: Template, id: string, value?: string | boolean, remembered?: Variables): Template;
349
+ declare function applyEdit(template: Template, id: string, value?: string | boolean, remembered?: Variables, labels?: Partial<EditorLabels>): Template;
295
350
  /**
296
351
  * Switches category. Authentication has its own shape, so moving in or out of it keeps only
297
352
  * the name and language.
@@ -311,5 +366,5 @@ declare function editorTree(template: Template, options?: EditorOptions & {
311
366
  idPrefix?: string;
312
367
  }): PreviewNode;
313
368
 
314
- export { Adapter, Category, HttpRequest, StandardTemplate, Template, TemplateValidationError, Variables, applyEdit, changeCategory, codes, editorSections, editorTree, extractVariables, previewTree, syncVariables, toCurl, toPreviewModel, toRequests, validate };
315
- export type { ButtonIcon, Code, EditorField, EditorOption, EditorOptions, EditorRow, EditorSection, Issue, Line, Platform, PreviewButton, PreviewCard, PreviewHeader, PreviewModel, PreviewNode, PreviewOptions, Severity, Span, TreeOptions, ValidateOptions, ValidationResult };
369
+ export { Adapter, Category, EDITOR_LABELS, HttpRequest, StandardTemplate, Template, TemplateValidationError, Variables, applyEdit, changeCategory, codes, editorSections, editorTree, extractVariables, previewTree, syncVariables, toCurl, toPreviewModel, toRequests, validate };
370
+ export type { ButtonIcon, Code, EditorField, EditorLabels, EditorOption, EditorOptions, EditorRow, EditorSection, Issue, Line, Platform, PreviewButton, PreviewCard, PreviewHeader, PreviewModel, PreviewNode, PreviewOptions, Severity, Span, TreeOptions, ValidateOptions, ValidationResult };
package/dist/index.js CHANGED
@@ -1230,43 +1230,93 @@ var ICONS = {
1230
1230
  };
1231
1231
 
1232
1232
  // src/editor/model.ts
1233
+ var EDITOR_LABELS = {
1234
+ basics: "Basics",
1235
+ name: "Name",
1236
+ nameHint: "lowercase, numbers and _",
1237
+ language: "Language",
1238
+ category: "Category",
1239
+ utility: "Utility",
1240
+ marketing: "Marketing",
1241
+ authentication: "Authentication",
1242
+ content: "Content",
1243
+ header: "Header",
1244
+ headerNone: "None",
1245
+ headerText: "Text",
1246
+ headerImage: "Image",
1247
+ headerVideo: "Video",
1248
+ headerDocument: "Document",
1249
+ headerTextField: "Header text",
1250
+ sampleFileUrl: "Sample file URL",
1251
+ body: "Body",
1252
+ footer: "Footer",
1253
+ formattingNote: "Use {{name}} for variables and *bold*, _italic_, ~strike~ for formatting.",
1254
+ buttons: "Buttons",
1255
+ buttonType: "Type",
1256
+ quickReply: "Quick reply",
1257
+ link: "Link",
1258
+ phone: "Phone",
1259
+ copyCode: "Copy code",
1260
+ buttonText: "Button text",
1261
+ url: "URL",
1262
+ phoneNumber: "Phone number",
1263
+ sampleCode: "Sample code",
1264
+ removeButton: "Remove button",
1265
+ addButton: "Add button",
1266
+ variables: "Variables",
1267
+ /** Followed by the variable's number, e.g. "position 1". */
1268
+ variablePosition: "position",
1269
+ exampleValue: "Example value",
1270
+ noVariables: "Type a placeholder such as {{first_name}} in the text to add one.",
1271
+ codeMessage: "Code message",
1272
+ securityRecommendation: "Add \u201Cdo not share this code\u201D",
1273
+ expiresAfter: "Expires after",
1274
+ expiresHint: "minutes, 1 to 90",
1275
+ optional: "optional",
1276
+ /** Default text of new buttons. */
1277
+ newQuickReply: "Reply",
1278
+ newLinkButton: "Open link",
1279
+ newPhoneButton: "Call us"
1280
+ };
1233
1281
  var LANGUAGES = [
1234
1282
  { value: "en_US", label: "English (US)" },
1235
1283
  { value: "pt_BR", label: "Portuguese (BR)" },
1236
1284
  { value: "es", label: "Spanish" }
1237
1285
  ];
1238
- var CATEGORIES2 = [
1239
- { value: "UTILITY", label: "Utility" },
1240
- { value: "MARKETING", label: "Marketing" },
1241
- { value: "AUTHENTICATION", label: "Authentication" }
1286
+ var categories = (l) => [
1287
+ { value: "UTILITY", label: l.utility },
1288
+ { value: "MARKETING", label: l.marketing },
1289
+ { value: "AUTHENTICATION", label: l.authentication }
1242
1290
  ];
1243
- var HEADER_KINDS = [
1244
- { value: "NONE", label: "None" },
1245
- { value: "TEXT", label: "Text" },
1246
- { value: "IMAGE", label: "Image" },
1247
- { value: "VIDEO", label: "Video" },
1248
- { value: "DOCUMENT", label: "Document" }
1291
+ var headerKinds = (l) => [
1292
+ { value: "NONE", label: l.headerNone },
1293
+ { value: "TEXT", label: l.headerText },
1294
+ { value: "IMAGE", label: l.headerImage },
1295
+ { value: "VIDEO", label: l.headerVideo },
1296
+ { value: "DOCUMENT", label: l.headerDocument }
1249
1297
  ];
1250
- var BUTTON_TYPES = [
1251
- { value: "QUICK_REPLY", label: "Quick reply" },
1252
- { value: "URL", label: "Link" },
1253
- { value: "PHONE_NUMBER", label: "Phone" },
1254
- { value: "COPY_CODE", label: "Copy code" }
1298
+ var BUTTON_TYPES = ["QUICK_REPLY", "URL", "PHONE_NUMBER", "COPY_CODE"];
1299
+ var buttonTypes = (l) => [
1300
+ { value: "QUICK_REPLY", label: l.quickReply },
1301
+ { value: "URL", label: l.link },
1302
+ { value: "PHONE_NUMBER", label: l.phone },
1303
+ { value: "COPY_CODE", label: l.copyCode }
1255
1304
  ];
1256
1305
  var MAX_BUTTONS = 10;
1257
1306
  var count = (text, max) => `${[...text].length}/${max}`;
1258
1307
  function editorSections(template, options = {}) {
1308
+ const l = { ...EDITOR_LABELS, ...options.labels };
1259
1309
  const basics = {
1260
1310
  id: "basics",
1261
- title: "Basics",
1311
+ title: l.basics,
1262
1312
  rows: [
1263
1313
  {
1264
1314
  fields: [
1265
1315
  {
1266
1316
  kind: "text",
1267
1317
  id: "name",
1268
- label: "Name",
1269
- hint: "lowercase, numbers and _",
1318
+ label: l.name,
1319
+ hint: l.nameHint,
1270
1320
  value: template.name,
1271
1321
  mono: true
1272
1322
  }
@@ -1277,47 +1327,48 @@ function editorSections(template, options = {}) {
1277
1327
  {
1278
1328
  kind: "select",
1279
1329
  id: "language",
1280
- label: "Language",
1330
+ label: l.language,
1281
1331
  value: template.language,
1282
1332
  options: options.languages ?? LANGUAGES
1283
1333
  },
1284
1334
  {
1285
1335
  kind: "segment",
1286
1336
  id: "category",
1287
- label: "Category",
1337
+ label: l.category,
1288
1338
  value: template.category,
1289
- options: CATEGORIES2
1339
+ options: categories(l)
1290
1340
  }
1291
1341
  ]
1292
1342
  }
1293
1343
  ]
1294
1344
  };
1295
1345
  if (template.category === "AUTHENTICATION")
1296
- return [basics, codeSection(template)];
1346
+ return [basics, codeSection(template, l)];
1297
1347
  return [
1298
1348
  basics,
1299
- contentSection(template),
1300
- buttonsSection(template),
1301
- variablesSection(template)
1349
+ contentSection(template, l),
1350
+ buttonsSection(template, l),
1351
+ variablesSection(template, l)
1302
1352
  ];
1303
1353
  }
1304
- function contentSection(template) {
1354
+ function contentSection(template, l) {
1305
1355
  const header2 = template.header;
1306
- const kind = HEADER_KINDS.some((option) => option.value === header2?.type) ? header2.type : "NONE";
1356
+ const kinds = headerKinds(l);
1357
+ const kind = kinds.some((option) => option.value === header2?.type) ? header2.type : "NONE";
1307
1358
  const headerRow = [
1308
1359
  {
1309
1360
  kind: "select",
1310
1361
  id: "header.kind",
1311
- label: "Header",
1362
+ label: l.header,
1312
1363
  value: kind,
1313
- options: HEADER_KINDS
1364
+ options: kinds
1314
1365
  }
1315
1366
  ];
1316
1367
  if (header2?.type === "TEXT")
1317
1368
  headerRow.push({
1318
1369
  kind: "text",
1319
1370
  id: "header.text",
1320
- label: "Header text",
1371
+ label: l.headerTextField,
1321
1372
  hint: count(header2.text, 60),
1322
1373
  value: header2.text
1323
1374
  });
@@ -1325,13 +1376,13 @@ function contentSection(template) {
1325
1376
  headerRow.push({
1326
1377
  kind: "text",
1327
1378
  id: "header.url",
1328
- label: "Sample file URL",
1379
+ label: l.sampleFileUrl,
1329
1380
  value: header2.url,
1330
1381
  mono: true
1331
1382
  });
1332
1383
  return {
1333
1384
  id: "content",
1334
- title: "Content",
1385
+ title: l.content,
1335
1386
  rows: [
1336
1387
  { fields: headerRow },
1337
1388
  {
@@ -1339,7 +1390,7 @@ function contentSection(template) {
1339
1390
  {
1340
1391
  kind: "textarea",
1341
1392
  id: "body",
1342
- label: "Body",
1393
+ label: l.body,
1343
1394
  hint: count(template.body.text, 1024),
1344
1395
  value: template.body.text
1345
1396
  }
@@ -1350,35 +1401,35 @@ function contentSection(template) {
1350
1401
  {
1351
1402
  kind: "text",
1352
1403
  id: "footer",
1353
- label: "Footer",
1404
+ label: l.footer,
1354
1405
  hint: count(template.footer?.text ?? "", 60),
1355
1406
  value: template.footer?.text ?? ""
1356
1407
  }
1357
1408
  ]
1358
1409
  }
1359
1410
  ],
1360
- note: "Use {{name}} for variables and *bold*, _italic_, ~strike~ for formatting."
1411
+ note: l.formattingNote
1361
1412
  };
1362
1413
  }
1363
- function buttonsSection(template) {
1414
+ function buttonsSection(template, l) {
1364
1415
  const buttons = template.buttons ?? [];
1365
1416
  const rows = buttons.map((button2, index) => {
1366
1417
  const at = `buttons.${index}`;
1367
- const type = BUTTON_TYPES.some((option) => option.value === button2.type) ? button2.type : "QUICK_REPLY";
1418
+ const type = BUTTON_TYPES.includes(button2.type) ? button2.type : "QUICK_REPLY";
1368
1419
  const fields = [
1369
1420
  {
1370
1421
  kind: "select",
1371
1422
  id: `${at}.type`,
1372
- label: "Type",
1423
+ label: l.buttonType,
1373
1424
  value: type,
1374
- options: BUTTON_TYPES
1425
+ options: buttonTypes(l)
1375
1426
  }
1376
1427
  ];
1377
1428
  if ("text" in button2 && button2.text !== void 0)
1378
1429
  fields.push({
1379
1430
  kind: "text",
1380
1431
  id: `${at}.text`,
1381
- label: "Button text",
1432
+ label: l.buttonText,
1382
1433
  hint: count(button2.text, 25),
1383
1434
  value: button2.text
1384
1435
  });
@@ -1386,7 +1437,7 @@ function buttonsSection(template) {
1386
1437
  fields.push({
1387
1438
  kind: "text",
1388
1439
  id: `${at}.url`,
1389
- label: "URL",
1440
+ label: l.url,
1390
1441
  value: button2.url,
1391
1442
  mono: true
1392
1443
  });
@@ -1394,7 +1445,7 @@ function buttonsSection(template) {
1394
1445
  fields.push({
1395
1446
  kind: "text",
1396
1447
  id: `${at}.phoneNumber`,
1397
- label: "Phone number",
1448
+ label: l.phoneNumber,
1398
1449
  value: button2.phoneNumber,
1399
1450
  mono: true
1400
1451
  });
@@ -1402,14 +1453,14 @@ function buttonsSection(template) {
1402
1453
  fields.push({
1403
1454
  kind: "text",
1404
1455
  id: `${at}.example`,
1405
- label: "Sample code",
1456
+ label: l.sampleCode,
1406
1457
  value: button2.example,
1407
1458
  mono: true
1408
1459
  });
1409
1460
  fields.push({
1410
1461
  kind: "action",
1411
1462
  id: `${at}.remove`,
1412
- label: "Remove button",
1463
+ label: l.removeButton,
1413
1464
  icon: "remove"
1414
1465
  });
1415
1466
  return { fields, list: true };
@@ -1419,7 +1470,7 @@ function buttonsSection(template) {
1419
1470
  {
1420
1471
  kind: "action",
1421
1472
  id: "buttons.add",
1422
- label: "Add button",
1473
+ label: l.addButton,
1423
1474
  icon: "add",
1424
1475
  disabled: buttons.length >= MAX_BUTTONS
1425
1476
  }
@@ -1427,42 +1478,42 @@ function buttonsSection(template) {
1427
1478
  });
1428
1479
  return {
1429
1480
  id: "buttons",
1430
- title: `Buttons ${buttons.length}/${MAX_BUTTONS}`,
1481
+ title: `${l.buttons} ${buttons.length}/${MAX_BUTTONS}`,
1431
1482
  rows
1432
1483
  };
1433
1484
  }
1434
- function variablesSection(template) {
1485
+ function variablesSection(template, l) {
1435
1486
  const variables = Object.entries(template.variables ?? {});
1436
1487
  return {
1437
1488
  id: "variables",
1438
- title: "Variables",
1489
+ title: l.variables,
1439
1490
  rows: variables.map(([name, variable], index) => ({
1440
1491
  fields: [
1441
1492
  {
1442
1493
  kind: "text",
1443
1494
  id: `variables.${name}`,
1444
1495
  label: `{{${name}}}`,
1445
- hint: `position ${index + 1}`,
1496
+ hint: `${l.variablePosition} ${index + 1}`,
1446
1497
  value: variable.example,
1447
- placeholder: "Example value"
1498
+ placeholder: l.exampleValue
1448
1499
  }
1449
1500
  ]
1450
1501
  })),
1451
- note: variables.length ? void 0 : "Type a placeholder such as {{first_name}} in the text to add one."
1502
+ note: variables.length ? void 0 : l.noVariables
1452
1503
  };
1453
1504
  }
1454
- function codeSection(template) {
1505
+ function codeSection(template, l) {
1455
1506
  const minutes = template.footer?.codeExpirationMinutes;
1456
1507
  return {
1457
1508
  id: "code",
1458
- title: "Code message",
1509
+ title: l.codeMessage,
1459
1510
  rows: [
1460
1511
  {
1461
1512
  fields: [
1462
1513
  {
1463
1514
  kind: "checkbox",
1464
1515
  id: "body.addSecurityRecommendation",
1465
- label: "Add \u201Cdo not share this code\u201D",
1516
+ label: l.securityRecommendation,
1466
1517
  checked: template.body?.addSecurityRecommendation ?? false
1467
1518
  }
1468
1519
  ]
@@ -1472,25 +1523,25 @@ function codeSection(template) {
1472
1523
  {
1473
1524
  kind: "text",
1474
1525
  id: "footer.codeExpirationMinutes",
1475
- label: "Expires after",
1476
- hint: "minutes, 1 to 90",
1526
+ label: l.expiresAfter,
1527
+ hint: l.expiresHint,
1477
1528
  value: minutes === void 0 ? "" : String(minutes),
1478
1529
  mono: true
1479
1530
  },
1480
1531
  {
1481
1532
  kind: "text",
1482
1533
  id: "button.text",
1483
- label: "Button text",
1484
- hint: "optional",
1534
+ label: l.buttonText,
1535
+ hint: l.optional,
1485
1536
  value: template.button.text ?? "",
1486
- placeholder: "Copy code"
1537
+ placeholder: l.copyCode
1487
1538
  }
1488
1539
  ]
1489
1540
  }
1490
1541
  ]
1491
1542
  };
1492
1543
  }
1493
- function applyEdit(template, id, value, remembered = {}) {
1544
+ function applyEdit(template, id, value, remembered = {}, labels = {}) {
1494
1545
  const text = typeof value === "string" ? value : "";
1495
1546
  if (id === "name") return { ...template, name: text };
1496
1547
  if (id === "language") return { ...template, language: text };
@@ -1523,9 +1574,12 @@ function applyEdit(template, id, value, remembered = {}) {
1523
1574
  [id.slice("variables.".length)]: { example: text }
1524
1575
  }
1525
1576
  };
1526
- return syncVariables(editStandard(template, id, text), remembered);
1577
+ return syncVariables(
1578
+ editStandard(template, id, text, { ...EDITOR_LABELS, ...labels }),
1579
+ remembered
1580
+ );
1527
1581
  }
1528
- function editStandard(template, id, text) {
1582
+ function editStandard(template, id, text, l) {
1529
1583
  if (id === "header.kind") return { ...template, header: newHeader(text) };
1530
1584
  if (id === "header.text")
1531
1585
  return { ...template, header: { type: "TEXT", text } };
@@ -1540,7 +1594,7 @@ function editStandard(template, id, text) {
1540
1594
  buttons: next2.length ? next2 : void 0
1541
1595
  });
1542
1596
  if (id === "buttons.add")
1543
- return buttons.length >= MAX_BUTTONS ? template : withButtons([...buttons, newButton("QUICK_REPLY")]);
1597
+ return buttons.length >= MAX_BUTTONS ? template : withButtons([...buttons, newButton(l, "QUICK_REPLY")]);
1544
1598
  const match = /^buttons\.(\d+)\.(\w+)$/.exec(id);
1545
1599
  if (!match) return template;
1546
1600
  const index = Number(match[1]);
@@ -1550,6 +1604,7 @@ function editStandard(template, id, text) {
1550
1604
  if (field === "remove")
1551
1605
  return withButtons(buttons.filter((_, i) => i !== index));
1552
1606
  const next = field === "type" ? newButton(
1607
+ l,
1553
1608
  text,
1554
1609
  "text" in button2 ? button2.text ?? "" : ""
1555
1610
  ) : { ...button2, [field]: text };
@@ -1591,13 +1646,13 @@ function newHeader(kind) {
1591
1646
  return { type: kind, url: "https://" };
1592
1647
  return void 0;
1593
1648
  }
1594
- function newButton(type, label = "") {
1649
+ function newButton(l, type, label = "") {
1595
1650
  if (type === "URL")
1596
- return { type, text: label || "Open link", url: "https://" };
1651
+ return { type, text: label || l.newLinkButton, url: "https://" };
1597
1652
  if (type === "PHONE_NUMBER")
1598
- return { type, text: label || "Call us", phoneNumber: "+" };
1653
+ return { type, text: label || l.newPhoneButton, phoneNumber: "+" };
1599
1654
  if (type === "COPY_CODE") return { type, example: "CODE10" };
1600
- return { type: "QUICK_REPLY", text: label || "Reply" };
1655
+ return { type: "QUICK_REPLY", text: label || l.newQuickReply };
1601
1656
  }
1602
1657
 
1603
1658
  // src/editor/tree.ts
@@ -1725,6 +1780,7 @@ function node2(tag, className, attrs, children = []) {
1725
1780
  return { tag, ...className && { class: className }, attrs, children };
1726
1781
  }
1727
1782
  export {
1783
+ EDITOR_LABELS,
1728
1784
  TemplateValidationError,
1729
1785
  UnsupportedByProviderError,
1730
1786
  applyEdit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talqui-oss/template-builder",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Preview, validate and create WhatsApp message templates",
5
5
  "license": "MIT",
6
6
  "repository": {