mirror of
https://github.com/usememos/memos.git
synced 2025-02-15 19:00:46 +01:00
feat: update RU i18n locale (#1422)
* feat: Fix i18n and RU locale * fix: eslint issues * change the position of deps --------- Co-authored-by: CorrectRoadH <a778917369@gmail.com>
This commit is contained in:
parent
d21abfc60c
commit
b03778fa73
@ -1,3 +1,4 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
import { useColorScheme } from "@mui/joy";
|
import { useColorScheme } from "@mui/joy";
|
||||||
import { useEffect, Suspense } from "react";
|
import { useEffect, Suspense } from "react";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
@ -58,6 +59,7 @@ const App = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.setAttribute("lang", locale);
|
document.documentElement.setAttribute("lang", locale);
|
||||||
i18n.changeLanguage(locale);
|
i18n.changeLanguage(locale);
|
||||||
|
dayjs.locale(locale);
|
||||||
storage.set({
|
storage.set({
|
||||||
locale: locale,
|
locale: locale,
|
||||||
});
|
});
|
||||||
|
@ -8,10 +8,12 @@ import { useMessageStore } from "../store/zustand/message";
|
|||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import { generateDialog } from "./Dialog";
|
import { generateDialog } from "./Dialog";
|
||||||
import showSettingDialog from "./SettingDialog";
|
import showSettingDialog from "./SettingDialog";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type Props = DialogProps;
|
type Props = DialogProps;
|
||||||
|
|
||||||
const AskAIDialog: React.FC<Props> = (props: Props) => {
|
const AskAIDialog: React.FC<Props> = (props: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { destroy, hide } = props;
|
const { destroy, hide } = props;
|
||||||
const fetchingState = useLoading(false);
|
const fetchingState = useLoading(false);
|
||||||
const messageStore = useMessageStore();
|
const messageStore = useMessageStore();
|
||||||
@ -79,7 +81,7 @@ const AskAIDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text flex flex-row items-center">
|
<p className="title-text flex flex-row items-center">
|
||||||
<Icon.Bot className="mr-1 w-5 h-auto opacity-80" />
|
<Icon.Bot className="mr-1 w-5 h-auto opacity-80" />
|
||||||
Ask AI
|
{t("ask-ai.title")}
|
||||||
</p>
|
</p>
|
||||||
<button className="btn close-btn" onClick={() => hide()}>
|
<button className="btn close-btn" onClick={() => hide()}>
|
||||||
<Icon.X />
|
<Icon.X />
|
||||||
@ -111,14 +113,14 @@ const AskAIDialog: React.FC<Props> = (props: Props) => {
|
|||||||
)}
|
)}
|
||||||
{!isEnabled && (
|
{!isEnabled && (
|
||||||
<div className="w-full flex flex-col justify-center items-center mt-4 space-y-2">
|
<div className="w-full flex flex-col justify-center items-center mt-4 space-y-2">
|
||||||
<p>You have not set up your OpenAI API key.</p>
|
<p>{t("ask-ai.not_enabled")}</p>
|
||||||
<Button onClick={() => handleGotoSystemSetting()}>Go to settings</Button>
|
<Button onClick={() => handleGotoSystemSetting()}>{t("ask-ai.go-to-settings")}</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="w-full relative mt-4">
|
<div className="w-full relative mt-4">
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full"
|
className="w-full"
|
||||||
placeholder="Ask anything…"
|
placeholder={t("ask-ai.placeholder")}
|
||||||
value={question}
|
value={question}
|
||||||
minRows={1}
|
minRows={1}
|
||||||
maxRows={5}
|
maxRows={5}
|
||||||
|
@ -4,6 +4,7 @@ import { toast } from "react-hot-toast";
|
|||||||
import { useResourceStore } from "../store/module";
|
import { useResourceStore } from "../store/module";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import { generateDialog } from "./Dialog";
|
import { generateDialog } from "./Dialog";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const fileTypeAutocompleteOptions = ["image/*", "text/*", "audio/*", "video/*", "application/*"];
|
const fileTypeAutocompleteOptions = ["image/*", "text/*", "audio/*", "video/*", "application/*"];
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CreateResourceDialog: React.FC<Props> = (props: Props) => {
|
const CreateResourceDialog: React.FC<Props> = (props: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { destroy, onCancel, onConfirm } = props;
|
const { destroy, onCancel, onConfirm } = props;
|
||||||
const resourceStore = useResourceStore();
|
const resourceStore = useResourceStore();
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
@ -144,14 +146,14 @@ const CreateResourceDialog: React.FC<Props> = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text">Create Resource</p>
|
<p className="title-text">{t("resources.create-dialog.title")}</p>
|
||||||
<button className="btn close-btn" onClick={handleCloseDialog}>
|
<button className="btn close-btn" onClick={handleCloseDialog}>
|
||||||
<Icon.X />
|
<Icon.X />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container !w-80">
|
<div className="dialog-content-container !w-80">
|
||||||
<Typography className="!mb-1" level="body2">
|
<Typography className="!mb-1" level="body2">
|
||||||
Upload method
|
{t("resources.create-dialog.upload-method")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Select
|
<Select
|
||||||
className="w-full mb-2"
|
className="w-full mb-2"
|
||||||
@ -159,15 +161,15 @@ const CreateResourceDialog: React.FC<Props> = (props: Props) => {
|
|||||||
value={state.selectedMode}
|
value={state.selectedMode}
|
||||||
startDecorator={<Icon.File className="w-4 h-auto" />}
|
startDecorator={<Icon.File className="w-4 h-auto" />}
|
||||||
>
|
>
|
||||||
<Option value="local-file">Local file</Option>
|
<Option value="local-file">{t("resources.create-dialog.local-file.option")}</Option>
|
||||||
<Option value="external-link">External link</Option>
|
<Option value="external-link">{t("resources.create-dialog.external-link.option")}</Option>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{state.selectedMode === "local-file" && (
|
{state.selectedMode === "local-file" && (
|
||||||
<>
|
<>
|
||||||
<div className="w-full relative bg-blue-50 dark:bg-zinc-900 rounded-md flex flex-row justify-center items-center py-8">
|
<div className="w-full relative bg-blue-50 dark:bg-zinc-900 rounded-md flex flex-row justify-center items-center py-8">
|
||||||
<label htmlFor="files" className="p-2 px-4 text-sm text-white cursor-pointer bg-blue-500 block rounded hover:opacity-80">
|
<label htmlFor="files" className="p-2 px-4 text-sm text-white cursor-pointer bg-blue-500 block rounded hover:opacity-80">
|
||||||
Choose a file...
|
{t("resources.create-dialog.local-file.choose")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="absolute inset-0 w-full h-full opacity-0"
|
className="absolute inset-0 w-full h-full opacity-0"
|
||||||
@ -194,7 +196,7 @@ const CreateResourceDialog: React.FC<Props> = (props: Props) => {
|
|||||||
{state.selectedMode === "external-link" && (
|
{state.selectedMode === "external-link" && (
|
||||||
<>
|
<>
|
||||||
<Typography className="!mb-1" level="body2">
|
<Typography className="!mb-1" level="body2">
|
||||||
Link
|
{t("resources.create-dialog.external-link.link")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input
|
<Input
|
||||||
className="mb-2"
|
className="mb-2"
|
||||||
@ -204,16 +206,22 @@ const CreateResourceDialog: React.FC<Props> = (props: Props) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
<Typography className="!mb-1" level="body2">
|
<Typography className="!mb-1" level="body2">
|
||||||
File name
|
{t("resources.create-dialog.external-link.file-name")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input className="mb-2" placeholder="File name" value={resourceCreate.filename} onChange={handleFileNameChanged} fullWidth />
|
<Input
|
||||||
|
className="mb-2"
|
||||||
|
placeholder={t("resources.create-dialog.external-link.file-name-placeholder")}
|
||||||
|
value={resourceCreate.filename}
|
||||||
|
onChange={handleFileNameChanged}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
<Typography className="!mb-1" level="body2">
|
<Typography className="!mb-1" level="body2">
|
||||||
Type
|
{t("resources.create-dialog.external-link.type")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
className="w-full"
|
className="w-full"
|
||||||
size="sm"
|
size="sm"
|
||||||
placeholder="File type"
|
placeholder={t("resources.create-dialog.external-link.type-placeholder")}
|
||||||
freeSolo={true}
|
freeSolo={true}
|
||||||
options={fileTypeAutocompleteOptions}
|
options={fileTypeAutocompleteOptions}
|
||||||
onChange={(_, value) => handleFileTypeChanged(value || "")}
|
onChange={(_, value) => handleFileTypeChanged(value || "")}
|
||||||
@ -223,10 +231,10 @@ const CreateResourceDialog: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
<div className="mt-2 w-full flex flex-row justify-end items-center space-x-1">
|
<div className="mt-2 w-full flex flex-row justify-end items-center space-x-1">
|
||||||
<Button variant="plain" color="neutral" onClick={handleCloseDialog}>
|
<Button variant="plain" color="neutral" onClick={handleCloseDialog}>
|
||||||
Cancel
|
{t("common.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleConfirmBtnClick} loading={state.uploadingFlag} disabled={!allowConfirmAction()}>
|
<Button onClick={handleConfirmBtnClick} loading={state.uploadingFlag} disabled={!allowConfirmAction()}>
|
||||||
Create
|
{t("common.create")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,12 +3,14 @@ import { toast } from "react-hot-toast";
|
|||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import { generateDialog } from "./Dialog";
|
import { generateDialog } from "./Dialog";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface Props extends DialogProps {
|
interface Props extends DialogProps {
|
||||||
memoId: MemoId;
|
memoId: MemoId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmbedMemoDialog: React.FC<Props> = (props: Props) => {
|
const EmbedMemoDialog: React.FC<Props> = (props: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { memoId, destroy } = props;
|
const { memoId, destroy } = props;
|
||||||
|
|
||||||
const memoEmbeddedCode = () => {
|
const memoEmbeddedCode = () => {
|
||||||
@ -23,20 +25,20 @@ const EmbedMemoDialog: React.FC<Props> = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="dialog-header-container">
|
<div className="dialog-header-container">
|
||||||
<p className="title-text">Embed Memo</p>
|
<p className="title-text">{t("embed-memo.title")}</p>
|
||||||
<button className="btn close-btn" onClick={() => destroy()}>
|
<button className="btn close-btn" onClick={() => destroy()}>
|
||||||
<Icon.X />
|
<Icon.X />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="dialog-content-container !w-80">
|
<div className="dialog-content-container !w-80">
|
||||||
<p className="text-base leading-6 mb-2">Copy and paste the below codes into your blog or website.</p>
|
<p className="text-base leading-6 mb-2">{t("embed-memo.text")}</p>
|
||||||
<pre className="w-full font-mono text-sm p-3 border rounded-lg">
|
<pre className="w-full font-mono text-sm p-3 border rounded-lg">
|
||||||
<code className="w-full break-all whitespace-pre-wrap">{memoEmbeddedCode()}</code>
|
<code className="w-full break-all whitespace-pre-wrap">{memoEmbeddedCode()}</code>
|
||||||
</pre>
|
</pre>
|
||||||
<p className="w-full text-sm leading-6 flex flex-row justify-between items-center mt-2">
|
<p className="w-full text-sm leading-6 flex flex-row justify-between items-center mt-2">
|
||||||
<span className="italic opacity-80">* Only the public memo supports.</span>
|
<span className="italic opacity-80">{t("embed-memo.only-public-supported")}</span>
|
||||||
<span className="btn-primary" onClick={handleCopyCode}>
|
<span className="btn-primary" onClick={handleCopyCode}>
|
||||||
Copy
|
{t("embed-memo.copy")}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,7 +112,7 @@ const Header = () => {
|
|||||||
className="px-4 pr-5 py-2 rounded-lg flex flex-row items-center text-lg dark:text-gray-200 hover:bg-white hover:shadow dark:hover:bg-zinc-700"
|
className="px-4 pr-5 py-2 rounded-lg flex flex-row items-center text-lg dark:text-gray-200 hover:bg-white hover:shadow dark:hover:bg-zinc-700"
|
||||||
onClick={() => showAskAIDialog()}
|
onClick={() => showAskAIDialog()}
|
||||||
>
|
>
|
||||||
<Icon.Bot className="mr-4 w-6 h-auto opacity-80" /> Ask AI
|
<Icon.Bot className="mr-4 w-6 h-auto opacity-80" /> {t("common.ask-ai")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="header-archived-memo"
|
id="header-archived-memo"
|
||||||
|
@ -205,7 +205,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
className={`status-text ${memo.visibility.toLocaleLowerCase()}`}
|
className={`status-text ${memo.visibility.toLocaleLowerCase()}`}
|
||||||
onClick={() => handleMemoVisibilityClick(memo.visibility)}
|
onClick={() => handleMemoVisibilityClick(memo.visibility)}
|
||||||
>
|
>
|
||||||
{memo.visibility}
|
{t(`visibility.${memo.visibility}`)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -237,7 +237,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
{t("memo.view-detail")}
|
{t("memo.view-detail")}
|
||||||
</span>
|
</span>
|
||||||
<span className="btn" onClick={handleShowEmbedMemoDialog}>
|
<span className="btn" onClick={handleShowEmbedMemoDialog}>
|
||||||
Embed memo
|
{t("memo.embed")}
|
||||||
</span>
|
</span>
|
||||||
<span className="btn archive-btn" onClick={handleArchiveMemoClick}>
|
<span className="btn archive-btn" onClick={handleArchiveMemoClick}>
|
||||||
{t("common.archive")}
|
{t("common.archive")}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { useState, useRef } from "react";
|
import { useState, useRef } from "react";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import useDebounce from "../hooks/useDebounce";
|
import useDebounce from "../hooks/useDebounce";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface ResourceSearchBarProps {
|
interface ResourceSearchBarProps {
|
||||||
setQuery: (queryText: string) => void;
|
setQuery: (queryText: string) => void;
|
||||||
}
|
}
|
||||||
const ResourceSearchBar = ({ setQuery }: ResourceSearchBarProps) => {
|
const ResourceSearchBar = ({ setQuery }: ResourceSearchBarProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [queryText, setQueryText] = useState("");
|
const [queryText, setQueryText] = useState("");
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ const ResourceSearchBar = ({ setQuery }: ResourceSearchBarProps) => {
|
|||||||
<input
|
<input
|
||||||
className="flex ml-2 w-24 grow text-sm outline-none bg-transparent dark:text-gray-200"
|
className="flex ml-2 w-24 grow text-sm outline-none bg-transparent dark:text-gray-200"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search resource "
|
placeholder={t("resources.search-bar-placeholder")}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={queryText}
|
value={queryText}
|
||||||
onChange={handleTextQueryInput}
|
onChange={handleTextQueryInput}
|
||||||
|
@ -3,8 +3,10 @@ import useDebounce from "../hooks/useDebounce";
|
|||||||
import { useFilterStore, useDialogStore, useLayoutStore } from "../store/module";
|
import { useFilterStore, useDialogStore, useLayoutStore } from "../store/module";
|
||||||
import { resolution } from "../utils/layout";
|
import { resolution } from "../utils/layout";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const SearchBar = () => {
|
const SearchBar = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const dialogStore = useDialogStore();
|
const dialogStore = useDialogStore();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
@ -64,7 +66,7 @@ const SearchBar = () => {
|
|||||||
<input
|
<input
|
||||||
className="flex ml-2 w-24 grow text-sm outline-none bg-transparent dark:text-gray-200"
|
className="flex ml-2 w-24 grow text-sm outline-none bg-transparent dark:text-gray-200"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search memos"
|
placeholder={t("search-bar.input-placeholder")}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={queryText}
|
value={queryText}
|
||||||
onChange={handleTextQueryInput}
|
onChange={handleTextQueryInput}
|
||||||
|
@ -79,7 +79,7 @@ const PreferencesSection = () => {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-label selector">
|
<div className="form-label selector">
|
||||||
<span className="normal-text">Default resource visibility</span>
|
<span className="normal-text">{t("setting.preference-section.default-resource-visibility")}</span>
|
||||||
<Select
|
<Select
|
||||||
className="!min-w-[10rem] w-auto text-sm"
|
className="!min-w-[10rem] w-auto text-sm"
|
||||||
value={setting.resourceVisibility}
|
value={setting.resourceVisibility}
|
||||||
@ -98,7 +98,7 @@ const PreferencesSection = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-label selector">
|
<div className="form-label selector">
|
||||||
<span className="normal-text">Daily Review Time Offset</span>
|
<span className="normal-text">{t("setting.preference-section.daily-review-time-offset")}</span>
|
||||||
<span className="w-auto inline-flex">
|
<span className="w-auto inline-flex">
|
||||||
<Select
|
<Select
|
||||||
placeholder="hh"
|
placeholder="hh"
|
||||||
|
@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { DAILY_TIMESTAMP } from "../../helpers/consts";
|
import { DAILY_TIMESTAMP } from "../../helpers/consts";
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
import "../../less/common/date-picker.less";
|
import "../../less/common/date-picker.less";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface DatePickerProps {
|
interface DatePickerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -10,6 +11,7 @@ interface DatePickerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { className, datestamp, handleDateStampChange } = props;
|
const { className, datestamp, handleDateStampChange } = props;
|
||||||
const [currentDateStamp, setCurrentDateStamp] = useState<DateStamp>(getMonthFirstDayDateStamp(datestamp));
|
const [currentDateStamp, setCurrentDateStamp] = useState<DateStamp>(getMonthFirstDayDateStamp(datestamp));
|
||||||
|
|
||||||
@ -67,13 +69,13 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="date-picker-day-container">
|
<div className="date-picker-day-container">
|
||||||
<div className="date-picker-day-header">
|
<div className="date-picker-day-header">
|
||||||
<span className="day-item">Mon</span>
|
<span className="day-item">{t("days.mon")}</span>
|
||||||
<span className="day-item">Tue</span>
|
<span className="day-item">{t("days.tue")}</span>
|
||||||
<span className="day-item">Wed</span>
|
<span className="day-item">{t("days.wed")}</span>
|
||||||
<span className="day-item">Thu</span>
|
<span className="day-item">{t("days.thu")}</span>
|
||||||
<span className="day-item">Fri</span>
|
<span className="day-item">{t("days.fri")}</span>
|
||||||
<span className="day-item">Sat</span>
|
<span className="day-item">{t("days.sat")}</span>
|
||||||
<span className="day-item">Sun</span>
|
<span className="day-item">{t("days.sun")}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{dayList.map((d) => {
|
{dayList.map((d) => {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"resources": "Resources",
|
"resources": "Resources",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"daily-review": "Daily Review",
|
"daily-review": "Daily Review",
|
||||||
|
"ask-ai": "Ask AI",
|
||||||
"archived": "Archived",
|
"archived": "Archived",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
@ -82,7 +83,24 @@
|
|||||||
"no-files-selected": "No files selected❗",
|
"no-files-selected": "No files selected❗",
|
||||||
"upload-successfully": "Upload successfully",
|
"upload-successfully": "Upload successfully",
|
||||||
"file-drag-drop-prompt": "Drag and drop your file here to upload file",
|
"file-drag-drop-prompt": "Drag and drop your file here to upload file",
|
||||||
"select": "Select"
|
"select": "Select",
|
||||||
|
"search-bar-placeholder": "Search resource",
|
||||||
|
"create-dialog": {
|
||||||
|
"title": "Create Resource",
|
||||||
|
"upload-method": "Upload method",
|
||||||
|
"local-file": {
|
||||||
|
"option": "Local file",
|
||||||
|
"choose": "Choose a file..."
|
||||||
|
},
|
||||||
|
"external-link": {
|
||||||
|
"option": "External link",
|
||||||
|
"link": "Link",
|
||||||
|
"file-name": "File name",
|
||||||
|
"file-name-placeholder": "File name",
|
||||||
|
"type": "Type",
|
||||||
|
"type-placeholder": "File type"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"archived": {
|
"archived": {
|
||||||
"archived-memos": "Archived Memos",
|
"archived-memos": "Archived Memos",
|
||||||
@ -102,6 +120,7 @@
|
|||||||
"memo": {
|
"memo": {
|
||||||
"view-detail": "View Detail",
|
"view-detail": "View Detail",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
|
"embed": "Embed memo",
|
||||||
"visibility": {
|
"visibility": {
|
||||||
"private": "Only visible to you",
|
"private": "Only visible to you",
|
||||||
"protected": "Visible to members",
|
"protected": "Visible to members",
|
||||||
@ -155,6 +174,9 @@
|
|||||||
"search": {
|
"search": {
|
||||||
"quickly-filter": "Quickly filter"
|
"quickly-filter": "Quickly filter"
|
||||||
},
|
},
|
||||||
|
"search-bar": {
|
||||||
|
"input-placeholder": "Search memos"
|
||||||
|
},
|
||||||
"setting": {
|
"setting": {
|
||||||
"my-account": "My Account",
|
"my-account": "My Account",
|
||||||
"preference": "Preferences",
|
"preference": "Preferences",
|
||||||
@ -171,13 +193,15 @@
|
|||||||
"preference-section": {
|
"preference-section": {
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"default-memo-visibility": "Default memo visibility",
|
"default-memo-visibility": "Default memo visibility",
|
||||||
|
"default-resource-visibility": "Default resource visibility",
|
||||||
"enable-folding-memo": "Enable folding memo",
|
"enable-folding-memo": "Enable folding memo",
|
||||||
"enable-double-click": "Enable double-click to edit",
|
"enable-double-click": "Enable double-click to edit",
|
||||||
"editor-font-style": "Editor font style",
|
"editor-font-style": "Editor font style",
|
||||||
"mobile-editor-style": "Mobile editor style",
|
"mobile-editor-style": "Mobile editor style",
|
||||||
"default-memo-sort-option": "Memo display time",
|
"default-memo-sort-option": "Memo display time",
|
||||||
"created_ts": "Created Time",
|
"created_ts": "Created Time",
|
||||||
"updated_ts": "Updated Time"
|
"updated_ts": "Updated Time",
|
||||||
|
"daily-review-time-offset": "Daily Review Time Offset"
|
||||||
},
|
},
|
||||||
"storage-section": {
|
"storage-section": {
|
||||||
"storage-services-list": "Storage service list",
|
"storage-services-list": "Storage service list",
|
||||||
@ -273,5 +297,22 @@
|
|||||||
"sat": "Sat",
|
"sat": "Sat",
|
||||||
"sunday": "Sunday",
|
"sunday": "Sunday",
|
||||||
"sun": "Sun"
|
"sun": "Sun"
|
||||||
|
},
|
||||||
|
"ask-ai": {
|
||||||
|
"title": "Ask AI",
|
||||||
|
"not-enabled": "You have not set up your OpenAI API key.",
|
||||||
|
"go-to-settings": "Go to settings",
|
||||||
|
"placeholder": "Ask anything…"
|
||||||
|
},
|
||||||
|
"embed-memo": {
|
||||||
|
"title": "Embed Memo",
|
||||||
|
"text": "Copy and paste the below codes into your blog or website.",
|
||||||
|
"only-public-supported": "* Only the public memo supports.",
|
||||||
|
"copy": "Copy"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"PUBLIC": "PUBLIC",
|
||||||
|
"PROTECTED": "PROTECTED",
|
||||||
|
"PRIVATE": "PRIVATE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"about": "Про Memos",
|
"about": "Про Memos",
|
||||||
|
"home": "Главная",
|
||||||
|
"daily-review": "По дням",
|
||||||
|
"resources": "Ресурсы",
|
||||||
|
"ask-ai": "Спросить ИИ",
|
||||||
|
"archived": "В архиве",
|
||||||
"email": "Эл. почта",
|
"email": "Эл. почта",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"repeat-password-short": "Повторить",
|
"repeat-password-short": "Повторить",
|
||||||
@ -48,8 +53,9 @@
|
|||||||
"link": "Ссылка",
|
"link": "Ссылка",
|
||||||
"vacuum": "Сжать",
|
"vacuum": "Сжать",
|
||||||
"select": "Выбрать",
|
"select": "Выбрать",
|
||||||
"avatar": "Avatar",
|
"avatar": "Аватар",
|
||||||
"database": "Database"
|
"database": "База-Данных",
|
||||||
|
"settings": "Настройки"
|
||||||
},
|
},
|
||||||
"slogan": "Self-hosted платформа с открытым исходным кодом для заметок и управления записями с поддержкой социальных функций.",
|
"slogan": "Self-hosted платформа с открытым исходным кодом для заметок и управления записями с поддержкой социальных функций.",
|
||||||
"auth": {
|
"auth": {
|
||||||
@ -80,7 +86,25 @@
|
|||||||
"warning-text-unused": "Вы уверены, что хотите удалить неиспользуемые ресурсы? ЭТО ДЕЙСТВИЕ НЕВОЗМОЖНО ОТМЕНИТЬ❗",
|
"warning-text-unused": "Вы уверены, что хотите удалить неиспользуемые ресурсы? ЭТО ДЕЙСТВИЕ НЕВОЗМОЖНО ОТМЕНИТЬ❗",
|
||||||
"no-unused-resources": "Нет неиспользуемых ресурсов",
|
"no-unused-resources": "Нет неиспользуемых ресурсов",
|
||||||
"name": "Название",
|
"name": "Название",
|
||||||
"clear": "Clear"
|
"clear": "Отчистить",
|
||||||
|
"search-bar-placeholder": "Поиск ресурсов",
|
||||||
|
"select": "Выбрать",
|
||||||
|
"create-dialog": {
|
||||||
|
"title": "Создать ресурс",
|
||||||
|
"upload-method": "Метод загрузки",
|
||||||
|
"local-file": {
|
||||||
|
"option": "Локальный файл",
|
||||||
|
"choose": "Выберите файл..."
|
||||||
|
},
|
||||||
|
"external-link": {
|
||||||
|
"option": "Внешняя ссылка",
|
||||||
|
"link": "Ссылка",
|
||||||
|
"file-name": "Название файла",
|
||||||
|
"file-name-placeholder": "Название файла",
|
||||||
|
"type": "Тип",
|
||||||
|
"type-placeholder": "Тип файла"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"archived": {
|
"archived": {
|
||||||
"archived-memos": "Заархивированные записи",
|
"archived-memos": "Заархивированные записи",
|
||||||
@ -100,11 +124,12 @@
|
|||||||
"memo": {
|
"memo": {
|
||||||
"view-detail": "Подробно",
|
"view-detail": "Подробно",
|
||||||
"copy": "Копировать",
|
"copy": "Копировать",
|
||||||
|
"embed": "Встроить запись",
|
||||||
"visibility": {
|
"visibility": {
|
||||||
"private": "Видно только вам",
|
"private": "Видно только вам",
|
||||||
"protected": "Видно только пользователям",
|
"protected": "Видно только пользователям",
|
||||||
"public": "Видно всем",
|
"public": "Видно всем",
|
||||||
"disabled": "Public memos are disabled"
|
"disabled": "Публичные записи отключены"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"memo-list": {
|
"memo-list": {
|
||||||
@ -149,6 +174,9 @@
|
|||||||
"search": {
|
"search": {
|
||||||
"quickly-filter": "Быстрый фильтр"
|
"quickly-filter": "Быстрый фильтр"
|
||||||
},
|
},
|
||||||
|
"search-bar": {
|
||||||
|
"input-placeholder": "Поиск заметок"
|
||||||
|
},
|
||||||
"setting": {
|
"setting": {
|
||||||
"my-account": "Мой аккаунт",
|
"my-account": "Мой аккаунт",
|
||||||
"preference": "Настройки",
|
"preference": "Настройки",
|
||||||
@ -163,13 +191,15 @@
|
|||||||
"preference-section": {
|
"preference-section": {
|
||||||
"theme": "Тема",
|
"theme": "Тема",
|
||||||
"default-memo-visibility": "Видимость записей по умолчанию",
|
"default-memo-visibility": "Видимость записей по умолчанию",
|
||||||
|
"default-resource-visibility": "Видимость ресурсов по умолчанию",
|
||||||
"enable-folding-memo": "Включить сворачивание записей",
|
"enable-folding-memo": "Включить сворачивание записей",
|
||||||
"editor-font-style": "Стиль шрифта",
|
"editor-font-style": "Стиль шрифта",
|
||||||
"mobile-editor-style": "Стиль мобильного редактора",
|
"mobile-editor-style": "Стиль мобильного редактора",
|
||||||
"default-memo-sort-option": "Отображаемое время записи",
|
"default-memo-sort-option": "Отображаемое время записи",
|
||||||
"created_ts": "Время создания",
|
"created_ts": "Время создания",
|
||||||
"updated_ts": "Время обновления",
|
"updated_ts": "Время обновления",
|
||||||
"enable-double-click": "Enable double-click to edit"
|
"enable-double-click": "Разрешить двойной клик для редактирования",
|
||||||
|
"daily-review-time-offset": "Смещение времени ежедневного просмотра"
|
||||||
},
|
},
|
||||||
"member-section": {
|
"member-section": {
|
||||||
"create-a-member": "Создать пользователя"
|
"create-a-member": "Создать пользователя"
|
||||||
@ -187,30 +217,30 @@
|
|||||||
"additional-script": "Настраиваемый скрипт",
|
"additional-script": "Настраиваемый скрипт",
|
||||||
"additional-style-placeholder": "Настраиваемый код CSS",
|
"additional-style-placeholder": "Настраиваемый код CSS",
|
||||||
"additional-script-placeholder": "Настраиваемый код JavaScript",
|
"additional-script-placeholder": "Настраиваемый код JavaScript",
|
||||||
"disable-public-memos": "Disable public memos"
|
"disable-public-memos": "Отключить публичные записи"
|
||||||
},
|
},
|
||||||
"appearance-option": {
|
"appearance-option": {
|
||||||
"system": "Системная",
|
"system": "Системная",
|
||||||
"light": "Светлая",
|
"light": "Светлая",
|
||||||
"dark": "Тёмная"
|
"dark": "Тёмная"
|
||||||
},
|
},
|
||||||
"storage": "Storage",
|
"storage": "Хранилище",
|
||||||
"sso": "SSO",
|
"sso": "SSO",
|
||||||
"storage-section": {
|
"storage-section": {
|
||||||
"storage-services-list": "Storage service list",
|
"storage-services-list": "Список хранилищ",
|
||||||
"create-a-service": "Create a service",
|
"create-a-service": "Создать сервис",
|
||||||
"update-a-service": "Update a service",
|
"update-a-service": "Обновить сервис",
|
||||||
"warning-text": "Are you sure to delete this storage service? THIS ACTION IS IRREVERSIBLE❗",
|
"warning-text": "Вы уверены, что хотите удалить это хранилище? ЭТО ДЕЙСТВИЕ НЕВОЗМОЖНО ОТМЕНИТЬ❗",
|
||||||
"delete-storage": "Delete Storage"
|
"delete-storage": "Удалить Хранилище"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"amount-text": {
|
"amount-text": {
|
||||||
"memo_one": "ЗАПИСЬ",
|
"memo_one": "ЗАПИСЬ",
|
||||||
"tag_one": "ТЕГ",
|
"tag_one": "ТЕГ",
|
||||||
"day_one": "ДЕНЬ",
|
"day_one": "ДЕНЬ",
|
||||||
"memo_other": "MEMOS",
|
"memo_other": "ЗАПИСИ",
|
||||||
"tag_other": "TAGS",
|
"tag_other": "ТЕГИ",
|
||||||
"day_other": "DAYS"
|
"day_other": "ДНИ"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"no-memos": "нет записей 🌃",
|
"no-memos": "нет записей 🌃",
|
||||||
@ -226,7 +256,7 @@
|
|||||||
"image-load-failed": "Ошибка загрузки изображения",
|
"image-load-failed": "Ошибка загрузки изображения",
|
||||||
"fill-form": "Пожалуйста, заполните форму",
|
"fill-form": "Пожалуйста, заполните форму",
|
||||||
"login-failed": "Ошибка входа",
|
"login-failed": "Ошибка входа",
|
||||||
"signup-failed": "Помилка реєстрації",
|
"signup-failed": "Ошибка регистрации",
|
||||||
"user-not-found": "Пользователь не найден",
|
"user-not-found": "Пользователь не найден",
|
||||||
"password-changed": "Пароль изменён",
|
"password-changed": "Пароль изменён",
|
||||||
"private-only": "Это частная заметка.",
|
"private-only": "Это частная заметка.",
|
||||||
@ -249,8 +279,8 @@
|
|||||||
"succeed-update-customized-profile": "Собственный профиль успешно обновлён",
|
"succeed-update-customized-profile": "Собственный профиль успешно обновлён",
|
||||||
"succeed-update-additional-script": "Настраиваемый скрипт успешно обновлён",
|
"succeed-update-additional-script": "Настраиваемый скрипт успешно обновлён",
|
||||||
"update-succeed": "Успешно обновлено",
|
"update-succeed": "Успешно обновлено",
|
||||||
"succeed-copy-code": "Succeed to copy code to clipboard.",
|
"succeed-copy-code": "Код успешно скопирован.",
|
||||||
"page-not-found": "404 - Page Not Found 😥"
|
"page-not-found": "404 - Страница не найдена 😥"
|
||||||
},
|
},
|
||||||
"days": {
|
"days": {
|
||||||
"monday": "Понедельник",
|
"monday": "Понедельник",
|
||||||
@ -267,5 +297,22 @@
|
|||||||
"sat": "Сб.",
|
"sat": "Сб.",
|
||||||
"sunday": "Воскресенье",
|
"sunday": "Воскресенье",
|
||||||
"sun": "Вс."
|
"sun": "Вс."
|
||||||
|
},
|
||||||
|
"ask-ai": {
|
||||||
|
"title": "Спросить ИИ",
|
||||||
|
"not-enabled": "Вам нужно установить ключ OpenAI API.",
|
||||||
|
"go-to-settings": "Перейти в настройки",
|
||||||
|
"placeholder": "Спросите что угодно…"
|
||||||
|
},
|
||||||
|
"embed-memo": {
|
||||||
|
"title": "Встраивание записи",
|
||||||
|
"text": "Скопируйте и вставьте код в Ваш блог или сайт",
|
||||||
|
"only-public-supported": "* Поддерживаются только публичные записи",
|
||||||
|
"copy": "Скопировать"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"PUBLIC": "ПУБЛИЧНОЕ",
|
||||||
|
"PROTECTED": "ЗАЩИЩЁННОЕ",
|
||||||
|
"PRIVATE": "ЛИЧНОЕ"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import "./i18n";
|
|||||||
import "dayjs/locale/zh";
|
import "dayjs/locale/zh";
|
||||||
import "dayjs/locale/fr";
|
import "dayjs/locale/fr";
|
||||||
import "dayjs/locale/vi";
|
import "dayjs/locale/vi";
|
||||||
|
import "dayjs/locale/ru";
|
||||||
import "./less/code-highlight.less";
|
import "./less/code-highlight.less";
|
||||||
import "./css/global.css";
|
import "./css/global.css";
|
||||||
import "./css/tailwind.css";
|
import "./css/tailwind.css";
|
||||||
|
@ -12,9 +12,7 @@ import showPreviewImageDialog from "../components/PreviewImageDialog";
|
|||||||
import Icon from "../components/Icon";
|
import Icon from "../components/Icon";
|
||||||
import DatePicker from "../components/base/DatePicker";
|
import DatePicker from "../components/base/DatePicker";
|
||||||
import DailyMemo from "../components/DailyMemo";
|
import DailyMemo from "../components/DailyMemo";
|
||||||
|
import dayjs from "dayjs";
|
||||||
const monthChineseStrArray = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"];
|
|
||||||
const weekdayChineseStrArray = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
||||||
|
|
||||||
const DailyReview = () => {
|
const DailyReview = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -80,6 +78,9 @@ const DailyReview = () => {
|
|||||||
toggleShowDatePicker(false);
|
toggleShowDatePicker(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const currentMonth = dayjs().format("MMM");
|
||||||
|
const currentDayOfWeek = dayjs().format("ddd");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="w-full max-w-2xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
<section className="w-full max-w-2xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||||
<MobileHeader showSearch={false} />
|
<MobileHeader showSearch={false} />
|
||||||
@ -127,11 +128,11 @@ const DailyReview = () => {
|
|||||||
<div className="mx-auto font-bold text-gray-600 dark:text-gray-300 text-center leading-6 mb-2">{currentDate.getFullYear()}</div>
|
<div className="mx-auto font-bold text-gray-600 dark:text-gray-300 text-center leading-6 mb-2">{currentDate.getFullYear()}</div>
|
||||||
<div className="flex flex-col justify-center items-center m-auto w-24 h-24 shadow rounded-3xl dark:bg-zinc-800">
|
<div className="flex flex-col justify-center items-center m-auto w-24 h-24 shadow rounded-3xl dark:bg-zinc-800">
|
||||||
<div className="text-center w-full leading-6 text-sm text-white bg-blue-700 rounded-t-3xl">
|
<div className="text-center w-full leading-6 text-sm text-white bg-blue-700 rounded-t-3xl">
|
||||||
{monthChineseStrArray[currentDate.getMonth()]}
|
{currentMonth[0].toUpperCase() + currentMonth.substring(1)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-black dark:text-white text-4xl font-medium leading-12">{currentDate.getDate()}</div>
|
<div className="text-black dark:text-white text-4xl font-medium leading-12">{currentDate.getDate()}</div>
|
||||||
<div className="dark:text-gray-300 text-center w-full leading-6 -mt-2 text-xs">
|
<div className="dark:text-gray-300 text-center w-full leading-6 -mt-2 text-xs">
|
||||||
{weekdayChineseStrArray[currentDate.getDay()]}
|
{currentDayOfWeek[0].toUpperCase() + currentDayOfWeek.substring(1)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user