mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
refactor: update import statements to use node built-in modules
chore: update lerna configuration formatting chore: bump version in package.json to 6.0.0 fix: modify build scripts for improved readability and consistency feat: enhance translation scripts with better error handling and progress tracking chore: improve plugin creation script for better user experience style: format svgo configuration for consistency
This commit is contained in:
1 parent
3540cd0cce
commit
fa755b5f61
11 files changed
+509
-496
No files matched your search
+143
-139
@@ -1,171 +1,175 @@
|
||||
import fs from 'fs';
|
||||
import cpy from 'cpy';
|
||||
import path from 'path';
|
||||
import { glob } from 'glob';
|
||||
import { removeDir } from './utils.js';
|
||||
import dotenv from 'dotenv';
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import cpy from 'cpy'
|
||||
import dotenv from 'dotenv'
|
||||
import { glob } from 'glob'
|
||||
import { removeDir } from './utils.js'
|
||||
|
||||
dotenv.config();
|
||||
dotenv.config()
|
||||
|
||||
// 配置常量
|
||||
const MAX_RETRIES = 3;
|
||||
const RETRY_DELAY = 1000; // 1秒
|
||||
const MAX_CHARS_PER_REQUEST = 128000;
|
||||
const BASE_PATH = 'packages/artplayer-vitepress/docs';
|
||||
const EN_PATH = 'packages/artplayer-vitepress/docs/en';
|
||||
const COPY_DIRS = ['advanced', 'component', 'plugin', 'start'];
|
||||
const MAX_RETRIES = 3
|
||||
const RETRY_DELAY = 1000 // 1秒
|
||||
const MAX_CHARS_PER_REQUEST = 128000
|
||||
const BASE_PATH = 'packages/artplayer-vitepress/docs'
|
||||
const EN_PATH = 'packages/artplayer-vitepress/docs/en'
|
||||
const COPY_DIRS = ['advanced', 'component', 'plugin', 'start']
|
||||
|
||||
function splitMarkdown(text, maxChars) {
|
||||
const parts = [];
|
||||
let currentPart = '';
|
||||
let inCodeBlock = false;
|
||||
let inSpecialBlock = false;
|
||||
const parts = []
|
||||
let currentPart = ''
|
||||
let inCodeBlock = false
|
||||
let inSpecialBlock = false
|
||||
|
||||
const lines = text.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const lines = text.split('\n')
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
|
||||
// 检测代码块
|
||||
if (line.trim().startsWith('```')) {
|
||||
inCodeBlock = !inCodeBlock;
|
||||
}
|
||||
|
||||
// 检测特殊块
|
||||
if (line.trim().startsWith(':::')) {
|
||||
inSpecialBlock = !inSpecialBlock;
|
||||
}
|
||||
|
||||
// 添加当前行到当前部分
|
||||
currentPart += line + '\n';
|
||||
|
||||
// 检测是否达到字符限制
|
||||
if (!inCodeBlock && !inSpecialBlock && currentPart.length > maxChars && i < lines.length - 1) {
|
||||
parts.push(currentPart);
|
||||
currentPart = '';
|
||||
}
|
||||
// 检测代码块
|
||||
if (line.trim().startsWith('```')) {
|
||||
inCodeBlock = !inCodeBlock
|
||||
}
|
||||
|
||||
// 添加最后一部分
|
||||
if (currentPart !== '') {
|
||||
parts.push(currentPart);
|
||||
// 检测特殊块
|
||||
if (line.trim().startsWith(':::')) {
|
||||
inSpecialBlock = !inSpecialBlock
|
||||
}
|
||||
|
||||
return parts;
|
||||
// 添加当前行到当前部分
|
||||
currentPart += `${line}\n`
|
||||
|
||||
// 检测是否达到字符限制
|
||||
if (!inCodeBlock && !inSpecialBlock && currentPart.length > maxChars && i < lines.length - 1) {
|
||||
parts.push(currentPart)
|
||||
currentPart = ''
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一部分
|
||||
if (currentPart !== '') {
|
||||
parts.push(currentPart)
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
async function fetchWithRetry(url, options, retries = MAX_RETRIES) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
if (retries > 0) {
|
||||
console.log(`Retrying... (${MAX_RETRIES - retries + 1}/${MAX_RETRIES})`);
|
||||
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
|
||||
return fetchWithRetry(url, options, retries - 1);
|
||||
}
|
||||
throw error;
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
return await response.json()
|
||||
}
|
||||
catch (error) {
|
||||
if (retries > 0) {
|
||||
console.log(`Retrying... (${MAX_RETRIES - retries + 1}/${MAX_RETRIES})`)
|
||||
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY))
|
||||
return fetchWithRetry(url, options, retries - 1)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const translateContent = async (content, targetLanguage) => {
|
||||
try {
|
||||
const TRANSLATE_URL = 'https://api.deepseek.com/chat/completions';
|
||||
const response = await fetchWithRetry(TRANSLATE_URL, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.DEEPL_API_KEY}`,
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
stream: false,
|
||||
model: 'deepseek-chat',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: `The following text is written in VitePress's extended Markdown syntax, Please translate it into ${targetLanguage}, and keep the Markdown format and don't add your explanation:\n\n${content}`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
async function translateContent(content, targetLanguage) {
|
||||
try {
|
||||
const TRANSLATE_URL = 'https://api.deepseek.com/chat/completions'
|
||||
const response = await fetchWithRetry(TRANSLATE_URL, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.DEEPL_API_KEY}`,
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
stream: false,
|
||||
model: 'deepseek-chat',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: `The following text is written in VitePress's extended Markdown syntax, Please translate it into ${targetLanguage}, and keep the Markdown format and don't add your explanation:\n\n${content}`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
|
||||
return response.choices[0].message.content;
|
||||
} catch (error) {
|
||||
console.error(`Translation failed: ${error.message}`);
|
||||
return content; // 返回原文作为回退
|
||||
}
|
||||
};
|
||||
return response.choices[0].message.content
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Translation failed: ${error.message}`)
|
||||
return content // 返回原文作为回退
|
||||
}
|
||||
}
|
||||
|
||||
class ProgressTracker {
|
||||
constructor(totalParts) {
|
||||
this.totalParts = totalParts;
|
||||
this.translatedPartsCount = 0;
|
||||
}
|
||||
constructor(totalParts) {
|
||||
this.totalParts = totalParts
|
||||
this.translatedPartsCount = 0
|
||||
}
|
||||
|
||||
update() {
|
||||
this.translatedPartsCount++;
|
||||
const progressPercentage = ((this.translatedPartsCount / this.totalParts) * 100).toFixed(2);
|
||||
console.log(`Overall progress: ${progressPercentage}% (${this.translatedPartsCount}/${this.totalParts})`);
|
||||
}
|
||||
update() {
|
||||
this.translatedPartsCount++
|
||||
const progressPercentage = ((this.translatedPartsCount / this.totalParts) * 100).toFixed(2)
|
||||
console.log(`Overall progress: ${progressPercentage}% (${this.translatedPartsCount}/${this.totalParts})`)
|
||||
}
|
||||
}
|
||||
|
||||
const translateMarkdownFiles = async (filePaths, targetLanguage, maxCharsPerRequest) => {
|
||||
// 先计算总共有多少部分需要翻译
|
||||
let totalParts = 0;
|
||||
for (const filePath of filePaths) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
totalParts += splitMarkdown(content, maxCharsPerRequest).length;
|
||||
async function translateMarkdownFiles(filePaths, targetLanguage, maxCharsPerRequest) {
|
||||
// 先计算总共有多少部分需要翻译
|
||||
let totalParts = 0
|
||||
for (const filePath of filePaths) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
totalParts += splitMarkdown(content, maxCharsPerRequest).length
|
||||
}
|
||||
|
||||
console.log(`Starting translation of ${filePaths.length} files, total ${totalParts} parts.`)
|
||||
const progressTracker = new ProgressTracker(totalParts)
|
||||
|
||||
for (const filePath of filePaths) {
|
||||
console.log(`Translating: ${path.relative(EN_PATH, filePath)}`)
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
const parts = splitMarkdown(content, maxCharsPerRequest)
|
||||
const translatedParts = []
|
||||
|
||||
for (const part of parts) {
|
||||
const translatedPart = await translateContent(part, targetLanguage)
|
||||
translatedParts.push(translatedPart)
|
||||
progressTracker.update()
|
||||
}
|
||||
|
||||
console.log(`Starting translation of ${filePaths.length} files, total ${totalParts} parts.`);
|
||||
const progressTracker = new ProgressTracker(totalParts);
|
||||
|
||||
for (const filePath of filePaths) {
|
||||
console.log(`Translating: ${path.relative(EN_PATH, filePath)}`);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const parts = splitMarkdown(content, maxCharsPerRequest);
|
||||
const translatedParts = [];
|
||||
|
||||
for (const part of parts) {
|
||||
const translatedPart = await translateContent(part, targetLanguage);
|
||||
translatedParts.push(translatedPart);
|
||||
progressTracker.update();
|
||||
}
|
||||
|
||||
const translatedContent = translatedParts.join('\n');
|
||||
fs.writeFileSync(filePath, translatedContent);
|
||||
}
|
||||
};
|
||||
const translatedContent = translatedParts.join('\n')
|
||||
fs.writeFileSync(filePath, translatedContent)
|
||||
}
|
||||
}
|
||||
|
||||
async function setupDirectoryStructure() {
|
||||
// 清理并创建目录结构
|
||||
removeDir(EN_PATH);
|
||||
await cpy(path.resolve(BASE_PATH, 'index.md'), path.resolve(EN_PATH));
|
||||
// 清理并创建目录结构
|
||||
removeDir(EN_PATH)
|
||||
await cpy(path.resolve(BASE_PATH, 'index.md'), path.resolve(EN_PATH))
|
||||
|
||||
// 并行复制所有目录
|
||||
await Promise.all(
|
||||
COPY_DIRS.map((dir) => cpy(path.resolve(BASE_PATH, dir), path.resolve(EN_PATH, dir), { flat: true })),
|
||||
);
|
||||
// 并行复制所有目录
|
||||
await Promise.all(
|
||||
COPY_DIRS.map(dir => cpy(path.resolve(BASE_PATH, dir), path.resolve(EN_PATH, dir), { flat: true })),
|
||||
)
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await setupDirectoryStructure();
|
||||
const mdsPaths = glob.sync(path.resolve(EN_PATH, '**/*.md'));
|
||||
await translateMarkdownFiles(mdsPaths, 'English', MAX_CHARS_PER_REQUEST);
|
||||
console.log('Translation completed successfully!');
|
||||
} catch (error) {
|
||||
console.error('Translation failed:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
try {
|
||||
await setupDirectoryStructure()
|
||||
const mdsPaths = glob.sync(path.resolve(EN_PATH, '**/*.md'))
|
||||
await translateMarkdownFiles(mdsPaths, 'English', MAX_CHARS_PER_REQUEST)
|
||||
console.log('Translation completed successfully!')
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Translation failed:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
})()
|
||||
Reference in new issue
Block a user