Fix date formatting on language change
This commit is contained in:
parent
ed3433f51b
commit
11bbad49fd
@ -46,6 +46,7 @@
|
||||
"react-dev-utils": "^10.2.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-i18next": "^11.7.0",
|
||||
"react-moment": "^0.9.7",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"redux": "^4.0.5",
|
||||
|
42
src/app.tsx
42
src/app.tsx
@ -1,23 +1,27 @@
|
||||
import React, { Dispatch, HTMLProps, useEffect } from 'react';
|
||||
import React, { HTMLProps, useEffect } from 'react';
|
||||
import { Link, Route, Switch } from "react-router-dom"
|
||||
import moment from "moment";
|
||||
import { route, routes } from "@/routing";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppState } from "@/state/reducer";
|
||||
import { StudentAction, StudentActions } from "@/state/actions/student";
|
||||
import { useSelector } from "react-redux";
|
||||
import { AppState, isReady } from "@/state/reducer";
|
||||
import { StudentActions } from "@/state/actions/student";
|
||||
import { sampleStudent } from "@/provider/dummy/student";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Student } from "@/data";
|
||||
import '@/styles/overrides.scss'
|
||||
import '@/styles/header.scss'
|
||||
import classNames from "classnames";
|
||||
import { EditionAction, EditionActions } from "@/state/actions/edition";
|
||||
import { EditionActions } from "@/state/actions/edition";
|
||||
import { sampleEdition } from "@/provider/dummy/edition";
|
||||
import { Edition } from "@/data/edition";
|
||||
import { SettingActions } from "@/state/actions/settings";
|
||||
import { useDispatch } from "@/state/actions";
|
||||
import { getLocale, Locale } from "@/state/reducer/settings";
|
||||
import i18n from "@/i18n";
|
||||
import moment from "moment";
|
||||
|
||||
const UserMenu = (props: HTMLProps<HTMLUListElement>) => {
|
||||
const student = useSelector<AppState, Student>(state => state.student as Student);
|
||||
const dispatch = useDispatch<Dispatch<StudentAction>>();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleUserLogin = () => {
|
||||
@ -47,17 +51,17 @@ const UserMenu = (props: HTMLProps<HTMLUListElement>) => {
|
||||
const LanguageSwitcher = ({ className, ...props }: HTMLProps<HTMLUListElement>) => {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const handleLanguageChange = (language: string) => () => {
|
||||
i18n.changeLanguage(language);
|
||||
document.documentElement.lang = language;
|
||||
moment.locale(language)
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleLanguageChange = (language: Locale) => () => {
|
||||
dispatch({ type: SettingActions.SetLocale, locale: language })
|
||||
}
|
||||
|
||||
const isActive = (language: string) => language.toLowerCase() === i18n.language.toLowerCase();
|
||||
|
||||
return <ul className={ classNames(className, "language-switcher") } { ...props }>
|
||||
{ ['pl', 'en'].map(language => <li key={ language }>
|
||||
<Link to="#" onClick={ handleLanguageChange(language) }
|
||||
<Link to="#" onClick={ handleLanguageChange(language as Locale) }
|
||||
className={ classNames("language-switcher__language", isActive(language) && "language-switcher__language--active") }>
|
||||
{ language }
|
||||
</Link>
|
||||
@ -66,16 +70,24 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps<HTMLUListElement>)
|
||||
}
|
||||
|
||||
function App() {
|
||||
const dispatch = useDispatch<Dispatch<EditionAction>>();
|
||||
const dispatch = useDispatch();
|
||||
const edition = useSelector<AppState, Edition | null>(state => state.edition);
|
||||
|
||||
const locale = useSelector<AppState, Locale>(state => getLocale(state.settings));
|
||||
|
||||
useEffect(() => {
|
||||
if (!edition) {
|
||||
dispatch({ type: EditionActions.Set, edition: sampleEdition });
|
||||
}
|
||||
})
|
||||
|
||||
const isReady = !!edition;
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(locale);
|
||||
document.documentElement.lang = locale;
|
||||
moment.locale(locale)
|
||||
})
|
||||
|
||||
const ready = useSelector(isReady);
|
||||
|
||||
return <>
|
||||
<header className="header">
|
||||
@ -96,7 +108,7 @@ function App() {
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
{ isReady && <Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch> }
|
||||
{ ready && <Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch> }
|
||||
</>;
|
||||
}
|
||||
|
||||
|
18
src/i18n.ts
18
src/i18n.ts
@ -2,10 +2,9 @@ import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import "moment/locale/pl"
|
||||
import "moment/locale/en-gb"
|
||||
import moment, { isDuration, isMoment } from "moment";
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
@ -21,9 +20,20 @@ i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: "en",
|
||||
fallbackLng: "pl",
|
||||
interpolation: {
|
||||
escapeValue: false
|
||||
escapeValue: false,
|
||||
format: (value, format, lng) => {
|
||||
if (isMoment(value)) {
|
||||
return value.locale(lng || "pl").format(format || "DD MMM YYYY");
|
||||
}
|
||||
|
||||
if (isDuration(value)) {
|
||||
return value.locale(lng || "pl").humanize();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -31,10 +31,10 @@ const Step = ({ until, label, completedOn, children, completed, ...props }: Step
|
||||
{ label }
|
||||
{ until && <Box>
|
||||
<Typography variant="subtitle2" color="textSecondary">
|
||||
{ t('until', { date: until.format("DD MMMM YYYY") }) }
|
||||
{ t('until', { date: until }) }
|
||||
{ isLate && <Typography color="error" display="inline"
|
||||
variant="body2"> - { t('late', { by: moment.duration(now.diff(until)).humanize() }) }</Typography> }
|
||||
{ !isLate && !completed && <Typography display="inline" variant="body2"> - { t('left', { left: left.humanize() }) }</Typography> }
|
||||
variant="body2"> - { t('late', { by: moment.duration(now.diff(until)) }) }</Typography> }
|
||||
{ !isLate && !completed && <Typography display="inline" variant="body2"> - { t('left', { left: left }) }</Typography> }
|
||||
</Typography>
|
||||
</Box> }
|
||||
</StepLabel>
|
||||
|
@ -0,0 +1,20 @@
|
||||
import { StudentAction, StudentActions } from "@/state/actions/student";
|
||||
import { EditionAction, EditionActions } from "@/state/actions/edition";
|
||||
import { SettingActions, SettingsAction } from "@/state/actions/settings";
|
||||
import { Dispatch } from "react";
|
||||
|
||||
import { useDispatch as useReduxDispatch } from "react-redux";
|
||||
|
||||
export * from "./base"
|
||||
export * from "./edition"
|
||||
export * from "./settings"
|
||||
export * from "./student"
|
||||
|
||||
export type Action = StudentAction | EditionAction | SettingsAction;
|
||||
|
||||
export const Actions = { ...StudentActions, ...EditionActions, ...SettingActions }
|
||||
export type Actions = typeof Actions;
|
||||
|
||||
export const useDispatch = () => useReduxDispatch<Dispatch<Action>>()
|
||||
|
||||
export default Actions;
|
12
src/state/actions/settings.ts
Normal file
12
src/state/actions/settings.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Action } from "@/state/actions/base";
|
||||
import { Locale } from "@/state/reducer/settings";
|
||||
|
||||
export enum SettingActions {
|
||||
SetLocale = "SET_LOCALE",
|
||||
}
|
||||
|
||||
export interface SetLocaleAction extends Action<SettingActions.SetLocale> {
|
||||
locale: Locale
|
||||
}
|
||||
|
||||
export type SettingsAction = SetLocaleAction;
|
@ -2,12 +2,16 @@ import { combineReducers } from "redux";
|
||||
|
||||
import studentReducer from "./student"
|
||||
import editionReducer from "@/state/reducer/edition";
|
||||
import settingsReducer from "@/state/reducer/settings";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
student: studentReducer,
|
||||
edition: editionReducer,
|
||||
settings: settingsReducer,
|
||||
})
|
||||
|
||||
export type AppState = ReturnType<typeof rootReducer>;
|
||||
|
||||
export default rootReducer;
|
||||
|
||||
export const isReady = (state: AppState) => !!state.edition;
|
||||
|
24
src/state/reducer/settings.ts
Normal file
24
src/state/reducer/settings.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { SettingActions, SettingsAction } from "@/state/actions/settings";
|
||||
|
||||
export type Locale = "pl" | "en"
|
||||
|
||||
export type SettingsState = {
|
||||
locale: Locale
|
||||
}
|
||||
|
||||
const defaultSettingsState: SettingsState = {
|
||||
locale: "pl",
|
||||
}
|
||||
|
||||
const settingsReducer = (state: SettingsState = defaultSettingsState, action: SettingsAction): SettingsState => {
|
||||
switch (action.type) {
|
||||
case SettingActions.SetLocale:
|
||||
return { ...state, locale: action.locale }
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default settingsReducer;
|
||||
|
||||
export const getLocale = (state: SettingsState): Locale => state.locale;
|
@ -3,9 +3,9 @@ login: login
|
||||
logout: logout
|
||||
logged-in-as: logged in as <1>{{ name }}</1>
|
||||
|
||||
until: until {{ date }}
|
||||
late: late by {{ by }}
|
||||
left: '{{ left }} left'
|
||||
until: until {{ date, DD MMMM YYYY }}
|
||||
late: late by {{ by, humanize }}
|
||||
left: '{{ left, humanize }} left'
|
||||
|
||||
dropzone: "Drag and drop a file here or click to choose"
|
||||
|
||||
|
@ -3,9 +3,9 @@ login: zaloguj się
|
||||
logout: wyloguj się
|
||||
logged-in-as: zalogowany jako <1>{{ name }}</1>
|
||||
|
||||
until: do {{ date }}
|
||||
late: '{{ by }} spóźnienia'
|
||||
left: jeszcze {{ left }}
|
||||
until: do {{ date, DD MMMM YYYY }}
|
||||
late: '{{ by, humanize }} spóźnienia'
|
||||
left: jeszcze {{ left, humanize }}
|
||||
|
||||
confirm: zatwierdź
|
||||
go-back: wstecz
|
||||
|
@ -7408,6 +7408,11 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-moment@^0.9.7:
|
||||
version "0.9.7"
|
||||
resolved "https://registry.yarnpkg.com/react-moment/-/react-moment-0.9.7.tgz#ca570466595b1aa4f7619e62da18b3bb2de8b6f3"
|
||||
integrity sha512-ifzUrUGF6KRsUN2pRG5k56kO0mJBr8kRkWb0wNvtFIsBIxOuPxhUpL1YlXwpbQCbHq23hUu6A0VEk64HsFxk9g==
|
||||
|
||||
react-redux@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
|
||||
|
Loading…
Reference in New Issue
Block a user