Files
triliumnext-korean-patch/updates/2026-09-05/work/trilium/build-api-pages.py
T

58 lines
5.0 KiB
Python

from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import urljoin
import json,html,re
b=Path('work/trilium');src=b/'api-source';out=b/'phase9/doc_notes/en/api-ko';out.mkdir(parents=True,exist_ok=True)
original=json.loads((src/'script-strings.json').read_text(encoding='utf-8'));translated=(b/'api-script-ko.txt').read_text(encoding='utf-8').splitlines();assert len(original)==len(translated)
mapping=dict(zip(original,translated));ui={'Index':'목차','Properties':'속성','Methods':'메서드','Constructors':'생성자','Constructor':'생성자','Parameters':'매개변수','Returns':'반환값','Type declaration':'형식 선언','Type Parameters':'형식 매개변수','Inherited from':'상속한 항목','Overrides':'재정의한 항목','Defined in':'정의 위치','Optional':'선택 사항','Readonly':'읽기 전용','Protected':'보호됨','Private':'비공개','Static':'정적','Permalink':'고유 링크','Interface':'인터페이스','Class':'클래스','Hierarchy':'상속 구조','Implements':'구현','Accessors':'접근자','Default':'기본값'}
mapping.update(json.loads((b/'api-script-extra.json').read_text(encoding='utf-8')))
urls={'frontend':'https://triliumnext.github.io/Notes/Script%20API/interfaces/Frontend_Script_API.Api.html','fnote':'https://triliumnext.github.io/Notes/Script%20API/classes/Frontend_Script_API.FNote.html','backend':'https://triliumnext.github.io/Notes/Script%20API/interfaces/Backend_Script_API.Api.html'}
titles={'frontend':'프런트엔드 스크립트 API','fnote':'프런트엔드 노트 객체 FNote','backend':'백엔드 스크립트 API'}
class Page(HTMLParser):
def __init__(self,url):super().__init__(convert_charrefs=False);self.url=url;self.depth=0;self.parts=[];self.tags=[];self.count=0;self.untranslated=[]
def handle_starttag(self,t,attrs):
a=dict(attrs)
if not self.depth:
if t=='div' and a.get('class')=='col-content':self.depth=1
else:return
else:
if t=='div':self.depth+=1
self.tags.append(t) if t not in ['br','hr','wbr','img','input','meta','link','use'] else None
if t=='a' and 'href'in a and not a['href'].startswith('#'):
absolute=urljoin(self.url,a['href']);a['href']=absolute
for n,u in urls.items():
if absolute.split('#')[0]==u:a['href']=n+'.html'+('#'+absolute.split('#',1)[1] if '#'in absolute else '')
for k in ['title','aria-label']:
if k in a:a[k]=ui.get(a[k],a[k])
self.parts.append('<'+t+''.join(' '+k+'="'+html.escape(v or '',quote=True)+'"' for k,v in a.items())+'>')
def handle_startendtag(self,t,a):
self.handle_starttag(t,a)
if t not in ['br','hr','wbr','img','input','meta','link','use']:self.handle_endtag(t)
def handle_endtag(self,t):
if not self.depth:return
self.parts.append('</'+t+'>')
if t=='div':self.depth-=1
for i in range(len(self.tags)-1,-1,-1):
if self.tags[i]==t:self.tags=self.tags[:i];break
def handle_data(self,s):
if not self.depth:return
k=s.strip();v=s
if not any(t in ['pre','code'] for t in self.tags):
if k in mapping:v=s.replace(k,mapping[k]);self.count+=1
elif k in ui:v=s.replace(k,ui[k])
elif re.match(r'^(Interface|Class|Defined in|Inherited from|Overrides|Returns)\b',k):
for a,c in ui.items():v=v.replace(a,c)
elif re.search(r'[a-zA-Z]{2,}\s+[a-zA-Z]{2,}',k):self.untranslated.append(k)
self.parts.append(html.escape(v,quote=False))
def handle_entityref(self,n):
if self.depth:self.parts.append('&'+n+';')
def handle_charref(self,n):
if self.depth:self.parts.append('&#'+n+';')
style='body{font:16px/1.7 system-ui,sans-serif;max-width:1050px;margin:24px auto;padding:0 22px;color:#24292f}a{color:#075fbd}pre,.tsd-signature{background:#f3f5f7;padding:12px;overflow:auto}code{font-family:monospace}.tsd-kind-icon{width:18px;height:18px}section{margin:20px 0}h2{border-bottom:1px solid #ddd}h3{margin-top:30px}ul{padding-left:24px}.tsd-anchor-icon{display:none}nav{background:#eef6ff;padding:12px}details{margin:12px 0}svg{display:none}'
report={}
for n,url in urls.items():
p=Page(url);p.feed((src/(n+'.html')).read_text(encoding='utf-8'));assert p.count>0
page='<!doctype html><html lang="ko"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>'+titles[n]+'</title><style>'+style+'</style><body><h1>'+titles[n]+'</h1><nav><a href="frontend.html">프런트엔드 API</a> · <a href="fnote.html">노트 객체</a> · <a href="backend.html">백엔드 API</a></nav><p>연결된 공식 문서의 한국어 사본입니다. 함수 이름과 코드 문법은 원문을 유지했습니다. 외부 문서는 운영 버전과 다를 수 있으므로 지원 여부를 확인하세요. <a href="'+url+'" target="_blank" rel="noopener">공식 원문 보기</a></p>'+''.join(p.parts)+'</body></html>'
(out/(n+'.html')).write_text(page,encoding='utf-8');report[n]={'translatedTextNodes':p.count,'remainingProse':list(dict.fromkeys(p.untranslated))}
(b/'phase9/script-audit.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8');print(json.dumps(report,ensure_ascii=False))