feat: improve i18n support as a whole (#1526)

* feat: improve i18n support as a whole

- Remove dayjs in favor of /helpers/datetime.ts, which uses
Intl.DateTimeFormat and Date. Dayjs is not exactly i18n friendly
and has several locale related opened issues.

- Move/refactor date/time code from /helpers/utils.ts to
/helpers/datetime.ts.

- Fix Daily Review weekday not changing according to selected date.

- Localize Daily review weekday and month.

- Load i18n listed strings from /locales/{locale}.json in a dynamic way.
This makes much easier to add new locales, by just adding a properly
named json file and listing it only in /web/src/i18n.ts and
/api/user_setting.go.

- Fallback languages are now set in /web/src/i18n.ts.

- Full language codes are now preffered, but they fallback to 2-letter
codes when not available.

- The locale dropdown is now populated dynamically from the available
locales. Locale names are populated by the browser via
Intl.DisplayNames(locale).

- /web/src/i18n.ts now exports a type TLocale from availableLocales
array. This is used only by findNearestLanguageMatch(). As I was unable
to use this type in ".d.ts" files, I switched the Locale type from
/web/src/types/i18n.d.ts to string.

- Move pretty much all hardcoded text strings to i18n strings.

- Add pt-BR translation.

- Remove site.ts and move its content to a i18n string.

- Rename zh.json to zh-Hans.json to get the correct language name on
selector dropdown.

- Remove pt_BR.json and replace with pt-BR.json.

- Some minor layout spacing fixes to accommodate larger texts.

- Improve some error messages.

* Delete .yarnrc.yml

* Delete package-lock.json

* fix: 158:28  error  Insert `⏎`  prettier/prettier
This commit is contained in:
Lincoln Nogueira
2023-04-14 21:56:03 -03:00
committed by GitHub
parent 5652bb76d4
commit 557278fac0
53 changed files with 6761 additions and 4133 deletions

View File

@ -3,6 +3,7 @@ import { useFilterStore, useMemoStore, useUserStore } from "../store/module";
import { useTranslation } from "react-i18next";
import { getMemoStats } from "@/helpers/api";
import { DAILY_TIMESTAMP } from "@/helpers/consts";
import { getDateStampByDate, getDateString, getTimeStampByDate } from "@/helpers/datetime";
import * as utils from "@/helpers/utils";
import "@/less/usage-heat-map.less";
@ -32,7 +33,7 @@ const UsageHeatMap = () => {
const filterStore = useFilterStore();
const userStore = useUserStore();
const memoStore = useMemoStore();
const todayTimeStamp = utils.getDateStampByDate(Date.now());
const todayTimeStamp = getDateStampByDate(Date.now());
const todayDay = new Date(todayTimeStamp).getDay() + 1;
const nullCell = new Array(7 - todayDay).fill(0);
const usedDaysAmount = (tableConfig.width - 1) * tableConfig.height + todayDay;
@ -48,7 +49,7 @@ const UsageHeatMap = () => {
if (!userStore.state.user) {
return;
}
setCreatedDays(Math.ceil((Date.now() - utils.getTimeStampByDate(userStore.state.user.createdTs)) / 1000 / 3600 / 24));
setCreatedDays(Math.ceil((Date.now() - getTimeStampByDate(userStore.state.user.createdTs)) / 1000 / 3600 / 24));
}, [userStore.state.user]);
useEffect(() => {
@ -57,7 +58,7 @@ const UsageHeatMap = () => {
setMemoAmount(data.length);
const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestamp);
for (const record of data) {
const index = (utils.getDateStampByDate(record * 1000) - beginDayTimestamp) / (1000 * 3600 * 24) - 1;
const index = (getDateStampByDate(record * 1000) - beginDayTimestamp) / (1000 * 3600 * 24) - 1;
if (index >= 0) {
// because of dailight savings, some days may be 23 hours long instead of 24 hours long
// this causes the calculations to yield weird indices such as 40.93333333333
@ -83,7 +84,8 @@ const UsageHeatMap = () => {
const bounding = utils.getElementBounding(event.target as HTMLElement);
tempDiv.style.left = bounding.left + "px";
tempDiv.style.top = bounding.top - 2 + "px";
tempDiv.innerHTML = `${item.count} memos on <span className="date-text">${new Date(item.timestamp as number).toDateString()}</span>`;
const tMemoOnOpts = { amount: item.count, date: getDateString(item.timestamp as number) };
tempDiv.innerHTML = item.count === 1 ? t("heatmap.memo-on", tMemoOnOpts) : t("heatmap.memos-on", tMemoOnOpts);
document.body.appendChild(tempDiv);
if (tempDiv.offsetLeft - tempDiv.clientWidth / 2 < 0) {
@ -106,6 +108,10 @@ const UsageHeatMap = () => {
}
}, []);
// This interpolation is not being used because of the current styling,
// but it can improve translation quality by giving it a more meaningful context
const tMemoInOpts = { amount: "", period: "", date: "" };
return (
<>
<div className="usage-heat-map-wrapper" ref={containerElRef}>
@ -156,8 +162,10 @@ const UsageHeatMap = () => {
</div>
</div>
<p className="w-full pl-4 text-xs -mt-2 mb-3 text-gray-400 dark:text-zinc-400">
<span className="font-medium text-gray-500 dark:text-zinc-300">{memoAmount}</span> memos in{" "}
<span className="font-medium text-gray-500 dark:text-zinc-300">{createdDays}</span> days
<span className="font-medium text-gray-500 dark:text-zinc-300 number">{memoAmount} </span>
{memoAmount === 1 ? t("heatmap.memo-in", tMemoInOpts) : t("heatmap.memos-in", tMemoInOpts)}{" "}
<span className="font-medium text-gray-500 dark:text-zinc-300">{createdDays} </span>
{createdDays === 1 ? t("heatmap.day", tMemoInOpts) : t("heatmap.days", tMemoInOpts)}
</p>
</>
);