import { readFile, writeFile } from 'node:fs/promises' const overrides = { 'zh-TW': { common: { close: '關閉', reset: '重設', add: '新增', remove: '移除', delete: '刪除', clear: '清除', cancel: '取消', confirm: '確定', none: '無', enabled: '啟用', disabled: '停用', fixed: '固定', random: '隨機', curve: '曲線', followExport: '跟隨匯出', linear: '線性', bezier: '貝茲', above: '上方', below: '下方', left: '左側', right: '右側', seconds: '秒', frames: '幀', }, toolbar: { settings: '系統設定', undo: '復原上一步 (Ctrl+Z)', pan: '拖曳畫布 (Q)', translate: '位移 (W)', rotate: '旋轉 (E)', scale: '縮放 (R)', zoomIn: '放大', zoomOut: '縮小', fitView: '適合畫布', toggleGrid: '切換網格', resetView: '重設視圖', background: '背景圖', backgroundSettings: '背景圖顯示設定', changeBackground: '更換圖像', uploadBackground: '上傳圖像', clearBackground: '清除背景', noBackground: '未上傳圖像', opacity: '透明度', help: '使用說明', openHelp: '查看使用說明', }, config: { title: '設定管理', save: '儲存設定', load: '載入設定', selectPreset: '選擇預設', noPresets: '無預設檔案' }, image: { fixed: '固定', sequence: '序列幀', onceHide: '單次-尾幀消失', onceHold: '單次-尾幀固定', loopForward: '正向循環', loopReverse: '反向循環', weight: '權重 %', pivot: '貼圖軸點' }, emission: { title: '發射模式', stream: '持續', burst: '爆發', normalStream: '一般持續', loopStream: '循環持續', rate: '發射速率', duration: '發射時長', burstCount: '單次數量', delay: '延遲時間', shapeTitle: '發射器形狀', point: '點', circle: '圓形', rect: '矩形', cone: '錐形', path: '路徑', }, attributes: { title: '粒子屬性', initialParams: '粒子初始參數', lifecycleTitle: '粒子生命週期變化參數' }, appearance: { title: '外觀與資源', opacity: '透明度', renderLayer: '渲染層級(Spine 插槽)', imageResources: '圖片資源', independentAlpha: '獨立透明度', off: '關閉', normal: '正常', add: '相加', multiply: '相乘', screen: '濾色' }, scene: { title: '場景物件', particle: '粒子', subEmitter: '子發射器', collision: '碰撞', path: '路徑', positionX: 'X 位置', positionY: 'Y 位置', offsetX: 'X 偏移', offsetY: 'Y 偏移', rotation: '旋轉', scaleX: 'X 縮放', scaleY: 'Y 縮放', xPosition: 'X 位置', yPosition: 'Y 位置', scaleXShort: 'X 縮放', scaleYShort: 'Y 縮放', seed: '隨機種子' }, modifiers: { title: '修改器', damping: '阻尼', enableNoise: '開啟移動雜訊', enableGravity: '開啟重力', enableWind: '開啟風力', enableForceField: '開啟力場', enableVortex: '開啟漩渦', enableAttraction: '開啟吸附', enableTrail: '開啟拖尾', enableFollow: '開啟發射器跟隨', enableCollision: '開啟碰撞', enableSubEmitter: '開啟子發射器', }, timeline: { continuousLoop: '持續循環播放', abLoop: '獨立 A/B 區間循環', abLoopShort: 'AB 循環', emission: '持續發射', particleLife: '粒子存在', spineAnimation: 'Spine 動畫', ownerAnimation: '所屬動畫', frameStatus: '幀 {current} / {total}', }, export: { title: '匯出設定', spineVersion: 'Spine 版本', fps: '匯出 FPS', exportJson: '匯出 Spine JSON', exportImages: '匯出圖片 ZIP', connectorReady: '連接器可用 · Spine 已就緒', connectorOffline: '本機 Spine 連接器未啟動', spineNotFound: '連接器已啟動,但找不到 Spine', notAuthorized: '連接器已啟動,但目前網站尚未授權', checking: '偵測中', offline: '未啟動', notFound: '找不到 Spine', unauthorized: '未授權', connectorBusy: '正在重新計算並產生 Spine 專案', connectorGenerate: '使用本機 Spine 自動產生 {version} 專案' }, spine: { title: 'Spine 屬性', resources: 'Spine 資源', animations: 'Spine 動畫', hierarchy: '階層樹', xPosition: 'X 位置', yPosition: 'Y 位置', rotation: '旋轉', startTime: '開始時間', scaleX: 'X 縮放', scaleY: 'Y 縮放' }, help: { title: '使用說明', commonOps: '常用操作', shortcuts: '快速鍵', modules: '功能模組詳解' }, presets: { particleSweep: '1.粒子掃過', barBlocks: '2.長條方塊', forceTrail: '3.力場拖尾', lightningSequence: '4.雷電序列幀', texturePivot: '5.貼圖軸點', subEmitter: '6.子發射器' }, }, ja: { common: { close: '閉じる', reset: 'リセット', add: '追加', remove: '削除', delete: '削除', clear: 'クリア', cancel: 'キャンセル', confirm: '確定', none: 'なし', enabled: '有効', disabled: '無効', fixed: '固定', random: 'ランダム', curve: 'カーブ', followExport: 'エクスポート設定に従う', linear: 'リニア', bezier: 'ベジェ', above: '上', below: '下', left: '左', right: '右', seconds: '秒', frames: 'フレーム', }, toolbar: { settings: '設定', undo: '元に戻す (Ctrl+Z)', pan: 'キャンバス移動 (Q)', translate: '移動 (W)', rotate: '回転 (E)', scale: '拡大縮小 (R)', zoomIn: '拡大', zoomOut: '縮小', fitView: 'キャンバスに合わせる', toggleGrid: 'グリッド切替', resetView: 'ビューをリセット', background: '背景', backgroundSettings: '背景画像設定', changeBackground: '画像を変更', uploadBackground: '画像をアップロード', clearBackground: '背景をクリア', noBackground: '画像なし', opacity: '不透明度', help: 'ヘルプ', openHelp: 'ヘルプを開く', }, config: { title: '設定管理', save: '設定を保存', load: '設定を読み込む', selectPreset: 'プリセットを選択', noPresets: 'プリセットなし' }, image: { fixed: '固定', sequence: 'シーケンス', onceHide: '1回・最終フレームで非表示', onceHold: '1回・最終フレームを保持', loopForward: '正方向ループ', loopReverse: '逆方向ループ', weight: '重み %', pivot: 'テクスチャピボット' }, emission: { title: 'エミッションモード', stream: 'ストリーム', burst: 'バースト', normalStream: '通常ストリーム', loopStream: 'ループストリーム', rate: '放出レート', duration: '放出時間', burstCount: '一度の放出数', delay: '遅延時間', shapeTitle: 'エミッター形状', point: '点', circle: '円', rect: '矩形', cone: '円錐', path: 'パス', }, attributes: { title: 'パーティクル属性', initialParams: 'パーティクル初期値', lifecycleTitle: 'ライフタイム中の変化' }, appearance: { title: '外観とリソース', opacity: '不透明度', renderLayer: 'レンダー階層(Spine スロット)', imageResources: '画像リソース', independentAlpha: '独立した不透明度', off: 'オフ', normal: '通常', add: '加算', multiply: '乗算', screen: 'スクリーン' }, scene: { title: 'シーンオブジェクト', particle: 'パーティクル', subEmitter: 'サブエミッター', collision: 'コリジョン', path: 'パス', positionX: 'X 位置', positionY: 'Y 位置', offsetX: 'X オフセット', offsetY: 'Y オフセット', rotation: '回転', scaleX: 'X スケール', scaleY: 'Y スケール', xPosition: 'X 位置', yPosition: 'Y 位置', scaleXShort: 'X スケール', scaleYShort: 'Y スケール', seed: 'ランダムシード' }, modifiers: { title: 'モディファイア', damping: '減衰', enableNoise: '移動ノイズを有効化', enableGravity: '重力を有効化', enableWind: '風を有効化', enableForceField: 'フォースフィールドを有効化', enableVortex: '渦を有効化', enableAttraction: '吸着を有効化', enableTrail: 'トレイルを有効化', enableFollow: 'エミッター追従を有効化', enableCollision: 'コリジョンを有効化', enableSubEmitter: 'サブエミッターを有効化', }, timeline: { continuousLoop: '連続ループ再生', abLoop: '独立した A/B 区間ループ', abLoopShort: 'AB ループ', emission: '放出中', particleLife: 'パーティクル存在', spineAnimation: 'Spine アニメーション', ownerAnimation: '所属アニメーション', frameStatus: 'フレーム {current} / {total}', }, export: { title: 'エクスポート設定', spineVersion: 'Spine バージョン', fps: 'エクスポート FPS', exportJson: 'Spine JSON を書き出す', exportImages: '画像 ZIP を書き出す', connectorReady: 'コネクタ使用可能 · Spine 準備完了', connectorOffline: 'ローカル Spine コネクタが起動していません', spineNotFound: 'コネクタは起動していますが Spine が見つかりません', notAuthorized: 'コネクタは起動していますが、このサイトは未認証です', checking: '確認中', offline: '未起動', notFound: 'Spine が見つかりません', unauthorized: '未認証', connectorBusy: 'Spine プロジェクトを再計算して生成中', connectorGenerate: 'ローカル Spine で Spine {version} プロジェクトを生成' }, spine: { title: 'Spine プロパティ', resources: 'Spine リソース', animations: 'Spine アニメーション', hierarchy: '階層ツリー', xPosition: 'X 位置', yPosition: 'Y 位置', rotation: '回転', startTime: '開始時間', scaleX: 'X スケール', scaleY: 'Y スケール' }, help: { title: 'ヘルプ', commonOps: 'よく使う操作', shortcuts: 'ショートカット', modules: '機能モジュール' }, presets: { particleSweep: '1.パーティクルスイープ', barBlocks: '2.バーブロック', forceTrail: '3.フォースフィールドトレイル', lightningSequence: '4.雷シーケンス', texturePivot: '5.テクスチャピボット', subEmitter: '6.サブエミッター' }, }, ko: { common: { close: '닫기', reset: '초기화', add: '추가', remove: '제거', delete: '삭제', clear: '지우기', cancel: '취소', confirm: '확인', none: '없음', enabled: '사용', disabled: '사용 안 함', fixed: '고정', random: '랜덤', curve: '곡선', followExport: '내보내기 설정 따르기', linear: '선형', bezier: '베지어', above: '위', below: '아래', left: '왼쪽', right: '오른쪽', seconds: '초', frames: '프레임', }, toolbar: { settings: '설정', undo: '실행 취소 (Ctrl+Z)', pan: '캔버스 이동 (Q)', translate: '이동 (W)', rotate: '회전 (E)', scale: '크기 조절 (R)', zoomIn: '확대', zoomOut: '축소', fitView: '캔버스에 맞추기', toggleGrid: '그리드 전환', resetView: '보기 초기화', background: '배경', backgroundSettings: '배경 이미지 설정', changeBackground: '이미지 교체', uploadBackground: '이미지 업로드', clearBackground: '배경 지우기', noBackground: '이미지 없음', opacity: '불투명도', help: '도움말', openHelp: '도움말 열기', }, config: { title: '설정 관리', save: '설정 저장', load: '설정 불러오기', selectPreset: '프리셋 선택', noPresets: '프리셋 없음' }, image: { fixed: '고정', sequence: '시퀀스', onceHide: '1회·마지막 프레임에서 숨김', onceHold: '1회·마지막 프레임 유지', loopForward: '정방향 루프', loopReverse: '역방향 루프', weight: '가중치 %', pivot: '텍스처 피벗' }, emission: { title: '이미션 모드', stream: '스트림', burst: '버스트', normalStream: '일반 스트림', loopStream: '루프 스트림', rate: '이미션 속도', duration: '이미션 시간', burstCount: '1회 생성 수', delay: '지연 시간', shapeTitle: '이미터 모양', point: '점', circle: '원', rect: '사각형', cone: '원뿔', path: '경로', }, attributes: { title: '파티클 속성', initialParams: '파티클 초기값', lifecycleTitle: '수명에 따른 변화' }, appearance: { title: '외형과 리소스', opacity: '불투명도', renderLayer: '렌더링 레이어(Spine 슬롯)', imageResources: '이미지 리소스', independentAlpha: '독립 불투명도', off: '끄기', normal: '일반', add: '더하기', multiply: '곱하기', screen: '스크린' }, scene: { title: '장면 오브젝트', particle: '파티클', subEmitter: '하위 이미터', collision: '충돌', path: '경로', positionX: 'X 위치', positionY: 'Y 위치', offsetX: 'X 오프셋', offsetY: 'Y 오프셋', rotation: '회전', scaleX: 'X 크기', scaleY: 'Y 크기', xPosition: 'X 위치', yPosition: 'Y 위치', scaleXShort: 'X 크기', scaleYShort: 'Y 크기', seed: '랜덤 시드' }, modifiers: { title: '모디파이어', damping: '감쇠', enableNoise: '이동 노이즈 사용', enableGravity: '중력 사용', enableWind: '바람 사용', enableForceField: '힘장 사용', enableVortex: '소용돌이 사용', enableAttraction: '흡인 사용', enableTrail: '트레일 사용', enableFollow: '이미터 추적 사용', enableCollision: '충돌 사용', enableSubEmitter: '하위 이미터 사용', }, timeline: { continuousLoop: '연속 루프 재생', abLoop: '독립 A/B 구간 루프', abLoopShort: 'AB 루프', emission: '이미션 중', particleLife: '파티클 존재', spineAnimation: 'Spine 애니메이션', ownerAnimation: '소속 애니메이션', frameStatus: '프레임 {current} / {total}', }, export: { title: '내보내기 설정', spineVersion: 'Spine 버전', fps: '내보내기 FPS', exportJson: 'Spine JSON 내보내기', exportImages: '이미지 ZIP 내보내기', connectorReady: '커넥터 사용 가능 · Spine 준비됨', connectorOffline: '로컬 Spine 커넥터가 실행 중이 아닙니다', spineNotFound: '커넥터는 실행 중이지만 Spine을 찾을 수 없습니다', notAuthorized: '커넥터는 실행 중이지만 이 사이트는 승인되지 않았습니다', checking: '확인 중', offline: '오프라인', notFound: 'Spine 없음', unauthorized: '승인되지 않음', connectorBusy: 'Spine 프로젝트를 다시 계산하고 생성 중', connectorGenerate: '로컬 Spine으로 Spine {version} 프로젝트 생성' }, spine: { title: 'Spine 속성', resources: 'Spine 리소스', animations: 'Spine 애니메이션', hierarchy: '계층 트리', xPosition: 'X 위치', yPosition: 'Y 위치', rotation: '회전', startTime: '시작 시간', scaleX: 'X 크기', scaleY: 'Y 크기' }, help: { title: '도움말', commonOps: '자주 쓰는 작업', shortcuts: '단축키', modules: '기능 모듈' }, presets: { particleSweep: '1.파티클 스윕', barBlocks: '2.바 블록', forceTrail: '3.힘장 트레일', lightningSequence: '4.번개 시퀀스', texturePivot: '5.텍스처 피벗', subEmitter: '6.하위 이미터' }, }, fr: { common: { close: 'Fermer', reset: 'Réinitialiser', add: 'Ajouter', remove: 'Retirer', delete: 'Supprimer', clear: 'Effacer', cancel: 'Annuler', confirm: 'Confirmer', none: 'Aucun', enabled: 'Activé', disabled: 'Désactivé', fixed: 'Fixe', random: 'Aléatoire', curve: 'Courbe', followExport: 'Suivre l’export', linear: 'Linéaire', bezier: 'Bézier', above: 'Au-dessus', below: 'En dessous', left: 'Gauche', right: 'Droite', seconds: 's', frames: 'images', }, toolbar: { settings: 'Paramètres', undo: 'Annuler (Ctrl+Z)', pan: 'Déplacer le canevas (Q)', translate: 'Déplacement (W)', rotate: 'Rotation (E)', scale: 'Échelle (R)', zoomIn: 'Zoom avant', zoomOut: 'Zoom arrière', fitView: 'Ajuster le canevas', toggleGrid: 'Afficher/masquer la grille', resetView: 'Réinitialiser la vue', background: 'Arrière-plan', backgroundSettings: 'Paramètres de l’arrière-plan', changeBackground: 'Remplacer l’image', uploadBackground: 'Importer une image', clearBackground: 'Effacer l’arrière-plan', noBackground: 'Aucune image', opacity: 'Opacité', help: 'Aide', openHelp: 'Ouvrir l’aide', }, config: { title: 'Configuration', save: 'Enregistrer', load: 'Charger', selectPreset: 'Choisir un préréglage', noPresets: 'Aucun préréglage' }, image: { fixed: 'Image fixe', sequence: 'Séquence', onceHide: 'Une fois, masquer à la fin', onceHold: 'Une fois, conserver la dernière image', loopForward: 'Boucle avant', loopReverse: 'Boucle arrière', weight: 'Poids %', pivot: 'Pivot de texture' }, emission: { title: 'Mode d’émission', stream: 'Continu', burst: 'Rafale', normalStream: 'Continu standard', loopStream: 'Boucle continue', rate: 'Taux d’émission', duration: 'Durée d’émission', burstCount: 'Quantité par rafale', delay: 'Délai', shapeTitle: 'Forme de l’émetteur', point: 'Point', circle: 'Cercle', rect: 'Rectangle', cone: 'Cône', path: 'Chemin', }, attributes: { title: 'Attributs des particules', initialParams: 'Valeurs initiales', lifecycleTitle: 'Évolution pendant la durée de vie' }, appearance: { title: 'Apparence et ressources', opacity: 'Opacité', renderLayer: 'Calque de rendu (slot Spine)', imageResources: 'Ressources d’image', independentAlpha: 'Opacité indépendante', off: 'Désactivé', normal: 'Normal', add: 'Ajout', multiply: 'Multiplication', screen: 'Écran' }, scene: { title: 'Objets de scène', particle: 'Particules', subEmitter: 'Sous-émetteur', collision: 'Collision', path: 'Chemin', positionX: 'Position X', positionY: 'Position Y', offsetX: 'Décalage X', offsetY: 'Décalage Y', rotation: 'Rotation', scaleX: 'Échelle X', scaleY: 'Échelle Y', xPosition: 'Position X', yPosition: 'Position Y', scaleXShort: 'Échelle X', scaleYShort: 'Échelle Y', seed: 'Graine aléatoire' }, modifiers: { title: 'Modificateurs', damping: 'Amortissement', enableNoise: 'Activer le bruit de mouvement', enableGravity: 'Activer la gravité', enableWind: 'Activer le vent', enableForceField: 'Activer le champ de force', enableVortex: 'Activer le tourbillon', enableAttraction: 'Activer l’attraction', enableTrail: 'Activer la traînée', enableFollow: 'Activer le suivi de l’émetteur', enableCollision: 'Activer la collision', enableSubEmitter: 'Activer les sous-émetteurs', }, timeline: { continuousLoop: 'Lecture en boucle continue', abLoop: 'Boucle indépendante de plage A/B', abLoopShort: 'Boucle AB', emission: 'Émission', particleLife: 'Particules actives', spineAnimation: 'Animation Spine', ownerAnimation: 'Animation associée', frameStatus: 'Image {current} / {total}', }, export: { title: 'Paramètres d’export', spineVersion: 'Version de Spine', fps: 'FPS d’export', exportJson: 'Exporter Spine JSON', exportImages: 'Exporter les images ZIP', connectorReady: 'Connecteur disponible · Spine prêt', connectorOffline: 'Le connecteur Spine local n’est pas démarré', spineNotFound: 'Le connecteur est démarré, mais Spine est introuvable', notAuthorized: 'Le connecteur est démarré, mais ce site n’est pas autorisé', checking: 'Vérification', offline: 'Hors ligne', notFound: 'Spine introuvable', unauthorized: 'Non autorisé', connectorBusy: 'Recalcul et génération du projet Spine', connectorGenerate: 'Générer un projet Spine {version} avec Spine local' }, spine: { title: 'Propriétés Spine', resources: 'Ressources Spine', animations: 'Animations Spine', hierarchy: 'Hiérarchie', xPosition: 'Position X', yPosition: 'Position Y', rotation: 'Rotation', startTime: 'Heure de début', scaleX: 'Échelle X', scaleY: 'Échelle Y' }, help: { title: 'Aide', commonOps: 'Actions courantes', shortcuts: 'Raccourcis', modules: 'Guide des modules' }, presets: { particleSweep: '1.Balayage de particules', barBlocks: '2.Blocs rectangulaires', forceTrail: '3.Traînée de champ de force', lightningSequence: '4.Séquence d’éclairs', texturePivot: '5.Pivot de texture', subEmitter: '6.Sous-émetteur' }, }, de: { common: { close: 'Schließen', reset: 'Zurücksetzen', add: 'Hinzufügen', remove: 'Entfernen', delete: 'Löschen', clear: 'Leeren', cancel: 'Abbrechen', confirm: 'Bestätigen', none: 'Keine', enabled: 'Aktiviert', disabled: 'Deaktiviert', fixed: 'Fest', random: 'Zufällig', curve: 'Kurve', followExport: 'Exporteinstellung folgen', linear: 'Linear', bezier: 'Bézier', above: 'Über', below: 'Unter', left: 'Links', right: 'Rechts', seconds: 's', frames: 'Frames', }, toolbar: { settings: 'Einstellungen', undo: 'Rückgängig (Ctrl+Z)', pan: 'Canvas verschieben (Q)', translate: 'Verschieben (W)', rotate: 'Drehen (E)', scale: 'Skalieren (R)', zoomIn: 'Vergrößern', zoomOut: 'Verkleinern', fitView: 'An Canvas anpassen', toggleGrid: 'Raster umschalten', resetView: 'Ansicht zurücksetzen', background: 'Hintergrund', backgroundSettings: 'Hintergrundeinstellungen', changeBackground: 'Bild ersetzen', uploadBackground: 'Bild hochladen', clearBackground: 'Hintergrund löschen', noBackground: 'Kein Bild', opacity: 'Deckkraft', help: 'Hilfe', openHelp: 'Hilfe öffnen', }, config: { title: 'Konfiguration', save: 'Konfiguration speichern', load: 'Konfiguration laden', selectPreset: 'Voreinstellung wählen', noPresets: 'Keine Voreinstellungen' }, image: { fixed: 'Einzelbild', sequence: 'Sequenz', onceHide: 'Einmal, am Ende ausblenden', onceHold: 'Einmal, letztes Bild halten', loopForward: 'Vorwärts-Schleife', loopReverse: 'Rückwärts-Schleife', weight: 'Gewicht %', pivot: 'Textur-Pivot' }, emission: { title: 'Emissionsmodus', stream: 'Kontinuierlich', burst: 'Burst', normalStream: 'Normaler Stream', loopStream: 'Loop-Stream', rate: 'Emissionsrate', duration: 'Emissionsdauer', burstCount: 'Menge pro Burst', delay: 'Verzögerung', shapeTitle: 'Emitterform', point: 'Punkt', circle: 'Kreis', rect: 'Rechteck', cone: 'Kegel', path: 'Pfad', }, attributes: { title: 'Partikeleigenschaften', initialParams: 'Initialwerte', lifecycleTitle: 'Änderung über die Lebensdauer' }, appearance: { title: 'Aussehen und Ressourcen', opacity: 'Deckkraft', renderLayer: 'Renderebene (Spine-Slot)', imageResources: 'Bildressourcen', independentAlpha: 'Unabhängige Deckkraft', off: 'Aus', normal: 'Normal', add: 'Addieren', multiply: 'Multiplizieren', screen: 'Bildschirm' }, scene: { title: 'Szenenobjekte', particle: 'Partikel', subEmitter: 'Sub-Emitter', collision: 'Kollision', path: 'Pfad', positionX: 'X-Position', positionY: 'Y-Position', offsetX: 'X-Versatz', offsetY: 'Y-Versatz', rotation: 'Drehung', scaleX: 'X-Skalierung', scaleY: 'Y-Skalierung', xPosition: 'X-Position', yPosition: 'Y-Position', scaleXShort: 'X-Skalierung', scaleYShort: 'Y-Skalierung', seed: 'Zufallsseed' }, modifiers: { title: 'Modifikatoren', damping: 'Dämpfung', enableNoise: 'Bewegungsrauschen aktivieren', enableGravity: 'Schwerkraft aktivieren', enableWind: 'Wind aktivieren', enableForceField: 'Kraftfeld aktivieren', enableVortex: 'Wirbel aktivieren', enableAttraction: 'Anziehung aktivieren', enableTrail: 'Schweif aktivieren', enableFollow: 'Emitter-Verfolgung aktivieren', enableCollision: 'Kollision aktivieren', enableSubEmitter: 'Sub-Emitter aktivieren', }, timeline: { continuousLoop: 'Dauerhaft in Schleife abspielen', abLoop: 'Unabhängige A/B-Bereichsschleife', abLoopShort: 'AB-Schleife', emission: 'Emission', particleLife: 'Aktive Partikel', spineAnimation: 'Spine-Animation', ownerAnimation: 'Zugehörige Animation', frameStatus: 'Frame {current} / {total}', }, export: { title: 'Exporteinstellungen', spineVersion: 'Spine-Version', fps: 'Export-FPS', exportJson: 'Spine JSON exportieren', exportImages: 'Bilder-ZIP exportieren', connectorReady: 'Connector verfügbar · Spine bereit', connectorOffline: 'Der lokale Spine-Connector läuft nicht', spineNotFound: 'Der Connector läuft, aber Spine wurde nicht gefunden', notAuthorized: 'Der Connector läuft, aber diese Website ist nicht autorisiert', checking: 'Prüfung', offline: 'Offline', notFound: 'Spine nicht gefunden', unauthorized: 'Nicht autorisiert', connectorBusy: 'Spine-Projekt wird neu berechnet und erstellt', connectorGenerate: 'Spine-{version}-Projekt mit lokalem Spine erstellen' }, spine: { title: 'Spine-Eigenschaften', resources: 'Spine-Ressourcen', animations: 'Spine-Animationen', hierarchy: 'Hierarchie', xPosition: 'X-Position', yPosition: 'Y-Position', rotation: 'Drehung', startTime: 'Startzeit', scaleX: 'X-Skalierung', scaleY: 'Y-Skalierung' }, help: { title: 'Hilfe', commonOps: 'Häufige Aktionen', shortcuts: 'Tastenkürzel', modules: 'Modulhandbuch' }, presets: { particleSweep: '1.Partikel-Sweep', barBlocks: '2.Balkenblöcke', forceTrail: '3.Kraftfeldschweif', lightningSequence: '4.Blitzsequenz', texturePivot: '5.Textur-Pivot', subEmitter: '6.Sub-Emitter' }, }, 'pt-BR': { common: { close: 'Fechar', reset: 'Redefinir', add: 'Adicionar', remove: 'Remover', delete: 'Excluir', clear: 'Limpar', cancel: 'Cancelar', confirm: 'Confirmar', none: 'Nenhum', enabled: 'Ativado', disabled: 'Desativado', fixed: 'Fixo', random: 'Aleatório', curve: 'Curva', followExport: 'Seguir exportação', linear: 'Linear', bezier: 'Bézier', above: 'Acima', below: 'Abaixo', left: 'Esquerda', right: 'Direita', seconds: 's', frames: 'quadros', }, toolbar: { settings: 'Configurações', undo: 'Desfazer (Ctrl+Z)', pan: 'Mover tela (Q)', translate: 'Mover (W)', rotate: 'Girar (E)', scale: 'Escalar (R)', zoomIn: 'Ampliar', zoomOut: 'Reduzir', fitView: 'Ajustar à tela', toggleGrid: 'Alternar grade', resetView: 'Redefinir visualização', background: 'Fundo', backgroundSettings: 'Configurações do fundo', changeBackground: 'Trocar imagem', uploadBackground: 'Enviar imagem', clearBackground: 'Limpar fundo', noBackground: 'Nenhuma imagem', opacity: 'Opacidade', help: 'Ajuda', openHelp: 'Abrir ajuda', }, config: { title: 'Gerenciamento de configuração', save: 'Salvar configuração', load: 'Carregar configuração', selectPreset: 'Selecionar predefinição', noPresets: 'Nenhuma predefinição' }, image: { fixed: 'Imagem única', sequence: 'Sequência', onceHide: 'Uma vez, ocultar no final', onceHold: 'Uma vez, manter último quadro', loopForward: 'Loop para frente', loopReverse: 'Loop reverso', weight: 'Peso %', pivot: 'Pivô da textura' }, emission: { title: 'Modo de emissão', stream: 'Contínuo', burst: 'Explosão', normalStream: 'Contínuo normal', loopStream: 'Loop contínuo', rate: 'Taxa de emissão', duration: 'Duração da emissão', burstCount: 'Quantidade por explosão', delay: 'Atraso', shapeTitle: 'Forma do emissor', point: 'Ponto', circle: 'Círculo', rect: 'Retângulo', cone: 'Cone', path: 'Caminho', }, attributes: { title: 'Atributos das partículas', initialParams: 'Valores iniciais', lifecycleTitle: 'Alterações durante o tempo de vida' }, appearance: { title: 'Aparência e recursos', opacity: 'Opacidade', renderLayer: 'Camada de renderização (slot Spine)', imageResources: 'Recursos de imagem', independentAlpha: 'Opacidade independente', off: 'Desligado', normal: 'Normal', add: 'Adição', multiply: 'Multiplicação', screen: 'Tela' }, scene: { title: 'Objetos da cena', particle: 'Partículas', subEmitter: 'Subemissor', collision: 'Colisão', path: 'Caminho', positionX: 'Posição X', positionY: 'Posição Y', offsetX: 'Deslocamento X', offsetY: 'Deslocamento Y', rotation: 'Rotação', scaleX: 'Escala X', scaleY: 'Escala Y', xPosition: 'Posição X', yPosition: 'Posição Y', scaleXShort: 'Escala X', scaleYShort: 'Escala Y', seed: 'Semente aleatória' }, modifiers: { title: 'Modificadores', damping: 'Amortecimento', enableNoise: 'Ativar ruído de movimento', enableGravity: 'Ativar gravidade', enableWind: 'Ativar vento', enableForceField: 'Ativar campo de força', enableVortex: 'Ativar vórtice', enableAttraction: 'Ativar atração', enableTrail: 'Ativar rastro', enableFollow: 'Ativar seguimento do emissor', enableCollision: 'Ativar colisão', enableSubEmitter: 'Ativar subemissores', }, timeline: { continuousLoop: 'Reprodução em loop contínuo', abLoop: 'Loop independente do intervalo A/B', abLoopShort: 'Loop AB', emission: 'Emissão', particleLife: 'Partículas ativas', spineAnimation: 'Animação Spine', ownerAnimation: 'Animação associada', frameStatus: 'Quadro {current} / {total}', }, export: { title: 'Configurações de exportação', spineVersion: 'Versão do Spine', fps: 'FPS de exportação', exportJson: 'Exportar Spine JSON', exportImages: 'Exportar imagens ZIP', connectorReady: 'Conector disponível · Spine pronto', connectorOffline: 'O conector local do Spine não está em execução', spineNotFound: 'O conector está ativo, mas o Spine não foi encontrado', notAuthorized: 'O conector está ativo, mas este site não está autorizado', checking: 'Verificando', offline: 'Offline', notFound: 'Spine não encontrado', unauthorized: 'Não autorizado', connectorBusy: 'Recalculando e gerando o projeto Spine', connectorGenerate: 'Gerar projeto Spine {version} com o Spine local' }, spine: { title: 'Propriedades do Spine', resources: 'Recursos Spine', animations: 'Animações Spine', hierarchy: 'Hierarquia', xPosition: 'Posição X', yPosition: 'Posição Y', rotation: 'Rotação', startTime: 'Tempo inicial', scaleX: 'Escala X', scaleY: 'Escala Y' }, help: { title: 'Ajuda', commonOps: 'Ações comuns', shortcuts: 'Atalhos', modules: 'Guia dos módulos' }, presets: { particleSweep: '1.Varredura de partículas', barBlocks: '2.Blocos retangulares', forceTrail: '3.Rastro de campo de força', lightningSequence: '4.Sequência de raios', texturePivot: '5.Pivô da textura', subEmitter: '6.Subemissor' }, }, es: { common: { close: 'Cerrar', reset: 'Restablecer', add: 'Añadir', remove: 'Quitar', delete: 'Eliminar', clear: 'Limpiar', cancel: 'Cancelar', confirm: 'Confirmar', none: 'Ninguno', enabled: 'Activado', disabled: 'Desactivado', fixed: 'Fijo', random: 'Aleatorio', curve: 'Curva', followExport: 'Seguir exportación', linear: 'Lineal', bezier: 'Bézier', above: 'Arriba', below: 'Abajo', left: 'Izquierda', right: 'Derecha', seconds: 's', frames: 'fotogramas', }, toolbar: { settings: 'Ajustes', undo: 'Deshacer (Ctrl+Z)', pan: 'Mover lienzo (Q)', translate: 'Mover (W)', rotate: 'Girar (E)', scale: 'Escalar (R)', zoomIn: 'Acercar', zoomOut: 'Alejar', fitView: 'Ajustar al lienzo', toggleGrid: 'Mostrar/ocultar cuadrícula', resetView: 'Restablecer vista', background: 'Fondo', backgroundSettings: 'Ajustes del fondo', changeBackground: 'Cambiar imagen', uploadBackground: 'Subir imagen', clearBackground: 'Borrar fondo', noBackground: 'Sin imagen', opacity: 'Opacidad', help: 'Ayuda', openHelp: 'Abrir ayuda', }, config: { title: 'Gestión de configuración', save: 'Guardar configuración', load: 'Cargar configuración', selectPreset: 'Seleccionar preajuste', noPresets: 'Sin preajustes' }, image: { fixed: 'Imagen única', sequence: 'Secuencia', onceHide: 'Una vez, ocultar al final', onceHold: 'Una vez, mantener último fotograma', loopForward: 'Bucle hacia delante', loopReverse: 'Bucle inverso', weight: 'Peso %', pivot: 'Pivote de textura' }, emission: { title: 'Modo de emisión', stream: 'Continuo', burst: 'Ráfaga', normalStream: 'Continuo normal', loopStream: 'Bucle continuo', rate: 'Tasa de emisión', duration: 'Duración de emisión', burstCount: 'Cantidad por ráfaga', delay: 'Retardo', shapeTitle: 'Forma del emisor', point: 'Punto', circle: 'Círculo', rect: 'Rectángulo', cone: 'Cono', path: 'Ruta', }, attributes: { title: 'Atributos de partículas', initialParams: 'Valores iniciales', lifecycleTitle: 'Cambios durante la vida útil' }, appearance: { title: 'Apariencia y recursos', opacity: 'Opacidad', renderLayer: 'Capa de renderizado (slot de Spine)', imageResources: 'Recursos de imagen', independentAlpha: 'Opacidad independiente', off: 'Desactivado', normal: 'Normal', add: 'Suma', multiply: 'Multiplicación', screen: 'Pantalla' }, scene: { title: 'Objetos de escena', particle: 'Partículas', subEmitter: 'Subemisor', collision: 'Colisión', path: 'Ruta', positionX: 'Posición X', positionY: 'Posición Y', offsetX: 'Desplazamiento X', offsetY: 'Desplazamiento Y', rotation: 'Rotación', scaleX: 'Escala X', scaleY: 'Escala Y', xPosition: 'Posición X', yPosition: 'Posición Y', scaleXShort: 'Escala X', scaleYShort: 'Escala Y', seed: 'Semilla aleatoria' }, modifiers: { title: 'Modificadores', damping: 'Amortiguación', enableNoise: 'Activar ruido de movimiento', enableGravity: 'Activar gravedad', enableWind: 'Activar viento', enableForceField: 'Activar campo de fuerza', enableVortex: 'Activar vórtice', enableAttraction: 'Activar atracción', enableTrail: 'Activar estela', enableFollow: 'Activar seguimiento del emisor', enableCollision: 'Activar colisión', enableSubEmitter: 'Activar subemisores', }, timeline: { continuousLoop: 'Reproducción en bucle continuo', abLoop: 'Bucle independiente del rango A/B', abLoopShort: 'Bucle AB', emission: 'Emisión', particleLife: 'Partículas activas', spineAnimation: 'Animación de Spine', ownerAnimation: 'Animación asociada', frameStatus: 'Fotograma {current} / {total}', }, export: { title: 'Ajustes de exportación', spineVersion: 'Versión de Spine', fps: 'FPS de exportación', exportJson: 'Exportar Spine JSON', exportImages: 'Exportar imágenes ZIP', connectorReady: 'Conector disponible · Spine listo', connectorOffline: 'El conector local de Spine no está iniciado', spineNotFound: 'El conector está iniciado, pero no se encontró Spine', notAuthorized: 'El conector está iniciado, pero este sitio no está autorizado', checking: 'Comprobando', offline: 'Sin conexión', notFound: 'Spine no encontrado', unauthorized: 'No autorizado', connectorBusy: 'Recalculando y generando el proyecto de Spine', connectorGenerate: 'Generar proyecto Spine {version} con Spine local' }, spine: { title: 'Propiedades de Spine', resources: 'Recursos de Spine', animations: 'Animaciones de Spine', hierarchy: 'Jerarquía', xPosition: 'Posición X', yPosition: 'Posición Y', rotation: 'Rotación', startTime: 'Tiempo inicial', scaleX: 'Escala X', scaleY: 'Escala Y' }, help: { title: 'Ayuda', commonOps: 'Acciones comunes', shortcuts: 'Atajos', modules: 'Guía de módulos' }, presets: { particleSweep: '1.Barrido de partículas', barBlocks: '2.Bloques rectangulares', forceTrail: '3.Estela de campo de fuerza', lightningSequence: '4.Secuencia de rayos', texturePivot: '5.Pivote de textura', subEmitter: '6.Subemisor' }, }, } function merge(target, source) { for (const [key, value] of Object.entries(source)) { if (value && typeof value === 'object' && !Array.isArray(value)) { target[key] ||= {} merge(target[key], value) } else { target[key] = value } } } for (const [locale, values] of Object.entries(overrides)) { const path = `src/i18n/locales/${locale}.json` const messages = JSON.parse(await readFile(path, 'utf8')) merge(messages, values) await writeFile(path, `${JSON.stringify(messages, null, 2)}\n`) } console.log('Applied terminology overrides for all added locales.')