Merge branch 'core/admin-apis' of github.com:strapi/strapi into core/menu-api

This commit is contained in:
soupette 2021-06-16 11:45:14 +02:00
commit c68f815a51
72 changed files with 1125 additions and 880 deletions

View File

@ -0,0 +1,15 @@
'use strict';
module.exports = {
webpack: (config, webpack) => {
// Note: we provide webpack above so you should not `require` it
// Perform customizations to webpack config
// Important: return the modified config
return config;
},
app: config => {
config.locales = ['fr'];
return config;
},
};

View File

@ -1,3 +1,4 @@
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
@ -15,8 +16,6 @@ export default {
isRequired: pluginPkg.strapi.required || false,
mainComponent: () => 'My plugin',
name,
settings: null,
trads: {},
menu: {
pluginsSectionLinks: [
{
@ -34,4 +33,27 @@ export default {
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "[pluginId]-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -0,0 +1,9 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import getTrad from '../../utils/getTrad';
const App = () => {
return <FormattedMessage id={getTrad('plugin.name')} defaultMessage="My plugin" />;
};
export default App;

View File

@ -0,0 +1,3 @@
{
"plugin.name": "My plugin"
}

View File

@ -0,0 +1,5 @@
import pluginId from '../pluginId';
const getTrad = id => `${pluginId}.${id}`;
export default getTrad;

View File

@ -54,7 +54,10 @@ module.exports = {
],
moduleNameMapper,
rootDir: process.cwd(),
setupFiles: ['<rootDir>/test/config/front/test-bundler.js'],
setupFiles: [
'<rootDir>/test/config/front/test-bundler.js',
'<rootDir>/packages/admin-test-utils/lib/mocks/LocalStorageMock.js',
],
testPathIgnorePatterns: [
'/node_modules/',
'<rootDir>/examples/getstarted/',

View File

@ -4,7 +4,6 @@
const { combineReducers, createStore } = require('redux');
const reducers = {
language: jest.fn(() => ({ locale: 'en' })),
menu: jest.fn(() => ({
generalSectionLinks: [
{

View File

@ -0,0 +1,25 @@
'use strict';
class LocalStorageMock {
constructor() {
this.store = {};
}
clear() {
this.store = {};
}
getItem(key) {
return this.store[key] || null;
}
setItem(key, value) {
this.store[key] = String(value);
}
removeItem(key) {
delete this.store[key];
}
}
global.localStorage = new LocalStorageMock();

View File

@ -4,6 +4,7 @@ import { BrowserRouter } from 'react-router-dom';
import { QueryClientProvider, QueryClient } from 'react-query';
import { ThemeProvider } from 'styled-components';
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
import pick from 'lodash/pick';
import createHook from '@strapi/hooks';
import invariant from 'invariant';
import configureStore from './core/store/configureStore';
@ -17,9 +18,7 @@ import Fonts from './components/Fonts';
import GlobalStyle from './components/GlobalStyle';
import Notifications from './components/Notifications';
import themes from './themes';
// TODO
import translations from './translations';
import languageNativeNames from './translations/languageNativeNames';
window.strapi = {
backendURL: process.env.STRAPI_ADMIN_BACKEND_URL,
@ -33,16 +32,15 @@ const queryClient = new QueryClient({
},
});
const appLocales = Object.keys(translations);
class StrapiApp {
constructor({ appPlugins, library, middlewares, reducers }) {
constructor({ appPlugins, library, locales, middlewares, reducers }) {
this.appLocales = ['en', ...locales.filter(loc => loc !== 'en')];
this.appPlugins = appPlugins || {};
this.library = library;
this.middlewares = middlewares;
this.plugins = {};
this.reducers = reducers;
this.translations = translations;
this.translations = {};
this.hooksDict = {};
this.menu = [];
this.settings = {
@ -186,20 +184,51 @@ class StrapiApp {
return this.plugins[pluginId];
};
// FIXME
registerPluginTranslations(pluginId, trads) {
const pluginTranslations = appLocales.reduce((acc, currentLanguage) => {
const currentLocale = trads[currentLanguage];
async loadAdminTrads() {
const arrayOfPromises = this.appLocales.map(locale => {
return import(/* webpackChunkName: "[request]" */ `./translations/${locale}.json`)
.then(({ default: data }) => {
return { data, locale };
})
.catch(() => {
return { data: {}, locale };
});
});
const adminLocales = await Promise.all(arrayOfPromises);
if (currentLocale) {
const localeprefixedWithPluginId = Object.keys(currentLocale).reduce((acc2, current) => {
acc2[`${pluginId}.${current}`] = currentLocale[current];
this.translations = adminLocales.reduce((acc, current) => {
acc[current.locale] = current.data;
return acc2;
}, {});
return acc;
}, {});
acc[currentLanguage] = localeprefixedWithPluginId;
}
return Promise.resolve();
}
async loadTrads() {
const arrayOfPromises = Object.keys(this.appPlugins)
.map(plugin => {
const registerTrads = this.appPlugins[plugin].registerTrads;
if (registerTrads) {
return registerTrads({ locales: this.appLocales });
}
return null;
})
.filter(a => a);
const pluginsTrads = await Promise.all(arrayOfPromises);
const mergedTrads = pluginsTrads.reduce((acc, currentPluginTrads) => {
const pluginTrads = currentPluginTrads.reduce((acc1, current) => {
acc1[current.locale] = current.data;
return acc1;
}, {});
Object.keys(pluginTrads).forEach(locale => {
acc[locale] = { ...acc[locale], ...pluginTrads[locale] };
});
return acc;
}, {});
@ -207,19 +236,16 @@ class StrapiApp {
this.translations = Object.keys(this.translations).reduce((acc, current) => {
acc[current] = {
...this.translations[current],
...(pluginTranslations[current] || {}),
...(mergedTrads[current] || {}),
};
return acc;
}, {});
return Promise.resolve();
}
registerPlugin = pluginConf => {
// FIXME
// Translations should be loaded differently
// This is a temporary fix
this.registerPluginTranslations(pluginConf.id, pluginConf.trads);
const plugin = Plugin(pluginConf);
this.plugins[plugin.pluginId] = plugin;
@ -245,6 +271,7 @@ class StrapiApp {
render() {
const store = this.createStore();
const localeNames = pick(languageNativeNames, this.appLocales);
const {
components: { components },
@ -266,7 +293,7 @@ class StrapiApp {
settings={this.settings}
>
<LibraryProvider components={components} fields={fields}>
<LanguageProvider messages={this.translations}>
<LanguageProvider messages={this.translations} localeNames={localeNames}>
<AutoReloadOverlayBlockerProvider>
<OverlayBlocker>
<Notifications>
@ -286,5 +313,5 @@ class StrapiApp {
}
}
export default ({ appPlugins, library, middlewares, reducers }) =>
new StrapiApp({ appPlugins, library, middlewares, reducers });
export default ({ appPlugins, library, locales, middlewares, reducers }) =>
new StrapiApp({ appPlugins, library, locales, middlewares, reducers });

View File

@ -0,0 +1,7 @@
module.exports = {
app: config => {
config.locales = ['fr'];
return config;
},
};

View File

@ -1,16 +0,0 @@
/*
*
* LanguageProvider actions
*
*/
/* eslint-disable */
import { CHANGE_LOCALE } from './constants';
export function changeLocale(languageLocale) {
return {
type: CHANGE_LOCALE,
locale: languageLocale,
};
}

View File

@ -1,8 +0,0 @@
/*
*
* LanguageProvider constants
*
*/
/* eslint-disable */
export const CHANGE_LOCALE = 'app/LanguageToggle/CHANGE_LOCALE';

View File

@ -1,14 +0,0 @@
import { useDispatch } from 'react-redux';
import { changeLocale } from '../actions';
const useChangeLanguage = () => {
const dispatch = useDispatch();
const changeLanguage = nextLocale => {
dispatch(changeLocale(nextLocale));
};
return changeLanguage;
};
export default useChangeLanguage;

View File

@ -6,44 +6,45 @@
* IntlProvider component and i18n messages (loaded from `app/translations`)
*/
import React from 'react';
import React, { useEffect, useReducer } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { IntlProvider } from 'react-intl';
import { defaultsDeep } from 'lodash';
import { selectLocale } from './selectors';
import defaultsDeep from 'lodash/defaultsDeep';
import LocalesProvider from '../LocalesProvider';
import localStorageKey from './utils/localStorageKey';
import init from './init';
import reducer, { initialState } from './reducer';
// eslint-disable-next-line react/prefer-stateless-function
export class LanguageProvider extends React.Component {
render() {
const messages = defaultsDeep(this.props.messages[this.props.locale], this.props.messages.en);
const LanguageProvider = ({ children, localeNames, messages }) => {
const [{ locale }, dispatch] = useReducer(reducer, initialState, () => init(localeNames));
return (
<IntlProvider
locale={this.props.locale}
defaultLocale="en"
messages={messages}
textComponent="span"
>
{React.Children.only(this.props.children)}
</IntlProvider>
);
}
}
useEffect(() => {
// Set user language in local storage.
window.localStorage.setItem(localStorageKey, locale);
}, [locale]);
const changeLocale = locale => {
dispatch({
type: 'CHANGE_LOCALE',
locale,
});
};
const appMessages = defaultsDeep(messages[locale], messages.en);
return (
<IntlProvider locale={locale} defaultLocale="en" messages={appMessages} textComponent="span">
<LocalesProvider changeLocale={changeLocale} localeNames={localeNames} messages={appMessages}>
{children}
</LocalesProvider>
</IntlProvider>
);
};
LanguageProvider.propTypes = {
children: PropTypes.element.isRequired,
locale: PropTypes.string.isRequired,
localeNames: PropTypes.objectOf(PropTypes.string).isRequired,
messages: PropTypes.object.isRequired,
};
const mapStateToProps = createSelector(selectLocale(), locale => ({ locale }));
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
export default connect(mapStateToProps, mapDispatchToProps)(LanguageProvider);
export default LanguageProvider;

View File

@ -0,0 +1,13 @@
import localStorageKey from './utils/localStorageKey';
const init = localeNames => {
const languageFromLocaleStorage = window.localStorage.getItem(localStorageKey);
const appLanguage = localeNames[languageFromLocaleStorage] ? languageFromLocaleStorage : 'en';
return {
locale: appLanguage,
localeNames,
};
};
export default init;

View File

@ -4,47 +4,27 @@
*
*/
import { get, includes, split } from 'lodash';
// Import supported languages from the translations folder
import trads from '../../translations';
import { CHANGE_LOCALE } from './constants';
const languages = Object.keys(trads);
// Define a key to store and get user preferences in local storage.
const localStorageKey = 'strapi-admin-language';
// Detect user language.
const userLanguage =
window.localStorage.getItem(localStorageKey) ||
window.navigator.language ||
window.navigator.userLanguage;
let foundLanguage = includes(languages, userLanguage) && userLanguage;
if (!foundLanguage) {
// Split user language in a correct format.
const userLanguageShort = get(split(userLanguage, '-'), '0');
// Check that the language is included in the admin configuration.
foundLanguage = includes(languages, userLanguageShort) && userLanguageShort;
}
const initialState = {
locale: foundLanguage || 'en',
localeNames: { en: 'English' },
locale: 'en',
};
function languageProviderReducer(state = initialState, action) {
const languageProviderReducer = (state = initialState, action) => {
switch (action.type) {
case CHANGE_LOCALE:
// Set user language in local storage.
window.localStorage.setItem(localStorageKey, action.locale);
case 'CHANGE_LOCALE': {
const { locale } = action;
return { ...state, locale: action.locale };
default:
if (!state.localeNames[locale]) {
return state;
}
return { ...state, locale };
}
default: {
return state;
}
}
}
};
export default languageProviderReducer;
export { initialState };

View File

@ -1,17 +0,0 @@
import { createSelector } from 'reselect';
/**
* Direct selector to the languageToggle state domain
*/
const selectLanguage = () => state => state.language;
/**
* Select the language locale
*/
const selectLocale = () => createSelector(selectLanguage(), languageState => languageState.locale);
const makeSelectLocale = () => createSelector(selectLocale(), locale => ({ locale }));
export default makeSelectLocale;
export { selectLanguage, selectLocale };

View File

@ -0,0 +1,62 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useIntl } from 'react-intl';
import useLocalesProvider from '../../LocalesProvider/useLocalesProvider';
import LanguageProvider from '../index';
import en from '../../../translations/en.json';
import fr from '../../../translations/fr.json';
const messages = { en, fr };
const localeNames = { en: 'English', fr: 'Français' };
describe('LanguageProvider', () => {
afterEach(() => {
localStorage.removeItem('strapi-admin-language');
});
it('should not crash', () => {
const { container } = render(
<LanguageProvider messages={messages} localeNames={localeNames}>
<div>Test</div>
</LanguageProvider>
);
expect(container.firstChild).toMatchInlineSnapshot(`
<div>
Test
</div>
`);
});
it('should change the locale and set the strapi-admin-language item in the localStorage', () => {
const Test = () => {
const { locale } = useIntl();
const { changeLocale } = useLocalesProvider();
return (
<div>
<h1>{localeNames[locale]}</h1>
<button type="button" onClick={() => changeLocale('fr')}>
CHANGE
</button>
</div>
);
};
render(
<LanguageProvider messages={messages} localeNames={localeNames}>
<Test />
</LanguageProvider>
);
expect(localStorage.getItem('strapi-admin-language')).toEqual('en');
expect(screen.getByText('English')).toBeInTheDocument();
userEvent.click(screen.getByText('CHANGE'));
expect(screen.getByText('Français')).toBeInTheDocument();
expect(localStorage.getItem('strapi-admin-language')).toEqual('fr');
});
});

View File

@ -0,0 +1,34 @@
import init from '../init';
const localeNames = { en: 'English', fr: 'Français' };
describe('LanguageProvider | init', () => {
afterEach(() => {
localStorage.removeItem('strapi-admin-language');
});
it('should return the language from the localStorage', () => {
localStorage.setItem('strapi-admin-language', 'fr');
expect(init(localeNames)).toEqual({
locale: 'fr',
localeNames,
});
});
it('should return "en" when the strapi-admin-language is not set in the locale storage', () => {
expect(init(localeNames)).toEqual({
locale: 'en',
localeNames,
});
});
it('should return "en" when the language from the local storage is not included in the localeNames', () => {
localStorage.setItem('strapi-admin-language', 'foo');
expect(init(localeNames)).toEqual({
locale: 'en',
localeNames,
});
});
});

View File

@ -0,0 +1,41 @@
import reducer, { initialState } from '../reducer';
describe('LanguageProvider | reducer', () => {
let state;
beforeEach(() => {
state = initialState;
});
it('should return the initialState', () => {
const action = { type: undefined };
expect(reducer(state, action)).toEqual(initialState);
});
it('should change the locale correctly when the locale is defined in the localeNames', () => {
state = {
localeNames: { en: 'English', fr: 'Français' },
locale: 'en',
};
const action = { type: 'CHANGE_LOCALE', locale: 'fr' };
const expected = {
localeNames: { en: 'English', fr: 'Français' },
locale: 'fr',
};
expect(reducer(state, action)).toEqual(expected);
});
it('should not change the locale when the language is not defined in the localeNames', () => {
state = {
localeNames: { en: 'English', fr: 'Français' },
locale: 'en',
};
const action = { type: 'CHANGE_LOCALE', locale: 'foo' };
expect(reducer(state, action)).toEqual(state);
});
});

View File

@ -0,0 +1,3 @@
const localStorageKey = 'strapi-admin-language';
export default localStorageKey;

View File

@ -4,76 +4,42 @@
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { bindActionCreators, compose } from 'redux';
import cn from 'classnames';
import React, { useState } from 'react';
import { useIntl } from 'react-intl';
import { ButtonDropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
import translationMessages, { languageNativeNames } from '../../translations';
import makeSelectLocale from '../LanguageProvider/selectors';
import { changeLocale } from '../LanguageProvider/actions';
import useLocalesProvider from '../LocalesProvider/useLocalesProvider';
import Wrapper from './Wrapper';
// TODO
const languages = Object.keys(translationMessages);
export class LocaleToggle extends React.Component {
// eslint-disable-line
state = { isOpen: false };
const LocaleToggle = () => {
const { changeLocale, localeNames } = useLocalesProvider();
toggle = () => this.setState(prevState => ({ isOpen: !prevState.isOpen }));
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(prev => !prev);
const { locale } = useIntl();
render() {
const {
currentLocale: { locale },
} = this.props;
return (
<Wrapper>
<ButtonDropdown isOpen={isOpen} toggle={toggle}>
<DropdownToggle className="localeDropdownContent">
<span>{localeNames[locale]}</span>
</DropdownToggle>
return (
<Wrapper>
<ButtonDropdown isOpen={this.state.isOpen} toggle={this.toggle}>
<DropdownToggle className="localeDropdownContent">
<span>{languageNativeNames[locale]}</span>
</DropdownToggle>
<DropdownMenu className="localeDropdownMenu">
{languages.map(language => (
<DropdownMenu className="localeDropdownMenu">
{Object.keys(localeNames).map(lang => {
return (
<DropdownItem
key={language}
onClick={() => this.props.changeLocale(language)}
className={cn(
'localeToggleItem',
locale === language ? 'localeToggleItemActive' : ''
)}
key={lang}
onClick={() => changeLocale(lang)}
className={`localeToggleItem ${locale === lang ? 'localeToggleItemActive' : ''}`}
>
{languageNativeNames[language]}
{localeNames[lang]}
</DropdownItem>
))}
</DropdownMenu>
</ButtonDropdown>
</Wrapper>
);
}
}
LocaleToggle.propTypes = {
changeLocale: PropTypes.func.isRequired,
currentLocale: PropTypes.object.isRequired,
);
})}
</DropdownMenu>
</ButtonDropdown>
</Wrapper>
);
};
const mapStateToProps = createStructuredSelector({
currentLocale: makeSelectLocale(),
});
export function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
changeLocale,
},
dispatch
);
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(withConnect)(LocaleToggle);
export default LocaleToggle;

View File

@ -1,61 +1,217 @@
import React from 'react';
import { shallow } from 'enzyme';
import { DropdownItem } from 'reactstrap';
import { changeLocale } from '../../LanguageProvider/actions';
import { LocaleToggle, mapDispatchToProps } from '../index';
import { render } from '@testing-library/react';
import LanguageProvider from '../../LanguageProvider';
import en from '../../../translations/en.json';
import LocaleToggle from '../index';
const messages = { en };
const localeNames = { en: 'English' };
describe('<LocaleToggle />', () => {
let props;
beforeEach(() => {
props = {
changeLocale: jest.fn(),
currentLocale: {
locale: 'en',
},
};
});
it('should not crash', () => {
shallow(<LocaleToggle {...props} />);
});
const App = (
<LanguageProvider messages={messages} localeNames={localeNames}>
<LocaleToggle />
</LanguageProvider>
);
describe('<LocaleToggle />, toggle instance', () => {
it('should update the state when called', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { toggle } = renderedComponent.instance();
const { container } = render(App);
expect(container.firstChild).toMatchInlineSnapshot(`
.c0 {
-webkit-font-smoothing: antialiased;
}
toggle();
.c0 > div {
height: 6rem;
line-height: 5.8rem;
z-index: 999;
}
expect(renderedComponent.state('isOpen')).toBe(true);
});
.c0 > div > button {
width: 100%;
padding: 0 30px;
background: transparent;
border: none;
border-radius: 0;
color: #333740;
font-weight: 500;
text-align: right;
cursor: pointer;
-webkit-transition: background 0.2s ease-out;
transition: background 0.2s ease-out;
}
it('call the toggle handle on click', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
renderedComponent.setState({ isOpen: true });
const dropDown = renderedComponent.find(DropdownItem).at(0);
dropDown.simulate('click');
.c0 > div > button:hover,
.c0 > div > button:focus,
.c0 > div > button:active {
color: #333740;
background-color: #fafafb !important;
}
expect(props.changeLocale).toHaveBeenCalled();
});
});
.c0 > div > button > i,
.c0 > div > button > svg {
margin-left: 10px;
-webkit-transition: -webkit-transform 0.3s ease-out;
-webkit-transition: transform 0.3s ease-out;
transition: transform 0.3s ease-out;
}
describe('<LocaleToggle />, mapDispatchToProps', () => {
describe('changeLocale', () => {
it('should be injected', () => {
const dispatch = jest.fn();
const result = mapDispatchToProps(dispatch);
.c0 > div > button > i[alt='true'],
.c0 > div > button > svg[alt='true'] {
-webkit-transform: rotateX(180deg);
-ms-transform: rotateX(180deg);
transform: rotateX(180deg);
}
expect(result.changeLocale).toBeDefined();
});
.c0 .localeDropdownContent {
-webkit-font-smoothing: antialiased;
}
it('should dispatch the changeLocale action when called', () => {
const dispatch = jest.fn();
const result = mapDispatchToProps(dispatch);
result.changeLocale();
.c0 .localeDropdownContent span {
color: #333740;
font-size: 13px;
font-family: Lato;
font-weight: 500;
-webkit-letter-spacing: 0.5;
-moz-letter-spacing: 0.5;
-ms-letter-spacing: 0.5;
letter-spacing: 0.5;
vertical-align: baseline;
}
expect(dispatch).toHaveBeenCalledWith(changeLocale());
});
});
.c0 .localeDropdownMenu {
min-width: 90px !important;
max-height: 162px !important;
overflow: auto !important;
margin: 0 !important;
padding: 0;
line-height: 1.8rem;
border: none !important;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
box-shadow: 0 1px 4px 0px rgba(40,42,49,0.05);
}
.c0 .localeDropdownMenu:before {
content: '';
position: absolute;
top: -3px;
left: -1px;
width: calc(100% + 1px);
height: 3px;
box-shadow: 0 1px 2px 0 rgba(40,42,49,0.16);
}
.c0 .localeDropdownMenu > button {
height: 40px;
padding: 0px 15px;
line-height: 40px;
color: #f75b1d;
font-size: 13px;
font-weight: 500;
-webkit-letter-spacing: 0.5;
-moz-letter-spacing: 0.5;
-ms-letter-spacing: 0.5;
letter-spacing: 0.5;
}
.c0 .localeDropdownMenu > button:hover,
.c0 .localeDropdownMenu > button:focus,
.c0 .localeDropdownMenu > button:active {
background-color: #fafafb !important;
border-radius: 0px;
cursor: pointer;
}
.c0 .localeDropdownMenu > button:first-child {
line-height: 50px;
margin-bottom: 4px;
}
.c0 .localeDropdownMenu > button:first-child:hover,
.c0 .localeDropdownMenu > button:first-child:active {
color: #333740;
}
.c0 .localeDropdownMenu > button:not(:first-child) {
height: 36px;
line-height: 36px;
}
.c0 .localeDropdownMenu > button:not(:first-child) > i,
.c0 .localeDropdownMenu > button:not(:first-child) > svg {
margin-left: 10px;
}
.c0 .localeDropdownMenuNotLogged {
background: transparent !important;
box-shadow: none !important;
border: 1px solid #e3e9f3 !important;
border-top: 0px !important;
}
.c0 .localeDropdownMenuNotLogged button {
padding-left: 17px;
}
.c0 .localeDropdownMenuNotLogged button:hover {
background-color: #f7f8f8 !important;
}
.c0 .localeDropdownMenuNotLogged:before {
box-shadow: none !important;
}
.c0 .localeToggleItem img {
max-height: 13.37px;
margin-left: 9px;
}
.c0 .localeToggleItem:active {
color: black;
}
.c0 .localeToggleItem:hover {
background-color: #fafafb !important;
}
.c0 .localeToggleItemActive {
color: #333740 !important;
}
<div
class="c0"
>
<div
class="btn-group"
>
<button
aria-expanded="false"
aria-haspopup="true"
class="localeDropdownContent btn btn-secondary"
type="button"
>
<span>
English
</span>
</button>
<div
aria-hidden="true"
class="localeDropdownMenu dropdown-menu"
role="menu"
tabindex="-1"
>
<button
class="localeToggleItem localeToggleItemActive dropdown-item"
role="menuitem"
tabindex="0"
type="button"
>
English
</button>
</div>
</div>
</div>
`);
});
});

View File

@ -0,0 +1,5 @@
import { createContext } from 'react';
const LocalesProviderContext = createContext();
export default LocalesProviderContext;

View File

@ -0,0 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import LocalesProviderContext from './context';
const LocalesProvider = ({ changeLocale, children, localeNames, messages }) => {
return (
<LocalesProviderContext.Provider value={{ changeLocale, localeNames, messages }}>
{children}
</LocalesProviderContext.Provider>
);
};
LocalesProvider.propTypes = {
changeLocale: PropTypes.func.isRequired,
children: PropTypes.element.isRequired,
localeNames: PropTypes.object.isRequired,
messages: PropTypes.object.isRequired,
};
export default LocalesProvider;

View File

@ -0,0 +1,23 @@
import React from 'react';
import { render } from '@testing-library/react';
import LocalesProvider from '../index';
describe('LocalesProvider', () => {
it('should not crash', () => {
const { container } = render(
<LocalesProvider
changeLocale={jest.fn()}
localeNames={{ en: 'English' }}
messages={{ en: {} }}
>
<div>Test</div>
</LocalesProvider>
);
expect(container.firstChild).toMatchInlineSnapshot(`
<div>
Test
</div>
`);
});
});

View File

@ -0,0 +1,10 @@
import { useContext } from 'react';
import LocalesProviderContext from './context';
const useLocalesProvider = () => {
const { changeLocale, localeNames, messages } = useContext(LocalesProviderContext);
return { changeLocale, localeNames, messages };
};
export default useLocalesProvider;

View File

@ -4,12 +4,12 @@ import PropTypes from 'prop-types';
import favicon from '../../favicon.png';
const PageTitle = ({ title }) => (
<Helmet title={title} link={[{ rel: 'icon', type: 'image/png', href: favicon }]} />
);
const PageTitle = ({ title }) => {
return <Helmet title={title} link={[{ rel: 'icon', type: 'image/png', href: favicon }]} />;
};
PageTitle.propTypes = {
title: PropTypes.string.isRequired,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).isRequired,
};
export default memo(PageTitle);

View File

@ -1,22 +1,37 @@
import ReactDOM from 'react-dom';
import StrapiApp from './StrapiApp';
import { Components, Fields, Middlewares, Reducers } from './core/apis';
import appCustomisations from './admin.config';
import plugins from './plugins';
import appReducers from './reducers';
const appConfig = {
locales: [],
};
const customConfig = appCustomisations.app(appConfig);
const library = {
components: Components(),
fields: Fields(),
};
const middlewares = Middlewares();
const reducers = Reducers({ appReducers });
const app = StrapiApp({ appPlugins: plugins, library, middlewares, reducers });
const app = StrapiApp({
appPlugins: plugins,
library,
locales: customConfig.locales,
middlewares,
reducers,
});
const MOUNT_NODE = document.getElementById('app');
const run = async () => {
await app.loadAdminTrads();
await app.initialize();
await app.boot();
await app.loadTrads();
ReactDOM.render(app.render(), MOUNT_NODE);
};

View File

@ -13,13 +13,14 @@ import RBACProvider from '../../../components/RBACProvider';
import Admin from '../index';
const messages = { en };
const localeNames = { en: 'English' };
const store = fixtures.store.store;
const makeApp = (history, plugins) => (
<Provider store={store}>
<StrapiAppProvider plugins={plugins}>
<LanguageProvider messages={messages}>
<LanguageProvider messages={messages} localeNames={localeNames}>
<Notifications>
<AppInfosContext.Provider
value={{ latestStrapiReleaseTag: 'v4', shouldUpdateStrapi: false }}

View File

@ -6,7 +6,7 @@ import { BaselineAlignment, auth, useNotification, useQuery } from '@strapi/help
import { Padded } from '@buffetjs/core';
import PropTypes from 'prop-types';
import forms from 'ee_else_ce/pages/AuthPage/utils/forms';
import useLocalesProvider from '../../components/LocalesProvider/useLocalesProvider';
import NavTopRightWrapper from '../../components/NavTopRightWrapper';
import PageTitle from '../../components/PageTitle';
import LocaleToggle from '../../components/LocaleToggle';
@ -14,12 +14,11 @@ import checkFormValidity from '../../utils/checkFormValidity';
import formatAPIErrors from '../../utils/formatAPIErrors';
import init from './init';
import { initialState, reducer } from './reducer';
import useChangeLanguage from '../../components/LanguageProvider/hooks/useChangeLanguage';
const AuthPage = ({ hasAdmin, setHasAdmin }) => {
const toggleNotification = useNotification();
const { push } = useHistory();
const changeLocale = useChangeLanguage();
const { changeLocale } = useLocalesProvider();
const {
params: { authType },
} = useRouteMatch('/auth/:authType');

View File

@ -108,7 +108,7 @@ const HomePage = ({ history: { push } }) => {
return (
<>
<FormattedMessage id="HomePage.helmet.title">
{title => <PageTitle title={title} />}
{title => <PageTitle title={title[0]} />}
</FormattedMessage>
<Container className="container-fluid">
<div className="row">

View File

@ -4,25 +4,22 @@ import { Padded, Text } from '@buffetjs/core';
import { Col } from 'reactstrap';
import { get } from 'lodash';
import { useIntl } from 'react-intl';
import translationMessages, { languageNativeNames } from '../../translations';
import ContainerFluid from '../../components/ContainerFluid';
import useLocalesProvider from '../../components/LocalesProvider/useLocalesProvider';
import PageTitle from '../../components/PageTitle';
import SizedInput from '../../components/SizedInput';
import { Header } from '../../components/Settings';
import FormBloc from '../../components/FormBloc';
import { useSettingsForm } from '../../hooks';
import useChangeLanguage from '../../components/LanguageProvider/hooks/useChangeLanguage';
import ProfilePageLabel from './components';
import { form, schema } from './utils';
const languages = Object.keys(translationMessages);
const ProfilePage = () => {
const changeLanguage = useChangeLanguage();
const { changeLocale, localeNames } = useLocalesProvider();
const { formatMessage } = useIntl();
const onSubmitSuccessCb = data => {
changeLanguage(data.preferedLanguage);
changeLocale(data.preferedLanguage);
auth.setUserInfo(data);
};
@ -129,8 +126,8 @@ const ProfilePage = () => {
selectedValue={get(modifiedData, 'preferedLanguage')}
onChange={nextLocaleCode => setField('preferedLanguage', nextLocaleCode)}
>
{languages.map(language => {
const langName = languageNativeNames[language];
{Object.keys(localeNames).map(language => {
const langName = localeNames[language];
return (
<Option value={language} key={language}>

View File

@ -1,9 +1,7 @@
import languageProviderReducer from './components/LanguageProvider/reducer';
import menuReducer from './components/LeftMenu/reducer';
import rbacProviderReducer from './components/RBACProvider/reducer';
const reducers = {
language: languageProviderReducer,
menu: menuReducer,
rbacProvider: rbacProviderReducer,
};

View File

@ -6,10 +6,11 @@ import appReducers from '../reducers';
const library = { fields: {}, components: {} };
const middlewares = { middlewares: [] };
const reducers = { reducers: appReducers };
const locales = [];
describe('ADMIN | StrapiApp', () => {
it('should render the app without plugins', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
const { container } = render(app.render());
expect(container.firstChild).toMatchInlineSnapshot(`
@ -43,7 +44,7 @@ describe('ADMIN | StrapiApp', () => {
});
it('should create a valid store', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
const store = app.createStore();
@ -52,7 +53,7 @@ describe('ADMIN | StrapiApp', () => {
describe('Hook api', () => {
it('runs the "moto" hooks in series', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
app.createHook('hello');
app.createHook('moto');
@ -70,7 +71,7 @@ describe('ADMIN | StrapiApp', () => {
});
it('runs the "moto" hooks in series asynchronously', async () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
app.createHook('hello');
app.createHook('moto');
@ -88,7 +89,7 @@ describe('ADMIN | StrapiApp', () => {
});
it('runs the "moto" hooks in waterfall', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
app.createHook('hello');
app.createHook('moto');
@ -104,7 +105,7 @@ describe('ADMIN | StrapiApp', () => {
});
it('runs the "moto" hooks in waterfall asynchronously', async () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
app.createHook('hello');
app.createHook('moto');
@ -120,7 +121,7 @@ describe('ADMIN | StrapiApp', () => {
});
it('runs the "moto" hooks in parallel', async () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
app.createHook('hello');
app.createHook('moto');
@ -140,14 +141,14 @@ describe('ADMIN | StrapiApp', () => {
describe('Settings api', () => {
it('the settings should be defined', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
expect(app.settings).toBeDefined();
expect(app.settings.global).toBeDefined();
});
it('should creates a new section', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
const section = { id: 'foo', intlLabel: { id: 'foo', defaultMessage: 'foo' } };
const links = [
{
@ -164,7 +165,7 @@ describe('ADMIN | StrapiApp', () => {
});
it('should add a link correctly to the global sectionn', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
const link = {
Component: jest.fn(),
to: '/bar',
@ -179,7 +180,7 @@ describe('ADMIN | StrapiApp', () => {
});
it('should add an array of links correctly to the global section', () => {
const app = StrapiApp({ middlewares, reducers, library });
const app = StrapiApp({ middlewares, reducers, library, locales });
const links = [
{
Component: jest.fn(),

View File

@ -1,85 +0,0 @@
// FIXME:
// Load only needed translations
// import ar from './ar.json';
// import cs from './cs.json';
// import de from './de.json';
// import dk from './dk.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import he from './he.json';
// import id from './id.json';
// import it from './it.json';
// import ja from './ja.json';
// import ko from './ko.json';
// import ms from './ms.json';
// import nl from './nl.json';
// import pl from './pl.json';
// import ptBR from './pt-BR.json';
// import pt from './pt.json';
// import ru from './ru.json';
// import th from './th.json';
// import tr from './tr.json';
// import vi from './vi.json';
// import zhHans from './zh-Hans.json';
// import zh from './zh.json';
// import sk from './sk.json';
// import uk from './uk.json';
const trads = {
// ar,
// cs,
// de,
// dk,
en,
// es,
fr,
// he,
// id,
// it,
// ja,
// ko,
// ms,
// nl,
// pl,
// 'pt-BR': ptBR,
// pt,
// ru,
// sk,
// th,
// tr,
// uk,
// vi,
// 'zh-Hans': zhHans,
// zh,
};
export const languageNativeNames = {
// ar: 'العربية',
// cs: 'Čeština',
// de: 'Deutsch',
// dk: 'Dansk',
en: 'English',
// es: 'Español',
fr: 'Français',
// he: 'עברית',
// id: 'Indonesian',
// it: 'Italiano',
// ja: '日本語',
// ko: '한국어',
// ms: 'Melayu',
// nl: 'Nederlands',
// pl: 'Polski',
// 'pt-BR': 'Português (Brasil)',
// pt: 'Português (Portugal)',
// ru: 'Русский',
// sk: 'Slovenčina',
// th: 'ไทย',
// tr: 'Türkçe',
// uk: 'Українська',
// vi: 'Tiếng Việt',
// 'zh-Hans': '中文 (简体)',
// zh: '中文 (繁體)',
};
export default trads;

View File

@ -0,0 +1,30 @@
const languageNativeNames = {
ar: 'العربية',
cs: 'Čeština',
de: 'Deutsch',
dk: 'Dansk',
en: 'English',
es: 'Español',
fr: 'Français',
he: 'עברית',
id: 'Indonesian',
it: 'Italiano',
ja: '日本語',
ko: '한국어',
ms: 'Melayu',
nl: 'Nederlands',
pl: 'Polski',
'pt-BR': 'Português (Brasil)',
pt: 'Português (Portugal)',
ru: 'Русский',
sk: 'Slovenčina',
sv: 'Swedish',
th: 'ไทย',
tr: 'Türkçe',
uk: 'Українська',
vi: 'Tiếng Việt',
'zh-Hans': '中文 (简体)',
zh: '中文 (繁體)',
};
export default languageNativeNames;

View File

@ -1,9 +1,15 @@
import translationMessages, { languageNativeNames } from '../index';
const fs = require('fs-extra');
const path = require('path');
const languageNativeNames = require('../languageNativeNames').default;
const languages = fs
.readdirSync(path.join(__dirname, '..'))
.filter(file => file.includes('.json'))
.map(file => file.replace('.json', ''));
describe('translations', () => {
describe('languageNativeNames', () => {
it('should has native name for every locale', () => {
const languages = Object.keys(translationMessages);
languages.forEach(language => {
expect(typeof languageNativeNames[language] === 'string').toBe(true);
expect(!!languageNativeNames[language]).toBe(true);

View File

@ -199,6 +199,16 @@ async function createCacheDir(dir) {
await copyCustomAdmin(path.join(dir, 'admin'), cacheDir);
}
// Copy admin.config.js
const customAdminConfigFilePath = path.join(dir, 'admin', 'admin.config.js');
if (fs.existsSync(customAdminConfigFilePath)) {
await fs.copy(
customAdminConfigFilePath,
path.resolve(cacheDir, 'admin', 'src', 'admin.config.js')
);
}
// create plugins.js with plugins requires
await createPluginsJs(pluginsToCopy, localPluginsToCopy, cacheDir);

View File

@ -85,7 +85,7 @@
"react-error-boundary": "3.1.1",
"react-fast-compare": "^3.2.0",
"react-helmet": "^6.1.0",
"react-intl": "4.5.0",
"react-intl": "5.20.2",
"react-is": "^16.12.0",
"react-loadable": "^5.5.0",
"react-query": "3.8.3",

View File

@ -4,12 +4,11 @@
// Here's the file: strapi/docs/3.0.0-beta.x/guides/registering-a-field-in-admin.md
// Also the strapi-generate-plugins/files/admin/src/index.js needs to be updated
// IF THE DOC IS NOT UPDATED THE PULL REQUEST WILL NOT BE MERGED
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import pluginLogo from './assets/images/logo.svg';
import reducers from './reducers';
import trads from './translations';
import pluginPermissions from './permissions';
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
@ -32,7 +31,6 @@ export default {
isRequired: pluginPkg.strapi.required || false,
name,
pluginLogo,
trads,
menu: {
pluginsSectionLinks: [
{
@ -49,4 +47,27 @@ export default {
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "content-manager-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -40,7 +40,7 @@ describe('Content manager | App | main', () => {
uid: 'category',
title: 'Categories',
name: 'category',
to: '/category',
to: `/plugins/${pluginId}/collectionType/category`,
kind: 'collectionType',
isDisplayed: true,
permissions: [
@ -82,7 +82,7 @@ describe('Content manager | App | main', () => {
const rootReducer = combineReducers(cmReducers);
const store = createStore(rootReducer, { [`${pluginId}_app`]: contentManagerState });
const history = createMemoryHistory();
history.push('/plugins/content-manager/collectionType/category');
history.push(`/plugins/${pluginId}`);
const { container } = render(
<Provider store={store}>
@ -94,6 +94,7 @@ describe('Content manager | App | main', () => {
expect(screen.getByText('Home page')).toBeVisible();
expect(screen.getByText('Categories')).toBeVisible();
expect(history.location.pathname).toEqual(`/plugins/${pluginId}/collectionType/category`);
expect(container.firstChild).toMatchInlineSnapshot(`
.c2 {
margin-bottom: 0;
@ -338,7 +339,9 @@ describe('Content manager | App | main', () => {
>
<li>
<a
href="/category"
aria-current="page"
class="active"
href="/plugins/content-manager/collectionType/category"
>
<p>
Categories
@ -402,6 +405,53 @@ describe('Content manager | App | main', () => {
`);
});
it('should redirect to the single type', () => {
const contentManagerState = {
collectionTypeLinks: [],
singleTypeLinks: [
{
uid: 'homepage',
title: 'Home page',
name: 'homepage',
to: `/plugins/${pluginId}/homepage`,
kind: 'singleType',
isDisplayed: true,
permissions: [
{
action: 'plugins::content-manager.explorer.read',
subject: 'homepage',
},
],
},
],
models: [
{
kind: 'collectionType',
uid: 'category',
info: { label: 'Categories', name: 'category' },
},
{ kind: 'singleType', uid: 'homepage', info: { label: 'Home page', name: 'homepage' } },
],
components: [],
status: 'resolved',
};
useModels.mockImplementation(() => contentManagerState);
const rootReducer = combineReducers(cmReducers);
const store = createStore(rootReducer, { [`${pluginId}_app`]: contentManagerState });
const history = createMemoryHistory();
history.push(`/plugins/${pluginId}`);
render(
<Provider store={store}>
<Router history={history}>
<ContentManagerApp />
</Router>
</Provider>
);
expect(history.location.pathname).toEqual(`/plugins/${pluginId}/homepage`);
});
it('should redirect to 403 page', () => {
const history = createMemoryHistory();
const contentManagerState = {
@ -426,7 +476,7 @@ describe('Content manager | App | main', () => {
);
const rootReducer = combineReducers(cmReducers);
const store = createStore(rootReducer, { [`${pluginId}_app`]: contentManagerState });
history.push('/plugins/content-manager/collectionType/category');
history.push(`/plugins/${pluginId}/collectionType/category`);
render(
<Provider store={store}>
@ -456,14 +506,11 @@ describe('Content manager | App | main', () => {
);
const rootReducer = combineReducers(cmReducers);
const store = createStore(rootReducer, { [`${pluginId}_app`]: contentManagerState });
history.push('/plugins/content-manager/collectionType/category');
history.push(`/plugins/${pluginId}/collectionType/category`);
render(
<Provider store={store}>
<Router
history={history}
// initialEntries={['/plugins/content-manager/collectionType/category']}
>
<Router history={history}>
<ContentManagerApp />
</Router>
</Provider>

View File

@ -163,7 +163,7 @@
"permissions.not-allowed.update": "You are not allowed to see this document",
"plugin.description.long": "Quick way to see, edit and delete the data in your database.",
"plugin.description.short": "Quick way to see, edit and delete the data in your database.",
"plugin.name": "Content manager",
"plugin.name": "Content Manager",
"popUpWarning.bodyMessage.contentType.delete": "Are you sure you want to delete this entry?",
"popUpWarning.bodyMessage.contentType.delete.all": "Are you sure you want to delete these entries?",
"popUpWarning.warning.cancelAllSettings": "Are you sure you want to cancel your modifications?",

View File

@ -1,53 +0,0 @@
// import ar from './ar.json';
// import cs from './cs.json';
// import de from './de.json';
// import dk from './dk.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import id from './id.json';
// import it from './it.json';
// import ja from './ja.json';
// import ko from './ko.json';
// import ms from './ms.json';
// import nl from './nl.json';
// import pl from './pl.json';
// import ptBR from './pt-BR.json';
// import pt from './pt.json';
// import ru from './ru.json';
// import th from './th.json';
// import tr from './tr.json';
// import uk from './uk.json';
// import vi from './vi.json';
// import zhHans from './zh-Hans.json';
// import zh from './zh.json';
// import sk from './sk.json';
const trads = {
// ar,
// cs,
// de,
// dk,
en,
// es,
fr,
// id,
// it,
// ja,
// ko,
// ms,
// nl,
// pl,
// 'pt-BR': ptBR,
// pt,
// ru,
// th,
// tr,
// uk,
// vi,
// 'zh-Hans': zhHans,
// zh,
// sk,
};
export default trads;

View File

@ -16,6 +16,8 @@
"@buffetjs/styles": "3.3.5",
"@buffetjs/utils": "3.3.5",
"@sindresorhus/slugify": "1.1.0",
"@strapi/helper-plugin": "3.6.0",
"@strapi/utils": "3.6.0",
"classnames": "^2.3.1",
"codemirror": "^5.61.0",
"draft-js": "^0.11.5",
@ -36,7 +38,7 @@
"react": "^16.14.0",
"react-dom": "^16.9.0",
"react-fast-compare": "^3.2.0",
"react-intl": "4.5.0",
"react-intl": "5.20.2",
"react-redux": "7.2.3",
"react-router": "^5.2.0",
"react-router-dom": "^5.0.0",
@ -45,8 +47,6 @@
"redux-immutable": "^4.0.0",
"reselect": "^4.0.0",
"sanitize-html": "2.3.3",
"@strapi/helper-plugin": "3.6.0",
"@strapi/utils": "3.6.0",
"yup": "^0.27.0"
},
"author": {

View File

@ -4,10 +4,9 @@
// Here's the file: strapi/docs/3.0.0-beta.x/guides/registering-a-field-in-admin.md
// Also the strapi-generate-plugins/files/admin/src/index.js needs to be updated
// IF THE DOC IS NOT UPDATED THE PULL REQUEST WILL NOT BE MERGED
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginLogo from './assets/images/logo.svg';
import trads from './translations';
import pluginPermissions from './permissions';
import pluginId from './pluginId';
import reducers from './reducers';
@ -28,7 +27,6 @@ export default {
isReady: true,
name,
pluginLogo,
trads,
menu: {
pluginsSectionLinks: [
{
@ -49,4 +47,27 @@ export default {
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "content-type-builder-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,51 +0,0 @@
// import ar from './ar.json';
// import cs from './cs.json';
// import de from './de.json';
// import dk from './dk.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import id from './id.json';
// import it from './it.json';
// import ja from './ja.json';
// import ko from './ko.json';
// import ms from './ms.json';
// import nl from './nl.json';
// import pl from './pl.json';
// import ptBR from './pt-BR.json';
// import pt from './pt.json';
// import ru from './ru.json';
// import th from './th.json';
// import tr from './tr.json';
// import uk from './uk.json';
// import zhHans from './zh-Hans.json';
// import zh from './zh.json';
// import sk from './sk.json';
const trads = {
// ar,
// cs,
// de,
// dk,
en,
// es,
fr,
// id,
// it,
// ja,
// ko,
// ms,
// nl,
// pl,
// 'pt-BR': ptBR,
// pt,
// ru,
// th,
// tr,
// uk,
// 'zh-Hans': zhHans,
// zh,
// sk,
};
export default trads;

View File

@ -15,13 +15,17 @@
"@buffetjs/styles": "3.3.5",
"@buffetjs/utils": "3.3.5",
"@sindresorhus/slugify": "1.1.0",
"@strapi/generate": "3.6.0",
"@strapi/generate-api": "3.6.0",
"@strapi/helper-plugin": "3.6.0",
"@strapi/utils": "3.6.0",
"fs-extra": "^9.1.0",
"immutable": "^3.8.2",
"lodash": "4.17.21",
"pluralize": "^8.0.0",
"react": "^16.14.0",
"react-dom": "^16.9.0",
"react-intl": "4.5.0",
"react-intl": "5.20.2",
"react-redux": "7.2.3",
"react-router": "^5.2.0",
"react-router-dom": "^5.0.0",
@ -29,10 +33,6 @@
"redux": "^4.0.1",
"redux-immutable": "^4.0.0",
"reselect": "^4.0.0",
"@strapi/generate": "3.6.0",
"@strapi/generate-api": "3.6.0",
"@strapi/helper-plugin": "3.6.0",
"@strapi/utils": "3.6.0",
"yup": "^0.27.0"
},
"author": {

View File

@ -6,12 +6,11 @@
// IF THE DOC IS NOT UPDATED THE PULL REQUEST WILL NOT BE MERGED
import React from 'react';
import { CheckPagePermissions } from '@strapi/helper-plugin';
import { CheckPagePermissions, prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import pluginLogo from './assets/images/logo.svg';
import pluginPermissions from './permissions';
import trads from './translations';
import getTrad from './utils/getTrad';
import SettingsPage from './pages/Settings';
@ -53,8 +52,30 @@ export default {
isRequired: pluginPkg.strapi.required || false,
name,
pluginLogo,
trads,
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "email-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,53 +0,0 @@
// import ar from './ar.json';
// import cs from './cs.json';
// import de from './de.json';
// import dk from './dk.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import id from './id.json';
// import it from './it.json';
// import ja from './ja.json';
// import ko from './ko.json';
// import ms from './ms.json';
// import nl from './nl.json';
// import pl from './pl.json';
// import ptBR from './pt-BR.json';
// import pt from './pt.json';
// import ru from './ru.json';
// import th from './th.json';
// import tr from './tr.json';
// import uk from './uk.json';
// import vi from './vi.json';
// import zhHans from './zh-Hans.json';
// import zh from './zh.json';
// import sk from './sk.json';
const trads = {
// ar,
// cs,
// de,
// dk,
en,
// es,
fr,
// id,
// it,
// ja,
// ko,
// ms,
// nl,
// pl,
// 'pt-BR': ptBR,
// pt,
// ru,
// th,
// tr,
// uk,
// vi,
// 'zh-Hans': zhHans,
// zh,
// sk,
};
export default trads;

View File

@ -160,6 +160,7 @@ export { default as generateFiltersFromSearch } from './utils/generateFiltersFro
export { default as generateSearchFromFilters } from './utils/generateSearchFromFilters';
export { default as generateSearchFromObject } from './utils/generateSearchFromObject';
export { default as prefixFileUrlWithBackendUrl } from './utils/prefixFileUrlWithBackendUrl';
export { default as prefixPluginTranslations } from './utils/prefixPluginTranslations';
// SVGS
export { default as LayoutIcon } from './svgs/Layout';

View File

@ -0,0 +1,9 @@
const prefixPluginTranslations = (trad, pluginId) => {
return Object.keys(trad).reduce((acc, current) => {
acc[`${pluginId}.${current}`] = trad[current];
return acc;
}, {});
};
export default prefixPluginTranslations;

View File

@ -80,7 +80,7 @@
"react": "^16.14.0",
"react-dom": "^16.9.0",
"react-helmet": "^6.1.0",
"react-intl": "4.5.0",
"react-intl": "5.20.2",
"react-router": "^5.2.0",
"react-router-dom": "^5.0.0",
"reactstrap": "8.4.1",

View File

@ -5,7 +5,7 @@
// Also the strapi-generate-plugins/files/admin/src/index.js needs to be updated
// IF THE DOC IS NOT UPDATED THE PULL REQUEST WILL NOT BE MERGED
import React from 'react';
import { CheckPagePermissions } from '@strapi/helper-plugin';
import { CheckPagePermissions, prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginLogo from './assets/images/logo.svg';
import pluginPermissions from './permissions';
@ -14,7 +14,6 @@ import InputMedia from './components/InputMedia';
import InputModalStepper from './components/InputModalStepper';
import SettingsPage from './pages/SettingsPage';
import reducers from './reducers';
import trads from './translations';
import pluginId from './pluginId';
import { getTrad } from './utils';
@ -41,7 +40,7 @@ export default {
isRequired: pluginPkg.strapi.required || false,
name,
pluginLogo,
trads,
menu: {
pluginsSectionLinks: [
{
@ -73,4 +72,27 @@ export default {
permissions: pluginPermissions.settings,
});
},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "upload-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,37 +0,0 @@
// import de from './de.json';
// import dk from './dk.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import he from './he.json';
// import it from './it.json';
// import ja from './ja.json';
// import ms from './ms.json';
// import ru from './ru.json';
// import th from './th.json';
// import uk from './uk.json';
// import ptBR from './pt-BR.json';
// import sk from './sk.json';
// import zh from './zh.json';
// import pl from './pl.json';
const trads = {
// de,
// dk,
en,
// es,
fr,
// he,
// it,
// ja,
// ms,
// ru,
// th,
// uk,
// 'pt-BR': ptBR,
// sk,
// zh,
// pl,
};
export default trads;

View File

@ -32,7 +32,7 @@
"react": "^16.14.0",
"react-copy-to-clipboard": "^5.0.3",
"react-dom": "^16.9.0",
"react-intl": "4.5.0",
"react-intl": "5.20.2",
"react-redux": "7.2.3",
"react-router": "^5.2.0",
"react-router-dom": "^5.0.0",

View File

@ -0,0 +1,18 @@
'use strict';
/* eslint-disable no-unused-vars */
// TODO move the file in the config folder
module.exports = {
webpack: (config, webpack) => {
// Note: we provide webpack above so you should not `require` it
// Perform customizations to webpack config
// Important: return the modified config
return config;
},
app: config => {
config.locales = ['fr'];
return config;
},
};

View File

@ -1,8 +1,8 @@
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import App from './containers/App';
import Initializer from './containers/Initializer';
import trads from './translations';
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const icon = pluginPkg.strapi.icon;
@ -39,8 +39,28 @@ export default {
},
],
},
trads,
});
},
boot(app) {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(`./translations/${locale}.json`)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,9 +0,0 @@
import en from './en.json';
import fr from './fr.json';
const trads = {
en,
fr,
};
export default trads;

View File

@ -4,12 +4,12 @@
// Here's the file: strapi/docs/3.0.0-beta.x/guides/registering-a-field-in-admin.md
// Also the strapi-generate-plugins/files/admin/src/index.js needs to be updated
// IF THE DOC IS NOT UPDATED THE PULL REQUEST WILL NOT BE MERGED
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginPermissions from './permissions';
import pluginId from './pluginId';
import pluginLogo from './assets/images/logo.svg';
import App from './pages/App';
import trads from './translations';
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const icon = pluginPkg.strapi.icon;
@ -28,8 +28,6 @@ export default {
name,
pluginLogo,
// TODO
trads,
// TODO
menu: {
pluginsSectionLinks: [
{
@ -46,4 +44,27 @@ export default {
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "documentation-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,50 +0,0 @@
// TODO
// import ar from './ar.json';
// import cs from './cs.json';
// import de from './de.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import id from './id.json';
// import it from './it.json';
// import ko from './ko.json';
// import ms from './ms.json';
// import nl from './nl.json';
// import pl from './pl.json';
// import ptBR from './pt-BR.json';
// import pt from './pt.json';
// import ru from './ru.json';
// import th from './th.json';
// import tr from './tr.json';
// import uk from './uk.json';
// import vi from './vi.json';
// import zhHans from './zh-Hans.json';
// import zh from './zh.json';
// import sk from './sk.json';
const trads = {
// ar,
// cs,
// de,
en,
// es,
fr,
// id,
// it,
// ko,
// ms,
// nl,
// pl,
// 'pt-BR': ptBR,
// pt,
// ru,
// th,
// tr,
// uk,
// vi,
// 'zh-Hans': zhHans,
// zh,
// sk,
};
export default trads;

View File

@ -27,7 +27,7 @@
"react": "^16.14.0",
"react-copy-to-clipboard": "^5.0.3",
"react-dom": "^16.9.0",
"react-intl": "4.5.0",
"react-intl": "5.20.2",
"react-redux": "7.2.3",
"react-router": "^5.2.0",
"react-router-dom": "^5.0.0",

View File

@ -1,7 +1,7 @@
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import pluginLogo from './assets/images/logo.svg';
import trads from './translations';
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const icon = pluginPkg.strapi.icon;
@ -17,8 +17,30 @@ export default {
isRequired: pluginPkg.strapi.required || false,
name,
pluginLogo,
trads,
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "graphql-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,9 +0,0 @@
import en from './en.json';
import fr from './fr.json';
const trads = {
en,
fr,
};
export default trads;

View File

@ -1,5 +1,6 @@
import get from 'lodash/get';
import * as yup from 'yup';
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginLogo from './assets/images/logo.svg';
import CheckboxConfirmation from './components/CheckboxConfirmation';
@ -10,7 +11,6 @@ import LocalePicker from './components/LocalePicker';
import middlewares from './middlewares';
import pluginPermissions from './permissions';
import pluginId from './pluginId';
import trads from './translations';
import { getTrad } from './utils';
import mutateCTBContentTypeSchema from './utils/mutateCTBContentTypeSchema';
import LOCALIZED_FIELDS from './utils/localizedFields';
@ -37,7 +37,6 @@ export default {
mainComponent: null,
name,
pluginLogo,
trads,
});
},
boot(app) {
@ -161,4 +160,27 @@ export default {
});
}
},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "i18n-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,9 +0,0 @@
import en from './en.json';
import fr from './fr.json';
const trads = {
en,
fr,
};
export default trads;

View File

@ -1,7 +1,7 @@
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import pluginLogo from './assets/images/logo.svg';
import trads from './translations';
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const icon = pluginPkg.strapi.icon;
@ -17,8 +17,30 @@ export default {
isRequired: pluginPkg.strapi.required || false,
name,
pluginLogo,
trads,
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "sentry-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,49 +0,0 @@
// import ar from './ar.json';
// import cs from './cs.json';
// import de from './de.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import id from './id.json';
// import it from './it.json';
// import ko from './ko.json';
// import ms from './ms.json';
// import nl from './nl.json';
// import pl from './pl.json';
// import ptBR from './pt-BR.json';
// import pt from './pt.json';
// import ru from './ru.json';
// import th from './th.json';
// import tr from './tr.json';
// import uk from './uk.json';
// import vi from './vi.json';
// import zhHans from './zh-Hans.json';
// import zh from './zh.json';
// import sk from './sk.json';
const trads = {
// ar,
// cs,
// de,
en,
// es,
fr,
// id,
// it,
// ko,
// ms,
// nl,
// pl,
// 'pt-BR': ptBR,
// pt,
// ru,
// th,
// tr,
// uk,
// vi,
// 'zh-Hans': zhHans,
// zh,
// sk,
};
export default trads;

View File

@ -5,12 +5,11 @@
// Also the strapi-generate-plugins/files/admin/src/index.js needs to be updated
// IF THE DOC IS NOT UPDATED THE PULL REQUEST WILL NOT BE MERGED
import React from 'react';
import { CheckPagePermissions } from '@strapi/helper-plugin';
import { CheckPagePermissions, prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginLogo from './assets/images/logo.svg';
import pluginPermissions from './permissions';
import pluginId from './pluginId';
import trads from './translations';
import RolesPage from './pages/Roles';
import ProvidersPage from './pages/Providers';
import EmailTemplatesPage from './pages/EmailTemplates';
@ -100,8 +99,30 @@ export default {
isRequired: pluginPkg.strapi.required || false,
name,
pluginLogo,
trads,
});
},
boot() {},
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map(locale => {
return import(
/* webpackChunkName: "users-permissions-translation-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
})
);
return Promise.resolve(importedTrads);
},
};

View File

@ -1,55 +0,0 @@
// import ar from './ar.json';
// import cs from './cs.json';
// import de from './de.json';
// import dk from './dk.json';
import en from './en.json';
// import es from './es.json';
import fr from './fr.json';
// import id from './id.json';
// import it from './it.json';
// import ja from './ja.json';
// import ko from './ko.json';
// import ms from './ms.json';
// import nl from './nl.json';
// import pl from './pl.json';
// import ptBR from './pt-BR.json';
// import pt from './pt.json';
// import ru from './ru.json';
// import th from './th.json';
// import tr from './tr.json';
// import uk from './uk.json';
// import vi from './vi.json';
// import zhHans from './zh-Hans.json';
// import zh from './zh.json';
// import sk from './sk.json';
// import sv from './sv.json';
const trads = {
// ar,
// cs,
// de,
// dk,
en,
// es,
fr,
// id,
// it,
// ja,
// ko,
// ms,
// nl,
// pl,
// 'pt-BR': ptBR,
// pt,
// ru,
// th,
// tr,
// uk,
// vi,
// 'zh-Hans': zhHans,
// zh,
// sk,
// sv,
};
export default trads;

View File

@ -28,7 +28,7 @@
"purest": "3.1.0",
"react": "^16.14.0",
"react-dom": "^16.9.0",
"react-intl": "4.5.0",
"react-intl": "5.20.2",
"react-redux": "7.2.3",
"react-router": "^5.2.0",
"react-router-dom": "^5.0.0",

142
yarn.lock
View File

@ -1390,53 +1390,63 @@
dependencies:
tslib "^2.0.1"
"@formatjs/ecma402-abstract@1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz#ac6c17a8fffac43c6d68c849a7b732626d32654c"
integrity sha512-Mv027hcLFjE45K8UJ8PjRpdDGfR0aManEFj1KzoN8zXNveHGEygpZGfFf/FTTMl+QEVSrPAUlyxaCApvmv47AQ==
"@formatjs/ecma402-abstract@1.9.3":
version "1.9.3"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.9.3.tgz#00892014c805935b5b1345d238246e9bf3a2de50"
integrity sha512-DBrRUL65m4SVtfq+T4Qltd8+upAzfb9K1MX0UZ0hqQ0wpBY0PSIti9XJe0ZQ/j2v/KxpwQ0Jw5NLumKVezJFQg==
dependencies:
tslib "^2.0.1"
tslib "^2.1.0"
"@formatjs/intl-displaynames@^1.2.5":
version "1.2.10"
resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-1.2.10.tgz#bb9625cca90b099978cd967c6a98aaf4e23fc878"
integrity sha512-GROA2RP6+7Ouu0WnHFF78O5XIU7pBfI19WM1qm93l6MFWibUk67nCfVCK3VAYJkLy8L8ZxjkYT11VIAfvSz8wg==
"@formatjs/fast-memoize@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.1.1.tgz#3006b58aca1e39a98aca213356b42da5d173f26b"
integrity sha512-mIqBr5uigIlx13eZTOPSEh2buDiy3BCdMYUtewICREQjbb4xarDiVWoXSnrERM7NanZ+0TAHNXSqDe6HpEFQUg==
"@formatjs/icu-messageformat-parser@2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.0.6.tgz#7471c2116982f07b3d9b80e4572a870f20adbaf6"
integrity sha512-dgOZ2kq3sbjjC4P0IIghXFUiGY+x9yyypBJF9YFACjw8gPq/OSPmOzdMGvjY9hl4EeeIhhsDd4LIAN/3zHG99A==
dependencies:
"@formatjs/intl-utils" "^2.3.0"
"@formatjs/ecma402-abstract" "1.9.3"
"@formatjs/icu-skeleton-parser" "1.2.7"
tslib "^2.1.0"
"@formatjs/intl-listformat@^1.4.4":
version "1.4.8"
resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-1.4.8.tgz#70b81005e7dcf74329cb5b314a940ce5fce36cd0"
integrity sha512-WNMQlEg0e50VZrGIkgD5n7+DAMGt3boKi1GJALfhFMymslJb5i+5WzWxyj/3a929Z6MAFsmzRIJjKuv+BxKAOQ==
"@formatjs/icu-skeleton-parser@1.2.7":
version "1.2.7"
resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.2.7.tgz#a74954695c37470efdeff828799654088e567c34"
integrity sha512-xm1rJMOz4fwVfWH98VKtbTpZvyQ45plHilkCF16Nm6bAgosYC/IcMmgJisGr6uHqb5TvJRXE07+EvnkIIQjsdA==
dependencies:
"@formatjs/intl-utils" "^2.3.0"
"@formatjs/ecma402-abstract" "1.9.3"
tslib "^2.1.0"
"@formatjs/intl-numberformat@^5.5.2":
version "5.7.6"
resolved "https://registry.yarnpkg.com/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz#630206bb0acefd2d508ccf4f82367c6875cad611"
integrity sha512-ZlZfYtvbVHYZY5OG3RXizoCwxKxEKOrzEe2YOw9wbzoxF3PmFn0SAgojCFGLyNXkkR6xVxlylhbuOPf1dkIVNg==
"@formatjs/intl-displaynames@5.1.5":
version "5.1.5"
resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-5.1.5.tgz#fb65c09493c3488e11e72b7d9512f0c1cc18b247"
integrity sha512-338DoPv8C4BqLqE7Sn5GkJbbkpL0RG8VoMP6qMJywx7bXVgOdWXiXUl3owdCPvq0bpVGGxTl+UNnF+UH8wGdLg==
dependencies:
"@formatjs/ecma402-abstract" "1.4.0"
tslib "^2.0.1"
"@formatjs/ecma402-abstract" "1.9.3"
tslib "^2.1.0"
"@formatjs/intl-relativetimeformat@^4.5.12":
version "4.5.16"
resolved "https://registry.yarnpkg.com/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-4.5.16.tgz#7449cef3213dd66d25924ca41f125f87b58df95a"
integrity sha512-IQ0haY97oHAH5OYUdykNiepdyEWj3SAT+Fp9ZpR85ov2JNiFx+12WWlxlVS8ehdyncC2ZMt/SwFIy2huK2+6/A==
"@formatjs/intl-listformat@6.2.5":
version "6.2.5"
resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-6.2.5.tgz#b2534700807e3ca2c2d8180592c15751037c908a"
integrity sha512-LRGroM+uLc8dL5J8zwHhNNxWw45nnHQMphW3zEnD9AySKPbFRsrSxzV8LYA93U5mkvMSBf49RdEODpdeyDak/Q==
dependencies:
"@formatjs/intl-utils" "^2.3.0"
"@formatjs/ecma402-abstract" "1.9.3"
tslib "^2.1.0"
"@formatjs/intl-unified-numberformat@^3.3.3":
version "3.3.7"
resolved "https://registry.yarnpkg.com/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.3.7.tgz#9995a24568908188e716d81a1de5b702b2ee00e2"
integrity sha512-KnWgLRHzCAgT9eyt3OS34RHoyD7dPDYhRcuKn+/6Kv2knDF8Im43J6vlSW6Hm1w63fNq3ZIT1cFk7RuVO3Psag==
"@formatjs/intl@1.13.0":
version "1.13.0"
resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-1.13.0.tgz#31f11d88220b6b864e58d3a7c7cb46e3cee62bf3"
integrity sha512-Wu8NU+OT9ZmkRSeIfKBY8GRrsCpF/Abc7v6Q8wHzA/VxT7HZblhO8YryALISqc0Vdmq4xUnvbrX4cSNSXdafZg==
dependencies:
"@formatjs/intl-utils" "^2.3.0"
"@formatjs/intl-utils@^2.2.2", "@formatjs/intl-utils@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz#2dc8c57044de0340eb53a7ba602e59abf80dc799"
integrity sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==
"@formatjs/ecma402-abstract" "1.9.3"
"@formatjs/fast-memoize" "1.1.1"
"@formatjs/icu-messageformat-parser" "2.0.6"
"@formatjs/intl-displaynames" "5.1.5"
"@formatjs/intl-listformat" "6.2.5"
intl-messageformat "9.6.18"
tslib "^2.1.0"
"@fortawesome/fontawesome-common-types@^0.2.35":
version "0.2.35"
@ -3620,11 +3630,6 @@
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69"
integrity sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==
"@types/invariant@^2.2.31":
version "2.2.34"
resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.34.tgz#05e4f79f465c2007884374d4795452f995720bbe"
integrity sha512-lYUtmJ9BqUN688fGY1U1HZoWT1/Jrmgigx2loq4ZcJpICECm/Om3V314BxdzypO0u5PORKGMM6x0OXaljV1YFg==
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@ -10772,25 +10777,14 @@ interpret@^2.2.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
intl-format-cache@^4.2.24, intl-format-cache@^4.2.43:
version "4.3.1"
resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-4.3.1.tgz#484d31a9872161e6c02139349b259a6229ade377"
integrity sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q==
intl-messageformat-parser@^5.0.0, intl-messageformat-parser@^5.2.1:
version "5.5.1"
resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz#f09a692755813e6220081e3374df3fb1698bd0c6"
integrity sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==
intl-messageformat@9.6.18:
version "9.6.18"
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-9.6.18.tgz#785cc0c44a1a288fbbda63308907c3eab4eebe9f"
integrity sha512-phG2EtMS/J6C4pcbErPkTSoqJ+T6qNfPVWTv5cDnI/nuCDKRUHPLDp4QpMdxIWPSUYPj7Lq69vMEIqg91x+H1Q==
dependencies:
"@formatjs/intl-numberformat" "^5.5.2"
intl-messageformat@^8.3.7:
version "8.4.1"
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-8.4.1.tgz#f31c811efc561700b61ab4ee4716b870787c0fe5"
integrity sha512-N4jLt0KebfqXZZZQRwBwZMrqwccHzZnN6KSeUsfidIoHMPIlLIgq08KcYsn7bZS6adh1KKH4/99VODWDDDu85Q==
dependencies:
intl-format-cache "^4.2.43"
intl-messageformat-parser "^5.2.1"
"@formatjs/fast-memoize" "1.1.1"
"@formatjs/icu-messageformat-parser" "2.0.6"
tslib "^2.1.0"
into-stream@^5.1.0:
version "5.1.1"
@ -16623,23 +16617,20 @@ react-input-autosize@^3.0.0:
dependencies:
prop-types "^15.5.8"
react-intl@4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-4.5.0.tgz#f1ea00eb393b1a0e33850819b5ce8947abed187e"
integrity sha512-CQuFR9vjUYOjzxsm7KaVRdM4hKOyMNY2ejvniZCbz3Ni3jMbjfTgcXYmxqYBn0lenMaFg3G2ol7HKkoy2YSXlQ==
react-intl@5.20.2:
version "5.20.2"
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-5.20.2.tgz#e453889e028502b9913e4ed1c35642ec74daba05"
integrity sha512-Uv3OydXIjF1wABfUm0mI8SItNQW7zuFaU42W0qXYVRvSoRXMv9rUorC1FLHTiS5zifQCRvWs65VejL8PO5he8w==
dependencies:
"@formatjs/intl-displaynames" "^1.2.5"
"@formatjs/intl-listformat" "^1.4.4"
"@formatjs/intl-relativetimeformat" "^4.5.12"
"@formatjs/intl-unified-numberformat" "^3.3.3"
"@formatjs/intl-utils" "^2.2.2"
"@formatjs/ecma402-abstract" "1.9.3"
"@formatjs/icu-messageformat-parser" "2.0.6"
"@formatjs/intl" "1.13.0"
"@formatjs/intl-displaynames" "5.1.5"
"@formatjs/intl-listformat" "6.2.5"
"@types/hoist-non-react-statics" "^3.3.1"
"@types/invariant" "^2.2.31"
hoist-non-react-statics "^3.3.2"
intl-format-cache "^4.2.24"
intl-messageformat "^8.3.7"
intl-messageformat-parser "^5.0.0"
shallow-equal "^1.2.1"
intl-messageformat "9.6.18"
tslib "^2.1.0"
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6:
version "16.13.1"
@ -18058,11 +18049,6 @@ shallow-clone@^3.0.0:
dependencies:
kind-of "^6.0.2"
shallow-equal@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da"
integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==
shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"