diff --git a/apps/fronted/src/components/editor/Field.tsx b/apps/fronted/src/components/editor/Field.tsx index 2ff261c..cd1bbcf 100644 --- a/apps/fronted/src/components/editor/Field.tsx +++ b/apps/fronted/src/components/editor/Field.tsx @@ -144,6 +144,7 @@ const Field = ({ ); return; } + setShowPolishDialog(true); }} /> diff --git a/apps/fronted/src/components/editor/SidePanel.tsx b/apps/fronted/src/components/editor/SidePanel.tsx index e5dd3f1..a370d4a 100644 --- a/apps/fronted/src/components/editor/SidePanel.tsx +++ b/apps/fronted/src/components/editor/SidePanel.tsx @@ -3,6 +3,7 @@ import { useMemo } from "react"; import { motion } from "framer-motion"; import { Layout, Type, SpaceIcon, Palette } from "lucide-react"; import debounce from "lodash/debounce"; +import { useTranslations } from "next-intl"; import { Select, SelectContent, @@ -81,6 +82,19 @@ export function SidePanel() { } = activeResume || {}; const { themeColor } = globalSettings; + const t = useTranslations('workbench.sidePanel'); + + const fontOptions = [ + { value: "sans", label: t('typography.font.sans') }, + { value: "serif", label: t('typography.font.serif') }, + { value: "mono", label: t('typography.font.mono') }, + ]; + + const lineHeightOptions = [ + { value: "normal", label: t('typography.lineHeight.normal') }, + { value: "relaxed", label: t('typography.lineHeight.relaxed') }, + { value: "loose", label: t('typography.lineHeight.loose') }, + ]; const debouncedSetColor = useMemo( () => @@ -122,7 +136,7 @@ export function SidePanel() { animate={{ x: 0, opacity: 1 }} >
- + - 添加自定义模块 + {t('layout.addCustomSection')}
{/* 主题色设置 */} - +
{THEME_COLORS.map((presetTheme) => ( @@ -186,7 +200,7 @@ export function SidePanel() {
- 自定义 + {t('theme.custom')}
{/* 排版设置 */} - +
{/*
{[12, 13, 14, 15, 16, 18, 20, 24].map((size) => ( @@ -298,7 +311,7 @@ export function SidePanel() {
{/* 间距设置 */} - +
{/* 模式设置 */} - +
= ({ onUpdate, onDelete, }) => { + const t = useTranslations("workbench.basicPanel"); + return ( = ({ label: value, }) } - placeholder="标签" + placeholder={t("customFields.placeholders.label")} className={cn( "bg-neutral-50 dark:bg-neutral-900", "border-neutral-200 dark:border-neutral-700", @@ -87,7 +90,7 @@ const CustomField: React.FC = ({ value: value, }) } - placeholder="值" + placeholder={t("customFields.placeholders.value")} className={cn( "bg-neutral-50 dark:bg-neutral-900", "border-neutral-200 dark:border-neutral-700", @@ -142,6 +145,7 @@ const BasicPanel: React.FC = () => { visible: field.visible ?? true, })); }); + const t = useTranslations("workbench.basicPanel"); const handleBasicReorder = (newOrder: BasicFieldType[]) => { setBasicFields(newOrder); @@ -205,6 +209,7 @@ const BasicPanel: React.FC = () => { customFields: newOrder, }); }; + const renderBasicField = (field: BasicFieldType) => { const selectedIcon = basic?.icons?.[field.key] || "User"; @@ -258,7 +263,7 @@ const BasicPanel: React.FC = () => { /> )}
- {field.label} + {t(`basicFields.${field.key}`)}
{ - 基本字段 + {t("basicField")} { - 自定义字段 + {t("customField")} { > @@ -367,7 +372,7 @@ const BasicPanel: React.FC = () => { >
- Github 贡献图 + {t("githubContributions")} = ({ education, onSave, }) => { + const t = useTranslations("workbench.educationItem"); const handleChange = (field: keyof Education, value: string) => { onSave({ ...education, @@ -37,40 +39,40 @@ const EducationEditor: React.FC = ({
handleChange("school", value)} - placeholder="学校名称" + placeholder={t("placeholders.school")} required /> handleChange("location", value)} - placeholder="城市" + placeholder={t("placeholders.location")} />
handleChange("major", value)} - placeholder="专业名称" + placeholder={t("placeholders.major")} required /> handleChange("degree", value)} - placeholder="学历" + placeholder={t("placeholders.degree")} required />
handleChange("startDate", value)} type="date" @@ -78,7 +80,7 @@ const EducationEditor: React.FC = ({ required /> handleChange("endDate", value)} type="date" @@ -88,18 +90,18 @@ const EducationEditor: React.FC = ({
handleChange("gpa", value)} - placeholder="选填" + placeholder={t("placeholders.gpa")} /> handleChange("description", value)} type="editor" - placeholder="描述你的在校表现、获奖经历等..." + placeholder={t("placeholders.description")} />
diff --git a/apps/fronted/src/components/editor/education/EducationPanel.tsx b/apps/fronted/src/components/editor/education/EducationPanel.tsx index 68f8f41..3ab721d 100644 --- a/apps/fronted/src/components/editor/education/EducationPanel.tsx +++ b/apps/fronted/src/components/editor/education/EducationPanel.tsx @@ -3,20 +3,22 @@ import { cn } from "@/lib/utils"; import { useResumeStore } from "@/store/useResumeStore"; import { Reorder } from "framer-motion"; import { PlusCircle } from "lucide-react"; +import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import EducationItem from "./EducationItem"; import { Education } from "@/types/resume"; const EducationPanel = () => { + const t = useTranslations('workbench.educationPanel'); const { activeResume, updateEducation, updateEducationBatch } = useResumeStore(); const { education = [] } = activeResume || {}; const handleCreateProject = () => { const newEducation: Education = { id: crypto.randomUUID(), - school: "家里蹲大学", - major: "", - degree: "", + school: t('defaultProject.school'), + major: t('defaultProject.major'), + degree: t('defaultProject.degree'), startDate: "2015-09-01", endDate: "2019-06-30", description: "", @@ -50,10 +52,11 @@ const EducationPanel = () => {
); }; + export default EducationPanel; diff --git a/apps/fronted/src/components/editor/experience/ExperienceItem.tsx b/apps/fronted/src/components/editor/experience/ExperienceItem.tsx index abcd999..cc84f8a 100644 --- a/apps/fronted/src/components/editor/experience/ExperienceItem.tsx +++ b/apps/fronted/src/components/editor/experience/ExperienceItem.tsx @@ -13,6 +13,7 @@ import Field from "../Field"; import { Experience } from "@/types/resume"; import ThemeModal from "@/components/shared/ThemeModal"; import { useResumeStore } from "@/store/useResumeStore"; +import { useTranslations } from "next-intl"; interface ProjectEditorProps { experience: Experience; @@ -25,6 +26,8 @@ const ProjectEditor: React.FC = ({ experience, onSave, }) => { + const t = useTranslations("workbench.experiencePanel"); + const handleChange = (field: keyof Experience, value: string) => { onSave({ ...experience, @@ -37,33 +40,33 @@ const ProjectEditor: React.FC = ({
handleChange("company", value)} - placeholder="项目名称" + placeholder={t("placeholders.company")} required /> handleChange("position", value)} - placeholder="如:前端工程师" + placeholder={t("placeholders.position")} required />
handleChange("date", value)} - placeholder="如:2023.01 - 2023.06" + placeholder={t("placeholders.date")} required /> handleChange("details", value)} type="editor" - placeholder="简要描述项目的背景和目标..." + placeholder={t("placeholders.details")} />
diff --git a/apps/fronted/src/components/editor/experience/ExperiencePanel.tsx b/apps/fronted/src/components/editor/experience/ExperiencePanel.tsx index 2acab73..2511bbb 100644 --- a/apps/fronted/src/components/editor/experience/ExperiencePanel.tsx +++ b/apps/fronted/src/components/editor/experience/ExperiencePanel.tsx @@ -3,21 +3,23 @@ import { cn } from "@/lib/utils"; import { Reorder } from "framer-motion"; import { PlusCircle } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { useTranslations } from "next-intl"; import ExperienceItem from "./ExperienceItem"; import { Experience } from "@/types/resume"; import { useResumeStore } from "@/store/useResumeStore"; const ExperiencePanel = () => { + const t = useTranslations("workbench.experiencePanel"); const { activeResume, updateExperience, updateExperienceBatch } = useResumeStore(); const { experience = [] } = activeResume || {}; const handleCreateProject = () => { const newProject: Experience = { id: crypto.randomUUID(), - company: "某科技有限公司", - position: "高级前端工程师", - date: "2020-至今", - details: "负责公司核心产品...", + company: t("defaultProject.company"), + position: t("defaultProject.position"), + date: t("defaultProject.date"), + details: t("defaultProject.details"), visible: true, }; updateExperience(newProject); @@ -44,10 +46,11 @@ const ExperiencePanel = () => {
); }; + export default ExperiencePanel; diff --git a/apps/fronted/src/components/editor/project/ProjectItem.tsx b/apps/fronted/src/components/editor/project/ProjectItem.tsx index 5baab24..9b1a881 100644 --- a/apps/fronted/src/components/editor/project/ProjectItem.tsx +++ b/apps/fronted/src/components/editor/project/ProjectItem.tsx @@ -12,6 +12,7 @@ import { ChevronDown, Eye, EyeOff, GripVertical, Trash2 } from "lucide-react"; import { useCallback, useState } from "react"; import Field from "../Field"; import ThemeModal from "@/components/shared/ThemeModal"; +import { useTranslations } from "next-intl"; interface Project { id: string; @@ -30,6 +31,7 @@ interface ProjectEditorProps { } const ProjectEditor: React.FC = ({ project, onSave }) => { + const t = useTranslations("workbench.projectItem"); const handleChange = (field: keyof Project, value: string) => { onSave({ ...project, @@ -42,33 +44,33 @@ const ProjectEditor: React.FC = ({ project, onSave }) => {
handleChange("name", value)} - placeholder="项目名称" + placeholder={t("placeholders.name")} required /> handleChange("role", value)} - placeholder="如:前端负责人" + placeholder={t("placeholders.role")} required />
handleChange("date", value)} - placeholder="如:2023.01 - 2023.06" + placeholder={t("placeholders.date")} required /> handleChange("description", value)} type="editor" - placeholder="简要描述项目的背景和目标..." + placeholder={t("placeholders.description")} />
diff --git a/apps/fronted/src/components/editor/project/ProjectPanel.tsx b/apps/fronted/src/components/editor/project/ProjectPanel.tsx index ccebb98..377efb4 100644 --- a/apps/fronted/src/components/editor/project/ProjectPanel.tsx +++ b/apps/fronted/src/components/editor/project/ProjectPanel.tsx @@ -2,21 +2,23 @@ import { cn } from "@/lib/utils"; import { useResumeStore } from "@/store/useResumeStore"; import { Reorder } from "framer-motion"; import { PlusCircle } from "lucide-react"; +import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import ProjectItem from "./ProjectItem"; import { Project } from "@/types/resume"; const ProjectPanel = () => { + const t = useTranslations("workbench.projectPanel"); const { activeResume, updateProjects, updateProjectsBatch } = useResumeStore(); const { projects = [] } = activeResume || {}; const handleCreateProject = () => { const newProject: Project = { id: crypto.randomUUID(), - name: "Project Name", - role: "Project Role", - date: "", - description: "", + name: t("defaultProject.name"), + role: t("defaultProject.role"), + date: t("defaultProject.date"), + description: t("defaultProject.description"), visible: true, }; updateProjects(newProject); @@ -43,10 +45,11 @@ const ProjectPanel = () => {
); }; + export default ProjectPanel; diff --git a/apps/fronted/src/components/shared/PdfExport.tsx b/apps/fronted/src/components/shared/PdfExport.tsx index 7cb9f43..f14d864 100644 --- a/apps/fronted/src/components/shared/PdfExport.tsx +++ b/apps/fronted/src/components/shared/PdfExport.tsx @@ -4,6 +4,7 @@ import { Download, Loader2 } from "lucide-react"; import { toast } from "sonner"; import { useResumeStore } from "@/store/useResumeStore"; import { Button } from "@/components/ui/button"; +import { useTranslations } from "next-intl"; const getOptimizedStyles = () => { const styleCache = new Map(); @@ -70,6 +71,7 @@ const PdfExport = () => { const [isExporting, setIsExporting] = useState(false); const { activeResume } = useResumeStore(); const { globalSettings = {}, title } = activeResume || {}; + const t = useTranslations("pdfExport"); const handleExport = async () => { const exportStartTime = performance.now(); @@ -118,10 +120,10 @@ const PdfExport = () => { window.URL.revokeObjectURL(url); console.log(`Total export took ${performance.now() - exportStartTime}ms`); - toast.success("PDF 导出成功!"); + toast.success(t("toast.success")); } catch (error) { console.error("Export error:", error); - toast.error("PDF 导出失败,请重试"); + toast.error(t("toast.error")); } finally { setIsExporting(false); } @@ -140,7 +142,7 @@ const PdfExport = () => { ) : ( )} - {isExporting ? "导出中..." : "导出 PDF"} + {isExporting ? t("button.exporting") : t("button.default")}
); diff --git a/apps/fronted/src/components/shared/UpdateLocale.tsx b/apps/fronted/src/components/shared/UpdateLocale.tsx index 0ee2661..c9b3a7e 100644 --- a/apps/fronted/src/components/shared/UpdateLocale.tsx +++ b/apps/fronted/src/components/shared/UpdateLocale.tsx @@ -3,5 +3,5 @@ import { setUserLocale } from "@/i18n/db"; import { revalidatePath } from "next/cache"; export default async function updateLocale(locale: string) { setUserLocale(locale); - revalidatePath("/app"); + // revalidatePath("/app"); } diff --git a/apps/fronted/src/i18n/locales/en.json b/apps/fronted/src/i18n/locales/en.json index 5ade5fa..dca7e70 100644 --- a/apps/fronted/src/i18n/locales/en.json +++ b/apps/fronted/src/i18n/locales/en.json @@ -74,5 +74,228 @@ } } } + }, + "pdfExport": { + "button": { + "exporting": "Exporting...", + "default": "Export PDF" + }, + "toast": { + "success": "PDF exported successfully!", + "error": "PDF export failed, please try again" + } + }, + "workbench": { + "sidePanel": { + "layout": { + "title": "Layout", + "addCustomSection": "Add Custom Section" + }, + "theme": { + "title": "Theme Color", + "custom": "Custom" + }, + "typography": { + "title": "Typography", + "font": { + "title": "Font", + "sans": "Sans-serif", + "serif": "Serif", + "mono": "Monospace" + }, + "lineHeight": { + "title": "Line Height", + "normal": "Default", + "relaxed": "Relaxed", + "loose": "Loose" + }, + "baseFontSize": { + "title": "Base Font Size" + }, + "headerSize": { + "title": "Section Header Size" + }, + "subheaderSize": { + "title": "Section Subheader Size" + } + }, + "spacing": { + "title": "Spacing", + "pagePadding": { + "title": "Page Padding" + }, + "sectionSpacing": { + "title": "Section Spacing" + }, + "paragraphSpacing": { + "title": "Paragraph Spacing" + } + }, + "mode": { + "title": "Mode", + "useIconMode": { + "title": "Compact Mode" + } + } + }, + "basicPanel": { + "basicField": "Basic", + "customField": "Custom", + "customFields": { + "placeholders": { + "label": "Label", + "value": "Value" + }, + "addButton": "Add Custom Field" + }, + "basicFields": { + "name": "Name", + "title": "Title", + "email": "Email", + "phone": "Phone", + "website": "Website", + "location": "Location", + "birthDate": "Birth Date", + "employementStatus": "Employ" + }, + "fieldVisibility": { + "show": "Show", + "hide": "Hide" + }, + "githubContributions": "GitHub Contributions" + }, + "experiencePanel": { + "title": "Work Experience", + "addButton": "Add Work Experience", + "defaultProject": { + "company": "Tech Company Ltd.", + "position": "Senior Frontend Engineer", + "date": "2020-Present", + "details": "Responsible for core product development..." + }, + "placeholders": { + "company": "company name", + "position": "job title", + "date": "employment period", + "details": "job responsibilities and achievements" + } + }, + "experienceItem": { + "labels": { + "company": "Company Name", + "position": "Position", + "date": "Employment Period", + "details": "Job Responsibilities" + }, + "placeholders": { + "company": "Enter company name", + "position": "e.g., Frontend Engineer", + "date": "e.g., 2020-Present", + "details": "Describe your responsibilities and achievements in this role" + }, + "buttons": { + "edit": "Edit", + "save": "Save", + "cancel": "Cancel", + "delete": "Delete" + }, + "visibility": { + "show": "Show", + "hide": "Hide" + } + }, + "projectPanel": { + "title": "Project Experience", + "addButton": "Add Project", + "defaultProject": { + "name": "Personal Project", + "description": "Project Description", + "role": "Responsibilities", + "date": "2023.01 - 2023.06" + }, + "placeholders": { + "name": "Project Name", + "description": "Briefly describe project background and goals", + "role": "Your role and responsibilities in the project", + "date": "Project time range", + "link": "Project link (optional)" + } + }, + "projectItem": { + "labels": { + "name": "Project Name", + "role": "Project Role", + "date": "Project Period", + "description": "Project Description", + "technologies": "Tech Stack", + "link": "Project Link" + }, + "placeholders": { + "name": "Enter project name", + "role": "Your role in the project", + "date": "Project time range", + "description": "Briefly describe project background and goals", + "technologies": "Technologies and tools used", + "link": "Project link (optional)" + }, + "buttons": { + "edit": "Edit", + "save": "Save", + "cancel": "Cancel", + "delete": "Delete" + }, + "visibility": { + "show": "Show", + "hide": "Hide" + } + }, + "educationPanel": { + "title": "Education", + "addButton": "Add Education", + "defaultProject": { + "school": "School Name", + "degree": "Degree", + "major": "Major", + "date": "2020.09 - 2024.06" + }, + "placeholders": { + "school": "Enter school name", + "degree": "Select degree", + "major": "Enter major", + "date": "Enter study period" + } + }, + "educationItem": { + "labels": { + "school": "School Name", + "degree": "Degree", + "major": "Major", + "date": "Study Period", + "description": "Description", + "gpa": "GPA", + "location": "Location", + "startDate": "Start Date", + "endDate": "End Date" + }, + "placeholders": { + "school": "Enter school name", + "degree": "Select degree", + "major": "Enter major", + "date": "Enter study time range", + "description": "Enter school description", + "gpa": "Enter GPA", + "location": "Enter location" + }, + "buttons": { + "edit": "Edit", + "save": "Save", + "cancel": "Cancel", + "delete": "Delete" + }, + "visibility": { + "show": "Show", + "hide": "Hide" + } + } } } diff --git a/apps/fronted/src/i18n/locales/zh.json b/apps/fronted/src/i18n/locales/zh.json index 444d571..a4990b1 100644 --- a/apps/fronted/src/i18n/locales/zh.json +++ b/apps/fronted/src/i18n/locales/zh.json @@ -74,5 +74,228 @@ } } } + }, + "pdfExport": { + "button": { + "exporting": "导出中...", + "default": "导出 PDF" + }, + "toast": { + "success": "PDF 导出成功!", + "error": "PDF 导出失败,请重试" + } + }, + "workbench": { + "sidePanel": { + "layout": { + "title": "布局", + "addCustomSection": "添加自定义模块" + }, + "theme": { + "title": "主题色", + "custom": "自定义" + }, + "typography": { + "title": "排版", + "font": { + "title": "字体", + "sans": "无衬线体", + "serif": "衬线体", + "mono": "等宽体" + }, + "lineHeight": { + "title": "行高", + "normal": "默认", + "relaxed": "适中", + "loose": "宽松" + }, + "baseFontSize": { + "title": "基础字号" + }, + "headerSize": { + "title": "模块标题字号" + }, + "subheaderSize": { + "title": "模块二级标题字号" + } + }, + "spacing": { + "title": "间距", + "pagePadding": { + "title": "页边距" + }, + "sectionSpacing": { + "title": "模块间距" + }, + "paragraphSpacing": { + "title": "段落间距" + } + }, + "mode": { + "title": "模式", + "useIconMode": { + "title": "简洁模式" + } + } + }, + "basicPanel": { + "basicField": "基本信息", + "customField": "自定义字段", + "customFields": { + "placeholders": { + "label": "标签", + "value": "值" + }, + "addButton": "添加自定义字段" + }, + "basicFields": { + "name": "姓名", + "title": "职位", + "email": "邮箱", + "phone": "电话", + "website": "个人网站", + "location": "地址", + "birthDate": "出生日期", + "employementStatus": "求职状态" + }, + "fieldVisibility": { + "show": "显示", + "hide": "隐藏" + }, + "githubContributions": "GitHub 贡献" + }, + "experiencePanel": { + "title": "工作经历", + "addButton": "添加工作经历", + "defaultProject": { + "company": "某科技有限公司", + "position": "高级前端工程师", + "date": "2020-至今", + "details": "负责公司核心产品..." + }, + "placeholders": { + "company": "请输入公司名称", + "position": "请输入职位", + "date": "请输入工作时间", + "details": "请输入工作职责和成就" + } + }, + "experienceItem": { + "labels": { + "company": "公司名称", + "position": "岗位", + "date": "工作时间", + "details": "工作职责" + }, + "placeholders": { + "company": "请输入公司名称", + "position": "如:前端工程师", + "date": "如:2020-至今", + "details": "描述你在这份工作中的职责和成就" + }, + "buttons": { + "edit": "编辑", + "save": "保存", + "cancel": "取消", + "delete": "删除" + }, + "visibility": { + "show": "显示", + "hide": "隐藏" + } + }, + "projectPanel": { + "title": "项目经历", + "addButton": "添加项目", + "defaultProject": { + "name": "个人项目", + "description": "项目描述", + "role": "负责内容", + "technologies": "技术栈", + "date": "2023.01 - 2023.06" + }, + "placeholders": { + "name": "项目名称", + "description": "简要描述项目背景和目标", + "role": "你在项目中的角色和职责", + "technologies": "使用的技术和工具", + "date": "项目时间范围", + "link": "项目链接(可选)" + } + }, + "projectItem": { + "labels": { + "name": "项目名称", + "role": "项目角色", + "date": "项目时间", + "description": "项目描述", + "link": "项目链接" + }, + "placeholders": { + "name": "请输入项目名称", + "role": "你在项目中的角色", + "date": "项目时间范围", + "description": "简要描述项目背景和目标", + "link": "项目链接(可选)" + }, + "buttons": { + "edit": "编辑", + "save": "保存", + "cancel": "取消", + "delete": "删除" + }, + "visibility": { + "show": "显示", + "hide": "隐藏" + } + }, + "educationPanel": { + "title": "教育背景", + "addButton": "添加教育经历", + "defaultProject": { + "school": "学校名称", + "degree": "学历", + "major": "专业", + "date": "2020.09 - 2024.06" + }, + "placeholders": { + "school": "请输入学校名称", + "degree": "请选择学历", + "major": "请输入专业名称", + "date": "请输入就读时间范围" + } + }, + "educationItem": { + "labels": { + "school": "学校名称", + "degree": "学历", + "major": "专业", + "date": "就读时间", + "description": "学校简介", + "gpa": "GPA", + "location": "所在地", + "startDate": "开始时间", + "endDate": "结束时间" + }, + "placeholders": { + "school": "请输入学校名称", + "degree": "请选择学历", + "major": "请输入专业名称", + "date": "请输入就读时间范围", + "description": "请输入学校简介", + "gpa": "请输入GPA", + "location": "请输入所在地" + }, + "buttons": { + "edit": "编辑", + "save": "保存", + "cancel": "取消", + "delete": "删除" + }, + "visibility": { + "show": "显示", + "hide": "隐藏" + } + } } }