feat(locales): allow copying data from other locales (#5850) (#5881)

Co-authored-by: Kolja Markwardt <kolja.markwardt@edeka.de>
This commit is contained in:
Kolja Markwardt 2021-11-01 18:37:42 +01:00 committed by GitHub
parent 876b102e3a
commit 5e2d181498
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 21 deletions

View File

@ -4,22 +4,22 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { css } from '@emotion/core'; import { css } from '@emotion/core';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { import {
buttons,
colors, colors,
Dropdown, Dropdown,
DropdownItem, DropdownItem,
StyledDropdownButton, StyledDropdownButton,
buttons,
text, text,
} from 'netlify-cms-ui-default'; } from 'netlify-cms-ui-default';
import EditorControl from './EditorControl'; import EditorControl from './EditorControl';
import { import {
getI18nInfo, getI18nInfo,
isFieldTranslatable,
isFieldDuplicate,
isFieldHidden,
getLocaleDataPath, getLocaleDataPath,
hasI18n, hasI18n,
isFieldDuplicate,
isFieldHidden,
isFieldTranslatable,
} from '../../../lib/i18n'; } from '../../../lib/i18n';
const ControlPaneContainer = styled.div` const ControlPaneContainer = styled.div`
@ -45,23 +45,24 @@ const LocaleButtonWrapper = styled.div`
display: flex; display: flex;
`; `;
const LocaleRowWrapper = styled.div`
display: flex;
`;
const StyledDropdown = styled(Dropdown)` const StyledDropdown = styled(Dropdown)`
width: max-content; width: max-content;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
margin-right: 20px;
`; `;
function LocaleDropdown({ locales, selectedLocale, onLocaleChange, t }) { function LocaleDropdown({ locales, dropdownText, onLocaleChange }) {
return ( return (
<StyledDropdown <StyledDropdown
renderButton={() => { renderButton={() => {
return ( return (
<LocaleButtonWrapper> <LocaleButtonWrapper>
<LocaleButton> <LocaleButton>{dropdownText}</LocaleButton>
{t('editor.editorControlPane.i18n.writingInLocale', {
locale: selectedLocale.toUpperCase(),
})}
</LocaleButton>
</LocaleButtonWrapper> </LocaleButtonWrapper>
); );
}} }}
@ -113,6 +114,41 @@ export default class ControlPane extends React.Component {
this.props.onLocaleChange(val); this.props.onLocaleChange(val);
}; };
copyFromOtherLocale =
({ targetLocale, t }) =>
sourceLocale => {
if (
!window.confirm(
t('editor.editorControlPane.i18n.copyFromLocaleConfirm', {
locale: sourceLocale.toUpperCase(),
}),
)
) {
return;
}
const { entry, collection } = this.props;
const { locales, defaultLocale } = getI18nInfo(collection);
const locale = this.state.selectedLocale;
const i18n = locales && {
currentLocale: locale,
locales,
defaultLocale,
};
this.props.fields.forEach(field => {
if (isFieldTranslatable(field, targetLocale, sourceLocale)) {
const copyValue = getFieldValue({
field,
entry,
locale: sourceLocale,
isTranslatable: sourceLocale !== defaultLocale,
});
this.props.onChange(field, copyValue, undefined, i18n);
}
});
};
validate = async () => { validate = async () => {
this.props.fields.forEach(field => { this.props.fields.forEach(field => {
if (field.get('widget') === 'hidden') return; if (field.get('widget') === 'hidden') return;
@ -130,8 +166,8 @@ export default class ControlPane extends React.Component {
}; };
render() { render() {
const { collection, entry, fieldsMetaData, fieldsErrors, onChange, onValidate, t } = this.props; const { collection, entry, fields, fieldsMetaData, fieldsErrors, onChange, onValidate, t } =
const fields = this.props.fields; this.props;
if (!collection || !fields) { if (!collection || !fields) {
return null; return null;
@ -152,12 +188,20 @@ export default class ControlPane extends React.Component {
return ( return (
<ControlPaneContainer> <ControlPaneContainer>
{locales && ( {locales && (
<LocaleDropdown <LocaleRowWrapper>
locales={locales} <LocaleDropdown
selectedLocale={locale} locales={locales}
onLocaleChange={this.handleLocaleChange} dropdownText={t('editor.editorControlPane.i18n.writingInLocale', {
t={t} locale: locale.toUpperCase(),
/> })}
onLocaleChange={this.handleLocaleChange}
/>
<LocaleDropdown
locales={locales.filter(l => l !== locale)}
dropdownText={t('editor.editorControlPane.i18n.copyFromLocale')}
onLocaleChange={this.copyFromOtherLocale({ targetLocale: locale, t })}
/>
</LocaleRowWrapper>
)} )}
{fields {fields
.filter(f => f.get('widget') !== 'hidden') .filter(f => f.get('widget') !== 'hidden')
@ -179,9 +223,10 @@ export default class ControlPane extends React.Component {
})} })}
fieldsMetaData={fieldsMetaData} fieldsMetaData={fieldsMetaData}
fieldsErrors={fieldsErrors} fieldsErrors={fieldsErrors}
onChange={(field, newValue, newMetadata) => onChange={(field, newValue, newMetadata) => {
onChange(field, newValue, newMetadata, i18n) console.log('newMeta', newMetadata);
} onChange(field, newValue, newMetadata, i18n);
}}
onValidate={onValidate} onValidate={onValidate}
processControlRef={this.controlRef.bind(this)} processControlRef={this.controlRef.bind(this)}
controlRef={this.controlRef} controlRef={this.controlRef}

View File

@ -92,6 +92,9 @@ const de = {
}, },
i18n: { i18n: {
writingInLocale: 'Aktuelle Sprache: %{locale}', writingInLocale: 'Aktuelle Sprache: %{locale}',
copyFromLocale: 'Aus anderer Sprache übernehmen',
copyFromLocaleConfirm:
'Wollen Sie wirklich die Daten aus der Sprache %{locale} übernehmen?\nAlle bishergen Inhalte werden überschrieben.',
}, },
}, },
editor: { editor: {

View File

@ -92,6 +92,9 @@ const en = {
}, },
i18n: { i18n: {
writingInLocale: 'Writing in %{locale}', writingInLocale: 'Writing in %{locale}',
copyFromLocale: 'Fill in from another locale',
copyFromLocaleConfirm:
'Do you want to fill in data from %{locale} locale?\nAll existing content will be overwritten.',
}, },
}, },
editor: { editor: {