govuk_publishing_components 34.7.0 → 34.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js +3 -10
  3. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-form-tracker.js +113 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +2 -11
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-schemas.js +15 -1
  6. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js +20 -29
  7. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
  8. data/app/models/govuk_publishing_components/component_doc.rb +14 -0
  9. data/app/models/govuk_publishing_components/component_wrapper_helper_options.rb +14 -0
  10. data/app/views/govuk_publishing_components/component_guide/show.html.erb +21 -6
  11. data/app/views/govuk_publishing_components/components/_action_link.html.erb +5 -2
  12. data/app/views/govuk_publishing_components/components/_breadcrumbs.html.erb +6 -2
  13. data/app/views/govuk_publishing_components/components/_details.html.erb +3 -1
  14. data/app/views/govuk_publishing_components/components/docs/action_link.yml +1 -0
  15. data/app/views/govuk_publishing_components/components/docs/breadcrumbs.yml +1 -0
  16. data/app/views/govuk_publishing_components/components/docs/details.yml +17 -0
  17. data/lib/govuk_publishing_components/presenters/component_wrapper_helper.rb +78 -0
  18. data/lib/govuk_publishing_components/presenters/related_navigation_helper.rb +0 -7
  19. data/lib/govuk_publishing_components/version.rb +1 -1
  20. data/lib/govuk_publishing_components.rb +1 -0
  21. data/node_modules/govuk-frontend/govuk/all.js +4029 -3792
  22. data/node_modules/govuk-frontend/govuk/all.js.map +1 -0
  23. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +52 -51
  24. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js.map +1 -0
  25. data/node_modules/govuk-frontend/govuk/common/index.js +153 -145
  26. data/node_modules/govuk-frontend/govuk/common/index.js.map +1 -0
  27. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +324 -321
  28. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js.map +1 -0
  29. data/node_modules/govuk-frontend/govuk/common.js +154 -146
  30. data/node_modules/govuk-frontend/govuk/common.js.map +1 -0
  31. data/node_modules/govuk-frontend/govuk/components/_all.scss +1 -1
  32. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +23 -4
  33. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +2059 -1654
  34. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js.map +1 -0
  35. data/node_modules/govuk-frontend/govuk/components/accordion/fixtures.json +11 -11
  36. data/node_modules/govuk-frontend/govuk/components/accordion/template.njk +1 -1
  37. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +19 -19
  38. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +21 -10
  39. data/node_modules/govuk-frontend/govuk/components/button/button.js +927 -917
  40. data/node_modules/govuk-frontend/govuk/components/button/button.js.map +1 -0
  41. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +2050 -2040
  42. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js.map +1 -0
  43. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +1155 -1147
  44. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js.map +1 -0
  45. data/node_modules/govuk-frontend/govuk/components/cookie-banner/fixtures.json +23 -23
  46. data/node_modules/govuk-frontend/govuk/components/cookie-banner/template.njk +1 -1
  47. data/node_modules/govuk-frontend/govuk/components/details/details.js +800 -799
  48. data/node_modules/govuk-frontend/govuk/components/details/details.js.map +1 -0
  49. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +1058 -1045
  50. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js.map +1 -0
  51. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +6 -0
  52. data/node_modules/govuk-frontend/govuk/components/header/header.js +646 -998
  53. data/node_modules/govuk-frontend/govuk/components/header/header.js.map +1 -0
  54. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +760 -752
  55. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js.map +1 -0
  56. data/node_modules/govuk-frontend/govuk/components/pagination/fixtures.json +61 -0
  57. data/node_modules/govuk-frontend/govuk/components/pagination/template.njk +1 -1
  58. data/node_modules/govuk-frontend/govuk/components/phase-banner/macro-options.json +1 -1
  59. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +1110 -1107
  60. data/node_modules/govuk-frontend/govuk/components/radios/radios.js.map +1 -0
  61. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +1017 -1014
  62. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js.map +1 -0
  63. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +107 -0
  64. data/node_modules/govuk-frontend/govuk/components/summary-list/fixtures.json +318 -23
  65. data/node_modules/govuk-frontend/govuk/components/summary-list/macro-options.json +110 -0
  66. data/node_modules/govuk-frontend/govuk/components/summary-list/template.njk +72 -28
  67. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +1392 -1264
  68. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js.map +1 -0
  69. data/node_modules/govuk-frontend/govuk/i18n.js +363 -364
  70. data/node_modules/govuk-frontend/govuk/i18n.js.map +1 -0
  71. data/node_modules/govuk-frontend/govuk/settings/_measurements.scss +5 -5
  72. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js +242 -241
  73. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js.map +1 -0
  74. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +13 -12
  75. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js.map +1 -0
  76. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js +17 -16
  77. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js.map +1 -0
  78. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js +547 -546
  79. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js.map +1 -0
  80. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js +37 -36
  81. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js.map +1 -0
  82. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +251 -250
  83. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js.map +1 -0
  84. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js +21 -20
  85. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js.map +1 -0
  86. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js +198 -197
  87. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js.map +1 -0
  88. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js +198 -197
  89. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js.map +1 -0
  90. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js +106 -105
  91. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js.map +1 -0
  92. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js +400 -399
  93. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js.map +1 -0
  94. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js +239 -238
  95. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js.map +1 -0
  96. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js +72 -71
  97. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js.map +1 -0
  98. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +14 -13
  99. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js.map +1 -0
  100. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js +17 -16
  101. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js.map +1 -0
  102. data/node_modules/govuk-frontend/govuk-esm/all.mjs +2 -2
  103. data/node_modules/govuk-frontend/govuk-esm/common/index.mjs +17 -10
  104. data/node_modules/govuk-frontend/govuk-esm/common/normalise-dataset.mjs +3 -1
  105. data/node_modules/govuk-frontend/govuk-esm/components/accordion/accordion.mjs +135 -52
  106. data/node_modules/govuk-frontend/govuk-esm/components/button/button.mjs +11 -9
  107. data/node_modules/govuk-frontend/govuk-esm/components/character-count/character-count.mjs +10 -7
  108. data/node_modules/govuk-frontend/govuk-esm/components/checkboxes/checkboxes.mjs +24 -18
  109. data/node_modules/govuk-frontend/govuk-esm/components/details/details.mjs +23 -16
  110. data/node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs +15 -11
  111. data/node_modules/govuk-frontend/govuk-esm/components/header/header.mjs +3 -2
  112. data/node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs +3 -4
  113. data/node_modules/govuk-frontend/govuk-esm/components/radios/radios.mjs +10 -9
  114. data/node_modules/govuk-frontend/govuk-esm/components/skip-link/skip-link.mjs +5 -3
  115. data/node_modules/govuk-frontend/govuk-esm/components/tabs/tabs.mjs +165 -38
  116. data/node_modules/govuk-frontend/govuk-esm/i18n.mjs +9 -11
  117. data/node_modules/govuk-frontend/package.json +1 -1
  118. metadata +37 -2
@@ -1,390 +1,389 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
- typeof define === 'function' && define.amd ? define('GOVUKFrontend', ['exports'], factory) :
4
- (factory((global.GOVUKFrontend = {})));
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define('GOVUKFrontend', ['exports'], factory) :
4
+ (factory((global.GOVUKFrontend = {})));
5
5
  }(this, (function (exports) { 'use strict';
6
6
 
7
- /**
8
- * Internal support for selecting messages to render, with placeholder
9
- * interpolation and locale-aware number formatting and pluralisation
10
- *
11
- * @class
12
- * @private
13
- * @param {TranslationsFlattened} translations - Key-value pairs of the translation strings to use.
14
- * @param {object} [config] - Configuration options for the function.
15
- * @param {string} config.locale - An overriding locale for the PluralRules functionality.
16
- */
17
- function I18n (translations, config) {
18
- // Make list of translations available throughout function
19
- this.translations = translations || {};
20
-
21
- // The locale to use for PluralRules and NumberFormat
22
- this.locale = (config && config.locale) || document.documentElement.lang || 'en';
23
- }
24
-
25
- /**
26
- * The most used function - takes the key for a given piece of UI text and
27
- * returns the appropriate string.
28
- *
29
- * @param {string} lookupKey - The lookup key of the string to use.
30
- * @param {object} options - Any options passed with the translation string, e.g: for string interpolation.
31
- * @returns {string} The appropriate translation string.
32
- */
33
- I18n.prototype.t = function (lookupKey, options) {
34
- if (!lookupKey) {
35
- // Print a console error if no lookup key has been provided
36
- throw new Error('i18n: lookup key missing')
7
+ /**
8
+ * Internal support for selecting messages to render, with placeholder
9
+ * interpolation and locale-aware number formatting and pluralisation
10
+ *
11
+ * @class
12
+ * @private
13
+ * @param {Object<string, unknown>} translations - Key-value pairs of the translation strings to use.
14
+ * @param {object} [config] - Configuration options for the function.
15
+ * @param {string} [config.locale] - An overriding locale for the PluralRules functionality.
16
+ */
17
+ function I18n (translations, config) {
18
+ // Make list of translations available throughout function
19
+ this.translations = translations || {};
20
+
21
+ // The locale to use for PluralRules and NumberFormat
22
+ this.locale = (config && config.locale) || document.documentElement.lang || 'en';
37
23
  }
38
24
 
39
- // If the `count` option is set, determine which plural suffix is needed and
40
- // change the lookupKey to match. We check to see if it's undefined instead of
41
- // falsy, as this could legitimately be 0.
42
- if (options && typeof options.count !== 'undefined') {
43
- // Get the plural suffix
44
- lookupKey = lookupKey + '.' + this.getPluralSuffix(lookupKey, options.count);
45
- }
25
+ /**
26
+ * The most used function - takes the key for a given piece of UI text and
27
+ * returns the appropriate string.
28
+ *
29
+ * @param {string} lookupKey - The lookup key of the string to use.
30
+ * @param {Object<string, unknown>} [options] - Any options passed with the translation string, e.g: for string interpolation.
31
+ * @returns {string} The appropriate translation string.
32
+ * @throws {Error} Lookup key required
33
+ * @throws {Error} Options required for `${}` placeholders
34
+ */
35
+ I18n.prototype.t = function (lookupKey, options) {
36
+ if (!lookupKey) {
37
+ // Print a console error if no lookup key has been provided
38
+ throw new Error('i18n: lookup key missing')
39
+ }
46
40
 
47
- if (lookupKey in this.translations) {
48
- // Fetch the translation string for that lookup key
49
- var translationString = this.translations[lookupKey];
41
+ // If the `count` option is set, determine which plural suffix is needed and
42
+ // change the lookupKey to match. We check to see if it's undefined instead of
43
+ // falsy, as this could legitimately be 0.
44
+ if (options && typeof options.count !== 'undefined') {
45
+ // Get the plural suffix
46
+ lookupKey = lookupKey + '.' + this.getPluralSuffix(lookupKey, options.count);
47
+ }
50
48
 
51
- // Check for ${} placeholders in the translation string
52
- if (translationString.match(/%{(.\S+)}/)) {
53
- if (!options) {
54
- throw new Error('i18n: cannot replace placeholders in string if no option data provided')
55
- }
49
+ if (lookupKey in this.translations) {
50
+ // Fetch the translation string for that lookup key
51
+ var translationString = this.translations[lookupKey];
56
52
 
57
- return this.replacePlaceholders(translationString, options)
53
+ // Check for ${} placeholders in the translation string
54
+ if (translationString.match(/%{(.\S+)}/)) {
55
+ if (!options) {
56
+ throw new Error('i18n: cannot replace placeholders in string if no option data provided')
57
+ }
58
+
59
+ return this.replacePlaceholders(translationString, options)
60
+ } else {
61
+ return translationString
62
+ }
58
63
  } else {
59
- return translationString
64
+ // If the key wasn't found in our translations object,
65
+ // return the lookup key itself as the fallback
66
+ return lookupKey
60
67
  }
61
- } else {
62
- // If the key wasn't found in our translations object,
63
- // return the lookup key itself as the fallback
64
- return lookupKey
65
- }
66
- };
67
-
68
- /**
69
- * Takes a translation string with placeholders, and replaces the placeholders
70
- * with the provided data
71
- *
72
- * @param {string} translationString - The translation string
73
- * @param {object} options - Any options passed with the translation string, e.g: for string interpolation.
74
- * @returns {string} The translation string to output, with ${} placeholders replaced
75
- */
76
- I18n.prototype.replacePlaceholders = function (translationString, options) {
77
- var formatter;
78
-
79
- if (this.hasIntlNumberFormatSupport()) {
80
- formatter = new Intl.NumberFormat(this.locale);
81
- }
68
+ };
69
+
70
+ /**
71
+ * Takes a translation string with placeholders, and replaces the placeholders
72
+ * with the provided data
73
+ *
74
+ * @param {string} translationString - The translation string
75
+ * @param {Object<string, unknown>} options - Any options passed with the translation string, e.g: for string interpolation.
76
+ * @returns {string} The translation string to output, with ${} placeholders replaced
77
+ */
78
+ I18n.prototype.replacePlaceholders = function (translationString, options) {
79
+ var formatter;
80
+
81
+ if (this.hasIntlNumberFormatSupport()) {
82
+ formatter = new Intl.NumberFormat(this.locale);
83
+ }
84
+
85
+ return translationString.replace(/%{(.\S+)}/g, function (placeholderWithBraces, placeholderKey) {
86
+ if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
87
+ var placeholderValue = options[placeholderKey];
88
+
89
+ // If a user has passed `false` as the value for the placeholder
90
+ // treat it as though the value should not be displayed
91
+ if (placeholderValue === false) {
92
+ return ''
93
+ }
82
94
 
83
- return translationString.replace(/%{(.\S+)}/g, function (placeholderWithBraces, placeholderKey) {
84
- if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
85
- var placeholderValue = options[placeholderKey];
95
+ // If the placeholder's value is a number, localise the number formatting
96
+ if (typeof placeholderValue === 'number' && formatter) {
97
+ return formatter.format(placeholderValue)
98
+ }
86
99
 
87
- // If a user has passed `false` as the value for the placeholder
88
- // treat it as though the value should not be displayed
89
- if (placeholderValue === false) {
90
- return ''
100
+ return placeholderValue
101
+ } else {
102
+ throw new Error('i18n: no data found to replace ' + placeholderWithBraces + ' placeholder in string')
91
103
  }
104
+ })
105
+ };
106
+
107
+ /**
108
+ * Check to see if the browser supports Intl and Intl.PluralRules.
109
+ *
110
+ * It requires all conditions to be met in order to be supported:
111
+ * - The browser supports the Intl class (true in IE11)
112
+ * - The implementation of Intl supports PluralRules (NOT true in IE11)
113
+ * - The browser/OS has plural rules for the current locale (browser dependent)
114
+ *
115
+ * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
116
+ */
117
+ I18n.prototype.hasIntlPluralRulesSupport = function () {
118
+ return Boolean(window.Intl && ('PluralRules' in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length))
119
+ };
120
+
121
+ /**
122
+ * Check to see if the browser supports Intl and Intl.NumberFormat.
123
+ *
124
+ * It requires all conditions to be met in order to be supported:
125
+ * - The browser supports the Intl class (true in IE11)
126
+ * - The implementation of Intl supports NumberFormat (also true in IE11)
127
+ * - The browser/OS has number formatting rules for the current locale (browser dependent)
128
+ *
129
+ * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
130
+ */
131
+ I18n.prototype.hasIntlNumberFormatSupport = function () {
132
+ return Boolean(window.Intl && ('NumberFormat' in window.Intl && Intl.NumberFormat.supportedLocalesOf(this.locale).length))
133
+ };
134
+
135
+ /**
136
+ * Get the appropriate suffix for the plural form.
137
+ *
138
+ * Uses Intl.PluralRules (or our own fallback implementation) to get the
139
+ * 'preferred' form to use for the given count.
140
+ *
141
+ * Checks that a translation has been provided for that plural form – if it
142
+ * hasn't, it'll fall back to the 'other' plural form (unless that doesn't exist
143
+ * either, in which case an error will be thrown)
144
+ *
145
+ * @param {string} lookupKey - The lookup key of the string to use.
146
+ * @param {number} count - Number used to determine which pluralisation to use.
147
+ * @returns {PluralRule} The suffix associated with the correct pluralisation for this locale.
148
+ * @throws {Error} Plural form `.other` required when preferred plural form is missing
149
+ */
150
+ I18n.prototype.getPluralSuffix = function (lookupKey, count) {
151
+ // Validate that the number is actually a number.
152
+ //
153
+ // Number(count) will turn anything that can't be converted to a Number type
154
+ // into 'NaN'. isFinite filters out NaN, as it isn't a finite number.
155
+ count = Number(count);
156
+ if (!isFinite(count)) { return 'other' }
157
+
158
+ var preferredForm;
159
+
160
+ // Check to verify that all the requirements for Intl.PluralRules are met.
161
+ // If so, we can use that instead of our custom implementation. Otherwise,
162
+ // use the hardcoded fallback.
163
+ if (this.hasIntlPluralRulesSupport()) {
164
+ preferredForm = new Intl.PluralRules(this.locale).select(count);
165
+ } else {
166
+ preferredForm = this.selectPluralFormUsingFallbackRules(count);
167
+ }
92
168
 
93
- // If the placeholder's value is a number, localise the number formatting
94
- if (typeof placeholderValue === 'number' && formatter) {
95
- return formatter.format(placeholderValue)
169
+ // Use the correct plural form if provided
170
+ if (lookupKey + '.' + preferredForm in this.translations) {
171
+ return preferredForm
172
+ // Fall back to `other` if the plural form is missing, but log a warning
173
+ // to the console
174
+ } else if (lookupKey + '.other' in this.translations) {
175
+ if (console && 'warn' in console) {
176
+ console.warn('i18n: Missing plural form ".' + preferredForm + '" for "' +
177
+ this.locale + '" locale. Falling back to ".other".');
96
178
  }
97
179
 
98
- return placeholderValue
180
+ return 'other'
181
+ // If the required `other` plural form is missing, all we can do is error
99
182
  } else {
100
- throw new Error('i18n: no data found to replace ' + placeholderWithBraces + ' placeholder in string')
183
+ throw new Error(
184
+ 'i18n: Plural form ".other" is required for "' + this.locale + '" locale'
185
+ )
101
186
  }
102
- })
103
- };
104
-
105
- /**
106
- * Check to see if the browser supports Intl and Intl.PluralRules.
107
- *
108
- * It requires all conditions to be met in order to be supported:
109
- * - The browser supports the Intl class (true in IE11)
110
- * - The implementation of Intl supports PluralRules (NOT true in IE11)
111
- * - The browser/OS has plural rules for the current locale (browser dependent)
112
- *
113
- * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
114
- */
115
- I18n.prototype.hasIntlPluralRulesSupport = function () {
116
- return Boolean(window.Intl && ('PluralRules' in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length))
117
- };
118
-
119
- /**
120
- * Check to see if the browser supports Intl and Intl.NumberFormat.
121
- *
122
- * It requires all conditions to be met in order to be supported:
123
- * - The browser supports the Intl class (true in IE11)
124
- * - The implementation of Intl supports NumberFormat (also true in IE11)
125
- * - The browser/OS has number formatting rules for the current locale (browser dependent)
126
- *
127
- * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.
128
- */
129
- I18n.prototype.hasIntlNumberFormatSupport = function () {
130
- return Boolean(window.Intl && ('NumberFormat' in window.Intl && Intl.NumberFormat.supportedLocalesOf(this.locale).length))
131
- };
132
-
133
- /**
134
- * Get the appropriate suffix for the plural form.
135
- *
136
- * Uses Intl.PluralRules (or our own fallback implementation) to get the
137
- * 'preferred' form to use for the given count.
138
- *
139
- * Checks that a translation has been provided for that plural form – if it
140
- * hasn't, it'll fall back to the 'other' plural form (unless that doesn't exist
141
- * either, in which case an error will be thrown)
142
- *
143
- * @param {string} lookupKey - The lookup key of the string to use.
144
- * @param {number} count - Number used to determine which pluralisation to use.
145
- * @returns {PluralRule} The suffix associated with the correct pluralisation for this locale.
146
- */
147
- I18n.prototype.getPluralSuffix = function (lookupKey, count) {
148
- // Validate that the number is actually a number.
149
- //
150
- // Number(count) will turn anything that can't be converted to a Number type
151
- // into 'NaN'. isFinite filters out NaN, as it isn't a finite number.
152
- count = Number(count);
153
- if (!isFinite(count)) { return 'other' }
154
-
155
- var preferredForm;
156
-
157
- // Check to verify that all the requirements for Intl.PluralRules are met.
158
- // If so, we can use that instead of our custom implementation. Otherwise,
159
- // use the hardcoded fallback.
160
- if (this.hasIntlPluralRulesSupport()) {
161
- preferredForm = new Intl.PluralRules(this.locale).select(count);
162
- } else {
163
- preferredForm = this.selectPluralFormUsingFallbackRules(count);
164
- }
165
-
166
- // Use the correct plural form if provided
167
- if (lookupKey + '.' + preferredForm in this.translations) {
168
- return preferredForm
169
- // Fall back to `other` if the plural form is missing, but log a warning
170
- // to the console
171
- } else if (lookupKey + '.other' in this.translations) {
172
- if (console && 'warn' in console) {
173
- console.warn('i18n: Missing plural form ".' + preferredForm + '" for "' +
174
- this.locale + '" locale. Falling back to ".other".');
187
+ };
188
+
189
+ /**
190
+ * Get the plural form using our fallback implementation
191
+ *
192
+ * This is split out into a separate function to make it easier to test the
193
+ * fallback behaviour in an environment where Intl.PluralRules exists.
194
+ *
195
+ * @param {number} count - Number used to determine which pluralisation to use.
196
+ * @returns {PluralRule} The pluralisation form for count in this locale.
197
+ */
198
+ I18n.prototype.selectPluralFormUsingFallbackRules = function (count) {
199
+ // Currently our custom code can only handle positive integers, so let's
200
+ // make sure our number is one of those.
201
+ count = Math.abs(Math.floor(count));
202
+
203
+ var ruleset = this.getPluralRulesForLocale();
204
+
205
+ if (ruleset) {
206
+ return I18n.pluralRules[ruleset](count)
175
207
  }
176
208
 
177
209
  return 'other'
178
- // If the required `other` plural form is missing, all we can do is error
179
- } else {
180
- throw new Error(
181
- 'i18n: Plural form ".other" is required for "' + this.locale + '" locale'
182
- )
183
- }
184
- };
185
-
186
- /**
187
- * Get the plural form using our fallback implementation
188
- *
189
- * This is split out into a separate function to make it easier to test the
190
- * fallback behaviour in an environment where Intl.PluralRules exists.
191
- *
192
- * @param {number} count - Number used to determine which pluralisation to use.
193
- * @returns {PluralRule} The pluralisation form for count in this locale.
194
- */
195
- I18n.prototype.selectPluralFormUsingFallbackRules = function (count) {
196
- // Currently our custom code can only handle positive integers, so let's
197
- // make sure our number is one of those.
198
- count = Math.abs(Math.floor(count));
199
-
200
- var ruleset = this.getPluralRulesForLocale();
201
-
202
- if (ruleset) {
203
- return I18n.pluralRules[ruleset](count)
204
- }
205
-
206
- return 'other'
207
- };
208
-
209
- /**
210
- * Work out which pluralisation rules to use for the current locale
211
- *
212
- * The locale may include a regional indicator (such as en-GB), but we don't
213
- * usually care about this part, as pluralisation rules are usually the same
214
- * regardless of region. There are exceptions, however, (e.g. Portuguese) so
215
- * this searches by both the full and shortened locale codes, just to be sure.
216
- *
217
- * @returns {PluralRuleName | undefined} The name of the pluralisation rule to use (a key for one
218
- * of the functions in this.pluralRules)
219
- */
220
- I18n.prototype.getPluralRulesForLocale = function () {
221
- var locale = this.locale;
222
- var localeShort = locale.split('-')[0];
223
-
224
- // Look through the plural rules map to find which `pluralRule` is
225
- // appropriate for our current `locale`.
226
- for (var pluralRule in I18n.pluralRulesMap) {
227
- if (Object.prototype.hasOwnProperty.call(I18n.pluralRulesMap, pluralRule)) {
228
- var languages = I18n.pluralRulesMap[pluralRule];
229
- for (var i = 0; i < languages.length; i++) {
230
- if (languages[i] === locale || languages[i] === localeShort) {
231
- return pluralRule
210
+ };
211
+
212
+ /**
213
+ * Work out which pluralisation rules to use for the current locale
214
+ *
215
+ * The locale may include a regional indicator (such as en-GB), but we don't
216
+ * usually care about this part, as pluralisation rules are usually the same
217
+ * regardless of region. There are exceptions, however, (e.g. Portuguese) so
218
+ * this searches by both the full and shortened locale codes, just to be sure.
219
+ *
220
+ * @returns {PluralRuleName | undefined} The name of the pluralisation rule to use (a key for one
221
+ * of the functions in this.pluralRules)
222
+ */
223
+ I18n.prototype.getPluralRulesForLocale = function () {
224
+ var locale = this.locale;
225
+ var localeShort = locale.split('-')[0];
226
+
227
+ // Look through the plural rules map to find which `pluralRule` is
228
+ // appropriate for our current `locale`.
229
+ for (var pluralRule in I18n.pluralRulesMap) {
230
+ if (Object.prototype.hasOwnProperty.call(I18n.pluralRulesMap, pluralRule)) {
231
+ var languages = I18n.pluralRulesMap[pluralRule];
232
+ for (var i = 0; i < languages.length; i++) {
233
+ if (languages[i] === locale || languages[i] === localeShort) {
234
+ return pluralRule
235
+ }
232
236
  }
233
237
  }
234
238
  }
235
- }
236
- };
237
-
238
- /**
239
- * Map of plural rules to languages where those rules apply.
240
- *
241
- * Note: These groups are named for the most dominant or recognisable language
242
- * that uses each system. The groupings do not imply that the languages are
243
- * related to one another. Many languages have evolved the same systems
244
- * independently of one another.
245
- *
246
- * Code to support more languages can be found in the i18n spike:
247
- * {@link https://github.com/alphagov/govuk-frontend/blob/spike-i18n-support/src/govuk/i18n.mjs}
248
- *
249
- * Languages currently supported:
250
- *
251
- * Arabic: Arabic (ar)
252
- * Chinese: Burmese (my), Chinese (zh), Indonesian (id), Japanese (ja),
253
- * Javanese (jv), Korean (ko), Malay (ms), Thai (th), Vietnamese (vi)
254
- * French: Armenian (hy), Bangla (bn), French (fr), Gujarati (gu), Hindi (hi),
255
- * Persian Farsi (fa), Punjabi (pa), Zulu (zu)
256
- * German: Afrikaans (af), Albanian (sq), Azerbaijani (az), Basque (eu),
257
- * Bulgarian (bg), Catalan (ca), Danish (da), Dutch (nl), English (en),
258
- * Estonian (et), Finnish (fi), Georgian (ka), German (de), Greek (el),
259
- * Hungarian (hu), Luxembourgish (lb), Norwegian (no), Somali (so),
260
- * Swahili (sw), Swedish (sv), Tamil (ta), Telugu (te), Turkish (tr),
261
- * Urdu (ur)
262
- * Irish: Irish Gaelic (ga)
263
- * Russian: Russian (ru), Ukrainian (uk)
264
- * Scottish: Scottish Gaelic (gd)
265
- * Spanish: European Portuguese (pt-PT), Italian (it), Spanish (es)
266
- * Welsh: Welsh (cy)
267
- *
268
- * @type {Object<PluralRuleName, string[]>}
269
- */
270
- I18n.pluralRulesMap = {
271
- arabic: ['ar'],
272
- chinese: ['my', 'zh', 'id', 'ja', 'jv', 'ko', 'ms', 'th', 'vi'],
273
- french: ['hy', 'bn', 'fr', 'gu', 'hi', 'fa', 'pa', 'zu'],
274
- german: [
275
- 'af', 'sq', 'az', 'eu', 'bg', 'ca', 'da', 'nl', 'en', 'et', 'fi', 'ka',
276
- 'de', 'el', 'hu', 'lb', 'no', 'so', 'sw', 'sv', 'ta', 'te', 'tr', 'ur'
277
- ],
278
- irish: ['ga'],
279
- russian: ['ru', 'uk'],
280
- scottish: ['gd'],
281
- spanish: ['pt-PT', 'it', 'es'],
282
- welsh: ['cy']
283
- };
284
-
285
- /**
286
- * Different pluralisation rule sets
287
- *
288
- * Returns the appropriate suffix for the plural form associated with `n`.
289
- * Possible suffixes: 'zero', 'one', 'two', 'few', 'many', 'other' (the actual
290
- * meaning of each differs per locale). 'other' should always exist, even in
291
- * languages without plurals, such as Chinese.
292
- * {@link https://cldr.unicode.org/index/cldr-spec/plural-rules}
293
- *
294
- * The count must be a positive integer. Negative numbers and decimals aren't accounted for
295
- *
296
- * @type {Object<string, function(number): PluralRule>}
297
- */
298
- I18n.pluralRules = {
299
- arabic: function (n) {
300
- if (n === 0) { return 'zero' }
301
- if (n === 1) { return 'one' }
302
- if (n === 2) { return 'two' }
303
- if (n % 100 >= 3 && n % 100 <= 10) { return 'few' }
304
- if (n % 100 >= 11 && n % 100 <= 99) { return 'many' }
305
- return 'other'
306
- },
307
- chinese: function () {
308
- return 'other'
309
- },
310
- french: function (n) {
311
- return n === 0 || n === 1 ? 'one' : 'other'
312
- },
313
- german: function (n) {
314
- return n === 1 ? 'one' : 'other'
315
- },
316
- irish: function (n) {
317
- if (n === 1) { return 'one' }
318
- if (n === 2) { return 'two' }
319
- if (n >= 3 && n <= 6) { return 'few' }
320
- if (n >= 7 && n <= 10) { return 'many' }
321
- return 'other'
322
- },
323
- russian: function (n) {
324
- var lastTwo = n % 100;
325
- var last = lastTwo % 10;
326
- if (last === 1 && lastTwo !== 11) { return 'one' }
327
- if (last >= 2 && last <= 4 && !(lastTwo >= 12 && lastTwo <= 14)) { return 'few' }
328
- if (last === 0 || (last >= 5 && last <= 9) || (lastTwo >= 11 && lastTwo <= 14)) { return 'many' }
329
- // Note: The 'other' suffix is only used by decimal numbers in Russian.
330
- // We don't anticipate it being used, but it's here for consistency.
331
- return 'other'
332
- },
333
- scottish: function (n) {
334
- if (n === 1 || n === 11) { return 'one' }
335
- if (n === 2 || n === 12) { return 'two' }
336
- if ((n >= 3 && n <= 10) || (n >= 13 && n <= 19)) { return 'few' }
337
- return 'other'
338
- },
339
- spanish: function (n) {
340
- if (n === 1) { return 'one' }
341
- if (n % 1000000 === 0 && n !== 0) { return 'many' }
342
- return 'other'
343
- },
344
- welsh: function (n) {
345
- if (n === 0) { return 'zero' }
346
- if (n === 1) { return 'one' }
347
- if (n === 2) { return 'two' }
348
- if (n === 3) { return 'few' }
349
- if (n === 6) { return 'many' }
350
- return 'other'
351
- }
352
- };
353
-
354
- /**
355
- * Supported languages for plural rules
356
- *
357
- * @typedef {'arabic' | 'chinese' | 'french' | 'german' | 'irish' | 'russian' | 'scottish' | 'spanish' | 'welsh'} PluralRuleName
358
- */
359
-
360
- /**
361
- * Plural rule category mnemonic tags
362
- *
363
- * @typedef {'zero' | 'one' | 'two' | 'few' | 'many' | 'other'} PluralRule
364
- */
365
-
366
- /**
367
- * Translated message by plural rule they correspond to.
368
- *
369
- * Allows to group pluralised messages under a single key when passing
370
- * translations to a component's constructor
371
- *
372
- * @typedef {object} TranslationPluralForms
373
- * @property {string} [other] - General plural form
374
- * @property {string} [zero] - Plural form used with 0
375
- * @property {string} [one] - Plural form used with 1
376
- * @property {string} [two] - Plural form used with 2
377
- * @property {string} [few] - Plural form used for a few
378
- * @property {string} [many] - Plural form used for many
379
- */
380
-
381
- /**
382
- * Translated messages (flattened)
383
- *
384
- * @private
385
- * @typedef {Object<string, string> | {}} TranslationsFlattened
386
- */
387
-
388
- exports.I18n = I18n;
239
+ };
240
+
241
+ /**
242
+ * Map of plural rules to languages where those rules apply.
243
+ *
244
+ * Note: These groups are named for the most dominant or recognisable language
245
+ * that uses each system. The groupings do not imply that the languages are
246
+ * related to one another. Many languages have evolved the same systems
247
+ * independently of one another.
248
+ *
249
+ * Code to support more languages can be found in the i18n spike:
250
+ * {@link https://github.com/alphagov/govuk-frontend/blob/spike-i18n-support/src/govuk/i18n.mjs}
251
+ *
252
+ * Languages currently supported:
253
+ *
254
+ * Arabic: Arabic (ar)
255
+ * Chinese: Burmese (my), Chinese (zh), Indonesian (id), Japanese (ja),
256
+ * Javanese (jv), Korean (ko), Malay (ms), Thai (th), Vietnamese (vi)
257
+ * French: Armenian (hy), Bangla (bn), French (fr), Gujarati (gu), Hindi (hi),
258
+ * Persian Farsi (fa), Punjabi (pa), Zulu (zu)
259
+ * German: Afrikaans (af), Albanian (sq), Azerbaijani (az), Basque (eu),
260
+ * Bulgarian (bg), Catalan (ca), Danish (da), Dutch (nl), English (en),
261
+ * Estonian (et), Finnish (fi), Georgian (ka), German (de), Greek (el),
262
+ * Hungarian (hu), Luxembourgish (lb), Norwegian (no), Somali (so),
263
+ * Swahili (sw), Swedish (sv), Tamil (ta), Telugu (te), Turkish (tr),
264
+ * Urdu (ur)
265
+ * Irish: Irish Gaelic (ga)
266
+ * Russian: Russian (ru), Ukrainian (uk)
267
+ * Scottish: Scottish Gaelic (gd)
268
+ * Spanish: European Portuguese (pt-PT), Italian (it), Spanish (es)
269
+ * Welsh: Welsh (cy)
270
+ *
271
+ * @type {Object<PluralRuleName, string[]>}
272
+ */
273
+ I18n.pluralRulesMap = {
274
+ arabic: ['ar'],
275
+ chinese: ['my', 'zh', 'id', 'ja', 'jv', 'ko', 'ms', 'th', 'vi'],
276
+ french: ['hy', 'bn', 'fr', 'gu', 'hi', 'fa', 'pa', 'zu'],
277
+ german: [
278
+ 'af', 'sq', 'az', 'eu', 'bg', 'ca', 'da', 'nl', 'en', 'et', 'fi', 'ka',
279
+ 'de', 'el', 'hu', 'lb', 'no', 'so', 'sw', 'sv', 'ta', 'te', 'tr', 'ur'
280
+ ],
281
+ irish: ['ga'],
282
+ russian: ['ru', 'uk'],
283
+ scottish: ['gd'],
284
+ spanish: ['pt-PT', 'it', 'es'],
285
+ welsh: ['cy']
286
+ };
287
+
288
+ /**
289
+ * Different pluralisation rule sets
290
+ *
291
+ * Returns the appropriate suffix for the plural form associated with `n`.
292
+ * Possible suffixes: 'zero', 'one', 'two', 'few', 'many', 'other' (the actual
293
+ * meaning of each differs per locale). 'other' should always exist, even in
294
+ * languages without plurals, such as Chinese.
295
+ * {@link https://cldr.unicode.org/index/cldr-spec/plural-rules}
296
+ *
297
+ * The count must be a positive integer. Negative numbers and decimals aren't accounted for
298
+ *
299
+ * @type {Object<string, function(number): PluralRule>}
300
+ */
301
+ I18n.pluralRules = {
302
+ /* eslint-disable jsdoc/require-jsdoc */
303
+ arabic: function (n) {
304
+ if (n === 0) { return 'zero' }
305
+ if (n === 1) { return 'one' }
306
+ if (n === 2) { return 'two' }
307
+ if (n % 100 >= 3 && n % 100 <= 10) { return 'few' }
308
+ if (n % 100 >= 11 && n % 100 <= 99) { return 'many' }
309
+ return 'other'
310
+ },
311
+ chinese: function () {
312
+ return 'other'
313
+ },
314
+ french: function (n) {
315
+ return n === 0 || n === 1 ? 'one' : 'other'
316
+ },
317
+ german: function (n) {
318
+ return n === 1 ? 'one' : 'other'
319
+ },
320
+ irish: function (n) {
321
+ if (n === 1) { return 'one' }
322
+ if (n === 2) { return 'two' }
323
+ if (n >= 3 && n <= 6) { return 'few' }
324
+ if (n >= 7 && n <= 10) { return 'many' }
325
+ return 'other'
326
+ },
327
+ russian: function (n) {
328
+ var lastTwo = n % 100;
329
+ var last = lastTwo % 10;
330
+ if (last === 1 && lastTwo !== 11) { return 'one' }
331
+ if (last >= 2 && last <= 4 && !(lastTwo >= 12 && lastTwo <= 14)) { return 'few' }
332
+ if (last === 0 || (last >= 5 && last <= 9) || (lastTwo >= 11 && lastTwo <= 14)) { return 'many' }
333
+ // Note: The 'other' suffix is only used by decimal numbers in Russian.
334
+ // We don't anticipate it being used, but it's here for consistency.
335
+ return 'other'
336
+ },
337
+ scottish: function (n) {
338
+ if (n === 1 || n === 11) { return 'one' }
339
+ if (n === 2 || n === 12) { return 'two' }
340
+ if ((n >= 3 && n <= 10) || (n >= 13 && n <= 19)) { return 'few' }
341
+ return 'other'
342
+ },
343
+ spanish: function (n) {
344
+ if (n === 1) { return 'one' }
345
+ if (n % 1000000 === 0 && n !== 0) { return 'many' }
346
+ return 'other'
347
+ },
348
+ welsh: function (n) {
349
+ if (n === 0) { return 'zero' }
350
+ if (n === 1) { return 'one' }
351
+ if (n === 2) { return 'two' }
352
+ if (n === 3) { return 'few' }
353
+ if (n === 6) { return 'many' }
354
+ return 'other'
355
+ }
356
+ /* eslint-enable jsdoc/require-jsdoc */
357
+ };
358
+
359
+ /**
360
+ * Supported languages for plural rules
361
+ *
362
+ * @typedef {'arabic' | 'chinese' | 'french' | 'german' | 'irish' | 'russian' | 'scottish' | 'spanish' | 'welsh'} PluralRuleName
363
+ */
364
+
365
+ /**
366
+ * Plural rule category mnemonic tags
367
+ *
368
+ * @typedef {'zero' | 'one' | 'two' | 'few' | 'many' | 'other'} PluralRule
369
+ */
370
+
371
+ /**
372
+ * Translated message by plural rule they correspond to.
373
+ *
374
+ * Allows to group pluralised messages under a single key when passing
375
+ * translations to a component's constructor
376
+ *
377
+ * @typedef {object} TranslationPluralForms
378
+ * @property {string} [other] - General plural form
379
+ * @property {string} [zero] - Plural form used with 0
380
+ * @property {string} [one] - Plural form used with 1
381
+ * @property {string} [two] - Plural form used with 2
382
+ * @property {string} [few] - Plural form used for a few
383
+ * @property {string} [many] - Plural form used for many
384
+ */
385
+
386
+ exports.I18n = I18n;
389
387
 
390
388
  })));
389
+ //# sourceMappingURL=i18n.js.map