@defra/forms-engine-plugin 5.0.0-alpha.7 → 5.0.0-alpha.9

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 (178) hide show
  1. package/.public/javascripts/application.min.js.map +1 -1
  2. package/.public/javascripts/shared.min.js +1 -1
  3. package/.public/javascripts/shared.min.js.map +1 -1
  4. package/.public/stylesheets/application.min.css.map +1 -1
  5. package/.server/client/javascripts/maps.d.ts +5 -0
  6. package/.server/client/javascripts/maps.js +6 -0
  7. package/.server/client/javascripts/maps.js.map +1 -0
  8. package/.server/client/javascripts/shared.d.ts +1 -4
  9. package/.server/client/javascripts/shared.js +1 -4
  10. package/.server/client/javascripts/shared.js.map +1 -1
  11. package/.server/client/stylesheets/maps.scss +4 -0
  12. package/.server/client/stylesheets/shared.scss +1 -5
  13. package/.server/server/constants.d.ts +1 -0
  14. package/.server/server/constants.js +2 -1
  15. package/.server/server/constants.js.map +1 -1
  16. package/.server/server/plugins/engine/components/ComponentCollection.js +10 -6
  17. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
  18. package/.server/server/plugins/engine/components/DatePartsField.d.ts +4 -2
  19. package/.server/server/plugins/engine/components/DatePartsField.js +14 -11
  20. package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
  21. package/.server/server/plugins/engine/components/DeclarationField.d.ts +4 -2
  22. package/.server/server/plugins/engine/components/DeclarationField.js +5 -3
  23. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  24. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +24 -2
  25. package/.server/server/plugins/engine/components/EastingNorthingField.js +93 -52
  26. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  27. package/.server/server/plugins/engine/components/FormComponent.d.ts +4 -1
  28. package/.server/server/plugins/engine/components/FormComponent.js +3 -0
  29. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  30. package/.server/server/plugins/engine/components/LatLongField.d.ts +24 -2
  31. package/.server/server/plugins/engine/components/LatLongField.js +85 -46
  32. package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
  33. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +3 -2
  34. package/.server/server/plugins/engine/components/LocationFieldHelpers.js +10 -11
  35. package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
  36. package/.server/server/plugins/engine/components/MonthYearField.d.ts +4 -2
  37. package/.server/server/plugins/engine/components/MonthYearField.js +16 -13
  38. package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
  39. package/.server/server/plugins/engine/components/PaymentField.d.ts +1 -0
  40. package/.server/server/plugins/engine/components/PaymentField.js +9 -5
  41. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
  42. package/.server/server/plugins/engine/components/UkAddressField.js +22 -7
  43. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  44. package/.server/server/plugins/engine/components/YesNoField.d.ts +3 -2
  45. package/.server/server/plugins/engine/components/YesNoField.js +5 -3
  46. package/.server/server/plugins/engine/components/YesNoField.js.map +1 -1
  47. package/.server/server/plugins/engine/components/helpers/components.js +1 -2
  48. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  49. package/.server/server/plugins/engine/components/helpers/index.d.ts +7 -0
  50. package/.server/server/plugins/engine/components/helpers/index.js +13 -1
  51. package/.server/server/plugins/engine/components/helpers/index.js.map +1 -1
  52. package/.server/server/plugins/engine/helpers.d.ts +21 -2
  53. package/.server/server/plugins/engine/helpers.js +43 -5
  54. package/.server/server/plugins/engine/helpers.js.map +1 -1
  55. package/.server/server/plugins/engine/i18n/createFormTranslator.d.ts +4 -4
  56. package/.server/server/plugins/engine/i18n/createFormTranslator.js +27 -5
  57. package/.server/server/plugins/engine/i18n/createFormTranslator.js.map +1 -1
  58. package/.server/server/plugins/engine/i18n/extractBaseTranslations.d.ts +1 -1
  59. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js +9 -0
  60. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js.map +1 -1
  61. package/.server/server/plugins/engine/i18n/form.d.ts +13 -0
  62. package/.server/server/plugins/engine/i18n/form.js +35 -0
  63. package/.server/server/plugins/engine/i18n/form.js.map +1 -0
  64. package/.server/server/plugins/engine/i18n/languages.d.ts +10 -0
  65. package/.server/server/plugins/engine/i18n/languages.js +19 -0
  66. package/.server/server/plugins/engine/i18n/languages.js.map +1 -0
  67. package/.server/server/plugins/engine/i18n/translations/cy.json +3 -1
  68. package/.server/server/plugins/engine/i18n/translations/en-GB.json +3 -1
  69. package/.server/server/plugins/engine/index.d.ts +3 -0
  70. package/.server/server/plugins/engine/index.js +3 -0
  71. package/.server/server/plugins/engine/index.js.map +1 -1
  72. package/.server/server/plugins/engine/models/FormModel.d.ts +0 -4
  73. package/.server/server/plugins/engine/models/FormModel.js +9 -15
  74. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  75. package/.server/server/plugins/engine/models/SummaryViewModel.js +2 -1
  76. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  77. package/.server/server/plugins/engine/outputFormatters/human/v1.js +2 -1
  78. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  79. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +2 -0
  80. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +2 -0
  81. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  82. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +1 -1
  83. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +2 -2
  84. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  85. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -1
  86. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +2 -2
  87. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  88. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +6 -3
  89. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  90. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +1 -1
  91. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +3 -3
  92. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -1
  93. package/.server/server/plugins/engine/types/schema.js +1 -0
  94. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  95. package/.server/server/plugins/engine/types.d.ts +1 -0
  96. package/.server/server/plugins/engine/types.js.map +1 -1
  97. package/.server/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  98. package/.server/server/plugins/nunjucks/filters/applyUrlParam.d.ts +9 -0
  99. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  100. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js.map +1 -0
  101. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js +17 -0
  102. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js.map +1 -0
  103. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  104. package/.server/server/plugins/nunjucks/filters/index.d.ts +1 -0
  105. package/.server/server/plugins/nunjucks/filters/index.js +1 -0
  106. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -1
  107. package/.server/server/plugins/payment/helper.d.ts +11 -1
  108. package/.server/server/plugins/payment/helper.js +18 -2
  109. package/.server/server/plugins/payment/helper.js.map +1 -1
  110. package/.server/server/plugins/payment/helper.test.js +2 -1
  111. package/.server/server/plugins/payment/helper.test.js.map +1 -1
  112. package/.server/server/plugins/payment/service.d.ts +2 -1
  113. package/.server/server/plugins/payment/service.js +5 -2
  114. package/.server/server/plugins/payment/service.js.map +1 -1
  115. package/.server/server/plugins/payment/service.test.js +7 -3
  116. package/.server/server/plugins/payment/service.test.js.map +1 -1
  117. package/.server/server/plugins/payment/types.d.ts +4 -0
  118. package/.server/server/plugins/payment/types.js +1 -0
  119. package/.server/server/plugins/payment/types.js.map +1 -1
  120. package/.server/server/plugins/postcode-lookup/models/index.d.ts +247 -3
  121. package/.server/server/plugins/postcode-lookup/models/index.js +86 -33
  122. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -1
  123. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +15 -1
  124. package/.server/server/plugins/postcode-lookup/routes/index.js +47 -15
  125. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -1
  126. package/.server/server/plugins/postcode-lookup/routes/index.test.js +84 -0
  127. package/.server/server/plugins/postcode-lookup/routes/index.test.js.map +1 -0
  128. package/.server/server/plugins/postcode-lookup/types.d.ts +12 -0
  129. package/.server/server/plugins/postcode-lookup/types.js +6 -1
  130. package/.server/server/plugins/postcode-lookup/types.js.map +1 -1
  131. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  132. package/package.json +7 -1
  133. package/src/client/javascripts/maps.js +5 -0
  134. package/src/client/javascripts/shared.js +1 -4
  135. package/src/client/stylesheets/maps.scss +4 -0
  136. package/src/client/stylesheets/shared.scss +1 -5
  137. package/src/server/constants.js +2 -1
  138. package/src/server/plugins/engine/components/ComponentCollection.ts +15 -9
  139. package/src/server/plugins/engine/components/DatePartsField.ts +16 -12
  140. package/src/server/plugins/engine/components/DeclarationField.ts +6 -4
  141. package/src/server/plugins/engine/components/EastingNorthingField.ts +156 -88
  142. package/src/server/plugins/engine/components/FormComponent.ts +4 -1
  143. package/src/server/plugins/engine/components/LatLongField.ts +129 -57
  144. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +14 -11
  145. package/src/server/plugins/engine/components/MonthYearField.ts +16 -11
  146. package/src/server/plugins/engine/components/PaymentField.ts +9 -4
  147. package/src/server/plugins/engine/components/UkAddressField.ts +29 -6
  148. package/src/server/plugins/engine/components/YesNoField.ts +6 -5
  149. package/src/server/plugins/engine/components/helpers/components.ts +1 -2
  150. package/src/server/plugins/engine/components/helpers/index.ts +19 -0
  151. package/src/server/plugins/engine/helpers.ts +51 -5
  152. package/src/server/plugins/engine/i18n/createFormTranslator.ts +43 -12
  153. package/src/server/plugins/engine/i18n/extractBaseTranslations.ts +7 -1
  154. package/src/server/plugins/engine/i18n/form.ts +54 -0
  155. package/src/server/plugins/engine/i18n/languages.ts +19 -0
  156. package/src/server/plugins/engine/i18n/translations/cy.json +3 -1
  157. package/src/server/plugins/engine/i18n/translations/en-GB.json +3 -1
  158. package/src/server/plugins/engine/index.ts +6 -0
  159. package/src/server/plugins/engine/models/FormModel.ts +9 -15
  160. package/src/server/plugins/engine/models/SummaryViewModel.ts +2 -1
  161. package/src/server/plugins/engine/outputFormatters/human/v1.ts +2 -1
  162. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +3 -0
  163. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +2 -2
  164. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +2 -2
  165. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +9 -3
  166. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +6 -3
  167. package/src/server/plugins/engine/types/schema.ts +1 -0
  168. package/src/server/plugins/engine/types.ts +1 -0
  169. package/src/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  170. package/src/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  171. package/src/server/plugins/nunjucks/filters/index.js +1 -0
  172. package/src/server/plugins/payment/helper.js +22 -2
  173. package/src/server/plugins/payment/service.js +6 -2
  174. package/src/server/plugins/payment/types.js +1 -0
  175. package/src/server/plugins/postcode-lookup/models/index.js +75 -43
  176. package/src/server/plugins/postcode-lookup/routes/index.js +73 -16
  177. package/src/server/plugins/postcode-lookup/types.js +6 -1
  178. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","names":["ControllerPath","Engine","getErrorMessage","Boom","format","parseISO","StatusCodes","Liquid","logger","FORM_VERSION_METADATA_KEY","getAnswer","stripParam","FormAction","FormStatus","engine","outputEscape","jsTruthy","ownPropertyOnly","registerFilter","template","globals","context","evaluated","evaluateTemplate","path","pageDef","pages","get","query","page","pageMap","undefined","getPageHref","name","componentDef","components","component","componentMap","isFormComponent","field","translator","answer","relevantState","model","createTranslator","proceed","request","h","nextUrl","method","payload","returnUrl","isReturnAllowed","action","Continue","Validate","nextQuery","response","isPathRelative","redirect","redirectPath","code","SEE_OTHER","MOVED_TEMPORARILY","encodeUrl","link","URL","toString","err","error","pathOrQuery","queryOnly","Error","getHref","isRelative","params","Object","entries","filter","url","value","searchParams","set","pathname","search","href","startsWith","normalisePath","trim","replace","getPage","findPage","notFound","findPath","find","getStartPath","V2","startPath","def","at","Start","startPage","checkFormStatus","isPreview","state","Live","Draft","checkEmailAddressForLiveFormSubmission","emailAddress","internal","getErrors","details","length","map","getError","detail","message","key","text","createError","componentName","getExponentialBackoffDelay","depth","BASE_DELAY_MS","CAP_DELAY_MS","delay","Math","min","pageDefMap","componentDefMap","parseAndRenderSync","getCacheService","server","getPluginOptions","cacheService","getSaveAndExitHelpers","saveAndExit","plugins","handleLegacyRedirect","targetUrl","permanent","takeover","getFormVersion","definition","metadata"],"sources":["../../../../src/server/plugins/engine/helpers.ts"],"sourcesContent":["import {\n ControllerPath,\n Engine,\n getErrorMessage,\n type ComponentDef,\n type FormDefinition,\n type Page\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type ResponseToolkit, type Server } from '@hapi/hapi'\nimport { format, parseISO } from 'date-fns'\nimport { StatusCodes } from 'http-status-codes'\nimport { type Schema, type ValidationErrorItem } from 'joi'\nimport { Liquid } from 'liquidjs'\n\nimport { logger } from '~/src/server/common/helpers/logging/logger.js'\nimport { FORM_VERSION_METADATA_KEY } from '~/src/server/constants.js'\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { stripParam } from '~/src/server/plugins/engine/pageControllers/helpers/state.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n FormStatus,\n type FormParams,\n type FormQuery,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport const engine = new Liquid({\n outputEscape: 'escape',\n jsTruthy: true,\n ownPropertyOnly: false\n})\n\nexport interface GlobalScope {\n context: FormContext\n pages: Map<string, Page>\n components: Map<string, ComponentDef>\n}\n\nengine.registerFilter('evaluate', function (template?: string) {\n if (typeof template !== 'string') {\n return template\n }\n\n const globals = this.context.globals as GlobalScope\n const evaluated = evaluateTemplate(template, globals.context)\n\n return evaluated\n})\n\nengine.registerFilter('page', function (path?: string) {\n if (typeof path !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const pageDef = globals.pages.get(path)\n\n return pageDef\n})\n\nengine.registerFilter('href', function (path: string, query?: FormQuery) {\n if (typeof path !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const page = globals.context.pageMap.get(path)\n\n if (page === undefined) {\n return\n }\n\n return getPageHref(page, query)\n})\n\nengine.registerFilter('field', function (name: string) {\n if (typeof name !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const componentDef = globals.components.get(name)\n\n return componentDef\n})\n\nengine.registerFilter('answer', function (name: string) {\n if (typeof name !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const component = globals.context.componentMap.get(name)\n\n if (!component?.isFormComponent) {\n return\n }\n\n const field = component as Field\n const { translator } = globals.context\n const answer = getAnswer(\n field,\n globals.context.relevantState,\n translator ?? field.model.createTranslator(),\n {\n format: 'summary'\n }\n )\n\n return answer\n})\n\nexport function proceed(\n request: Pick<FormContextRequest, 'method' | 'payload' | 'query'>,\n h: FormResponseToolkit,\n nextUrl: string\n) {\n const { method, payload, query } = request\n const { returnUrl } = query\n\n const isReturnAllowed =\n payload && 'action' in payload\n ? payload.action === FormAction.Continue ||\n payload.action === FormAction.Validate\n : false\n\n // On POST, strip all query params to prevent them persisting across pages.\n // On GET, forward params (minus returnUrl) so pre-population query params\n // survive dispatch redirects (e.g. ?formId= reaching the start page).\n const nextQuery =\n method === 'get' ? stripParam(query, 'returnUrl') : undefined\n\n // Redirect to return location (optional)\n const response =\n isReturnAllowed && isPathRelative(returnUrl)\n ? h.redirect(returnUrl)\n : h.redirect(redirectPath(nextUrl, nextQuery))\n\n // Redirect POST to GET to avoid resubmission\n return method === 'post'\n ? response.code(StatusCodes.SEE_OTHER)\n : response.code(StatusCodes.MOVED_TEMPORARILY)\n}\n\n/**\n * Encodes a URL, returning undefined if the process fails.\n */\nexport function encodeUrl(link?: string) {\n if (link) {\n try {\n return new URL(link).toString() // escape the search params without breaking the ? and & reserved characters in rfc2368\n } catch (err) {\n logger.error(\n err,\n `[urlEncodingFailed] Failed to encode URL: ${link} - ${getErrorMessage(err)}`\n )\n throw err\n }\n }\n}\n\n/**\n * Get page href\n */\nexport function getPageHref(\n page: PageControllerClass,\n query?: FormQuery\n): string\n\n/**\n * Get page href by path\n */\nexport function getPageHref(\n page: PageControllerClass,\n path: string,\n query?: FormQuery\n): string\n\nexport function getPageHref(\n page: PageControllerClass,\n pathOrQuery?: string | FormQuery,\n queryOnly: FormQuery = {}\n) {\n const path = typeof pathOrQuery === 'string' ? pathOrQuery : page.path\n const query = typeof pathOrQuery === 'object' ? pathOrQuery : queryOnly\n\n if (!isPathRelative(path)) {\n throw Error(`Only relative URLs are allowed: ${path}`)\n }\n\n // Return path with page href as base\n return redirectPath(page.getHref(path), query)\n}\n\n/**\n * Get redirect path with optional query params\n */\nexport function redirectPath(nextUrl: string, query: FormQuery = {}) {\n const isRelative = isPathRelative(nextUrl)\n\n // Filter string query params only\n const params = Object.entries(query).filter(\n (query): query is [string, string] => typeof query[1] === 'string'\n )\n\n // Build URL with relative path support\n const url = isRelative\n ? new URL(nextUrl, 'http://example.com')\n : new URL(nextUrl)\n\n // Append query params\n for (const [name, value] of params) {\n url.searchParams.set(name, value)\n }\n\n if (isRelative) {\n return `${url.pathname}${url.search}`\n }\n\n return url.href\n}\n\nexport function isPathRelative(path?: string) {\n return (path ?? '').startsWith('/')\n}\n\nexport function normalisePath(path = '') {\n return path\n .trim() // Trim empty spaces\n .replace(/^\\//, '') // Remove leading slash\n .replace(/\\/$/, '') // Remove trailing slash\n}\n\nexport function getPage(\n model: FormModel | undefined,\n request: FormContextRequest\n) {\n const { params } = request\n\n const page = findPage(model, `/${params.path}`)\n\n if (!page) {\n throw Boom.notFound(`No page found for /${params.path}`)\n }\n\n return page\n}\n\nexport function findPage(model: FormModel | undefined, path?: string) {\n const findPath = `/${normalisePath(path)}`\n return model?.pages.find(({ path }) => path === findPath)\n}\n\nexport function getStartPath(model?: FormModel) {\n if (model?.engine === Engine.V2) {\n const startPath = normalisePath(model.def.pages.at(0)?.path)\n return startPath ? `/${startPath}` : ControllerPath.Start\n }\n\n const startPath = normalisePath(model?.def.startPage)\n return startPath ? `/${startPath}` : ControllerPath.Start\n}\n\nexport function checkFormStatus(params?: FormParams) {\n const isPreview = !!params?.state\n\n let state = FormStatus.Live\n\n if (isPreview && params.state === FormStatus.Draft) {\n state = FormStatus.Draft\n }\n\n return {\n isPreview,\n state\n }\n}\n\nexport function checkEmailAddressForLiveFormSubmission(\n emailAddress: string | undefined,\n isPreview: boolean\n) {\n if (!emailAddress && !isPreview) {\n throw Boom.internal(\n 'An email address is required to complete the form submission'\n )\n }\n}\n\n/**\n * Parses the errors from {@link Schema.validate} so they can be rendered by govuk-frontend templates\n * @param [details] - provided by {@link Schema.validate}\n */\nexport function getErrors(\n details?: ValidationErrorItem[]\n): FormSubmissionError[] | undefined {\n if (!details?.length) {\n return\n }\n\n return details.map(getError)\n}\n\nexport function getError(detail: ValidationErrorItem): FormSubmissionError {\n const { context, message, path } = detail\n\n const name = context?.key ?? ''\n const href = `#${name}`\n\n const text = message.replace(\n /\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z)/,\n (text) => format(parseISO(text), 'd MMMM yyyy')\n )\n\n return {\n path,\n href,\n name,\n text,\n context\n }\n}\n\nexport function createError(componentName: string, message: string) {\n return {\n href: `#${componentName}`,\n name: componentName,\n text: message\n }\n}\n\n/**\n * Calculates an exponential backoff delay (in milliseconds) based on the current retry depth,\n * using a base delay of 2000ms (2 seconds) and doubling for each additional depth, while capping the delay at 25,000ms (25 seconds).\n * @param depth - The current retry depth (1, 2, 3, …)\n * @returns The calculated delay in milliseconds.\n */\nexport function getExponentialBackoffDelay(depth: number): number {\n const BASE_DELAY_MS = 2000 // 2 seconds initial delay\n const CAP_DELAY_MS = 25000 // cap each delay to 25 seconds\n const delay = BASE_DELAY_MS * 2 ** (depth - 1)\n return Math.min(delay, CAP_DELAY_MS)\n}\n\nexport function evaluateTemplate(\n template: string,\n context: FormContext\n): string {\n const globals: GlobalScope = {\n context,\n pages: context.pageDefMap,\n components: context.componentDefMap\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return engine.parseAndRenderSync(template, context.relevantState, {\n globals\n })\n}\n\nexport function getCacheService(server: Server) {\n return getPluginOptions(server).cacheService\n}\n\nexport function getSaveAndExitHelpers(server: Server) {\n return getPluginOptions(server).saveAndExit\n}\n\nexport function getPluginOptions(server: Server) {\n return server.plugins['forms-engine-plugin']\n}\n\n/**\n * Handles logging and issuing a permanent redirect for legacy routes.\n * @param h - The Hapi response toolkit.\n * @param targetUrl - The URL to redirect to.\n * @returns The Hapi response object configured for permanent redirect.\n */\nexport function handleLegacyRedirect(h: ResponseToolkit, targetUrl: string) {\n return h.redirect(targetUrl).permanent().takeover()\n}\n\n/**\n * If the page doesn't have a title, set it from the title of the first form component\n * @param def - the form definition\n */\nexport interface FormVersionMetadata {\n versionNumber: number\n createdAt: Date\n}\n\n/**\n * Extracts form version metadata from a form definition\n */\nexport function getFormVersion(\n definition: Pick<FormDefinition, 'metadata'>\n): FormVersionMetadata | undefined {\n return definition.metadata?.[FORM_VERSION_METADATA_KEY] as\n | FormVersionMetadata\n | undefined\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"helpers.js","names":["ControllerPath","Engine","getErrorMessage","Boom","format","parseISO","cy","enGB","StatusCodes","Liquid","logger","CY","EN_GB","FORM_VERSION_METADATA_KEY","getAnswer","stripParam","FormAction","FormStatus","engine","outputEscape","jsTruthy","ownPropertyOnly","registerFilter","template","globals","context","evaluated","evaluateTemplate","path","pageDef","pages","get","query","page","pageMap","undefined","getPageHref","name","componentDef","components","component","componentMap","isFormComponent","field","translator","answer","relevantState","model","createTranslator","proceed","request","h","nextUrl","method","payload","returnUrl","isReturnAllowed","action","Continue","Validate","nextQuery","response","isPathRelative","redirect","redirectPath","code","SEE_OTHER","MOVED_TEMPORARILY","encodeUrl","link","URL","toString","err","error","pathOrQuery","queryOnly","Error","getHref","isRelative","params","Object","entries","filter","url","value","searchParams","set","pathname","search","href","startsWith","normalisePath","trim","replace","getPage","findPage","notFound","findPath","find","getStartPath","V2","startPath","def","at","Start","startPage","checkFormStatus","isPreview","state","Live","Draft","checkEmailAddressForLiveFormSubmission","emailAddress","internal","getErrors","language","details","length","map","detail","getError","message","key","text","locale","toLanguageLocale","createError","componentName","getExponentialBackoffDelay","depth","BASE_DELAY_MS","CAP_DELAY_MS","delay","Math","min","pageDefMap","componentDefMap","parseAndRenderSync","getCacheService","server","getPluginOptions","cacheService","getSaveAndExitHelpers","saveAndExit","plugins","handleLegacyRedirect","targetUrl","permanent","takeover","getFormVersion","definition","metadata","payLanguageCodes","languageLocales","defaultLanguageCode","defaultLanguageLocale","toPayLanguageCode"],"sources":["../../../../src/server/plugins/engine/helpers.ts"],"sourcesContent":["import {\n ControllerPath,\n Engine,\n getErrorMessage,\n type ComponentDef,\n type FormDefinition,\n type Page\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type ResponseToolkit, type Server } from '@hapi/hapi'\nimport { format, parseISO, type Locale } from 'date-fns'\nimport { cy, enGB } from 'date-fns/locale'\nimport { StatusCodes } from 'http-status-codes'\nimport { type Schema, type ValidationErrorItem } from 'joi'\nimport { Liquid } from 'liquidjs'\n\nimport { logger } from '~/src/server/common/helpers/logging/logger.js'\nimport { CY, EN_GB, FORM_VERSION_METADATA_KEY } from '~/src/server/constants.js'\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { stripParam } from '~/src/server/plugins/engine/pageControllers/helpers/state.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n FormStatus,\n type FormParams,\n type FormQuery,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport const engine = new Liquid({\n outputEscape: 'escape',\n jsTruthy: true,\n ownPropertyOnly: false\n})\n\nexport interface GlobalScope {\n context: FormContext\n pages: Map<string, Page>\n components: Map<string, ComponentDef>\n}\n\nengine.registerFilter('evaluate', function (template?: string) {\n if (typeof template !== 'string') {\n return template\n }\n\n const globals = this.context.globals as GlobalScope\n const evaluated = evaluateTemplate(template, globals.context)\n\n return evaluated\n})\n\nengine.registerFilter('page', function (path?: string) {\n if (typeof path !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const pageDef = globals.pages.get(path)\n\n return pageDef\n})\n\nengine.registerFilter('href', function (path: string, query?: FormQuery) {\n if (typeof path !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const page = globals.context.pageMap.get(path)\n\n if (page === undefined) {\n return\n }\n\n return getPageHref(page, query)\n})\n\nengine.registerFilter('field', function (name: string) {\n if (typeof name !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const componentDef = globals.components.get(name)\n\n return componentDef\n})\n\nengine.registerFilter('answer', function (name: string) {\n if (typeof name !== 'string') {\n return\n }\n\n const globals = this.context.globals as GlobalScope\n const component = globals.context.componentMap.get(name)\n\n if (!component?.isFormComponent) {\n return\n }\n\n const field = component as Field\n const { translator } = globals.context\n const answer = getAnswer(\n field,\n globals.context.relevantState,\n translator ?? field.model.createTranslator(),\n {\n format: 'summary'\n }\n )\n\n return answer\n})\n\nexport function proceed(\n request: Pick<FormContextRequest, 'method' | 'payload' | 'query'>,\n h: FormResponseToolkit,\n nextUrl: string\n) {\n const { method, payload, query } = request\n const { returnUrl } = query\n\n const isReturnAllowed =\n payload && 'action' in payload\n ? payload.action === FormAction.Continue ||\n payload.action === FormAction.Validate\n : false\n\n // On POST, strip all query params to prevent them persisting across pages.\n // On GET, forward params (minus returnUrl) so pre-population query params\n // survive dispatch redirects (e.g. ?formId= reaching the start page).\n const nextQuery =\n method === 'get' ? stripParam(query, 'returnUrl') : undefined\n\n // Redirect to return location (optional)\n const response =\n isReturnAllowed && isPathRelative(returnUrl)\n ? h.redirect(returnUrl)\n : h.redirect(redirectPath(nextUrl, nextQuery))\n\n // Redirect POST to GET to avoid resubmission\n return method === 'post'\n ? response.code(StatusCodes.SEE_OTHER)\n : response.code(StatusCodes.MOVED_TEMPORARILY)\n}\n\n/**\n * Encodes a URL, returning undefined if the process fails.\n */\nexport function encodeUrl(link?: string) {\n if (link) {\n try {\n return new URL(link).toString() // escape the search params without breaking the ? and & reserved characters in rfc2368\n } catch (err) {\n logger.error(\n err,\n `[urlEncodingFailed] Failed to encode URL: ${link} - ${getErrorMessage(err)}`\n )\n throw err\n }\n }\n}\n\n/**\n * Get page href\n */\nexport function getPageHref(\n page: PageControllerClass,\n query?: FormQuery\n): string\n\n/**\n * Get page href by path\n */\nexport function getPageHref(\n page: PageControllerClass,\n path: string,\n query?: FormQuery\n): string\n\nexport function getPageHref(\n page: PageControllerClass,\n pathOrQuery?: string | FormQuery,\n queryOnly: FormQuery = {}\n) {\n const path = typeof pathOrQuery === 'string' ? pathOrQuery : page.path\n const query = typeof pathOrQuery === 'object' ? pathOrQuery : queryOnly\n\n if (!isPathRelative(path)) {\n throw Error(`Only relative URLs are allowed: ${path}`)\n }\n\n // Return path with page href as base\n return redirectPath(page.getHref(path), query)\n}\n\n/**\n * Get redirect path with optional query params\n */\nexport function redirectPath(nextUrl: string, query: FormQuery = {}) {\n const isRelative = isPathRelative(nextUrl)\n\n // Filter string query params only\n const params = Object.entries(query).filter(\n (query): query is [string, string] => typeof query[1] === 'string'\n )\n\n // Build URL with relative path support\n const url = isRelative\n ? new URL(nextUrl, 'http://example.com')\n : new URL(nextUrl)\n\n // Append query params\n for (const [name, value] of params) {\n url.searchParams.set(name, value)\n }\n\n if (isRelative) {\n return `${url.pathname}${url.search}`\n }\n\n return url.href\n}\n\nexport function isPathRelative(path?: string) {\n return (path ?? '').startsWith('/')\n}\n\nexport function normalisePath(path = '') {\n return path\n .trim() // Trim empty spaces\n .replace(/^\\//, '') // Remove leading slash\n .replace(/\\/$/, '') // Remove trailing slash\n}\n\nexport function getPage(\n model: FormModel | undefined,\n request: FormContextRequest\n) {\n const { params } = request\n\n const page = findPage(model, `/${params.path}`)\n\n if (!page) {\n throw Boom.notFound(`No page found for /${params.path}`)\n }\n\n return page\n}\n\nexport function findPage(model: FormModel | undefined, path?: string) {\n const findPath = `/${normalisePath(path)}`\n return model?.pages.find(({ path }) => path === findPath)\n}\n\nexport function getStartPath(model?: FormModel) {\n if (model?.engine === Engine.V2) {\n const startPath = normalisePath(model.def.pages.at(0)?.path)\n return startPath ? `/${startPath}` : ControllerPath.Start\n }\n\n const startPath = normalisePath(model?.def.startPage)\n return startPath ? `/${startPath}` : ControllerPath.Start\n}\n\nexport function checkFormStatus(params?: FormParams) {\n const isPreview = !!params?.state\n\n let state = FormStatus.Live\n\n if (isPreview && params.state === FormStatus.Draft) {\n state = FormStatus.Draft\n }\n\n return {\n isPreview,\n state\n }\n}\n\nexport function checkEmailAddressForLiveFormSubmission(\n emailAddress: string | undefined,\n isPreview: boolean\n) {\n if (!emailAddress && !isPreview) {\n throw Boom.internal(\n 'An email address is required to complete the form submission'\n )\n }\n}\n\n/**\n * Parses the errors from {@link Schema.validate} so they can be rendered by govuk-frontend templates\n * @param [details] - provided by {@link Schema.validate}\n */\nexport function getErrors(\n language: string | undefined,\n details?: ValidationErrorItem[]\n): FormSubmissionError[] | undefined {\n if (!details?.length) {\n return\n }\n\n return details.map((detail) => getError(language, detail))\n}\n\nexport function getError(\n language: string | undefined,\n detail: ValidationErrorItem\n): FormSubmissionError {\n const { context, message, path } = detail\n\n const name = context?.key ?? ''\n const href = `#${name}`\n\n const text = message.replace(\n /\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z)/,\n (text) =>\n format(parseISO(text), 'd MMMM yyyy', {\n locale: toLanguageLocale(language)\n })\n )\n\n return {\n path,\n href,\n name,\n text,\n context\n }\n}\n\nexport function createError(componentName: string, message: string) {\n return {\n href: `#${componentName}`,\n name: componentName,\n text: message\n }\n}\n\n/**\n * Calculates an exponential backoff delay (in milliseconds) based on the current retry depth,\n * using a base delay of 2000ms (2 seconds) and doubling for each additional depth, while capping the delay at 25,000ms (25 seconds).\n * @param depth - The current retry depth (1, 2, 3, …)\n * @returns The calculated delay in milliseconds.\n */\nexport function getExponentialBackoffDelay(depth: number): number {\n const BASE_DELAY_MS = 2000 // 2 seconds initial delay\n const CAP_DELAY_MS = 25000 // cap each delay to 25 seconds\n const delay = BASE_DELAY_MS * 2 ** (depth - 1)\n return Math.min(delay, CAP_DELAY_MS)\n}\n\nexport function evaluateTemplate(\n template: string,\n context: FormContext\n): string {\n const globals: GlobalScope = {\n context,\n pages: context.pageDefMap,\n components: context.componentDefMap\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return engine.parseAndRenderSync(template, context.relevantState, {\n globals\n })\n}\n\nexport function getCacheService(server: Server) {\n return getPluginOptions(server).cacheService\n}\n\nexport function getSaveAndExitHelpers(server: Server) {\n return getPluginOptions(server).saveAndExit\n}\n\nexport function getPluginOptions(server: Server) {\n return server.plugins['forms-engine-plugin']\n}\n\n/**\n * Handles logging and issuing a permanent redirect for legacy routes.\n * @param h - The Hapi response toolkit.\n * @param targetUrl - The URL to redirect to.\n * @returns The Hapi response object configured for permanent redirect.\n */\nexport function handleLegacyRedirect(h: ResponseToolkit, targetUrl: string) {\n return h.redirect(targetUrl).permanent().takeover()\n}\n\n/**\n * If the page doesn't have a title, set it from the title of the first form component\n * @param def - the form definition\n */\nexport interface FormVersionMetadata {\n versionNumber: number\n createdAt: Date\n}\n\n/**\n * Extracts form version metadata from a form definition\n */\nexport function getFormVersion(\n definition: Pick<FormDefinition, 'metadata'>\n): FormVersionMetadata | undefined {\n return definition.metadata?.[FORM_VERSION_METADATA_KEY] as\n | FormVersionMetadata\n | undefined\n}\n\nconst payLanguageCodes = {\n [EN_GB]: 'en',\n [CY]: 'cy'\n} as Record<string, string>\n\nconst languageLocales = {\n [EN_GB]: enGB,\n [CY]: cy\n} as Record<string, Locale>\n\nconst defaultLanguageCode = 'en'\nconst defaultLanguageLocale = enGB\n\n/**\n * Converts a BCP 47 language tag to the ISO 639-1 code GOV.UK Pay accepts.\n * Defaults to English if the language is not supported.\n * @param { string | undefined } language - BCP 47 language code\n * @returns {string} language code required by GOV.UK Pay\n */\nexport function toPayLanguageCode(language: string | undefined): string {\n return payLanguageCodes[language ?? 'unknown'] ?? defaultLanguageCode\n}\n\n/**\n * Converts a BCP 47 language tag to the date-fns Locale (for date formatting).\n * Defaults to English if the language is not supported.\n * @param { string | undefined } language - BCP 47 language code\n * @returns {Locale} language locale\n */\nexport function toLanguageLocale(language: string | undefined): Locale {\n return languageLocales[language ?? 'unknown'] ?? defaultLanguageLocale\n}\n\n/**\n * @import { FormsService } from '~/src/server/types.js'\n * @import { Locale } from 'date-fns/locale'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -1,5 +1,5 @@
1
- import { type FormDefinition } from '@defra/forms-model';
1
+ import { type FormDefinition, type FormMetadata } from '@defra/forms-model';
2
2
  import { type i18n } from 'i18next';
3
- import { type Translator } from '../../../../server/plugins/engine/i18n/types.js';
4
- export declare function createFormTranslator(def: FormDefinition, language: string): Translator;
5
- export declare function loadFormTranslations(def: FormDefinition, i18nInstance: i18n): void;
3
+ export declare function createFormTranslator(metadata: FormMetadata, definition: FormDefinition | undefined, language: string): import("../../../../server/plugins/engine/i18n/types.js").Translator;
4
+ export declare function loadFormTranslations(def: FormDefinition | undefined, i18nInstance: i18n): void;
5
+ export declare function extractMetadataBaseTranslations(metadata: FormMetadata | undefined, i18nInstance: i18n): void;
@@ -1,25 +1,47 @@
1
1
  import { yesNoListNoItemId, yesNoListYesItemId } from '@defra/forms-model';
2
+ import { CY } from "../../../constants.js";
2
3
  import { createTranslator } from "./createTranslator.js";
3
4
  import { extractBaseTranslations } from "./extractBaseTranslations.js";
4
5
  import { createFormI18nInstance } from "./index.js";
5
- export function createFormTranslator(def, language) {
6
- const baseTranslations = extractBaseTranslations(def);
6
+ export function createFormTranslator(metadata, definition, language) {
7
+ const baseTranslations = extractBaseTranslations(definition);
7
8
  const i18nInstance = createFormI18nInstance(baseTranslations);
8
- loadFormTranslations(def, i18nInstance);
9
- return createTranslator(i18nInstance, language);
9
+ loadFormTranslations(definition, i18nInstance);
10
+ extractMetadataBaseTranslations(metadata, i18nInstance);
11
+ const translator = createTranslator(i18nInstance, language);
12
+ return translator;
10
13
  }
11
14
  export function loadFormTranslations(def, i18nInstance) {
15
+ if (!def) {
16
+ return;
17
+ }
12
18
  const formTranslations = def.metadata?.translations;
13
19
  if (formTranslations) {
14
20
  for (const [lng, resources] of Object.entries(formTranslations)) {
15
21
  i18nInstance.addResourceBundle(lng, 'form', resources, true, true);
16
22
 
17
23
  // Temporary workaround - until we develop a better solution
18
- if (lng === 'cy') {
24
+ if (lng === CY) {
19
25
  i18nInstance.addResource(lng, 'form', `listItems.${yesNoListYesItemId}.text`, 'Ie');
20
26
  i18nInstance.addResource(lng, 'form', `listItems.${yesNoListNoItemId}.text`, 'Nage');
21
27
  }
22
28
  }
23
29
  }
24
30
  }
31
+ export function extractMetadataBaseTranslations(metadata, i18nInstance) {
32
+ if (metadata) {
33
+ const translations = {
34
+ 'form.title': metadata.title,
35
+ 'form.contact.email.address': metadata.contact?.email?.address ?? '',
36
+ 'form.contact.email.responseTime': metadata.contact?.email?.responseTime ?? '',
37
+ 'form.contact.online.url': metadata.contact?.online?.url ?? '',
38
+ 'form.contact.online.text': metadata.contact?.online?.text ?? '',
39
+ 'form.contact.phone': metadata.contact?.phone ?? '',
40
+ 'form.submissionGuidance': metadata.submissionGuidance ?? '',
41
+ 'form.privacyNoticeText': metadata.privacyNoticeText ?? '',
42
+ 'form.privacyNoticeUrl': metadata.privacyNoticeUrl ?? ''
43
+ };
44
+ i18nInstance.addResourceBundle('en-GB', 'form', translations, true, true);
45
+ }
46
+ }
25
47
  //# sourceMappingURL=createFormTranslator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createFormTranslator.js","names":["yesNoListNoItemId","yesNoListYesItemId","createTranslator","extractBaseTranslations","createFormI18nInstance","createFormTranslator","def","language","baseTranslations","i18nInstance","loadFormTranslations","formTranslations","metadata","translations","lng","resources","Object","entries","addResourceBundle","addResource"],"sources":["../../../../../src/server/plugins/engine/i18n/createFormTranslator.ts"],"sourcesContent":["import {\n yesNoListNoItemId,\n yesNoListYesItemId,\n type FormDefinition\n} from '@defra/forms-model'\nimport { type i18n } from 'i18next'\n\nimport { createTranslator } from '~/src/server/plugins/engine/i18n/createTranslator.js'\nimport { extractBaseTranslations } from '~/src/server/plugins/engine/i18n/extractBaseTranslations.js'\nimport { createFormI18nInstance } from '~/src/server/plugins/engine/i18n/index.js'\nimport {\n type FormDefinitionTranslations,\n type Translator\n} from '~/src/server/plugins/engine/i18n/types.js'\n\nexport function createFormTranslator(\n def: FormDefinition,\n language: string\n): Translator {\n const baseTranslations = extractBaseTranslations(def)\n const i18nInstance = createFormI18nInstance(baseTranslations)\n loadFormTranslations(def, i18nInstance)\n\n return createTranslator(i18nInstance, language)\n}\n\nexport function loadFormTranslations(def: FormDefinition, i18nInstance: i18n) {\n const formTranslations = def.metadata?.translations as\n | FormDefinitionTranslations\n | undefined\n\n if (formTranslations) {\n for (const [lng, resources] of Object.entries(formTranslations)) {\n i18nInstance.addResourceBundle(lng, 'form', resources, true, true)\n\n // Temporary workaround - until we develop a better solution\n if (lng === 'cy') {\n i18nInstance.addResource(\n lng,\n 'form',\n `listItems.${yesNoListYesItemId}.text`,\n 'Ie'\n )\n i18nInstance.addResource(\n lng,\n 'form',\n `listItems.${yesNoListNoItemId}.text`,\n 'Nage'\n )\n }\n }\n }\n}\n"],"mappings":"AAAA,SACEA,iBAAiB,EACjBC,kBAAkB,QAEb,oBAAoB;AAG3B,SAASC,gBAAgB;AACzB,SAASC,uBAAuB;AAChC,SAASC,sBAAsB;AAM/B,OAAO,SAASC,oBAAoBA,CAClCC,GAAmB,EACnBC,QAAgB,EACJ;EACZ,MAAMC,gBAAgB,GAAGL,uBAAuB,CAACG,GAAG,CAAC;EACrD,MAAMG,YAAY,GAAGL,sBAAsB,CAACI,gBAAgB,CAAC;EAC7DE,oBAAoB,CAACJ,GAAG,EAAEG,YAAY,CAAC;EAEvC,OAAOP,gBAAgB,CAACO,YAAY,EAAEF,QAAQ,CAAC;AACjD;AAEA,OAAO,SAASG,oBAAoBA,CAACJ,GAAmB,EAAEG,YAAkB,EAAE;EAC5E,MAAME,gBAAgB,GAAGL,GAAG,CAACM,QAAQ,EAAEC,YAE1B;EAEb,IAAIF,gBAAgB,EAAE;IACpB,KAAK,MAAM,CAACG,GAAG,EAAEC,SAAS,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACN,gBAAgB,CAAC,EAAE;MAC/DF,YAAY,CAACS,iBAAiB,CAACJ,GAAG,EAAE,MAAM,EAAEC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC;;MAElE;MACA,IAAID,GAAG,KAAK,IAAI,EAAE;QAChBL,YAAY,CAACU,WAAW,CACtBL,GAAG,EACH,MAAM,EACN,aAAab,kBAAkB,OAAO,EACtC,IACF,CAAC;QACDQ,YAAY,CAACU,WAAW,CACtBL,GAAG,EACH,MAAM,EACN,aAAad,iBAAiB,OAAO,EACrC,MACF,CAAC;MACH;IACF;EACF;AACF","ignoreList":[]}
1
+ {"version":3,"file":"createFormTranslator.js","names":["yesNoListNoItemId","yesNoListYesItemId","CY","createTranslator","extractBaseTranslations","createFormI18nInstance","createFormTranslator","metadata","definition","language","baseTranslations","i18nInstance","loadFormTranslations","extractMetadataBaseTranslations","translator","def","formTranslations","translations","lng","resources","Object","entries","addResourceBundle","addResource","title","contact","email","address","responseTime","online","url","text","phone","submissionGuidance","privacyNoticeText","privacyNoticeUrl"],"sources":["../../../../../src/server/plugins/engine/i18n/createFormTranslator.ts"],"sourcesContent":["import {\n yesNoListNoItemId,\n yesNoListYesItemId,\n type FormDefinition,\n type FormMetadata\n} from '@defra/forms-model'\nimport { type i18n } from 'i18next'\n\nimport { CY } from '~/src/server/constants.js'\nimport { createTranslator } from '~/src/server/plugins/engine/i18n/createTranslator.js'\nimport { extractBaseTranslations } from '~/src/server/plugins/engine/i18n/extractBaseTranslations.js'\nimport { createFormI18nInstance } from '~/src/server/plugins/engine/i18n/index.js'\nimport { type FormDefinitionTranslations } from '~/src/server/plugins/engine/i18n/types.js'\n\nexport function createFormTranslator(\n metadata: FormMetadata,\n definition: FormDefinition | undefined,\n language: string\n) {\n const baseTranslations = extractBaseTranslations(definition)\n const i18nInstance = createFormI18nInstance(baseTranslations)\n loadFormTranslations(definition, i18nInstance)\n extractMetadataBaseTranslations(metadata, i18nInstance)\n\n const translator = createTranslator(i18nInstance, language)\n\n return translator\n}\n\nexport function loadFormTranslations(\n def: FormDefinition | undefined,\n i18nInstance: i18n\n) {\n if (!def) {\n return\n }\n\n const formTranslations = def.metadata?.translations as\n | FormDefinitionTranslations\n | undefined\n\n if (formTranslations) {\n for (const [lng, resources] of Object.entries(formTranslations)) {\n i18nInstance.addResourceBundle(lng, 'form', resources, true, true)\n\n // Temporary workaround - until we develop a better solution\n if (lng === CY) {\n i18nInstance.addResource(\n lng,\n 'form',\n `listItems.${yesNoListYesItemId}.text`,\n 'Ie'\n )\n i18nInstance.addResource(\n lng,\n 'form',\n `listItems.${yesNoListNoItemId}.text`,\n 'Nage'\n )\n }\n }\n }\n}\n\nexport function extractMetadataBaseTranslations(\n metadata: FormMetadata | undefined,\n i18nInstance: i18n\n) {\n if (metadata) {\n const translations = {\n 'form.title': metadata.title,\n 'form.contact.email.address': metadata.contact?.email?.address ?? '',\n 'form.contact.email.responseTime':\n metadata.contact?.email?.responseTime ?? '',\n 'form.contact.online.url': metadata.contact?.online?.url ?? '',\n 'form.contact.online.text': metadata.contact?.online?.text ?? '',\n 'form.contact.phone': metadata.contact?.phone ?? '',\n 'form.submissionGuidance': metadata.submissionGuidance ?? '',\n 'form.privacyNoticeText': metadata.privacyNoticeText ?? '',\n 'form.privacyNoticeUrl': metadata.privacyNoticeUrl ?? ''\n }\n i18nInstance.addResourceBundle('en-GB', 'form', translations, true, true)\n }\n}\n"],"mappings":"AAAA,SACEA,iBAAiB,EACjBC,kBAAkB,QAGb,oBAAoB;AAG3B,SAASC,EAAE;AACX,SAASC,gBAAgB;AACzB,SAASC,uBAAuB;AAChC,SAASC,sBAAsB;AAG/B,OAAO,SAASC,oBAAoBA,CAClCC,QAAsB,EACtBC,UAAsC,EACtCC,QAAgB,EAChB;EACA,MAAMC,gBAAgB,GAAGN,uBAAuB,CAACI,UAAU,CAAC;EAC5D,MAAMG,YAAY,GAAGN,sBAAsB,CAACK,gBAAgB,CAAC;EAC7DE,oBAAoB,CAACJ,UAAU,EAAEG,YAAY,CAAC;EAC9CE,+BAA+B,CAACN,QAAQ,EAAEI,YAAY,CAAC;EAEvD,MAAMG,UAAU,GAAGX,gBAAgB,CAACQ,YAAY,EAAEF,QAAQ,CAAC;EAE3D,OAAOK,UAAU;AACnB;AAEA,OAAO,SAASF,oBAAoBA,CAClCG,GAA+B,EAC/BJ,YAAkB,EAClB;EACA,IAAI,CAACI,GAAG,EAAE;IACR;EACF;EAEA,MAAMC,gBAAgB,GAAGD,GAAG,CAACR,QAAQ,EAAEU,YAE1B;EAEb,IAAID,gBAAgB,EAAE;IACpB,KAAK,MAAM,CAACE,GAAG,EAAEC,SAAS,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACL,gBAAgB,CAAC,EAAE;MAC/DL,YAAY,CAACW,iBAAiB,CAACJ,GAAG,EAAE,MAAM,EAAEC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC;;MAElE;MACA,IAAID,GAAG,KAAKhB,EAAE,EAAE;QACdS,YAAY,CAACY,WAAW,CACtBL,GAAG,EACH,MAAM,EACN,aAAajB,kBAAkB,OAAO,EACtC,IACF,CAAC;QACDU,YAAY,CAACY,WAAW,CACtBL,GAAG,EACH,MAAM,EACN,aAAalB,iBAAiB,OAAO,EACrC,MACF,CAAC;MACH;IACF;EACF;AACF;AAEA,OAAO,SAASa,+BAA+BA,CAC7CN,QAAkC,EAClCI,YAAkB,EAClB;EACA,IAAIJ,QAAQ,EAAE;IACZ,MAAMU,YAAY,GAAG;MACnB,YAAY,EAAEV,QAAQ,CAACiB,KAAK;MAC5B,4BAA4B,EAAEjB,QAAQ,CAACkB,OAAO,EAAEC,KAAK,EAAEC,OAAO,IAAI,EAAE;MACpE,iCAAiC,EAC/BpB,QAAQ,CAACkB,OAAO,EAAEC,KAAK,EAAEE,YAAY,IAAI,EAAE;MAC7C,yBAAyB,EAAErB,QAAQ,CAACkB,OAAO,EAAEI,MAAM,EAAEC,GAAG,IAAI,EAAE;MAC9D,0BAA0B,EAAEvB,QAAQ,CAACkB,OAAO,EAAEI,MAAM,EAAEE,IAAI,IAAI,EAAE;MAChE,oBAAoB,EAAExB,QAAQ,CAACkB,OAAO,EAAEO,KAAK,IAAI,EAAE;MACnD,yBAAyB,EAAEzB,QAAQ,CAAC0B,kBAAkB,IAAI,EAAE;MAC5D,wBAAwB,EAAE1B,QAAQ,CAAC2B,iBAAiB,IAAI,EAAE;MAC1D,uBAAuB,EAAE3B,QAAQ,CAAC4B,gBAAgB,IAAI;IACxD,CAAC;IACDxB,YAAY,CAACW,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAEL,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC;EAC3E;AACF","ignoreList":[]}
@@ -1,5 +1,5 @@
1
1
  import { type FormDefinition } from '@defra/forms-model';
2
2
  import { type FormDefinitionTranslations } from '../../../../server/plugins/engine/i18n/types.js';
3
3
  type BaseTranslations = FormDefinitionTranslations[string];
4
- export declare function extractBaseTranslations(def: FormDefinition): BaseTranslations;
4
+ export declare function extractBaseTranslations(def: FormDefinition | undefined): BaseTranslations;
5
5
  export {};
@@ -5,6 +5,15 @@ export function extractBaseTranslations(def) {
5
5
  const sections = {};
6
6
  const listItems = {};
7
7
  const form = {};
8
+ if (!def) {
9
+ return {
10
+ pages,
11
+ components,
12
+ sections,
13
+ listItems,
14
+ form
15
+ };
16
+ }
8
17
  for (const page of def.pages) {
9
18
  if (page.id && page.title) {
10
19
  pages[page.id] = {
@@ -1 +1 @@
1
- {"version":3,"file":"extractBaseTranslations.js","names":["hasComponentsEvenIfNoNext","extractBaseTranslations","def","pages","components","sections","listItems","form","page","id","title","component","entry","hint","content","shortDescription","Object","keys","length","section","list","lists","item","items","text","name"],"sources":["../../../../../src/server/plugins/engine/i18n/extractBaseTranslations.ts"],"sourcesContent":["import {\n hasComponentsEvenIfNoNext,\n type FormDefinition\n} from '@defra/forms-model'\n\nimport { type FormDefinitionTranslations } from '~/src/server/plugins/engine/i18n/types.js'\n\ntype BaseTranslations = FormDefinitionTranslations[string]\n\nexport function extractBaseTranslations(def: FormDefinition): BaseTranslations {\n const pages: BaseTranslations['pages'] = {}\n const components: BaseTranslations['components'] = {}\n const sections: BaseTranslations['sections'] = {}\n const listItems: BaseTranslations['listItems'] = {}\n const form: BaseTranslations['form'] = {}\n\n for (const page of def.pages) {\n if (page.id && page.title) {\n pages[page.id] = { title: page.title }\n }\n\n if (hasComponentsEvenIfNoNext(page)) {\n for (const component of page.components) {\n if (!component.id) continue\n\n const entry: BaseTranslations['components'][string] = {}\n\n if (component.title) entry.title = component.title\n if ('hint' in component && component.hint) entry.hint = component.hint\n if ('content' in component && component.content) {\n entry.content = component.content\n }\n if ('shortDescription' in component && component.shortDescription) {\n entry.shortDescription = component.shortDescription\n }\n\n if (Object.keys(entry).length) {\n components[component.id] = entry\n }\n }\n }\n }\n\n for (const section of def.sections) {\n if (section.id && section.title) {\n sections[section.id] = { title: section.title }\n }\n }\n\n for (const list of def.lists) {\n for (const item of list.items) {\n if (item.id && item.text) {\n listItems[item.id] = { text: item.text }\n }\n }\n }\n\n form.title = def.name ?? ''\n\n return { pages, components, sections, listItems, form }\n}\n"],"mappings":"AAAA,SACEA,yBAAyB,QAEpB,oBAAoB;AAM3B,OAAO,SAASC,uBAAuBA,CAACC,GAAmB,EAAoB;EAC7E,MAAMC,KAAgC,GAAG,CAAC,CAAC;EAC3C,MAAMC,UAA0C,GAAG,CAAC,CAAC;EACrD,MAAMC,QAAsC,GAAG,CAAC,CAAC;EACjD,MAAMC,SAAwC,GAAG,CAAC,CAAC;EACnD,MAAMC,IAA8B,GAAG,CAAC,CAAC;EAEzC,KAAK,MAAMC,IAAI,IAAIN,GAAG,CAACC,KAAK,EAAE;IAC5B,IAAIK,IAAI,CAACC,EAAE,IAAID,IAAI,CAACE,KAAK,EAAE;MACzBP,KAAK,CAACK,IAAI,CAACC,EAAE,CAAC,GAAG;QAAEC,KAAK,EAAEF,IAAI,CAACE;MAAM,CAAC;IACxC;IAEA,IAAIV,yBAAyB,CAACQ,IAAI,CAAC,EAAE;MACnC,KAAK,MAAMG,SAAS,IAAIH,IAAI,CAACJ,UAAU,EAAE;QACvC,IAAI,CAACO,SAAS,CAACF,EAAE,EAAE;QAEnB,MAAMG,KAA6C,GAAG,CAAC,CAAC;QAExD,IAAID,SAAS,CAACD,KAAK,EAAEE,KAAK,CAACF,KAAK,GAAGC,SAAS,CAACD,KAAK;QAClD,IAAI,MAAM,IAAIC,SAAS,IAAIA,SAAS,CAACE,IAAI,EAAED,KAAK,CAACC,IAAI,GAAGF,SAAS,CAACE,IAAI;QACtE,IAAI,SAAS,IAAIF,SAAS,IAAIA,SAAS,CAACG,OAAO,EAAE;UAC/CF,KAAK,CAACE,OAAO,GAAGH,SAAS,CAACG,OAAO;QACnC;QACA,IAAI,kBAAkB,IAAIH,SAAS,IAAIA,SAAS,CAACI,gBAAgB,EAAE;UACjEH,KAAK,CAACG,gBAAgB,GAAGJ,SAAS,CAACI,gBAAgB;QACrD;QAEA,IAAIC,MAAM,CAACC,IAAI,CAACL,KAAK,CAAC,CAACM,MAAM,EAAE;UAC7Bd,UAAU,CAACO,SAAS,CAACF,EAAE,CAAC,GAAGG,KAAK;QAClC;MACF;IACF;EACF;EAEA,KAAK,MAAMO,OAAO,IAAIjB,GAAG,CAACG,QAAQ,EAAE;IAClC,IAAIc,OAAO,CAACV,EAAE,IAAIU,OAAO,CAACT,KAAK,EAAE;MAC/BL,QAAQ,CAACc,OAAO,CAACV,EAAE,CAAC,GAAG;QAAEC,KAAK,EAAES,OAAO,CAACT;MAAM,CAAC;IACjD;EACF;EAEA,KAAK,MAAMU,IAAI,IAAIlB,GAAG,CAACmB,KAAK,EAAE;IAC5B,KAAK,MAAMC,IAAI,IAAIF,IAAI,CAACG,KAAK,EAAE;MAC7B,IAAID,IAAI,CAACb,EAAE,IAAIa,IAAI,CAACE,IAAI,EAAE;QACxBlB,SAAS,CAACgB,IAAI,CAACb,EAAE,CAAC,GAAG;UAAEe,IAAI,EAAEF,IAAI,CAACE;QAAK,CAAC;MAC1C;IACF;EACF;EAEAjB,IAAI,CAACG,KAAK,GAAGR,GAAG,CAACuB,IAAI,IAAI,EAAE;EAE3B,OAAO;IAAEtB,KAAK;IAAEC,UAAU;IAAEC,QAAQ;IAAEC,SAAS;IAAEC;EAAK,CAAC;AACzD","ignoreList":[]}
1
+ {"version":3,"file":"extractBaseTranslations.js","names":["hasComponentsEvenIfNoNext","extractBaseTranslations","def","pages","components","sections","listItems","form","page","id","title","component","entry","hint","content","shortDescription","Object","keys","length","section","list","lists","item","items","text","name"],"sources":["../../../../../src/server/plugins/engine/i18n/extractBaseTranslations.ts"],"sourcesContent":["import {\n hasComponentsEvenIfNoNext,\n type FormDefinition\n} from '@defra/forms-model'\n\nimport { type FormDefinitionTranslations } from '~/src/server/plugins/engine/i18n/types.js'\n\ntype BaseTranslations = FormDefinitionTranslations[string]\n\nexport function extractBaseTranslations(\n def: FormDefinition | undefined\n): BaseTranslations {\n const pages: BaseTranslations['pages'] = {}\n const components: BaseTranslations['components'] = {}\n const sections: BaseTranslations['sections'] = {}\n const listItems: BaseTranslations['listItems'] = {}\n const form: BaseTranslations['form'] = {}\n\n if (!def) {\n return { pages, components, sections, listItems, form }\n }\n\n for (const page of def.pages) {\n if (page.id && page.title) {\n pages[page.id] = { title: page.title }\n }\n\n if (hasComponentsEvenIfNoNext(page)) {\n for (const component of page.components) {\n if (!component.id) continue\n\n const entry: BaseTranslations['components'][string] = {}\n\n if (component.title) entry.title = component.title\n if ('hint' in component && component.hint) entry.hint = component.hint\n if ('content' in component && component.content) {\n entry.content = component.content\n }\n if ('shortDescription' in component && component.shortDescription) {\n entry.shortDescription = component.shortDescription\n }\n\n if (Object.keys(entry).length) {\n components[component.id] = entry\n }\n }\n }\n }\n\n for (const section of def.sections) {\n if (section.id && section.title) {\n sections[section.id] = { title: section.title }\n }\n }\n\n for (const list of def.lists) {\n for (const item of list.items) {\n if (item.id && item.text) {\n listItems[item.id] = { text: item.text }\n }\n }\n }\n\n form.title = def.name ?? ''\n\n return { pages, components, sections, listItems, form }\n}\n"],"mappings":"AAAA,SACEA,yBAAyB,QAEpB,oBAAoB;AAM3B,OAAO,SAASC,uBAAuBA,CACrCC,GAA+B,EACb;EAClB,MAAMC,KAAgC,GAAG,CAAC,CAAC;EAC3C,MAAMC,UAA0C,GAAG,CAAC,CAAC;EACrD,MAAMC,QAAsC,GAAG,CAAC,CAAC;EACjD,MAAMC,SAAwC,GAAG,CAAC,CAAC;EACnD,MAAMC,IAA8B,GAAG,CAAC,CAAC;EAEzC,IAAI,CAACL,GAAG,EAAE;IACR,OAAO;MAAEC,KAAK;MAAEC,UAAU;MAAEC,QAAQ;MAAEC,SAAS;MAAEC;IAAK,CAAC;EACzD;EAEA,KAAK,MAAMC,IAAI,IAAIN,GAAG,CAACC,KAAK,EAAE;IAC5B,IAAIK,IAAI,CAACC,EAAE,IAAID,IAAI,CAACE,KAAK,EAAE;MACzBP,KAAK,CAACK,IAAI,CAACC,EAAE,CAAC,GAAG;QAAEC,KAAK,EAAEF,IAAI,CAACE;MAAM,CAAC;IACxC;IAEA,IAAIV,yBAAyB,CAACQ,IAAI,CAAC,EAAE;MACnC,KAAK,MAAMG,SAAS,IAAIH,IAAI,CAACJ,UAAU,EAAE;QACvC,IAAI,CAACO,SAAS,CAACF,EAAE,EAAE;QAEnB,MAAMG,KAA6C,GAAG,CAAC,CAAC;QAExD,IAAID,SAAS,CAACD,KAAK,EAAEE,KAAK,CAACF,KAAK,GAAGC,SAAS,CAACD,KAAK;QAClD,IAAI,MAAM,IAAIC,SAAS,IAAIA,SAAS,CAACE,IAAI,EAAED,KAAK,CAACC,IAAI,GAAGF,SAAS,CAACE,IAAI;QACtE,IAAI,SAAS,IAAIF,SAAS,IAAIA,SAAS,CAACG,OAAO,EAAE;UAC/CF,KAAK,CAACE,OAAO,GAAGH,SAAS,CAACG,OAAO;QACnC;QACA,IAAI,kBAAkB,IAAIH,SAAS,IAAIA,SAAS,CAACI,gBAAgB,EAAE;UACjEH,KAAK,CAACG,gBAAgB,GAAGJ,SAAS,CAACI,gBAAgB;QACrD;QAEA,IAAIC,MAAM,CAACC,IAAI,CAACL,KAAK,CAAC,CAACM,MAAM,EAAE;UAC7Bd,UAAU,CAACO,SAAS,CAACF,EAAE,CAAC,GAAGG,KAAK;QAClC;MACF;IACF;EACF;EAEA,KAAK,MAAMO,OAAO,IAAIjB,GAAG,CAACG,QAAQ,EAAE;IAClC,IAAIc,OAAO,CAACV,EAAE,IAAIU,OAAO,CAACT,KAAK,EAAE;MAC/BL,QAAQ,CAACc,OAAO,CAACV,EAAE,CAAC,GAAG;QAAEC,KAAK,EAAES,OAAO,CAACT;MAAM,CAAC;IACjD;EACF;EAEA,KAAK,MAAMU,IAAI,IAAIlB,GAAG,CAACmB,KAAK,EAAE;IAC5B,KAAK,MAAMC,IAAI,IAAIF,IAAI,CAACG,KAAK,EAAE;MAC7B,IAAID,IAAI,CAACb,EAAE,IAAIa,IAAI,CAACE,IAAI,EAAE;QACxBlB,SAAS,CAACgB,IAAI,CAACb,EAAE,CAAC,GAAG;UAAEe,IAAI,EAAEF,IAAI,CAACE;QAAK,CAAC;MAC1C;IACF;EACF;EAEAjB,IAAI,CAACG,KAAK,GAAGR,GAAG,CAACuB,IAAI,IAAI,EAAE;EAE3B,OAAO;IAAEtB,KAAK;IAAEC,UAAU;IAAEC,QAAQ;IAAEC,SAAS;IAAEC;EAAK,CAAC;AACzD","ignoreList":[]}
@@ -0,0 +1,13 @@
1
+ import { type FormMetadata, type FormStatus } from '@defra/forms-model';
2
+ import { type Translator } from '../../../../server/plugins/engine/i18n/types.js';
3
+ import { type AnyFormRequest } from '../../../../server/plugins/engine/types.js';
4
+ /**
5
+ * Get translator for plugin, for the current form's metadata (as well as the plugin-specific boilerplate).
6
+ * This is an async call so we can read the form definition inside this call.
7
+ * See docs/features/multi-language.md for the language and translation flow overview.
8
+ * @param {AnyFormRequest} request
9
+ * @param {FormMetadata} metadata - the metadata of the form
10
+ * @param {FormStatus} status - the form status to use when retrieving the definition
11
+ * @param {string} language - the language to use for the translator
12
+ */
13
+ export declare function getCachedPluginTranslator(request: AnyFormRequest, metadata: FormMetadata, status: FormStatus, language: string): Promise<Translator>;
@@ -0,0 +1,35 @@
1
+ import Boom from '@hapi/boom';
2
+ import { LRUCache } from 'lru-cache';
3
+ import { getPluginOptions } from "../helpers.js";
4
+ import { createFormTranslator } from "./createFormTranslator.js";
5
+ const cache = new LRUCache({
6
+ max: 100,
7
+ ttl: 1000 * 60 * 15 // 15 minutes
8
+ });
9
+
10
+ /**
11
+ * Get translator for plugin, for the current form's metadata (as well as the plugin-specific boilerplate).
12
+ * This is an async call so we can read the form definition inside this call.
13
+ * See docs/features/multi-language.md for the language and translation flow overview.
14
+ * @param {AnyFormRequest} request
15
+ * @param {FormMetadata} metadata - the metadata of the form
16
+ * @param {FormStatus} status - the form status to use when retrieving the definition
17
+ * @param {string} language - the language to use for the translator
18
+ */
19
+ export async function getCachedPluginTranslator(request, metadata, status, language) {
20
+ const key = `${metadata.id}-${status}-${language}-plugin-meta`;
21
+ if (cache.has(key)) {
22
+ return cache.get(key);
23
+ }
24
+ const {
25
+ services
26
+ } = getPluginOptions(request.server);
27
+ const definition = await services?.formsService.getFormDefinition(metadata.id, status);
28
+ if (!definition) {
29
+ throw Boom.notFound(`FormDefinition not found for form ${metadata.id} and status ${status}`);
30
+ }
31
+ const translator = createFormTranslator(metadata, definition, language);
32
+ cache.set(key, translator);
33
+ return translator;
34
+ }
35
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","names":["Boom","LRUCache","getPluginOptions","createFormTranslator","cache","max","ttl","getCachedPluginTranslator","request","metadata","status","language","key","id","has","get","services","server","definition","formsService","getFormDefinition","notFound","translator","set"],"sources":["../../../../../src/server/plugins/engine/i18n/form.ts"],"sourcesContent":["import { type FormMetadata, type FormStatus } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { LRUCache } from 'lru-cache'\n\nimport { getPluginOptions } from '~/src/server/plugins/engine/helpers.js'\nimport { createFormTranslator } from '~/src/server/plugins/engine/i18n/createFormTranslator.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type AnyFormRequest } from '~/src/server/plugins/engine/types.js'\n\nconst cache = new LRUCache({\n max: 100,\n ttl: 1000 * 60 * 15 // 15 minutes\n})\n\n/**\n * Get translator for plugin, for the current form's metadata (as well as the plugin-specific boilerplate).\n * This is an async call so we can read the form definition inside this call.\n * See docs/features/multi-language.md for the language and translation flow overview.\n * @param {AnyFormRequest} request\n * @param {FormMetadata} metadata - the metadata of the form\n * @param {FormStatus} status - the form status to use when retrieving the definition\n * @param {string} language - the language to use for the translator\n */\nexport async function getCachedPluginTranslator(\n request: AnyFormRequest,\n metadata: FormMetadata,\n status: FormStatus,\n language: string\n) {\n const key = `${metadata.id}-${status}-${language}-plugin-meta`\n\n if (cache.has(key)) {\n return cache.get(key) as unknown as Translator\n }\n\n const { services } = getPluginOptions(request.server)\n\n const definition = await services?.formsService.getFormDefinition(\n metadata.id,\n status\n )\n\n if (!definition) {\n throw Boom.notFound(\n `FormDefinition not found for form ${metadata.id} and status ${status}`\n )\n }\n\n const translator = createFormTranslator(metadata, definition, language)\n\n cache.set(key, translator)\n\n return translator\n}\n"],"mappings":"AACA,OAAOA,IAAI,MAAM,YAAY;AAC7B,SAASC,QAAQ,QAAQ,WAAW;AAEpC,SAASC,gBAAgB;AACzB,SAASC,oBAAoB;AAI7B,MAAMC,KAAK,GAAG,IAAIH,QAAQ,CAAC;EACzBI,GAAG,EAAE,GAAG;EACRC,GAAG,EAAE,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC;AACtB,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,yBAAyBA,CAC7CC,OAAuB,EACvBC,QAAsB,EACtBC,MAAkB,EAClBC,QAAgB,EAChB;EACA,MAAMC,GAAG,GAAG,GAAGH,QAAQ,CAACI,EAAE,IAAIH,MAAM,IAAIC,QAAQ,cAAc;EAE9D,IAAIP,KAAK,CAACU,GAAG,CAACF,GAAG,CAAC,EAAE;IAClB,OAAOR,KAAK,CAACW,GAAG,CAACH,GAAG,CAAC;EACvB;EAEA,MAAM;IAAEI;EAAS,CAAC,GAAGd,gBAAgB,CAACM,OAAO,CAACS,MAAM,CAAC;EAErD,MAAMC,UAAU,GAAG,MAAMF,QAAQ,EAAEG,YAAY,CAACC,iBAAiB,CAC/DX,QAAQ,CAACI,EAAE,EACXH,MACF,CAAC;EAED,IAAI,CAACQ,UAAU,EAAE;IACf,MAAMlB,IAAI,CAACqB,QAAQ,CACjB,qCAAqCZ,QAAQ,CAACI,EAAE,eAAeH,MAAM,EACvE,CAAC;EACH;EAEA,MAAMY,UAAU,GAAGnB,oBAAoB,CAACM,QAAQ,EAAES,UAAU,EAAEP,QAAQ,CAAC;EAEvEP,KAAK,CAACmB,GAAG,CAACX,GAAG,EAAEU,UAAU,CAAC;EAE1B,OAAOA,UAAU;AACnB","ignoreList":[]}
@@ -0,0 +1,10 @@
1
+ import { type FormDefinition } from '@defra/forms-model';
2
+ /**
3
+ * Return a list of available languages.
4
+ * Empty array if only English, or an array of English and Welsh if translations have been defined.
5
+ * @param {FormDefinition} def
6
+ */
7
+ export declare function getAvailableLanguages(def: FormDefinition): {
8
+ name: string;
9
+ code: string;
10
+ }[];
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Return a list of available languages.
3
+ * Empty array if only English, or an array of English and Welsh if translations have been defined.
4
+ * @param {FormDefinition} def
5
+ */
6
+ export function getAvailableLanguages(def) {
7
+ // @ts-expect-error - dynamic property
8
+ if (def.metadata?.translations?.cy) {
9
+ return [{
10
+ name: 'English',
11
+ code: 'en-GB'
12
+ }, {
13
+ name: 'Cymraeg',
14
+ code: 'cy'
15
+ }];
16
+ }
17
+ return [];
18
+ }
19
+ //# sourceMappingURL=languages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"languages.js","names":["getAvailableLanguages","def","metadata","translations","cy","name","code"],"sources":["../../../../../src/server/plugins/engine/i18n/languages.ts"],"sourcesContent":["import { type FormDefinition } from '@defra/forms-model'\n\n/**\n * Return a list of available languages.\n * Empty array if only English, or an array of English and Welsh if translations have been defined.\n * @param {FormDefinition} def\n */\nexport function getAvailableLanguages(\n def: FormDefinition\n): { name: string; code: string }[] {\n // @ts-expect-error - dynamic property\n if (def.metadata?.translations?.cy) {\n return [\n { name: 'English', code: 'en-GB' },\n { name: 'Cymraeg', code: 'cy' }\n ]\n }\n return []\n}\n"],"mappings":"AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,qBAAqBA,CACnCC,GAAmB,EACe;EAClC;EACA,IAAIA,GAAG,CAACC,QAAQ,EAAEC,YAAY,EAAEC,EAAE,EAAE;IAClC,OAAO,CACL;MAAEC,IAAI,EAAE,SAAS;MAAEC,IAAI,EAAE;IAAQ,CAAC,EAClC;MAAED,IAAI,EAAE,SAAS;MAAEC,IAAI,EAAE;IAAK,CAAC,CAChC;EACH;EACA,OAAO,EAAE;AACX","ignoreList":[]}
@@ -29,7 +29,9 @@
29
29
  "continue": "Parhau",
30
30
  "saveAndExit": "Cadw a gadael",
31
31
  "draft": "ddrafft",
32
- "live": "fyw"
32
+ "live": "fyw",
33
+ "or": "neu",
34
+ "and": "a"
33
35
  },
34
36
 
35
37
  "pages": {
@@ -29,7 +29,9 @@
29
29
  "continue": "Continue",
30
30
  "saveAndExit": "Save and exit",
31
31
  "draft": "draft",
32
- "live": "live"
32
+ "live": "live",
33
+ "or": "or",
34
+ "and": "and"
33
35
  },
34
36
 
35
37
  "pages": {
@@ -5,6 +5,9 @@ export { getPageHref } from '../../../server/plugins/engine/helpers.js';
5
5
  export { context } from '../../../server/plugins/nunjucks/context.js';
6
6
  export { getFirstJourneyPage, getFormContext, getFormModel, resolveFormModel } from '../../../server/plugins/engine/form-context.js';
7
7
  export * from '../../../server/plugins/engine/form-availability.js';
8
+ export { getAvailableLanguages } from '../../../server/plugins/engine/i18n/languages.js';
9
+ export { extractMetadataBaseTranslations, loadFormTranslations } from '../../../server/plugins/engine/i18n/createFormTranslator.js';
10
+ export { extractBaseTranslations } from '../../../server/plugins/engine/i18n/extractBaseTranslations.js';
8
11
  export declare const VIEW_PATH = "src/server/plugins/engine/views";
9
12
  export declare const PLUGIN_PATH = "node_modules/@defra/forms-engine-plugin";
10
13
  export declare const STATE_NOT_YET_VALIDATED = "__stateNotYetValidated";
@@ -7,6 +7,9 @@ export { getPageHref } from "./helpers.js";
7
7
  export { context } from "../nunjucks/context.js";
8
8
  export { getFirstJourneyPage, getFormContext, getFormModel, resolveFormModel } from "./form-context.js";
9
9
  export * from "./form-availability.js";
10
+ export { getAvailableLanguages } from "./i18n/languages.js";
11
+ export { extractMetadataBaseTranslations, loadFormTranslations } from "./i18n/createFormTranslator.js";
12
+ export { extractBaseTranslations } from "./i18n/extractBaseTranslations.js";
10
13
  const globals = {
11
14
  checkComponentTemplates,
12
15
  checkErrorTemplates,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["markdownToHtml","engine","plugin","checkComponentTemplates","checkErrorTemplates","evaluate","govukRebrand","tGlobal","filters","getPageHref","context","getFirstJourneyPage","getFormContext","getFormModel","resolveFormModel","globals","t","VIEW_PATH","PLUGIN_PATH","STATE_NOT_YET_VALIDATED","CURRENT_PAGE_PATH_KEY","MAGIC_LINK_GROUP_ID","prepareNunjucksEnvironment","env","pluginOptions","name","nunjucksFilter","Object","entries","addFilter","text","startingHeaderLevel","baseUrl","nunjucksGlobal","addGlobal","filter","registerFilter","forEach","fn"],"sources":["../../../../src/server/plugins/engine/index.ts"],"sourcesContent":["import { markdownToHtml } from '@defra/forms-model'\nimport { type Environment } from 'nunjucks'\n\nimport { engine } from '~/src/server/plugins/engine/helpers.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand,\n tGlobal\n} from '~/src/server/plugins/nunjucks/environment.js'\nimport * as filters from '~/src/server/plugins/nunjucks/filters/index.js'\n\nexport { getPageHref } from '~/src/server/plugins/engine/helpers.js'\nexport { context } from '~/src/server/plugins/nunjucks/context.js'\nexport {\n getFirstJourneyPage,\n getFormContext,\n getFormModel,\n resolveFormModel\n} from '~/src/server/plugins/engine/form-context.js'\nexport * from '~/src/server/plugins/engine/form-availability.js'\n\nconst globals = {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand,\n t: tGlobal\n}\n\nexport const VIEW_PATH = 'src/server/plugins/engine/views'\nexport const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin'\n\nexport const STATE_NOT_YET_VALIDATED = '__stateNotYetValidated'\nexport const CURRENT_PAGE_PATH_KEY = '__currentPagePath'\nexport const MAGIC_LINK_GROUP_ID = '__magicLinkGroupId'\n\nexport const prepareNunjucksEnvironment = function (\n env: Environment,\n pluginOptions: PluginOptions\n) {\n for (const [name, nunjucksFilter] of Object.entries(filters)) {\n env.addFilter(name, nunjucksFilter)\n }\n\n env.addFilter('markdown', (text: string, startingHeaderLevel?: number) =>\n markdownToHtml(text, {\n baseUrl: pluginOptions.baseUrl,\n startingHeaderLevel\n })\n )\n\n for (const [name, nunjucksGlobal] of Object.entries(globals)) {\n env.addGlobal(name, nunjucksGlobal)\n }\n\n // Apply any additional filters to both the liquid and nunjucks engines\n if (pluginOptions.filters) {\n for (const [name, filter] of Object.entries(pluginOptions.filters)) {\n env.addFilter(name, filter)\n engine.registerFilter(name, filter)\n }\n }\n\n // Apply any additional globals to nunjucks engines\n if (pluginOptions.globals) {\n Object.entries(pluginOptions.globals).forEach(([name, fn]) => {\n env.addGlobal(name, fn)\n })\n }\n}\n\nexport default plugin\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,oBAAoB;AAGnD,SAASC,MAAM;AACf,SAASC,MAAM;AAEf,SACEC,uBAAuB,EACvBC,mBAAmB,EACnBC,QAAQ,EACRC,YAAY,EACZC,OAAO;AAET,OAAO,KAAKC,OAAO;AAEnB,SAASC,WAAW;AACpB,SAASC,OAAO;AAChB,SACEC,mBAAmB,EACnBC,cAAc,EACdC,YAAY,EACZC,gBAAgB;AAElB;AAEA,MAAMC,OAAO,GAAG;EACdZ,uBAAuB;EACvBC,mBAAmB;EACnBC,QAAQ;EACRC,YAAY;EACZU,CAAC,EAAET;AACL,CAAC;AAED,OAAO,MAAMU,SAAS,GAAG,iCAAiC;AAC1D,OAAO,MAAMC,WAAW,GAAG,yCAAyC;AAEpE,OAAO,MAAMC,uBAAuB,GAAG,wBAAwB;AAC/D,OAAO,MAAMC,qBAAqB,GAAG,mBAAmB;AACxD,OAAO,MAAMC,mBAAmB,GAAG,oBAAoB;AAEvD,OAAO,MAAMC,0BAA0B,GAAG,SAAAA,CACxCC,GAAgB,EAChBC,aAA4B,EAC5B;EACA,KAAK,MAAM,CAACC,IAAI,EAAEC,cAAc,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACpB,OAAO,CAAC,EAAE;IAC5De,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEC,cAAc,CAAC;EACrC;EAEAH,GAAG,CAACM,SAAS,CAAC,UAAU,EAAE,CAACC,IAAY,EAAEC,mBAA4B,KACnE/B,cAAc,CAAC8B,IAAI,EAAE;IACnBE,OAAO,EAAER,aAAa,CAACQ,OAAO;IAC9BD;EACF,CAAC,CACH,CAAC;EAED,KAAK,MAAM,CAACN,IAAI,EAAEQ,cAAc,CAAC,IAAIN,MAAM,CAACC,OAAO,CAACb,OAAO,CAAC,EAAE;IAC5DQ,GAAG,CAACW,SAAS,CAACT,IAAI,EAAEQ,cAAc,CAAC;EACrC;;EAEA;EACA,IAAIT,aAAa,CAAChB,OAAO,EAAE;IACzB,KAAK,MAAM,CAACiB,IAAI,EAAEU,MAAM,CAAC,IAAIR,MAAM,CAACC,OAAO,CAACJ,aAAa,CAAChB,OAAO,CAAC,EAAE;MAClEe,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEU,MAAM,CAAC;MAC3BlC,MAAM,CAACmC,cAAc,CAACX,IAAI,EAAEU,MAAM,CAAC;IACrC;EACF;;EAEA;EACA,IAAIX,aAAa,CAACT,OAAO,EAAE;IACzBY,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACT,OAAO,CAAC,CAACsB,OAAO,CAAC,CAAC,CAACZ,IAAI,EAAEa,EAAE,CAAC,KAAK;MAC5Df,GAAG,CAACW,SAAS,CAACT,IAAI,EAAEa,EAAE,CAAC;IACzB,CAAC,CAAC;EACJ;AACF,CAAC;AAED,eAAepC,MAAM","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["markdownToHtml","engine","plugin","checkComponentTemplates","checkErrorTemplates","evaluate","govukRebrand","tGlobal","filters","getPageHref","context","getFirstJourneyPage","getFormContext","getFormModel","resolveFormModel","getAvailableLanguages","extractMetadataBaseTranslations","loadFormTranslations","extractBaseTranslations","globals","t","VIEW_PATH","PLUGIN_PATH","STATE_NOT_YET_VALIDATED","CURRENT_PAGE_PATH_KEY","MAGIC_LINK_GROUP_ID","prepareNunjucksEnvironment","env","pluginOptions","name","nunjucksFilter","Object","entries","addFilter","text","startingHeaderLevel","baseUrl","nunjucksGlobal","addGlobal","filter","registerFilter","forEach","fn"],"sources":["../../../../src/server/plugins/engine/index.ts"],"sourcesContent":["import { markdownToHtml } from '@defra/forms-model'\nimport { type Environment } from 'nunjucks'\n\nimport { engine } from '~/src/server/plugins/engine/helpers.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand,\n tGlobal\n} from '~/src/server/plugins/nunjucks/environment.js'\nimport * as filters from '~/src/server/plugins/nunjucks/filters/index.js'\n\nexport { getPageHref } from '~/src/server/plugins/engine/helpers.js'\nexport { context } from '~/src/server/plugins/nunjucks/context.js'\nexport {\n getFirstJourneyPage,\n getFormContext,\n getFormModel,\n resolveFormModel\n} from '~/src/server/plugins/engine/form-context.js'\nexport * from '~/src/server/plugins/engine/form-availability.js'\nexport { getAvailableLanguages } from '~/src/server/plugins/engine/i18n/languages.js'\nexport {\n extractMetadataBaseTranslations,\n loadFormTranslations\n} from '~/src/server/plugins/engine/i18n/createFormTranslator.js'\nexport { extractBaseTranslations } from '~/src/server/plugins/engine/i18n/extractBaseTranslations.js'\n\nconst globals = {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand,\n t: tGlobal\n}\n\nexport const VIEW_PATH = 'src/server/plugins/engine/views'\nexport const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin'\n\nexport const STATE_NOT_YET_VALIDATED = '__stateNotYetValidated'\nexport const CURRENT_PAGE_PATH_KEY = '__currentPagePath'\nexport const MAGIC_LINK_GROUP_ID = '__magicLinkGroupId'\n\nexport const prepareNunjucksEnvironment = function (\n env: Environment,\n pluginOptions: PluginOptions\n) {\n for (const [name, nunjucksFilter] of Object.entries(filters)) {\n env.addFilter(name, nunjucksFilter)\n }\n\n env.addFilter('markdown', (text: string, startingHeaderLevel?: number) =>\n markdownToHtml(text, {\n baseUrl: pluginOptions.baseUrl,\n startingHeaderLevel\n })\n )\n\n for (const [name, nunjucksGlobal] of Object.entries(globals)) {\n env.addGlobal(name, nunjucksGlobal)\n }\n\n // Apply any additional filters to both the liquid and nunjucks engines\n if (pluginOptions.filters) {\n for (const [name, filter] of Object.entries(pluginOptions.filters)) {\n env.addFilter(name, filter)\n engine.registerFilter(name, filter)\n }\n }\n\n // Apply any additional globals to nunjucks engines\n if (pluginOptions.globals) {\n Object.entries(pluginOptions.globals).forEach(([name, fn]) => {\n env.addGlobal(name, fn)\n })\n }\n}\n\nexport default plugin\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,oBAAoB;AAGnD,SAASC,MAAM;AACf,SAASC,MAAM;AAEf,SACEC,uBAAuB,EACvBC,mBAAmB,EACnBC,QAAQ,EACRC,YAAY,EACZC,OAAO;AAET,OAAO,KAAKC,OAAO;AAEnB,SAASC,WAAW;AACpB,SAASC,OAAO;AAChB,SACEC,mBAAmB,EACnBC,cAAc,EACdC,YAAY,EACZC,gBAAgB;AAElB;AACA,SAASC,qBAAqB;AAC9B,SACEC,+BAA+B,EAC/BC,oBAAoB;AAEtB,SAASC,uBAAuB;AAEhC,MAAMC,OAAO,GAAG;EACdhB,uBAAuB;EACvBC,mBAAmB;EACnBC,QAAQ;EACRC,YAAY;EACZc,CAAC,EAAEb;AACL,CAAC;AAED,OAAO,MAAMc,SAAS,GAAG,iCAAiC;AAC1D,OAAO,MAAMC,WAAW,GAAG,yCAAyC;AAEpE,OAAO,MAAMC,uBAAuB,GAAG,wBAAwB;AAC/D,OAAO,MAAMC,qBAAqB,GAAG,mBAAmB;AACxD,OAAO,MAAMC,mBAAmB,GAAG,oBAAoB;AAEvD,OAAO,MAAMC,0BAA0B,GAAG,SAAAA,CACxCC,GAAgB,EAChBC,aAA4B,EAC5B;EACA,KAAK,MAAM,CAACC,IAAI,EAAEC,cAAc,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACxB,OAAO,CAAC,EAAE;IAC5DmB,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEC,cAAc,CAAC;EACrC;EAEAH,GAAG,CAACM,SAAS,CAAC,UAAU,EAAE,CAACC,IAAY,EAAEC,mBAA4B,KACnEnC,cAAc,CAACkC,IAAI,EAAE;IACnBE,OAAO,EAAER,aAAa,CAACQ,OAAO;IAC9BD;EACF,CAAC,CACH,CAAC;EAED,KAAK,MAAM,CAACN,IAAI,EAAEQ,cAAc,CAAC,IAAIN,MAAM,CAACC,OAAO,CAACb,OAAO,CAAC,EAAE;IAC5DQ,GAAG,CAACW,SAAS,CAACT,IAAI,EAAEQ,cAAc,CAAC;EACrC;;EAEA;EACA,IAAIT,aAAa,CAACpB,OAAO,EAAE;IACzB,KAAK,MAAM,CAACqB,IAAI,EAAEU,MAAM,CAAC,IAAIR,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACpB,OAAO,CAAC,EAAE;MAClEmB,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEU,MAAM,CAAC;MAC3BtC,MAAM,CAACuC,cAAc,CAACX,IAAI,EAAEU,MAAM,CAAC;IACrC;EACF;;EAEA;EACA,IAAIX,aAAa,CAACT,OAAO,EAAE;IACzBY,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACT,OAAO,CAAC,CAACsB,OAAO,CAAC,CAAC,CAACZ,IAAI,EAAEa,EAAE,CAAC,KAAK;MAC5Df,GAAG,CAACW,SAAS,CAACT,IAAI,EAAEa,EAAE,CAAC;IACzB,CAAC,CAAC;EACJ;AACF,CAAC;AAED,eAAexC,MAAM","ignoreList":[]}
@@ -77,7 +77,3 @@ export declare class FormModel {
77
77
  */
78
78
  moreThanOnePaymentQuestion(): boolean;
79
79
  }
80
- export declare function getAvailableLanguages(def: FormDefinition): {
81
- name: string;
82
- code: string;
83
- }[];
@@ -3,6 +3,7 @@ import { add, format } from 'date-fns';
3
3
  import { Parser } from 'expr-eval-fork';
4
4
  import joi from 'joi';
5
5
  import { logger } from "../../../common/helpers/logging/logger.js";
6
+ import { EN_GB } from "../../../constants.js";
6
7
  import "../components/YesNoField.js";
7
8
  import { hasListFormField } from "../components/helpers/components.js";
8
9
  import { todayAsDateOnly } from "../date-helper.js";
@@ -11,6 +12,7 @@ import { loadFormTranslations } from "../i18n/createFormTranslator.js";
11
12
  import { createTranslator } from "../i18n/createTranslator.js";
12
13
  import { extractBaseTranslations } from "../i18n/extractBaseTranslations.js";
13
14
  import { createFormI18nInstance } from "../i18n/index.js";
15
+ import { getAvailableLanguages } from "../i18n/languages.js";
14
16
  import { createPage } from "../pageControllers/helpers/pages.js";
15
17
  import { validationOptions as opts } from "../pageControllers/validationOptions.js";
16
18
  import * as defaultServices from "../services/index.js";
@@ -129,7 +131,12 @@ export class FormModel {
129
131
  }
130
132
 
131
133
  /** Returns a scoped translator pair for the given language. */
132
- createTranslator(language = 'en-GB') {
134
+ createTranslator(language = EN_GB) {
135
+ // @ts-expect-error - dynamic language lookup
136
+ if (!this.def.metadata?.translations?.[language]) {
137
+ // If not translations defined in the FormDefinition, always default to English
138
+ language = EN_GB;
139
+ }
133
140
  return createTranslator(this.i18nInstance, language);
134
141
  }
135
142
 
@@ -481,7 +488,7 @@ function validateFormState(request, page, context) {
481
488
 
482
489
  // Add relevant state errors
483
490
  if (error) {
484
- const errorsState = error.details.map(getError);
491
+ const errorsState = error.details.map(item => getError(EN_GB, item));
485
492
  return {
486
493
  ...context,
487
494
  errors: errors.concat(errorsState)
@@ -495,17 +502,4 @@ function getReferenceNumber(state) {
495
502
  }
496
503
  return state.$$__referenceNumber;
497
504
  }
498
- export function getAvailableLanguages(def) {
499
- // @ts-expect-error - dynamic property
500
- if (def.metadata?.translations?.cy) {
501
- return [{
502
- name: 'English',
503
- code: 'en-GB'
504
- }, {
505
- name: 'Cymraeg',
506
- code: 'cy'
507
- }];
508
- }
509
- return [];
510
- }
511
505
  //# sourceMappingURL=FormModel.js.map