mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
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:
@ -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>
|
||||
</>
|
||||
);
|
||||
|
Reference in New Issue
Block a user