UNPKG

@lobehub/i18n-cli

Version:

Lobe i18n is a CLI tool that automate translate your i18n localization with AI

9 lines (7 loc) 23.5 kB
"use strict";var de=Object.defineProperty;var i=(t,e)=>de(t,"name",{value:e,configurable:!0});var d=require("react/jsx-runtime"),p=require("@lobehub/cli-ui"),N=require("commander"),pe=require("update-notifier"),fe=require("conf"),ge=require("cosmiconfig"),C=require("@inkjs/ui"),L=require("react"),l=require("chalk"),he=require("dotenv"),m=require("lodash-es"),f=require("consola"),h=require("node:path"),S=require("ink"),_=require("p-map"),me=require("langchain/text_splitter"),K=require("gpt-tokenizer"),V=require("remark-frontmatter"),G=require("remark-gfm"),ye=require("remark-parse"),ke=require("remark-stringify"),z=require("unified"),Q=require("unist-util-visit"),we=require("just-diff"),ve=require("@langchain/openai"),Se=require("dirty-json"),Y=require("@langchain/core/prompts"),y=require("node:fs"),xe=require("json-stable-stringify"),W=require("glob"),be=require("node:process"),X=require("gray-matter");function Te(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(n){if(n!=="default"){var r=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,r.get?r:{enumerable:!0,get:i(function(){return t[n]},"get")})}}),e.default=t,Object.freeze(e)}i(Te,"_interopNamespaceDefault");var Ce=Te(be),je="@lobehub/i18n-cli",Oe="1.20.2",Me="Lobe i18n is a CLI tool that automate translate your i18n localization with AI",Ne=["ai","i18n","openai","gpt","langchain"],Le="https://github.comlobehub/lobe-cli-toolbox/tree/master/packages/lobe-i18n",Fe={url:"https://github.com/lobehub/lobe-cli-toolbox/issues/new"},Ie={type:"git",url:"https://github.com/lobehub/lobe-cli-toolbox.git"},Ae="MIT",Ee="LobeHub <i@lobehub.com>",$e=!1,qe="module",Pe={"@":"./src"},Je={require:{types:"./dist/index.d.cts",default:"./dist/index.cjs"},import:{types:"./dist/index.d.mts",default:"./dist/index.mjs"}},Be="./dist/index.cjs",Ue="./dist/index.mjs",Re="./dist/index.d.cts",De={"lobe-i18n":"dist/cli.js"},_e=["dist"],Ke={build:"npm run type-check && pkgroll --minify -p tsconfig.prod.json --env.NODE_ENV=production && npm run shebang",dev:"pkgroll -p tsconfig.prod.json --env.NODE_ENV=development --watch",link:"npm run build && npm link -f",shebang:"lobe-shebang -t ./dist/cli.js",start:"node ./dist/cli.js",test:"vitest --passWithNoTests","test:coverage":"vitest run --coverage --passWithNoTests","type-check":"tsc --noEmit"},Ve={"@inkjs/ui":"^1.0.0","@langchain/core":"^0.2.20","@langchain/openai":"^0.2.5","@lobehub/cli-ui":"1.10.0",chalk:"^5.3.0",commander:"^12.1.0",conf:"^12.0.0",consola:"^3.2.3",cosmiconfig:"^9.0.0","dirty-json":"^0.9.2",dotenv:"^16.4.5","fast-deep-equal":"^3.1.3",glob:"^10.4.5","gpt-tokenizer":"^2.2.1","gray-matter":"^4.0.3",ink:"^4.4.1","json-stable-stringify":"^1.1.1","just-diff":"^6.0.2",langchain:"^0.2.12","lodash-es":"^4.17.21","p-map":"^7.0.2",pangu:"^4.0.7",react:"^18.3.1","remark-frontmatter":"^4.0.1","remark-gfm":"^3.0.1","remark-parse":"^10.0.2","remark-stringify":"^10.0.3",swr:"^2.2.5",unified:"^11.0.5","unist-util-visit":"^5.0.0","update-notifier":"^7.2.0",zustand:"^4.5.4"},Ge={"@types/json-stable-stringify":"^1.0.36","@types/lodash-es":"^4.17.12","@types/node":"^20.16.13","@types/unist":"^3.0.3"},ze={node:">=18"},Qe={access:"public",registry:"https://registry.npmjs.org/"},A={name:je,version:Oe,description:Me,keywords:Ne,homepage:Le,bugs:Fe,repository:Ie,license:Ae,author:Ee,sideEffects:$e,type:qe,imports:Pe,exports:Je,main:Be,module:Ue,types:Re,bin:De,files:_e,scripts:Ke,dependencies:Ve,devDependencies:Ge,engines:ze,publishConfig:Qe},j=(t=>(t.MDAST="mdast",t.STRING="string",t))(j||{});const E=i(t=>`.${t}.md`,"getDefaultExtension"),Ye={"gpt-3.5-turbo":16385,"gpt-3.5-turbo-0125":16385,"gpt-3.5-turbo-1106":16385,"gpt-3.5-turbo-16k":16385,"gpt-4":8192,"gpt-4-0613":8192,"gpt-4-32k":32768,"gpt-4-0125-preview":128e3,"gpt-4-vision-preview":128e3,"gpt-4-turbo":128e3,"gpt-4-turbo-vision":128e3,"gpt-4-turbo-preview":128e3,"gpt-4-1106-vision-preview":128e3,"gpt-4-1106-preview":128e3,"gpt-4o-2024-05-13":128e3,"gpt-4o-mini":16385},H="gpt-4o-mini",We={concurrency:5,markdown:{entry:[],mode:j.STRING,outputExtensions:E},modelName:H,temperature:0},v=i((t,e)=>{t[e]||p.alert.error(`Can't find ${l.bold.yellow("outputLocales")} in config`)},"checkOptionKeys"),Z={apiBaseUrl:{default:"",type:"string"},openaiToken:{default:"",type:"string"}},$=new fe({projectName:"lobe-i18n",schema:Z});class Xe{static{i(this,"ExplorerConfig")}explorer;customConfig;constructor(){this.explorer=ge.cosmiconfigSync("i18n")}loadCustomConfig(e){this.customConfig=e}getConfigFile(){return this.customConfig?this.explorer.load(this.customConfig)?.config:this.explorer.search()?.config}}const q=new Xe;he.config();const P=i(t=>$.get(t),"getConfig"),He=i(t=>Z[t].default,"getDefulatConfig"),Ze=i((t,e)=>$.set(t,e),"setConfig"),et=i(()=>process.env.OPENAI_API_KEY||P("openaiToken"),"getOpenAIApiKey"),tt=i(()=>process.env.OPENAI_PROXY_URL||P("apiBaseUrl"),"getOpenAIProxyUrl"),J=i(()=>{const t=q.getConfigFile();return t?m.merge(We,t):p.alert.error(`Can't find ${l.bold.yellow("config")}`,!0)},"getConfigFile"),nt=i(()=>{const t=J();return v(t,"entry"),v(t,"entryLocale"),v(t,"output"),v(t,"outputLocales"),t},"getLocaleConfig"),rt=i(()=>{const t=J();if(!t.markdown)return p.alert.error(`Can't find ${l.bold.yellow("config.markdown")}`,!0);const e=m.merge(t?.markdown||{},{entryLocale:t?.markdown?.entryLocale||t.entryLocale,outputLocales:t?.markdown?.outputLocales||t.outputLocales});return v(e,"entry"),v(e,"entryLocale"),v(e,"outputLocales"),e},"getMarkdownConfigFile");var k={getConfig:P,getConfigFile:J,getDefulatConfig:He,getLocaleConfig:nt,getMarkdownConfigFile:rt,getOpenAIApiKey:et,getOpenAIProxyUrl:tt,setConfig:Ze};const ot=i(()=>{const t=$.store;return{get:k.getConfig,getDefault:k.getDefulatConfig,set:k.setConfig,store:t}},"useConfStore"),st=L.memo(()=>{const[t,e]=L.useState(),{store:n,set:r,getDefault:o}=ot(),a=i((c,u)=>{r(c,u),e("")},"setConfig"),s=L.useMemo(()=>[{children:d.jsx(C.TextInput,{defaultValue:n.openaiToken,onSubmit:i(c=>a("openaiToken",c),"onSubmit"),placeholder:"Input OpenAI token..."}),defaultValue:o("openaiToken"),key:"openaiToken",label:"OpenAI token",showValue:!1,value:n.openaiToken},{children:d.jsx(C.TextInput,{defaultValue:n.apiBaseUrl,onSubmit:i(c=>a("apiBaseUrl",c),"onSubmit"),placeholder:"Set openAI API proxy, default value: https://api.openai.com/v1/..."}),defaultValue:o("apiBaseUrl"),desc:"OpenAI API proxy, default value: https://api.openai.com/v1/",key:"apiBaseUrl",label:"OpenAI API proxy",showValue:!1,value:n.apiBaseUrl}],[n]);return d.jsx(p.ConfigPanel,{active:t,items:s,logo:"\u{1F92F}",setActive:e,title:"Lobe I18N Config"})}),F=L.memo(({hide:t,filename:e,to:n,from:r,progress:o,maxStep:a,step:s,isLoading:c,needToken:u})=>{const g=p.useTheme();return t?null:d.jsxs(p.SplitView,{flexDirection:"column",children:[d.jsx(S.Text,{backgroundColor:g.colorBgLayout,color:g.colorText,children:` \u{1F4DD} ${e} `}),d.jsxs(S.Text,{color:g.colorTextDescription,children:["- from ",d.jsx(S.Text,{bold:!0,color:g.colorInfo,children:r})," to ",d.jsx(S.Text,{bold:!0,color:g.colorInfo,children:n}),d.jsx(S.Text,{color:g.colorTextDescription,children:` [Tokens: ${u}]`})]}),c?d.jsxs(S.Box,{children:[d.jsx(C.Spinner,{label:` ${o}% [${s}/${a} chunks] `}),d.jsx(C.ProgressBar,{value:o})]}):d.jsx(C.StatusMessage,{variant:"success",children:"Success"})]})}),it=3,I=2,ee=2,w=i(t=>K.encode(t).length,"calcToken"),O=i(t=>K.encode(String(t)).length,"calcEncodedKeyToken"),te=i(t=>O(t)+it,"calcPrimitiveValueToken"),ne=i((t,e=0)=>m.sumBy(Object.entries(t),([n,r])=>O(n)+I+(m.isPlainObject(r)?ne(r,e+1):te(r)))+ee+e,"calcJsonToken"),at=new Set([` `,`\r `,"[!NOTE]","[!IMPORTANT]","[!WARNING]","[!CAUTION]","\\[!NOTE]","\\[!IMPORTANT]","\\[!WARNING]","\\[!CAUTION]","\\[!NOTE]\\","\\[!IMPORTANT]\\","\\[!WARNING]\\","\\[!CAUTION]\\"]),ct=i(t=>at.has(t)?!0:/^[\s\p{P}\u{1F300}-\u{1F5FF}\u{1F600}-\u{1F64F}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}]*$/u.test(t.replaceAll(" ","")),"checkMdString"),re=i(async t=>z.unified().use(ye).use(G).use(V).parse(t.trim()),"convertMarkdownToMdast"),lt=i((t,e)=>{const n={};let r=0;return Q.visit(t,e||"text",o=>{n[r]=o.value,r++}),n},"convertMdastToMdastObj"),ut=i(t=>{const e={};for(const[n,r]of Object.entries(t))ct(r)||(e[Number(n)]=r);return e},"pickMdastObj"),dt=i(({mdast:t,entry:e,target:n},r)=>{const o={...e,...n};let a=0;return Q.visit(t,r||"text",s=>{s.value=o[a],a++}),t},"mergeMdastObj"),B=i(async t=>z.unified().use(ke,{bullet:"-",emphasis:"*",fences:!0,listItemIndent:1,rule:"-",strong:"*",tightDefinitions:!0}).use(V).use(G).stringify(t).toString(),"convertMdastToMarkdown"),U=i((t,e)=>{const n=we.diff(e,t),r=n.filter(c=>c.op==="add"),o=n.filter(c=>c.op==="remove"),a=m.cloneDeep(e),s={};for(const c of o)m.unset(a,c.path);for(const c of r)m.set(s,c.path,c.value);return{add:r,entry:s,remove:o,target:a}},"diff"),pt=i((t,e)=>m.reduce(Object.entries(t),(n,[r,o])=>{let[a,s]=n.pop()||[{},ee];const c=m.isPlainObject(o)?ne(o,1):te(o);return s+O(r)+I+c<=e?(a[r]=o,s+=O(r)+I+c,n.push([a,s])):n.push([a,s],[{[r]:o},O(r)+I+c]),n},[]).map(([n])=>n),"splitJSONtoSmallChunks"),oe=i((t,e)=>{let n=(Ye[t.modelName||H]-w(e))/3;return t.splitToken&&t.splitToken<n&&(n=t.splitToken),n=Math.floor(n),n},"getSplitToken"),ft=i((t,e,n,r)=>{const o=U(e,n).entry,a=oe(t,r);return pt(o,a)},"splitJsonToChunks");let gt=class{static{i(this,"TranslateMarkdown")}mdast;entry={};config;check;definition;constructor(e){this.config=e,this.check=["text","yaml",e?.markdown?.translateCode&&"code"].filter(Boolean)}async genTarget(e){return this.mdast=await re(e),this.entry=lt(this.mdast,this.check),ut(this.entry)}async genMarkdownByMdast(e){if(!e)return;const n=dt({entry:this.entry,mdast:this.mdast,target:e},this.check);return B(n)}async clearMarkdownString(e){const n=[],r=await re(e);return r.children=r.children.map(o=>o.type==="definition"?(n.push(o),!1):o).filter(Boolean),{content:await B(r),definition:await B({children:n,type:"root"})}}async genSplitMarkdown(e,n){this.definition="";const{content:r,definition:o}=await this.clearMarkdownString(e);return this.definition=o,await me.RecursiveCharacterTextSplitter.fromLanguage("markdown",{chunkOverlap:0,chunkSize:oe(this.config,n),lengthFunction:i(s=>w(s),"lengthFunction")}).splitText(r)}async genMarkdownByString(e){return[...e,this.definition].join(` `)}};const ht=i(t=>{let e={};for(const n of t)e=m.merge(e,n);return e},"mergeJsonFromChunks"),se="You can adjust the tone and style, taking into account the cultural connotations and regional differences of certain words. As a translator, you need to translate the original text into a translation that meets the standards of accuracy and elegance.",mt=i((t=se)=>Y.ChatPromptTemplate.fromMessages([["system",["Translate the i18n JSON file from {from} to {to} according to the BCP 47 standard",`Here are some reference to help with better translation. ---${t}---`,"Keep the keys the same as the original file and make sure the output remains a valid i18n JSON file.","Do not include any additional text or explanations outside the JSON object.Start directly with a left brace and end with a right brace."].filter(Boolean).join(` `)],["human","{json}"]]),"promptJsonTranslate"),yt=i((t=se)=>Y.ChatPromptTemplate.fromMessages([["system",["Translate the markdown file from {from} to {to} according to the BCP 47 standard",`Here are some reference to help with better translation. ---${t}---`,"Make sure the output remains a valid markdown file."].filter(Boolean).join(` `)],["human","{text}"]]),"promptStringTranslate");let kt=class{static{i(this,"TranslateLocale")}model;config;isJsonMode;promptJson;promptString;constructor(e,n,r){this.config=e,this.model=new ve.ChatOpenAI({configuration:{baseURL:r},maxConcurrency:e.concurrency,maxRetries:4,modelName:e.modelName,openAIApiKey:n,temperature:e.temperature,topP:e.topP}),this.promptJson=mt(e.reference),this.promptString=yt(e.reference),this.isJsonMode=!!this.config?.experimental?.jsonMode}async runByString({from:e,to:n,text:r}){try{const o=await this.promptString.formatMessages({from:e||this.config.entryLocale,text:r,to:n}),s=(await this.model.call(o)).text;return s||this.handleError(),s}catch(o){this.handleError(o)}}async runByJson({from:e,to:n,json:r}){try{const o=await this.promptJson.formatMessages({from:e||this.config.entryLocale,json:JSON.stringify(r),to:n}),a=await this.model.invoke(o,this.isJsonMode?{response_format:{type:"json_object"}}:void 0),s=this.isJsonMode?a.content:a.text;s||this.handleError();try{return JSON.parse(s)}catch{p.alert.warn("parse fail, try to use dirty json");try{return Se.parse(s)}catch{p.alert.error("i18n dirty json fail"),p.alert.error(s,!0)}}}catch(o){this.handleError(o)}}handleError(e){p.alert.error(`Translate failed, ${e||"please check your network or try again..."}`,!0)}};class ie{static{i(this,"I18n")}config;step=0;maxStep=1;translateLocaleService;translateMarkdownService;constructor({openAIApiKey:e,openAIProxyUrl:n,config:r}){this.config=r,this.translateLocaleService=new kt(r,e,n),this.translateMarkdownService=new gt(r)}async translateMarkdown(e){return e.mode===j.STRING?this.translateMarkdownByString(e):this.translateMarkdownByMdast(e)}async translateMarkdownByString({md:e,to:n,onProgress:r,from:o}){const a=await this.translateLocaleService.promptString.formatMessages({from:o,text:"",to:n}),s=await this.translateMarkdownService.genSplitMarkdown(e,JSON.stringify(a));if(this.maxStep=s.length,this.step=0,s.length===0)return;const c=s.length*w(JSON.stringify(a))+w(JSON.stringify(s));r?.({isLoading:!0,maxStep:this.maxStep,needToken:c,progress:0,step:0});const u=await _(s,async M=>{r?.({isLoading:this.step<this.maxStep,maxStep:this.maxStep,needToken:c,progress:this.step<this.maxStep?Math.floor(this.step/this.maxStep*100):100,step:this.step});const T=await this.translateLocaleService.runByString({from:o,text:M,to:n});return this.step<this.maxStep&&this.step++,T},{concurrency:this.config?.concurrency});return r?.({isLoading:!1,maxStep:this.maxStep,needToken:c,progress:100,step:this.maxStep}),{result:await this.translateMarkdownService.genMarkdownByString(u),tokenUsage:c+w(JSON.stringify(u))}}async translateMarkdownByMdast({md:e,...n}){const r=await this.translateMarkdownService.genTarget(e),o=await this.translate({...n,entry:r,target:{}});if(!o?.result)return;const a=await this.translateMarkdownService.genMarkdownByMdast(o);if(a)return{result:a,tokenUsage:o.tokenUsage}}async translate({entry:e,target:n,to:r,onProgress:o,from:a}){const s=await this.translateLocaleService.promptJson.formatMessages({from:a,json:{},to:r}),c=ft(this.config,e,n,JSON.stringify(s));if(this.maxStep=c.length,this.step=0,c.length===0)return;const u=c.length*w(JSON.stringify(s))+w(JSON.stringify(c));o?.({isLoading:!0,maxStep:this.maxStep,needToken:u,progress:0,step:0});const g=await _(c,async T=>{o?.({isLoading:this.step<this.maxStep,maxStep:this.maxStep,needToken:u,progress:this.step<this.maxStep?Math.floor(this.step/this.maxStep*100):100,step:this.step});const ue=await this.translateLocaleService.runByJson({from:a,json:T,to:r});return this.step<this.maxStep&&this.step++,ue},{concurrency:this.config?.concurrency});return o?.({isLoading:!1,maxStep:this.maxStep,needToken:u,progress:100,step:this.maxStep}),{result:await m.merge(n,ht(g)),tokenUsage:u+w(JSON.stringify(g))}}}const R=i(t=>{const e=y.readFileSync(t,"utf8");return JSON.parse(e)},"readJSON"),x=i((t,e)=>{const n=xe(e,{space:" "});y.writeFileSync(t,n+`\r `,"utf8")},"writeJSON"),wt=i(t=>y.readFileSync(t,"utf8"),"readMarkdown"),vt=i((t,e)=>{y.writeFileSync(t,e,"utf8")},"writeMarkdown"),St=i(t=>{for(const e of t.outputLocales){const n=h.resolve(t.output,`${e}.json`);y.existsSync(n)||x(n,{})}},"checkLocales"),xt=i((t,e)=>{for(const n of t.outputLocales){const r=h.resolve(t.output,n);y.existsSync(r)||y.mkdirSync(r)}for(const n of t.outputLocales)for(const r of e){const o=h.resolve(t.output,n,r);try{const a=h.dirname(o);y.mkdirSync(a,{recursive:!0})}catch{}y.existsSync(o)||x(o,{})}},"checkLocaleFolders"),bt=i(t=>{try{const e=h.resolve("./",t.entry);return y.existsSync(e)||p.alert.error(`Can't find ${l.bold.yellow(t.entry)} in dir`,!0),R(e)}catch{Ce.exit(1)}},"getEntryFile"),Tt=i(t=>{const e=t.entry.replaceAll("*","").replaceAll("*.json",""),n=W.globSync(h.join(e,"**/*.json").replaceAll("\\","/"),{nodir:!0}),r={};for(const o of n)r[h.relative(e,o)]=R(o);if(Object.keys(r).length===0){p.alert.error(`Can't find .json files in ${l.bold.yellow(e)}`,!0);return}return r},"getEntryFolderFiles"),ae=i(t=>{const e=R(t);return e||(x(t,{}),{})},"getLocaleObj"),D=i((t,e)=>{try{if(t===e)return!0;if(typeof t!=typeof e)return!1;if(typeof t=="object"&&typeof e=="object"){const n=Object.keys(t),r=Object.keys(e);if(n.length!==r.length)return!1;for(const o of n)if(!r.includes(o)||!D(t[o],e[o]))return!1}return typeof t==typeof e}catch{return!1}},"isEqualJsonKeys");class Ct{static{i(this,"TranslateLocale")}config;query=[];i18n;constructor(){this.config=k.getLocaleConfig(),this.i18n=new ie({config:this.config,openAIApiKey:k.getOpenAIApiKey(),openAIProxyUrl:k.getOpenAIProxyUrl()})}async start(){f.consola.start("Lobe I18N is analyzing your project... \u{1F92F}\u{1F30F}\u{1F50D}"),!this.config.entry.includes(".json")||this.config.entry.includes("*")?this.genFolderQuery():this.genFlatQuery(),this.query.length>0?await this.runQuery():f.consola.success("No content requiring translation was found."),f.consola.success("All i18n tasks have been completed\uFF01")}async runQuery(){f.consola.info(`Current model setting: ${l.cyan(this.config.modelName)} (temperature: ${l.cyan(this.config.temperature)}) ${this.config.experimental?.jsonMode?l.red(" [JSON Mode]"):""}}`);let e=0;for(const n of this.query){const r={filename:n.filename,from:n.from||this.config.entryLocale,to:n.to},{rerender:o,clear:a}=p.render(d.jsx(F,{hide:!0,isLoading:!0,maxStep:1,progress:0,step:0,...r})),s=await this.i18n.translate({...n,onProgress:i(u=>{u.maxStep>0?o(d.jsx(F,{...u,...r})):a()},"onProgress")});a();const c=h.relative(".",n.filename);s?.result&&Object.keys(s.result).length>0?(x(n.filename,s.result),e+=s.tokenUsage,f.consola.success(l.yellow(c),l.gray(`[Token usage: ${s.tokenUsage}]`))):f.consola.warn("No translation result was found:",l.yellow(c))}e>0&&f.consola.info("Total token usage:",l.cyan(e))}genFolderQuery(){const e=this.config,n=Tt(e),r=Object.keys(n);f.consola.info(`Running in ${l.bold.cyan("\u{1F4C2} Folder Mode")} and has found ${l.bold.cyan(r.length)} files.`),xt(e,r);for(const o of e.outputLocales)for(const[a,s]of r.entries()){process.stdout.isTTY&&(process.stdout.clearLine(0),process.stdout.cursorTo(0)),process.stdout.write(`${l.cyan(o)}${l.gray(`[${a+1}/${r.length}] - `)}${l.yellow(s)}`);const c=h.resolve(e.output,o,s),u=n[s],g=U(u,ae(c)).target;x(c,g),!D(u,g)&&this.query.push({entry:u,filename:c,from:e.entryLocale,target:g,to:o})}process.stdout.isTTY&&(process.stdout.clearLine(0),process.stdout.cursorTo(0))}genFlatQuery(){const e=this.config,n=bt(e);f.consola.start(`Running in ${l.bold.cyan("\u{1F4C4} Flat Mode")}, and translating ${l.bold.cyan(e.outputLocales.join("/"))} locales..`),St(e);for(const r of e.outputLocales){const o=h.resolve(e.output,r)+".json",a=n,s=U(a,ae(o)).target;x(o,s),!D(a,s)&&this.query.push({entry:a,filename:o,from:e.entryLocale,target:s,to:r})}}}const ce=i((t,e)=>t.map(n=>n.includes("*")||n.includes(e)?n:h.join(n,`**/*${e}`).replaceAll("\\","/")),"matchInputPattern");class le{static{i(this,"TranslateMarkdown")}config;markdownConfig;query=[];i18n;constructor(){this.markdownConfig=k.getMarkdownConfigFile();const e=k.getConfigFile();this.config={...e,entryLocale:e.entryLocale||this.markdownConfig.entryLocale,markdown:this.markdownConfig,outputLocales:e.outputLocales||this.markdownConfig.outputLocales},this.i18n=new ie({config:this.config,openAIApiKey:k.getOpenAIApiKey(),openAIProxyUrl:k.getOpenAIProxyUrl()})}async start(){f.consola.start("Lobe I18N is analyzing your markdown... \u{1F92F}\u{1F30F}\u{1F50D}");const e=this.markdownConfig.entry;(!e||e.length===0)&&p.alert.error("No markdown entry was found.",!0);let n=W.globSync(ce(e,".md"),{ignore:this.markdownConfig.exclude?ce(this.markdownConfig.exclude||[],".md"):void 0,nodir:!0});this.markdownConfig.entryExtension&&(n=n.filter(r=>r.includes(this.markdownConfig.entryExtension||".md"))),(!n||n.length===0)&&p.alert.error("No markdown entry was found.",!0),this.genFilesQuery(n),this.query.length>0?await this.runQuery():f.consola.success("No content requiring translation was found."),f.consola.success("All i18n tasks have been completed\uFF01")}async runQuery(){f.consola.info(`Current model setting: ${l.cyan(this.config.modelName)} (temperature: ${l.cyan(this.config.temperature)}) ${this.config.experimental?.jsonMode?l.red(" [JSON Mode]"):""}}`);let e=0;for(const n of this.query){const r={filename:n.filename,from:n.from||this.markdownConfig.entryLocale||this.config.entryLocale,to:n.to},{rerender:o,clear:a}=p.render(d.jsx(F,{hide:!0,isLoading:!0,maxStep:1,progress:0,step:0,...r})),s=await this.i18n.translateMarkdown({...n,onProgress:i(u=>{u.maxStep>0?o(d.jsx(F,{...u,...r})):a()},"onProgress")});a();const c=h.relative(".",n.filename);if(s?.result&&Object.keys(s.result).length>0){let u=s.result;this.markdownConfig.includeMatter||(u=X.stringify(s.result,n.matter)),vt(n.filename,u),e+=s.tokenUsage,f.consola.success(l.yellow(c),l.gray(`[Token usage: ${s.tokenUsage}]`))}else f.consola.warn("No translation result was found:",l.yellow(c))}e>0&&f.consola.info("Total token usage:",l.cyan(e))}genFilesQuery(e,n){const r=this.markdownConfig;n||f.consola.start(`Running in ${l.bold.cyan(`\u{1F4C4} ${e.length} Markdown`)}, and translating to ${l.bold.cyan(r?.outputLocales?.join("/"))} locales..`);for(const o of e)try{const a=wt(o);for(const s of r.outputLocales||[]){const c=this.getTargetExtension(s,o,a),u=this.getTargetFilename(o,c);if(y.existsSync(u))continue;const g=this.getMode(o,a),{data:M,content:T}=X(a);f.consola.info(`\u{1F4C4} To ${s}: ${l.yellow(u)}`),this.query.push({filename:u,from:r.entryLocale,matter:M,md:this.markdownConfig.includeMatter?a:T,mode:g,to:s})}}catch{p.alert.error(`${o} not found`,!0)}}getTargetExtension(e,n,r){return this.markdownConfig.outputExtensions?.(e,{fileContent:r,filePath:n,getDefaultExtension:E})||E(e)}getTargetFilename(e,n){if(this.markdownConfig.entryExtension)return h.resolve(".",e.replace(this.markdownConfig.entryExtension||".md",n));if(this.markdownConfig.entryLocale&&e.includes(`.${this.markdownConfig.entryLocale}.`))return[e.split(`.${this.markdownConfig.entryLocale}.`)[0],n].join("");{const r=e.split(".");return r.pop(),[r.join("."),n].join("")}}getMode(e,n){const r=this.markdownConfig.mode;return r?m.isString(r)?r:r({fileContent:n,filePath:e})||j.STRING:j.STRING}}const jt=pe({pkg:A,shouldNotifyInNpmScript:!0});jt.notify({isGlobal:!0});const b=new N.Command;b.name("lobe-i18n").description(A.description).version(A.version).addOption(new N.Option("-o, --option","Setup lobe-i18n preferences")).addOption(new N.Option("-c, --config <string>","Specify the configuration file")).addOption(new N.Option("-m, --with-md","Run i18n translation and markdown translation simultaneously")),b.command("locale",{isDefault:!0}).action(async()=>{const t=b.opts();t.option?p.render(d.jsx(st,{})):(t.config&&q.loadCustomConfig(t.config),await new Ct().start(),t.withMd&&await new le().start())}),b.command("md").action(async()=>{const t=b.opts();t.config&&q.loadCustomConfig(t.config),await new le().start()}),b.parse();