Add formatjs/intl locale support

This commit is contained in:
Jamie Curnow
2021-07-26 12:04:48 +10:00
parent bc2bd67cda
commit 7530fc7602
9 changed files with 185 additions and 68 deletions

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ frontend/build
frontend/yarn-error.log frontend/yarn-error.log
frontend/yarn.lock frontend/yarn.lock
frontend/.npmrc frontend/.npmrc
frontend/src/locale/lang
test/cypress/fixtures/example.json test/cypress/fixtures/example.json
.vscode .vscode
docker-build docker-build

View File

@@ -1,4 +1,4 @@
version: '2' version: "2"
tasks: tasks:
default: default:
@@ -10,7 +10,9 @@ tasks:
sources: sources:
- internal/**/*.go - internal/**/*.go
- cmd/**/*.go - cmd/**/*.go
- ../frontend/src/locale/src/*.json
cmds: cmds:
- task: locale
- task: build - task: build
- cmd: echo -e "==> Running..." - cmd: echo -e "==> Running..."
silent: true silent: true
@@ -54,3 +56,14 @@ tasks:
silent: true silent: true
- cmd: bash scripts/test.sh - cmd: bash scripts/test.sh
silent: true silent: true
locale:
desc: Locale
dir: /app/frontend
cmds:
- cmd: yarn locale-compile
silent: true
ignore_error: true
- cmd: chown -R "$PUID:$PGID" src/locale/lang
silent: true
ignore_error: true

View File

@@ -39,9 +39,10 @@
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
"prettier": "2.3.2", "prettier": "2.3.2",
"query-string": "7.0.1", "query-string": "7.0.1",
"react": "17.0.2", "react": "^17.0.2",
"react-async": "10.0.1", "react-async": "10.0.1",
"react-dom": "17.0.2", "react-dom": "17.0.2",
"react-intl": "^5.20.6",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
"rooks": "5.0.2", "rooks": "5.0.2",
@@ -60,7 +61,9 @@
"eject": "react-scripts eject", "eject": "react-scripts eject",
"prettier": "prettier \"**/*.+(js|json|yml|css|ts|tsx)\"", "prettier": "prettier \"**/*.+(js|json|yml|css|ts|tsx)\"",
"format": "yarn prettier -- --write", "format": "yarn prettier -- --write",
"lint:fix": "eslint --fix --ext .ts --ext .tsx ." "lint:fix": "eslint --fix --ext .ts --ext .tsx .",
"locale-extract": "formatjs extract 'src/**/*.tsx' --out-file src/locale/src/en.json",
"locale-compile": "formatjs compile-folder src/locale/src src/locale/lang --ast"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
@@ -88,5 +91,8 @@
"statements": 0 "statements": 0
} }
} }
},
"devDependencies": {
"@formatjs/cli": "^4.2.29"
} }
} }

View File

@@ -2,14 +2,18 @@ import React from "react";
import Router from "components/Router"; import Router from "components/Router";
import { AuthProvider, HealthProvider } from "context"; import { AuthProvider, HealthProvider } from "context";
import { intl } from "locale";
import { RawIntlProvider } from "react-intl";
function App() { function App() {
return ( return (
<RawIntlProvider value={intl}>
<HealthProvider> <HealthProvider>
<AuthProvider> <AuthProvider>
<Router /> <Router />
</AuthProvider> </AuthProvider>
</HealthProvider> </HealthProvider>
</RawIntlProvider>
); );
} }

View File

@@ -0,0 +1,37 @@
import { createIntl, createIntlCache } from "react-intl";
import langEn from "./lang/en.json";
// todo
/*
const messages: Record<string, Record<string, string>> = {
"en-US": { selectalanguage: "Select a language" },
"pt-BR": { selectalanguage: "Selecione uma linguagem" },
};
*/
// end todo
const loadMessages = (locale: string) => {
switch (locale) {
/*
case 'fr':
return import("./lang/fr.json");
*/
default:
return langEn;
}
};
export const initialLocale = "en-US";
export const cache = createIntlCache();
const initialMessages = loadMessages(initialLocale);
console.log("MESSAGES:", initialMessages);
export const intl = createIntl(
// @ts-ignore messages file typings are correct
{ locale: initialLocale, messages: initialMessages },
cache,
);
export const fmt = intl.formatMessage;

View File

@@ -0,0 +1 @@
export * from "./IntlProvider";

View File

@@ -0,0 +1,20 @@
{
"setup.create": {
"defaultMessage": "Create Account"
},
"setup.title": {
"defaultMessage": "Create your first Account"
},
"user.email": {
"defaultMessage": "Email"
},
"user.name": {
"defaultMessage": "Name"
},
"user.nickname": {
"defaultMessage": "Nickname"
},
"user.password": {
"defaultMessage": "Password"
}
}

View File

@@ -3,6 +3,8 @@ import React, { useEffect, useRef, useState, ChangeEvent } from "react";
import { createUser } from "api/npm"; import { createUser } from "api/npm";
import { Alert, Button } from "components"; import { Alert, Button } from "components";
import { useAuthState, useHealthState } from "context"; import { useAuthState, useHealthState } from "context";
import { fmt } from "locale";
import { FormattedMessage } from "react-intl";
import logo from "../../img/logo-text-vertical-grey.png"; import logo from "../../img/logo-text-vertical-grey.png";
@@ -81,7 +83,10 @@ function Setup() {
onSubmit={onSubmit}> onSubmit={onSubmit}>
<div className="card-body"> <div className="card-body">
<h2 className="card-title text-center mb-4"> <h2 className="card-title text-center mb-4">
Create your first Account <FormattedMessage
id="setup.title"
defaultMessage="Create your first Account"
/>
</h2> </h2>
{errorMessage ? ( {errorMessage ? (
<Alert type="danger" className="text-center"> <Alert type="danger" className="text-center">
@@ -90,7 +95,9 @@ function Setup() {
) : null} ) : null}
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Name</label> <label className="form-label">
<FormattedMessage id="user.name" defaultMessage="Name" />
</label>
<input <input
ref={nameRef} ref={nameRef}
onChange={onChange} onChange={onChange}
@@ -98,24 +105,34 @@ function Setup() {
name="name" name="name"
value={formData.name} value={formData.name}
disabled={loading} disabled={loading}
placeholder="Name" placeholder={fmt({ id: "user.name", defaultMessage: "Name" })}
required required
/> />
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Nickname</label> <label className="form-label">
<FormattedMessage
id="user.nickname"
defaultMessage="Nickname"
/>
</label>
<input <input
onChange={onChange} onChange={onChange}
className="form-control" className="form-control"
name="nickname" name="nickname"
value={formData.nickname} value={formData.nickname}
disabled={loading} disabled={loading}
placeholder="Nickname" placeholder={fmt({
id: "user.nickname",
defaultMessage: "Nickname",
})}
required required
/> />
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Email</label> <label className="form-label">
<FormattedMessage id="user.email" defaultMessage="Email" />
</label>
<input <input
type="email" type="email"
onChange={onChange} onChange={onChange}
@@ -123,13 +140,21 @@ function Setup() {
name="email" name="email"
value={formData.email} value={formData.email}
disabled={loading} disabled={loading}
placeholder="Email" placeholder={fmt({
id: "user.email",
defaultMessage: "Email",
})}
maxLength={150} maxLength={150}
required required
/> />
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Password</label> <label className="form-label">
<FormattedMessage
id="user.password"
defaultMessage="Password"
/>
</label>
<input <input
type="password" type="password"
onChange={onChange} onChange={onChange}
@@ -137,7 +162,10 @@ function Setup() {
name="password" name="password"
value={formData.password} value={formData.password}
disabled={loading} disabled={loading}
placeholder="Password" placeholder={fmt({
id: "user.password",
defaultMessage: "Password",
})}
minLength={8} minLength={8}
maxLength={100} maxLength={100}
autoComplete="off" autoComplete="off"
@@ -146,7 +174,10 @@ function Setup() {
</div> </div>
<div className="form-footer"> <div className="form-footer">
<Button color="cyan" loading={loading} className="w-100"> <Button color="cyan" loading={loading} className="w-100">
Create Account <FormattedMessage
id="setup.create"
defaultMessage="Create Account"
/>
</Button> </Button>
</div> </div>
</div> </div>

View File

@@ -13,8 +13,12 @@ docker_cmd() {
cd "${DIR}/../.." || exit 1 cd "${DIR}/../.." || exit 1
echo -e "${BLUE} ${CYAN}Installing Frontend deps ...${RESET}" echo -e "${BLUE} ${CYAN}Installing Frontend deps ...${RESET}"
rm -rf frontend/node_modules rm -rf frontend/node_modules
docker_cmd yarn install docker_cmd yarn install
echo -e "${BLUE} ${CYAN}Compiling locales ...${RESET}"
docker_cmd yarn locale-compile
docker_cmd chown -R "$(id -u):$(id -g)" /app/frontend/src/locale/lang
echo -e "${BLUE} ${CYAN}Running eslint ...${RESET}" echo -e "${BLUE} ${CYAN}Running eslint ...${RESET}"
docker_cmd yarn eslint src docker_cmd yarn eslint src
docker_cmd yarn eslint -f junit src -o eslint.xml docker_cmd yarn eslint -f junit src -o eslint.xml