Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(DeleteResource): Add wording & i18n #1243

Merged
merged 20 commits into from
Apr 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions I18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ e.g:
LIST_DISPLAY
HEADERBAR_GO_PORTAL

### Specific translation cases

In some cases, you want to add some html markup or style to your translations. For this case, you can use Trans component
https://react.i18next.com/components/trans-component.html

```jsx
import { Trans } from 'react-i18next';

<Trans i18nKey="DELETE_RESOURCE_MESSAGE">
Are you sure you want to remove the {{ resourceLabel: resourceInfo.resourceTypeLabel }}
<strong> {{ resourceName: resourceInfo.label }} </strong> ?
</Trans>
```

For this case, the translation json value is :
`Are you sure you want to remove the <1>{{ resourceLabel }}</1> <2><1>{{ resourceName }}</1></2>?`

More infos in the react-i18next's documentation

## Use i18n with UI in your App

### Create an instance of i18n
Expand Down
39 changes: 12 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,65 +25,50 @@
"build-storybook": "npm run test:demo && export ACTION='test:demo' && export TRAVIS_BRANCH='master' && export TRAVIS_BUILD_DIR=`pwd` && .travis/after_success_static.sh && .travis/after_success_demo.sh && .travis/after_success_coverage.sh",
"publish-storybook": "npm run build-storybook && cd .static && surge",
"changelog": "git log --pretty=\"format:%C(bold green)%ad%C(reset) %<(70,trunc)%s\" --date=short --color",
"extract-i18n": "npm run extract-i18n-components && npm run extract-i18n-forms",
"extract-i18n": "npm run extract-i18n-components && npm run extract-i18n-forms && npm run extract-i18n-containers",
"extract-i18n-components": "i18next packages/components/src -r --default-values -o i18n/components -n tui-components -l en --write-old false --fileFilter '*.js' --directoryFilter '!__snapshots__'",
"extract-i18n-forms": "i18next packages/forms/src -r --default-values -o i18n/forms -n tui-forms -l en --write-old false --fileFilter '*.js' --directoryFilter '!__snapshots__'"
"extract-i18n-forms": "i18next packages/forms/src -r --default-values -o i18n/forms -n tui-forms -l en --write-old false --fileFilter '*.js' --directoryFilter '!__snapshots__'",
"extract-i18n-containers": "i18next packages/containers/src -r --default-values -o i18n/containers -n tui-containers -l en --write-old false --fileFilter '*.js' --directoryFilter '!__snapshots__'"
},
"watch": {
"build-cmf": {
"patterns": [
"packages/cmf/src/**/*"
],
"patterns": ["packages/cmf/src/**/*"],
"extensions": "js"
},
"build-components": {
"patterns": [
"packages/components/src/**/*"
],
"patterns": ["packages/components/src/**/*"],
"extensions": "js,css,scss,svg"
},
"build-sagas": {
"patterns": [
"packages/sagas/src/**/*"
],
"patterns": ["packages/sagas/src/**/*"],
"extensions": "js"
},
"build-containers": {
"patterns": [
"packages/containers/src/**/*"
],
"patterns": ["packages/containers/src/**/*"],
"extensions": "js,css,scss,svg"
},
"build-forms": {
"patterns": [
"packages/forms/src/**/*"
],
"patterns": ["packages/forms/src/**/*"],
"extensions": "js,scss"
},
"build-icons": {
"patterns": [
"packages/icons/src/svg/*"
],
"patterns": ["packages/icons/src/svg/*"],
"extensions": "svg"
},
"build-logging": {
"patterns": [
"packages/logging/src/**/*"
],
"patterns": ["packages/logging/src/**/*"],
"extensions": "js"
},
"build-theme": {
"patterns": [
"packages/theme/src/**/*"
],
"patterns": ["packages/theme/src/**/*"],
"extensions": "css,scss"
}
},
"repository": {
"type": "git",
"url": "https://github.com/Talend/ui.git"
},
"engines" : { "node" : ">=8.10.0 <9.0.0" },
"engines": { "node": ">=8.10.0 <9.0.0" },
"version": "0.0.0",
"private": true,
"workspaces": [
Expand Down
22 changes: 19 additions & 3 deletions packages/containers/examples/ExampleDeleteResource.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from 'react';
import { I18nextProvider } from 'react-i18next';
import i18n, { LanguageSwitcher } from './config/i18n';
import { DeleteResource } from '../src';

/*
Expand Down Expand Up @@ -32,10 +34,12 @@ Also require two fields, uri it contains the uri to call and resourceType, the t
const views = {
uri: '/myEndpoint',
resourceType: 'myResourceType',
resourceTypeLabel: 'resource',
header: 'My header title',
'cancel-action': 'dialog:delete:cancel',
'validate-action': 'dialog:delete:validate',
routeParams: { id: 'myID' },
female: true,
};

const params = {
Expand All @@ -47,6 +51,18 @@ const props = {
params,
};

export default function ExampleAction() {
return <DeleteResource {...props} />;
}
export default {
default: () => (
<div>
<DeleteResource {...props} />;
</div>
),
translated: () => (
<I18nextProvider i18n={i18n}>
<div>
<LanguageSwitcher />
<DeleteResource {...props} />
</div>
</I18nextProvider>
),
};
50 changes: 49 additions & 1 deletion packages/containers/examples/config/i18n.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import React from 'react';
import i18n from 'i18next';
import { I18N_DOMAIN_COMPONENTS } from '@talend/react-components';
import I18N_DOMAIN_CONTAINERS from '../../src/constant';

i18n.init({
lng: 'en',
resources: {
en: {
[I18N_DOMAIN_CONTAINERS]: {
DELETE_RESOURCE_MESSAGE:
'Are you sure you want to remove the <1>{{ resourceLabel }}</1> <2><1>{{ resourceName }}</1></2>?',
DELETE_RESOURCE_MESSAGE_female:
'Are you sure you want to remove the <1>{{ resourceLabel }}</1> <2><1>{{ resourceName }}</1></2>?',
},
},
fr: {
[I18N_DOMAIN_COMPONENTS]: {
LIST_TOOLBAR_DISPLAY: 'Affichage :',
Expand All @@ -17,6 +28,12 @@ i18n.init({
LIST_FILTER_REMOVE: 'Supprimer le filtre',
VIRTUALIZEDLIST_NO_RESULT: 'Pas de résultat',
},
[I18N_DOMAIN_CONTAINERS]: {
DELETE_RESOURCE_MESSAGE:
'Êtes-vous sûr(e) de vouloir supprimer le <1>{{ resourceLabel }}</1> <2><1>{{ resourceName }}</1></2> ?',
DELETE_RESOURCE_MESSAGE_female:
'Êtes-vous sûr(e) de vouloir supprimer la <1>{{ resourceLabel }}</1> <2><1>{{ resourceName }}</1></2> ?',
},
},
it: {
[I18N_DOMAIN_COMPONENTS]: {
Expand All @@ -32,10 +49,41 @@ i18n.init({
LIST_FILTER_REMOVE: 'Rimuova il filtro',
VIRTUALIZEDLIST_NO_RESULT: 'Nessun risultato',
},
[I18N_DOMAIN_CONTAINERS]: {
DELETE_RESOURCE_MESSAGE: 'Sei sicuro di voler eliminare {{resourceLabel}} ',
},
},
},
debug: false,
saveMissing: true,
debug: true,
wait: true, // globally set to wait for loaded translations in translate hoc
});

export const LanguageSwitcher = () => {
const style = {
position: 'fixed',
bottom: 0,
width: '100vw',
textAlign: 'center',
zIndex: 100000,
};

function renderBtn(locale, isDefault) {
return (
<button className="btn" onClick={() => i18n.changeLanguage(locale)}>
{locale} {isDefault && '(default)'}
</button>
);
}

return (
<nav style={style}>
<div className="btn-group">
{renderBtn('en', true)}
{renderBtn('fr')}
</div>
</nav>
);
};

export default i18n;
2 changes: 2 additions & 0 deletions packages/containers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,14 @@
"bson-objectid": "1.1.5",
"classnames": "2.2.5",
"immutable": "3.8.1",
"i18next": "^9.0.0",
"invariant": "2.2.2",
"keycode": "2.2.0",
"lodash": "4.17.4",
"prop-types": "15.5.10",
"react": "^15.6.2",
"react-bootstrap": "0.31.5",
"react-i18next": "^5.2.0",
"react-redux": "5.0.5",
"reselect": "^2.5.4"
},
Expand Down
25 changes: 23 additions & 2 deletions packages/containers/src/DeleteResource/DeleteResource.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import React from 'react';
import PropTypes from 'prop-types';
import { componentState } from '@talend/react-cmf';
import { ConfirmDialog } from '@talend/react-components';
import { translate, Trans } from 'react-i18next';
import { getActionsProps } from '../actionAPI';
import deleteResourceConst from './deleteResource.constants';
import DEFAULT_I18N from '../translate';
import I18N_DOMAIN_CONTAINERS from '../constant';

/**
* DeleteResource is used to delete a specific resource.
* When the component is mounted, it opens a confirm dialog.
* It uses the saga matching pattern to launch a race between the cancel and validate action.
*/
export default class DeleteResource extends React.Component {
export class DeleteResource extends React.Component {
static displayName = 'Container(DeleteResource)';
static propTypes = {
...componentState.propTypes,
Expand All @@ -19,11 +22,16 @@ export default class DeleteResource extends React.Component {
header: PropTypes.string,
uri: PropTypes.string.isRequired,
resourceType: PropTypes.string.isRequired,
resourceTypeLabel: PropTypes.string,
female: PropTypes.string,
};
static contextTypes = {
registry: PropTypes.object.isRequired,
store: PropTypes.object.isRequired,
};
static defaultProps = {
t: DEFAULT_I18N.t.bind(DEFAULT_I18N),
};

constructor(props, context) {
super(props, context);
Expand All @@ -49,6 +57,9 @@ export default class DeleteResource extends React.Component {
getResourceInfo() {
return {
resourceType: this.props.resourceType,
resourceTypeLabel: this.props.resourceTypeLabel
? this.props.resourceTypeLabel
: this.props.resourceType,
uri: this.props.uri,
...this.getLabel(),
id: this.props.params.id,
Expand All @@ -70,15 +81,25 @@ export default class DeleteResource extends React.Component {
const resourceInfo = this.getResourceInfo();
const validateAction = this.getActions(deleteResourceConst.VALIDATE_ACTION, resourceInfo);
const cancelAction = this.getActions(deleteResourceConst.CANCEL_ACTION, resourceInfo);
const i18nKey = this.props.female
? 'DELETE_RESOURCE_MESSAGE_female'
: 'DELETE_RESOURCE_MESSAGE';
return (
<ConfirmDialog
show
header={this.props.header}
cancelAction={cancelAction}
validateAction={validateAction}
>
<div>{resourceInfo.label}</div>
<div>
<Trans i18nKey={i18nKey}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 things on i18n :

  • add npm scripts to extract translation keys in root package.js
  • this trans is not compatible with i18next-parser, I won't block this PR for that since it is really needed and the solution is the good one I think, but we need a solution

Copy link

@jare-talend jare-talend Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsomsanith The next major version of i18next-parser will be compatible with Trans. I work on i18n on the next sprint so I like to update this package to manage it.

Copy link
Contributor Author

@romainseb romainseb Apr 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The npm script is on the container package.json
  2. Currently yes, there is some work here on this side -> Improved parsing of Trans component i18next/i18next-parser#85

For now, i think this is still the best way to get the translations with html markup.

There is another issue currently, as the Trans component manage to make it work the count attribute but the "context" attribute to declare male or female translation is not working as it is. I try to make a PR on this point in react-i18next ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. it should be added to the root package.json like the others, so that yarn extract-i18n extract all packages translations in a single folder. Take a look at root npm scripts

Are you sure you want to remove the {{ resourceLabel: resourceInfo.resourceTypeLabel }}
<strong> {{ resourceName: resourceInfo.label }} </strong> ?
</Trans>
</div>
</ConfirmDialog>
);
}
}

export default translate(I18N_DOMAIN_CONTAINERS, { i18n: DEFAULT_I18N })(DeleteResource);
12 changes: 7 additions & 5 deletions packages/containers/src/DeleteResource/DeleteResource.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { shallow } from 'enzyme';
import { store } from '@talend/react-cmf/lib/mock';
import Immutable from 'immutable';

import Container from './DeleteResource.container';
import { DeleteResource } from './DeleteResource.container';
import Connected from './DeleteResource.connect';

const state = store.state();
Expand Down Expand Up @@ -40,10 +40,12 @@ describe('Container DeleteResource', () => {
resource: new Immutable.Map({ label: 'myLabel' }),
header: 'My header title',
params: { id: 'myResourceID' },
resourceTypeLabel: 'resourceLabel',
female: true,
'validate-action': 'dialog:delete:validate',
'cancel-action': 'dialog:delete:cancel',
};
const wrapper = shallow(<Container {...props} />, { context });
const wrapper = shallow(<DeleteResource {...props} />, { context });
expect(wrapper.getElement()).toMatchSnapshot();
});
it('should render with wrong resourceInfo params', () => {
Expand All @@ -55,14 +57,14 @@ describe('Container DeleteResource', () => {
'validate-action': 'dialog:delete:validate',
'cancel-action': 'dialog:delete:cancel',
};
const wrapper = shallow(<Container {...props} />, { context });
const wrapper = shallow(<DeleteResource {...props} />, { context });
expect(wrapper.getElement()).toMatchSnapshot();
});
});

describe('Connected DeleteResource', () => {
it('should connect TestGenerator', () => {
expect(Connected.displayName).toBe(`Connect(CMF(${Container.displayName}))`);
expect(Connected.WrappedComponent).toBe(Container);
expect(Connected.displayName).toBe('Connect(CMF(Translate(Container(DeleteResource))))');
expect(Connected.WrappedComponent).toBe(DeleteResource);
});
});
2 changes: 2 additions & 0 deletions packages/containers/src/DeleteResource/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ Required :
* **redirectUrl** : is the url to redirect when delete is complete or cancel action is triggered

Optional :
* **resourceLabel** : is the parameter to show the type to remove if the resourceType is not readable by the user
* **routerParamAttribute** : is the attribute defined in the route to give the resource id
* **resourcePath** : array of string, is appended to resourceType key to deep location of a subset of a collection element
the delete service will use it to check if the resource exist in your application state tree
* **female** : Only for i18n, allow to set the i18nkey to tell of the resource type if female or not

example with resourceType only
```javascript
Expand Down
Loading