@plone/volto 16.20.4 → 16.20.6

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 (38) hide show
  1. package/.changelog.draft +2 -6
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +39 -13
  4. package/CONTRIBUTING.md +1 -1
  5. package/README.md +4 -4
  6. package/package.json +1 -1
  7. package/packages/volto-slate/package.json +1 -1
  8. package/src/actions/language/language.js +8 -8
  9. package/src/components/manage/Add/Add.jsx +2 -2
  10. package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
  11. package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
  12. package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
  13. package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
  14. package/src/components/manage/Toolbar/Types.jsx +2 -2
  15. package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
  16. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
  17. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
  18. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
  19. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +7 -2
  20. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
  21. package/src/components/theme/Footer/Footer.jsx +2 -13
  22. package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
  23. package/src/components/theme/Login/Login.jsx +1 -0
  24. package/src/components/theme/Logo/Logo.jsx +2 -1
  25. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
  26. package/src/components/theme/Navigation/NavItem.jsx +4 -2
  27. package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
  28. package/src/components/theme/View/EventDatesInfo.jsx +2 -1
  29. package/src/components/theme/Widgets/DateWidget.jsx +2 -1
  30. package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
  31. package/src/helpers/Url/Url.js +8 -3
  32. package/src/helpers/Url/Url.test.js +14 -0
  33. package/src/helpers/Utils/Utils.js +21 -9
  34. package/src/helpers/Utils/Utils.test.js +4 -4
  35. package/src/helpers/index.js +5 -2
  36. package/src/middleware/api.js +7 -3
  37. package/src/server.jsx +28 -23
  38. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
package/.changelog.draft CHANGED
@@ -1,10 +1,6 @@
1
- ## 16.20.4 (2023-04-20)
1
+ ## 16.20.6 (2023-05-12)
2
2
 
3
3
  ### Bugfix
4
4
 
5
- - Fix fetching API paths with urlencoded characters in the querystring. @davisagli [#4718](https://github.com/plone/volto/issues/4718)
6
-
7
- ### Internal
8
-
9
- - Security upgrade for momentjs [#4716](https://github.com/plone/volto/issues/4716)
5
+ - Fix language negotiation for language codes that include a region (e.g. `pt-br`). @davisagli [#4644](https://github.com/plone/volto/issues/4644)
10
6
 
Binary file
package/CHANGELOG.md CHANGED
@@ -8,6 +8,32 @@
8
8
 
9
9
  <!-- towncrier release notes start -->
10
10
 
11
+ ## 16.20.6 (2023-05-12)
12
+
13
+ ### Bugfix
14
+
15
+ - Fix language negotiation for language codes that include a region (e.g. `pt-br`). @davisagli [#4644](https://github.com/plone/volto/issues/4644)
16
+
17
+
18
+ ## 16.20.5 (2023-05-12)
19
+
20
+ ### Bugfix
21
+
22
+ - Apply suggestion from browser for password field @lord2anil [#3990](https://github.com/plone/volto/issues/3990)
23
+ - The tabs for the add page was unresponsive on mobile devices. Fixed this by changing flex-wrap property. @sudhanshu1309 [#4506](https://github.com/plone/volto/issues/4506)
24
+ - (fix):Object.normaliseMail: Cannot read properties of null @dobri1408 [#4558](https://github.com/plone/volto/issues/4558)
25
+
26
+ ### Internal
27
+
28
+ - Upgrade to Plone 6.0.4 @sneridagh [#4743](https://github.com/plone/volto/issues/4743)
29
+
30
+ ### Documentation
31
+
32
+ - Added documentation regarding the static middleware. @BhardwajAditya-github [#4518](https://github.com/plone/volto/issues/4518)
33
+ - Backport most documentation differences from `master` to `16.x.x`. @stevepiercy [#4727](https://github.com/plone/volto/issues/4727)
34
+ - Fix link in Volto, remove from linkcheck ignore in Documentation. @stevepiercy [#4742](https://github.com/plone/volto/issues/4742)
35
+
36
+
11
37
  ## 16.20.4 (2023-04-20)
12
38
 
13
39
  ### Bugfix
@@ -274,7 +300,7 @@
274
300
 
275
301
  ### Documentation
276
302
 
277
- - Complete teaser docs, add new section in `Blocks`: `Core Blocks developers notes` @sneridagh [#4461](https://github.com/plone/volto/issues/4461)
303
+ - Complete teaser docs, add new section in `Blocks`: `Core Blocks developers notes` @sneridagh [#4461](https://github.com/plone/volto/pull/4461)
278
304
 
279
305
 
280
306
  ## 16.14.0 (2023-03-03)
@@ -604,7 +630,7 @@
604
630
  - Enable the use of yarn 3 in the build by default @sneridagh
605
631
  - The `ContentsBreadcrumbs` component now renders the whole language name of the language root folder (if any) instead of just the `id` (before: `de`, now: `Deutsch`) @sneridagh
606
632
 
607
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
633
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
608
634
 
609
635
  ### Feature
610
636
 
@@ -1099,7 +1125,7 @@ See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more informa
1099
1125
  - Moved all sentry-related code from Volto to the `@plone-collective/volto-sentry` package. @tiberiuichim
1100
1126
  - The listing block icon has been improved to avoid confusion with the normal text list. @sneridagh
1101
1127
 
1102
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
1128
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
1103
1129
 
1104
1130
  ### Feature
1105
1131
 
@@ -1209,7 +1235,7 @@ See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more informa
1209
1235
 
1210
1236
  - Sentry integration is now lazy-loaded. The `sentryOptions` key from the `settings` registry becomes a callable that passes resolved sentry libraries. @tiberiuichim
1211
1237
 
1212
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
1238
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
1213
1239
 
1214
1240
  ### Feature
1215
1241
 
@@ -1273,7 +1299,7 @@ See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more informa
1273
1299
  - Upgrade to Razzle 4 @davisagli
1274
1300
  - Jest downgraded from 27 to 26 @davisagli
1275
1301
 
1276
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
1302
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
1277
1303
 
1278
1304
  ### Internal
1279
1305
 
@@ -1313,7 +1339,7 @@ See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more informa
1313
1339
  ### Breaking
1314
1340
 
1315
1341
  - `react-window` no longer a Volto dependency @sneridagh
1316
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
1342
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
1317
1343
 
1318
1344
  ### Bugfix
1319
1345
 
@@ -1352,7 +1378,7 @@ See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more informa
1352
1378
  ### Breaking
1353
1379
 
1354
1380
  - Move Layout constants to `config.views.layoutViewsNamesMapping`. Complete the list. i18n the list. Improve Display component. @sneridagh
1355
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
1381
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
1356
1382
 
1357
1383
  ### Feature
1358
1384
 
@@ -1398,7 +1424,7 @@ Undo html_static_path configuration in `plone/documentation`, and restore image
1398
1424
  ### Breaking
1399
1425
 
1400
1426
  - Main workflow change menu changed from Pastanaga UI simplification to classic Plone implementation. @sneridagh
1401
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
1427
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
1402
1428
 
1403
1429
  ### Feature
1404
1430
 
@@ -1494,7 +1520,7 @@ Undo html_static_path configuration in `plone/documentation`, and restore image
1494
1520
  - change password-reset url to be consistent with Plone configuration @erral
1495
1521
  - Simplify over the existing Component Registry API. The `component` key has been flattened for simplification and now it's mapped directly to the `component` argument of `registerComponent`. @sneridagh
1496
1522
 
1497
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
1523
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
1498
1524
 
1499
1525
  ### Feature
1500
1526
 
@@ -2072,14 +2098,14 @@ Use next release instead: https://github.com/plone/volto/releases/tag/16.0.0-alp
2072
2098
  ### Breaking
2073
2099
 
2074
2100
  - Upgrade `react-cookie` to the latest version. @sneridagh @robgietema
2075
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
2101
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
2076
2102
  - Language Switcher no longer takes care of the change of the language on the Redux Store. This responsibility has been unified in the API Redux middleware @sneridagh
2077
2103
  - Markup change in `LinkView` component.
2078
2104
  - Rename `core-sandbox` to `coresandbox` for sake of consistency @sneridagh
2079
2105
  - Extend the original intent and rename `RAZZLE_TESTING_ADDONS` to `ADDONS`. @sneridagh
2080
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
2106
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
2081
2107
  - Lazyload Draft.js library. See the upgrade guide on how that impacts you, in case you have extended the rich text editor configuration @tiberiuichim @kreafox
2082
- See https://6.dev-docs.plone.org/volto/upgrade-guide/index.html for more information.
2108
+ See https://6.docs.plone.org/volto/upgrade-guide/index.html for more information.
2083
2109
  - Deprecating `lang` cookie in favor of Plone official one `I18N_LANGUAGE` @sneridagh
2084
2110
 
2085
2111
  ### Feature
@@ -5025,7 +5051,7 @@ https://6.docs.plone.org/volto/upgrade-guide/index.html
5025
5051
  - Added item type as a tooltip in contents @nzambello
5026
5052
  - Added Italian translations and translated array, token and select widget. @giuliaghisini
5027
5053
  - Added uploading image preview in FileWidget @iFlameing
5028
- - Allow custom express middleware declared with `settings.expressMiddleware`. See [Custom Express middleware](https://6.dev-docs.plone.org/volto/recipes/express.html) @tiberiuichim
5054
+ - Allow custom express middleware declared with `settings.expressMiddleware`. See [Custom Express middleware](https://6.docs.plone.org/volto/recipes/express.html) @tiberiuichim
5029
5055
 
5030
5056
  ### Bugfix
5031
5057
 
package/CONTRIBUTING.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # Contributing to Volto
2
2
 
3
- See [Contributing to Volto](https://6.dev-docs.plone.org/volto/developer-guidelines/contributing.html).
3
+ See [Contributing to Volto](https://6.docs.plone.org/volto/developer-guidelines/contributing.html).
package/README.md CHANGED
@@ -181,7 +181,7 @@ Please create a new [issue](https://github.com/plone/volto/issues/new) or [pull
181
181
 
182
182
  ## Documentation
183
183
 
184
- You can find the latest (in-progress) documentation in [https://6.dev-docs.plone.org/](https://6.dev-docs.plone.org/volto/index.html)
184
+ You can find the latest documentation in [https://6.docs.plone.org/](https://6.docs.plone.org/volto/index.html)
185
185
 
186
186
  ## Training
187
187
 
@@ -243,7 +243,7 @@ We do not guarantee that deprecated browsers (e.g., Internet Explorer 11) are su
243
243
 
244
244
  ## Upgrades
245
245
 
246
- You can find the upgrade guide here: https://6.dev-docs.plone.org/volto/upgrade-guide/index.html
246
+ You can find the upgrade guide here: https://6.docs.plone.org/volto/upgrade-guide/index.html
247
247
 
248
248
  ## Volto Development
249
249
 
@@ -318,11 +318,11 @@ yarn test
318
318
 
319
319
  Here you can find a guide on how acceptance testing is done in Volto:
320
320
 
321
- https://6.dev-docs.plone.org/volto/developer-guidelines/acceptance-tests.html
321
+ https://6.docs.plone.org/volto/developer-guidelines/acceptance-tests.html
322
322
 
323
323
  ## Translations
324
324
 
325
- If you would like contribute to translate Volto into several languages, please, read the [Internationalization (i18n) guide](https://6.dev-docs.plone.org/volto/recipes/i18n.html).
325
+ If you would like contribute to translate Volto into several languages, please, read the [Internationalization (i18n) guide](https://6.docs.plone.org/volto/recipes/i18n.html).
326
326
 
327
327
  ## Contributors
328
328
 
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "16.20.4",
12
+ "version": "16.20.6",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone/volto-slate",
3
- "version": "16.20.4",
3
+ "version": "16.20.6",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -1,5 +1,9 @@
1
1
  import { updateIntl } from 'react-intl-redux';
2
- import { normalizeLanguageName, getCookieOptions } from '@plone/volto/helpers';
2
+ import {
3
+ toGettextLang,
4
+ toReactIntlLang,
5
+ getCookieOptions,
6
+ } from '@plone/volto/helpers';
3
7
  import Cookies from 'universal-cookie';
4
8
 
5
9
  export function changeLanguageCookies(language, req) {
@@ -11,15 +15,11 @@ export function changeLanguageCookies(language, req) {
11
15
  });
12
16
 
13
17
  if (!req) {
14
- cookies.set(
15
- 'I18N_LANGUAGE',
16
- normalizeLanguageName(language) || '',
17
- cookieOptions,
18
- );
18
+ cookies.set('I18N_LANGUAGE', toGettextLang(language) || '', cookieOptions);
19
19
  } else {
20
20
  req.universalCookies.set(
21
21
  'I18N_LANGUAGE',
22
- normalizeLanguageName(language) || '',
22
+ toGettextLang(language) || '',
23
23
  cookieOptions,
24
24
  );
25
25
  }
@@ -36,7 +36,7 @@ export function changeLanguage(language, locale, req) {
36
36
  changeLanguageCookies(language, req);
37
37
 
38
38
  return updateIntl({
39
- locale: language,
39
+ locale: toReactIntlLang(language),
40
40
  messages: locale,
41
41
  });
42
42
  }
@@ -33,7 +33,7 @@ import {
33
33
  getBlocksLayoutFieldname,
34
34
  getLanguageIndependentFields,
35
35
  langmap,
36
- normalizeLanguageName,
36
+ toGettextLang,
37
37
  } from '@plone/volto/helpers';
38
38
 
39
39
  import { preloadLazyLibs } from '@plone/volto/helpers/Loadable';
@@ -219,7 +219,7 @@ class Add extends Component {
219
219
  onCancel() {
220
220
  if (this.props.location?.state?.translationOf) {
221
221
  const language = this.props.location.state.languageFrom;
222
- const langFileName = normalizeLanguageName(language);
222
+ const langFileName = toGettextLang(language);
223
223
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
224
224
  this.props.changeLanguage(language, locale.default);
225
225
  });
@@ -4,6 +4,7 @@ import { defineMessages, injectIntl } from 'react-intl';
4
4
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
5
5
  import { compose } from 'redux';
6
6
  import { Icon } from '@plone/volto/components';
7
+ import { toBackendLang } from '@plone/volto/helpers/Utils/Utils';
7
8
  import { connect } from 'react-redux';
8
9
 
9
10
  import leftKey from '@plone/volto/icons/left-key.svg';
@@ -82,7 +83,9 @@ const DateRangeFacet = (props) => {
82
83
  noBorder
83
84
  showClearDates
84
85
  customCloseIcon={<CloseIcon />}
85
- displayFormat={moment.localeData(lang).longDateFormat('L')}
86
+ displayFormat={moment
87
+ .localeData(toBackendLang(lang))
88
+ .longDateFormat('L')}
86
89
  focusedInput={focused}
87
90
  onFocusChange={(focusedInput) => setFocused(focusedInput)}
88
91
  onDatesChange={({ startDate, endDate }) => {
@@ -6,7 +6,7 @@ import {
6
6
  getTranslationLocator,
7
7
  getContent,
8
8
  } from '@plone/volto/actions';
9
- import { flattenToAppURL, normalizeLanguageName } from '@plone/volto/helpers';
9
+ import { flattenToAppURL, toGettextLang } from '@plone/volto/helpers';
10
10
  import config from '@plone/volto/registry';
11
11
 
12
12
  const CreateTranslation = (props) => {
@@ -33,7 +33,7 @@ const CreateTranslation = (props) => {
33
33
  return () => {
34
34
  // We change the interface language
35
35
  if (config.settings.supportedLanguages.includes(language)) {
36
- const langFileName = normalizeLanguageName(language);
36
+ const langFileName = toGettextLang(language);
37
37
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
38
38
  dispatch(changeLanguage(language, locale.default));
39
39
  });
@@ -15,7 +15,8 @@ import {
15
15
  Api,
16
16
  flattenToAppURL,
17
17
  langmap,
18
- normalizeLanguageName,
18
+ toGettextLang,
19
+ toReactIntlLang,
19
20
  } from '@plone/volto/helpers';
20
21
  import { createBrowserHistory } from 'history';
21
22
  const messages = defineMessages({
@@ -55,9 +56,9 @@ const TranslationObject = ({
55
56
  setLoadingLocale(true);
56
57
  let lang =
57
58
  config.settings.supportedLanguages[Object.keys(locales).length];
58
- const langFileName = normalizeLanguageName(lang);
59
+ const langFileName = toGettextLang(lang);
59
60
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
60
- setLocales({ ...locales, [lang]: locale.default });
61
+ setLocales({ ...locales, [toReactIntlLang(lang)]: locale.default });
61
62
  setLoadingLocale(false);
62
63
  });
63
64
  }
@@ -15,7 +15,7 @@ import { toast } from 'react-toastify';
15
15
  import { Form, Toast } from '@plone/volto/components';
16
16
  import languages from '@plone/volto/constants/Languages';
17
17
  import { changeLanguage } from '@plone/volto/actions';
18
- import { normalizeLanguageName } from '@plone/volto/helpers';
18
+ import { toGettextLang } from '@plone/volto/helpers';
19
19
  import config from '@plone/volto/registry';
20
20
 
21
21
  const messages = defineMessages({
@@ -86,7 +86,7 @@ class PersonalPreferences extends Component {
86
86
  onSubmit(data) {
87
87
  let language = data.language || 'en';
88
88
  if (config.settings.supportedLanguages.includes(language)) {
89
- const langFileName = normalizeLanguageName(language);
89
+ const langFileName = toGettextLang(language);
90
90
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
91
91
  this.props.changeLanguage(language, locale.default);
92
92
  });
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
4
4
  import { Link } from 'react-router-dom';
5
5
  import { filter, find, isEmpty, map } from 'lodash';
6
6
  import { FormattedMessage } from 'react-intl';
7
- import { flattenToAppURL, langmap } from '@plone/volto/helpers';
7
+ import { flattenToAppURL, langmap, toBackendLang } from '@plone/volto/helpers';
8
8
  import config from '@plone/volto/registry';
9
9
 
10
10
  const Types = ({ types, pathname, content, currentLanguage }) => {
@@ -59,7 +59,7 @@ const Types = ({ types, pathname, content, currentLanguage }) => {
59
59
  find(content['@components'].translations.items, {
60
60
  language: lang,
61
61
  }),
62
- ) && currentLanguage !== lang,
62
+ ) && toBackendLang(currentLanguage) !== lang,
63
63
  );
64
64
 
65
65
  return (
@@ -10,7 +10,7 @@ import { connect } from 'react-redux';
10
10
  import loadable from '@loadable/component';
11
11
  import cx from 'classnames';
12
12
  import { Icon, FormFieldWrapper } from '@plone/volto/components';
13
- import { parseDateTime } from '@plone/volto/helpers';
13
+ import { parseDateTime, toBackendLang } from '@plone/volto/helpers';
14
14
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
15
15
 
16
16
  import leftKey from '@plone/volto/icons/left-key.svg';
@@ -101,7 +101,7 @@ export class DatetimeWidgetComponent extends Component {
101
101
  // if passed value matches the construction time, we guess it's a default
102
102
  isDefault:
103
103
  parseDateTime(
104
- this.props.lang,
104
+ toBackendLang(this.props.lang),
105
105
  this.props.value,
106
106
  undefined,
107
107
  this.moment,
@@ -111,7 +111,7 @@ export class DatetimeWidgetComponent extends Component {
111
111
 
112
112
  getInternalValue() {
113
113
  return parseDateTime(
114
- this.props.lang,
114
+ toBackendLang(this.props.lang),
115
115
  this.props.value,
116
116
  undefined,
117
117
  this.moment,
@@ -211,7 +211,9 @@ export class DatetimeWidgetComponent extends Component {
211
211
  {...(noPastDates ? {} : { isOutsideRange: () => false })}
212
212
  onFocusChange={this.onFocusChange}
213
213
  noBorder
214
- displayFormat={moment.localeData(lang).longDateFormat('L')}
214
+ displayFormat={moment
215
+ .localeData(toBackendLang(lang))
216
+ .longDateFormat('L')}
215
217
  navPrev={<PrevIcon />}
216
218
  navNext={<NextIcon />}
217
219
  id={`${id}-date`}
@@ -233,7 +235,9 @@ export class DatetimeWidgetComponent extends Component {
233
235
  showSecond={false}
234
236
  use12Hours={lang === 'en'}
235
237
  id={`${id}-time`}
236
- format={moment.localeData(lang).longDateFormat('LT')}
238
+ format={moment
239
+ .localeData(toBackendLang(lang))
240
+ .longDateFormat('LT')}
237
241
  placeholder={intl.formatMessage(messages.time)}
238
242
  focusOnOpen
239
243
  placement="bottomRight"
@@ -7,6 +7,7 @@ import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { Form, Grid, Button } from 'semantic-ui-react';
9
9
  import { Days } from './Utils';
10
+ import { toBackendLang } from '@plone/volto/helpers';
10
11
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
11
12
  import { useSelector } from 'react-redux';
12
13
 
@@ -18,7 +19,7 @@ import { useSelector } from 'react-redux';
18
19
  const ByDayField = ({ label, value, onChange, moment: momentlib }) => {
19
20
  const lang = useSelector((state) => state.intl.locale);
20
21
  const moment = momentlib.default;
21
- moment.locale(lang);
22
+ moment.locale(toBackendLang(lang));
22
23
 
23
24
  const toggleWeekDay = (dayName) => {
24
25
  var day = Days[dayName];
@@ -8,6 +8,7 @@ import PropTypes from 'prop-types';
8
8
  import { map } from 'lodash';
9
9
  import { Form } from 'semantic-ui-react';
10
10
  import SelectInput from './SelectInput';
11
+ import { toBackendLang } from '@plone/volto/helpers';
11
12
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
12
13
  import { useSelector } from 'react-redux';
13
14
 
@@ -25,7 +26,7 @@ const MonthOfTheYearField = ({
25
26
  }) => {
26
27
  const moment = momentlib.default;
27
28
  const lang = useSelector((state) => state.intl.locale);
28
- moment.locale(lang);
29
+ moment.locale(toBackendLang(lang));
29
30
  const monthList = [
30
31
  ...map(moment.months(), (m, i) => ({
31
32
  value: i + 1,
@@ -11,6 +11,7 @@ import { List, Button, Header, Label } from 'semantic-ui-react';
11
11
  import { Icon } from '@plone/volto/components';
12
12
  import addSVG from '@plone/volto/icons/circle-plus.svg';
13
13
  import trashSVG from '@plone/volto/icons/delete.svg';
14
+ import { toBackendLang } from '@plone/volto/helpers';
14
15
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
15
16
 
16
17
  import { useSelector } from 'react-redux';
@@ -68,7 +69,7 @@ const Occurences_ = ({
68
69
  }) => {
69
70
  const moment = momentlib.default;
70
71
  const lang = useSelector((state) => state.intl.locale);
71
- moment.locale(lang);
72
+ moment.locale(toBackendLang(lang));
72
73
  let all = [];
73
74
  const isExcluded = (date) => {
74
75
  var dateISO = toISOString(date);
@@ -23,6 +23,7 @@ import {
23
23
  } from 'semantic-ui-react';
24
24
 
25
25
  import { SelectWidget, Icon, DatetimeWidget } from '@plone/volto/components';
26
+ import { toBackendLang } from '@plone/volto/helpers';
26
27
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
27
28
 
28
29
  import saveSVG from '@plone/volto/icons/save.svg';
@@ -183,7 +184,7 @@ class RecurrenceWidget extends Component {
183
184
  const { RRuleSet, rrulestr } = props.rrule;
184
185
 
185
186
  this.moment = this.props.moment.default;
186
- this.moment.locale(this.props.lang);
187
+ this.moment.locale(toBackendLang(this.props.lang));
187
188
 
188
189
  let rruleSet = this.props.value
189
190
  ? rrulestr(props.value, {
@@ -201,7 +202,11 @@ class RecurrenceWidget extends Component {
201
202
  open: false,
202
203
  rruleSet: rruleSet,
203
204
  formValues: this.getFormValues(rruleSet),
204
- RRULE_LANGUAGE: rrulei18n(this.props.intl, this.moment, this.props.lang),
205
+ RRULE_LANGUAGE: rrulei18n(
206
+ this.props.intl,
207
+ this.moment,
208
+ toBackendLang(this.props.lang),
209
+ ),
205
210
  };
206
211
  }
207
212
 
@@ -8,6 +8,7 @@ import { map } from 'lodash';
8
8
  import { Days } from './Utils';
9
9
  import SelectInput from './SelectInput';
10
10
  import { Form } from 'semantic-ui-react';
11
+ import { toBackendLang } from '@plone/volto/helpers';
11
12
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
12
13
  import { useSelector } from 'react-redux';
13
14
 
@@ -22,7 +23,7 @@ const WeekdayOfTheMonthField = (props) => {
22
23
  const lang = useSelector((state) => state.intl.locale);
23
24
 
24
25
  const moment = momentlib.default;
25
- moment.locale(lang);
26
+ moment.locale(toBackendLang(lang));
26
27
 
27
28
  const weekdayOfTheMonthList = [
28
29
  ...map(Object.keys(Days), (d) => ({
@@ -9,7 +9,6 @@ import { map } from 'lodash';
9
9
  import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
10
10
  import { useSelector, shallowEqual } from 'react-redux';
11
11
  import { UniversalLink } from '@plone/volto/components';
12
- import config from '@plone/volto/registry';
13
12
  import { flattenToAppURL, addAppURL } from '@plone/volto/helpers';
14
13
 
15
14
  const messages = defineMessages({
@@ -26,10 +25,8 @@ const messages = defineMessages({
26
25
  * @returns {string} Markup of the component
27
26
  */
28
27
  const Footer = ({ intl }) => {
29
- const { settings } = config;
30
- const { lang, siteActions = [] } = useSelector(
28
+ const { siteActions = [] } = useSelector(
31
29
  (state) => ({
32
- lang: state.intl.locale,
33
30
  siteActions: state.actions?.actions?.site_actions,
34
31
  }),
35
32
  shallowEqual,
@@ -97,15 +94,7 @@ const Footer = ({ intl }) => {
97
94
  <UniversalLink
98
95
  className="item"
99
96
  href={
100
- settings.isMultilingual
101
- ? `/${lang}/${
102
- item.url
103
- ? flattenToAppURL(item.url)
104
- : addAppURL(item.id)
105
- }`
106
- : item.url
107
- ? flattenToAppURL(item.url)
108
- : addAppURL(item.id)
97
+ item.url ? flattenToAppURL(item.url) : addAppURL(item.id)
109
98
  }
110
99
  >
111
100
  {item?.title}
@@ -11,7 +11,12 @@ import { useSelector } from 'react-redux';
11
11
  import cx from 'classnames';
12
12
  import { find, map } from 'lodash';
13
13
 
14
- import { Helmet, langmap, flattenToAppURL } from '@plone/volto/helpers';
14
+ import {
15
+ Helmet,
16
+ langmap,
17
+ flattenToAppURL,
18
+ toReactIntlLang,
19
+ } from '@plone/volto/helpers';
15
20
 
16
21
  import config from '@plone/volto/registry';
17
22
 
@@ -42,7 +47,7 @@ const LanguageSelector = (props) => {
42
47
  aria-label={`${intl.formatMessage(
43
48
  messages.switchLanguageTo,
44
49
  )} ${langmap[lang].nativeName.toLowerCase()}`}
45
- className={cx({ selected: lang === currentLang })}
50
+ className={cx({ selected: toReactIntlLang(lang) === currentLang })}
46
51
  to={translation ? flattenToAppURL(translation['@id']) : `/${lang}`}
47
52
  title={langmap[lang].nativeName}
48
53
  onClick={() => {
@@ -57,7 +62,7 @@ const LanguageSelector = (props) => {
57
62
  </div>
58
63
  ) : (
59
64
  <Helmet>
60
- <html lang={settings.defaultLanguage} />
65
+ <html lang={toReactIntlLang(settings.defaultLanguage)} />
61
66
  </Helmet>
62
67
  );
63
68
  };
@@ -239,6 +239,7 @@ class Login extends Component {
239
239
  <Input
240
240
  type="password"
241
241
  id="password"
242
+ autocomplete="current-password"
242
243
  name="password"
243
244
  placeholder={this.props.intl.formatMessage(
244
245
  messages.password,
@@ -8,6 +8,7 @@ import { Image } from 'semantic-ui-react';
8
8
  import { useSelector } from 'react-redux';
9
9
  import config from '@plone/volto/registry';
10
10
  import { UniversalLink } from '@plone/volto/components';
11
+ import { toBackendLang } from '@plone/volto/helpers';
11
12
  import LogoImage from '@plone/volto/components/theme/Logo/Logo.svg';
12
13
 
13
14
  const messages = defineMessages({
@@ -34,7 +35,7 @@ const Logo = () => {
34
35
 
35
36
  return (
36
37
  <UniversalLink
37
- href={settings.isMultilingual ? `/${lang}` : '/'}
38
+ href={settings.isMultilingual ? `/${toBackendLang(lang)}` : '/'}
38
39
  title={intl.formatMessage(messages.site)}
39
40
  >
40
41
  <Image
@@ -4,7 +4,7 @@ import { useDispatch } from 'react-redux';
4
4
  import { useCookies } from 'react-cookie';
5
5
  import config from '@plone/volto/registry';
6
6
  import { changeLanguage } from '@plone/volto/actions';
7
- import { normalizeLanguageName } from '@plone/volto/helpers';
7
+ import { toGettextLang } from '@plone/volto/helpers';
8
8
 
9
9
  const MultilingualRedirector = (props) => {
10
10
  const { settings } = config;
@@ -23,7 +23,7 @@ const MultilingualRedirector = (props) => {
23
23
  // const detectedLang = (navigator.language || navigator.userLanguage).substring(0, 2);
24
24
  let mounted = true;
25
25
  if (settings.isMultilingual && pathname === '/') {
26
- const langFileName = normalizeLanguageName(redirectToLanguage);
26
+ const langFileName = toGettextLang(redirectToLanguage);
27
27
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
28
28
  if (mounted) {
29
29
  dispatch(changeLanguage(redirectToLanguage, locale.default));
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { NavLink } from 'react-router-dom';
3
- import { isInternalURL } from '@plone/volto/helpers';
3
+ import { isInternalURL, toBackendLang } from '@plone/volto/helpers';
4
4
  import config from '@plone/volto/registry';
5
5
 
6
6
  const NavItem = ({ item, lang }) => {
@@ -15,7 +15,9 @@ const NavItem = ({ item, lang }) => {
15
15
  className="item"
16
16
  activeClassName="active"
17
17
  exact={
18
- settings.isMultilingual ? item.url === `/${lang}` : item.url === ''
18
+ settings.isMultilingual
19
+ ? item.url === `/${toBackendLang(lang)}`
20
+ : item.url === ''
19
21
  }
20
22
  >
21
23
  {item.title}
@@ -10,7 +10,7 @@ import { connect } from 'react-redux';
10
10
  import { asyncConnect } from '@plone/volto/helpers';
11
11
  import { defineMessages, injectIntl } from 'react-intl';
12
12
  import { Container } from 'semantic-ui-react';
13
- import { Helmet } from '@plone/volto/helpers';
13
+ import { Helmet, toBackendLang } from '@plone/volto/helpers';
14
14
  import { Link } from 'react-router-dom';
15
15
  import config from '@plone/volto/registry';
16
16
 
@@ -40,7 +40,7 @@ class Sitemap extends Component {
40
40
  componentDidMount() {
41
41
  const { settings } = config;
42
42
  if (settings.isMultilingual) {
43
- this.props.getNavigation(`${this.props.lang}`, 4);
43
+ this.props.getNavigation(`${toBackendLang(this.props.lang)}`, 4);
44
44
  } else {
45
45
  this.props.getNavigation('', 4);
46
46
  }
@@ -108,7 +108,9 @@ export default compose(
108
108
  const { settings } = config;
109
109
  const lang = getState().intl.locale;
110
110
  if (settings.isMultilingual) {
111
- return __SERVER__ && dispatch(getNavigation(`${lang}`, 4));
111
+ return (
112
+ __SERVER__ && dispatch(getNavigation(`${toBackendLang(lang)}`, 4))
113
+ );
112
114
  } else {
113
115
  return __SERVER__ && dispatch(getNavigation('', 4));
114
116
  }
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import { List } from 'semantic-ui-react';
4
4
  import cx from 'classnames';
5
5
 
6
+ import { toBackendLang } from '@plone/volto/helpers';
6
7
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
7
8
  import { useSelector } from 'react-redux';
8
9
 
@@ -28,7 +29,7 @@ const When_ = ({ start, end, whole_day, open_end, moment: momentlib }) => {
28
29
  const lang = useSelector((state) => state.intl.locale);
29
30
 
30
31
  const moment = momentlib.default;
31
- moment.locale(lang);
32
+ moment.locale(toBackendLang(lang));
32
33
 
33
34
  const datesInfo = datesForDisplay(start, end, moment);
34
35
  if (!datesInfo) {
@@ -2,10 +2,11 @@ import React from 'react';
2
2
  import cx from 'classnames';
3
3
  import moment from 'moment';
4
4
  import { useSelector } from 'react-redux';
5
+ import { toBackendLang } from '@plone/volto/helpers';
5
6
 
6
7
  const DateWidget = ({ value, children, className, format = 'll' }) => {
7
8
  const lang = useSelector((state) => state.intl.locale);
8
- moment.locale(lang);
9
+ moment.locale(toBackendLang(lang));
9
10
  return value ? (
10
11
  <span className={cx(className, 'date', 'widget')}>
11
12
  {children
@@ -2,10 +2,11 @@ import React from 'react';
2
2
  import cx from 'classnames';
3
3
  import moment from 'moment';
4
4
  import { useSelector } from 'react-redux';
5
+ import { toBackendLang } from '@plone/volto/helpers';
5
6
 
6
7
  const DatetimeWidget = ({ value, children, className, format = 'lll' }) => {
7
8
  const lang = useSelector((state) => state.intl.locale);
8
- moment.locale(lang);
9
+ moment.locale(toBackendLang(lang));
9
10
  return value ? (
10
11
  <span className={cx(className, 'datetime', 'widget')}>
11
12
  {children
@@ -280,14 +280,14 @@ export function isTelephone(text) {
280
280
  }
281
281
 
282
282
  export function normaliseMail(email) {
283
- if (email.toLowerCase().startsWith('mailto:')) {
283
+ if (email?.toLowerCase()?.startsWith('mailto:')) {
284
284
  return email;
285
285
  }
286
286
  return `mailto:${email}`;
287
287
  }
288
288
 
289
289
  export function normalizeTelephone(tel) {
290
- if (tel.toLowerCase().startsWith('tel:')) {
290
+ if (tel?.toLowerCase()?.startsWith('tel:')) {
291
291
  return tel;
292
292
  }
293
293
  return `tel:${tel}`;
@@ -310,12 +310,17 @@ export function checkAndNormalizeUrl(url) {
310
310
  res.url = URLUtils.normalizeTelephone(url);
311
311
  } else {
312
312
  //url
313
- if (!res.url.startsWith('/') && !res.url.startsWith('#')) {
313
+ if (
314
+ res.url?.length >= 0 &&
315
+ !res.url.startsWith('/') &&
316
+ !res.url.startsWith('#')
317
+ ) {
314
318
  res.url = URLUtils.normalizeUrl(url);
315
319
  if (!URLUtils.isUrl(res.url)) {
316
320
  res.isValid = false;
317
321
  }
318
322
  }
323
+ if (res.url === undefined || res.url === null) res.isValid = false;
319
324
  }
320
325
  return res;
321
326
  }
@@ -14,6 +14,9 @@ import {
14
14
  removeProtocol,
15
15
  addAppURL,
16
16
  expandToBackendURL,
17
+ checkAndNormalizeUrl,
18
+ normaliseMail,
19
+ normalizeTelephone,
17
20
  } from './Url';
18
21
 
19
22
  beforeEach(() => {
@@ -61,6 +64,17 @@ describe('Url', () => {
61
64
  it('return empty string if no url is empty string', () => {
62
65
  expect(getBaseUrl('')).toBe('');
63
66
  });
67
+ it('return a null/undefined mailto adress ', () => {
68
+ expect(normaliseMail(null)).toBe('mailto:null');
69
+ expect(normaliseMail(undefined)).toBe('mailto:undefined');
70
+ });
71
+ it('return a null/undefined telephone number', () => {
72
+ expect(normalizeTelephone(null)).toBe('tel:null');
73
+ expect(normalizeTelephone(undefined)).toBe('tel:undefined');
74
+ });
75
+ it('null returns an invalid link', () => {
76
+ expect(checkAndNormalizeUrl(null).isValid).toBe(false);
77
+ });
64
78
  });
65
79
 
66
80
  describe('getView', () => {
@@ -174,13 +174,13 @@ export const parseDateTime = (locale, value, format, moment) => {
174
174
  };
175
175
 
176
176
  /**
177
- * Converts a language code to the format `lang_region`
177
+ * Converts a language code like pt-br to the format `pt_BR` (`lang_region`)
178
178
  * Useful for passing from Plone's i18n lang names to Xnix locale names
179
- * eg. LC_MESSAGES/lang_region.po filenames
179
+ * eg. LC_MESSAGES/lang_region.po filenames. Also used in the I18N_LANGUAGE cookie.
180
180
  * @param {string} language Language to be converted
181
181
  * @returns {string} Language converted
182
182
  */
183
- export const normalizeLanguageName = (language) => {
183
+ export const toGettextLang = (language) => {
184
184
  if (language.includes('-')) {
185
185
  let normalizedLang = language.split('-');
186
186
  normalizedLang = `${normalizedLang[0]}_${normalizedLang[1].toUpperCase()}`;
@@ -189,23 +189,35 @@ export const normalizeLanguageName = (language) => {
189
189
 
190
190
  return language;
191
191
  };
192
+ export const normalizeLanguageName = toGettextLang;
192
193
 
193
194
  /**
194
- * Converts a language code to the format `lang-region`
195
- * `react-intl` only supports this syntax, so coming from the language
196
- * negotiation of the `locale` lib, one need to convert it first
195
+ * Converts a language code like pt-br or pt_BR to the format `pt-BR`.
196
+ * `react-intl` only supports this syntax. We also use it for the locales
197
+ * in the volto Redux store.
197
198
  * @param {string} language Language to be converted
198
199
  * @returns {string} Language converted
199
200
  */
200
- export const toLangUnderscoreRegion = (language) => {
201
- if (language.includes('_')) {
202
- let langCode = language.split('_');
201
+ export const toReactIntlLang = (language) => {
202
+ if (language.includes('_') || language.includes('-')) {
203
+ let langCode = language.split(/[-_]/);
203
204
  langCode = `${langCode[0]}-${langCode[1].toUpperCase()}`;
204
205
  return langCode;
205
206
  }
206
207
 
207
208
  return language;
208
209
  };
210
+ export const toLangUnderscoreRegion = toReactIntlLang; // old name for backwards-compat
211
+
212
+ /**
213
+ * Converts a language code like pt_BR or pt-BR to the format `pt-br`.
214
+ * This format is used on the backend and in volto config settings.
215
+ * @param {string} language Language to be converted
216
+ * @returns {string} Language converted
217
+ */
218
+ export const toBackendLang = (language) => {
219
+ return toReactIntlLang(language).toLowerCase();
220
+ };
209
221
 
210
222
  /**
211
223
  * Lookup if a given expander is set in apiExpanders for the given path and action type
@@ -6,7 +6,7 @@ import {
6
6
  getColor,
7
7
  getInitials,
8
8
  hasApiExpander,
9
- normalizeLanguageName,
9
+ toGettextLang,
10
10
  parseDateTime,
11
11
  removeFromArray,
12
12
  reorderArray,
@@ -284,12 +284,12 @@ describe('Utils tests', () => {
284
284
  });
285
285
  });
286
286
 
287
- describe('normalizeLanguageName', () => {
287
+ describe('toGettextLang', () => {
288
288
  it('Normalizes an extended language (pt_BR)', () => {
289
- expect(normalizeLanguageName('pt-br')).toStrictEqual('pt_BR');
289
+ expect(toGettextLang('pt-br')).toStrictEqual('pt_BR');
290
290
  });
291
291
  it('Normalizes a simple language (ca)', () => {
292
- expect(normalizeLanguageName('ca')).toStrictEqual('ca');
292
+ expect(toGettextLang('ca')).toStrictEqual('ca');
293
293
  });
294
294
  });
295
295
 
@@ -81,8 +81,11 @@ export {
81
81
  applyConfig,
82
82
  withServerErrorCode,
83
83
  parseDateTime,
84
- normalizeLanguageName,
85
- toLangUnderscoreRegion,
84
+ toGettextLang,
85
+ normalizeLanguageName, // old name for toGettextLang
86
+ toReactIntlLang,
87
+ toLangUnderscoreRegion, // old name for toReactIntlLang
88
+ toBackendLang,
86
89
  hasApiExpander,
87
90
  replaceItemOfArray,
88
91
  cloneDeepSchema,
@@ -18,7 +18,11 @@ import {
18
18
  SET_APIERROR,
19
19
  } from '@plone/volto/constants/ActionTypes';
20
20
  import { changeLanguage } from '@plone/volto/actions';
21
- import { normalizeLanguageName, getCookieOptions } from '@plone/volto/helpers';
21
+ import {
22
+ toGettextLang,
23
+ toReactIntlLang,
24
+ getCookieOptions,
25
+ } from '@plone/volto/helpers';
22
26
  let socket = null;
23
27
 
24
28
  /**
@@ -205,11 +209,11 @@ const apiMiddlewareFactory = (api) => ({ dispatch, getState }) => (next) => (
205
209
  const lang = result?.language?.token;
206
210
  if (
207
211
  lang &&
208
- getState().intl.language !== lang &&
212
+ getState().intl.locale !== toReactIntlLang(lang) &&
209
213
  !subrequest &&
210
214
  config.settings.supportedLanguages.includes(lang)
211
215
  ) {
212
- const langFileName = normalizeLanguageName(lang);
216
+ const langFileName = toGettextLang(lang);
213
217
  import('~/../locales/' + langFileName + '.json').then((locale) => {
214
218
  dispatch(changeLanguage(lang, locale.default));
215
219
  });
package/src/server.jsx CHANGED
@@ -26,8 +26,9 @@ import {
26
26
  Html,
27
27
  Api,
28
28
  persistAuthToken,
29
- normalizeLanguageName,
30
- toLangUnderscoreRegion,
29
+ toBackendLang,
30
+ toGettextLang,
31
+ toReactIntlLang,
31
32
  } from '@plone/volto/helpers';
32
33
  import { changeLanguage } from '@plone/volto/actions';
33
34
 
@@ -44,9 +45,9 @@ let locales = {};
44
45
 
45
46
  if (config.settings) {
46
47
  config.settings.supportedLanguages.forEach((lang) => {
47
- const langFileName = normalizeLanguageName(lang);
48
+ const langFileName = toGettextLang(lang);
48
49
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
49
- locales = { ...locales, [lang]: locale.default };
50
+ locales = { ...locales, [toReactIntlLang(lang)]: locale.default };
50
51
  });
51
52
  });
52
53
  }
@@ -101,13 +102,15 @@ server.use(function (err, req, res, next) {
101
102
  function setupServer(req, res, next) {
102
103
  const api = new Api(req);
103
104
 
104
- const lang = new locale.Locales(
105
- req.universalCookies.get('I18N_LANGUAGE') ||
106
- config.settings.defaultLanguage ||
107
- req.headers['accept-language'],
108
- )
109
- .best(supported)
110
- .toString();
105
+ const lang = toReactIntlLang(
106
+ new locale.Locales(
107
+ req.universalCookies.get('I18N_LANGUAGE') ||
108
+ config.settings.defaultLanguage ||
109
+ req.headers['accept-language'],
110
+ )
111
+ .best(supported)
112
+ .toString(),
113
+ );
111
114
 
112
115
  // Minimum initial state for the fake Redux store instance
113
116
  const initialState = {
@@ -176,13 +179,15 @@ server.get('/*', (req, res) => {
176
179
 
177
180
  const browserdetect = detect(req.headers['user-agent']);
178
181
 
179
- const lang = new locale.Locales(
180
- req.universalCookies.get('I18N_LANGUAGE') ||
181
- config.settings.defaultLanguage ||
182
- req.headers['accept-language'],
183
- )
184
- .best(supported)
185
- .toString();
182
+ const lang = toReactIntlLang(
183
+ new locale.Locales(
184
+ req.universalCookies.get('I18N_LANGUAGE') ||
185
+ config.settings.defaultLanguage ||
186
+ req.headers['accept-language'],
187
+ )
188
+ .best(supported)
189
+ .toString(),
190
+ );
186
191
 
187
192
  const authToken = req.universalCookies.get('auth_token');
188
193
  const initialState = {
@@ -217,7 +222,7 @@ server.get('/*', (req, res) => {
217
222
 
218
223
  loadOnServer({ store, location, routes, api })
219
224
  .then(() => {
220
- const cookie_lang =
225
+ const initialLang =
221
226
  req.universalCookies.get('I18N_LANGUAGE') ||
222
227
  config.settings.defaultLanguage ||
223
228
  req.headers['accept-language'];
@@ -230,15 +235,15 @@ server.get('/*', (req, res) => {
230
235
  // present the language token field, for some reason. In this case, we
231
236
  // should follow the cookie rather then switching the language
232
237
  const contentLang = store.getState().content.get?.error
233
- ? cookie_lang
238
+ ? initialLang
234
239
  : store.getState().content.data?.language?.token ||
235
240
  config.settings.defaultLanguage;
236
241
 
237
- if (cookie_lang !== contentLang) {
238
- const newLocale = toLangUnderscoreRegion(
242
+ if (toBackendLang(initialLang) !== contentLang) {
243
+ const newLang = toReactIntlLang(
239
244
  new locale.Locales(contentLang).best(supported).toString(),
240
245
  );
241
- store.dispatch(changeLanguage(newLocale, locales[newLocale], req));
246
+ store.dispatch(changeLanguage(newLang, locales[newLang], req));
242
247
  }
243
248
 
244
249
  const context = {};
@@ -489,3 +489,7 @@
489
489
  margin-left: 5px;
490
490
  }
491
491
  }
492
+
493
+ .formtabs {
494
+ flex-wrap: wrap;
495
+ }