UNPKG

@voerkai18n/cli

Version:

命令行工具,用来初始化/提取/编译/自动翻译等工具链

171 lines (154 loc) 5.49 kB
const path = require("path"); const fs = require("node:fs"); const {readFile, writeFile } = require('flex-tools/fs/nodefs'); const glob = require("fast-glob"); const { getDefaultLanguage } = require("./utils"); let messageIds = [] /** * 提取消息id * @param {*} ctx * @returns */ async function getMessageIds(ctx){ const { getTranslateMessagesDir } = ctx const files = await glob(["*.json","!*.bak*.json"],{ cwd: getTranslateMessagesDir(), absolute: true }) const ids = [] for(let file of files){ const content = await readFile(file,{ encoding: "utf-8" }) const messages = JSON.parse(content) for(let message of Object.values(messages)){ if(message.$id) ids.push(message.$id) } } messageIds = [...new Set(ids)] if(messageIds.length !== ids.length){ // 选出ids中的重复的id const repeatIds = ids.filter((id, index) => ids.indexOf(id) !== index && ids.lastIndexOf(id) === index); logsets.log(logsets.colors.red(t("发现重复的文本id: {}")),repeatIds.join(", ")) } return messageIds } function getNextId(){ let id = messageIds.length + 1 while(messageIds.includes(id)){ id++ } messageIds.push(id) return id } /** * 生成要翻译的文本内容 * { * 名称空间:{ * message:{ * $id:1, * $files:["文件名(行号)"], * [lanauge.name]: "message", * * } * } * } * @param {*} messages * @returns */ function formatMessages(messages,ctx){ const languages = ctx.languages.map(lng=>lng.name) const defaultLanguage = getDefaultLanguage(languages) const defaultMessageRecord =(text)=>languages.reduce((acc,lng)=>{ if(lng!=defaultLanguage) acc[lng] = text return acc },{}) return messages.reduce((results,record)=>{ const message = record.message if(!results[record.namespace]){ results[record.namespace] = {} } const messageRecord = message in results[record.namespace] ? results[record.namespace][message] : defaultMessageRecord(message) if(!messageRecord.$files) messageRecord.$files = [] const relFile = path.relative(process.cwd(),record.file).replaceAll("\\","/") messageRecord.$files.push(`${relFile}(${record.rang.start}-${record.rang.end})`) messageRecord.$id = getNextId() results[record.namespace][record.message] = messageRecord return results },{}) } async function overwriteMessages(namespaceFile,newMessages){ for(let newMessage of Object.values(newMessages)){ newMessage.$id = getNextId() } await writeFile(namespaceFile,JSON.stringify(newMessages,null,4)) } /** * 根据从extraceTexts提取的文本按namespace进行分组 * @param {*} messages */ async function updateMessages(formattedMessages,ctx){ const mode = ctx.mode || 'sync' const translateMessagesDir = ctx.getTranslateMessagesDir() for(let [namespace,messages] of Object.entries(formattedMessages)){ const namespaceFile = path.join(translateMessagesDir,`${namespace}.json`) if(fs.existsSync(namespaceFile)){ if(mode === 'overwrite'){ await overwriteMessages(namespaceFile,messages,ctx) }else{ await syncMessages(namespaceFile,messages,ctx) } }else{ await overwriteMessages(namespaceFile,messages,ctx) } } } /** * sync:同步(默认值),两者自动合并,并且会删除在源码文件中不存在的文本。如果某个翻译已经翻译了一半也会保留。此值适用于大部情况,推荐。 overwrite:覆盖现存的翻译内容。这会导致已经进行了一半的翻译数据丢失,慎用。 merge:合并,与sync的差别在于不会删除源码中已不存在的文本。 * @param {*} newMessages * @param {*} settings */ async function syncMessages(namespaceFile, newMessages) { let oldMessages = await readFile(namespaceFile, { encoding: "utf-8" }); oldMessages = JSON.parse(oldMessages) || {}; const results = {}; for (let [message, newMessage] of Object.entries(newMessages)) { if (message in oldMessages) { results[message] = {}; const oldMessage = oldMessages[message]; Object.entries(newMessage).forEach(([langName, langMessage]) => { if (langName.startsWith("$")) { results[message][langName] = newMessage[langName]; } else { if ( langName in oldMessage && (oldMessage[langName] !== message || Array.isArray(oldMessage[langName])) ) { results[message][langName] = oldMessage[langName]; } else { results[message][langName] = langMessage; } } }); if (oldMessage["$id"]) { results[message]["$id"] = oldMessage["$id"]; } else { results[message]["$id"] = getNextId(); } } else { results[message] = newMessage; results[message]["$id"] = getNextId(); } } await writeFile(namespaceFile, JSON.stringify(results, null, 4)); return results; } module.exports = { formatMessages, updateMessages, overwriteMessages, getDefaultLanguage, getMessageIds, syncMessages, getNextId }