From 8cc92e0c42f7a64053c6288026158e40ad8b89e0 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 4 Aug 2021 14:31:55 +0100 Subject: [PATCH] Start work on fluent categories. Fix Locale loading when syncing invalid lang. --- .../common/messaging/MessageBase.tsx | 4 +- src/components/ui/fluent/CategoryButton.tsx | 26 +++++++++ src/context/Locale.tsx | 56 +++++++++++++++---- src/lib/i18n.tsx | 14 +---- 4 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 src/components/ui/fluent/CategoryButton.tsx diff --git a/src/components/common/messaging/MessageBase.tsx b/src/components/common/messaging/MessageBase.tsx index 861a559a..26132494 100644 --- a/src/components/common/messaging/MessageBase.tsx +++ b/src/components/common/messaging/MessageBase.tsx @@ -218,7 +218,7 @@ export const MessageDetail = observer( @@ -236,7 +236,7 @@ export const MessageDetail = observer( diff --git a/src/components/ui/fluent/CategoryButton.tsx b/src/components/ui/fluent/CategoryButton.tsx new file mode 100644 index 00000000..39298ce8 --- /dev/null +++ b/src/components/ui/fluent/CategoryButton.tsx @@ -0,0 +1,26 @@ +import styled from "styled-components"; + +import { Children } from "../../../types/Preact"; + +const CategoryBase = styled.div` + height: 54px; + padding: 8px 12px; + border-radius: 6px; + margin-bottom: 10px; + + background: var(--secondary-header); + + gap: 12px; + display: flex; + align-items: center; + flex-direction: row; +`; + +interface Props { + icon?: Children; + children?: Children; +} + +export default function CategoryButton({ icon, children }: Props) { + return {icon}; +} diff --git a/src/context/Locale.tsx b/src/context/Locale.tsx index 7572bd78..3fc7e2e8 100644 --- a/src/context/Locale.tsx +++ b/src/context/Locale.tsx @@ -149,35 +149,60 @@ interface Props { locale: Language; } +export interface Dictionary { + dayjs?: { + defaults?: { + twelvehour?: "yes" | "no"; + separator?: string; + date?: "traditional" | "simplified" | "ISO8601"; + }; + timeFormat?: string; + }; + [key: string]: + | Record> + | string + | undefined; +} + function Locale({ children, locale }: Props) { - // TODO: create and use LanguageDefinition type here - const [defns, setDefinition] = - useState>(definition); - const lang = Languages[locale]; + const [defns, setDefinition] = useState(definition as any); + + // Load relevant language information, fallback to English if invalid. + const lang = Languages[locale] ?? Languages.en; - // TODO: clean this up and use the built in Intl API function transformLanguage(source: { [key: string]: any }) { + // Fallback untranslated strings to English (UK) const obj = defaultsDeep(source, definition); - const dayjs = obj.dayjs; - const defaults = dayjs.defaults; + // Take relevant objects out, dayjs and defaults + // should exist given we just took defaults above. + const { dayjs } = obj; + const { defaults } = dayjs; + + // Determine whether we are using 12-hour clock. const twelvehour = defaults?.twelvehour ? defaults.twelvehour === "yes" : false; + // Determine what date separator we are using. const separator: string = defaults?.date_separator ?? "/"; + + // Determine what date format we are using. const date: "traditional" | "simplified" | "ISO8601" = defaults?.date_format ?? "traditional"; + // Available date formats. const DATE_FORMATS = { traditional: `DD${separator}MM${separator}YYYY`, simplified: `MM${separator}DD${separator}YYYY`, ISO8601: "YYYY-MM-DD", }; - dayjs["sameElse"] = DATE_FORMATS[date]; + // Replace data in dayjs object, make sure to provide fallbacks. + dayjs["sameElse"] = DATE_FORMATS[date] ?? DATE_FORMATS.traditional; dayjs["timeFormat"] = twelvehour ? "hh:mm A" : "HH:mm"; + // Replace {{time}} format string in dayjs strings with the time format. Object.keys(dayjs) .filter((k) => typeof dayjs[k] === "string") .forEach( @@ -191,8 +216,10 @@ function Locale({ children, locale }: Props) { return obj; } - useEffect(() => { + function loadLanguage(locale: string) { if (locale === "en") { + // If English, make sure to restore everything to defaults. + // Use what we already have. const defn = transformLanguage(definition); setDefinition(defn); dayjs.locale("en"); @@ -202,24 +229,33 @@ function Locale({ children, locale }: Props) { import(`../../external/lang/${lang.i18n}.json`).then( async (lang_file) => { + // Transform the definitions data. const defn = transformLanguage(lang_file.default); + + // Determine and load dayjs locales. const target = lang.dayjs ?? lang.i18n; const dayjs_locale = await import( `../../node_modules/dayjs/esm/locale/${target}.js` ); + // Load dayjs locales. dayjs.locale(target, dayjs_locale.default); if (defn.dayjs) { + // Override dayjs calendar locales with our own. dayjs.updateLocale(target, { calendar: defn.dayjs }); } + // Apply definition to app. setDefinition(defn); }, ); - }, [locale, lang]); + } + + useEffect(() => loadLanguage(locale), [locale, lang]); useEffect(() => { + // Apply RTL language format. document.body.style.direction = lang.rtl ? "rtl" : ""; }, [lang.rtl]); diff --git a/src/lib/i18n.tsx b/src/lib/i18n.tsx index 769314ad..0507e78a 100644 --- a/src/lib/i18n.tsx +++ b/src/lib/i18n.tsx @@ -1,6 +1,8 @@ import { IntlContext, translate } from "preact-i18n"; import { useContext } from "preact/hooks"; +import { Dictionary } from "../context/Locale"; + import { Children } from "../types/Preact"; interface Fields { @@ -12,18 +14,6 @@ interface Props { fields: Fields; } -export interface Dictionary { - dayjs: { - defaults: { - twelvehour: "yes" | "no"; - separator: string; - date: "traditional" | "simplified" | "ISO8601"; - }; - timeFormat: string; - }; - [key: string]: Object | string; -} - export interface IntlType { intl: { dictionary: Dictionary;