import fs from 'node:fs'; import crypto from 'node:crypto'; import {translateProse} from './demo-prose.mjs'; import {translateDiagram} from './demo-diagrams.mjs'; import {translateStructured,translateCode} from './demo-structured.mjs'; const b='work/trilium'; const notes=JSON.parse(fs.readFileSync(b+'/scoped/notes.json')); const titles=JSON.parse(fs.readFileSync(b+'/demo-titles.json')); titles.Sequence='시퀀스 다이어그램'; const simple=JSON.parse(fs.readFileSync(b+'/demo-simple.json')); Object.assign(simple,{ 'Bob likes to read popular science books so something like that…':'밥은 대중 과학서를 좋아하니 그런 책을 골라 보자.', 'Main characters':'주요 등장인물','… here put main characters …':'주요 등장인물을 여기에 적습니다.','Plot':'줄거리','… describe main plot lines …':'주요 줄거리를 정리합니다.','Tone':'분위기','Genre':'장르','scifi / drama / romance':'과학소설 / 드라마 / 로맨스','Similar books':'비슷한 책','highlght 1':'인상 깊은 구절 1','my comment':'나의 생각','Carp of course!':'역시 잉어 요리지!', 'This table shows notes with most revisions':'이전 버전이 많이 저장된 노트를 보여 줍니다.', 'This table shows notes which are linked by other notes through relations':'다른 노트에서 관계로 연결한 횟수가 많은 노트를 보여 줍니다.', 'This table shows 100 largest notes, including their revisions and attachments.':'이전 버전과 첨부 파일을 포함해 용량이 가장 큰 노트 100개를 보여 줍니다.', 'Note':'노트','Revision count':'이전 버전 수','Relation count':'관계 수','Size':'용량','Attribute name':'속성 이름','Count':'개수','Clone count':'복제본 수','Note type':'노트 유형','Count (not deleted)':'현재 노트 수','Count (deleted)':'삭제된 노트 수','Word count:':'단어 수:','Character count:':'글자 수:' }); const normalized=Object.fromEntries(Object.entries(simple).map(([k,v])=>[k.trim().replace(/\s+/g,' '),v.trim()])); function textNodes(c){return c.split(/(<[^>]*>)/).map((part,i)=>i%2?part:(normalized[part.trim().replace(/\s+/g,' ')]!==undefined?part.replace(/\S[\s\S]*\S|\S/,normalized[part.trim().replace(/\s+/g,' ')]):part)).join('')} const changes=[];const missing=[]; for(const n of notes){ let title=titles[n.title]||n.title; if(n.type==='code'&&title!==n.title){let label=title.replace(/[a-zA-Z0-9_.]+/g,'').trim()||'자바스크립트';title=label+' ('+n.title.replace(/[^a-z0-9_]/gi,'')+')';} let content=n.content; if(n.type==='text')content=textNodes(translateProse(n)); if(n.type==='code'&&n.mime==='text/html')content=textNodes(content); if(n.type==='code'&&n.noteId==='SqPyuFmsgFMb')content=content.replace('Word count:','단어 수:').replace('Character count:','글자 수:'); if(n.type==='code'&&n.noteId==='PUAIjmnDHzwt')content=content.replace('See explanation ','설명은 ').replace('>here<','>여기<').replace('.','에서 확인하세요.'); if(n.type==='code'&&n.noteId==='2Y8gtMg4lkeL')content=content.replace("title: 'New task'","title: '새 할 일'"); if(n.type==='code'&&n.noteId==='LybeV4wPIF18')content=content.replace("'new task'","'새 할 일'"); if(n.type==='code'&&n.noteId==='njPTYevnqFRO')content=content.replace('Weight (kg)','체중 (kg)'); if(n.type==='code'&&n.noteId==='dq9PJyGEVMVu')content=content.replace('" bytes"','"바이트"'); if(n.type==='code'&&['EW0u2LoAZTYv','C5fffQJ0Vzr1'].includes(n.noteId))content=content.replace("addRow('total'","addRow('합계'"); if(n.type==='mermaid')content=translateDiagram(n); if(['mindMap','canvas'].includes(n.type))content=translateStructured(n); if(['code','text'].includes(n.type))content=translateCode(n,content); if(title!==n.title||content!==n.content){let o={noteId:n.noteId,oldTitle:n.title,title};if(content!==n.content){o.oldContentSha256=crypto.createHash('sha256').update(n.content||'').digest('hex');o.content=content}changes.push(o)} if(n.type==='text'){ let text=content.replace(//g,'').replace(/[\s\S]*?<\/span>/g,'').replace(/<[^>]*>/g,' ').replace(/https?:\/\/[^\s<]+/g,'').replace(/&\w+;/g,' ').replace(/\s+/g,' '); if(/[a-zA-Z]{3,} [a-zA-Z]{3,}/.test(text))missing.push({noteId:n.noteId,title,text}); } } fs.writeFileSync(b+'/release/demo-migration.json',JSON.stringify({version:1,changes},null,2)); fs.writeFileSync(b+'/demo-remaining.json',JSON.stringify(missing,null,2)); console.log(JSON.stringify({changes:changes.length,remainingEnglishText:missing.length}));