fix: take into account user lang in Time component (#1793)

This PR takes into account the user selected language to format the time in the Time component.

Co-authored-by: tommoor <tom.moor@gmail.com>
This commit is contained in:
Rubén Moya 2021-01-14 18:08:14 +01:00 committed by GitHub
parent e8b7782f5e
commit 93ac9892d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 59 deletions

View File

@ -0,0 +1,81 @@
// @flow
import distanceInWordsToNow from "date-fns/distance_in_words_to_now";
import format from "date-fns/format";
import * as React from "react";
import Tooltip from "components/Tooltip";
import useUserLocale from "hooks/useUserLocale";
const locales = {
en: require(`date-fns/locale/en`),
de: require(`date-fns/locale/de`),
es: require(`date-fns/locale/es`),
fr: require(`date-fns/locale/fr`),
ko: require(`date-fns/locale/ko`),
pt: require(`date-fns/locale/pt`),
};
let callbacks = [];
// This is a shared timer that fires every minute, used for
// updating all Time components across the page all at once.
setInterval(() => {
callbacks.forEach((cb) => cb());
}, 1000 * 60);
function eachMinute(fn) {
callbacks.push(fn);
return () => {
callbacks = callbacks.filter((cb) => cb !== fn);
};
}
type Props = {
dateTime: string,
children?: React.Node,
tooltipDelay?: number,
addSuffix?: boolean,
shorten?: boolean,
};
function Time({ addSuffix, children, dateTime, shorten, tooltipDelay }: Props) {
const userLocale = useUserLocale();
const [_, setMinutesMounted] = React.useState(0); // eslint-disable-line no-unused-vars
const callback = React.useRef();
React.useEffect(() => {
callback.current = eachMinute(() => {
setMinutesMounted((state) => ++state);
});
return () => {
if (callback.current) {
callback.current();
}
};
}, []);
let content = distanceInWordsToNow(dateTime, {
addSuffix,
locale: locales[userLocale],
});
if (shorten) {
content = content
.replace("about", "")
.replace("less than a minute ago", "just now")
.replace("minute", "min");
}
return (
<Tooltip
tooltip={format(dateTime, "MMMM Do, YYYY h:mm a")}
delay={tooltipDelay}
placement="bottom"
>
<time dateTime={dateTime}>{children || content}</time>
</Tooltip>
);
}
export default Time;

View File

@ -1,24 +1,8 @@
// @flow
import distanceInWordsToNow from "date-fns/distance_in_words_to_now";
import format from "date-fns/format";
import * as React from "react";
import Tooltip from "components/Tooltip";
let callbacks = [];
// This is a shared timer that fires every minute, used for
// updating all Time components across the page all at once.
setInterval(() => {
callbacks.forEach((cb) => cb());
}, 1000 * 60);
function eachMinute(fn) {
callbacks.push(fn);
return () => {
callbacks = callbacks.filter((cb) => cb !== fn);
};
}
const LocaleTime = React.lazy(() => import("components/LocaleTime"));
type Props = {
dateTime: string,
@ -28,44 +12,27 @@ type Props = {
shorten?: boolean,
};
class Time extends React.Component<Props> {
removeEachMinuteCallback: () => void;
function Time(props: Props) {
let content = distanceInWordsToNow(props.dateTime, {
addSuffix: props.addSuffix,
});
componentDidMount() {
this.removeEachMinuteCallback = eachMinute(() => {
this.forceUpdate();
});
if (props.shorten) {
content = content
.replace("about", "")
.replace("less than a minute ago", "just now")
.replace("minute", "min");
}
componentWillUnmount() {
this.removeEachMinuteCallback();
}
render() {
const { shorten, addSuffix } = this.props;
let content = distanceInWordsToNow(this.props.dateTime, {
addSuffix,
});
if (shorten) {
content = content
.replace("about", "")
.replace("less than a minute ago", "just now")
.replace("minute", "min");
}
return (
<Tooltip
tooltip={format(this.props.dateTime, "MMMM Do, YYYY h:mm a")}
delay={this.props.tooltipDelay}
placement="bottom"
>
<time dateTime={this.props.dateTime}>
{this.props.children || content}
</time>
</Tooltip>
);
}
return (
<React.Suspense
fallback={
<time dateTime={props.dateTime}>{props.children || content}</time>
}
>
<LocaleTime {...props} />
</React.Suspense>
);
}
export default Time;

View File

@ -0,0 +1,12 @@
// @flow
import useStores from "./useStores";
export default function useUserLocale() {
const { auth } = useStores();
if (!auth.user) {
return undefined;
}
return auth.user.language.split("_")[0];
}

View File

@ -9,7 +9,7 @@ Object {
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
"isAdmin": false,
"isSuspended": false,
"language": null,
"language": "en_US",
"lastActiveAt": null,
"name": "User 1",
},
@ -45,7 +45,7 @@ Object {
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
"isAdmin": false,
"isSuspended": false,
"language": null,
"language": "en_US",
"lastActiveAt": null,
"name": "User 1",
},
@ -81,7 +81,7 @@ Object {
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
"isAdmin": true,
"isSuspended": false,
"language": null,
"language": "en_US",
"lastActiveAt": null,
"name": "User 1",
},
@ -126,7 +126,7 @@ Object {
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
"isAdmin": false,
"isSuspended": true,
"language": null,
"language": "en_US",
"lastActiveAt": null,
"name": "User 1",
},

View File

@ -7,7 +7,7 @@ Object {
"id": "123",
"isAdmin": undefined,
"isSuspended": undefined,
"language": undefined,
"language": "en_US",
"lastActiveAt": undefined,
"name": "Test User",
}
@ -20,7 +20,7 @@ Object {
"id": "123",
"isAdmin": undefined,
"isSuspended": undefined,
"language": undefined,
"language": "en_US",
"lastActiveAt": undefined,
"name": "Test User",
}

View File

@ -24,7 +24,7 @@ export default (user: User, options: Options = {}): ?UserPresentation => {
userData.isAdmin = user.isAdmin;
userData.isSuspended = user.isSuspended;
userData.avatarUrl = user.avatarUrl;
userData.language = user.language;
userData.language = user.language || process.env.DEFAULT_LANGUAGE || "en_US";
if (options.includeDetails) {
userData.email = user.email;

View File

@ -31,6 +31,9 @@ export const initI18n = () => {
return i18n;
};
// Note: Updating the available languages? Make sure to also update the
// locales array in app/components/LocaleTime.js to enable translation for timestamps.
export const languageOptions = [
{ label: "English (US)", value: "en_US" },
{ label: "Deutsch (Deutschland)", value: "de_DE" },