Skip to content

Commit 44728bd

Browse files
authored
feat: add multilingual support (#238)
1 parent c2aef60 commit 44728bd

19 files changed

+1406
-102
lines changed

.eslintrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"env": {
55
"browser": true,
66
"node": true,
7-
"es6": true
7+
"es6": true,
8+
"jest": true
89
},
910
"settings": {
1011
"react": {

README.md

+11-4
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,12 @@ netlifyIdentity.close();
7171

7272
// Log out the user
7373
netlifyIdentity.logout();
74-
// refresh the user's JWT
74+
// refresh the user's JWT
7575
// Note: this method returns a promise.
7676
netlifyIdentity.refresh().then((jwt)=>console.log(jwt))
77+
78+
// Change language
79+
netlifyIdentity.setLocale('en');
7780
```
7881

7982
#### A note on script tag versioning
@@ -86,7 +89,7 @@ and will only reflect breaking changes in the markup API.
8689
Netlify Identity Widget also has a
8790
[module API](https://www.npmjs.com/package/netlify-identity-widget):
8891

89-
```
92+
```bash
9093
yarn add netlify-identity-widget
9194
```
9295

@@ -97,6 +100,7 @@ const netlifyIdentity = require('netlify-identity-widget');
97100

98101
netlifyIdentity.init({
99102
container: '#netlify-modal' // defaults to document.body,
103+
locale: 'en' // defaults to 'en'
100104
});
101105

102106
netlifyIdentity.open(); // open the modal
@@ -116,10 +120,13 @@ netlifyIdentity.close();
116120
// Log out the user
117121
netlifyIdentity.logout();
118122

119-
// refresh the user's JWT
123+
// refresh the user's JWT
120124
// Note: this method returns a promise.
121125
netlifyIdentity.refresh().then((jwt)=>console.log(jwt))
122126

127+
// Change language
128+
netlifyIdentity.setLocale('en');
129+
123130
// Access the underlying GoTrue JS client.
124131
// Note that doing things directly through the GoTrue client brings a risk of getting out of
125132
// sync between your state and the widget’s state.
@@ -136,6 +143,7 @@ module API. Options include:
136143
container: '#some-query-selector'; // container to attach to
137144
APIUrl: 'https://www.example.com/.netlify/functions/identity'; // Absolute url to endpoint. ONLY USE IN SPECIAL CASES!
138145
namePlaceholder: 'some-placeholder-for-Name'; // custom placeholder for name input form
146+
locale: 'en'; // language code for translations - available: en, fr - default to en
139147
}
140148
```
141149

@@ -162,7 +170,6 @@ https://olddvdscreensaver.com
162170

163171
![](devmode.png)
164172

165-
166173
## List of Alternatives
167174

168175
**Lowest level JS Library**: If you want to use the official Javascript bindings to GoTrue, Netlify's underlying Identity service written in Go, use https://github.com/netlify/gotrue-js

package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
"dependencies": {},
1010
"devDependencies": {
1111
"@babel/cli": "^7.10.1",
12-
"@babel/register": "^7.10.1",
1312
"@babel/core": "^7.10.2",
1413
"@babel/plugin-proposal-class-properties": "^7.10.1",
1514
"@babel/plugin-proposal-decorators": "^7.10.1",
@@ -18,6 +17,7 @@
1817
"@babel/plugin-transform-react-jsx": "^7.10.1",
1918
"@babel/polyfill": "^7.10.1",
2019
"@babel/preset-env": "^7.10.2",
20+
"@babel/register": "^7.10.1",
2121
"auto-changelog": "^2.0.0",
2222
"babel-eslint": "^10.1.0",
2323
"babel-jest": "^26.0.1",
@@ -34,6 +34,7 @@
3434
"gh-release": "^3.4.0",
3535
"gotrue-js": "^0.9.21",
3636
"html-webpack-plugin": "^4.3.0",
37+
"jest": "^26.0.1",
3738
"mkdirp": "^0.5.1",
3839
"mobx": "^3.2.2",
3940
"mobx-preact": "^1.1.0",
@@ -79,7 +80,9 @@
7980
"react-demo": "cd example && yarn && yarn start",
8081
"release": "node ./script/release.js",
8182
"lint": "eslint src",
82-
"test": "run-s lint format-check",
83+
"test": "run-s lint format-check test:unit",
84+
"test:unit": "jest",
85+
"test:unit:watch": "jest --watch",
8386
"version": "run-s release changelog"
8487
}
8588
}

src/components/app.js

+25-19
Original file line numberDiff line numberDiff line change
@@ -11,48 +11,48 @@ const pagesWithHeader = { login: true, signup: true };
1111
const pages = {
1212
login: {
1313
login: true,
14-
button: "Log in",
15-
button_saving: "Logging in",
14+
button: "log_in",
15+
button_saving: "logging_in",
1616
email: true,
1717
password: true,
1818
link: "amnesia",
19-
link_text: "Forgot password?",
19+
link_text: "forgot_password",
2020
providers: true
2121
},
2222
signup: {
2323
signup: true,
24-
button: "Sign up",
25-
button_saving: "Signing Up",
24+
button: "sign_up",
25+
button_saving: "signing_up",
2626
name: true,
2727
email: true,
2828
password: true,
2929
providers: true
3030
},
3131
amnesia: {
32-
title: "Recover password",
33-
button: "Send recovery email",
34-
button_saving: "Sending recovery email",
32+
title: "recover_password",
33+
button: "send_recovery_email",
34+
button_saving: "sending_recovery_email",
3535
email: true,
3636
link: "login",
37-
link_text: "Never mind"
37+
link_text: "never_mind"
3838
},
3939
recovery: {
40-
title: "Recover password",
41-
button: "Update password",
42-
button_saving: "Updating password",
40+
title: "recover_password",
41+
button: "update_password",
42+
button_saving: "updating_password",
4343
password: true,
4444
link: "login",
45-
link_text: "Never mind"
45+
link_text: "never_mind"
4646
},
4747
invite: {
48-
title: "Complete your signup",
49-
button: "Sign up",
50-
button_saving: "Signing Up",
48+
title: "complete_your_signup",
49+
button: "sign_up",
50+
button_saving: "signing_up",
5151
password: true,
5252
providers: true
5353
},
5454
user: {
55-
title: "Logged in"
55+
title: "logged_in"
5656
}
5757
};
5858

@@ -96,6 +96,7 @@ class App extends Component {
9696
<SiteURLForm
9797
devMode={store.siteURL != null}
9898
onSiteURL={store.siteURL ? this.clearSiteURL : this.handleSiteURL}
99+
t={store.translate}
99100
/>
100101
);
101102
}
@@ -108,11 +109,12 @@ class App extends Component {
108109
user={store.user}
109110
saving={store.saving}
110111
onLogout={this.handleLogout}
112+
t={store.translate}
111113
/>
112114
);
113115
}
114116
if (store.modal.page === "signup" && store.settings.disable_signup) {
115-
return <Message type="signup_disabled" />;
117+
return <Message type="signup_disabled" t={store.translate} />;
116118
}
117119

118120
return (
@@ -123,19 +125,21 @@ class App extends Component {
123125
saving={store.saving}
124126
onSubmit={this.handleUser}
125127
namePlaceholder={store.namePlaceholder}
128+
t={store.translate}
126129
/>
127130
{!store.user && page.link && store.gotrue && (
128131
<button
129132
onclick={pageLinkHandler}
130133
className="btnLink forgotPasswordLink"
131134
>
132-
{page.link_text}
135+
{store.translate(page.link_text)}
133136
</button>
134137
)}
135138
{store.isLocal ? (
136139
<SiteURLForm
137140
devMode={store.siteURL != null}
138141
onSiteURL={store.siteURL ? this.clearSiteURL : this.handleSiteURL}
142+
t={store.translate}
139143
/>
140144
) : (
141145
<div />
@@ -172,6 +176,7 @@ class App extends Component {
172176
providers={providers}
173177
labels={store.settings.external_labels || {}}
174178
onLogin={this.handleExternalLogin}
179+
t={store.translate}
175180
/>
176181
) : null;
177182
}
@@ -195,6 +200,7 @@ class App extends Component {
195200
onPage={this.handlePage}
196201
onClose={this.handleClose}
197202
logo={store.modal.logo}
203+
t={store.translate}
198204
>
199205
{this.renderBody()}
200206
{this.renderProviders()}

src/components/controls.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Controls extends Component {
2424
};
2525

2626
render() {
27-
const { user } = this.props.store;
27+
const { user, translate: t } = this.props.store;
2828

2929
if (this.props.mode === "button") {
3030
return (
@@ -33,7 +33,7 @@ class Controls extends Component {
3333
href="#"
3434
onClick={this.handleButton}
3535
>
36-
{this.props.text || (user ? "Log out" : "Log in")}
36+
{this.props.text || (user ? t("log_out") : t("log_in"))}
3737
</a>
3838
);
3939
}
@@ -42,7 +42,7 @@ class Controls extends Component {
4242
return (
4343
<ul className="netlify-identity-menu">
4444
<li className="netlify-identity-item netlify-identity-user-details">
45-
Logged in as{" "}
45+
{t("logged_in_as")}{" "}
4646
<span className="netlify-identity-user">
4747
{user.user_metadata.name || user.email}
4848
</span>
@@ -53,7 +53,7 @@ class Controls extends Component {
5353
href="#"
5454
onClick={this.handleLogout}
5555
>
56-
Log out
56+
{t("log_out")}
5757
</a>
5858
</li>
5959
</ul>
@@ -68,7 +68,7 @@ class Controls extends Component {
6868
href="#"
6969
onClick={this.handleSignup}
7070
>
71-
Sign up
71+
{t("sign_up")}
7272
</a>
7373
</li>
7474
<li className="netlify-identity-item">
@@ -77,7 +77,7 @@ class Controls extends Component {
7777
href="#"
7878
onClick={this.handleLogin}
7979
>
80-
Log in
80+
{t("log_in")}
8181
</a>
8282
</li>
8383
</ul>

src/components/forms/logout.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,26 @@ export default class LogoutForm extends Component {
77
this.props.onLogout();
88
};
99
render() {
10-
const { user, saving } = this.props;
10+
const { user, saving, t } = this.props;
1111

1212
return (
1313
<form
1414
onSubmit={this.handleLogout}
1515
className={`form ${saving ? "disabled" : ""}`}
1616
>
1717
<p className="infoText">
18-
Logged in as <br />
18+
{t("logged_in_as")} <br />
1919
<span className="infoTextEmail">
2020
{user.user_metadata.full_name ||
2121
user.user_metadata.name ||
2222
user.email}
2323
</span>
2424
</p>
25-
<Button saving={saving} text="Log out" saving_text="Logging out" />
25+
<Button
26+
saving={saving}
27+
text={t("log_out")}
28+
saving_text={t("logging_out")}
29+
/>
2630
</form>
2731
);
2832
}

src/components/forms/message.js

+7-11
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,34 @@ import { h, Component } from "preact";
33
const messages = {
44
confirm: {
55
type: "success",
6-
text:
7-
"A confirmation message was sent to your email, click the link there to continue."
6+
text: "message_confirm"
87
},
98
password_mail: {
109
type: "success",
11-
text:
12-
"We've sent a recovery email to your account, follow the link there to reset your password."
10+
text: "message_password_mail"
1311
},
1412
email_changed: {
1513
type: "sucess",
16-
text: "Your email address has been updated!"
14+
text: "message_email_changed"
1715
},
1816
verfication_error: {
1917
type: "error",
20-
text:
21-
"There was an error verifying your account. Please try again or contact an administrator."
18+
text: "message_verfication_error"
2219
},
2320
signup_disabled: {
2421
type: "error",
25-
text:
26-
"Public signups are disabled. Contact an administrator and ask for an invite."
22+
text: "message_signup_disabled"
2723
}
2824
};
2925

3026
export default class Message extends Component {
3127
render() {
32-
const { type } = this.props;
28+
const { type, t } = this.props;
3329
const msg = messages[type];
3430

3531
return (
3632
<div className={`flashMessage ${msg.type}`}>
37-
<span>{msg.text}</span>
33+
<span>{t(msg.text)}</span>
3834
</div>
3935
);
4036
}

src/components/forms/providers.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ class Provider extends Component {
77
};
88

99
render() {
10-
const { provider, label } = this.props;
10+
const { provider, label, t } = this.props;
1111

1212
return (
1313
<button
1414
onClick={this.handleLogin}
1515
className={`provider${provider} btn btnProvider`}
1616
>
17-
Continue with {label}
17+
{`${t("continue_with")} ${label}`}
1818
</button>
1919
);
2020
}
@@ -30,7 +30,7 @@ export default class Providers extends Component {
3030
}
3131

3232
render() {
33-
const { providers, onLogin } = this.props;
33+
const { providers, onLogin, t } = this.props;
3434

3535
return (
3636
<div className="providersGroup">
@@ -41,6 +41,7 @@ export default class Providers extends Component {
4141
provider={p}
4242
label={this.getLabel(p)}
4343
onLogin={onLogin}
44+
t={t}
4445
/>
4546
))}
4647
</div>

0 commit comments

Comments
 (0)