feat: add internationalization support for date selection and year input placeholders

This commit is contained in:
JOYCEQL
2025-03-15 11:34:46 +08:00
committed by qingchen
parent ed41a75e0e
commit efa61639a6
5 changed files with 604 additions and 194 deletions
+7 -5
View File
@@ -1,11 +1,9 @@
"use client";
import { useState, useEffect, useMemo } from "react";
import { motion } from "framer-motion";
import { CalendarIcon } from "lucide-react";
import { CalendarIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { ChevronUpIcon, ChevronDownIcon } from "lucide-react";
import { format } from "date-fns";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
@@ -159,7 +157,11 @@ const Field = ({
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{currentDate ? formatDate(currentDate) : <span></span>}
{currentDate ? (
formatDate(currentDate)
) : (
<span>{t("field.selectDate")}</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
@@ -167,7 +169,7 @@ const Field = ({
<div className="relative">
<Input
type="number"
placeholder="输入年份"
placeholder={t("field.enterYear")}
className="w-full pr-8 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
value={yearInput}
onChange={handleYearInput}
+318 -70
View File
@@ -1,5 +1,6 @@
import React from "react";
import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
import {
Popover,
PopoverContent,
@@ -86,119 +87,366 @@ interface IconSelectorProps {
onChange: (value: string) => void;
}
const iconOptions: IconOption[] = [
const getIconOptions = (t: any): IconOption[] => [
// 个人信息类
{ label: "用户", value: "User", icon: User, category: "个人信息" },
{ label: "邮箱", value: "Mail", icon: Mail, category: "个人信息" },
{ label: "电话", value: "Phone", icon: Phone, category: "个人信息" },
{ label: "地址", value: "MapPin", icon: MapPin, category: "个人信息" },
{ label: "网站", value: "Globe", icon: Globe, category: "个人信息" },
{
label: "手机",
label: t("icons.user"),
value: "User",
icon: User,
category: t("categories.personal"),
},
{
label: t("icons.email"),
value: "Mail",
icon: Mail,
category: t("categories.personal"),
},
{
label: t("icons.phone"),
value: "Phone",
icon: Phone,
category: t("categories.personal"),
},
{
label: t("icons.address"),
value: "MapPin",
icon: MapPin,
category: t("categories.personal"),
},
{
label: t("icons.website"),
value: "Globe",
icon: Globe,
category: t("categories.personal"),
},
{
label: t("icons.mobile"),
value: "Smartphone",
icon: Smartphone,
category: "个人信息",
category: t("categories.personal"),
},
// 教育背景类
{
label: "学历",
label: t("icons.education"),
value: "GraduationCap",
icon: GraduationCap,
category: "教育背景",
category: t("categories.education"),
},
{
label: t("icons.school"),
value: "School",
icon: School,
category: t("categories.education"),
},
{
label: t("icons.major"),
value: "Book",
icon: Book,
category: t("categories.education"),
},
{
label: t("icons.library"),
value: "Library",
icon: Library,
category: t("categories.education"),
},
{
label: t("icons.scholarship"),
value: "Award",
icon: Award,
category: t("categories.education"),
},
{ label: "学校", value: "School", icon: School, category: "教育背景" },
{ label: "专业", value: "Book", icon: Book, category: "教育背景" },
{ label: "图书馆", value: "Library", icon: Library, category: "教育背景" },
{ label: "奖学金", value: "Award", icon: Award, category: "教育背景" },
// 工作经验类
{ label: "工作", value: "Briefcase", icon: Briefcase, category: "工作经验" },
{ label: "公司", value: "Building2", icon: Building2, category: "工作经验" },
{ label: "办公室", value: "Building", icon: Building, category: "工作经验" },
{
label: "日期范围",
label: t("icons.work"),
value: "Briefcase",
icon: Briefcase,
category: t("categories.experience"),
},
{
label: t("icons.company"),
value: "Building2",
icon: Building2,
category: t("categories.experience"),
},
{
label: t("icons.office"),
value: "Building",
icon: Building,
category: t("categories.experience"),
},
{
label: t("icons.dateRange"),
value: "CalendarRange",
icon: CalendarRange,
category: "工作经验",
category: t("categories.experience"),
},
{
label: t("icons.workTime"),
value: "Clock",
icon: Clock,
category: t("categories.experience"),
},
{ label: "工作时间", value: "Clock", icon: Clock, category: "工作经验" },
// 技能类
{ label: "编程", value: "Code", icon: Code, category: "技能" },
{ label: "系统", value: "Cpu", icon: Cpu, category: "技能" },
{ label: "数据库", value: "Database", icon: Database, category: "技能" },
{ label: "终端", value: "Terminal", icon: Terminal, category: "技能" },
{ label: "技术栈", value: "Layers", icon: Layers, category: "技能" },
// 语言类
{ label: "语言", value: "Languages", icon: Languages, category: "语言" },
{
label: "口语",
value: "MessageSquare",
icon: MessageSquare,
category: "语言",
label: t("icons.programming"),
value: "Code",
icon: Code,
category: t("categories.skills"),
},
{
label: "交流",
label: t("icons.system"),
value: "Cpu",
icon: Cpu,
category: t("categories.skills"),
},
{
label: t("icons.database"),
value: "Database",
icon: Database,
category: t("categories.skills"),
},
{
label: t("icons.terminal"),
value: "Terminal",
icon: Terminal,
category: t("categories.skills"),
},
{
label: t("icons.techStack"),
value: "Layers",
icon: Layers,
category: t("categories.skills"),
},
// 语言类
{
label: t("icons.language"),
value: "Languages",
icon: Languages,
category: t("categories.languages"),
},
{
label: t("icons.speaking"),
value: "MessageSquare",
icon: MessageSquare,
category: t("categories.languages"),
},
{
label: t("icons.communication"),
value: "MessagesSquare",
icon: MessagesSquare,
category: "语言",
category: t("categories.languages"),
},
// 项目经验类
{ label: "项目", value: "FolderGit2", icon: FolderGit2, category: "项目" },
{ label: "分支", value: "GitBranch", icon: GitBranch, category: "项目" },
{ label: "发布", value: "Rocket", icon: Rocket, category: "项目" },
{ label: "目标", value: "Target", icon: Target, category: "项目" },
{
label: t("icons.project"),
value: "FolderGit2",
icon: FolderGit2,
category: t("categories.projects"),
},
{
label: t("icons.branch"),
value: "GitBranch",
icon: GitBranch,
category: t("categories.projects"),
},
{
label: t("icons.release"),
value: "Rocket",
icon: Rocket,
category: t("categories.projects"),
},
{
label: t("icons.target"),
value: "Target",
icon: Target,
category: t("categories.projects"),
},
// 成就与证书类
{ label: "奖杯", value: "Trophy", icon: Trophy, category: "成就证书" },
{ label: "奖牌", value: "Medal", icon: Medal, category: "成就证书" },
{ label: "星级", value: "Star", icon: Star, category: "成就证书" },
{
label: t("icons.trophy"),
value: "Trophy",
icon: Trophy,
category: t("categories.achievements"),
},
{
label: t("icons.medal"),
value: "Medal",
icon: Medal,
category: t("categories.achievements"),
},
{
label: t("icons.star"),
value: "Star",
icon: Star,
category: t("categories.achievements"),
},
// 兴趣爱好类
{ label: "兴趣", value: "Heart", icon: Heart, category: "兴趣爱好" },
{ label: "音乐", value: "Music", icon: Music, category: "兴趣爱好" },
{ label: "艺术", value: "Palette", icon: Palette, category: "兴趣爱好" },
{ label: "摄影", value: "Camera", icon: Camera, category: "兴趣爱好" },
{
label: t("icons.interest"),
value: "Heart",
icon: Heart,
category: t("categories.hobbies"),
},
{
label: t("icons.music"),
value: "Music",
icon: Music,
category: t("categories.hobbies"),
},
{
label: t("icons.art"),
value: "Palette",
icon: Palette,
category: t("categories.hobbies"),
},
{
label: t("icons.photography"),
value: "Camera",
icon: Camera,
category: t("categories.hobbies"),
},
// 社交媒体类
{ label: "Github", value: "Github", icon: Github, category: "社交媒体" },
{ label: "领英", value: "Linkedin", icon: Linkedin, category: "社交媒体" },
{ label: "推特", value: "Twitter", icon: Twitter, category: "社交媒体" },
{ label: "脸书", value: "Facebook", icon: Facebook, category: "社交媒体" },
{ label: "照片", value: "Instagram", icon: Instagram, category: "社交媒体" },
{
label: "Github",
value: "Github",
icon: Github,
category: t("categories.social"),
},
{
label: t("icons.linkedin"),
value: "Linkedin",
icon: Linkedin,
category: t("categories.social"),
},
{
label: t("icons.twitter"),
value: "Twitter",
icon: Twitter,
category: t("categories.social"),
},
{
label: t("icons.facebook"),
value: "Facebook",
icon: Facebook,
category: t("categories.social"),
},
{
label: t("icons.instagram"),
value: "Instagram",
icon: Instagram,
category: t("categories.social"),
},
// 其他类
{ label: "简介", value: "FileText", icon: FileText, category: "其他" },
{ label: "审核", value: "FileCheck", icon: FileCheck, category: "其他" },
{ label: "筛选", value: "Filter", icon: Filter, category: "其他" },
{ label: "链接", value: "Link", icon: Link, category: "其他" },
{ label: "薪资", value: "Wallet", icon: Wallet, category: "其他" },
{ label: "创意", value: "Lightbulb", icon: Lightbulb, category: "其他" },
{ label: "发送", value: "Send", icon: Send, category: "其他" },
{ label: "分享", value: "Share2", icon: Share2, category: "其他" },
{ label: "设置", value: "Settings", icon: Settings, category: "其他" },
{ label: "搜索", value: "SearchIcon", icon: SearchIcon, category: "其他" },
{ label: "标记", value: "Flag", icon: Flag, category: "其他" },
{ label: "收藏", value: "Bookmark", icon: Bookmark, category: "其他" },
{ label: "点赞", value: "ThumbsUp", icon: ThumbsUp, category: "其他" },
{ label: "技能", value: "Zap", icon: Zap, category: "其他" },
{
label: t("icons.profile"),
value: "FileText",
icon: FileText,
category: t("categories.others"),
},
{
label: t("icons.review"),
value: "FileCheck",
icon: FileCheck,
category: t("categories.others"),
},
{
label: t("icons.filter"),
value: "Filter",
icon: Filter,
category: t("categories.others"),
},
{
label: t("icons.link"),
value: "Link",
icon: Link,
category: t("categories.others"),
},
{
label: t("icons.salary"),
value: "Wallet",
icon: Wallet,
category: t("categories.others"),
},
{
label: t("icons.idea"),
value: "Lightbulb",
icon: Lightbulb,
category: t("categories.others"),
},
{
label: t("icons.send"),
value: "Send",
icon: Send,
category: t("categories.others"),
},
{
label: t("icons.share"),
value: "Share2",
icon: Share2,
category: t("categories.others"),
},
{
label: t("icons.settings"),
value: "Settings",
icon: Settings,
category: t("categories.others"),
},
{
label: t("icons.search"),
value: "SearchIcon",
icon: SearchIcon,
category: t("categories.others"),
},
{
label: t("icons.flag"),
value: "Flag",
icon: Flag,
category: t("categories.others"),
},
{
label: t("icons.bookmark"),
value: "Bookmark",
icon: Bookmark,
category: t("categories.others"),
},
{
label: t("icons.thumbsUp"),
value: "ThumbsUp",
icon: ThumbsUp,
category: t("categories.others"),
},
{
label: t("icons.skill"),
value: "Zap",
icon: Zap,
category: t("categories.others"),
},
];
const IconSelector: React.FC<IconSelectorProps> = ({ value, onChange }) => {
const t = useTranslations("iconSelector");
const iconOptions = React.useMemo(() => getIconOptions(t), [t]);
const [open, setOpen] = React.useState(false);
const [searchTerm, setSearchTerm] = React.useState("");
const [isHovered, setIsHovered] = React.useState("");
const [selectedCategory, setSelectedCategory] = React.useState("全部");
const [selectedCategory, setSelectedCategory] = React.useState(t("all"));
const selectedIcon =
iconOptions.find((i) => i.value === value) || iconOptions[0];
const Icon = selectedIcon.icon;
const categories = [
"全部",
t("all"),
...Array.from(new Set(iconOptions.map((icon) => icon.category))),
];
@@ -207,7 +455,7 @@ const IconSelector: React.FC<IconSelectorProps> = ({ value, onChange }) => {
icon.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
icon.value.toLowerCase().includes(searchTerm.toLowerCase());
const matchesCategory =
selectedCategory === "全部" || icon.category === selectedCategory;
selectedCategory === t("all") || icon.category === selectedCategory;
return matchesSearch && matchesCategory;
});
@@ -266,7 +514,7 @@ const IconSelector: React.FC<IconSelectorProps> = ({ value, onChange }) => {
/>
<input
type="text"
placeholder="搜索图标..."
placeholder={t("searchPlaceholder")}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className={cn(
@@ -354,9 +602,9 @@ const IconSelector: React.FC<IconSelectorProps> = ({ value, onChange }) => {
)}
>
<SearchIcon className="w-12 h-12 mb-2 opacity-20" />
<p></p>
<p>{t("noMatchingIcons")}</p>
<p className="text-xs opacity-70">
{searchTerm ? "请尝试其他搜索关键词" : "请选择其他分类"}
{searchTerm ? t("tryOtherKeywords") : t("selectOtherCategory")}
</p>
</div>
)}
+49 -119
View File
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect } from "react";
import { useEditor, EditorContent, BubbleMenu } from "@tiptap/react";
import { useEditor, EditorContent } from "@tiptap/react";
import { useTranslations } from "next-intl";
import StarterKit from "@tiptap/starter-kit";
import TextAlign from "@tiptap/extension-text-align";
import TextStyle from "@tiptap/extension-text-style";
@@ -16,19 +17,15 @@ import {
Bold,
Italic,
Underline as UnderlineIcon,
Strikethrough,
AlignLeft,
AlignCenter,
AlignRight,
AlignJustify,
List,
ListOrdered,
Quote,
Undo,
Redo,
PaintBucket,
Type,
ChevronDown,
Highlighter,
Wand2,
} from "lucide-react";
@@ -47,25 +44,30 @@ interface RichTextEditorProps {
onPolish?: () => void;
}
const COLORS = [
{ label: "黑色", value: "#000000" },
{ label: "深灰", value: "#333333" },
{ label: "灰色", value: "#666666" },
{ label: "红色", value: "#FF0000" },
{ label: "橙色", value: "#FF4D00" },
{ label: "橙黄", value: "#FF9900" },
{ label: "黄色", value: "#FFCC00" },
{ label: "黄绿", value: "#33CC00" },
{ label: "绿色", value: "#00CC00" },
{ label: "青色", value: "#00CCCC" },
{ label: "浅蓝", value: "#0066FF" },
{ label: "蓝色", value: "#0000FF" },
{ label: "紫色", value: "#6600FF" },
{ label: "紫红", value: "#CC00FF" },
{ label: "粉色", value: "#FF00FF" },
interface ColorOption {
label: string;
value: string;
}
const getColors = (t: any): ColorOption[] => [
{ label: t("colors.black"), value: "#000000" },
{ label: t("colors.darkGray"), value: "#333333" },
{ label: t("colors.gray"), value: "#666666" },
{ label: t("colors.red"), value: "#FF0000" },
{ label: t("colors.orange"), value: "#FF4D00" },
{ label: t("colors.orangeYellow"), value: "#FF9900" },
{ label: t("colors.yellow"), value: "#FFCC00" },
{ label: t("colors.yellowGreen"), value: "#33CC00" },
{ label: t("colors.green"), value: "#00CC00" },
{ label: t("colors.cyan"), value: "#00CCCC" },
{ label: t("colors.lightBlue"), value: "#0066FF" },
{ label: t("colors.blue"), value: "#0000FF" },
{ label: t("colors.purple"), value: "#6600FF" },
{ label: t("colors.magenta"), value: "#CC00FF" },
{ label: t("colors.pink"), value: "#FF00FF" },
];
const BG_COLORS = COLORS;
const getBgColors = getColors;
interface MenuButtonProps {
onClick: () => void;
@@ -140,6 +142,8 @@ const MenuButton = ({
const TextColorButton = ({ editor }) => {
const [activeColor, setActiveColor] = React.useState<string | null>(null);
const t = useTranslations("richEditor");
const colors = getColors(t);
React.useEffect(() => {
const color = editor?.getAttributes("textStyle").color;
@@ -163,14 +167,14 @@ const TextColorButton = ({ editor }) => {
: "none",
}}
/>
<span className="sr-only"></span>
<span className="sr-only">{t("textColor")}</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-64 p-3 rounded-lg">
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<PaintBucket className="h-4 w-4" />
<span className="text-sm font-medium"></span>
<span className="text-sm font-medium">{t("textColor")}</span>
</div>
<div className="grid grid-cols-5 gap-1">
<button
@@ -184,7 +188,7 @@ const TextColorButton = ({ editor }) => {
/
</span>
</button>
{COLORS.map((color) => (
{colors.map((color: ColorOption) => (
<button
key={color.value}
className={`h-8 w-8 rounded-md border hover:scale-110 transition-transform relative
@@ -214,8 +218,10 @@ const TextColorButton = ({ editor }) => {
const BackgroundColorButton = ({ editor }) => {
const [activeBgColor, setActiveBgColor] = React.useState<string | null>(null);
const t = useTranslations("richEditor");
const bgColors = getBgColors(t);
React.useEffect(() => {
useEffect(() => {
// highlight 属性可能直接返回颜色值
const highlight =
editor?.getAttributes("highlight").color ||
@@ -248,14 +254,14 @@ const BackgroundColorButton = ({ editor }) => {
/>
)}
</div>
<span className="sr-only"></span>
<span className="sr-only">{t("backgroundColor")}</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-64 p-3 rounded-lg">
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<Highlighter className="h-4 w-4" />
<span className="text-sm font-medium"></span>
<span className="text-sm font-medium">{t("backgroundColor")}</span>
</div>
<div className="grid grid-cols-5 gap-1">
<button
@@ -269,7 +275,7 @@ const BackgroundColorButton = ({ editor }) => {
/
</span>
</button>
{BG_COLORS.map((color) => (
{bgColors.map((color: ColorOption) => (
<button
key={color.value}
className={`h-8 w-8 rounded-md border hover:scale-110 transition-transform relative
@@ -300,81 +306,12 @@ const BackgroundColorButton = ({ editor }) => {
);
};
const HeadingSelect = ({ editor }) => {
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant="ghost"
size="sm"
className={cn(
"h-9 px-2.5 rounded-md flex items-center gap-1.5 min-w-[100px]",
"hover:bg-primary/5 dark:hover:bg-neutral-800"
)}
>
<Type className="h-5 w-5" />
<span className="text-sm">
{editor.isActive("heading", { level: 1 })
? "标题 1"
: editor.isActive("heading", { level: 2 })
? "标题 2"
: editor.isActive("heading", { level: 3 })
? "标题 3"
: "正文"}
</span>
<ChevronDown className="h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className={cn("w-40 p-1 rounded-lg", "bg-white dark:bg-neutral-900")}
>
<div className="flex flex-col gap-0.5">
{[
{ label: "正文", value: "p" },
{ label: "标题 1", value: "1" },
{ label: "标题 2", value: "2" },
{ label: "标题 3", value: "3" },
].map((item) => (
<Button
key={item.value}
variant="ghost"
className={cn(
"w-full justify-start px-2 py-1.5 text-sm rounded-md",
editor.isActive(
item.value === "p" ? "paragraph" : "heading",
item.value === "p"
? undefined
: { level: parseInt(item.value) }
)
? "bg-primary/10 text-primary dark:bg-neutral-800 dark:text-neutral-200"
: ""
)}
onClick={() => {
if (item.value === "p") {
editor.chain().focus().setParagraph().run();
} else {
editor
.chain()
.focus()
.toggleHeading({ level: parseInt(item.value) })
.run();
}
}}
>
{item.label}
</Button>
))}
</div>
</PopoverContent>
</Popover>
);
};
const RichTextEditor = ({
content = "",
onChange,
onPolish,
}: RichTextEditorProps) => {
const t = useTranslations("richEditor");
const editor = useEditor({
extensions: [
StarterKit.configure({
@@ -446,42 +383,35 @@ const RichTextEditor = ({
)}
onClick={(e) => e.stopPropagation()}
>
{/* Toolbar */}
<div
className={cn(
"border-b px-2 py-1.5 flex flex-wrap items-center gap-3",
"bg-background dark:bg-neutral-900/50 dark:border-neutral-800"
)}
>
<div className="flex items-center">
<HeadingSelect editor={editor} />
</div>
<div className={cn("h-5 w-px", "bg-border/60 dark:bg-neutral-800")} />
<div className="flex items-center gap-0.5">
<MenuButton
onClick={() => editor.chain().focus().toggleBold().run()}
isActive={editor.isActive("bold")}
tooltip="加粗"
tooltip={t("bold")}
>
<Bold className="h-5 w-5" />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleItalic().run()}
isActive={editor.isActive("italic")}
tooltip="斜体"
tooltip={t("italic")}
>
<Italic className="h-5 w-5" />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleUnderline().run()}
isActive={editor.isActive("underline")}
tooltip="下划线"
tooltip={t("underline")}
>
<UnderlineIcon className="h-5 w-5" />
</MenuButton>
<TextColorButton editor={editor} tooltip="文字颜色" />
<TextColorButton editor={editor} />
<BackgroundColorButton editor={editor} />
</div>
@@ -491,28 +421,28 @@ const RichTextEditor = ({
<MenuButton
onClick={() => editor.chain().focus().setTextAlign("left").run()}
isActive={editor.isActive({ textAlign: "left" })}
tooltip="左对齐"
tooltip={t("alignLeft")}
>
<AlignLeft className="h-5 w-5" />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().setTextAlign("center").run()}
isActive={editor.isActive({ textAlign: "center" })}
tooltip="居中对齐"
tooltip={t("alignCenter")}
>
<AlignCenter className="h-5 w-5" />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().setTextAlign("right").run()}
isActive={editor.isActive({ textAlign: "right" })}
tooltip="右对齐"
tooltip={t("alignRight")}
>
<AlignRight className="h-5 w-5" />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().setTextAlign("justify").run()}
isActive={editor.isActive({ textAlign: "justify" })}
tooltip="两端对齐"
tooltip={t("alignJustify")}
>
<AlignJustify className="h-5 w-5" />
</MenuButton>
@@ -524,14 +454,14 @@ const RichTextEditor = ({
<MenuButton
onClick={() => editor.chain().focus().toggleBulletList().run()}
isActive={editor.isActive("bulletList")}
tooltip="无序列表"
tooltip={t("bulletList")}
>
<List className="h-5 w-5" />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().toggleOrderedList().run()}
isActive={editor.isActive("orderedList")}
tooltip="有序列表"
tooltip={t("orderedList")}
>
<ListOrdered className="h-5 w-5" />
</MenuButton>
@@ -543,14 +473,14 @@ const RichTextEditor = ({
<MenuButton
onClick={() => editor.chain().focus().undo().run()}
disabled={!editor.can().undo()}
tooltip="撤销"
tooltip={t("undo")}
>
<Undo className="h-4 w-4" />
</MenuButton>
<MenuButton
onClick={() => editor.chain().focus().redo().run()}
disabled={!editor.can().redo()}
tooltip="重做"
tooltip={t("redo")}
>
<Redo className="h-4 w-4" />
</MenuButton>
@@ -562,7 +492,7 @@ const RichTextEditor = ({
className="bg-gradient-to-r from-purple-400 to-pink-500 hover:from-pink-500 hover:to-purple-400 text-white border-none shadow-md transition-all duration-300"
>
<Wand2 className="h-4 w-4 mr-2" />
AI
{t("aiPolish")}
</Button>
)}
</div>
+115
View File
@@ -434,6 +434,121 @@
}
}
},
"field": {
"selectDate": "Select Date",
"enterYear": "Enter Year"
},
"richEditor": {
"bold": "Bold",
"italic": "Italic",
"underline": "Underline",
"textColor": "Text Color",
"backgroundColor": "Background Color",
"alignLeft": "Align Left",
"alignCenter": "Align Center",
"alignRight": "Align Right",
"alignJustify": "Justify",
"bulletList": "Bullet List",
"orderedList": "Ordered List",
"undo": "Undo",
"redo": "Redo",
"aiPolish": "AI Polish",
"paragraph": "Paragraph",
"heading1": "Heading 1",
"heading2": "Heading 2",
"heading3": "Heading 3",
"colors": {
"black": "Black",
"darkGray": "Dark Gray",
"gray": "Gray",
"red": "Red",
"orange": "Orange",
"orangeYellow": "Orange Yellow",
"yellow": "Yellow",
"yellowGreen": "Yellow Green",
"green": "Green",
"cyan": "Cyan",
"lightBlue": "Light Blue",
"blue": "Blue",
"purple": "Purple",
"magenta": "Magenta",
"pink": "Pink"
}
},
"iconSelector": {
"all": "All",
"searchPlaceholder": "Search icons...",
"noMatchingIcons": "No matching icons found",
"tryOtherKeywords": "Please try other search keywords",
"selectOtherCategory": "Please select another category",
"categories": {
"personal": "Personal Info",
"education": "Education",
"experience": "Work Experience",
"skills": "Skills",
"languages": "Languages",
"projects": "Projects",
"achievements": "Achievements",
"hobbies": "Hobbies",
"social": "Social Media",
"others": "Others"
},
"icons": {
"user": "User",
"email": "Email",
"phone": "Phone",
"address": "Address",
"website": "Website",
"mobile": "Mobile",
"education": "Education",
"school": "School",
"major": "Major",
"library": "Library",
"scholarship": "Scholarship",
"work": "Work",
"company": "Company",
"office": "Office",
"dateRange": "Date Range",
"workTime": "Work Time",
"programming": "Programming",
"system": "System",
"database": "Database",
"terminal": "Terminal",
"techStack": "Tech Stack",
"language": "Language",
"speaking": "Speaking",
"communication": "Communication",
"project": "Project",
"branch": "Branch",
"release": "Release",
"target": "Target",
"trophy": "Trophy",
"medal": "Medal",
"star": "Star",
"interest": "Interest",
"music": "Music",
"art": "Art",
"photography": "Photography",
"linkedin": "LinkedIn",
"twitter": "Twitter",
"facebook": "Facebook",
"instagram": "Instagram",
"profile": "Profile",
"review": "Review",
"filter": "Filter",
"link": "Link",
"salary": "Salary",
"idea": "Idea",
"send": "Send",
"share": "Share",
"settings": "Settings",
"search": "Search",
"flag": "Flag",
"bookmark": "Bookmark",
"thumbsUp": "Thumbs Up",
"skill": "Skill"
}
},
"aiPolishDialog": {
"title": "AI Polish",
"description": {
+115
View File
@@ -411,6 +411,121 @@
}
}
},
"field": {
"selectDate": "选择日期",
"enterYear": "输入年份"
},
"richEditor": {
"bold": "加粗",
"italic": "斜体",
"underline": "下划线",
"textColor": "文字颜色",
"backgroundColor": "背景颜色",
"alignLeft": "左对齐",
"alignCenter": "居中对齐",
"alignRight": "右对齐",
"alignJustify": "两端对齐",
"bulletList": "无序列表",
"orderedList": "有序列表",
"undo": "撤销",
"redo": "重做",
"aiPolish": "AI 润色",
"paragraph": "正文",
"heading1": "标题 1",
"heading2": "标题 2",
"heading3": "标题 3",
"colors": {
"black": "黑色",
"darkGray": "深灰",
"gray": "灰色",
"red": "红色",
"orange": "橙色",
"orangeYellow": "橙黄",
"yellow": "黄色",
"yellowGreen": "黄绿",
"green": "绿色",
"cyan": "青色",
"lightBlue": "浅蓝",
"blue": "蓝色",
"purple": "紫色",
"magenta": "紫红",
"pink": "粉色"
}
},
"iconSelector": {
"all": "全部",
"searchPlaceholder": "搜索图标...",
"noMatchingIcons": "未找到匹配的图标",
"tryOtherKeywords": "请尝试其他搜索关键词",
"selectOtherCategory": "请选择其他分类",
"categories": {
"personal": "个人信息",
"education": "教育背景",
"experience": "工作经验",
"skills": "技能",
"languages": "语言",
"projects": "项目",
"achievements": "成就证书",
"hobbies": "兴趣爱好",
"social": "社交媒体",
"others": "其他"
},
"icons": {
"user": "用户",
"email": "邮箱",
"phone": "电话",
"address": "地址",
"website": "网站",
"mobile": "手机",
"education": "学历",
"school": "学校",
"major": "专业",
"library": "图书馆",
"scholarship": "奖学金",
"work": "工作",
"company": "公司",
"office": "办公室",
"dateRange": "日期范围",
"workTime": "工作时间",
"programming": "编程",
"system": "系统",
"database": "数据库",
"terminal": "终端",
"techStack": "技术栈",
"language": "语言",
"speaking": "口语",
"communication": "交流",
"project": "项目",
"branch": "分支",
"release": "发布",
"target": "目标",
"trophy": "奖杯",
"medal": "奖牌",
"star": "星级",
"interest": "兴趣",
"music": "音乐",
"art": "艺术",
"photography": "摄影",
"linkedin": "领英",
"twitter": "推特",
"facebook": "脸书",
"instagram": "照片",
"profile": "简介",
"review": "审核",
"filter": "筛选",
"link": "链接",
"salary": "薪资",
"idea": "创意",
"send": "发送",
"share": "分享",
"settings": "设置",
"search": "搜索",
"flag": "标记",
"bookmark": "收藏",
"thumbsUp": "点赞",
"skill": "技能"
}
},
"photoConfig": {
"title": "头像设置",
"description": "自定义您的简历头像",