diff --git a/app/views/full-page-examples/localisation/index.njk b/app/views/full-page-examples/localisation/index.njk new file mode 100644 index 0000000000..d1e0710f35 --- /dev/null +++ b/app/views/full-page-examples/localisation/index.njk @@ -0,0 +1,212 @@ +--- +scenario: >- + Examples of various components translated into Welsh. +--- + +{# Example content derived from: https://www.gov.uk/adnewyddu-trwydded-yrru #} +{% extends "full-page-example.njk" %} + +{% from "skip-link/macro.njk" import govukSkipLink %} +{% from "footer/macro.njk" import govukFooter %} +{% from "breadcrumbs/macro.njk" import govukBreadcrumbs %} +{% from "button/macro.njk" import govukButton %} +{% from "accordion/macro.njk" import govukAccordion %} +{% from "character-count/macro.njk" import govukCharacterCount %} + +{% set pageTitle = "Adnewyddu eich trwydded yrru" %} +{% block pageTitle %}{{ pageTitle }} - GOV.UK{% endblock %} + +{% set htmlLang = "cy" %} + +{% block skipLink %} + {{ govukSkipLink({ + href: '#main-content', + text: "Symud i'r prif gynnwys" + }) }} +{% endblock %} + +{% block beforeContent %} + {{ govukBreadcrumbs({ + items: [ + { + text: "Hafan", + href: "#/" + }, + { + text: "Gyrru a chludiant", + href: "#/browse/driving" + }, + { + text: "Trwyddedau gyrru", + href: "#/browse/driving/driving-licences" + } + ] + }) }} +{% endblock %} + +{% block content %} +
+
+

{{ pageTitle }}

+ +

Bydd arnoch angen pasbort y DU dilys i adnewyddu eich trwydded ar-lein gyda DVLA.

+ +

Adnewyddu ar-lein

+ +

Bydd eich trwydded newydd yn ddilys o’r dyddiad y mae’ch cais yn cael ei gymeradwyo, nid o ddyddiad dod i ben eich trwydded gyfredol.

+ + {{ govukButton({ + text: "Dechrau nawr", + href: "#", + classes: "govuk-!-margin-top-2 govuk-!-margin-bottom-8", + isStartButton: true + }) }} + + {{ govukCharacterCount({ + label: { text: "Enw(au) cyntaf" }, + hint: { text: "Rhowch eich enw cyntaf ac unrhyw enwau canol." }, + limitHint: "Ysgrifennwch hyd at %{count} o nodau", + id: "character-count", + maxlength: 20, + value: 'A1234567890' + }) }} + + {% set moreInformationHTML %} +

Mae’n rhaid ichi adnewyddu trwydded cerdyn-llun bob 10 mlynedd - byddwch yn derbyn nodyn atgoffa cyn bod eich trwydded gyfredol yn dod i ben.

+ +

Beth sydd arnoch angen

+ +

I adnewyddu ar-lein, mae arnoch angen y canlynol:

+ + + +

Data personol

+ +

Bydd DVLA yn anfon ebost cadarnhau atoch unwaith eich bod wedi gwneud cais. Efallai y gofynnir ichi gymryd rhan mewn ymchwil drwy e-bost, ond gallwch optio allan.

+ {% endset %} + + {% set otherWaysToApplyHTML %} +

Gwneud cais mewn Swyddfa’r Post

+ +

Byddwch yn cael llythyr atgoffa yn y post. Gallwch gymryd hwn i Swyddfa’r Post sy’n delio ag adnewyddu trwyddedau cerdyn-llun DVLA.

+ +

Bydd hefyd angen ichi gymryd:

+ + + +

Os nad oes gennych lythyr atgoffa, bydd arnoch angen eich trwydded yrru cerdyn-llun i wneud cais mewn Swyddfa’r Post.

+ +
+

Ni allwch wneud cais mewn Swyddfa’r Post os yw’ch enw wedi newid. Bydd angen ichi wneud cais drwy’r post.

+
+ +

Gwneud cais drwy’r post

+ +
+

Mae’n cymryd yn hirach na’r arfer i brosesu ceisiadau a anfonir trwy’r post oherwydd coronafeirws (COVID-19).

+
+ +

Gallwch gael ‘pecyn D1W’ o ffurflenni o Swyddfa’r Post sy’n delio ag adnewyddu trwyddedau cerdyn-llun neu dreth cerbyd DVLA.

+ +

Mae angen ichi gynnwys y pethau canlynol gyda’ch ffurflenni wedi’u cwblhau:

+ + + +

Mae hefyd angen ichi gynnwys dogfennau adnabod os ydych wedi newid eich enw.

+ +

Anfonwch y cais i:

+ +

DVLA +
Abertawe +
SA99 1DH +
+

+ +

Ar ôl ichi wneud cais mewn Swyddfa’r Post neu drwy’r post

+ +

Gallwch barhau i yrru tra rydych yn aros am eich trwydded newydd gyrraedd.

+ + {% endset %} + + {{ govukAccordion({ + id: "accordion-default", + items: [ + { + heading: { text: "Rhagor o wybodaeth" }, + content: { + html: moreInformationHTML + } + }, + { + heading: { text: "Ffyrdd eraill o wneud cais" }, + content: { + html: otherWaysToApplyHTML + } + } + ] + }) }} +
+ + +
+{% endblock %} + +{% block footer %} + {{ govukFooter({ + contentLicence: 'Mae’r holl gynnwys ar gael dan Drwydded y Llywodraeth Agored v3.0, ac eithrio lle nodir yn wahanol', + copyright: '© Hawlfraint y Goron' + }) }} +{% endblock %} + +{% block bodyEnd %} + + +{% endblock %} diff --git a/src/govuk/all.mjs b/src/govuk/all.mjs index 7ae16cb3e8..c42c98e867 100644 --- a/src/govuk/all.mjs +++ b/src/govuk/all.mjs @@ -26,7 +26,7 @@ function initAll (options) { var $accordions = scope.querySelectorAll('[data-module="govuk-accordion"]') nodeListForEach($accordions, function ($accordion) { - new Accordion($accordion).init() + new Accordion($accordion, options.accordion || {}).init() }) var $details = scope.querySelectorAll('[data-module="govuk-details"]') @@ -36,7 +36,7 @@ function initAll (options) { var $characterCounts = scope.querySelectorAll('[data-module="govuk-character-count"]') nodeListForEach($characterCounts, function ($characterCount) { - new CharacterCount($characterCount).init() + new CharacterCount($characterCount, options.character_count || {}).init() }) var $checkboxes = scope.querySelectorAll('[data-module="govuk-checkboxes"]') diff --git a/src/govuk/components/accordion/accordion.mjs b/src/govuk/components/accordion/accordion.mjs index ccbc7638ea..787a16c345 100644 --- a/src/govuk/components/accordion/accordion.mjs +++ b/src/govuk/components/accordion/accordion.mjs @@ -15,12 +15,28 @@ */ +import I18n from '../../i18n.mjs' import { nodeListForEach } from '../../common.mjs' import '../../vendor/polyfills/Function/prototype/bind.mjs' import '../../vendor/polyfills/Element/prototype/classList.mjs' +import '../../vendor/polyfills/Object/assign.mjs' -function Accordion ($module) { +function Accordion ($module, options) { this.$module = $module + + var defaultOptions = { + i18n: { + translations: { + show_this_section: 'Show this section', + hide_this_section: 'Hide this section', + show_all_sections: 'Show all sections', + hide_all_sections: 'Hide all sections' + } + } + } + this.options = Object.assign({}, defaultOptions, options) + this.i18n = new I18n(this.options.i18n) + this.moduleId = $module.getAttribute('id') this.$sections = $module.querySelectorAll('.govuk-accordion__section') this.$showAllButton = '' @@ -233,15 +249,11 @@ Accordion.prototype.setExpanded = function (expanded, $section) { var $icon = $section.querySelector('.' + this.upChevronIconClass) var $showHideText = $section.querySelector('.' + this.sectionShowHideTextClass) var $button = $section.querySelector('.' + this.sectionButtonClass) - var $newButtonText = expanded ? 'Hide' : 'Show' - - // Build additional copy of "this section" for assistive technology and place inside toggle link - var $visuallyHiddenText = document.createElement('span') - $visuallyHiddenText.classList.add('govuk-visually-hidden') - $visuallyHiddenText.innerHTML = ' this section' + var $newButtonText = expanded + ? this.i18n.t('hide_this_section') + : this.i18n.t('show_this_section') $showHideText.innerHTML = $newButtonText - $showHideText.appendChild($visuallyHiddenText) $button.setAttribute('aria-expanded', expanded) // Swap icon, change class @@ -278,7 +290,9 @@ Accordion.prototype.checkIfAllSectionsOpen = function () { Accordion.prototype.updateShowAllButton = function (expanded) { var $showAllIcon = this.$showAllButton.querySelector('.' + this.upChevronIconClass) var $showAllText = this.$showAllButton.querySelector('.' + this.showAllTextClass) - var newButtonText = expanded ? 'Hide all sections' : 'Show all sections' + var newButtonText = expanded + ? this.i18n.t('hide_all_sections') + : this.i18n.t('show_all_sections') this.$showAllButton.setAttribute('aria-expanded', expanded) $showAllText.innerHTML = newButtonText diff --git a/src/govuk/components/character-count/character-count.mjs b/src/govuk/components/character-count/character-count.mjs index 215dd6ce70..0a3138229f 100644 --- a/src/govuk/components/character-count/character-count.mjs +++ b/src/govuk/components/character-count/character-count.mjs @@ -1,9 +1,30 @@ +import I18n from '../../i18n.mjs' import '../../vendor/polyfills/Function/prototype/bind.mjs' import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normaliziation import '../../vendor/polyfills/Element/prototype/classList.mjs' +import '../../vendor/polyfills/Object/assign.mjs' -function CharacterCount ($module) { +function CharacterCount ($module, options) { this.$module = $module + this.options = options || {} + + var defaultOptions = { + i18n: { + translations: { + characters_under_limit_one: 'You have %{count} character remaining', + characters_under_limit_other: 'You have %{count} characters remaining', + characters_over_limit_one: 'You have %{count} character too many', + characters_over_limit_other: 'You have %{count} characters too many', + words_under_limit_one: 'You have %{count} word remaining', + words_under_limit_other: 'You have %{count} words remaining', + words_over_limit_one: 'You have %{count} word too many', + words_over_limit_other: 'You have %{count} words too many' + } + } + } + this.options = Object.assign({}, defaultOptions, options) + this.i18n = new I18n(this.options.i18n) + this.$textarea = $module.querySelector('.govuk-js-character-count') this.$visibleCountMessage = null this.$screenReaderCountMessage = null @@ -192,18 +213,23 @@ CharacterCount.prototype.formattedUpdateMessage = function () { var options = this.options var remainingNumber = this.maxLength - this.count($textarea.value) - var charVerb = 'remaining' - var charNoun = 'character' - var displayNumber = remainingNumber - if (options.maxwords) { - charNoun = 'word' + if (options.maxwords && remainingNumber < 0) { + return this.i18n.t('words_over_limit', { + count: Math.abs(remainingNumber) + }) + } else if (options.maxwords) { + return this.i18n.t('words_under_limit', { + count: remainingNumber + }) + } else if (remainingNumber < 0) { + return this.i18n.t('characters_over_limit', { + count: Math.abs(remainingNumber) + }) + } else { + return this.i18n.t('characters_under_limit', { + count: remainingNumber + }) } - charNoun = charNoun + ((remainingNumber === -1 || remainingNumber === 1) ? '' : 's') - - charVerb = (remainingNumber < 0) ? 'too many' : 'remaining' - displayNumber = Math.abs(remainingNumber) - - return 'You have ' + displayNumber + ' ' + charNoun + ' ' + charVerb } // Checks whether the value is over the configured threshold for the input. diff --git a/src/govuk/components/character-count/character-count.test.js b/src/govuk/components/character-count/character-count.test.js index 01b55612c6..f52f6b1a40 100644 --- a/src/govuk/components/character-count/character-count.test.js +++ b/src/govuk/components/character-count/character-count.test.js @@ -28,9 +28,9 @@ describe('Character count', () => { it('shows the static message', async () => { await goToExample() - const message = await page.$eval('.govuk-character-count__message', el => el.innerHTML.trim()) + const messageClasses = await page.$eval('.govuk-character-count__message', el => el.className) - expect(message).toEqual('You can enter up to 10 characters') + expect(messageClasses).not.toContain('govuk-visually-hidden') }) }) diff --git a/src/govuk/components/character-count/character-count.yaml b/src/govuk/components/character-count/character-count.yaml index 7f092f3b32..f8ec34e637 100644 --- a/src/govuk/components/character-count/character-count.yaml +++ b/src/govuk/components/character-count/character-count.yaml @@ -37,6 +37,10 @@ params: required: false description: Options for the hint component. isComponent: true +- name: limitHint + type: string + required: false + description: Text describing the maximum number of characters or words that can be entered, which is announced to screen readers and displayed as a fallback if the character count JavaScript does not run. Instances of `%{count}` within the string will be replaced by the value of either the `maxlength` or `maxwords` parameters. By default, fallback text in English is constructed based on what other parameters are provided to the component. - name: errorMessage type: object required: false @@ -154,6 +158,25 @@ examples: threshold: 75 label: text: Full address + + - name: with very high character limit + data: + id: with-very-high-limit + name: with-very-high-limit + maxlength: 1280 + label: + text: Life story + + - name: with localised fallback text + description: Fallback counter text (shown if JavaScript is not available) translated into Welsh + data: + id: with-localisation + name: with-localisation + maxlength: 10 + label: + text: Full address + limitHint: Gallwch nodi hyd at %{count} o gymeriadau + # Hidden examples are not shown in the review app, but are used for tests and HTML fixtures - name: classes @@ -233,6 +256,7 @@ examples: label: text: Can you provide more detail? - name: with id with special characters + hidden: true data: id: user1.profile[address] name: address diff --git a/src/govuk/components/character-count/template.njk b/src/govuk/components/character-count/template.njk index 297604176a..aced4d1280 100644 --- a/src/govuk/components/character-count/template.njk +++ b/src/govuk/components/character-count/template.njk @@ -26,8 +26,11 @@ errorMessage: params.errorMessage, attributes: params.attributes }) }} + + {%- set limitHintLength = params.maxlength or params.maxwords %} + {%- set limitHintDefault = 'You can enter up to %{count} ' + ('words' if params.maxwords else 'characters') %} {{ govukHint({ - text: 'You can enter up to ' + (params.maxlength or params.maxwords) + (' words' if params.maxwords else ' characters'), + text: (params.limitHint or limitHintDefault) | replace('%{count}', limitHintLength), id: params.id + '-info', classes: 'govuk-character-count__message' + (' ' + params.countMessage.classes if params.countMessage.classes) }) }} diff --git a/src/govuk/components/character-count/template.test.js b/src/govuk/components/character-count/template.test.js index bed9028feb..a049f59c09 100644 --- a/src/govuk/components/character-count/template.test.js +++ b/src/govuk/components/character-count/template.test.js @@ -114,6 +114,13 @@ describe('Character count', () => { const $countMessage = $('.govuk-character-count__message') expect($countMessage.hasClass('app-custom-count-message')).toBeTruthy() }) + + it('can be localised', async () => { + const $ = render('character-count', examples['with localised fallback text']) + + const $countMessage = $('.govuk-character-count__message') + expect($countMessage.text()).toContain('Gallwch nodi hyd at 10 o gymeriadau') + }) }) describe('when it has the spellcheck attribute', () => { diff --git a/src/govuk/components/footer/footer.yaml b/src/govuk/components/footer/footer.yaml index 17d117a185..14f84ee297 100644 --- a/src/govuk/components/footer/footer.yaml +++ b/src/govuk/components/footer/footer.yaml @@ -67,6 +67,22 @@ params: type: object required: false description: HTML attributes (for example data attributes) to add to the anchor in the footer navigation section. +- name: contentLicence + type: string + required: false + description: HTML or text to replace the default Open Government Licence notice with. +- name: hideOpenGovernmentLicenceLogo + type: boolean + required: false + description: Removes the Open Government Licence logo, if the licence being used isn't the OGL. +- name: copyright + type: string + required: false + description: Text to replace the default Crown Copyright notice with. +- name: hideCrownCopyrightArms + type: boolean + required: false + description: Removes the Crown Copyright notice and royal coat of arms. - name: containerClasses type: string required: false @@ -117,6 +133,20 @@ examples: data: {} +- name: with custom licence and copyright notices + description: Licence and copyright information translated into Welsh + data: + contentLicence: 'Mae’r holl gynnwys ar gael dan Drwydded y Llywodraeth Agored v3.0, ac eithrio lle nodir yn wahanol' + copyright: '© Hawlfraint y Goron' + +- name: with licence and copyright graphics removed + description: Suitable for services where the Open Government Licence and Crown Copyright don't apply, such as internal caseworking systems + data: + hideOpenGovernmentLicenceLogo: true + hideCrownCopyrightArms: true + copyright: © Megadodo Publications + contentLicence: The Hitchhiker's Guide is available under a CC Attribution 4.0 Interplanetary Licence + - name: with meta description: Secondary navigation with meta information relating to the site data: @@ -159,12 +189,6 @@ examples: text: Ligula Nullam Ultricies html: Built by the Department of Magical Law Enforcement -- name: with custom meta - description: Custom meta section - data: - meta: - text: GOV.UK Prototype Kit v7.0.1 - - name: with default width navigation (one column) data: navigation: diff --git a/src/govuk/components/footer/template.njk b/src/govuk/components/footer/template.njk index 61226227e8..f87794d2f5 100644 --- a/src/govuk/components/footer/template.njk +++ b/src/govuk/components/footer/template.njk @@ -50,37 +50,47 @@ {% endif %} {% endif %} - {#- The SVG needs `focusable="false"` so that Internet Explorer does not - treat it as an interactive element - without this it will be - 'focusable' when using the keyboard to navigate. #} - + {% if not params.hideOpenGovernmentLicenceLogo %} + {#- The SVG needs `focusable="false"` so that Internet Explorer does not + treat it as an interactive element - without this it will be + 'focusable' when using the keyboard to navigate. #} + + {% endif %} - All content is available under the - Open Government Licence v3.0, except where otherwise stated + {% if params.contentLicence %} + {{ params.contentLicence | safe }} + {% else %} + All content is available under the + Open Government Licence v3.0, except where otherwise stated + {% endif %} diff --git a/src/govuk/components/footer/template.test.js b/src/govuk/components/footer/template.test.js index 86a91f47b6..59c1a0656a 100644 --- a/src/govuk/components/footer/template.test.js +++ b/src/govuk/components/footer/template.test.js @@ -207,4 +207,50 @@ describe('footer', () => { expect($sectionBreak.length).toBeFalsy() }) }) + + describe('content licence', () => { + it('is visible', () => { + const $ = render('footer', examples.default) + + const $licenceMessage = $('.govuk-footer__licence-description') + expect($licenceMessage.text().trim()).toContain('Open Government Licence v3.0') + }) + + it('can be customised', () => { + const $ = render('footer', examples['with custom licence and copyright notices']) + + const $licenceMessage = $('.govuk-footer__licence-description') + expect($licenceMessage.text().trim()).toContain('Drwydded y Llywodraeth Agored v3.0') + }) + + it('logo can be hidden', () => { + const $ = render('footer', examples['with licence and copyright graphics removed']) + + const $licenceLogo = $('.govuk-footer__licence-logo') + expect($licenceLogo.length).toBeFalsy() + }) + }) + + describe('crown copyright', () => { + it('is visible', () => { + const $ = render('footer', examples.default) + + const $copyrightMessage = $('.govuk-footer__copyright-logo') + expect($copyrightMessage.text().trim()).toContain('© Crown copyright') + }) + + it('can be customised', () => { + const $ = render('footer', examples['with custom licence and copyright notices']) + + const $copyrightMessage = $('.govuk-footer__copyright-logo') + expect($copyrightMessage.text().trim()).toContain('© Hawlfraint y Goron') + }) + + it('logo can be hidden', () => { + const $ = render('footer', examples['with licence and copyright graphics removed']) + + const $copyrightLogo = $('.govuk-footer__copyright-logo') + expect($copyrightLogo.length).toBeFalsy() + }) + }) }) diff --git a/src/govuk/components/header/header.yaml b/src/govuk/components/header/header.yaml index b8c5e1c462..59fce95bc3 100644 --- a/src/govuk/components/header/header.yaml +++ b/src/govuk/components/header/header.yaml @@ -51,11 +51,15 @@ params: - name: navigationLabel type: string required: false - description: Text for the `aria-label` attribute of the navigation. Defaults to 'Navigation menu'. + description: Text for the `aria-label` attribute of the navigation. Defaults to 'Menu'. - name: menuButtonLabel type: string required: false - description: Text for the `aria-label` attribute of the button that toggles the navigation. Defaults to 'Show or hide navigation menu'. + description: Text for the `aria-label` attribute of the button that toggles the navigation. Defaults to 'Show or hide menu'. +- name: menuButton + type: string + required: false + description: Text for the button that toggles the navigation. Defaults to 'Menu'. - name: containerClasses type: string required: false @@ -131,6 +135,21 @@ examples: text: Navigation item 3 - href: '#4' text: Navigation item 4 + +- name: with custom menu button text + description: Button translated into Welsh + data: + menuButton: Dewislen + navigation: + - href: '#1' + text: Eitem llywio 1 + active: true + - href: '#2' + text: Eitem llywio 2 + - href: '#3' + text: Eitem llywio 3 + - href: '#4' + text: Eitem llywio 4 - name: with custom menu button label data: diff --git a/src/govuk/components/header/template.njk b/src/govuk/components/header/template.njk index 915bcbbff4..f4cfe198f9 100644 --- a/src/govuk/components/header/template.njk +++ b/src/govuk/components/header/template.njk @@ -61,7 +61,7 @@ {% endif %} {% if params.navigation %}