1
Fork 0
mirror of https://github.com/pds-nest/nest.git synced 2024-11-25 14:34:19 +00:00

Use custom localization system

This commit is contained in:
Steffo 2021-05-18 02:04:06 +02:00
parent c0e87fb6ba
commit 20f60530e4
Signed by: steffo
GPG key ID: 6965406171929D01
28 changed files with 180 additions and 121 deletions

View file

@ -5,6 +5,7 @@ import GlobalTheme from "./components/providers/GlobalTheme"
import GlobalServer from "./components/providers/GlobalServer" import GlobalServer from "./components/providers/GlobalServer"
import GlobalUser from "./components/providers/GlobalUser" import GlobalUser from "./components/providers/GlobalUser"
import PageSwitcher from "./PageSwitcher" import PageSwitcher from "./PageSwitcher"
import GlobalLanguage from "./components/providers/GlobalLanguage"
/** /**
@ -15,16 +16,18 @@ import PageSwitcher from "./PageSwitcher"
*/ */
export default function App() { export default function App() {
return ( return (
<GlobalServer> <GlobalLanguage>
<GlobalUser> <GlobalServer>
<GlobalTheme> <GlobalUser>
<BrowserRouter> <GlobalTheme>
<Layout> <BrowserRouter>
<PageSwitcher/> <Layout>
</Layout> <PageSwitcher/>
</BrowserRouter> </Layout>
</GlobalTheme> </BrowserRouter>
</GlobalUser> </GlobalTheme>
</GlobalServer> </GlobalUser>
</GlobalServer>
</GlobalLanguage>
) )
} }

View file

@ -1,6 +1,3 @@
import LocalizedStrings from "react-localization"
/** /**
* All strings contained in the app should be present in this dict. * All strings contained in the app should be present in this dict.
* *
@ -12,7 +9,7 @@ import LocalizedStrings from "react-localization"
* "{number} km radius" * "{number} km radius"
* ``` * ```
*/ */
export default new LocalizedStrings({ export default {
// 🇮🇹 // 🇮🇹
it: { it: {
appName: "N.E.S.T.", appName: "N.E.S.T.",
@ -214,5 +211,4 @@ export default new LocalizedStrings({
admin: "Ylläpitäjä", admin: "Ylläpitäjä",
user: "Käyttäjä", user: "Käyttäjä",
} }
}) }

View file

@ -1,4 +1,4 @@
import React, { useState } from "react" import React, { useContext, useState } from "react"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faClock, faPlus } from "@fortawesome/free-solid-svg-icons" import { faClock, faPlus } from "@fortawesome/free-solid-svg-icons"
@ -10,7 +10,7 @@ import useRepositoryEditor from "../../hooks/useRepositoryEditor"
import ButtonToggleBeforeAfter from "./ButtonToggleBeforeAfter" import ButtonToggleBeforeAfter from "./ButtonToggleBeforeAfter"
import Condition from "../../utils/Condition" import Condition from "../../utils/Condition"
import convertToLocalISODate from "../../utils/convertToLocalISODate" import convertToLocalISODate from "../../utils/convertToLocalISODate"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
const INVALID_USER_CHARACTERS = /[^0-9TZ:+-]/g const INVALID_USER_CHARACTERS = /[^0-9TZ:+-]/g
@ -28,6 +28,7 @@ export default function BoxConditionDatetime({ ...props }) {
const [datetime, setDatetime] = useState("") const [datetime, setDatetime] = useState("")
const [ba, setBa] = useState(false) const [ba, setBa] = useState(false)
const { addCondition } = useRepositoryEditor() const { addCondition } = useRepositoryEditor()
const {strings} = useContext(ContextLanguage)
const onInputChange = event => { const onInputChange = event => {
let text = event.target.value let text = event.target.value
@ -54,11 +55,11 @@ export default function BoxConditionDatetime({ ...props }) {
<BoxFull <BoxFull
header={ header={
<span> <span>
{Localization.searchBy} {strings.searchBy}
&nbsp; &nbsp;
<FontAwesomeIcon icon={faClock}/> <FontAwesomeIcon icon={faClock}/>
&nbsp; &nbsp;
{Localization.byTimePeriod} {strings.byTimePeriod}
</span> </span>
} }
{...props} {...props}

View file

@ -1,4 +1,4 @@
import React, { useState } from "react" import React, { useContext, useState } from "react"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faHashtag, faPlus } from "@fortawesome/free-solid-svg-icons" import { faHashtag, faPlus } from "@fortawesome/free-solid-svg-icons"
@ -8,7 +8,7 @@ import Style from "./BoxConditionHashtag.module.css"
import ButtonIconOnly from "../base/ButtonIconOnly" import ButtonIconOnly from "../base/ButtonIconOnly"
import useRepositoryEditor from "../../hooks/useRepositoryEditor" import useRepositoryEditor from "../../hooks/useRepositoryEditor"
import Condition from "../../utils/Condition" import Condition from "../../utils/Condition"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
// Official hashtag regex from https://stackoverflow.com/a/22490853/4334568 // Official hashtag regex from https://stackoverflow.com/a/22490853/4334568
// noinspection RegExpAnonymousGroup,LongLine // noinspection RegExpAnonymousGroup,LongLine
@ -26,6 +26,7 @@ const INVALID_HASHTAG_CHARACTERS = /([^a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\
export default function BoxConditionHashtag({ ...props }) { export default function BoxConditionHashtag({ ...props }) {
const [hashtag, setHashtag] = useState("") const [hashtag, setHashtag] = useState("")
const { addCondition } = useRepositoryEditor() const { addCondition } = useRepositoryEditor()
const {strings} = useContext(ContextLanguage)
const onInputChange = event => { const onInputChange = event => {
let text = event.target.value let text = event.target.value
@ -45,11 +46,11 @@ export default function BoxConditionHashtag({ ...props }) {
<BoxFull <BoxFull
header={ header={
<span> <span>
{Localization.searchBy} {strings.searchBy}
&nbsp; &nbsp;
<FontAwesomeIcon icon={faHashtag}/> <FontAwesomeIcon icon={faHashtag}/>
&nbsp; &nbsp;
{Localization.byHashtag} {strings.byHashtag}
</span> </span>
} }
{...props} {...props}

View file

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from "react" import React, { useCallback, useContext, useEffect, useState } from "react"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faAt, faMapPin, faPlus } from "@fortawesome/free-solid-svg-icons" import { faAt, faMapPin, faPlus } from "@fortawesome/free-solid-svg-icons"
@ -7,7 +7,7 @@ import ButtonIconOnly from "../base/ButtonIconOnly"
import { MapContainer, TileLayer } from "react-leaflet" import { MapContainer, TileLayer } from "react-leaflet"
import useRepositoryEditor from "../../hooks/useRepositoryEditor" import useRepositoryEditor from "../../hooks/useRepositoryEditor"
import Condition from "../../utils/Condition" import Condition from "../../utils/Condition"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
const STARTING_POSITION = { lat: 41.89309, lng: 12.48289 } const STARTING_POSITION = { lat: 41.89309, lng: 12.48289 }
@ -54,6 +54,7 @@ export default function BoxConditionMap({ ...props }) {
const [zoom, setZoom] = useState(STARTING_ZOOM) const [zoom, setZoom] = useState(STARTING_ZOOM)
const [map, setMap] = useState(null) const [map, setMap] = useState(null)
const { addCondition } = useRepositoryEditor() const { addCondition } = useRepositoryEditor()
const {strings} = useContext(ContextLanguage)
const onMove = useCallback( const onMove = useCallback(
() => { () => {
@ -101,11 +102,11 @@ export default function BoxConditionMap({ ...props }) {
<BoxFull <BoxFull
header={ header={
<span> <span>
{Localization.searchBy} {strings.searchBy}
&nbsp; &nbsp;
<FontAwesomeIcon icon={faMapPin}/> <FontAwesomeIcon icon={faMapPin}/>
&nbsp; &nbsp;
{Localization.byZone} {strings.byZone}
</span> </span>
} }
childrenClassName={Style.BoxConditionMapContents} childrenClassName={Style.BoxConditionMapContents}

View file

@ -1,4 +1,4 @@
import React, { useState } from "react" import React, { useContext, useState } from "react"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faAt, faPlus } from "@fortawesome/free-solid-svg-icons" import { faAt, faPlus } from "@fortawesome/free-solid-svg-icons"
@ -8,7 +8,7 @@ import Style from "./BoxConditionUser.module.css"
import ButtonIconOnly from "../base/ButtonIconOnly" import ButtonIconOnly from "../base/ButtonIconOnly"
import useRepositoryEditor from "../../hooks/useRepositoryEditor" import useRepositoryEditor from "../../hooks/useRepositoryEditor"
import Condition from "../../utils/Condition" import Condition from "../../utils/Condition"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
const INVALID_USER_CHARACTERS = /[^a-zA-Z0-9]/g const INVALID_USER_CHARACTERS = /[^a-zA-Z0-9]/g
@ -25,6 +25,7 @@ const INVALID_USER_CHARACTERS = /[^a-zA-Z0-9]/g
export default function BoxConditionUser({ ...props }) { export default function BoxConditionUser({ ...props }) {
const [user, setUser] = useState("") const [user, setUser] = useState("")
const { addCondition } = useRepositoryEditor() const { addCondition } = useRepositoryEditor()
const {strings} = useContext(ContextLanguage)
const onInputChange = event => { const onInputChange = event => {
let text = event.target.value let text = event.target.value
@ -44,11 +45,11 @@ export default function BoxConditionUser({ ...props }) {
<BoxFull <BoxFull
header={ header={
<span> <span>
{Localization.searchBy} {strings.searchBy}
&nbsp; &nbsp;
<FontAwesomeIcon icon={faAt}/> <FontAwesomeIcon icon={faAt}/>
&nbsp; &nbsp;
{Localization.byUser} {strings.byUser}
</span> </span>
} }
{...props} {...props}

View file

@ -1,8 +1,8 @@
import React from "react" import React, { useContext } from "react"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import ConditionBadge from "./ConditionBadge" import ConditionBadge from "./ConditionBadge"
import useRepositoryEditor from "../../hooks/useRepositoryEditor" import useRepositoryEditor from "../../hooks/useRepositoryEditor"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -14,11 +14,12 @@ import Localization from "../../Localization"
*/ */
export default function BoxConditions({ ...props }) { export default function BoxConditions({ ...props }) {
const { conditions } = useRepositoryEditor() const { conditions } = useRepositoryEditor()
const {strings} = useContext(ContextLanguage)
const badges = conditions.map((cond, pos) => <ConditionBadge key={pos} {...cond}/>) const badges = conditions.map((cond, pos) => <ConditionBadge key={pos} {...cond}/>)
return ( return (
<BoxFull header={Localization.conditions} {...props}> <BoxFull header={strings.conditions} {...props}>
{badges} {badges}
</BoxFull> </BoxFull>
) )

View file

@ -7,7 +7,7 @@ import ContextUser from "../../contexts/ContextUser"
import { useHistory } from "react-router" import { useHistory } from "react-router"
import Style from "./BoxLoggedIn.module.css" import Style from "./BoxLoggedIn.module.css"
import CurrentServer from "./CurrentServer" import CurrentServer from "./CurrentServer"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -20,12 +20,13 @@ import Localization from "../../Localization"
export default function BoxLoggedIn({ ...props }) { export default function BoxLoggedIn({ ...props }) {
const { logout } = useContext(ContextUser) const { logout } = useContext(ContextUser)
const history = useHistory() const history = useHistory()
const {strings} = useContext(ContextLanguage)
return ( return (
<BoxFull header={Localization.loggedInTitle} {...props}> <BoxFull header={strings.loggedInTitle} {...props}>
<div className={Style.BoxLoggedInContents}> <div className={Style.BoxLoggedInContents}>
<div> <div>
{Localization.loggedInOn} <CurrentServer/> {Localization.loggedInAs} <LoggedInUser/>. {strings.loggedInOn} <CurrentServer/> {strings.loggedInAs} <LoggedInUser/>.
</div> </div>
<div> <div>
<Button <Button
@ -33,7 +34,7 @@ export default function BoxLoggedIn({ ...props }) {
logout() logout()
history.push("/login") history.push("/login")
}} icon={faSignOutAlt} }} icon={faSignOutAlt}
>{Localization.logout}</Button> >{strings.logout}</Button>
</div> </div>
</div> </div>
</BoxFull> </BoxFull>

View file

@ -8,7 +8,7 @@ import FormButton from "../base/formparts/FormButton"
import ContextUser from "../../contexts/ContextUser" import ContextUser from "../../contexts/ContextUser"
import { useHistory } from "react-router" import { useHistory } from "react-router"
import FormAlert from "../base/formparts/FormAlert" import FormAlert from "../base/formparts/FormAlert"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -25,6 +25,7 @@ export default function BoxLogin({ ...props }) {
const [error, setError] = useState(null) const [error, setError] = useState(null)
const { login } = useContext(ContextUser) const { login } = useContext(ContextUser)
const history = useHistory() const history = useHistory()
const {strings} = useContext(ContextLanguage)
const doLogin = async () => { const doLogin = async () => {
if(working) { if(working) {
@ -45,9 +46,9 @@ export default function BoxLogin({ ...props }) {
} }
return ( return (
<BoxFull header={Localization.login} {...props}> <BoxFull header={strings.login} {...props}>
<FormLabelled> <FormLabelled>
<FormLabel text={Localization.email} htmlFor={"login-email"}> <FormLabel text={strings.email} htmlFor={"login-email"}>
<InputWithIcon <InputWithIcon
id={"login-email"} id={"login-email"}
name={"login-email"} name={"login-email"}
@ -57,7 +58,7 @@ export default function BoxLogin({ ...props }) {
icon={faEnvelope} icon={faEnvelope}
/> />
</FormLabel> </FormLabel>
<FormLabel text={Localization.passwd} htmlFor={"login-password"}> <FormLabel text={strings.passwd} htmlFor={"login-password"}>
<InputWithIcon <InputWithIcon
id={"login-password"} id={"login-password"}
name={"login-password"} name={"login-password"}
@ -78,7 +79,7 @@ export default function BoxLogin({ ...props }) {
color={"Green"} color={"Green"}
disabled={working} disabled={working}
> >
{Localization.login} {strings.login}
</FormButton> </FormButton>
</FormLabelled> </FormLabelled>
</BoxFull> </BoxFull>

View file

@ -4,7 +4,7 @@ import { faFolderOpen } from "@fortawesome/free-solid-svg-icons"
import ContextUser from "../../contexts/ContextUser" import ContextUser from "../../contexts/ContextUser"
import Loading from "../base/Loading" import Loading from "../base/Loading"
import BoxFullScrollable from "../base/BoxFullScrollable" import BoxFullScrollable from "../base/BoxFullScrollable"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
* A {@link BoxFull} listing all the user's active repositories. * A {@link BoxFull} listing all the user's active repositories.
@ -25,13 +25,14 @@ export default function BoxRepositoriesActive({
...props ...props
}) { }) {
const { user } = useContext(ContextUser) const { user } = useContext(ContextUser)
const {strings} = useContext(ContextLanguage)
let contents let contents
if(repositories === null) { if(repositories === null) {
contents = <Loading/> contents = <Loading/>
} }
else if(repositories.length === 0) { else if(repositories.length === 0) {
contents = <i>{Localization.emptyMenu}.</i> contents = <i>{strings.emptyMenu}.</i>
} }
else { else {
contents = repositories.map(repo => ( contents = repositories.map(repo => (
@ -50,7 +51,7 @@ export default function BoxRepositoriesActive({
} }
return ( return (
<BoxFullScrollable header={Localization.menuActive} {...props}> <BoxFullScrollable header={strings.menuActive} {...props}>
{contents} {contents}
</BoxFullScrollable> </BoxFullScrollable>
) )

View file

@ -4,7 +4,7 @@ import { faFolderOpen } from "@fortawesome/free-solid-svg-icons"
import ContextUser from "../../contexts/ContextUser" import ContextUser from "../../contexts/ContextUser"
import Loading from "../base/Loading" import Loading from "../base/Loading"
import BoxFullScrollable from "../base/BoxFullScrollable" import BoxFullScrollable from "../base/BoxFullScrollable"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -26,13 +26,14 @@ export default function BoxRepositoriesArchived({
...props ...props
}) { }) {
const { user } = useContext(ContextUser) const { user } = useContext(ContextUser)
const {strings} = useContext(ContextLanguage)
let contents let contents
if(repositories === null) { if(repositories === null) {
contents = <Loading/> contents = <Loading/>
} }
else if(repositories.length === 0) { else if(repositories.length === 0) {
contents = <i>{Localization.emptyMenu}.</i> contents = <i>{strings.emptyMenu}.</i>
} }
else { else {
contents = repositories.map(repo => ( contents = repositories.map(repo => (
@ -51,7 +52,7 @@ export default function BoxRepositoriesArchived({
} }
return ( return (
<BoxFullScrollable header={Localization.menuArchived} {...props}> <BoxFullScrollable header={strings.menuArchived} {...props}>
{contents} {contents}
</BoxFullScrollable> </BoxFullScrollable>
) )

View file

@ -1,4 +1,4 @@
import React from "react" import React, { useContext } from "react"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import FormLabelled from "../base/FormLabelled" import FormLabelled from "../base/FormLabelled"
import FormLabel from "../base/formparts/FormLabel" import FormLabel from "../base/formparts/FormLabel"
@ -10,7 +10,7 @@ import useRepositoryEditor from "../../hooks/useRepositoryEditor"
import FormAlert from "../base/formparts/FormAlert" import FormAlert from "../base/formparts/FormAlert"
import goToOnSuccess from "../../utils/goToOnSuccess" import goToOnSuccess from "../../utils/goToOnSuccess"
import { useHistory } from "react-router" import { useHistory } from "react-router"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -34,16 +34,17 @@ export default function BoxRepositoryCreate({ running, ...props }) {
} = useRepositoryEditor() } = useRepositoryEditor()
const history = useHistory() const history = useHistory()
const {strings} = useContext(ContextLanguage)
return ( return (
<BoxFull header={Localization.createRepo} {...props}> <BoxFull header={strings.createRepo} {...props}>
<FormLabelled <FormLabelled
onSubmit={e => { onSubmit={e => {
e.preventDefault() e.preventDefault()
save() save()
}} }}
> >
<FormLabel htmlFor={"repo-name"} text={Localization.repoName}> <FormLabel htmlFor={"repo-name"} text={strings.repoName}>
<InputWithIcon <InputWithIcon
id={"repo-name"} id={"repo-name"}
icon={faFolder} icon={faFolder}
@ -51,14 +52,14 @@ export default function BoxRepositoryCreate({ running, ...props }) {
onChange={e => setName(e.target.value)} onChange={e => setName(e.target.value)}
/> />
</FormLabel> </FormLabel>
<FormLabel htmlFor={"filter-mode"} text={Localization.request}> <FormLabel htmlFor={"filter-mode"} text={strings.request}>
<label> <label>
<Radio <Radio
name={"filter-mode"} name={"filter-mode"}
onChange={() => setEvaluationMode(0)} onChange={() => setEvaluationMode(0)}
checked={evaluationMode === 0} checked={evaluationMode === 0}
/> />
{Localization.filterOR} {strings.filterOR}
</label> </label>
&nbsp; &nbsp;
<label> <label>
@ -67,7 +68,7 @@ export default function BoxRepositoryCreate({ running, ...props }) {
onChange={() => setEvaluationMode(1)} onChange={() => setEvaluationMode(1)}
checked={evaluationMode === 1} checked={evaluationMode === 1}
/> />
{Localization.filterAND} {strings.filterAND}
</label> </label>
</FormLabel> </FormLabel>
{error ? {error ?
@ -84,7 +85,7 @@ export default function BoxRepositoryCreate({ running, ...props }) {
onClick={() => revert()} onClick={() => revert()}
disabled={running} disabled={running}
> >
{Localization.rollback} {strings.rollback}
</Button> </Button>
<Button <Button
style={{ "gridColumn": "2" }} style={{ "gridColumn": "2" }}
@ -93,7 +94,7 @@ export default function BoxRepositoryCreate({ running, ...props }) {
onClick={_ => goToOnSuccess(save, history, "/repositories")()} onClick={_ => goToOnSuccess(save, history, "/repositories")()}
disabled={running} disabled={running}
> >
{Localization.save} {strings.save}
</Button> </Button>
</> </>
: :
@ -104,7 +105,7 @@ export default function BoxRepositoryCreate({ running, ...props }) {
onClick={_ => goToOnSuccess(save, history, "/repositories")()} onClick={_ => goToOnSuccess(save, history, "/repositories")()}
disabled={running} disabled={running}
> >
{Localization.createRepo} {strings.createRepo}
</Button> </Button>
} }

View file

@ -5,7 +5,7 @@ import FormLabel from "../base/formparts/FormLabel"
import InputWithIcon from "../base/InputWithIcon" import InputWithIcon from "../base/InputWithIcon"
import { faGlobe } from "@fortawesome/free-solid-svg-icons" import { faGlobe } from "@fortawesome/free-solid-svg-icons"
import ContextServer from "../../contexts/ContextServer" import ContextServer from "../../contexts/ContextServer"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -17,11 +17,12 @@ import Localization from "../../Localization"
*/ */
export default function BoxSetServer({ ...props }) { export default function BoxSetServer({ ...props }) {
const { server, setServer } = useContext(ContextServer) const { server, setServer } = useContext(ContextServer)
const {strings} = useContext(ContextLanguage)
return ( return (
<BoxFull header={Localization.server} {...props}> <BoxFull header={strings.server} {...props}>
<FormLabelled> <FormLabelled>
<FormLabel text={Localization.baseURL} htmlFor={"set-server-base-url"}> <FormLabel text={strings.baseURL} htmlFor={"set-server-base-url"}>
<InputWithIcon <InputWithIcon
id={"set-server-base-url"} id={"set-server-base-url"}
type={"url"} type={"url"}

View file

@ -1,4 +1,4 @@
import React, { useCallback, useState } from "react" import React, { useCallback, useContext, useState } from "react"
import FormLabelled from "../base/FormLabelled" import FormLabelled from "../base/FormLabelled"
import FormLabel from "../base/formparts/FormLabel" import FormLabel from "../base/formparts/FormLabel"
import InputWithIcon from "../base/InputWithIcon" import InputWithIcon from "../base/InputWithIcon"
@ -6,7 +6,7 @@ import { faEnvelope, faKey, faPlus, faUser } from "@fortawesome/free-solid-svg-i
import FormButton from "../base/formparts/FormButton" import FormButton from "../base/formparts/FormButton"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import FormAlert from "../base/formparts/FormAlert" import FormAlert from "../base/formparts/FormAlert"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
export default function BoxUserCreate({ createUser, running, ...props }) { export default function BoxUserCreate({ createUser, running, ...props }) {
@ -14,6 +14,7 @@ export default function BoxUserCreate({ createUser, running, ...props }) {
const [email, setEmail] = useState("") const [email, setEmail] = useState("")
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [error, setError] = useState(undefined) const [error, setError] = useState(undefined)
const {strings} = useContext(ContextLanguage)
const onButtonClick = useCallback( const onButtonClick = useCallback(
async () => { async () => {
@ -28,9 +29,9 @@ export default function BoxUserCreate({ createUser, running, ...props }) {
) )
return ( return (
<BoxFull header={Localization.userCreate} {...props}> <BoxFull header={strings.userCreate} {...props}>
<FormLabelled> <FormLabelled>
<FormLabel text={Localization.userName}> <FormLabel text={strings.userName}>
<InputWithIcon <InputWithIcon
icon={faUser} icon={faUser}
type={"text"} type={"text"}
@ -38,7 +39,7 @@ export default function BoxUserCreate({ createUser, running, ...props }) {
onChange={event => setUsername(event.target.value)} onChange={event => setUsername(event.target.value)}
/> />
</FormLabel> </FormLabel>
<FormLabel text={Localization.email}> <FormLabel text={strings.email}>
<InputWithIcon <InputWithIcon
icon={faEnvelope} icon={faEnvelope}
type={"text"} type={"text"}
@ -46,7 +47,7 @@ export default function BoxUserCreate({ createUser, running, ...props }) {
onChange={event => setEmail(event.target.value)} onChange={event => setEmail(event.target.value)}
/> />
</FormLabel> </FormLabel>
<FormLabel text={Localization.passwd}> <FormLabel text={strings.passwd}>
<InputWithIcon <InputWithIcon
icon={faKey} icon={faKey}
type={"password"} type={"password"}
@ -65,7 +66,7 @@ export default function BoxUserCreate({ createUser, running, ...props }) {
onClick={onButtonClick} onClick={onButtonClick}
disabled={running} disabled={running}
> >
{Localization.create} {strings.create}
</FormButton> </FormButton>
</FormLabelled> </FormLabelled>
</BoxFull> </BoxFull>

View file

@ -1,11 +1,13 @@
import React from "react" import React, { useContext } from "react"
import Loading from "../base/Loading" import Loading from "../base/Loading"
import BoxFullScrollable from "../base/BoxFullScrollable" import BoxFullScrollable from "../base/BoxFullScrollable"
import SummaryUser from "./SummaryUser" import SummaryUser from "./SummaryUser"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
export default function BoxUserList({ users, destroyUser, running, ...props }) { export default function BoxUserList({ users, destroyUser, running, ...props }) {
const {strings} = useContext(ContextLanguage)
let contents let contents
if(users === null) { if(users === null) {
contents = <Loading/> contents = <Loading/>
@ -16,7 +18,7 @@ export default function BoxUserList({ users, destroyUser, running, ...props }) {
} }
return ( return (
<BoxFullScrollable header={Localization.userList} {...props}> <BoxFullScrollable header={strings.userList} {...props}>
{contents} {contents}
</BoxFullScrollable> </BoxFullScrollable>
) )

View file

@ -1,7 +1,7 @@
import React from "react" import React, { useContext } from "react"
import BoxFull from "../base/BoxFull" import BoxFull from "../base/BoxFull"
import ReactWordcloud from "@steffo/nest-react-wordcloud" import ReactWordcloud from "@steffo/nest-react-wordcloud"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -13,8 +13,10 @@ import Localization from "../../Localization"
* @constructor * @constructor
*/ */
export default function BoxWordcloud({ words, props }) { export default function BoxWordcloud({ words, props }) {
const {strings} = useContext(ContextLanguage)
return ( return (
<BoxFull header={Localization.wordcloud} {...props}> <BoxFull header={strings.wordcloud} {...props}>
<div style={{"width": "100%", "height": "100%"}}> <div style={{"width": "100%", "height": "100%"}}>
<ReactWordcloud <ReactWordcloud
options={{ options={{

View file

@ -1,12 +1,13 @@
import React, { useState } from "react" import React, { useContext, useState } from "react"
import Style from "./ButtonToggleBeforeAfter.module.css" import Style from "./ButtonToggleBeforeAfter.module.css"
import classNames from "classnames" import classNames from "classnames"
import Button from "../base/Button" import Button from "../base/Button"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
export default function ButtonToggleBeforeAfter({ onUpdate, className, ...props }) { export default function ButtonToggleBeforeAfter({ onUpdate, className, ...props }) {
const [value, setValue] = useState(false) const [value, setValue] = useState(false)
const {strings} = useContext(ContextLanguage)
const onButtonClick = () => { const onButtonClick = () => {
onUpdate(!value) onUpdate(!value)
@ -20,7 +21,7 @@ export default function ButtonToggleBeforeAfter({ onUpdate, className, ...props
onClick={onButtonClick} onClick={onButtonClick}
{...props} {...props}
> >
{value ? Localization.timeBefore : Localization.timeAfter} {value ? strings.timeBefore : strings.timeAfter}
</Button> </Button>
) )
} }

View file

@ -2,7 +2,7 @@ import React, { useContext } from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faUser } from "@fortawesome/free-solid-svg-icons" import { faUser } from "@fortawesome/free-solid-svg-icons"
import ContextUser from "../../contexts/ContextUser" import ContextUser from "../../contexts/ContextUser"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -14,11 +14,12 @@ import Localization from "../../Localization"
*/ */
export default function LoggedInUser({ ...props }) { export default function LoggedInUser({ ...props }) {
const { user } = useContext(ContextUser) const { user } = useContext(ContextUser)
const {strings} = useContext(ContextLanguage)
if(!user) { if(!user) {
return ( return (
<i {...props}> <i {...props}>
{Localization.notLoggedIn} {strings.notLoggedIn}
</i> </i>
) )
} }

View file

@ -4,7 +4,7 @@ import LogoDark from "../../media/LogoDark.png"
import LogoLight from "../../media/LogoLight.png" import LogoLight from "../../media/LogoLight.png"
import ContextTheme from "../../contexts/ContextTheme" import ContextTheme from "../../contexts/ContextTheme"
import classNames from "classnames" import classNames from "classnames"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -18,9 +18,8 @@ import Localization from "../../Localization"
* @constructor * @constructor
*/ */
export default function Logo({ className, ...props }) { export default function Logo({ className, ...props }) {
// I have no idea why IntelliJ is complaining about this line
// It's perfectly fine!
const { theme } = useContext(ContextTheme) const { theme } = useContext(ContextTheme)
const {strings} = useContext(ContextLanguage)
let logo let logo
if(theme === "ThemeDark") { if(theme === "ThemeDark") {
@ -37,8 +36,8 @@ export default function Logo({ className, ...props }) {
<img <img
src={logo} src={logo}
className={classNames(Style.Logo, className)} className={classNames(Style.Logo, className)}
alt={Localization.appName} alt={strings.appName}
title={Localization.appFullName} title={strings.appFullName}
{...props} {...props}
/> />
) )

View file

@ -1,6 +1,6 @@
import React, { useState } from "react" import React, { useContext } from "react"
import Select from "../base/Select" import Select from "../base/Select"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -12,17 +12,10 @@ import Localization from "../../Localization"
* @constructor * @constructor
*/ */
export default function SelectLanguage({ ...props }) { export default function SelectLanguage({ ...props }) {
const [_language, _setLanguage] = useState(Localization.getLanguage()) const {strings, lang, setLang} = useContext(ContextLanguage)
const setLanguage = event => {
const language = event.target.value
console.info("Changing language to: ", language)
Localization.setLanguage(language)
_setLanguage(language)
}
return ( return (
<Select value={_language} onChange={setLanguage} {...props}> <Select value={lang} onChange={event => setLang(event.target.value)} {...props}>
<option value={"it"}>🇮🇹 Italiano</option> <option value={"it"}>🇮🇹 Italiano</option>
<option value={"en"}>🇬🇧 English</option> <option value={"en"}>🇬🇧 English</option>
<option value={"fi"}>🇫🇮 Suomi</option> <option value={"fi"}>🇫🇮 Suomi</option>

View file

@ -1,7 +1,7 @@
import React, { useContext } from "react" import React, { useContext } from "react"
import Select from "../base/Select" import Select from "../base/Select"
import ContextTheme from "../../contexts/ContextTheme" import ContextTheme from "../../contexts/ContextTheme"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -14,11 +14,12 @@ import Localization from "../../Localization"
*/ */
export default function SelectTheme({ ...props }) { export default function SelectTheme({ ...props }) {
const { theme, setTheme } = useContext(ContextTheme) const { theme, setTheme } = useContext(ContextTheme)
const {strings} = useContext(ContextLanguage)
return ( return (
<Select value={theme} onChange={e => setTheme(e.target.value)} {...props}> <Select value={theme} onChange={e => setTheme(e.target.value)} {...props}>
<option value={"ThemeDark"}>{Localization.darkMode}</option> <option value={"ThemeDark"}>{strings.darkMode}</option>
<option value={"ThemeLight"}>{Localization.lightMode}</option> <option value={"ThemeLight"}>{strings.lightMode}</option>
</Select> </Select>
) )
} }

View file

@ -12,7 +12,7 @@ import {
faUserCog, faUserCog,
} from "@fortawesome/free-solid-svg-icons" } from "@fortawesome/free-solid-svg-icons"
import ContextUser from "../../contexts/ContextUser" import ContextUser from "../../contexts/ContextUser"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -26,6 +26,7 @@ import Localization from "../../Localization"
*/ */
export default function Sidebar({ className, ...props }) { export default function Sidebar({ className, ...props }) {
const { user } = useContext(ContextUser) const { user } = useContext(ContextUser)
const {strings} = useContext(ContextLanguage)
return ( return (
<aside className={classNames(Style.Sidebar, className)} {...props}> <aside className={classNames(Style.Sidebar, className)} {...props}>
@ -33,20 +34,20 @@ export default function Sidebar({ className, ...props }) {
{ {
user ? user ?
<> <>
<ButtonSidebar to={"/dashboard"} icon={faHome}>{Localization.dashboard}</ButtonSidebar> <ButtonSidebar to={"/dashboard"} icon={faHome}>{strings.dashboard}</ButtonSidebar>
<ButtonSidebar to={"/repositories"} icon={faFolder}>{Localization.repositories}</ButtonSidebar> <ButtonSidebar to={"/repositories"} icon={faFolder}>{strings.repositories}</ButtonSidebar>
<ButtonSidebar to={"/alerts"} icon={faExclamationTriangle}>{Localization.alerts}</ButtonSidebar> <ButtonSidebar to={"/alerts"} icon={faExclamationTriangle}>{strings.alerts}</ButtonSidebar>
<ButtonSidebar to={"/settings"} icon={faCog}>{Localization.settings}</ButtonSidebar> <ButtonSidebar to={"/settings"} icon={faCog}>{strings.settings}</ButtonSidebar>
</> </>
: :
<> <>
<ButtonSidebar to={"/login"} icon={faKey}>{Localization.login}</ButtonSidebar> <ButtonSidebar to={"/login"} icon={faKey}>{strings.login}</ButtonSidebar>
</> </>
} }
{ {
user && user.isAdmin ? user && user.isAdmin ?
<> <>
<ButtonSidebar to={"/users"} icon={faUserCog}>{Localization.users}</ButtonSidebar> <ButtonSidebar to={"/users"} icon={faUserCog}>{strings.users}</ButtonSidebar>
</> </>
: :
null null

View file

@ -1,9 +1,9 @@
import React from "react" import React, { useContext } from "react"
import Button from "../base/Button" import Button from "../base/Button"
import { faArchive, faFolder, faFolderOpen, faPencilAlt, faTrash } from "@fortawesome/free-solid-svg-icons" import { faArchive, faFolder, faFolderOpen, faPencilAlt, faTrash } from "@fortawesome/free-solid-svg-icons"
import { useHistory } from "react-router" import { useHistory } from "react-router"
import Summary from "../base/Summary" import Summary from "../base/Summary"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
/** /**
@ -26,6 +26,7 @@ export default function SummaryRepository(
{ repo, refresh, canDelete, deleteSelf, canEdit, canArchive, archiveSelf, running, className, ...props }, { repo, refresh, canDelete, deleteSelf, canEdit, canArchive, archiveSelf, running, className, ...props },
) { ) {
const history = useHistory() const history = useHistory()
const {strings} = useContext(ContextLanguage)
const onRepoClick = () => { const onRepoClick = () => {
history.push(`/repositories/${repo.id}`) history.push(`/repositories/${repo.id}`)
@ -43,7 +44,7 @@ export default function SummaryRepository(
onClick={deleteSelf} onClick={deleteSelf}
disabled={running} disabled={running}
> >
{Localization.delete} {strings.delete}
</Button> </Button>
: null} : null}
{canEdit ? {canEdit ?
@ -53,7 +54,7 @@ export default function SummaryRepository(
onClick={onEditClick} onClick={onEditClick}
disabled={running} disabled={running}
> >
{Localization.Edit} {strings.edit}
</Button> </Button>
: null} : null}
{canArchive ? {canArchive ?
@ -63,7 +64,7 @@ export default function SummaryRepository(
onClick={archiveSelf} onClick={archiveSelf}
disabled={running} disabled={running}
> >
{Localization.achive} {strings.archive}
</Button> </Button>
: null} : null}
</> </>
@ -74,9 +75,9 @@ export default function SummaryRepository(
title={repo.name} title={repo.name}
subtitle={repo.owner ? repo.owner.username : null} subtitle={repo.owner ? repo.owner.username : null}
onClick={onRepoClick} onClick={onRepoClick}
upperLabel={Localization.created} upperLabel={strings.created}
upperValue={repo.start ? new Date(repo.start).toLocaleString() : null} upperValue={repo.start ? new Date(repo.start).toLocaleString() : null}
lowerLabel={Localization.archived} lowerLabel={strings.archived}
lowerValue={repo.end ? new Date(repo.end).toLocaleString() : null} lowerValue={repo.end ? new Date(repo.end).toLocaleString() : null}
buttons={buttons} buttons={buttons}
{...props} {...props}

View file

@ -3,11 +3,12 @@ import Summary from "../base/Summary"
import { faStar, faTrash, faUser } from "@fortawesome/free-solid-svg-icons" import { faStar, faTrash, faUser } from "@fortawesome/free-solid-svg-icons"
import Button from "../base/Button" import Button from "../base/Button"
import ContextUser from "../../contexts/ContextUser" import ContextUser from "../../contexts/ContextUser"
import Localization from "../../Localization" import ContextLanguage from "../../contexts/ContextLanguage"
export default function SummaryUser({ user, destroyUser, running, ...props }) { export default function SummaryUser({ user, destroyUser, running, ...props }) {
const { user: loggedUser } = useContext(ContextUser) const { user: loggedUser } = useContext(ContextUser)
const {strings} = useContext(ContextLanguage)
const buttons = <> const buttons = <>
{loggedUser.email !== user.email ? {loggedUser.email !== user.email ?
@ -21,7 +22,7 @@ export default function SummaryUser({ user, destroyUser, running, ...props }) {
}} }}
disabled={running} disabled={running}
> >
{Localization.delete} {strings.delete}
</Button> </Button>
: null} : null}
</> </>
@ -31,8 +32,8 @@ export default function SummaryUser({ user, destroyUser, running, ...props }) {
icon={user.isAdmin ? faStar : faUser} icon={user.isAdmin ? faStar : faUser}
title={user.username} title={user.username}
subtitle={user.email} subtitle={user.email}
upperLabel={Localization.type} upperLabel={strings.type}
upperValue={user.isAdmin ? Localization.admin : Localization.user} upperValue={user.isAdmin ? strings.admin : strings.user}
buttons={buttons} buttons={buttons}
{...props} {...props}
/> />

View file

@ -0,0 +1,26 @@
import React from "react"
import useLocalStorageState from "../../hooks/useLocalStorageState"
import ContextLanguage from "../../contexts/ContextLanguage"
import LocalizationStrings from "../../LocalizationStrings"
/**
* Provides {@link ContextLanguage} to all contained elements.
*
* Defaults to using Italian.
*
* @param children
* @returns {JSX.Element}
* @constructor
*/
export default function GlobalLanguage({ children }) {
const [lang, setLang] = useLocalStorageState("language", "it")
const strings = LocalizationStrings[lang]
return (
<ContextLanguage.Provider value={{ lang, setLang, strings }}>
{children}
</ContextLanguage.Provider>
)
}

View file

@ -0,0 +1,14 @@
import { createContext } from "react"
/**
* A context containing an object with the following values:
* - `lang`: a string corresponding to the ISO 639-1 code of the current language
* - `setLang`: a function to change the current language
* - `strings`: an object containing all strings of the current language
*/
export default createContext({
lang: null,
setLang: () => console.error("Trying to setLang while outside a ContextServer.Provider!"),
strings: null,
})

View file

@ -1,10 +1,13 @@
import React from "react" import React, { useContext } from "react"
import Style from "./PageAlerts.module.css" import Style from "./PageAlerts.module.css"
import classNames from "classnames" import classNames from "classnames"
import BoxFull from "../components/base/BoxFull" import BoxFull from "../components/base/BoxFull"
import ContextLanguage from "../contexts/ContextLanguage"
export default function PageAlerts({ children, className, ...props }) { export default function PageAlerts({ children, className, ...props }) {
const {strings} = useContext(ContextLanguage)
return ( return (
<div className={classNames(Style.PageAlerts, className)} {...props}> <div className={classNames(Style.PageAlerts, className)} {...props}>
<BoxFull header={"I tuoi allarmi"} className={Style.YourAlerts}> <BoxFull header={"I tuoi allarmi"} className={Style.YourAlerts}>

View file

@ -1,11 +1,14 @@
import React from "react" import React, { useContext } from "react"
import Style from "./PageDashboard.module.css" import Style from "./PageDashboard.module.css"
import classNames from "classnames" import classNames from "classnames"
import BoxHeader from "../components/base/BoxHeader" import BoxHeader from "../components/base/BoxHeader"
import RepositoryEditor from "../components/providers/RepositoryEditor" import RepositoryEditor from "../components/providers/RepositoryEditor"
import ContextLanguage from "../contexts/ContextLanguage"
export default function PageDashboard({ children, className, ...props }) { export default function PageDashboard({ children, className, ...props }) {
const {strings} = useContext(ContextLanguage)
return ( return (
<div className={classNames(Style.PageHome, className)} {...props}> <div className={classNames(Style.PageHome, className)} {...props}>
<BoxHeader className={Style.Header}> <BoxHeader className={Style.Header}>