Fix date formatting on language change

This commit is contained in:
Kacper Donat 2020-07-25 16:18:47 +02:00 committed by Gitea
parent ed3433f51b
commit 11bbad49fd
11 changed files with 116 additions and 28 deletions

View File

@ -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",

View File

@ -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> }
</>;
}

View File

@ -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;
}
}
})

View File

@ -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>

View File

@ -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;

View 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;

View File

@ -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;

View 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;

View File

@ -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"

View File

@ -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

View File

@ -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"