Skip to content

Commit e9080a0

Browse files
Axel GarnierCiyax
Axel Garnier
andcommitted
FEATURE: use in your native language (closes Hypertopic#70)
Co-Authored-By: Ciyax <[email protected]>
1 parent 96c11c2 commit e9080a0

22 files changed

+376
-36
lines changed

.babelrc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": [
3+
"macros"
4+
],
5+
}

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ build
99

1010
# misc
1111
.DS_Store
12+
13+
# lingui
14+
src/locales/_build
15+
src/locales/*/messages.js

.linguirc

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"localeDir": "src/locales/",
3+
"srcPathDirs": ["src/"],
4+
"format": "po",
5+
"sourceLocale": "fr"
6+
}

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ before_script:
1515
- docker pull benel/cucumber-capybara
1616

1717
script:
18+
- npm run extract
19+
- npm run compile
1820
- npm run build
1921
- npm run preprod
2022
- docker run --rm -v "$(pwd)":/app -t --net="host" --env APP_HOST="http://`hostname`:3000" benel/cucumber-capybara --retry 2 --fail-fast

features/support/env.rb

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
require 'capybara/cucumber'
22
require 'capybara/cuprite'
33

4+
Before do
5+
Capybara.current_session.driver.add_headers("Accept-Language" => "fr")
6+
end
47
Capybara.run_server = false
58
Capybara.default_driver = :cuprite
69
Capybara.javascript_driver = :cuprite

package.json

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"@lingui/react": "^2.9.1",
67
"bootstrap": "^4.4.1",
78
"disqus-react": "^1.0.7",
89
"hypertopic": "^3.2.0",
@@ -19,10 +20,19 @@
1920
"yaml": "^1.8.2"
2021
},
2122
"devDependencies": {
23+
"@babel/core": "^7.9.6",
24+
"@lingui/cli": "^2.9.1",
25+
"@lingui/macro": "^2.9.1",
26+
"babel-core": "^7.0.0-bridge.0",
27+
"babel-plugin-macros": "^2.8.0",
28+
"babel-preset-react": "^6.24.1",
2229
"react-scripts": "^3.4.1",
2330
"serve": "^11.3.0"
2431
},
2532
"scripts": {
33+
"add-locale": "lingui add-locale",
34+
"extract": "lingui extract",
35+
"compile": "lingui compile",
2636
"start": "env EXTEND_ESLINT=true react-scripts start",
2737
"build": "react-scripts build",
2838
"preprod": "serve -s build -p 3000 &",

public/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!doctype html>
2-
<html lang="en">
2+
<html lang="fr">
33
<head>
44
<meta charset="utf-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1">

src/components/Authenticated.jsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { Component } from 'react';
2+
import { t, Trans } from '@lingui/macro';
3+
import { i18n } from '../index.js';
24

35
class Authenticated extends Component {
46

@@ -17,22 +19,22 @@ class Authenticated extends Component {
1719
if (this.state.user) {
1820
return (
1921
<div className="Authenticated"> {this.state.user}
20-
<a href="#logout" onClick={this.handleLogout}>Se déconnecter</a>
22+
<a href="#logout" onClick={this.handleLogout}><Trans>Se déconnecter</Trans></a>
2123
</div>
2224
);
2325
}
2426
if (this.state.ask) {
2527
return(
2628
<form className="Authenticated" onSubmit={this.handleLogin}>
27-
<input placeholder="nom d'utilisateur" ref={(x) => this.login = x} />
28-
<input placeholder="mot de passe" ref={(x) => this.password = x} type="password" />
29-
<input type="submit" value="Se connecter" />
29+
<input placeholder={i18n._(t`nom d'utilisateur`)} ref={(x) => this.login = x} />
30+
<input placeholder={i18n._(t`mot de passe`)} ref={(x) => this.password = x} type="password" />
31+
<input type="submit" value={i18n._(t`Se connecter`)} />
3032
</form>
3133
);
3234
}
3335
return (
3436
<div className="Authenticated">
35-
<a href="#login" onClick={this.handleAsk}>Se connecter...</a>
37+
<a href="#login" onClick={this.handleAsk}><Trans>Se connecter...</Trans></a>
3638
</div>
3739
);
3840
}

src/components/itemPage/Attribute.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React from 'react';
2+
import { t } from '@lingui/macro';
3+
import { i18n } from '../../index.js';
24

35
class Attribute extends React.Component {
46

@@ -84,7 +86,7 @@ function Buttons(props) {
8486
function AttributeValue(props) {
8587
if (props.edited) return (
8688
<div className="Value edit">
87-
<input value={props.editedValue} placeholder="valeur" autoFocus
89+
<input value={props.editedValue} placeholder={i18n._(t`valeur`)} autoFocus
8890
onChange={props.onChange} onKeyDown={props.onKeyDown}
8991
/>
9092
</div>

src/components/itemPage/Item.jsx

+10-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import Resource from './Resource.jsx';
99
import Header from '../Header.jsx';
1010
import SameNameBlock from './SameNameBlock.jsx';
1111
import { DiscussionEmbed } from 'disqus-react';
12+
import { t, Trans } from '@lingui/macro';
13+
import { i18n } from "../../index.js"
1214

1315
const HIDDEN = ['topic', 'resource', 'thumbnail', 'item'];
1416

@@ -39,17 +41,18 @@ class Item extends Component {
3941
<Header conf={conf} />
4042
<div className="Status row h5">
4143
<Link to="/" className="badge badge-pill badge-light TopicTag">
42-
<span className="badge badge-pill badge-dark oi oi-chevron-left"> </span> Retour à l'accueil
44+
<span className="badge badge-pill badge-dark oi oi-chevron-left"> </span>
45+
<Trans>Retour à l'accueil</Trans>
4346
</Link>
4447
</div>
4548
<div className="container-fluid">
4649
<div className="App-content row">
4750
<div className="col-md-4 p-4">
4851
<div className="Description">
49-
<h2 className="h4 font-weight-bold text-center">Description</h2>
52+
<h2 className="h4 font-weight-bold text-center"><Trans>Description</Trans></h2>
5053
<div className="p-3">
5154
<div className="Attributes">
52-
<h3 className="h4">Attributs du document</h3>
55+
<h3 className="h4"><Trans>Attributs du document</Trans></h3>
5356
<hr/>
5457
<div>
5558
{attributes}
@@ -183,9 +186,9 @@ class Item extends Component {
183186
}
184187
}
185188

186-
var placeholder="Ajouter un attribut et une valeur...";
189+
var placeholder= t`Ajouter un attribut et une valeur...`;
187190
if (this.state.attributeInputFocus) {
188-
placeholder="attribut : valeur";
191+
placeholder= t`attribut : valeur`;
189192
}
190193

191194
return (
@@ -194,7 +197,7 @@ class Item extends Component {
194197
<input ref={(input) => this.attributeInput=input} value={this.state.attributeInputValue}
195198
onChange={attributeInputChange} onKeyDown={attributeInputChangeKeyDown}
196199
onFocus={attributeInputFocus} onBlur={attributeInputBlur}
197-
id="new-attribute" className="form-control" placeholder={placeholder} type="text" />
200+
id="new-attribute" className="form-control" placeholder={i18n._(placeholder)} type="text" />
198201
</div>
199202
<div className="input-group-append">
200203
<button type="button" className="btn btn-sm ValidateButton btn"
@@ -278,7 +281,7 @@ class Item extends Component {
278281

279282

280283
_removeTopic = async (topicToDelete) => {
281-
if (window.confirm('Voulez-vous réellement que l\'item affiché ne soit plus décrit à l\'aide de cette rubrique ?')) {
284+
if (window.confirm(i18n._(t`Voulez-vous réellement que l'item affiché ne soit plus décrit à l'aide de cette rubrique ?`))) {
282285
return new Hypertopic((await conf).services)
283286
.item({
284287
_id: this.props.match.params.item,

src/components/itemPage/SameNameBlock.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { Component } from 'react';
22
import Hypertopic from 'hypertopic';
33
import conf from '../../config.js';
4+
import { Trans } from '@lingui/macro';
45

56
class SameNameBlock extends Component {
67

@@ -16,7 +17,7 @@ class SameNameBlock extends Component {
1617
if (this.state.sameNameItemsList.length > 0) {
1718
return (
1819
<div className="Description">
19-
<h2 className="h4 font-weight-bold text-center">Items ayant le même nom</h2>
20+
<h2 className="h4 font-weight-bold text-center"><Trans>Items ayant le même nom</Trans></h2>
2021
<div className="Items m-3">
2122
{items}
2223
</div>

src/components/itemPage/Viewpoint.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {Topics} from '../../model.js';
55
import TopicPath from './TopicPath.jsx';
66
import TopicTree from '../../TopicTree.js';
77
import InputWithSuggestions from '../InputWithSuggestions.jsx';
8+
import { t } from '@lingui/macro';
9+
import { i18n } from "../../index.js"
810

911
class Viewpoint extends React.Component {
1012
constructor(props) {
@@ -66,7 +68,7 @@ class Viewpoint extends React.Component {
6668
render() {
6769
const paths = this._getPaths();
6870
const inputProps = {
69-
placeholder: this.state.newTopic ? 'Choisir une rubrique parent...' : 'Ajouter une rubrique...',
71+
placeholder: this.state.newTopic ? i18n._(t`Choisir une rubrique parent...`) : i18n._(t`Ajouter une rubrique...`),
7072
value: this.state.topicInputvalue,
7173
onFocus: this.onTopicInputFocus,
7274
onBlur: this.onTopicInputBlur,

src/components/portfolioPage/Badge.jsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { Component } from 'react';
2+
import { t } from '@lingui/macro';
3+
import { i18n } from '../../index.js';
24

35
class Badge extends Component {
46
constructor() {
@@ -12,11 +14,11 @@ class Badge extends Component {
1214
<span className={'badge badge-pill badge-light TopicTag ' + (this.props.exclusion ? 'text-danger' : '')}>
1315
{this.props.name}
1416
<button className={'badge badge-pill badge-dark oi ml-1 border-0 ' + (this.props.exclusion ? 'oi-plus' : 'oi-minus')}
15-
title={this.props.exclusion ? 'Inclure' : 'Exclure'}
17+
title={this.props.exclusion ? i18n._(t`Inclure`) : i18n._(t`Exclure`)}
1618
onClick={this.handleChangeState}
1719
> </button>
1820
<button className="badge badge-pill badge-dark oi oi-x border-0"
19-
title="Déselectionner"
21+
title={i18n._(t`Déselectionner`)}
2022
onClick={this.handleDeletion}>
2123
</button>
2224
</span>

src/components/portfolioPage/Button.jsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { Component } from 'react';
2+
import { t } from '@lingui/macro';
3+
import { i18n } from '../../index.js';
24

35
class Button extends Component {
46
constructor() {
@@ -8,8 +10,8 @@ class Button extends Component {
810
render() {
911
let union = (this.props.topics.type === 'union');
1012
return (<button className="badge badge-pill badge-secondary border-0 m-1 align-middle"
11-
title={union ? 'Ou' : 'Et'}
12-
onClick={this.handleChangeState}> {union ? 'Ou' : 'Et'}</button>
13+
title={union ? i18n._(t`Ou`) : i18n._(t`Et`)}
14+
onClick={this.handleChangeState}> {union ? i18n._(t`Ou`) : i18n._(t`Et`)}</button>
1315
);
1416
}
1517

src/components/portfolioPage/Portfolio.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Header from '../Header.jsx';
99
import Status from './Status.jsx';
1010
import SearchBar from './SearchBar.jsx';
1111
import ViewpointCreator from './ViewpointCreator.jsx';
12+
import { Trans } from '@lingui/macro';
1213

1314
class Portfolio extends Component {
1415
constructor() {
@@ -41,7 +42,7 @@ class Portfolio extends Component {
4142
<div className="App-content row">
4243
<div className="col-md-4 p-4">
4344
<div className="Description">
44-
<h2 className="h4 font-weight-bold text-center">Points de vue</h2>
45+
<h2 className="h4 font-weight-bold text-center"><Trans>Points de vue</Trans></h2>
4546
<div className="p-3">
4647
<ViewpointCreator />
4748
{viewpoints}

src/components/portfolioPage/SearchBar.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { withRouter } from 'react-router';
33
import memoize from 'memoize-one';
44
import {Topics} from '../../model.js';
55
import InputWithSuggestions from '../InputWithSuggestions.jsx';
6+
import { t } from '@lingui/macro';
7+
import { i18n } from '../../index.js';
68

79
class SearchBar extends React.Component {
810

@@ -36,7 +38,7 @@ class SearchBar extends React.Component {
3638

3739
render() {
3840
const inputProps = {
39-
placeholder: 'Rechercher...',
41+
placeholder: i18n._(t`Rechercher...`),
4042
value: this.state.pattern,
4143
className: 'form-control',
4244
type: 'search',

src/components/portfolioPage/Status.jsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import React, { Component } from 'react';
22
import Button from './Button.jsx';
33
import Badge from './Badge.jsx';
44
import { withRouter } from 'react-router-dom';
5+
import { Trans } from '@lingui/macro';
56

67
class Status extends Component {
78
render() {
89
if (this.props.selectionJSON.data.length === 0)
9-
return 'Tous les items';
10+
return <Trans>Tous les items</Trans>;
1011

1112
let status = [];
1213

@@ -22,7 +23,7 @@ class Status extends Component {
2223
.map(
2324
t =>
2425
(t.topic === null)
25-
? 'Thème inconnu'
26+
? <Trans>Thème inconnu</Trans>
2627
: <Badge exclusion={t.excluded} id={t.id} name={t.topic.name} _changeItemState={this._changeItemState}/>
2728
);
2829
status.push(topicsHTML.map((e, i) => i < topicsHTML.length - 1 ? [e, <Button topics={topics} selectionJSON={this.props.selectionJSON} _changeUnionState={this._changeUnionState}/>] : [e]).reduce((a, b) => a.concat(b))

src/components/portfolioPage/ViewpointCreator.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { Component } from 'react';
22
import { withRouter } from 'react-router-dom';
3+
import { Trans } from '@lingui/macro';
34

45
function makeID() {
56
var id = '';
@@ -14,7 +15,7 @@ class ViewpointCreator extends Component {
1415
render() {
1516
return (<div className="text-center">
1617
<button className="btn btn-light creationButton" onClick={_ => this._goToNewViewpoint()}>
17-
Nouveau point de vue
18+
<Trans>Nouveau point de vue</Trans>
1819
</button>
1920
</div>);
2021
}

src/components/viewpointPage/Outliner.jsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import conf from '../../config.js';
55
import Header from '../Header.jsx';
66
import TopicTree from '../../TopicTree.js';
77
import Node from './Node.jsx';
8+
import { t, Trans } from '@lingui/macro';
9+
import { i18n } from '../../index.js';
810

911
const _log = (x) => console.log(JSON.stringify(x, null, 2));
1012
const _error = (x) => console.error(x.message);
@@ -26,7 +28,8 @@ class Outliner extends React.Component {
2628
<Header conf={conf} />
2729
<div className="Status row h5">
2830
<Link to="/" className="badge badge-pill badge-light TopicTag">
29-
<span className="badge badge-pill badge-dark oi oi-chevron-left"> </span> Retour à l'accueil
31+
<span className="badge badge-pill badge-dark oi oi-chevron-left"> </span>
32+
<Trans>Retour à l'accueil</Trans>
3033
</Link>
3134
</div>
3235
<div className="container-fluid">
@@ -53,7 +56,7 @@ class Outliner extends React.Component {
5356

5457
_getTitle() {
5558
return (<form className="input-group" onSubmit={(e) => this._newVP(e)}>
56-
<input type="text" name="newTitle" className="form-control" placeholder="Nom du point de vue" />
59+
<input type="text" name="newTitle" className="form-control" placeholder={i18n._(t`Nom du point de vue`)} />
5760
<div className="input-group-append">
5861
<button type="submit" className="btn add btn-sm btn-light"><span className="oi oi-plus"> </span></button>
5962
</div>
@@ -62,8 +65,8 @@ class Outliner extends React.Component {
6265

6366
_getStatus() {
6467
return (this.state.title !== undefined)
65-
? 'Modification du point de vue'
66-
: 'Création du point de vue';
68+
? <Trans>Modification du point de vue</Trans>
69+
: <Trans>Création du point de vue</Trans>;
6770
}
6871

6972
async _newVP(e) {

0 commit comments

Comments
 (0)