mirror of
https://github.com/maputnik/editor.git
synced 2026-07-25 23:37:27 +00:00
Add react-i18next for multi-language support (#917)
This is a rough start on adding react-i18next. I'll be working on adding more translatable strings and translations in the coming days. I'm going to need to wrap class components in HOCs, so let me know if there's something I should be fixing before doing that. I'm thinking now to keep the exported class names exactly the same, and rename the existing classes by prefixing an `I` (for internal). For example: ``` export default class AppToolbar ... ``` becomes ``` class IAppToolbar ... const AppToolbar = withTranslation()(IAppToolbar); export default AppToolbar; ``` I'll be able to contribute Japanese strings (I've talked to a couple people on my team and they'll be happy to help as well), so that's the language I decided to go with in this PR. Closes #746 --------- Co-authored-by: Ko Nagase <nagase@georepublic.co.jp> Co-authored-by: Harel M <harel.mazor@gmail.com>
This commit is contained in:
committed by
GitHub
parent
35840409b8
commit
58edd262b0
@@ -19,31 +19,45 @@ import FieldSourceLayer from './FieldSourceLayer'
|
||||
import { changeType, changeProperty } from '../libs/layer'
|
||||
import layout from '../config/layout.json'
|
||||
import {formatLayerId} from '../libs/format';
|
||||
import { WithTranslation, withTranslation } from 'react-i18next';
|
||||
import { TFunction } from 'i18next';
|
||||
|
||||
|
||||
function getLayoutForType(type: LayerSpecification["type"]) {
|
||||
return layout[type] ? layout[type] : layout.invalid;
|
||||
function getLayoutForType(type: LayerSpecification["type"], t: TFunction) {
|
||||
return layout[type] ? {
|
||||
...layout[type],
|
||||
groups: layout[type].groups.map(group => {
|
||||
return {
|
||||
...group,
|
||||
id: group.title.replace(/ /g, "_"),
|
||||
title: t(group.title)
|
||||
};
|
||||
}),
|
||||
} : layout.invalid;
|
||||
}
|
||||
|
||||
function layoutGroups(layerType: LayerSpecification["type"]): {title: string, type: string, fields?: string[]}[] {
|
||||
function layoutGroups(layerType: LayerSpecification["type"], t: TFunction): {id: string, title: string, type: string, fields?: string[]}[] {
|
||||
const layerGroup = {
|
||||
title: 'Layer',
|
||||
id: 'layer',
|
||||
title: t('Layer'),
|
||||
type: 'layer'
|
||||
}
|
||||
const filterGroup = {
|
||||
title: 'Filter',
|
||||
id: 'filter',
|
||||
title: t('Filter'),
|
||||
type: 'filter'
|
||||
}
|
||||
const editorGroup = {
|
||||
title: 'JSON Editor',
|
||||
id: 'jsoneditor',
|
||||
title: t('JSON Editor'),
|
||||
type: 'jsoneditor'
|
||||
}
|
||||
return [layerGroup, filterGroup]
|
||||
.concat(getLayoutForType(layerType).groups)
|
||||
.concat(getLayoutForType(layerType, t).groups)
|
||||
.concat([editorGroup])
|
||||
}
|
||||
|
||||
type LayerEditorProps = {
|
||||
type LayerEditorInternalProps = {
|
||||
layer: LayerSpecification
|
||||
sources: {[key: string]: SourceSpecification}
|
||||
vectorLayers: {[key: string]: any}
|
||||
@@ -58,14 +72,14 @@ type LayerEditorProps = {
|
||||
isLastLayer?: boolean
|
||||
layerIndex: number
|
||||
errors?: any[]
|
||||
};
|
||||
} & WithTranslation;
|
||||
|
||||
type LayerEditorState = {
|
||||
editorGroups: {[keys:string]: boolean}
|
||||
};
|
||||
|
||||
/** Layer editor supporting multiple types of layers. */
|
||||
export default class LayerEditor extends React.Component<LayerEditorProps, LayerEditorState> {
|
||||
class LayerEditorInternal extends React.Component<LayerEditorInternalProps, LayerEditorState> {
|
||||
static defaultProps = {
|
||||
onLayerChanged: () => {},
|
||||
onLayerIdChange: () => {},
|
||||
@@ -76,22 +90,22 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
|
||||
reactIconBase: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props: LayerEditorProps) {
|
||||
constructor(props: LayerEditorInternalProps) {
|
||||
super(props)
|
||||
|
||||
//TODO: Clean this up and refactor into function
|
||||
const editorGroups: {[keys:string]: boolean} = {}
|
||||
layoutGroups(this.props.layer.type).forEach(group => {
|
||||
layoutGroups(this.props.layer.type, props.t).forEach(group => {
|
||||
editorGroups[group.title] = true
|
||||
})
|
||||
|
||||
this.state = { editorGroups }
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props: LayerEditorProps, state: LayerEditorState) {
|
||||
static getDerivedStateFromProps(props: Readonly<LayerEditorInternalProps>, state: LayerEditorState) {
|
||||
const additionalGroups = { ...state.editorGroups }
|
||||
|
||||
getLayoutForType(props.layer.type).groups.forEach(group => {
|
||||
getLayoutForType(props.layer.type, props.t).groups.forEach(group => {
|
||||
if(!(group.title in additionalGroups)) {
|
||||
additionalGroups[group.title] = true
|
||||
}
|
||||
@@ -242,17 +256,19 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
|
||||
const groupIds: string[] = [];
|
||||
const layerType = this.props.layer.type
|
||||
const groups = layoutGroups(layerType).filter(group => {
|
||||
const groups = layoutGroups(layerType, t).filter(group => {
|
||||
return !(layerType === 'background' && group.type === 'source')
|
||||
}).map(group => {
|
||||
const groupId = group.title.replace(/ /g, "_");
|
||||
const groupId = group.id;
|
||||
groupIds.push(groupId);
|
||||
return <LayerEditorGroup
|
||||
data-wd-key={group.title}
|
||||
id={groupId}
|
||||
key={group.title}
|
||||
key={groupId}
|
||||
title={group.title}
|
||||
isActive={this.state.editorGroups[group.title]}
|
||||
onActiveToggle={this.onGroupToggle.bind(this, group.title)}
|
||||
@@ -265,25 +281,25 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
|
||||
|
||||
const items: {[key: string]: {text: string, handler: () => void, disabled?: boolean}} = {
|
||||
delete: {
|
||||
text: "Delete",
|
||||
text: t("Delete"),
|
||||
handler: () => this.props.onLayerDestroy(this.props.layerIndex)
|
||||
},
|
||||
duplicate: {
|
||||
text: "Duplicate",
|
||||
text: t("Duplicate"),
|
||||
handler: () => this.props.onLayerCopy(this.props.layerIndex)
|
||||
},
|
||||
hide: {
|
||||
text: (layout.visibility === "none") ? "Show" : "Hide",
|
||||
text: (layout.visibility === "none") ? t("Show") : t("Hide"),
|
||||
handler: () => this.props.onLayerVisibilityToggle(this.props.layerIndex)
|
||||
},
|
||||
moveLayerUp: {
|
||||
text: "Move layer up",
|
||||
text: t("Move layer up"),
|
||||
// Not actually used...
|
||||
disabled: this.props.isFirstLayer,
|
||||
handler: () => this.moveLayer(-1)
|
||||
},
|
||||
moveLayerDown: {
|
||||
text: "Move layer down",
|
||||
text: t("Move layer down"),
|
||||
// Not actually used...
|
||||
disabled: this.props.isLastLayer,
|
||||
handler: () => this.moveLayer(+1)
|
||||
@@ -297,12 +313,12 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
|
||||
|
||||
return <section className="maputnik-layer-editor"
|
||||
role="main"
|
||||
aria-label="Layer editor"
|
||||
aria-label={t("Layer editor")}
|
||||
>
|
||||
<header>
|
||||
<div className="layer-header">
|
||||
<h2 className="layer-header__title">
|
||||
Layer: {formatLayerId(this.props.layer.id)}
|
||||
{t("Layer: {{layerId}}", { layerId: formatLayerId(this.props.layer.id) })}
|
||||
</h2>
|
||||
<div className="layer-header__info">
|
||||
<Wrapper
|
||||
@@ -310,7 +326,11 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
|
||||
onSelection={handleSelection}
|
||||
closeOnSelection={false}
|
||||
>
|
||||
<Button id="skip-target-layer-editor" data-wd-key="skip-target-layer-editor" className='more-menu__button' title="Layer options">
|
||||
<Button
|
||||
id="skip-target-layer-editor"
|
||||
data-wd-key="skip-target-layer-editor"
|
||||
className='more-menu__button'
|
||||
title={"Layer options"}>
|
||||
<MdMoreVert className="more-menu__button__svg" />
|
||||
</Button>
|
||||
<Menu>
|
||||
@@ -340,3 +360,6 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
|
||||
</section>
|
||||
}
|
||||
}
|
||||
|
||||
const LayerEditor = withTranslation()(LayerEditorInternal);
|
||||
export default LayerEditor;
|
||||
|
||||
Reference in New Issue
Block a user