@plone/volto 17.0.0-alpha.6 → 17.0.0-alpha.7

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 (47) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +1 -1
  3. package/packages/volto-slate/package.json +1 -1
  4. package/src/actions/language/language.js +8 -8
  5. package/src/components/manage/Add/Add.jsx +2 -2
  6. package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
  7. package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
  8. package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
  9. package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
  10. package/src/components/manage/Toolbar/Types.jsx +2 -2
  11. package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
  12. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
  13. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
  14. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
  15. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +7 -2
  16. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
  17. package/src/components/theme/Footer/Footer.jsx +2 -13
  18. package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
  19. package/src/components/theme/Logo/Logo.jsx +2 -1
  20. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
  21. package/src/components/theme/Navigation/NavItem.jsx +4 -2
  22. package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
  23. package/src/components/theme/View/EventDatesInfo.jsx +2 -1
  24. package/src/components/theme/Widgets/DateWidget.jsx +2 -1
  25. package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
  26. package/src/helpers/Utils/Utils.js +21 -9
  27. package/src/helpers/Utils/Utils.test.js +4 -4
  28. package/src/helpers/index.js +5 -2
  29. package/src/middleware/api.js +7 -3
  30. package/src/server.jsx +28 -23
  31. package/.changelog.draft +0 -33
  32. package/.editorconfig +0 -36
  33. package/.storybook/main.js +0 -127
  34. package/.storybook/manager.js +0 -15
  35. package/.storybook/preview.js +0 -21
  36. package/.storybook/static/previewImage.svg +0 -48
  37. package/.vale.ini +0 -10
  38. package/.yarnrc.yml +0 -5
  39. package/jsdoc.json +0 -16
  40. package/netlify.toml +0 -5
  41. package/pyvenv.cfg +0 -3
  42. package/share/man/man1/ttx.1 +0 -225
  43. package/styles/Vocab/Base/accept.txt +0 -0
  44. package/styles/Vocab/Base/reject.txt +0 -0
  45. package/styles/Vocab/Plone/accept.txt +0 -10
  46. package/styles/Vocab/Plone/reject.txt +0 -5
  47. package/towncrier.toml +0 -33
package/CHANGELOG.md CHANGED
@@ -8,6 +8,13 @@
8
8
 
9
9
  <!-- towncrier release notes start -->
10
10
 
11
+ ## 17.0.0-alpha.7 (2023-05-11)
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
+
11
18
  ## 17.0.0-alpha.6 (2023-05-11)
12
19
 
13
20
  ### Feature
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "17.0.0-alpha.6",
12
+ "version": "17.0.0-alpha.7",
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": "17.0.0-alpha.6",
3
+ "version": "17.0.0-alpha.7",
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
  };
@@ -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
@@ -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
 
@@ -80,8 +80,11 @@ export {
80
80
  applyConfig,
81
81
  withServerErrorCode,
82
82
  parseDateTime,
83
- normalizeLanguageName,
84
- toLangUnderscoreRegion,
83
+ toGettextLang,
84
+ normalizeLanguageName, // old name for toGettextLang
85
+ toReactIntlLang,
86
+ toLangUnderscoreRegion, // old name for toReactIntlLang
87
+ toBackendLang,
85
88
  hasApiExpander,
86
89
  replaceItemOfArray,
87
90
  cloneDeepSchema,