@0001-labs/design-system-1 0.0.0-stage → 0.3.0-alpha.13

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.
Files changed (699) hide show
  1. package/DS1/0-face/control.ts +99 -0
  2. package/DS1/0-face/define.ts +5 -0
  3. package/DS1/0-face/device.ts +414 -0
  4. package/DS1/0-face/grid-fit.ts +63 -0
  5. package/DS1/0-face/grid.ts +144 -0
  6. package/DS1/0-face/i18n.ts +1023 -0
  7. package/DS1/0-face/language-routing.ts +135 -0
  8. package/DS1/0-face/language-validator.ts +46 -0
  9. package/DS1/0-face/languages.ts +242 -0
  10. package/DS1/0-face/layout-inspector.ts +184 -0
  11. package/DS1/0-face/preferences.ts +39 -0
  12. package/DS1/0-face/pricing.ts +57 -0
  13. package/DS1/0-face/scaling.ts +25 -0
  14. package/DS1/0-face/shared-words.ts +45 -0
  15. package/DS1/0-face/theme.ts +58 -0
  16. package/DS1/0-face/tooltips.ts +82 -0
  17. package/DS1/0-face/translations/da.json +63 -0
  18. package/DS1/0-face/translations/de.json +63 -0
  19. package/DS1/0-face/translations/en.json +63 -0
  20. package/DS1/0-face/translations/es.json +63 -0
  21. package/DS1/0-face/translations/fi.json +63 -0
  22. package/DS1/0-face/translations/fr.json +63 -0
  23. package/DS1/0-face/translations/is.json +56 -0
  24. package/DS1/0-face/translations/it.json +63 -0
  25. package/DS1/0-face/translations/ja.json +63 -0
  26. package/DS1/0-face/translations/ko.json +63 -0
  27. package/DS1/0-face/translations/nb.json +63 -0
  28. package/DS1/0-face/translations/pt-BR.json +63 -0
  29. package/DS1/0-face/translations/pt.json +63 -0
  30. package/DS1/0-face/translations/sv.json +63 -0
  31. package/DS1/0-face/translations/zh.json +63 -0
  32. package/DS1/0-face/translations-format.ts +216 -0
  33. package/DS1/1-root/component-appearance.css +32 -0
  34. package/DS1/1-root/component-metrics.css +21 -0
  35. package/DS1/1-root/component-tokens.css +90 -0
  36. package/DS1/1-root/controls.css +261 -0
  37. package/DS1/1-root/fonts/GT-America-Compressed-Regular.woff2 +0 -0
  38. package/DS1/1-root/fonts/GT-America-Standard-Medium.woff2 +0 -0
  39. package/DS1/1-root/fonts/GT-America-Standard-Regular.woff2 +0 -0
  40. package/DS1/1-root/fonts/GT-Canon-M-Standard-Medium-Trial.woff2 +0 -0
  41. package/DS1/1-root/fonts/Inter-Arrows.woff2 +0 -0
  42. package/DS1/1-root/fonts/Inter-LICENSE.md +7 -0
  43. package/DS1/1-root/fonts/Iosevka-LICENSE.md +7 -0
  44. package/DS1/1-root/fonts/Iosevka-Regular.woff2 +0 -0
  45. package/DS1/1-root/grid.css +31 -0
  46. package/DS1/1-root/one.css +602 -0
  47. package/DS1/1-root/one.semantic.css +683 -0
  48. package/DS1/1-root/palette.ts +91 -0
  49. package/DS1/2-core/ds-avatar.ts +130 -0
  50. package/DS1/2-core/ds-badge.ts +77 -0
  51. package/DS1/2-core/ds-banner.ts +122 -0
  52. package/DS1/2-core/ds-button.ts +163 -0
  53. package/DS1/2-core/ds-card.ts +137 -0
  54. package/DS1/2-core/ds-checkbox.ts +14 -0
  55. package/DS1/2-core/ds-color.ts +32 -0
  56. package/DS1/2-core/ds-composer.ts +182 -0
  57. package/DS1/2-core/ds-cycle.ts +628 -0
  58. package/DS1/2-core/ds-date.ts +24 -0
  59. package/DS1/2-core/ds-file-picker.ts +83 -0
  60. package/DS1/2-core/ds-gap.ts +34 -0
  61. package/DS1/2-core/ds-icon.ts +161 -0
  62. package/DS1/2-core/ds-input.ts +518 -0
  63. package/DS1/2-core/ds-language.ts +94 -0
  64. package/DS1/2-core/ds-pagedots.ts +52 -0
  65. package/DS1/2-core/ds-radio.ts +25 -0
  66. package/DS1/2-core/ds-select.ts +44 -0
  67. package/DS1/2-core/ds-split.ts +122 -0
  68. package/DS1/2-core/ds-swatch.ts +20 -0
  69. package/DS1/2-core/ds-switch.ts +13 -0
  70. package/DS1/2-core/ds-text.ts +190 -0
  71. package/DS1/2-core/ds-textarea.ts +136 -0
  72. package/DS1/2-core/ds-token.ts +40 -0
  73. package/DS1/2-core/ds-tooltip.ts +115 -0
  74. package/DS1/2-core/generated/ds-icon-map.ts +583 -0
  75. package/DS1/2-core/generated/icons-extra.ts +243 -0
  76. package/DS1/2-core/generated/icons-immediate.ts +49 -0
  77. package/DS1/2-core/styles/ds-avatar.css +125 -0
  78. package/DS1/2-core/styles/ds-badge.css +26 -0
  79. package/DS1/2-core/styles/ds-banner.css +77 -0
  80. package/DS1/2-core/styles/ds-button-host.css +214 -0
  81. package/DS1/2-core/styles/ds-button.css +131 -0
  82. package/DS1/2-core/styles/ds-checkbox.css +48 -0
  83. package/DS1/2-core/styles/ds-color.css +24 -0
  84. package/DS1/2-core/styles/ds-composer-frame.css +11 -0
  85. package/DS1/2-core/styles/ds-composer.css +55 -0
  86. package/DS1/2-core/styles/ds-cycle.css +18 -0
  87. package/DS1/2-core/styles/ds-date.css +9 -0
  88. package/DS1/2-core/styles/ds-file-picker.css +66 -0
  89. package/DS1/2-core/styles/ds-gap.css +71 -0
  90. package/DS1/2-core/styles/ds-icon.css +44 -0
  91. package/DS1/2-core/styles/ds-input.css +233 -0
  92. package/DS1/2-core/styles/ds-language.css +78 -0
  93. package/DS1/2-core/styles/ds-pagedots.css +31 -0
  94. package/DS1/2-core/styles/ds-radio.css +83 -0
  95. package/DS1/2-core/styles/ds-select.css +95 -0
  96. package/DS1/2-core/styles/ds-split.css +62 -0
  97. package/DS1/2-core/styles/ds-switch.css +50 -0
  98. package/DS1/2-core/styles/ds-text.css +87 -0
  99. package/DS1/2-core/styles/ds-textarea.css +27 -0
  100. package/DS1/2-core/styles/ds-token.css +9 -0
  101. package/DS1/2-core/styles/ds-tooltip.css +37 -0
  102. package/DS1/3-unit/context-trigger.ts +104 -0
  103. package/DS1/3-unit/ds-accordion.ts +120 -0
  104. package/DS1/3-unit/ds-avatar-group.ts +99 -0
  105. package/DS1/3-unit/ds-form.ts +304 -0
  106. package/DS1/3-unit/ds-list.ts +36 -0
  107. package/DS1/3-unit/ds-menu.ts +482 -0
  108. package/DS1/3-unit/ds-row.ts +48 -0
  109. package/DS1/3-unit/ds-table.ts +68 -0
  110. package/DS1/3-unit/ds-tabs.ts +77 -0
  111. package/DS1/3-unit/ds-token-field.ts +127 -0
  112. package/DS1/3-unit/styles/context-trigger.css +6 -0
  113. package/DS1/3-unit/styles/ds-accordion.css +64 -0
  114. package/DS1/3-unit/styles/ds-avatar-group.css +51 -0
  115. package/DS1/3-unit/styles/ds-list.css +26 -0
  116. package/DS1/3-unit/styles/ds-menu.css +97 -0
  117. package/DS1/3-unit/styles/ds-row.css +37 -0
  118. package/DS1/3-unit/styles/ds-table.css +80 -0
  119. package/DS1/3-unit/styles/ds-tabs.css +44 -0
  120. package/DS1/3-unit/styles/ds-token-field.css +11 -0
  121. package/DS1/4-page/ds-container.ts +28 -0
  122. package/DS1/4-page/ds-dialog.ts +331 -0
  123. package/DS1/4-page/ds-grid.ts +74 -0
  124. package/DS1/4-page/ds-layout.ts +949 -0
  125. package/DS1/4-page/ds-panel.ts +38 -0
  126. package/DS1/4-page/email.ts +170 -0
  127. package/DS1/4-page/styles/ds-container.css +35 -0
  128. package/DS1/4-page/styles/ds-dialog.css +94 -0
  129. package/DS1/4-page/styles/ds-grid.css +95 -0
  130. package/DS1/4-page/styles/ds-layout.css +246 -0
  131. package/DS1/4-page/styles/ds-panel.css +31 -0
  132. package/DS1/components.ts +13 -0
  133. package/DS1/index.ts +86 -0
  134. package/DS1/react/accordion.ts +33 -0
  135. package/DS1/react/avatar-group.ts +11 -0
  136. package/DS1/react/avatar.ts +42 -0
  137. package/DS1/react/badge.ts +7 -0
  138. package/DS1/react/button.ts +63 -0
  139. package/DS1/react/choice.ts +64 -0
  140. package/DS1/react/composer.ts +18 -0
  141. package/DS1/react/context-menu.ts +167 -0
  142. package/DS1/react/dialog.ts +102 -0
  143. package/DS1/react/file-picker.ts +34 -0
  144. package/DS1/react/input.ts +184 -0
  145. package/DS1/react/menu.ts +28 -0
  146. package/DS1/react/split.ts +42 -0
  147. package/DS1/react/token-field.ts +32 -0
  148. package/DS1/vite-env.d.ts +13 -0
  149. package/DS1/x-icon/1x.svg +4 -0
  150. package/DS1/x-icon/1xdots.svg +11 -0
  151. package/DS1/x-icon/1xgrid.svg +8 -0
  152. package/DS1/x-icon/1xlines.svg +5 -0
  153. package/DS1/x-icon/2x.svg +4 -0
  154. package/DS1/x-icon/2xdots.svg +18 -0
  155. package/DS1/x-icon/2xgrid-alt-2.svg +9 -0
  156. package/DS1/x-icon/2xgrid-alt-3.svg +8 -0
  157. package/DS1/x-icon/2xgrid-alt.svg +15 -0
  158. package/DS1/x-icon/2xgrid.svg +6 -0
  159. package/DS1/x-icon/2xlines.svg +6 -0
  160. package/DS1/x-icon/4x4.svg +18 -0
  161. package/DS1/x-icon/app.svg +4 -0
  162. package/DS1/x-icon/apple.svg +3 -0
  163. package/DS1/x-icon/archive-alt.svg +4 -0
  164. package/DS1/x-icon/archive.svg +4 -0
  165. package/DS1/x-icon/arrange-archive.svg +1 -0
  166. package/DS1/x-icon/arrange-check.svg +1 -0
  167. package/DS1/x-icon/arrange-chevron.svg +1 -0
  168. package/DS1/x-icon/arrange-connect.svg +1 -0
  169. package/DS1/x-icon/arrange-draft.svg +1 -0
  170. package/DS1/x-icon/arrange-dropdown.svg +1 -0
  171. package/DS1/x-icon/arrange-home.svg +1 -0
  172. package/DS1/x-icon/arrange-link.svg +1 -0
  173. package/DS1/x-icon/arrange-menu.svg +1 -0
  174. package/DS1/x-icon/arrange-more.svg +1 -0
  175. package/DS1/x-icon/arrange-page.svg +1 -0
  176. package/DS1/x-icon/arrange-plus.svg +1 -0
  177. package/DS1/x-icon/arrange-site.svg +1 -0
  178. package/DS1/x-icon/avatar.svg +4 -0
  179. package/DS1/x-icon/badge-bands-3d.svg +1 -0
  180. package/DS1/x-icon/badge-bands.svg +1 -0
  181. package/DS1/x-icon/badge-blue-fan-3d.svg +1 -0
  182. package/DS1/x-icon/badge-blue-fan.svg +1 -0
  183. package/DS1/x-icon/badge-bricks-3d.svg +1 -0
  184. package/DS1/x-icon/badge-bricks.svg +1 -0
  185. package/DS1/x-icon/badge-broken-stripes-3d.svg +1 -0
  186. package/DS1/x-icon/badge-broken-stripes.svg +1 -0
  187. package/DS1/x-icon/badge-checker-3d.svg +1 -0
  188. package/DS1/x-icon/badge-checker.svg +1 -0
  189. package/DS1/x-icon/badge-diamond-3d.svg +1 -0
  190. package/DS1/x-icon/badge-diamond.svg +1 -0
  191. package/DS1/x-icon/badge-fan-3d.svg +1 -0
  192. package/DS1/x-icon/badge-fan-dark-3d.svg +1 -0
  193. package/DS1/x-icon/badge-fan-dark.svg +1 -0
  194. package/DS1/x-icon/badge-fan.svg +1 -0
  195. package/DS1/x-icon/badge-halves-3d.svg +1 -0
  196. package/DS1/x-icon/badge-halves-green-3d.svg +1 -0
  197. package/DS1/x-icon/badge-halves-green.svg +1 -0
  198. package/DS1/x-icon/badge-halves.svg +1 -0
  199. package/DS1/x-icon/badge-red-3d.svg +1 -0
  200. package/DS1/x-icon/badge-red-blue-bricks-3d.svg +1 -0
  201. package/DS1/x-icon/badge-red-blue-bricks.svg +1 -0
  202. package/DS1/x-icon/badge-red.svg +1 -0
  203. package/DS1/x-icon/badge-round-fan-3d.svg +1 -0
  204. package/DS1/x-icon/badge-round-fan.svg +1 -0
  205. package/DS1/x-icon/badge-shard-3d.svg +1 -0
  206. package/DS1/x-icon/badge-shard.svg +1 -0
  207. package/DS1/x-icon/badge-split-circle-3d.svg +1 -0
  208. package/DS1/x-icon/badge-split-circle.svg +1 -0
  209. package/DS1/x-icon/badge-tall-bricks-3d.svg +1 -0
  210. package/DS1/x-icon/badge-tall-bricks.svg +1 -0
  211. package/DS1/x-icon/badge-weave-3d.svg +1 -0
  212. package/DS1/x-icon/badge-weave.svg +1 -0
  213. package/DS1/x-icon/bag.svg +4 -0
  214. package/DS1/x-icon/block-accordion.svg +1 -0
  215. package/DS1/x-icon/block-banner.svg +1 -0
  216. package/DS1/x-icon/block-breadcrumbs.svg +1 -0
  217. package/DS1/x-icon/block-button.svg +1 -0
  218. package/DS1/x-icon/block-divider.svg +1 -0
  219. package/DS1/x-icon/block-forms.svg +1 -0
  220. package/DS1/x-icon/block-group.svg +1 -0
  221. package/DS1/x-icon/block-heading.svg +1 -0
  222. package/DS1/x-icon/block-image.svg +1 -0
  223. package/DS1/x-icon/block-logo-marquee.svg +1 -0
  224. package/DS1/x-icon/block-page-progress.svg +1 -0
  225. package/DS1/x-icon/block-section.svg +1 -0
  226. package/DS1/x-icon/block-table.svg +1 -0
  227. package/DS1/x-icon/block-tabs.svg +1 -0
  228. package/DS1/x-icon/block-text.svg +1 -0
  229. package/DS1/x-icon/bold.svg +3 -0
  230. package/DS1/x-icon/bullet.svg +5 -0
  231. package/DS1/x-icon/calendar.svg +6 -0
  232. package/DS1/x-icon/callout.svg +5 -0
  233. package/DS1/x-icon/camera.svg +5 -0
  234. package/DS1/x-icon/categories.svg +12 -0
  235. package/DS1/x-icon/check-alt.svg +3 -0
  236. package/DS1/x-icon/check.svg +3 -0
  237. package/DS1/x-icon/close.svg +3 -0
  238. package/DS1/x-icon/code-block.svg +4 -0
  239. package/DS1/x-icon/code.svg +3 -0
  240. package/DS1/x-icon/collapse.svg +3 -0
  241. package/DS1/x-icon/color.svg +4 -0
  242. package/DS1/x-icon/column.svg +3 -0
  243. package/DS1/x-icon/comment.svg +3 -0
  244. package/DS1/x-icon/compose.svg +4 -0
  245. package/DS1/x-icon/connect.svg +5 -0
  246. package/DS1/x-icon/content.svg +6 -0
  247. package/DS1/x-icon/default.svg +4 -0
  248. package/DS1/x-icon/delete.svg +5 -0
  249. package/DS1/x-icon/dictate.svg +6 -0
  250. package/DS1/x-icon/divider.svg +3 -0
  251. package/DS1/x-icon/down.svg +3 -0
  252. package/DS1/x-icon/duplicate.svg +8 -0
  253. package/DS1/x-icon/email.svg +3 -0
  254. package/DS1/x-icon/equation.svg +3 -0
  255. package/DS1/x-icon/exercise.svg +3 -0
  256. package/DS1/x-icon/expand.svg +3 -0
  257. package/DS1/x-icon/ezo-accordion-chevron.svg +11 -0
  258. package/DS1/x-icon/ezo-account-close.svg +1 -0
  259. package/DS1/x-icon/ezo-android-screen-archive.svg +4 -0
  260. package/DS1/x-icon/ezo-android-screen-calendar.svg +6 -0
  261. package/DS1/x-icon/ezo-android-screen-camera.svg +5 -0
  262. package/DS1/x-icon/ezo-android-screen-check.svg +3 -0
  263. package/DS1/x-icon/ezo-android-screen-compose.svg +4 -0
  264. package/DS1/x-icon/ezo-android-screen-globe.svg +1 -0
  265. package/DS1/x-icon/ezo-android-screen-lists.svg +3 -0
  266. package/DS1/x-icon/ezo-android-screen-shared.svg +5 -0
  267. package/DS1/x-icon/ezo-android-screen-stats.svg +3 -0
  268. package/DS1/x-icon/ezo-android-screen-store.svg +4 -0
  269. package/DS1/x-icon/ezo-android-screen-views.svg +4 -0
  270. package/DS1/x-icon/ezo-android-screen-work.svg +3 -0
  271. package/DS1/x-icon/ezo-arrow-cursor.svg +8 -0
  272. package/DS1/x-icon/ezo-block-accordion.svg +1 -0
  273. package/DS1/x-icon/ezo-block-divider.svg +1 -0
  274. package/DS1/x-icon/ezo-block-group.svg +1 -0
  275. package/DS1/x-icon/ezo-block-image.svg +1 -0
  276. package/DS1/x-icon/ezo-block-section.svg +1 -0
  277. package/DS1/x-icon/ezo-block-table.svg +1 -0
  278. package/DS1/x-icon/ezo-code.svg +14 -0
  279. package/DS1/x-icon/ezo-down.svg +3 -0
  280. package/DS1/x-icon/ezo-figma-premium-star.svg +5 -0
  281. package/DS1/x-icon/ezo-item-button-complete.svg +4 -0
  282. package/DS1/x-icon/ezo-item-button-header.svg +3 -0
  283. package/DS1/x-icon/ezo-item-button-open.svg +3 -0
  284. package/DS1/x-icon/ezo-item-button-reopen.svg +3 -0
  285. package/DS1/x-icon/ezo-item-button-star.svg +3 -0
  286. package/DS1/x-icon/ezo-item-button-start.svg +3 -0
  287. package/DS1/x-icon/ezo-item-button-unheader.svg +7 -0
  288. package/DS1/x-icon/ezo-item-button-unstar.svg +5 -0
  289. package/DS1/x-icon/ezo-item-star.svg +5 -0
  290. package/DS1/x-icon/ezo-left.svg +3 -0
  291. package/DS1/x-icon/ezo-logo.svg +1 -0
  292. package/DS1/x-icon/ezo-mic.svg +8 -0
  293. package/DS1/x-icon/ezo-move-column.svg +14 -0
  294. package/DS1/x-icon/ezo-move-row.svg +18 -0
  295. package/DS1/x-icon/ezo-navigation-check.svg +1 -0
  296. package/DS1/x-icon/ezo-note-back.svg +5 -0
  297. package/DS1/x-icon/ezo-note-menu-complete.svg +11 -0
  298. package/DS1/x-icon/ezo-note-menu-delete.svg +4 -0
  299. package/DS1/x-icon/ezo-note-menu-duplicate.svg +8 -0
  300. package/DS1/x-icon/ezo-note-menu-note.svg +5 -0
  301. package/DS1/x-icon/ezo-note-menu-page.svg +3 -0
  302. package/DS1/x-icon/ezo-note-menu-star.svg +5 -0
  303. package/DS1/x-icon/ezo-note-menu-start.svg +6 -0
  304. package/DS1/x-icon/ezo-note-menu-stop.svg +6 -0
  305. package/DS1/x-icon/ezo-note-more.svg +7 -0
  306. package/DS1/x-icon/ezo-note-star.svg +5 -0
  307. package/DS1/x-icon/ezo-password-visibility-see.svg +6 -0
  308. package/DS1/x-icon/ezo-password-visibility-unsee.svg +9 -0
  309. package/DS1/x-icon/ezo-pattern-call-to-action.svg +1 -0
  310. package/DS1/x-icon/ezo-play.svg +5 -0
  311. package/DS1/x-icon/ezo-premium-star.svg +5 -0
  312. package/DS1/x-icon/ezo-setting-1xdots.svg +13 -0
  313. package/DS1/x-icon/ezo-setting-1xgrid.svg +10 -0
  314. package/DS1/x-icon/ezo-setting-1xlines.svg +7 -0
  315. package/DS1/x-icon/ezo-setting-2xdots.svg +8 -0
  316. package/DS1/x-icon/ezo-setting-2xgrid.svg +8 -0
  317. package/DS1/x-icon/ezo-setting-2xlines.svg +6 -0
  318. package/DS1/x-icon/ezo-setting-blank.svg +5 -0
  319. package/DS1/x-icon/ezo-store-chevron.svg +1 -0
  320. package/DS1/x-icon/ezo-upgrade-close.svg +5 -0
  321. package/DS1/x-icon/folder.svg +3 -0
  322. package/DS1/x-icon/form.svg +5 -0
  323. package/DS1/x-icon/gallery.svg +3 -0
  324. package/DS1/x-icon/globe.svg +1 -0
  325. package/DS1/x-icon/google.svg +6 -0
  326. package/DS1/x-icon/group-alt-2.svg +3 -0
  327. package/DS1/x-icon/group-alt.svg +7 -0
  328. package/DS1/x-icon/group.svg +3 -0
  329. package/DS1/x-icon/h1.svg +3 -0
  330. package/DS1/x-icon/h2.svg +3 -0
  331. package/DS1/x-icon/h3.svg +3 -0
  332. package/DS1/x-icon/handle.svg +8 -0
  333. package/DS1/x-icon/head.svg +3 -0
  334. package/DS1/x-icon/help-alt.svg +3 -0
  335. package/DS1/x-icon/help.svg +5 -0
  336. package/DS1/x-icon/home.svg +3 -0
  337. package/DS1/x-icon/icon.svg +4 -0
  338. package/DS1/x-icon/image.svg +5 -0
  339. package/DS1/x-icon/in.svg +4 -0
  340. package/DS1/x-icon/indent.svg +4 -0
  341. package/DS1/x-icon/italic.svg +3 -0
  342. package/DS1/x-icon/item.svg +3 -0
  343. package/DS1/x-icon/left.svg +4 -0
  344. package/DS1/x-icon/life.svg +8 -0
  345. package/DS1/x-icon/link.svg +3 -0
  346. package/DS1/x-icon/list-line.svg +3 -0
  347. package/DS1/x-icon/lists.svg +3 -0
  348. package/DS1/x-icon/lock-filled.svg +3 -0
  349. package/DS1/x-icon/lock.svg +3 -0
  350. package/DS1/x-icon/logo-marquee.svg +5 -0
  351. package/DS1/x-icon/loop.svg +6 -0
  352. package/DS1/x-icon/mention.svg +4 -0
  353. package/DS1/x-icon/menu.svg +3 -0
  354. package/DS1/x-icon/mic.svg +5 -0
  355. package/DS1/x-icon/minimize-alt.svg +4 -0
  356. package/DS1/x-icon/minimize.svg +4 -0
  357. package/DS1/x-icon/money.svg +6 -0
  358. package/DS1/x-icon/more.svg +5 -0
  359. package/DS1/x-icon/navigation-check.svg +1 -0
  360. package/DS1/x-icon/note-alt-2.svg +6 -0
  361. package/DS1/x-icon/note-alt.svg +4 -0
  362. package/DS1/x-icon/note-lines.svg +6 -0
  363. package/DS1/x-icon/note.svg +4 -0
  364. package/DS1/x-icon/notifications.svg +3 -0
  365. package/DS1/x-icon/numbered.svg +4 -0
  366. package/DS1/x-icon/open-alt.svg +3 -0
  367. package/DS1/x-icon/open-source.svg +3 -0
  368. package/DS1/x-icon/open.svg +3 -0
  369. package/DS1/x-icon/outdent.svg +4 -0
  370. package/DS1/x-icon/pad.svg +8 -0
  371. package/DS1/x-icon/page-18.svg +3 -0
  372. package/DS1/x-icon/page-alt.svg +3 -0
  373. package/DS1/x-icon/page-note.svg +1 -0
  374. package/DS1/x-icon/page.svg +3 -0
  375. package/DS1/x-icon/panel.svg +4 -0
  376. package/DS1/x-icon/pattern-call-to-action.svg +1 -0
  377. package/DS1/x-icon/pattern-card.svg +1 -0
  378. package/DS1/x-icon/pattern-feature.svg +1 -0
  379. package/DS1/x-icon/pattern-hero.svg +1 -0
  380. package/DS1/x-icon/pen.svg +4 -0
  381. package/DS1/x-icon/play-alt.svg +3 -0
  382. package/DS1/x-icon/plus.svg +3 -0
  383. package/DS1/x-icon/product-sources.json +2557 -0
  384. package/DS1/x-icon/quote.svg +3 -0
  385. package/DS1/x-icon/redo.svg +3 -0
  386. package/DS1/x-icon/relationships.svg +3 -0
  387. package/DS1/x-icon/rewind-30-alt.svg +4 -0
  388. package/DS1/x-icon/rewind-30.svg +5 -0
  389. package/DS1/x-icon/rewind.svg +5 -0
  390. package/DS1/x-icon/right.svg +3 -0
  391. package/DS1/x-icon/row.svg +3 -0
  392. package/DS1/x-icon/school.svg +4 -0
  393. package/DS1/x-icon/sdown.svg +3 -0
  394. package/DS1/x-icon/search-30.svg +4 -0
  395. package/DS1/x-icon/search.svg +3 -0
  396. package/DS1/x-icon/see-30.svg +4 -0
  397. package/DS1/x-icon/see.svg +4 -0
  398. package/DS1/x-icon/settings.svg +8 -0
  399. package/DS1/x-icon/shared.svg +5 -0
  400. package/DS1/x-icon/slash.svg +3 -0
  401. package/DS1/x-icon/star-alt-2.svg +3 -0
  402. package/DS1/x-icon/star-alt.svg +3 -0
  403. package/DS1/x-icon/star.svg +3 -0
  404. package/DS1/x-icon/stats.svg +3 -0
  405. package/DS1/x-icon/status-alt.svg +11 -0
  406. package/DS1/x-icon/status-filled.svg +26 -0
  407. package/DS1/x-icon/status.svg +10 -0
  408. package/DS1/x-icon/store.svg +4 -0
  409. package/DS1/x-icon/strike.svg +3 -0
  410. package/DS1/x-icon/sup.svg +3 -0
  411. package/DS1/x-icon/table.svg +4 -0
  412. package/DS1/x-icon/template.svg +4 -0
  413. package/DS1/x-icon/templates.svg +4 -0
  414. package/DS1/x-icon/text.svg +3 -0
  415. package/DS1/x-icon/title.svg +3 -0
  416. package/DS1/x-icon/titlestyle.svg +11 -0
  417. package/DS1/x-icon/todo.svg +5 -0
  418. package/DS1/x-icon/toggle.svg +4 -0
  419. package/DS1/x-icon/triangle.svg +3 -0
  420. package/DS1/x-icon/underline.svg +3 -0
  421. package/DS1/x-icon/undo.svg +3 -0
  422. package/DS1/x-icon/ungroup.svg +4 -0
  423. package/DS1/x-icon/unhead.svg +7 -0
  424. package/DS1/x-icon/unicon.svg +6 -0
  425. package/DS1/x-icon/unlock.svg +3 -0
  426. package/DS1/x-icon/unmic.svg +6 -0
  427. package/DS1/x-icon/unsee.svg +5 -0
  428. package/DS1/x-icon/unstar.svg +5 -0
  429. package/DS1/x-icon/untitle.svg +7 -0
  430. package/DS1/x-icon/up-right.svg +3 -0
  431. package/DS1/x-icon/up.svg +3 -0
  432. package/DS1/x-icon/views.svg +4 -0
  433. package/DS1/x-icon/warning.svg +3 -0
  434. package/DS1/x-icon/work.svg +3 -0
  435. package/LICENSE +21 -0
  436. package/README.md +240 -2
  437. package/dist/0-face/control.d.ts +24 -0
  438. package/dist/0-face/control.d.ts.map +1 -0
  439. package/dist/0-face/control.js +117 -0
  440. package/dist/0-face/define.d.ts +3 -0
  441. package/dist/0-face/define.d.ts.map +1 -0
  442. package/dist/0-face/define.js +7 -0
  443. package/dist/0-face/device.d.ts +71 -0
  444. package/dist/0-face/device.d.ts.map +1 -0
  445. package/dist/0-face/device.js +318 -0
  446. package/dist/0-face/grid-fit.d.ts +3 -0
  447. package/dist/0-face/grid-fit.d.ts.map +1 -0
  448. package/dist/0-face/grid-fit.js +73 -0
  449. package/dist/0-face/grid.d.ts +3 -0
  450. package/dist/0-face/grid.d.ts.map +1 -0
  451. package/dist/0-face/grid.js +144 -0
  452. package/dist/0-face/i18n.d.ts +138 -0
  453. package/dist/0-face/i18n.d.ts.map +1 -0
  454. package/dist/0-face/i18n.js +725 -0
  455. package/dist/0-face/language-routing.d.ts +47 -0
  456. package/dist/0-face/language-routing.d.ts.map +1 -0
  457. package/dist/0-face/language-routing.js +91 -0
  458. package/dist/0-face/language-validator.d.ts +11 -0
  459. package/dist/0-face/language-validator.d.ts.map +1 -0
  460. package/dist/0-face/language-validator.js +31 -0
  461. package/dist/0-face/languages.d.ts +53 -0
  462. package/dist/0-face/languages.d.ts.map +1 -0
  463. package/dist/0-face/languages.js +192 -0
  464. package/dist/0-face/layout-inspector.d.ts +6 -0
  465. package/dist/0-face/layout-inspector.d.ts.map +1 -0
  466. package/dist/0-face/layout-inspector.js +197 -0
  467. package/dist/0-face/preferences.d.ts +20 -0
  468. package/dist/0-face/preferences.d.ts.map +1 -0
  469. package/dist/0-face/preferences.js +19 -0
  470. package/dist/0-face/pricing.d.ts +15 -0
  471. package/dist/0-face/pricing.d.ts.map +1 -0
  472. package/dist/0-face/pricing.js +46 -0
  473. package/dist/0-face/scaling.d.ts +10 -0
  474. package/dist/0-face/scaling.d.ts.map +1 -0
  475. package/dist/0-face/scaling.js +14 -0
  476. package/dist/0-face/shared-words.d.ts +14 -0
  477. package/dist/0-face/shared-words.d.ts.map +1 -0
  478. package/dist/0-face/shared-words.js +32 -0
  479. package/dist/0-face/theme.d.ts +4 -0
  480. package/dist/0-face/theme.d.ts.map +1 -0
  481. package/dist/0-face/theme.js +48 -0
  482. package/dist/0-face/tooltips.d.ts +8 -0
  483. package/dist/0-face/tooltips.d.ts.map +1 -0
  484. package/dist/0-face/tooltips.js +90 -0
  485. package/dist/0-face/translations/da.json +63 -0
  486. package/dist/0-face/translations/de.json +63 -0
  487. package/dist/0-face/translations/en.json +63 -0
  488. package/dist/0-face/translations/es.json +63 -0
  489. package/dist/0-face/translations/fi.json +63 -0
  490. package/dist/0-face/translations/fr.json +63 -0
  491. package/dist/0-face/translations/is.json +56 -0
  492. package/dist/0-face/translations/it.json +63 -0
  493. package/dist/0-face/translations/ja.json +63 -0
  494. package/dist/0-face/translations/ko.json +63 -0
  495. package/dist/0-face/translations/nb.json +63 -0
  496. package/dist/0-face/translations/pt-BR.json +63 -0
  497. package/dist/0-face/translations/pt.json +63 -0
  498. package/dist/0-face/translations/sv.json +63 -0
  499. package/dist/0-face/translations/zh.json +63 -0
  500. package/dist/0-face/translations-format.d.ts +62 -0
  501. package/dist/0-face/translations-format.d.ts.map +1 -0
  502. package/dist/0-face/translations-format.js +159 -0
  503. package/dist/1-root/palette.d.ts +31 -0
  504. package/dist/1-root/palette.d.ts.map +1 -0
  505. package/dist/1-root/palette.js +83 -0
  506. package/dist/2-core/ds-avatar.d.ts +82 -0
  507. package/dist/2-core/ds-avatar.d.ts.map +1 -0
  508. package/dist/2-core/ds-avatar.js +102 -0
  509. package/dist/2-core/ds-badge.d.ts +33 -0
  510. package/dist/2-core/ds-badge.d.ts.map +1 -0
  511. package/dist/2-core/ds-badge.js +77 -0
  512. package/dist/2-core/ds-banner.d.ts +68 -0
  513. package/dist/2-core/ds-banner.d.ts.map +1 -0
  514. package/dist/2-core/ds-banner.js +98 -0
  515. package/dist/2-core/ds-button.d.ts +102 -0
  516. package/dist/2-core/ds-button.d.ts.map +1 -0
  517. package/dist/2-core/ds-button.js +133 -0
  518. package/dist/2-core/ds-card.d.ts +39 -0
  519. package/dist/2-core/ds-card.d.ts.map +1 -0
  520. package/dist/2-core/ds-card.js +119 -0
  521. package/dist/2-core/ds-checkbox.d.ts +13 -0
  522. package/dist/2-core/ds-checkbox.d.ts.map +1 -0
  523. package/dist/2-core/ds-checkbox.js +11 -0
  524. package/dist/2-core/ds-color.d.ts +15 -0
  525. package/dist/2-core/ds-color.d.ts.map +1 -0
  526. package/dist/2-core/ds-color.js +30 -0
  527. package/dist/2-core/ds-composer.d.ts +69 -0
  528. package/dist/2-core/ds-composer.d.ts.map +1 -0
  529. package/dist/2-core/ds-composer.js +183 -0
  530. package/dist/2-core/ds-cycle.d.ts +85 -0
  531. package/dist/2-core/ds-cycle.d.ts.map +1 -0
  532. package/dist/2-core/ds-cycle.js +497 -0
  533. package/dist/2-core/ds-date.d.ts +16 -0
  534. package/dist/2-core/ds-date.d.ts.map +1 -0
  535. package/dist/2-core/ds-date.js +15 -0
  536. package/dist/2-core/ds-file-picker.d.ts +65 -0
  537. package/dist/2-core/ds-file-picker.d.ts.map +1 -0
  538. package/dist/2-core/ds-file-picker.js +80 -0
  539. package/dist/2-core/ds-gap.d.ts +27 -0
  540. package/dist/2-core/ds-gap.d.ts.map +1 -0
  541. package/dist/2-core/ds-gap.js +22 -0
  542. package/dist/2-core/ds-icon.d.ts +58 -0
  543. package/dist/2-core/ds-icon.d.ts.map +1 -0
  544. package/dist/2-core/ds-icon.js +128 -0
  545. package/dist/2-core/ds-input.d.ts +97 -0
  546. package/dist/2-core/ds-input.d.ts.map +1 -0
  547. package/dist/2-core/ds-input.js +429 -0
  548. package/dist/2-core/ds-language.d.ts +75 -0
  549. package/dist/2-core/ds-language.d.ts.map +1 -0
  550. package/dist/2-core/ds-language.js +74 -0
  551. package/dist/2-core/ds-pagedots.d.ts +32 -0
  552. package/dist/2-core/ds-pagedots.d.ts.map +1 -0
  553. package/dist/2-core/ds-pagedots.js +36 -0
  554. package/dist/2-core/ds-radio.d.ts +35 -0
  555. package/dist/2-core/ds-radio.d.ts.map +1 -0
  556. package/dist/2-core/ds-radio.js +19 -0
  557. package/dist/2-core/ds-select.d.ts +30 -0
  558. package/dist/2-core/ds-select.d.ts.map +1 -0
  559. package/dist/2-core/ds-select.js +45 -0
  560. package/dist/2-core/ds-split.d.ts +75 -0
  561. package/dist/2-core/ds-split.d.ts.map +1 -0
  562. package/dist/2-core/ds-split.js +101 -0
  563. package/dist/2-core/ds-swatch.d.ts +24 -0
  564. package/dist/2-core/ds-swatch.d.ts.map +1 -0
  565. package/dist/2-core/ds-swatch.js +16 -0
  566. package/dist/2-core/ds-switch.d.ts +13 -0
  567. package/dist/2-core/ds-switch.d.ts.map +1 -0
  568. package/dist/2-core/ds-switch.js +10 -0
  569. package/dist/2-core/ds-text.d.ts +75 -0
  570. package/dist/2-core/ds-text.d.ts.map +1 -0
  571. package/dist/2-core/ds-text.js +142 -0
  572. package/dist/2-core/ds-textarea.d.ts +46 -0
  573. package/dist/2-core/ds-textarea.d.ts.map +1 -0
  574. package/dist/2-core/ds-textarea.js +121 -0
  575. package/dist/2-core/ds-token.d.ts +48 -0
  576. package/dist/2-core/ds-token.d.ts.map +1 -0
  577. package/dist/2-core/ds-token.js +44 -0
  578. package/dist/2-core/ds-tooltip.d.ts +48 -0
  579. package/dist/2-core/ds-tooltip.d.ts.map +1 -0
  580. package/dist/2-core/ds-tooltip.js +99 -0
  581. package/dist/2-core/generated/ds-icon-map.d.ts +295 -0
  582. package/dist/2-core/generated/ds-icon-map.d.ts.map +1 -0
  583. package/dist/2-core/generated/ds-icon-map.js +579 -0
  584. package/dist/2-core/generated/icons-extra.d.ts +2 -0
  585. package/dist/2-core/generated/icons-extra.d.ts.map +1 -0
  586. package/dist/2-core/generated/icons-extra.js +243 -0
  587. package/dist/2-core/generated/icons-immediate.d.ts +3 -0
  588. package/dist/2-core/generated/icons-immediate.d.ts.map +1 -0
  589. package/dist/2-core/generated/icons-immediate.js +49 -0
  590. package/dist/3-unit/context-trigger.d.ts +22 -0
  591. package/dist/3-unit/context-trigger.d.ts.map +1 -0
  592. package/dist/3-unit/context-trigger.js +130 -0
  593. package/dist/3-unit/ds-accordion.d.ts +55 -0
  594. package/dist/3-unit/ds-accordion.d.ts.map +1 -0
  595. package/dist/3-unit/ds-accordion.js +105 -0
  596. package/dist/3-unit/ds-avatar-group.d.ts +40 -0
  597. package/dist/3-unit/ds-avatar-group.d.ts.map +1 -0
  598. package/dist/3-unit/ds-avatar-group.js +84 -0
  599. package/dist/3-unit/ds-form.d.ts +70 -0
  600. package/dist/3-unit/ds-form.d.ts.map +1 -0
  601. package/dist/3-unit/ds-form.js +232 -0
  602. package/dist/3-unit/ds-list.d.ts +25 -0
  603. package/dist/3-unit/ds-list.d.ts.map +1 -0
  604. package/dist/3-unit/ds-list.js +25 -0
  605. package/dist/3-unit/ds-menu.d.ts +162 -0
  606. package/dist/3-unit/ds-menu.d.ts.map +1 -0
  607. package/dist/3-unit/ds-menu.js +607 -0
  608. package/dist/3-unit/ds-row.d.ts +43 -0
  609. package/dist/3-unit/ds-row.d.ts.map +1 -0
  610. package/dist/3-unit/ds-row.js +32 -0
  611. package/dist/3-unit/ds-table.d.ts +33 -0
  612. package/dist/3-unit/ds-table.d.ts.map +1 -0
  613. package/dist/3-unit/ds-table.js +43 -0
  614. package/dist/3-unit/ds-tabs.d.ts +50 -0
  615. package/dist/3-unit/ds-tabs.d.ts.map +1 -0
  616. package/dist/3-unit/ds-tabs.js +64 -0
  617. package/dist/3-unit/ds-token-field.d.ts +64 -0
  618. package/dist/3-unit/ds-token-field.d.ts.map +1 -0
  619. package/dist/3-unit/ds-token-field.js +184 -0
  620. package/dist/4-page/ds-container.d.ts +17 -0
  621. package/dist/4-page/ds-container.d.ts.map +1 -0
  622. package/dist/4-page/ds-container.js +11 -0
  623. package/dist/4-page/ds-dialog.d.ts +105 -0
  624. package/dist/4-page/ds-dialog.d.ts.map +1 -0
  625. package/dist/4-page/ds-dialog.js +358 -0
  626. package/dist/4-page/ds-grid.d.ts +28 -0
  627. package/dist/4-page/ds-grid.d.ts.map +1 -0
  628. package/dist/4-page/ds-grid.js +49 -0
  629. package/dist/4-page/ds-layout.d.ts +31 -0
  630. package/dist/4-page/ds-layout.d.ts.map +1 -0
  631. package/dist/4-page/ds-layout.js +930 -0
  632. package/dist/4-page/ds-panel.d.ts +26 -0
  633. package/dist/4-page/ds-panel.d.ts.map +1 -0
  634. package/dist/4-page/ds-panel.js +29 -0
  635. package/dist/4-page/email.d.ts +49 -0
  636. package/dist/4-page/email.d.ts.map +1 -0
  637. package/dist/4-page/email.js +134 -0
  638. package/dist/components.d.ts +14 -0
  639. package/dist/components.d.ts.map +1 -0
  640. package/dist/components.js +13 -0
  641. package/dist/design-system-1.bundle.js +9115 -0
  642. package/dist/design-system-1.bundle.js.map +1 -0
  643. package/dist/design-system-1.bundle.min.js +6879 -0
  644. package/dist/design-system-1.bundle.min.js.map +1 -0
  645. package/dist/design-system-1.css +2 -0
  646. package/dist/icons-extra-BoAACA4X.js +944 -0
  647. package/dist/icons-extra-BoAACA4X.js.map +1 -0
  648. package/dist/icons-extra-DlxTo_Y4.js +247 -0
  649. package/dist/icons-extra-DlxTo_Y4.js.map +1 -0
  650. package/dist/index.d.ts +57 -0
  651. package/dist/index.d.ts.map +1 -0
  652. package/dist/index.js +73 -0
  653. package/dist/react/accordion.d.ts +21 -0
  654. package/dist/react/accordion.d.ts.map +1 -0
  655. package/dist/react/accordion.js +30 -0
  656. package/dist/react/avatar-group.d.ts +6 -0
  657. package/dist/react/avatar-group.d.ts.map +1 -0
  658. package/dist/react/avatar-group.js +5 -0
  659. package/dist/react/avatar.d.ts +13 -0
  660. package/dist/react/avatar.d.ts.map +1 -0
  661. package/dist/react/avatar.js +18 -0
  662. package/dist/react/badge.d.ts +11 -0
  663. package/dist/react/badge.d.ts.map +1 -0
  664. package/dist/react/badge.js +4 -0
  665. package/dist/react/button.d.ts +13 -0
  666. package/dist/react/button.d.ts.map +1 -0
  667. package/dist/react/button.js +37 -0
  668. package/dist/react/choice.d.ts +43 -0
  669. package/dist/react/choice.d.ts.map +1 -0
  670. package/dist/react/choice.js +32 -0
  671. package/dist/react/composer.d.ts +17 -0
  672. package/dist/react/composer.d.ts.map +1 -0
  673. package/dist/react/composer.js +8 -0
  674. package/dist/react/context-menu.d.ts +657 -0
  675. package/dist/react/context-menu.d.ts.map +1 -0
  676. package/dist/react/context-menu.js +133 -0
  677. package/dist/react/dialog.d.ts +1173 -0
  678. package/dist/react/dialog.d.ts.map +1 -0
  679. package/dist/react/dialog.js +79 -0
  680. package/dist/react/file-picker.d.ts +29 -0
  681. package/dist/react/file-picker.d.ts.map +1 -0
  682. package/dist/react/file-picker.js +17 -0
  683. package/dist/react/input.d.ts +35 -0
  684. package/dist/react/input.d.ts.map +1 -0
  685. package/dist/react/input.js +128 -0
  686. package/dist/react/menu.d.ts +37 -0
  687. package/dist/react/menu.d.ts.map +1 -0
  688. package/dist/react/menu.js +31 -0
  689. package/dist/react/split.d.ts +15 -0
  690. package/dist/react/split.d.ts.map +1 -0
  691. package/dist/react/split.js +32 -0
  692. package/dist/react/token-field.d.ts +36 -0
  693. package/dist/react/token-field.d.ts.map +1 -0
  694. package/dist/react/token-field.js +33 -0
  695. package/dist/shared-words-Cg6Phtx0.js +915 -0
  696. package/dist/shared-words-Cg6Phtx0.js.map +1 -0
  697. package/dist/shared-words-Ddz0yUEF.js +933 -0
  698. package/dist/shared-words-Ddz0yUEF.js.map +1 -0
  699. package/package.json +149 -3
@@ -0,0 +1,1023 @@
1
+ import {
2
+ LANGUAGES,
3
+ LANGUAGE_METADATA,
4
+ fallbackLanguages,
5
+ isLanguage,
6
+ languageOrder,
7
+ normalizeLanguage,
8
+ } from "./languages";
9
+ import type { Language } from "./languages";
10
+ import { configureLanguageRouting } from "./language-routing";
11
+ import type { LanguageRoutingOptions } from "./language-routing";
12
+ import { readLanguageFile, resolveText } from "./translations-format";
13
+ import type {
14
+ TranslationParams,
15
+ TranslationTable,
16
+ TranslationsFile,
17
+ } from "./translations-format";
18
+
19
+ // Define the supported languages using ISO codes (extensible)
20
+ export type LanguageCode = string;
21
+
22
+ // One language's words, key to text
23
+ export type TranslationData = {
24
+ [key: string]: string;
25
+ };
26
+
27
+ // Every language's words, keyed by language code
28
+ export type TranslationMap = Record<string, TranslationData>;
29
+
30
+ /**
31
+ * How a product sets up the language module. Every field is optional: without
32
+ * any, the module behaves as it always has (the whole table from
33
+ * `./translations.json`, the languages offered are the ones in the words).
34
+ *
35
+ * Pass it to `configureI18n()` straight after importing the module, or put it
36
+ * on `window.DS_ONE_I18N` before the module loads.
37
+ */
38
+ export type I18nOptions = {
39
+ /** Fetch external words at startup. Disable when the product supplies them in code. */
40
+ autoLoad?: boolean;
41
+ /**
42
+ * Every language the product speaks. When set, the picker offers these
43
+ * whether or not their words have arrived yet, which matters when a page
44
+ * loads only its own language (`languageFile`). Without it, the languages
45
+ * offered are the ones found in the loaded words.
46
+ */
47
+ languages?: readonly LanguageCode[];
48
+ /**
49
+ * Where one language's words are published, so a page loads its own
50
+ * language rather than the whole table: a pattern with `{language}` in it
51
+ * (`"/translations/{language}.json"`) or a function. Each file holds
52
+ * `{ "<language>": { …words } }`. When a language's file is missing, the
53
+ * whole table is the fallback. Without it, only the whole table is loaded.
54
+ */
55
+ languageFile?: string | ((language: LanguageCode) => string) | null;
56
+ /**
57
+ * Where the whole table is published. Defaults to `./translations.json`.
58
+ * `window.DS_ONE_TRANSLATIONS_FILE` and the `data-ds-one-translations`,
59
+ * `ds-one:translations` and `ds-one-translations` markers in the page win
60
+ * over it.
61
+ */
62
+ translationsFile?: string;
63
+ /** The localStorage key that keeps the chosen language. Defaults to `language`. */
64
+ storageKey?: string;
65
+ /**
66
+ * Report keys with no translation on the console, each once. Off by
67
+ * default, since components also show what people write themselves, and
68
+ * that is not a key anyone forgot. `window.DS_ONE_I18N_DEBUG = true` or
69
+ * localStorage `ds-one:i18n-debug` set to `on` turn it on as well.
70
+ */
71
+ reportMissingKeys?: boolean | (() => boolean);
72
+ /**
73
+ * Load Design system 1's shared interface words (buttons, sign-in, empty
74
+ * states, dates, language names) beneath the product's own words, so the
75
+ * product's text wins where both have a key. Off by default.
76
+ */
77
+ sharedWords?: boolean;
78
+ /**
79
+ * Which languages get an address prefix (`/ja/…`) and whether the default
80
+ * language has one; see `configureLanguageRouting`. Without `languages`
81
+ * here, the product's `languages` get a prefix.
82
+ */
83
+ routing?: LanguageRoutingOptions;
84
+ };
85
+
86
+ // Bundled translations (keys.json may not exist - will use external translations if not available)
87
+ // This is a fallback for when external translations aren't loaded
88
+ let translationKeys: TranslationMap = {};
89
+
90
+ /**
91
+ * Every language Design system 1 orders, names and maps browser languages to:
92
+ * the list in `./languages` (`LANGUAGES`). A product turns on the ones it
93
+ * translates with `configureI18n({ languages })`.
94
+ */
95
+ export const SUPPORTED_LANGUAGES: readonly Language[] = LANGUAGES;
96
+
97
+ const DISPLAY_NAME_CACHE = new Map<string, Intl.DisplayNames>();
98
+ let displayNameFallbackWarningShown = false;
99
+
100
+ // Declare window property for external translations
101
+ declare global {
102
+ interface Window {
103
+ DS_ONE_TRANSLATIONS?: Record<string, TranslationData>;
104
+ DS_ONE_TRANSLATIONS_FILE?: string;
105
+ DS_ONE_I18N?: I18nOptions;
106
+ DS_ONE_I18N_DEBUG?: boolean;
107
+ }
108
+ }
109
+
110
+ const DEFAULT_TRANSLATION_FILE = "./translations.json";
111
+ const DEFAULT_STORAGE_KEY = "language";
112
+ const DEBUG_STORAGE_KEY = "ds-one:i18n-debug";
113
+
114
+ const defaultLanguage: LanguageCode = "en";
115
+
116
+ let i18nOptions: I18nOptions =
117
+ typeof window !== "undefined" && window.DS_ONE_I18N
118
+ ? { ...window.DS_ONE_I18N }
119
+ : {};
120
+
121
+ // Design system 1's shared words, kept apart from the product's so a product's
122
+ // file arriving never wipes them, and looked up beneath the product's words.
123
+ let sharedWords: TranslationsFile | undefined;
124
+
125
+ // ============================================================================
126
+ // Storage
127
+ // ============================================================================
128
+
129
+ function storage(): Storage | undefined {
130
+ try {
131
+ return typeof window !== "undefined"
132
+ ? (window.localStorage ?? undefined)
133
+ : undefined;
134
+ } catch {
135
+ // Storage can be blocked (private windows, cleared site data)
136
+ return undefined;
137
+ }
138
+ }
139
+
140
+ function storageKey(): string {
141
+ return i18nOptions.storageKey || DEFAULT_STORAGE_KEY;
142
+ }
143
+
144
+ function readStoredLanguage(): LanguageCode | null {
145
+ try {
146
+ return (storage()?.getItem(storageKey()) as LanguageCode | null) || null;
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+
152
+ function storeLanguage(language: LanguageCode): void {
153
+ try {
154
+ storage()?.setItem(storageKey(), language);
155
+ } catch {
156
+ // The choice still holds for this page
157
+ }
158
+ }
159
+
160
+ // ============================================================================
161
+ // Language codes
162
+ // ============================================================================
163
+
164
+ function extractPrimarySubtag(code: LanguageCode): string {
165
+ if (!code) {
166
+ return "";
167
+ }
168
+ return code.toLowerCase().split(/[-_]/)[0] ?? "";
169
+ }
170
+
171
+ /**
172
+ * The language whose file a page loads for `language`: one of the configured
173
+ * languages when a list is set (null when it speaks none of them), otherwise
174
+ * the primary subtag.
175
+ */
176
+ function fileLanguage(language: LanguageCode): LanguageCode | null {
177
+ const configured = i18nOptions.languages;
178
+ if (!configured || configured.length === 0) {
179
+ return extractPrimarySubtag(language) || null;
180
+ }
181
+
182
+ const normalized = (language || "").toLowerCase().replace("_", "-");
183
+ const exact = configured.find((code) => code.toLowerCase() === normalized);
184
+ if (exact) {
185
+ return exact;
186
+ }
187
+
188
+ const primary = extractPrimarySubtag(language);
189
+ return configured.find((code) => code.toLowerCase() === primary) ?? null;
190
+ }
191
+
192
+ function languageFileFor(language: LanguageCode): string | null {
193
+ const pattern = i18nOptions.languageFile;
194
+ if (!pattern) {
195
+ return null;
196
+ }
197
+ const path =
198
+ typeof pattern === "function"
199
+ ? pattern(language)
200
+ : pattern.split("{language}").join(language);
201
+ return normalizeCandidate(path);
202
+ }
203
+
204
+ // ============================================================================
205
+ // Loading translations
206
+ // ============================================================================
207
+
208
+ // CDN Loader: Automatically detects and loads translation JSON files
209
+ // for CDN users who want to use external translations
210
+
211
+ let loadAttempted = false;
212
+
213
+ function normalizeCandidate(path: string): string | null {
214
+ if (!path) {
215
+ return null;
216
+ }
217
+ const trimmed = path.trim();
218
+ if (!trimmed) {
219
+ return null;
220
+ }
221
+
222
+ if (
223
+ trimmed.startsWith("./") ||
224
+ trimmed.startsWith("../") ||
225
+ trimmed.startsWith("/") ||
226
+ /^https?:\/\//i.test(trimmed)
227
+ ) {
228
+ return trimmed;
229
+ }
230
+
231
+ return `./${trimmed}`;
232
+ }
233
+
234
+ function findAttributeCandidate(): string | null {
235
+ if (typeof document === "undefined" || !document.querySelector) {
236
+ return null;
237
+ }
238
+
239
+ const scriptWithAttribute = document.querySelector(
240
+ "script[data-ds-one-translations]"
241
+ );
242
+ const scriptCandidate = scriptWithAttribute?.getAttribute(
243
+ "data-ds-one-translations"
244
+ );
245
+ if (scriptCandidate) {
246
+ return scriptCandidate;
247
+ }
248
+
249
+ const metaCandidate = document
250
+ .querySelector('meta[name="ds-one:translations"]')
251
+ ?.getAttribute("content");
252
+ if (metaCandidate) {
253
+ return metaCandidate;
254
+ }
255
+
256
+ const linkCandidate = document
257
+ .querySelector('link[rel="ds-one-translations"]')
258
+ ?.getAttribute("href");
259
+ if (linkCandidate) {
260
+ return linkCandidate;
261
+ }
262
+
263
+ return null;
264
+ }
265
+
266
+ /** Where the whole table is: what the page names, else the option, else the default. */
267
+ function resolveTableSources(): string[] {
268
+ const candidates: string[] = [];
269
+
270
+ const windowCandidate =
271
+ typeof window !== "undefined" ? window.DS_ONE_TRANSLATIONS_FILE : null;
272
+ const attributeCandidate = findAttributeCandidate();
273
+
274
+ // Only use explicitly configured paths, or the single default
275
+ const windowNormalized = normalizeCandidate(windowCandidate ?? "");
276
+ if (windowNormalized) {
277
+ candidates.push(windowNormalized);
278
+ }
279
+
280
+ const attrNormalized = normalizeCandidate(attributeCandidate ?? "");
281
+ if (attrNormalized && !candidates.includes(attrNormalized)) {
282
+ candidates.push(attrNormalized);
283
+ }
284
+
285
+ // Only try the default if no explicit path was configured
286
+ if (candidates.length === 0) {
287
+ candidates.push(
288
+ normalizeCandidate(i18nOptions.translationsFile ?? "") ??
289
+ DEFAULT_TRANSLATION_FILE
290
+ );
291
+ }
292
+
293
+ return candidates;
294
+ }
295
+
296
+ function resolveTranslationSources(): string[] {
297
+ const tables = resolveTableSources();
298
+ // With one file per language, the page's own language first, then the
299
+ // whole table.
300
+ const startLanguage =
301
+ fileLanguage(currentLanguage.value) ?? defaultLanguage;
302
+ const own = languageFileFor(startLanguage);
303
+ return own ? [own, ...tables.filter((source) => source !== own)] : tables;
304
+ }
305
+
306
+ /** The language whose own file `source` is, when it is one. */
307
+ function languageOfSource(source: string): LanguageCode | undefined {
308
+ const start = fileLanguage(currentLanguage.value) ?? defaultLanguage;
309
+ return languageFileFor(start) === source ? start : undefined;
310
+ }
311
+
312
+ function validateTranslationMap(
313
+ candidate: unknown
314
+ ): candidate is TranslationMap {
315
+ if (!candidate || typeof candidate !== "object") {
316
+ return false;
317
+ }
318
+
319
+ return Object.values(candidate).every(
320
+ (entry) => entry && typeof entry === "object"
321
+ );
322
+ }
323
+
324
+ // One request per file, shared by startup and a language switch alike. A file
325
+ // that failed is forgotten, so a later attempt asks again.
326
+ const translationFiles = new Map<string, Promise<TranslationMap | null>>();
327
+
328
+ function fetchTranslationFile(
329
+ source: string,
330
+ language?: LanguageCode
331
+ ): Promise<TranslationMap | null> {
332
+ let pending = translationFiles.get(source);
333
+ if (!pending) {
334
+ pending = readTranslationFile(source, language).then((translations) => {
335
+ if (!translations) {
336
+ translationFiles.delete(source);
337
+ }
338
+ return translations;
339
+ });
340
+ translationFiles.set(source, pending);
341
+ }
342
+ return pending;
343
+ }
344
+
345
+ async function readTranslationFile(
346
+ source: string,
347
+ language?: LanguageCode
348
+ ): Promise<TranslationMap | null> {
349
+ try {
350
+ const response = await fetch(source);
351
+
352
+ if (!response.ok) {
353
+ // 404 is expected if no translations file exists - don't log as error
354
+ return null;
355
+ }
356
+
357
+ const json: unknown = await response.json();
358
+ // A language's own file may hold its table bare ({ "Save": "Gem" })
359
+ const translations = language
360
+ ? (readLanguageFile(language, json) ?? json)
361
+ : json;
362
+
363
+ if (!validateTranslationMap(translations)) {
364
+ console.warn(
365
+ `[Design system 1] Invalid translation format in ${source}. Expected object with language codes as keys.`
366
+ );
367
+ return null;
368
+ }
369
+
370
+ const languages = Object.keys(translations);
371
+ if (languages.length === 0) {
372
+ console.warn(`[Design system 1] No languages found in ${source}`);
373
+ return null;
374
+ }
375
+
376
+ return translations;
377
+ } catch {
378
+ // Silently fail - file likely doesn't exist or isn't valid JSON
379
+ return null;
380
+ }
381
+ }
382
+
383
+ /** Add words to what is loaded, keeping the languages already there. */
384
+ function mergeTranslations(translations: TranslationMap): void {
385
+ if (typeof window === "undefined") {
386
+ translationKeys = { ...translationKeys, ...translations };
387
+ return;
388
+ }
389
+ window.DS_ONE_TRANSLATIONS = {
390
+ ...window.DS_ONE_TRANSLATIONS,
391
+ ...translations,
392
+ };
393
+ }
394
+
395
+ /**
396
+ * Attempts to load translations from a JSON file in the same directory
397
+ */
398
+ async function loadExternalTranslations(): Promise<boolean> {
399
+ // Only attempt once
400
+ if (loadAttempted) {
401
+ return false;
402
+ }
403
+ loadAttempted = true;
404
+
405
+ if (typeof window === "undefined") {
406
+ return false;
407
+ }
408
+
409
+ // Check if translations are already loaded (e.g., by the application)
410
+ if (
411
+ window.DS_ONE_TRANSLATIONS &&
412
+ Object.keys(window.DS_ONE_TRANSLATIONS).length > 0
413
+ ) {
414
+ debugLog(
415
+ `Translations already loaded (${Object.keys(window.DS_ONE_TRANSLATIONS).length} languages), skipping auto-load`
416
+ );
417
+ return true;
418
+ }
419
+
420
+ const sources = resolveTranslationSources();
421
+
422
+ for (const source of sources) {
423
+ const translations = await fetchTranslationFile(
424
+ source,
425
+ languageOfSource(source)
426
+ );
427
+ if (!translations) {
428
+ continue;
429
+ }
430
+
431
+ mergeTranslations(translations);
432
+
433
+ const languages = Object.keys(translations);
434
+ debugLog(
435
+ `External translations loaded from ${source}: ${languages.length} language(s) – ${languages.join(", ")}`
436
+ );
437
+
438
+ window.dispatchEvent(new CustomEvent("translations-ready"));
439
+ return true;
440
+ }
441
+
442
+ debugLog(
443
+ `No external translations found at ${sources[0] ?? DEFAULT_TRANSLATION_FILE}. Using bundled translations.`
444
+ );
445
+
446
+ return false;
447
+ }
448
+
449
+ const languageLoads = new Map<string, Promise<void>>();
450
+
451
+ /**
452
+ * Fetch a language's words when the page needs one it has not got: its own
453
+ * file, or the whole table when that file is missing. Does nothing unless
454
+ * `languageFile` is configured, since otherwise the whole table, with every
455
+ * language in it, is what the page loads at startup.
456
+ */
457
+ export function loadLanguage(language: LanguageCode): Promise<void> {
458
+ if (typeof window === "undefined" || !i18nOptions.languageFile) {
459
+ return Promise.resolve();
460
+ }
461
+
462
+ const code = fileLanguage(language) ?? defaultLanguage;
463
+ if (getTranslationData()[code]) {
464
+ return Promise.resolve();
465
+ }
466
+
467
+ let pending = languageLoads.get(code);
468
+ if (!pending) {
469
+ pending = (async () => {
470
+ const own = languageFileFor(code);
471
+ let translations = own ? await fetchTranslationFile(own, code) : null;
472
+ for (const source of resolveTableSources()) {
473
+ if (translations) {
474
+ break;
475
+ }
476
+ translations = await fetchTranslationFile(source);
477
+ }
478
+ if (!translations) {
479
+ return;
480
+ }
481
+ mergeTranslations(translations);
482
+ window.dispatchEvent(new CustomEvent("translations-ready"));
483
+ })();
484
+ languageLoads.set(code, pending);
485
+ }
486
+ return pending;
487
+ }
488
+
489
+ // Get translation data - prioritize external, fall back to bundled
490
+ function getTranslationData(): TranslationMap {
491
+ // Check for externally loaded translations first (CDN usage)
492
+ if (typeof window !== "undefined" && window.DS_ONE_TRANSLATIONS) {
493
+ return window.DS_ONE_TRANSLATIONS;
494
+ }
495
+
496
+ // Fall back to bundled translations
497
+ return translationKeys;
498
+ }
499
+
500
+ // ============================================================================
501
+ // Language names and ordering
502
+ // ============================================================================
503
+
504
+ function sortLanguageCodes(codes: LanguageCode[]): LanguageCode[] {
505
+ return [...codes].sort((a, b) => {
506
+ const priorityA = languageOrder(a);
507
+ const priorityB = languageOrder(b);
508
+
509
+ if (priorityA !== priorityB) {
510
+ return priorityA - priorityB;
511
+ }
512
+
513
+ return a.localeCompare(b);
514
+ });
515
+ }
516
+
517
+ function getDisplayNameForLocale(
518
+ locale: string,
519
+ code: LanguageCode
520
+ ): string | undefined {
521
+ const normalizedLocale = locale?.replace("_", "-");
522
+ if (!normalizedLocale) {
523
+ return undefined;
524
+ }
525
+
526
+ try {
527
+ let displayNames = DISPLAY_NAME_CACHE.get(normalizedLocale);
528
+
529
+ if (!displayNames) {
530
+ displayNames = new Intl.DisplayNames([normalizedLocale], {
531
+ type: "language",
532
+ });
533
+ DISPLAY_NAME_CACHE.set(normalizedLocale, displayNames);
534
+ }
535
+
536
+ const cleanedCode = code.replace("_", "-");
537
+
538
+ // Try full tag first
539
+ const fullMatch = displayNames.of(cleanedCode);
540
+ if (fullMatch && fullMatch !== cleanedCode) {
541
+ return fullMatch;
542
+ }
543
+
544
+ // Then the primary subtag
545
+ const baseMatch = displayNames.of(extractPrimarySubtag(cleanedCode));
546
+ if (baseMatch) {
547
+ return baseMatch;
548
+ }
549
+ } catch {
550
+ // Intl.DisplayNames may not be supported; fall back gracefully
551
+ if (!displayNameFallbackWarningShown) {
552
+ console.info(
553
+ "[Design system 1] Intl.DisplayNames is not available, using fallback language names."
554
+ );
555
+ displayNameFallbackWarningShown = true;
556
+ }
557
+ }
558
+
559
+ return undefined;
560
+ }
561
+
562
+ // English names from the language list, for when Intl.DisplayNames is missing
563
+ function getFallbackDisplayName(code: LanguageCode): string | undefined {
564
+ const language = normalizeLanguage(code, null);
565
+ return language ? LANGUAGE_METADATA[language].label : undefined;
566
+ }
567
+
568
+ export function getLanguageDisplayName(
569
+ code: LanguageCode,
570
+ options: { locale?: string } = {}
571
+ ): string {
572
+ if (!code) {
573
+ return "";
574
+ }
575
+
576
+ const localesToTry: string[] = [];
577
+
578
+ if (options.locale) {
579
+ localesToTry.push(options.locale);
580
+ }
581
+
582
+ if (typeof navigator !== "undefined") {
583
+ if (Array.isArray(navigator.languages)) {
584
+ localesToTry.push(...navigator.languages);
585
+ }
586
+ if (navigator.language) {
587
+ localesToTry.push(navigator.language);
588
+ }
589
+ }
590
+
591
+ localesToTry.push(defaultLanguage);
592
+ localesToTry.push("en");
593
+
594
+ const tried = new Set<string>();
595
+
596
+ for (const locale of localesToTry) {
597
+ if (!locale || tried.has(locale)) {
598
+ continue;
599
+ }
600
+ tried.add(locale);
601
+
602
+ const displayName = getDisplayNameForLocale(locale, code);
603
+ if (displayName) {
604
+ return displayName;
605
+ }
606
+ }
607
+
608
+ const fallback = getFallbackDisplayName(code);
609
+ if (fallback) {
610
+ return fallback;
611
+ }
612
+
613
+ // Fall back to the primary subtag in uppercase
614
+ const primary = extractPrimarySubtag(code);
615
+ return primary ? primary.toUpperCase() : code;
616
+ }
617
+
618
+ // ============================================================================
619
+ // Browser language
620
+ // ============================================================================
621
+
622
+ /** The languages a browser's language may become: the product's, else the list. */
623
+ function configuredLanguages(): readonly LanguageCode[] | null {
624
+ return i18nOptions.languages?.length ? i18nOptions.languages : null;
625
+ }
626
+
627
+ /**
628
+ * A browser language tag as one the product speaks: within the configured
629
+ * languages when they are set (a relative standing in, so `no` reads `nb` for
630
+ * a product that speaks `nb`), else within the whole list. Null when none fits.
631
+ */
632
+ function resolvePreferredLanguage(languageTag: string): LanguageCode | null {
633
+ if (!languageTag) {
634
+ return null;
635
+ }
636
+
637
+ const configured = configuredLanguages();
638
+ const members = configured
639
+ ? configured.filter(isLanguage)
640
+ : (LANGUAGES as readonly Language[]);
641
+
642
+ const match = members.length
643
+ ? normalizeLanguage(languageTag, null, members)
644
+ : null;
645
+ if (match) {
646
+ return match;
647
+ }
648
+
649
+ // A configured language outside the list (`nl`), by its primary subtag
650
+ return configured ? fileLanguage(languageTag) : null;
651
+ }
652
+
653
+ /**
654
+ * The browser's language, as one the product speaks: the first of the
655
+ * browser's languages that fits. When none does, a product that configured
656
+ * its languages gets English; one that configured nothing gets the browser's
657
+ * own tag, since its words may still be loaded under it.
658
+ */
659
+ export function getBrowserLanguage(): LanguageCode {
660
+ if (typeof navigator === "undefined" || !navigator) {
661
+ return defaultLanguage;
662
+ }
663
+
664
+ const candidates = [
665
+ navigator.language,
666
+ ...(Array.isArray(navigator.languages) ? navigator.languages : []),
667
+ ].filter((tag): tag is string => Boolean(tag));
668
+
669
+ for (const candidate of candidates) {
670
+ const resolved = resolvePreferredLanguage(candidate);
671
+ if (resolved) {
672
+ return resolved;
673
+ }
674
+ }
675
+
676
+ if (!configuredLanguages() && candidates[0]) {
677
+ return candidates[0];
678
+ }
679
+ return defaultLanguage;
680
+ }
681
+
682
+ // ============================================================================
683
+ // Current language
684
+ // ============================================================================
685
+
686
+ function dispatchLanguageChanged(language: LanguageCode): void {
687
+ if (typeof window === "undefined") {
688
+ return;
689
+ }
690
+ window.dispatchEvent(
691
+ new CustomEvent("language-changed", {
692
+ detail: { language },
693
+ bubbles: true,
694
+ composed: true,
695
+ })
696
+ );
697
+ }
698
+
699
+ // Create a reactive signal for the current language (Portfolio pattern)
700
+ export const currentLanguage = {
701
+ value: readStoredLanguage() || getBrowserLanguage(),
702
+ set: function (lang: LanguageCode) {
703
+ this.value = lang;
704
+ storeLanguage(lang);
705
+ void loadLanguage(lang);
706
+ // What reads the document's language (dates, screen readers, hyphenation)
707
+ // follows too, rather than keeping the language the page was opened in.
708
+ if (typeof document !== "undefined" && document.documentElement) {
709
+ document.documentElement.lang = lang;
710
+ }
711
+ dispatchLanguageChanged(lang);
712
+ },
713
+ };
714
+
715
+ /**
716
+ * Set the language module up for a product (see `I18nOptions`). Call it
717
+ * straight after importing the module: the first translations are fetched
718
+ * once the code that imported it has run. Called later, it fetches the current
719
+ * language's file if one is now configured.
720
+ */
721
+ export function configureI18n(next: I18nOptions): void {
722
+ const previousKey = storageKey();
723
+ const previousLanguages = i18nOptions.languages;
724
+ i18nOptions = { ...i18nOptions, ...next };
725
+
726
+ applyRouting();
727
+
728
+ if (
729
+ storageKey() !== previousKey ||
730
+ i18nOptions.languages !== previousLanguages
731
+ ) {
732
+ // The stored choice under the new key, or the browser's language, as one
733
+ // the product speaks
734
+ const stored = readStoredLanguage();
735
+ let language: LanguageCode = stored || getBrowserLanguage();
736
+ if (stored && configuredLanguages()) {
737
+ language = resolvePreferredLanguage(stored) ?? defaultLanguage;
738
+ }
739
+ if (language !== currentLanguage.value) {
740
+ currentLanguage.value = language;
741
+ dispatchLanguageChanged(language);
742
+ }
743
+ }
744
+
745
+ if (next.sharedWords) {
746
+ void loadSharedWords();
747
+ }
748
+
749
+ if (loadAttempted && i18nOptions.languageFile) {
750
+ void loadLanguage(currentLanguage.value);
751
+ }
752
+ }
753
+
754
+ /** Address prefixes follow the product's languages unless routing names its own. */
755
+ function applyRouting(): void {
756
+ if (!i18nOptions.routing && !i18nOptions.languages) {
757
+ return;
758
+ }
759
+ configureLanguageRouting({
760
+ ...(i18nOptions.languages ? { languages: i18nOptions.languages } : {}),
761
+ ...i18nOptions.routing,
762
+ });
763
+ }
764
+
765
+ let sharedWordsLoad: Promise<void> | null = null;
766
+
767
+ /**
768
+ * Load Design system 1's shared interface words beneath the product's own
769
+ * (what `configureI18n({ sharedWords: true })` does). They arrive in a chunk
770
+ * of their own, so products that do not use them do not carry them.
771
+ */
772
+ export function loadSharedWords(): Promise<void> {
773
+ if (!sharedWordsLoad) {
774
+ sharedWordsLoad = import("./shared-words").then(({ SHARED_WORDS }) => {
775
+ sharedWords = SHARED_WORDS;
776
+ if (typeof window !== "undefined") {
777
+ window.dispatchEvent(new CustomEvent("translations-ready"));
778
+ }
779
+ });
780
+ }
781
+ return sharedWordsLoad;
782
+ }
783
+
784
+ applyRouting();
785
+ if (i18nOptions.sharedWords) {
786
+ void loadSharedWords();
787
+ }
788
+
789
+ // Auto-load translations when this module is imported (for CDN bundle).
790
+ // Waits a task, so a product can configure the module first, even after a
791
+ // dynamic import.
792
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
793
+ const start = () => {
794
+ if (i18nOptions.autoLoad !== false) void loadExternalTranslations();
795
+ };
796
+ if (document.readyState === "loading") {
797
+ document.addEventListener("DOMContentLoaded", start);
798
+ } else {
799
+ // DOM is already ready
800
+ setTimeout(start, 0);
801
+ }
802
+ }
803
+
804
+ // Listen for external translations being loaded
805
+ if (typeof window !== "undefined") {
806
+ window.addEventListener("translations-ready", () => {
807
+ // Dispatch that translations are loaded
808
+ window.dispatchEvent(new CustomEvent("translations-loaded"));
809
+
810
+ // Dispatch language-changed to update all components
811
+ dispatchLanguageChanged(currentLanguage.value);
812
+ });
813
+
814
+ // Initialize translations on module load
815
+ // Use setTimeout to give other parts of the app time to set up event listeners first
816
+ setTimeout(() => {
817
+ if (typeof window === "undefined") {
818
+ return;
819
+ }
820
+ window.dispatchEvent(new CustomEvent("translations-loaded"));
821
+
822
+ // Also dispatch language-changed with the current language
823
+ dispatchLanguageChanged(currentLanguage.value);
824
+ }, 100);
825
+ }
826
+
827
+ // ============================================================================
828
+ // Looking words up
829
+ // ============================================================================
830
+
831
+ // Missing keys are ordinary: the same components carry what people write
832
+ // themselves, and a list called "Iroco.com" is not a key anyone forgot. So the
833
+ // console stays quiet unless someone is hunting for a missing key, and then
834
+ // each key is only reported once.
835
+ const REPORTED_MISSING_KEYS = new Set<string>();
836
+
837
+ // Loading is quiet too: what loaded, and from where, is only said when
838
+ // someone has turned the debug switch on.
839
+ function debugLog(message: string): void {
840
+ if (!debugSwitchOn()) {
841
+ return;
842
+ }
843
+ console.info(`[Design system 1] ${message}`);
844
+ }
845
+
846
+ function debugSwitchOn(): boolean {
847
+ if (typeof window === "undefined") {
848
+ return false;
849
+ }
850
+ if (window.DS_ONE_I18N_DEBUG === true) {
851
+ return true;
852
+ }
853
+ try {
854
+ return storage()?.getItem(DEBUG_STORAGE_KEY) === "on";
855
+ } catch {
856
+ return false;
857
+ }
858
+ }
859
+
860
+ function missingKeyReportingOn(): boolean {
861
+ const option = i18nOptions.reportMissingKeys;
862
+ try {
863
+ if (typeof option === "function" ? option() : option) {
864
+ return true;
865
+ }
866
+ } catch {
867
+ // A failing check reports nothing
868
+ }
869
+
870
+ return debugSwitchOn();
871
+ }
872
+
873
+ function reportMissingKey(key: string, language: LanguageCode): void {
874
+ if (!missingKeyReportingOn()) {
875
+ return;
876
+ }
877
+ const seen = `${language}:${key}`;
878
+ if (REPORTED_MISSING_KEYS.has(seen)) {
879
+ return;
880
+ }
881
+ REPORTED_MISSING_KEYS.add(seen);
882
+ console.warn(
883
+ `[Design system 1 (Internationalization)] No translation found for key "${key}" in "${language}"`
884
+ );
885
+ }
886
+
887
+ /**
888
+ * A key's text in the current language, with its placeholders (`{name}` and
889
+ * `{{name}}` alike) filled from `params`.
890
+ */
891
+ export function translate(key: string, params?: TranslationParams): string {
892
+ return textInLanguage(key, currentLanguage.value, params);
893
+ }
894
+
895
+ /**
896
+ * A key's text in a given language, whatever the page's own language is (a
897
+ * drawing made for one language, say). The fallback order: the language, its
898
+ * primary subtag (`ja-JP` reads `ja`), its relatives (`nn` reads `nb`,
899
+ * `pt-BR` reads `pt`), English, then the key itself. In each, the product's
900
+ * words come before the shared words.
901
+ */
902
+ export function textInLanguage(
903
+ key: string,
904
+ language: LanguageCode,
905
+ params?: TranslationParams
906
+ ): string {
907
+ const data = getTranslationData();
908
+ const resolved = resolveText(key, language, [data, sharedWords], params);
909
+ if (resolved.language !== null) {
910
+ return resolved.text;
911
+ }
912
+
913
+ // Loaded a language at a time, English may not have arrived yet
914
+ const codes = fallbackLanguages(language);
915
+ if (
916
+ !codes.includes(defaultLanguage) &&
917
+ !data?.[defaultLanguage] &&
918
+ codes.some((code) => data?.[code])
919
+ ) {
920
+ void loadLanguage(defaultLanguage);
921
+ }
922
+
923
+ reportMissingKey(key, codes[codes.length - 1] ?? language);
924
+ return resolved.text;
925
+ }
926
+
927
+ export function hasTranslation(
928
+ key: string,
929
+ language: LanguageCode = currentLanguage.value
930
+ ): boolean {
931
+ if (!key) {
932
+ return false;
933
+ }
934
+
935
+ const codes = fallbackLanguages(language);
936
+ if (!codes.includes(defaultLanguage)) {
937
+ codes.push(defaultLanguage);
938
+ }
939
+
940
+ return [getTranslationData(), sharedWords].some((source) =>
941
+ codes.some((code) => {
942
+ const langData = source?.[code];
943
+ return Boolean(
944
+ langData && Object.prototype.hasOwnProperty.call(langData, key)
945
+ );
946
+ })
947
+ );
948
+ }
949
+
950
+ // Get text - synchronous version for components
951
+ export function getText(key: string, params?: TranslationParams): string {
952
+ return translate(key, params);
953
+ }
954
+
955
+ // Get available languages: the configured ones, else the ones in the loaded words
956
+ export function getAvailableLanguages(): Promise<LanguageCode[]> {
957
+ return Promise.resolve(getAvailableLanguagesSync());
958
+ }
959
+
960
+ // Synchronous version for immediate use
961
+ export function getAvailableLanguagesSync(): LanguageCode[] {
962
+ if (i18nOptions.languages && i18nOptions.languages.length > 0) {
963
+ return sortLanguageCodes([...i18nOptions.languages]);
964
+ }
965
+
966
+ const currentData = getTranslationData();
967
+
968
+ if (currentData && Object.keys(currentData).length > 0) {
969
+ return sortLanguageCodes(Object.keys(currentData));
970
+ }
971
+ return [defaultLanguage];
972
+ }
973
+
974
+ /**
975
+ * Add words to what is loaded: one language's (`loadTranslations("da", {…})`)
976
+ * or several at once (`loadTranslations({ en: {…}, da: {…} })`), from a JSON
977
+ * file or a TypeScript module of the same shape (a `Partial` table is fine).
978
+ * Components update as they do when a translation file arrives.
979
+ */
980
+ export function loadTranslations(
981
+ language: LanguageCode,
982
+ translations: TranslationTable
983
+ ): void;
984
+ export function loadTranslations(translations: TranslationsFile): void;
985
+ export function loadTranslations(
986
+ languageOrMap: LanguageCode | TranslationsFile,
987
+ translations?: TranslationTable
988
+ ): void {
989
+ const map: TranslationsFile =
990
+ typeof languageOrMap === "string"
991
+ ? { [languageOrMap]: translations ?? {} }
992
+ : languageOrMap;
993
+
994
+ if (!validateTranslationMap(map)) {
995
+ console.warn(
996
+ "[Design system 1] loadTranslations expects words keyed by language code."
997
+ );
998
+ return;
999
+ }
1000
+
1001
+ const data = getTranslationData();
1002
+ const merged: TranslationMap = {};
1003
+ for (const [code, words] of Object.entries(map)) {
1004
+ const defined: TranslationData = {};
1005
+ for (const [key, text] of Object.entries(words ?? {})) {
1006
+ if (typeof text === "string") {
1007
+ defined[key] = text;
1008
+ }
1009
+ }
1010
+ merged[code] = { ...data[code], ...defined };
1011
+ }
1012
+ mergeTranslations(merged);
1013
+
1014
+ if (typeof window !== "undefined") {
1015
+ window.dispatchEvent(new CustomEvent("translations-ready"));
1016
+ }
1017
+ }
1018
+
1019
+ // Set language (Portfolio pattern)
1020
+ export function setLanguage(language: LanguageCode): void {
1021
+ // The signal owns persistence, loading, document language and notification.
1022
+ currentLanguage.set(language);
1023
+ }