diff --git a/src/__data__/languages.json b/src/__data__/languages.json index c6be617..2ce307b 100644 --- a/src/__data__/languages.json +++ b/src/__data__/languages.json @@ -9,6 +9,7 @@ "nativeName": "Deutsch", "country": "de", "dayjsLocale": "de", + "timeagoLocale": "de", "contributors": [ { "name": "Lignum", @@ -21,6 +22,7 @@ "nativeName": "Français", "country": "fr", "dayjsLocale": "fr", + "timeagoLocale": "fr", "contributors": [ { "name": "Anavrins", @@ -33,6 +35,7 @@ "nativeName": "Nederlands", "country": "nl", "dayjsLocale": "nl", + "timeagoLocale": "nl", "contributors": [ { "name": "HydroNitrogen", @@ -45,6 +48,7 @@ "nativeName": "Polski", "country": "pl", "dayjsLocale": "pl", + "timeagoLocale": "pl", "contributors": [ { "name": "Wojbie", @@ -57,6 +61,7 @@ "nativeName": "Tiếng Việt", "country": "vn", "dayjsLocale": "vi", + "timeagoLocale": "vi", "contributors": [ { "name": "Boom", diff --git a/src/components/DateTime.tsx b/src/components/DateTime.tsx index 8f04988..7115e93 100644 --- a/src/components/DateTime.tsx +++ b/src/components/DateTime.tsx @@ -1,9 +1,11 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import { useContext } from "react"; import classNames from "classnames"; import { Tooltip } from "antd"; +import { TimeagoFormatterContext } from "@global/LocaleContext"; import { useBooleanSetting } from "@utils/settings"; import dayjs from "dayjs"; @@ -32,6 +34,9 @@ tooltip, ...props }: Props): JSX.Element | null { + // Get the locale's formatter + const formatter = useContext(TimeagoFormatterContext); + const showRelativeDates = useBooleanSetting("showRelativeDates"); if (!date) return null; @@ -49,7 +54,7 @@ const contents = ( {isTimeAgo - ? + ? : dayjs(realDate).format("YYYY/MM/DD HH:mm:ss")} ); diff --git a/src/global/LocaleContext.tsx b/src/global/LocaleContext.tsx index 2ad3dcf..ff85d2b 100644 --- a/src/global/LocaleContext.tsx +++ b/src/global/LocaleContext.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt -import { FC, useEffect } from "react"; +import { FC, createContext, useEffect, useState } from "react"; import { ConfigProvider } from "antd"; import { Locale } from "antd/lib/locale-provider"; import localeValues from "antd/lib/locale/default"; @@ -11,14 +11,21 @@ import dayjs from "dayjs"; +import { Formatter } from "react-timeago"; +import buildFormatter from "react-timeago/lib/formatters/buildFormatter"; + import Debug from "debug"; const debug = Debug("kristweb:locale-context"); +export const TimeagoFormatterContext = createContext(undefined); + export const LocaleContext: FC = ({ children }): JSX.Element => { const { t, i18n } = useTranslation(); const langCode = i18n.language; const languages = getLanguages(); + const [timeagoFormatter, setTimeagoFormatter] = useState<{ formatter: Formatter }>(); + // Load the day.js locale if available useEffect(() => { if (!languages) return; @@ -51,7 +58,38 @@ .catch(console.error); }, [langCode, languages]); - return - {children} - ; + // Load the timeago locale if available + useEffect(() => { + if (!languages) return; + const lang = languages[langCode]; + + // See if the language has a timeago locale set. If not, revert to default + const timeagoLocale = lang?.timeagoLocale; + if (!timeagoLocale) { + debug("language %s doesn't have a timeago locale, reverting to default", langCode); + setTimeagoFormatter(undefined); + return; + } + + // Load the locale + debug("loading timeago locale %s for language %s", timeagoLocale, langCode); + import( + /* webpackInclude: /\.js$/ */ + /* webpackMode: "lazy" */ + /* webpackChunkName: "locale-timeago-[request]" */ + `react-timeago/lib/language-strings/${timeagoLocale}` + ) + .then(strings => { + debug("got timeagoLocale locale %s", timeagoLocale); + console.log(strings.default); + setTimeagoFormatter({ formatter: buildFormatter(strings.default) }); + }) + .catch(console.error); + }, [langCode, languages]); + + return + + {children} + + ; }; diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts index 530c774..de4c6bf 100644 --- a/src/utils/i18n.ts +++ b/src/utils/i18n.ts @@ -22,6 +22,7 @@ nativeName?: string; country?: string; dayjsLocale?: string; + timeagoLocale?: string; contributors: Contributor[]; } diff --git a/tsconfig.json b/tsconfig.json index 95c888e..e6ab60a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,9 +21,14 @@ "isolatedModules": true, "noEmit": true, "noImplicitAny": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "typeRoots": [ + "./node_modules/@types", + "./typings" + ] }, "include": [ - "src" + "src", + "typings" ] } diff --git a/typings/react-timeago/lib/formatters/index.d.ts b/typings/react-timeago/lib/formatters/index.d.ts new file mode 100644 index 0000000..753e289 --- /dev/null +++ b/typings/react-timeago/lib/formatters/index.d.ts @@ -0,0 +1,68 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under GPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt + +// TODO: PR this to DefinitelyTyped + +// Based off of the Flow types: +// https://github.com/nmn/react-timeago/blob/master/src/formatters/buildFormatter.js + +type Unit = + | "second" + | "minute" + | "hour" + | "day" + | "week" + | "month" + | "year"; + +type Suffix = "ago" | "from now"; + +type Formatter = ( + value: number, + unit: Unit, + suffix: Suffix, + epochMiliseconds: number, + nextFormatter?: Formatter +) => React.ReactNode; + +type StringOrFn = string | ((value: number, millisDelta: number) => string); +type NumberArray = [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, +]; + +interface L10nsStrings { + prefixAgo?: StringOrFn; + prefixFromNow?: StringOrFn; + suffixAgo?: StringOrFn; + suffixFromNow?: StringOrFn; + second?: StringOrFn; + seconds?: StringOrFn; + minute?: StringOrFn; + minutes?: StringOrFn; + hour?: StringOrFn; + hours?: StringOrFn; + day?: StringOrFn; + days?: StringOrFn; + week?: StringOrFn; + weeks?: StringOrFn; + month?: StringOrFn; + months?: StringOrFn; + year?: StringOrFn; + years?: StringOrFn; + wordSeparator?: string; + numbers?: NumberArray; +} + +declare module "react-timeago/lib/formatters/*" { + export default function buildFormatter(strings: L10nsStrings): Formatter; +}