diff --git a/src/server/plugins/engine/components/PaymentField.ts b/src/server/plugins/engine/components/PaymentField.ts index 53d63d028..5fd2425dc 100644 --- a/src/server/plugins/engine/components/PaymentField.ts +++ b/src/server/plugins/engine/components/PaymentField.ts @@ -45,6 +45,7 @@ export class PaymentField extends FormComponent { declare options: PaymentFieldComponent['options'] declare formSchema: ObjectSchema declare stateSchema: ObjectSchema + declare def: PaymentFieldComponent isAppendageStateSingleObject = true constructor( @@ -54,6 +55,7 @@ export class PaymentField extends FormComponent { super(def, props) this.options = def.options + this.def = def const paymentStateSchema = joi .object({ @@ -110,6 +112,8 @@ export class PaymentField extends FormComponent { getViewModel(context: RenderContext) { const { payload } = context const viewModel = super.getViewModel(context) + const { translator } = context + const { tComponent } = translator // Payload is pre-populated from state if a payment has already been made const paymentState = this.isPaymentState(payload[this.name] as unknown) @@ -124,7 +128,8 @@ export class PaymentField extends FormComponent { return { ...viewModel, amount: formatCurrency(amount), - description: this.options.description, + description: + tComponent(this.def, 'paymentDescription') || this.options.description, paymentState } } diff --git a/src/server/plugins/engine/i18n/createFormTranslator.ts b/src/server/plugins/engine/i18n/createFormTranslator.ts index 3c0b51e8a..ddeae11c0 100644 --- a/src/server/plugins/engine/i18n/createFormTranslator.ts +++ b/src/server/plugins/engine/i18n/createFormTranslator.ts @@ -1,4 +1,8 @@ -import { type FormDefinition } from '@defra/forms-model' +import { + yesNoListNoItemId, + yesNoListYesItemId, + type FormDefinition +} from '@defra/forms-model' import { type i18n } from 'i18next' import { createTranslator } from '~/src/server/plugins/engine/i18n/createTranslator.js' @@ -28,6 +32,22 @@ export function loadFormTranslations(def: FormDefinition, i18nInstance: i18n) { if (formTranslations) { for (const [lng, resources] of Object.entries(formTranslations)) { i18nInstance.addResourceBundle(lng, 'form', resources, true, true) + + // Temporary workaround - until we develop a better solution + if (lng === 'cy') { + i18nInstance.addResource( + lng, + 'form', + `listItems.${yesNoListYesItemId}.text`, + 'Ie' + ) + i18nInstance.addResource( + lng, + 'form', + `listItems.${yesNoListNoItemId}.text`, + 'Nage' + ) + } } } } diff --git a/src/server/plugins/engine/i18n/translations/cy.json b/src/server/plugins/engine/i18n/translations/cy.json index 25d86a998..9697c726e 100644 --- a/src/server/plugins/engine/i18n/translations/cy.json +++ b/src/server/plugins/engine/i18n/translations/cy.json @@ -111,6 +111,7 @@ "body": "Os hoffech dderbyn e-bost prawf, [[link]] yr hoffech i gyflwyniadau ffurflen fynd iddo.", "linkText": "nodwch y cyfeiriad e-bost (yn agor mewn tab newydd)" }, + "bannerTitle": "Pwysig", "bannerForce": "Rhagolwg o ffurflen [[previewMode]] yw hon. Peidiwch â rhoi gwybodaeth bersonol.", "bannerText": "Dyma ragolwg o dudalen ffurflen [[previewMode]] rydych chi'n ei golygu.", "previousPages": "Mae'n dibynnu ar atebion o dudalennau cynharach yn y ffurflen. Yn y fersiwn fyw, bydd angen i ddefnyddwyr gwblhau'r cwestiynau hynny yn gyntaf." @@ -220,7 +221,7 @@ "yesNoField": { "yes": "Ie", - "no": "Na", + "no": "Nage", "validation": { "selectYesNoRequired": "{{#label}} - dewiswch ie neu na" } @@ -245,7 +246,7 @@ }, "paymentField": { - "detailsRequired": "Angen manylion talu", + "detailsRequired": "Angen talu", "pendingTransaction": "Mae'n bosibl y gwelwch drafodiad ar y gweill yn eich cyfrif banc ond dim ond pan fyddwch yn cyflwyno'r ffurflen y codir tâl arnoch.", "addPaymentDetails": "Ychwanegu manylion talu", "cannotTakePayment": "Mae problem ac ni allwn dderbyn taliad. Cysylltwch â ni (manylion yn nhroedyn y ffurflen hon) neu cadwch eich cynnydd a dychwelyd at y ffurflen yn ddiweddarach.", @@ -266,6 +267,19 @@ "uploadedFilesHeading": "Ffeiliau a uwchlwythwyd", "filesCount_one": "Uwchlwythwyd [[count]] ffeil", "filesCount_other": "Uwchlwythwyd [[count]] ffeil" + }, + + "characterCount": { + "_comment": "Note the use of %{count} instead of [[count]]. This is because we are passing to the govuk template, not passing to Joi", + "underLimit": { + "one": "Mae gennych %{count} nod ar ôl", + "other": "Mae gennych %{count} nod ar ôl" + }, + "atLimit": "Mae gennych 0 nod ar ôl", + "overLimit": { + "one": "Mae gennych %{count} nod yn ormod", + "other": "Mae gennych %{count} nod yn ormod" + } } } } diff --git a/src/server/plugins/engine/i18n/translations/en-GB.json b/src/server/plugins/engine/i18n/translations/en-GB.json index 507ef8027..7eb2e3852 100644 --- a/src/server/plugins/engine/i18n/translations/en-GB.json +++ b/src/server/plugins/engine/i18n/translations/en-GB.json @@ -111,6 +111,7 @@ "body": "If you want to receive a test email, [[link]] you want form submissions to go to.", "linkText": "enter the email address (opens in new tab)" }, + "bannerTitle": "Important", "bannerForce": "This is a preview of a [[previewMode]] form. Do not enter personal information.", "bannerText": "This is a preview of a [[previewMode]] form page you are editing.", "previousPages": "It depends on answers from earlier pages in the form. In the live version, users will need to complete those questions first." @@ -245,7 +246,7 @@ }, "paymentField": { - "detailsRequired": "Payment details required", + "detailsRequired": "Payment required", "pendingTransaction": "You may see a pending transaction in your bank account but you will only be charged when you submit the form.", "addPaymentDetails": "Add payment details", "cannotTakePayment": "There is a problem and we cannot take a payment. Contact us (details in the footer of this form) or save your progress and return to the form later.", @@ -266,6 +267,19 @@ "uploadedFilesHeading": "Uploaded files", "filesCount_one": "[[count]] file uploaded", "filesCount_other": "[[count]] files uploaded" + }, + + "characterCount": { + "_comment": "Note the use of %{count} instead of [[count]]. This is because we are passing to the govuk template, not passing to Joi", + "underLimit": { + "one": "You have %{count} character remaining", + "other": "You have %{count} characters remaining" + }, + "atLimit": "You have 0 characters remaining", + "overLimit": { + "one": "You have %{count} character too many", + "other": "You have %{count} characters too many" + } } } } diff --git a/src/server/plugins/engine/i18n/types.ts b/src/server/plugins/engine/i18n/types.ts index 8aa665f56..a10db632f 100644 --- a/src/server/plugins/engine/i18n/types.ts +++ b/src/server/plugins/engine/i18n/types.ts @@ -22,6 +22,7 @@ export type FormDefinitionTranslations = Record< hint: string content: string shortDescription: string + paymentDescription: string }> > sections: Record>> diff --git a/src/server/plugins/engine/pageControllers/QuestionPageController.ts b/src/server/plugins/engine/pageControllers/QuestionPageController.ts index a7893e84f..3c6f6281e 100644 --- a/src/server/plugins/engine/pageControllers/QuestionPageController.ts +++ b/src/server/plugins/engine/pageControllers/QuestionPageController.ts @@ -174,9 +174,14 @@ export class QuestionPageController extends PageController { if (!pageTitle) { pageTitle = labelOrLegend.text } - labelOrLegend.text = isOptional - ? `${pageTitle} ${t('common.optional')}` - : pageTitle + + // The question text may have already appended 'optional' in the relevant language + const optionalWord = t('common.optional') + const optionalAlreadyAppended = pageTitle.endsWith(` ${optionalWord}`) + labelOrLegend.text = + isOptional && !optionalAlreadyAppended + ? `${pageTitle} ${optionalWord}` + : pageTitle } // Fallback if still no pageTitle set diff --git a/src/server/plugins/engine/views/components/multilinetextfield.html b/src/server/plugins/engine/views/components/multilinetextfield.html index fa3eb9480..6460a63cc 100644 --- a/src/server/plugins/engine/views/components/multilinetextfield.html +++ b/src/server/plugins/engine/views/components/multilinetextfield.html @@ -3,7 +3,18 @@ {% macro MultilineTextField(component) %} {% if component.model.isCharacterOrWordCount == true %} - {{ govukCharacterCount(component.model) }} + {% set characterParams = component.model | merge({ + charactersUnderLimitText: { + one: t('components.characterCount.underLimit.one'), + other: t('components.characterCount.underLimit.other') + }, + charactersAtLimitText: t('components.characterCount.atLimit'), + charactersOverLimitText: { + one: t('components.characterCount.overLimit.one'), + other: t('components.characterCount.overLimit.other') + } + } ) %} + {{ govukCharacterCount(characterParams) }} {% else %} {{ govukTextarea(component.model) }} {% endif %} diff --git a/src/server/plugins/engine/views/components/paymentfield.html b/src/server/plugins/engine/views/components/paymentfield.html index 7f0bf8c8f..63c760098 100644 --- a/src/server/plugins/engine/views/components/paymentfield.html +++ b/src/server/plugins/engine/views/components/paymentfield.html @@ -16,7 +16,7 @@

{{ t('components.paymentField.preAuthorisedTitle') }

{{ t('components.paymentField.preAuthorisedBody') }}

{% else %} {# No pre-authorisation - show payment form #} -

{{ model.label.text if model.label and model.label.text else t('components.paymentField.detailsRequired') }}

+

{{ t('components.paymentField.detailsRequired') }}

{{ description }}

diff --git a/src/server/plugins/engine/views/partials/preview-banner.html b/src/server/plugins/engine/views/partials/preview-banner.html index 3d4cc427b..4c151c3f3 100644 --- a/src/server/plugins/engine/views/partials/preview-banner.html +++ b/src/server/plugins/engine/views/partials/preview-banner.html @@ -8,7 +8,9 @@

{%- endmacro -%} -{% call govukNotificationBanner() %} +{% call govukNotificationBanner({ + titleText: t('pages.preview.bannerTitle') +}) %} {% set previewModeTranslated = t('common.' + previewMode) %} {% if not context.isForceAccess %}