@defra/forms-engine-plugin 5.0.0-alpha.1 → 5.0.0-alpha.10

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 (278) hide show
  1. package/.public/javascripts/application.min.js +1 -1
  2. package/.public/javascripts/application.min.js.map +1 -1
  3. package/.public/javascripts/shared.min.js +1 -1
  4. package/.public/javascripts/shared.min.js.map +1 -1
  5. package/.public/stylesheets/application.min.css.map +1 -1
  6. package/.server/client/javascripts/file-upload.js +5 -3
  7. package/.server/client/javascripts/file-upload.js.map +1 -1
  8. package/.server/client/javascripts/geospatial-map.js +31 -26
  9. package/.server/client/javascripts/geospatial-map.js.map +1 -1
  10. package/.server/client/javascripts/location-map.js +9 -1
  11. package/.server/client/javascripts/location-map.js.map +1 -1
  12. package/.server/client/javascripts/maps.d.ts +5 -0
  13. package/.server/client/javascripts/maps.js +6 -0
  14. package/.server/client/javascripts/maps.js.map +1 -0
  15. package/.server/client/javascripts/shared.d.ts +1 -3
  16. package/.server/client/javascripts/shared.js +1 -3
  17. package/.server/client/javascripts/shared.js.map +1 -1
  18. package/.server/client/javascripts/sssi-dataset.d.ts +18 -20
  19. package/.server/client/javascripts/sssi-dataset.js +37 -39
  20. package/.server/client/javascripts/sssi-dataset.js.map +1 -1
  21. package/.server/client/stylesheets/maps.scss +4 -0
  22. package/.server/client/stylesheets/shared.scss +1 -5
  23. package/.server/server/constants.d.ts +2 -0
  24. package/.server/server/constants.js +2 -0
  25. package/.server/server/constants.js.map +1 -1
  26. package/.server/server/plugins/engine/components/AutocompleteField.d.ts +9 -3
  27. package/.server/server/plugins/engine/components/AutocompleteField.js +12 -0
  28. package/.server/server/plugins/engine/components/AutocompleteField.js.map +1 -1
  29. package/.server/server/plugins/engine/components/CheckboxesField.d.ts +4 -1
  30. package/.server/server/plugins/engine/components/CheckboxesField.js +13 -0
  31. package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
  32. package/.server/server/plugins/engine/components/ComponentBase.d.ts +1 -0
  33. package/.server/server/plugins/engine/components/ComponentBase.js +2 -0
  34. package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -1
  35. package/.server/server/plugins/engine/components/ComponentCollection.js +11 -6
  36. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
  37. package/.server/server/plugins/engine/components/DatePartsField.d.ts +6 -3
  38. package/.server/server/plugins/engine/components/DatePartsField.js +12 -7
  39. package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
  40. package/.server/server/plugins/engine/components/DeclarationField.d.ts +5 -2
  41. package/.server/server/plugins/engine/components/DeclarationField.js +6 -4
  42. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  43. package/.server/server/plugins/engine/components/Details.d.ts +1 -0
  44. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +26 -2
  45. package/.server/server/plugins/engine/components/EastingNorthingField.js +93 -54
  46. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  47. package/.server/server/plugins/engine/components/EmailAddressField.d.ts +4 -1
  48. package/.server/server/plugins/engine/components/EmailAddressField.js +2 -1
  49. package/.server/server/plugins/engine/components/EmailAddressField.js.map +1 -1
  50. package/.server/server/plugins/engine/components/FileUploadField.d.ts +12 -0
  51. package/.server/server/plugins/engine/components/FileUploadField.js +20 -2
  52. package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
  53. package/.server/server/plugins/engine/components/FormComponent.d.ts +5 -1
  54. package/.server/server/plugins/engine/components/FormComponent.js +5 -2
  55. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  56. package/.server/server/plugins/engine/components/GeospatialField.d.ts +1 -0
  57. package/.server/server/plugins/engine/components/GeospatialField.js +1 -1
  58. package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
  59. package/.server/server/plugins/engine/components/Html.d.ts +1 -0
  60. package/.server/server/plugins/engine/components/InsetText.d.ts +1 -0
  61. package/.server/server/plugins/engine/components/LatLongField.d.ts +26 -2
  62. package/.server/server/plugins/engine/components/LatLongField.js +85 -48
  63. package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
  64. package/.server/server/plugins/engine/components/List.d.ts +1 -0
  65. package/.server/server/plugins/engine/components/ListFormComponent.d.ts +1 -0
  66. package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +8 -2
  67. package/.server/server/plugins/engine/components/LocationFieldBase.js +18 -5
  68. package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
  69. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +5 -2
  70. package/.server/server/plugins/engine/components/LocationFieldHelpers.js +10 -11
  71. package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
  72. package/.server/server/plugins/engine/components/Markdown.d.ts +1 -0
  73. package/.server/server/plugins/engine/components/Markdown.js +1 -1
  74. package/.server/server/plugins/engine/components/Markdown.js.map +1 -1
  75. package/.server/server/plugins/engine/components/MonthYearField.d.ts +5 -2
  76. package/.server/server/plugins/engine/components/MonthYearField.js +13 -9
  77. package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
  78. package/.server/server/plugins/engine/components/MultilineTextField.d.ts +1 -0
  79. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +2 -1
  80. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +4 -3
  81. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -1
  82. package/.server/server/plugins/engine/components/NotificationBanner.d.ts +1 -0
  83. package/.server/server/plugins/engine/components/NumberField.d.ts +5 -0
  84. package/.server/server/plugins/engine/components/NumberField.js +16 -0
  85. package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
  86. package/.server/server/plugins/engine/components/OsGridRefField.d.ts +2 -1
  87. package/.server/server/plugins/engine/components/OsGridRefField.js +4 -3
  88. package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -1
  89. package/.server/server/plugins/engine/components/PaymentField.d.ts +3 -0
  90. package/.server/server/plugins/engine/components/PaymentField.js +17 -6
  91. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
  92. package/.server/server/plugins/engine/components/SelectField.d.ts +1 -0
  93. package/.server/server/plugins/engine/components/SelectionControlField.d.ts +1 -0
  94. package/.server/server/plugins/engine/components/TelephoneNumberField.d.ts +1 -0
  95. package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -0
  96. package/.server/server/plugins/engine/components/UkAddressField.js +23 -8
  97. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  98. package/.server/server/plugins/engine/components/YesNoField.d.ts +5 -2
  99. package/.server/server/plugins/engine/components/YesNoField.js +17 -8
  100. package/.server/server/plugins/engine/components/YesNoField.js.map +1 -1
  101. package/.server/server/plugins/engine/components/helpers/components.js +1 -2
  102. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  103. package/.server/server/plugins/engine/components/helpers/index.d.ts +7 -0
  104. package/.server/server/plugins/engine/components/helpers/index.js +13 -1
  105. package/.server/server/plugins/engine/components/helpers/index.js.map +1 -1
  106. package/.server/server/plugins/engine/components/types.d.ts +1 -0
  107. package/.server/server/plugins/engine/components/types.js.map +1 -1
  108. package/.server/server/plugins/engine/helpers.d.ts +22 -2
  109. package/.server/server/plugins/engine/helpers.js +44 -5
  110. package/.server/server/plugins/engine/helpers.js.map +1 -1
  111. package/.server/server/plugins/engine/i18n/buildValidationMessages.d.ts +3 -0
  112. package/.server/server/plugins/engine/i18n/buildValidationMessages.js +10 -7
  113. package/.server/server/plugins/engine/i18n/buildValidationMessages.js.map +1 -1
  114. package/.server/server/plugins/engine/i18n/createFormTranslator.d.ts +4 -4
  115. package/.server/server/plugins/engine/i18n/createFormTranslator.js +33 -4
  116. package/.server/server/plugins/engine/i18n/createFormTranslator.js.map +1 -1
  117. package/.server/server/plugins/engine/i18n/extractBaseTranslations.d.ts +1 -1
  118. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js +9 -0
  119. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js.map +1 -1
  120. package/.server/server/plugins/engine/i18n/form.d.ts +13 -0
  121. package/.server/server/plugins/engine/i18n/form.js +35 -0
  122. package/.server/server/plugins/engine/i18n/form.js.map +1 -0
  123. package/.server/server/plugins/engine/i18n/languages.d.ts +10 -0
  124. package/.server/server/plugins/engine/i18n/languages.js +19 -0
  125. package/.server/server/plugins/engine/i18n/languages.js.map +1 -0
  126. package/.server/server/plugins/engine/i18n/translations/cy.json +73 -6
  127. package/.server/server/plugins/engine/i18n/translations/en-GB.json +43 -4
  128. package/.server/server/plugins/engine/i18n/types.d.ts +2 -0
  129. package/.server/server/plugins/engine/i18n/types.js.map +1 -1
  130. package/.server/server/plugins/engine/index.d.ts +4 -0
  131. package/.server/server/plugins/engine/index.js +4 -0
  132. package/.server/server/plugins/engine/index.js.map +1 -1
  133. package/.server/server/plugins/engine/models/FormModel.d.ts +0 -4
  134. package/.server/server/plugins/engine/models/FormModel.js +9 -15
  135. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  136. package/.server/server/plugins/engine/models/SummaryViewModel.js +9 -4
  137. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  138. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +3 -5
  139. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  140. package/.server/server/plugins/engine/outputFormatters/human/v1.js +2 -1
  141. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  142. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +2 -0
  143. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +2 -0
  144. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  145. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +1 -1
  146. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +22 -9
  147. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  148. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -1
  149. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +7 -3
  150. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  151. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +3 -4
  152. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  153. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +10 -4
  154. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  155. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +1 -1
  156. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +3 -3
  157. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -1
  158. package/.server/server/plugins/engine/pageControllers/validationOptions.js +23 -2
  159. package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
  160. package/.server/server/plugins/engine/types/schema.js +1 -0
  161. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  162. package/.server/server/plugins/engine/types.d.ts +1 -0
  163. package/.server/server/plugins/engine/types.js.map +1 -1
  164. package/.server/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  165. package/.server/server/plugins/engine/views/components/multilinetextfield.html +12 -1
  166. package/.server/server/plugins/engine/views/components/paymentfield.html +1 -1
  167. package/.server/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  168. package/.server/server/plugins/engine/views/partials/preview-banner.html +3 -1
  169. package/.server/server/plugins/nunjucks/filters/applyUrlParam.d.ts +9 -0
  170. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  171. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js.map +1 -0
  172. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js +17 -0
  173. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js.map +1 -0
  174. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  175. package/.server/server/plugins/nunjucks/filters/index.d.ts +1 -0
  176. package/.server/server/plugins/nunjucks/filters/index.js +1 -0
  177. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -1
  178. package/.server/server/plugins/payment/helper.d.ts +11 -1
  179. package/.server/server/plugins/payment/helper.js +18 -2
  180. package/.server/server/plugins/payment/helper.js.map +1 -1
  181. package/.server/server/plugins/payment/helper.test.js +2 -1
  182. package/.server/server/plugins/payment/helper.test.js.map +1 -1
  183. package/.server/server/plugins/payment/service.d.ts +2 -1
  184. package/.server/server/plugins/payment/service.js +5 -2
  185. package/.server/server/plugins/payment/service.js.map +1 -1
  186. package/.server/server/plugins/payment/service.test.js +7 -3
  187. package/.server/server/plugins/payment/service.test.js.map +1 -1
  188. package/.server/server/plugins/payment/types.d.ts +4 -0
  189. package/.server/server/plugins/payment/types.js +1 -0
  190. package/.server/server/plugins/payment/types.js.map +1 -1
  191. package/.server/server/plugins/postcode-lookup/models/index.d.ts +249 -3
  192. package/.server/server/plugins/postcode-lookup/models/index.js +87 -33
  193. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -1
  194. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +15 -1
  195. package/.server/server/plugins/postcode-lookup/routes/index.js +47 -15
  196. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -1
  197. package/.server/server/plugins/postcode-lookup/routes/index.test.js +84 -0
  198. package/.server/server/plugins/postcode-lookup/routes/index.test.js.map +1 -0
  199. package/.server/server/plugins/postcode-lookup/types.d.ts +12 -0
  200. package/.server/server/plugins/postcode-lookup/types.js +6 -1
  201. package/.server/server/plugins/postcode-lookup/types.js.map +1 -1
  202. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  203. package/.server/server/utils/type-utils.d.ts +1 -2
  204. package/.server/server/utils/type-utils.js +0 -3
  205. package/.server/server/utils/type-utils.js.map +1 -1
  206. package/package.json +8 -2
  207. package/src/client/javascripts/file-upload.js +10 -3
  208. package/src/client/javascripts/geospatial-map.js +32 -30
  209. package/src/client/javascripts/location-map.js +7 -1
  210. package/src/client/javascripts/maps.js +5 -0
  211. package/src/client/javascripts/shared.js +1 -3
  212. package/src/client/javascripts/sssi-dataset.js +39 -41
  213. package/src/client/stylesheets/maps.scss +4 -0
  214. package/src/client/stylesheets/shared.scss +1 -5
  215. package/src/server/constants.js +2 -0
  216. package/src/server/plugins/engine/components/AutocompleteField.ts +13 -0
  217. package/src/server/plugins/engine/components/CheckboxesField.ts +13 -1
  218. package/src/server/plugins/engine/components/ComponentBase.ts +2 -0
  219. package/src/server/plugins/engine/components/ComponentCollection.ts +16 -11
  220. package/src/server/plugins/engine/components/DatePartsField.ts +20 -9
  221. package/src/server/plugins/engine/components/DeclarationField.ts +7 -6
  222. package/src/server/plugins/engine/components/EastingNorthingField.ts +154 -89
  223. package/src/server/plugins/engine/components/EmailAddressField.ts +2 -1
  224. package/src/server/plugins/engine/components/FileUploadField.ts +21 -2
  225. package/src/server/plugins/engine/components/FormComponent.ts +6 -5
  226. package/src/server/plugins/engine/components/GeospatialField.ts +6 -1
  227. package/src/server/plugins/engine/components/LatLongField.ts +128 -58
  228. package/src/server/plugins/engine/components/LocationFieldBase.ts +23 -6
  229. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +14 -11
  230. package/src/server/plugins/engine/components/Markdown.ts +2 -3
  231. package/src/server/plugins/engine/components/MonthYearField.ts +20 -15
  232. package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +9 -3
  233. package/src/server/plugins/engine/components/NumberField.ts +21 -1
  234. package/src/server/plugins/engine/components/OsGridRefField.ts +9 -3
  235. package/src/server/plugins/engine/components/PaymentField.ts +15 -5
  236. package/src/server/plugins/engine/components/UkAddressField.ts +30 -7
  237. package/src/server/plugins/engine/components/YesNoField.ts +17 -9
  238. package/src/server/plugins/engine/components/helpers/components.ts +1 -2
  239. package/src/server/plugins/engine/components/helpers/index.ts +19 -0
  240. package/src/server/plugins/engine/components/types.ts +1 -0
  241. package/src/server/plugins/engine/helpers.ts +52 -5
  242. package/src/server/plugins/engine/i18n/buildValidationMessages.ts +10 -7
  243. package/src/server/plugins/engine/i18n/createFormTranslator.ts +62 -11
  244. package/src/server/plugins/engine/i18n/extractBaseTranslations.ts +7 -1
  245. package/src/server/plugins/engine/i18n/form.ts +54 -0
  246. package/src/server/plugins/engine/i18n/languages.ts +19 -0
  247. package/src/server/plugins/engine/i18n/translations/cy.json +73 -6
  248. package/src/server/plugins/engine/i18n/translations/en-GB.json +43 -4
  249. package/src/server/plugins/engine/i18n/types.ts +2 -0
  250. package/src/server/plugins/engine/index.ts +7 -0
  251. package/src/server/plugins/engine/models/FormModel.ts +9 -15
  252. package/src/server/plugins/engine/models/SummaryViewModel.ts +6 -5
  253. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +3 -6
  254. package/src/server/plugins/engine/outputFormatters/human/v1.ts +2 -1
  255. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +3 -0
  256. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +18 -8
  257. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +10 -5
  258. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +2 -2
  259. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -4
  260. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +6 -3
  261. package/src/server/plugins/engine/pageControllers/validationOptions.ts +27 -2
  262. package/src/server/plugins/engine/types/schema.ts +1 -0
  263. package/src/server/plugins/engine/types.ts +1 -0
  264. package/src/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  265. package/src/server/plugins/engine/views/components/multilinetextfield.html +12 -1
  266. package/src/server/plugins/engine/views/components/paymentfield.html +1 -1
  267. package/src/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  268. package/src/server/plugins/engine/views/partials/preview-banner.html +3 -1
  269. package/src/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  270. package/src/server/plugins/nunjucks/filters/index.js +1 -0
  271. package/src/server/plugins/payment/helper.js +22 -2
  272. package/src/server/plugins/payment/service.js +6 -2
  273. package/src/server/plugins/payment/types.js +1 -0
  274. package/src/server/plugins/postcode-lookup/models/index.js +75 -43
  275. package/src/server/plugins/postcode-lookup/routes/index.js +73 -16
  276. package/src/server/plugins/postcode-lookup/types.js +6 -1
  277. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  278. package/src/server/utils/type-utils.ts +1 -11
@@ -4,6 +4,7 @@ import {
4
4
  type Section
5
5
  } from '@defra/forms-model'
6
6
 
7
+ import { EN_GB } from '../../../constants.js'
7
8
  import { PaymentField } from '../components/PaymentField.js'
8
9
  import { type PaymentState } from '../components/PaymentField.types.js'
9
10
  import {
@@ -96,7 +97,7 @@ export class SummaryViewModel {
96
97
  .validate(this.context.relevantState, { ...opts, stripUnknown: true })
97
98
 
98
99
  // Format errors
99
- this.errors = result.error?.details.map(getError)
100
+ this.errors = result.error?.details.map((item) => getError(EN_GB, item))
100
101
  this.details = this.summaryDetails(request, sections, translator)
101
102
 
102
103
  // Format check answers
@@ -212,12 +213,12 @@ function ItemRepeat(
212
213
  translator: Translator
213
214
  ): DetailItemRepeat {
214
215
  const { collection, repeat } = page
215
- const { name, title } = repeat.options
216
-
216
+ const { name } = repeat.options
217
+ const { t, tPage } = translator
218
+ const title = tPage(page.pageDef, 'repeatTitle') || repeat.options.title
217
219
  const values = page.getListFromState(state)
218
220
  const count = values.length
219
- const value =
220
- count === 0 ? '' : translator.t('pages.repeater.pageTitle', { count })
221
+ const value = count === 0 ? '' : t('pages.repeater.pageTitle', { count })
221
222
 
222
223
  return {
223
224
  name,
@@ -3,6 +3,7 @@ import {
3
3
  type SubmitResponsePayload
4
4
  } from '@defra/forms-model'
5
5
 
6
+ import { EN_GB } from '../../../../constants.js'
6
7
  import {
7
8
  getFormVersion,
8
9
  type checkFormStatus
@@ -42,18 +43,14 @@ export function format(
42
43
  formSlug: formMetadata?.slug ?? '',
43
44
  status: formStatus.state,
44
45
  isPreview: formStatus.isPreview,
45
- notificationEmail: formMetadata?.notificationEmail ?? ''
46
+ notificationEmail: formMetadata?.notificationEmail ?? '',
47
+ language: context.translator?.language ?? EN_GB
46
48
  }
47
49
 
48
50
  if (versionMetadata) {
49
51
  meta.versionMetadata = versionMetadata
50
52
  }
51
53
 
52
- const definitionLanguage = model.def.metadata?.language
53
- if (typeof definitionLanguage === 'string') {
54
- meta.language = definitionLanguage
55
- }
56
-
57
54
  const main = Object.fromEntries(
58
55
  Object.entries(v2Main).map(([key, value]) => {
59
56
  if (value === undefined) {
@@ -5,6 +5,7 @@ import {
5
5
  import { addDays, format as dateFormat } from 'date-fns'
6
6
 
7
7
  import { config } from '../../../../../config/index.js'
8
+ import { EN_GB } from '../../../../constants.js'
8
9
  import { getAnswer } from '../../components/helpers/components.js'
9
10
  import { escapeMarkdown } from '../../components/helpers/index.js'
10
11
  import { PaymentField } from '../../components/index.js'
@@ -116,7 +117,7 @@ function appendPaymentSection(paymentItems: DetailItem[], lines: string[]) {
116
117
 
117
118
  const formattedAmount = formatCurrency(paymentState.amount)
118
119
  const dateOfPayment = paymentState.preAuth?.createdAt
119
- ? formatPaymentDate(paymentState.preAuth.createdAt)
120
+ ? formatPaymentDate(paymentState.preAuth.createdAt, EN_GB)
120
121
  : ''
121
122
 
122
123
  lines.push(
@@ -28,6 +28,7 @@ interface PaymentOutput {
28
28
  reference: string
29
29
  amount: number
30
30
  description: string
31
+ descriptionInEng?: string
31
32
  createdAt: string
32
33
  }
33
34
 
@@ -93,6 +94,7 @@ export function format(
93
94
  * reference: 'REF-123',
94
95
  * amount: 10.00,
95
96
  * description: 'Application fee',
97
+ * descriptionInEng: 'Applicaiton fee',
96
98
  * createdAt: '2025-01-23T10:30:00.000Z'
97
99
  * }
98
100
  * }
@@ -204,6 +206,7 @@ function extractPayment(
204
206
  reference: paymentState.reference,
205
207
  amount: paymentState.amount,
206
208
  description: paymentState.description,
209
+ descriptionInEng: item.field.def.options.description,
207
210
  createdAt: paymentState.preAuth?.createdAt ?? ''
208
211
  }
209
212
  }
@@ -188,17 +188,19 @@ export class FileUploadPageController extends QuestionPageController {
188
188
 
189
189
  const { filename } = fileToRemove.status.form.file
190
190
 
191
- const { t } = this.getTranslator(request)
191
+ const translator = this.getTranslator(request)
192
+ const { t } = translator
192
193
 
193
194
  return h.view(this.fileDeleteViewName, {
194
195
  ...viewModel,
195
196
  context,
196
197
  backLink: this.getBackLink(request, context, t),
197
- pageTitle: `Are you sure you want to remove this file?`,
198
+ pageTitle: t('pages.fileUpload.removeFileTitle'),
198
199
  itemTitle: filename,
199
- confirmation: { text: 'You cannot recover removed files.' },
200
- buttonConfirm: { text: 'Remove file' },
201
- buttonCancel: { text: 'Cancel' }
200
+ confirmation: { text: t('pages.fileUpload.removeFileBody') },
201
+ buttonConfirm: { text: t('pages.fileUpload.removeFileButton') },
202
+ buttonCancel: { text: t('pages.fileUpload.cancel') },
203
+ t
202
204
  } satisfies ItemDeletePageViewModel)
203
205
  }
204
206
  }
@@ -224,7 +226,7 @@ export class FileUploadPageController extends QuestionPageController {
224
226
  }
225
227
  }
226
228
 
227
- getErrors(details?: ValidationErrorItem[]) {
229
+ getErrors(details?: ValidationErrorItem[], language?: string) {
228
230
  const { fileUpload } = this
229
231
 
230
232
  if (details) {
@@ -237,7 +239,7 @@ export class FileUploadPageController extends QuestionPageController {
237
239
  if (!isUploadError || isUploadRootError) {
238
240
  // The error is for the root of the upload or another
239
241
  // field on the page so defer to the getError helper
240
- errors.push(getError(error))
242
+ errors.push(getError(language, error))
241
243
  } else {
242
244
  const { context, path, type } = error
243
245
 
@@ -266,6 +268,7 @@ export class FileUploadPageController extends QuestionPageController {
266
268
  ): FeaturedFormPageViewModel {
267
269
  const { fileUpload } = this
268
270
  const { state } = context
271
+ const { t } = translator
269
272
 
270
273
  const upload = this.getUploadFromState(state)
271
274
 
@@ -277,6 +280,12 @@ export class FileUploadPageController extends QuestionPageController {
277
280
  ({ model }) => model.id === fileUpload.name
278
281
  )
279
282
 
283
+ formComponent.model.upload = {
284
+ count: formComponent.model.upload?.count ?? 0,
285
+ summaryList: formComponent.model.upload?.summaryList ?? { rows: [] },
286
+ uploadingLabel: t('components.fileUploadField.uploading')
287
+ }
288
+
280
289
  const index = components.indexOf(formComponent)
281
290
 
282
291
  const proxyUrl = getProxyUrlForLocalDevelopment(upload?.uploadUrl)
@@ -290,7 +299,8 @@ export class FileUploadPageController extends QuestionPageController {
290
299
  // Split out components before/after
291
300
  componentsBefore: components.slice(0, index),
292
301
  components: components.slice(index),
293
- proxyUrl
302
+ proxyUrl,
303
+ t
294
304
  }
295
305
  }
296
306
 
@@ -174,9 +174,14 @@ export class QuestionPageController extends PageController {
174
174
  if (!pageTitle) {
175
175
  pageTitle = labelOrLegend.text
176
176
  }
177
- labelOrLegend.text = isOptional
178
- ? `${pageTitle} ${t('common.optional')}`
179
- : pageTitle
177
+
178
+ // The question text may have already appended 'optional' in the relevant language
179
+ const optionalWord = t('common.optional')
180
+ const optionalAlreadyAppended = pageTitle.endsWith(` ${optionalWord}`)
181
+ labelOrLegend.text =
182
+ isOptional && !optionalAlreadyAppended
183
+ ? `${pageTitle} ${optionalWord}`
184
+ : pageTitle
180
185
  }
181
186
 
182
187
  // Fallback if still no pageTitle set
@@ -359,8 +364,8 @@ export class QuestionPageController extends PageController {
359
364
  return this.collection.getStateFromValidForm(payload)
360
365
  }
361
366
 
362
- getErrors(details?: ValidationErrorItem[]) {
363
- return getErrors(details)
367
+ getErrors(details?: ValidationErrorItem[], language?: string) {
368
+ return getErrors(language, details)
364
369
  }
365
370
 
366
371
  async getState(request: AnyFormRequest) {
@@ -406,9 +406,9 @@ export class RepeatPageController extends QuestionPageController {
406
406
  const { query } = request
407
407
  const { isForceAccess, errors } = context
408
408
 
409
- const { t } = translator
409
+ const { t, tPage } = translator
410
410
 
411
- const { title } = repeat.options
411
+ const title = tPage(this.pageDef, 'repeatTitle') || repeat.options.title
412
412
 
413
413
  const summaryList: SummaryList = {
414
414
  classes: 'govuk-summary-list--long-actions',
@@ -114,7 +114,7 @@ export class SummaryPageController extends QuestionPageController {
114
114
  viewModel.paymentDetails = this.buildPaymentDetails(
115
115
  paymentField,
116
116
  paymentState,
117
- t
117
+ translator
118
118
  )
119
119
  }
120
120
 
@@ -151,8 +151,9 @@ export class SummaryPageController extends QuestionPageController {
151
151
  paymentState: NonNullable<
152
152
  ReturnType<PaymentField['getPaymentStateFromState']>
153
153
  >,
154
- t: (key: string, opts?: Record<string, unknown>) => string
154
+ translator: Translator
155
155
  ) {
156
+ const { t } = translator
156
157
  const rows = [
157
158
  {
158
159
  key: { text: t('pages.summary.paymentFor') },
@@ -171,7 +172,12 @@ export class SummaryPageController extends QuestionPageController {
171
172
  if (paymentState.preAuth?.createdAt) {
172
173
  rows.push({
173
174
  key: { text: t('pages.summary.dateOfPayment') },
174
- value: { text: formatPaymentDate(paymentState.preAuth.createdAt) }
175
+ value: {
176
+ text: formatPaymentDate(
177
+ paymentState.preAuth.createdAt,
178
+ translator.language
179
+ )
180
+ }
175
181
  })
176
182
  }
177
183
 
@@ -557,7 +563,12 @@ function submitData(
557
563
  retrievalKey,
558
564
  referenceNumber,
559
565
  main: buildMainRecords(items, translator),
560
- repeaters: buildRepeaterRecords(items, translator)
566
+ repeaters: buildRepeaterRecords(items, translator),
567
+ // Only populate the language property if the form is multi-language enabled
568
+ // @ts-expect-error - dynamic language property
569
+ language: model.def.metadata?.translations?.cy
570
+ ? translator.language
571
+ : undefined
561
572
  }
562
573
  return submit(payload)
563
574
  }
@@ -36,7 +36,7 @@ export function buildMainRecords(
36
36
 
37
37
  for (const item of fieldItems) {
38
38
  if (item.field instanceof PaymentField) {
39
- records.push(...buildPaymentRecords(item))
39
+ records.push(...buildPaymentRecords(item, translator))
40
40
  } else if (item.field instanceof GeospatialField) {
41
41
  // Stringify of GeoJSON is done here rather than inside `getContextValueFromState`
42
42
  // so we don't incur the overhead of JSON.stringify on every request when building context
@@ -67,7 +67,10 @@ export function buildMainRecords(
67
67
  *
68
68
  * Returns an empty array if no payment state exists.
69
69
  */
70
- export function buildPaymentRecords(item: DetailItemField): SubmitRecord[] {
70
+ export function buildPaymentRecords(
71
+ item: DetailItemField,
72
+ translator: Translator
73
+ ): SubmitRecord[] {
71
74
  const paymentState = (item.field as PaymentField).getPaymentStateFromState(
72
75
  item.state
73
76
  )
@@ -96,7 +99,7 @@ export function buildPaymentRecords(item: DetailItemField): SubmitRecord[] {
96
99
  name: `${item.name}_paymentDate`,
97
100
  title: 'Payment date',
98
101
  value: paymentState.preAuth?.createdAt
99
- ? formatPaymentDate(paymentState.preAuth.createdAt)
102
+ ? formatPaymentDate(paymentState.preAuth.createdAt, translator.language)
100
103
  : ''
101
104
  }
102
105
  ]
@@ -58,7 +58,21 @@ export const messages: LanguageMessagesExt = {
58
58
  'date.min': messageTemplate.dateMin,
59
59
  'date.max': messageTemplate.dateMax,
60
60
 
61
- 'object.invalidjson': messageTemplate.format
61
+ 'array.min': messageTemplate.arrayMin,
62
+ 'array.max': messageTemplate.arrayMax,
63
+ 'array.length': messageTemplate.arrayLength,
64
+
65
+ 'features.min': messageTemplate.featuresMin,
66
+ 'features.max': messageTemplate.featuresMax,
67
+ 'features.length': messageTemplate.featuresLength,
68
+
69
+ 'files.min': messageTemplate.filesMin,
70
+ 'files.max': messageTemplate.filesMax,
71
+ 'filees.length': messageTemplate.filesLength,
72
+
73
+ 'object.invalidjson': messageTemplate.format,
74
+
75
+ 'phoneNumber.invalid': messageTemplate.format
62
76
  }
63
77
 
64
78
  export const messagesPre: LanguageMessages =
@@ -76,6 +90,7 @@ export function buildLanguageMessages(
76
90
  'string.email': vm.format,
77
91
  'string.pattern.base': vm.pattern,
78
92
  'string.maxWords': vm.maxWords,
93
+ 'string.unicode': vm.unicode,
79
94
  'number.base': vm.number,
80
95
  'number.precision': vm.numberPrecision,
81
96
  'number.integer': vm.numberInteger,
@@ -91,7 +106,17 @@ export function buildLanguageMessages(
91
106
  'date.format': vm.dateFormat,
92
107
  'date.min': vm.dateMin,
93
108
  'date.max': vm.dateMax,
94
- 'object.invalidjson': vm.format
109
+ 'array.min': vm.arrayMin,
110
+ 'array.max': vm.arrayMax,
111
+ 'array.length': vm.arrayLength,
112
+ 'features.min': vm.featuresMin,
113
+ 'features.max': vm.featuresMax,
114
+ 'features.length': vm.featuresLength,
115
+ 'files.min': vm.filesMin,
116
+ 'files.max': vm.filesMax,
117
+ 'files.length': vm.filesLength,
118
+ 'object.invalidjson': vm.format,
119
+ 'phoneNumber.invalid': vm.format
95
120
  } as unknown as LanguageMessages
96
121
  }
97
122
 
@@ -50,6 +50,7 @@ export const formAdapterSubmissionMessageDataSchema =
50
50
  reference: Joi.string().required(),
51
51
  amount: Joi.number().required(),
52
52
  description: Joi.string().required(),
53
+ descriptionInEng: Joi.string().optional(),
53
54
  createdAt: Joi.string().required()
54
55
  })
55
56
  .optional(),
@@ -558,6 +558,7 @@ export interface FormAdapterPayment {
558
558
  reference: string
559
559
  amount: number
560
560
  description: string
561
+ descriptionInEng?: string
561
562
  createdAt: string
562
563
  }
563
564
 
@@ -3,7 +3,7 @@
3
3
  {% macro FileUploadField(component) %}
4
4
  {% set upload = component.model.upload %}
5
5
 
6
- <div id="uploadedFilesContainer">
6
+ <div id="uploadedFilesContainer" data-uploadinglabel="{{ upload.uploadingLabel }}">
7
7
  <h2 class="govuk-heading-m">{{ t('components.fileUploadField.uploadedFilesHeading') }}</h2>
8
8
  {% if upload.count %}
9
9
  <p class="govuk-body" role="alert">{{ t('components.fileUploadField.filesCount', { count: upload.count }) }}</p>
@@ -3,7 +3,18 @@
3
3
 
4
4
  {% macro MultilineTextField(component) %}
5
5
  {% if component.model.isCharacterOrWordCount == true %}
6
- {{ govukCharacterCount(component.model) }}
6
+ {% set characterParams = component.model | merge({
7
+ charactersUnderLimitText: {
8
+ one: t('components.characterCount.underLimit.one'),
9
+ other: t('components.characterCount.underLimit.other')
10
+ },
11
+ charactersAtLimitText: t('components.characterCount.atLimit'),
12
+ charactersOverLimitText: {
13
+ one: t('components.characterCount.overLimit.one'),
14
+ other: t('components.characterCount.overLimit.other')
15
+ }
16
+ } ) %}
17
+ {{ govukCharacterCount(characterParams) }}
7
18
  {% else %}
8
19
  {{ govukTextarea(component.model) }}
9
20
  {% endif %}
@@ -16,7 +16,7 @@
16
16
  <p class="govuk-body">{{ t('components.paymentField.preAuthorisedBody') }}</p>
17
17
  {% else %}
18
18
  {# No pre-authorisation - show payment form #}
19
- <h2 class="govuk-heading-m">{{ model.label.text if model.label and model.label.text else t('components.paymentField.detailsRequired') }}</h2>
19
+ <h2 class="govuk-heading-m">{{ t('components.paymentField.detailsRequired') }}</h2>
20
20
 
21
21
  <p class="govuk-body">{{ description }}</p>
22
22
 
@@ -61,7 +61,7 @@
61
61
  },
62
62
  classes: "govuk-button--secondary govuk-!-margin-right-1 govuk-!-margin-bottom-0"
63
63
  }) }}
64
- <p class="govuk-body govuk-!-margin-bottom-0">or <button class="govuk-link govuk-button--link govuk-!-margin-right-1 govuk-!-margin-bottom-0" name="action" value="external-{{component.model.name}}--step:manual">{{ t('components.addressField.enterManually') }}</button></p>
64
+ <p class="govuk-body govuk-!-margin-bottom-0">{{ t('common.or')}} <button class="govuk-link govuk-button--link govuk-!-margin-right-1 govuk-!-margin-bottom-0" name="action" value="external-{{component.model.name}}--step:manual">{{ t('components.addressField.enterManually') }}</button></p>
65
65
  </div>
66
66
  {# Include a line break if this is the last component #}
67
67
  {% if components[components.length - 1] == component %}
@@ -8,7 +8,9 @@
8
8
  </p>
9
9
  {%- endmacro -%}
10
10
 
11
- {% call govukNotificationBanner() %}
11
+ {% call govukNotificationBanner({
12
+ titleText: t('pages.preview.bannerTitle')
13
+ }) %}
12
14
  {% set previewModeTranslated = t('common.' + previewMode) %}
13
15
  {% if not context.isForceAccess %}
14
16
  <p class="govuk-notification-banner__heading govuk-!-margin-bottom-0">
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Nunjucks filter to add/replace a parameter on a query string of a url.
3
+ * Currently only used in forms-runner.
4
+ * @param {string} urlPath - existing relative url with query string
5
+ * @param {string} paramName - name of parameter
6
+ * @param {string} paramValue - value of parameter
7
+ * @returns {string}
8
+ */
9
+ export function applyUrlParam(urlPath, paramName, paramValue) {
10
+ if (typeof urlPath !== 'string') {
11
+ return ''
12
+ }
13
+ const url = new URL(urlPath, 'https://dummy')
14
+ url.searchParams.set(paramName, paramValue)
15
+
16
+ return url.pathname + url.search
17
+ }
@@ -6,3 +6,4 @@ export { href } from './href.js'
6
6
  export { field } from './field.js'
7
7
  export { page } from './page.js'
8
8
  export { merge } from './merge.js'
9
+ export { applyUrlParam } from './applyUrlParam.js'
@@ -1,5 +1,6 @@
1
1
  import { format } from 'date-fns'
2
2
 
3
+ import { toLanguageLocale } from '../engine/helpers.js'
3
4
  import { PaymentService } from './service.js'
4
5
 
5
6
  export const DEFAULT_PAYMENT_HELP_URL =
@@ -28,10 +29,29 @@ export async function createPaymentService(
28
29
  /**
29
30
  * Formats a payment date for display
30
31
  * @param {string} isoString - ISO date string
32
+ * @param {string} language - the target language
31
33
  * @returns {string} Formatted date string (e.g., "26 January 2026 5:01pm")
32
34
  */
33
- export function formatPaymentDate(isoString) {
34
- return format(new Date(isoString), 'd MMMM yyyy h:mmaaa')
35
+ export function formatPaymentDate(isoString, language) {
36
+ return formatDateForLanguage(
37
+ new Date(isoString),
38
+ 'd MMMM yyyy h:mmaaa',
39
+ language
40
+ )
41
+ }
42
+
43
+ /**
44
+ * Formats a date for display in a specific language.
45
+ * To use additional languages, the appropriate module must be imported from 'date-fns/locale'
46
+ * @param { Date | string } date - the date
47
+ * @param {string} formatMask - the format mask
48
+ * @param {string} language - the target language
49
+ * @returns {string} Formatted date string (e.g., "26 January 2026 5:01pm")
50
+ */
51
+ export function formatDateForLanguage(date, formatMask, language) {
52
+ return format(date, formatMask, {
53
+ locale: toLanguageLocale(language)
54
+ })
35
55
  }
36
56
 
37
57
  /**
@@ -1,6 +1,7 @@
1
1
  import { StatusCodes } from 'http-status-codes'
2
2
 
3
3
  import { logger } from '../../common/helpers/logging/logger.js'
4
+ import { toPayLanguageCode } from '../engine/helpers.js'
4
5
  import {
5
6
  buildPaymentInfo,
6
7
  convertPenceToPounds
@@ -38,7 +39,7 @@ export class PaymentService {
38
39
  * @param {string} returnUrl
39
40
  * @param {string} reference
40
41
  * @param {boolean} isLivePayment
41
- * @param {{ formId: string, slug: string } | undefined } metadata
42
+ * @param {{ formId: string, slug: string, language: string } | undefined } metadata
42
43
  * @param {string} [email] - optional email to prepopulate on GOV.UK Pay
43
44
  */
44
45
  async createPayment(
@@ -50,6 +51,8 @@ export class PaymentService {
50
51
  metadata,
51
52
  email
52
53
  ) {
54
+ const language = toPayLanguageCode(metadata?.language)
55
+
53
56
  try {
54
57
  /** @type {CreatePaymentRequest} */
55
58
  const payload = {
@@ -58,7 +61,8 @@ export class PaymentService {
58
61
  reference,
59
62
  metadata,
60
63
  return_url: returnUrl,
61
- delayed_capture: true
64
+ delayed_capture: true,
65
+ language
62
66
  }
63
67
 
64
68
  // Prepopulate email on GOV.UK Pay if provided
@@ -18,6 +18,7 @@
18
18
  * @property {string} reference - Unique reference for the payment
19
19
  * @property {string} description - Human-readable description of the payment
20
20
  * @property {string} return_url - URL to redirect the user to after payment
21
+ * @property {string} language - the desired language for translations
21
22
  * @property {boolean} [delayed_capture] - Whether to delay capturing the payment
22
23
  * @property {{ formId: string, slug: string }} [metadata] - Additional metadata for the payment
23
24
  * @property {string} [email] - Email to prepopulate on GOV.UK Pay (max 254 chars)