diff --git a/src/pages/editor.tsx b/src/pages/editor.tsx index 3840e75..aa34606 100644 --- a/src/pages/editor.tsx +++ b/src/pages/editor.tsx @@ -16,7 +16,7 @@ import { } from "@/lib/db"; import { FolderOpen, Settings } from "lucide-react"; import { useTranslationInlineEdit } from "@/hooks/biz/use-translation-inline-edit"; -import { buildStructureFromObject, flattenEntries, flattenValues, type FlatEntry, insertEntrySibling, removeEntryAtPath, renameEntryAtPath, moveEntryByOffset } from "@/lib/i18n-structure"; +import { buildStructureFromObject, flattenEntries, flattenValues, unflattenValues, type FlatEntry, insertEntrySibling, removeEntryAtPath, renameEntryAtPath, moveEntryByOffset } from "@/lib/i18n-structure"; import { ImportLanguageModal } from "@/components/biz/import-language-modal"; import { ExportLanguageModal } from "@/components/biz/export-language-modal"; import { EntryNameModal } from "@/components/biz/entry-name-modal"; @@ -215,18 +215,74 @@ export default function Editor({ projectId }: EditorProps) { } }, [projectId, setSourcesMeta]); + const updateStructureFromDefaultLanguage = useCallback( + async (opts?: { silent?: boolean }) => { + if (!projectId) return false; + + // 获取默认语言 + const defaultLang = sourcesState.defaultLanguage; + if (!defaultLang) { + if (!opts?.silent) { + toast.error("未配置默认语言,无法更新翻译结构"); + } + return false; + } + + // 获取默认语言的翻译内容 + const translations = valuesByLang[defaultLang]; + if (!translations || Object.keys(translations).length === 0) { + if (!opts?.silent) { + toast.error(`默认语言 ${defaultLang} 的翻译内容为空`); + } + return false; + } + + try { + // 从扁平结构转换为嵌套对象 + const nestedObj = unflattenValues(translations); + // 从嵌套对象构建结构树 + const root = buildStructureFromObject(nestedObj); + // 保存到数据库 + await upsertStructure({ projectId, root }); + setStructure({ projectId, root }); + + if (!opts?.silent) { + toast.success("翻译结构已更新"); + } + return true; + } catch (err) { + const message = (err as Error)?.message ?? "更新结构失败"; + if (!opts?.silent) { + toast.error(message); + } + console.error("更新结构失败", err); + return false; + } + }, + [projectId, sourcesState.defaultLanguage, valuesByLang] + ); + const syncExternalSources = useCallback( - async (opts?: { silent?: boolean; reloadConfig?: boolean }) => { + async (opts?: { silent?: boolean; reloadConfig?: boolean; updateStructure?: boolean }) => { let preloaded: LoadedProjectSources | null = null; if (opts?.reloadConfig) { preloaded = await bootstrapSources(); } + let syncResult: boolean; if (preloaded) { - return applyLoadedSources(preloaded, opts); + syncResult = await applyLoadedSources(preloaded, opts); + } else { + syncResult = await syncFromAdapter(opts); } - return syncFromAdapter(opts); + + // 如果同步成功且需要更新结构,则从默认语言更新结构 + if (syncResult && opts?.updateStructure) { + await updateStructureFromDefaultLanguage({ silent: opts.silent }); + } + + return syncResult; }, - [applyLoadedSources, bootstrapSources, syncFromAdapter] + [applyLoadedSources, bootstrapSources, syncFromAdapter, updateStructureFromDefaultLanguage] ); const refresh = useCallback( @@ -482,7 +538,7 @@ export default function Editor({ projectId }: EditorProps) { void syncExternalSources({ silent: false }); break; case "read-all": - void syncExternalSources({ silent: false, reloadConfig: true }); + void syncExternalSources({ silent: false, reloadConfig: true, updateStructure: true }); break; case "save": void handleSaveAllConnected();