Start work on fluent categories.

Fix Locale loading when syncing invalid lang.
This commit is contained in:
Paul 2021-08-04 14:31:55 +01:00
parent c93f0245f1
commit 8cc92e0c42
4 changed files with 76 additions and 24 deletions

View file

@ -218,7 +218,7 @@ export const MessageDetail = observer(
<time className="copyTime"> <time className="copyTime">
<i className="copyBracket">[</i> <i className="copyBracket">[</i>
{dayjs(decodeTime(message._id)).format( {dayjs(decodeTime(message._id)).format(
dict.dayjs.timeFormat, dict.dayjs?.timeFormat,
)} )}
<i className="copyBracket">]</i> <i className="copyBracket">]</i>
</time> </time>
@ -236,7 +236,7 @@ export const MessageDetail = observer(
<time> <time>
<i className="copyBracket">[</i> <i className="copyBracket">[</i>
{dayjs(decodeTime(message._id)).format( {dayjs(decodeTime(message._id)).format(
dict.dayjs.timeFormat, dict.dayjs?.timeFormat,
)} )}
<i className="copyBracket">]</i> <i className="copyBracket">]</i>
</time> </time>

View file

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

View file

@ -149,35 +149,60 @@ interface Props {
locale: Language; locale: Language;
} }
export interface Dictionary {
dayjs?: {
defaults?: {
twelvehour?: "yes" | "no";
separator?: string;
date?: "traditional" | "simplified" | "ISO8601";
};
timeFormat?: string;
};
[key: string]:
| Record<string, Omit<Dictionary, "dayjs">>
| string
| undefined;
}
function Locale({ children, locale }: Props) { function Locale({ children, locale }: Props) {
// TODO: create and use LanguageDefinition type here const [defns, setDefinition] = useState<Dictionary>(definition as any);
const [defns, setDefinition] =
useState<Record<string, unknown>>(definition); // Load relevant language information, fallback to English if invalid.
const lang = Languages[locale]; const lang = Languages[locale] ?? Languages.en;
// TODO: clean this up and use the built in Intl API
function transformLanguage(source: { [key: string]: any }) { function transformLanguage(source: { [key: string]: any }) {
// Fallback untranslated strings to English (UK)
const obj = defaultsDeep(source, definition); 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 const twelvehour = defaults?.twelvehour
? defaults.twelvehour === "yes" ? defaults.twelvehour === "yes"
: false; : false;
// Determine what date separator we are using.
const separator: string = defaults?.date_separator ?? "/"; const separator: string = defaults?.date_separator ?? "/";
// Determine what date format we are using.
const date: "traditional" | "simplified" | "ISO8601" = const date: "traditional" | "simplified" | "ISO8601" =
defaults?.date_format ?? "traditional"; defaults?.date_format ?? "traditional";
// Available date formats.
const DATE_FORMATS = { const DATE_FORMATS = {
traditional: `DD${separator}MM${separator}YYYY`, traditional: `DD${separator}MM${separator}YYYY`,
simplified: `MM${separator}DD${separator}YYYY`, simplified: `MM${separator}DD${separator}YYYY`,
ISO8601: "YYYY-MM-DD", 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"; dayjs["timeFormat"] = twelvehour ? "hh:mm A" : "HH:mm";
// Replace {{time}} format string in dayjs strings with the time format.
Object.keys(dayjs) Object.keys(dayjs)
.filter((k) => typeof dayjs[k] === "string") .filter((k) => typeof dayjs[k] === "string")
.forEach( .forEach(
@ -191,8 +216,10 @@ function Locale({ children, locale }: Props) {
return obj; return obj;
} }
useEffect(() => { function loadLanguage(locale: string) {
if (locale === "en") { if (locale === "en") {
// If English, make sure to restore everything to defaults.
// Use what we already have.
const defn = transformLanguage(definition); const defn = transformLanguage(definition);
setDefinition(defn); setDefinition(defn);
dayjs.locale("en"); dayjs.locale("en");
@ -202,24 +229,33 @@ function Locale({ children, locale }: Props) {
import(`../../external/lang/${lang.i18n}.json`).then( import(`../../external/lang/${lang.i18n}.json`).then(
async (lang_file) => { async (lang_file) => {
// Transform the definitions data.
const defn = transformLanguage(lang_file.default); const defn = transformLanguage(lang_file.default);
// Determine and load dayjs locales.
const target = lang.dayjs ?? lang.i18n; const target = lang.dayjs ?? lang.i18n;
const dayjs_locale = await import( const dayjs_locale = await import(
`../../node_modules/dayjs/esm/locale/${target}.js` `../../node_modules/dayjs/esm/locale/${target}.js`
); );
// Load dayjs locales.
dayjs.locale(target, dayjs_locale.default); dayjs.locale(target, dayjs_locale.default);
if (defn.dayjs) { if (defn.dayjs) {
// Override dayjs calendar locales with our own.
dayjs.updateLocale(target, { calendar: defn.dayjs }); dayjs.updateLocale(target, { calendar: defn.dayjs });
} }
// Apply definition to app.
setDefinition(defn); setDefinition(defn);
}, },
); );
}, [locale, lang]); }
useEffect(() => loadLanguage(locale), [locale, lang]);
useEffect(() => { useEffect(() => {
// Apply RTL language format.
document.body.style.direction = lang.rtl ? "rtl" : ""; document.body.style.direction = lang.rtl ? "rtl" : "";
}, [lang.rtl]); }, [lang.rtl]);

View file

@ -1,6 +1,8 @@
import { IntlContext, translate } from "preact-i18n"; import { IntlContext, translate } from "preact-i18n";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
import { Dictionary } from "../context/Locale";
import { Children } from "../types/Preact"; import { Children } from "../types/Preact";
interface Fields { interface Fields {
@ -12,18 +14,6 @@ interface Props {
fields: Fields; 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 { export interface IntlType {
intl: { intl: {
dictionary: Dictionary; dictionary: Dictionary;