chore: turn cn comment to en for common space (#376)
This commit is contained in:
parent
f7d73cd391
commit
a1f3a9aead
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
# 设置默认值
|
||||
# Set default value
|
||||
TARGET_BRANCH=${targetBranch}
|
||||
CI_MODE=${CI:-false}
|
||||
|
||||
|
|
@ -36,11 +36,11 @@ else
|
|||
files=$(git diff --name-only --diff-filter=AM --cached $EXCLUDE_STRING)
|
||||
fi
|
||||
|
||||
# 体积限制为512KB
|
||||
# The volume limit is 512KB.
|
||||
size_limit=$((512))
|
||||
large_files_info=""
|
||||
|
||||
IFS=$'\n' # 处理文件名存在空格情况
|
||||
IFS=$'\n' # Handling the existence of spaces in the file name
|
||||
for file in $files; do
|
||||
file_size=$(wc -c <"$file" 2>/dev/null)
|
||||
if [ $? -ne 0 ]; then
|
||||
|
|
|
|||
|
|
@ -37,10 +37,10 @@ jobs:
|
|||
- name: Process changed files
|
||||
id: process-files
|
||||
run: |
|
||||
# 获取所有变更文件
|
||||
# Get all change files
|
||||
all_files="${{ steps.changed-files.outputs.all_changed_files }}"
|
||||
|
||||
# 过滤掉 common/changes 目录下的文件
|
||||
# Filter out files in the common/changes directory
|
||||
filtered_files=""
|
||||
for file in $all_files; do
|
||||
if [[ ! "$file" =~ ^common/changes/.* ]]; then
|
||||
|
|
@ -52,10 +52,10 @@ jobs:
|
|||
fi
|
||||
done
|
||||
|
||||
# 创建 JSON 格式的缓存文件
|
||||
# Create cached files in JSON format
|
||||
echo "[$( echo "$filtered_files" | sed 's/ /", "/g' | sed 's/^/"/' | sed 's/$/"/' )]" > changed-files-cache.json
|
||||
|
||||
# 输出缓存文件路径供后续步骤使用
|
||||
# Output cache file path for subsequent steps
|
||||
echo "cache_file=changed-files-cache.json" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "过滤前文件数量: $(echo $all_files | wc -w)"
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default class SelectTeamPlugin implements IPlugin {
|
|||
apply(hooks: IHooks): void {
|
||||
hooks.prompts.tap("SelectTeamPlugin", (prompts: IPromptsHookParams) => {
|
||||
|
||||
// 只留下以team-为前缀的
|
||||
// Leave only the prefix team-
|
||||
const teamNamePrefix = /^team-/;
|
||||
const choices = rushJson.allowedProjectTags.filter(
|
||||
teamName => teamNamePrefix.test(teamName)
|
||||
|
|
@ -41,20 +41,20 @@ export default class SelectTeamPlugin implements IPlugin {
|
|||
teamName => teamName.replace(teamNamePrefix, '')
|
||||
);
|
||||
|
||||
// unshift一个问题,使得用户选择完模版后展示该问题。
|
||||
// Unshift an issue, causing the user to display the issue after selecting a template.
|
||||
prompts.promptQueue.unshift({
|
||||
type: "list",
|
||||
name: "team",
|
||||
message: "Select your team",
|
||||
choices,
|
||||
default: 0, // 默认选择choices[0]
|
||||
default: 0, // Default choices [0]
|
||||
});
|
||||
|
||||
const projectFolderPrompt = prompts.promptQueue.find(
|
||||
item => item.name === 'projectFolder'
|
||||
);
|
||||
projectFolderPrompt.default = (answers) => {
|
||||
// 文件夹名去除scope,如 @coze-arch/foo -> foo
|
||||
// Remove the scope from the folder name, such as @code-arch/foo - > foo
|
||||
const folderDir = answers.packageName.split('/').slice(-1)[0];
|
||||
return `frontend/packages/${answers.team}/${folderDir}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ import type {
|
|||
IPromptsHookParams,
|
||||
} from 'rush-init-project-plugin';
|
||||
// FIXME:
|
||||
// 按照 https://github.com/bytemate/rush-plugins/blob/main/rush-plugins/rush-init-project-plugin/docs/init_project_configuration.md
|
||||
// 一文的指引,无法正确 resolve 到对应模块,暂时没找到解决方案,故此处先用相对路径引用
|
||||
// 未来需要调整为正常的 node_modules 引用方式
|
||||
// According to https://github.com/bytemate/rush-plugins/blob/main/rush-plugins/rush-init-project-plugin/docs/init_project_configuration.md
|
||||
// The guidelines of this article cannot be correctly resolved to the corresponding module, and a solution has not been found for the time being, so the relative path reference is used here first
|
||||
// Future needs to be adjusted to normal node_modules citation
|
||||
import { createLog } from '../../autoinstallers/plugins/node_modules/rush-init-project-plugin';
|
||||
import { exec } from './utils';
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import type {
|
|||
IPromptsHookParams,
|
||||
} from 'rush-init-project-plugin';
|
||||
// FIXME:
|
||||
// 按照 https://github.com/bytemate/rush-plugins/blob/main/rush-plugins/rush-init-project-plugin/docs/init_project_configuration.md
|
||||
// 一文的指引,无法正确 resolve 到对应模块,暂时没找到解决方案,故此处先用相对路径引用
|
||||
// 未来需要调整为正常的 node_modules 引用方式
|
||||
// According to https://github.com/bytemate/rush-plugins/blob/main/rush-plugins/rush-init-project-plugin/docs/init_project_configuration.md
|
||||
// The guidelines of this article cannot be correctly resolved to the corresponding module, and a solution has not been found for the time being, so the relative path reference is used here first
|
||||
// Future needs to be adjusted to normal node_modules citation
|
||||
import {
|
||||
getTemplatesFolder,
|
||||
getTemplateNameList,
|
||||
|
|
|
|||
|
|
@ -21,18 +21,18 @@ export function parseCommandLineArguments() {
|
|||
const args = process.argv.slice(2);
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
// 循环遍历所有参数
|
||||
// Loop through all parameters
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
// 检查当前参数是否是一个选项(以 "--" 开头)
|
||||
// Check if the current argument is an option (starting with "--").
|
||||
if (args[i].startsWith('--')) {
|
||||
const key = args[i].substring(2); // 移除 "--" 前缀
|
||||
const key = args[i].substring(2); // Remove the "--" prefix
|
||||
|
||||
// 检查下一个参数是否存在,且不是另一个选项
|
||||
// Check if the next argument exists and is not another option
|
||||
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
||||
result[key] = args[i + 1]; // 将下一个参数作为当前选项的值
|
||||
i++; // 跳过下一个参数,因为它已经被处理为当前选项的值
|
||||
result[key] = args[i + 1]; // Set the next argument as the value of the current option
|
||||
i++; // Skip the next argument because it has already been processed to the value of the current option
|
||||
} else {
|
||||
result[key] = ''; // 如果没有值,只设置选项的键
|
||||
result[key] = ''; // If there is no value, only set the key of the option
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ export function DemoComponent(props: { name: string }): JSX.Element {
|
|||
const [foo] = useState('hello world');
|
||||
const { name } = props;
|
||||
return (
|
||||
// font-bold 来自 taiwindcss
|
||||
// 建议优先使用 taiwindcss
|
||||
// Font-bold from taiwindcss
|
||||
// It is recommended to use taiwindcss first.
|
||||
<div className={classNames(s.foo, 'font-bold')}>
|
||||
{foo} {name}!
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
const { NODE_ENV } = process.env;
|
||||
|
||||
const IS_DEV_MODE = NODE_ENV === 'development'; // 本地环境
|
||||
const IS_PRODUCT_MODE = NODE_ENV === 'production'; // 生产环境
|
||||
const IS_DEV_MODE = NODE_ENV === 'development'; // local environment
|
||||
const IS_PRODUCT_MODE = NODE_ENV === 'production'; // production environment
|
||||
|
||||
const IS_CI = process.env.CI === 'true';
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export const updateDTS = (
|
|||
) => {
|
||||
const start = Date.now();
|
||||
|
||||
// 初始化一个 ts-morph 项目
|
||||
// Initialize a ts-morph project
|
||||
const project = new Project({
|
||||
compilerOptions: {
|
||||
incremental: true,
|
||||
|
|
@ -51,19 +51,19 @@ export const updateDTS = (
|
|||
noEmitOnError: true,
|
||||
},
|
||||
});
|
||||
// 添加想要解析的文件
|
||||
// Add the file you want to parse
|
||||
const file = project.addSourceFileAtPath(inputFileName);
|
||||
|
||||
// 获取你想要解析的变量
|
||||
// Get the variable you want to parse
|
||||
const envs = file.getVariableDeclarationOrThrow(envVarName);
|
||||
// 获取 envs 变量的初始值
|
||||
// Get the initial value of the envs variable
|
||||
const initializer = envs.getInitializerIfKindOrThrow(
|
||||
SyntaxKind.ObjectLiteralExpression,
|
||||
);
|
||||
// 获取 envs 对象的属性
|
||||
// Get the properties of the envs object
|
||||
const properties = initializer.getProperties();
|
||||
|
||||
// 创建一个新的文件,用来保存生成的类型定义
|
||||
// Create a new file to hold the generated type definition
|
||||
const typeDefs = project.createSourceFile(
|
||||
outputFileName,
|
||||
`// 基于${inputFileName}自动生成,请勿手动修改`,
|
||||
|
|
@ -81,7 +81,7 @@ export const updateDTS = (
|
|||
});
|
||||
};
|
||||
|
||||
// 遍历每一个属性
|
||||
// Iterate through each attribute
|
||||
properties.forEach(property => {
|
||||
if (
|
||||
property instanceof PropertyAssignment ||
|
||||
|
|
@ -93,9 +93,9 @@ export const updateDTS = (
|
|||
const type = expression.getType();
|
||||
|
||||
if (type.isObject()) {
|
||||
// 如果类型是一个对象类型,获取其属性
|
||||
// If the type is an object type, obtain its properties
|
||||
const spreadProperties = type.getProperties();
|
||||
// 遍历属性
|
||||
// traversal properties
|
||||
for (const spreadProperty of spreadProperties) {
|
||||
const declaration = spreadProperty.getDeclarations()?.[0];
|
||||
if (declaration) {
|
||||
|
|
@ -113,7 +113,7 @@ export const updateDTS = (
|
|||
}
|
||||
}
|
||||
});
|
||||
// 保存文件
|
||||
// Save file
|
||||
typeDefs.addVariableStatements(
|
||||
declarations
|
||||
.sort((a, b) => (a.name > b.name ? 1 : -1))
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
import { RouterProvider } from 'react-router-dom';
|
||||
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import browserClient from '@slardar/web'; // 默认引入的是CN地区的
|
||||
import browserClient from '@slardar/web'; // The default introduction is the CN region
|
||||
import { reporter } from '@coze-arch/logger';
|
||||
|
||||
import { router } from '@/router';
|
||||
|
|
|
|||
|
|
@ -29,6 +29,6 @@ export const createUserInfoSlice: StateCreator<
|
|||
> = set => ({
|
||||
userInfo: '',
|
||||
iniUserInfo: () => {
|
||||
// TODO: 用户信息相关方法获取
|
||||
// TODO: User information related methods
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// 基于env/index.ts自动生成,请勿手动修改
|
||||
// Automatically generated based on env/index.ts, do not modify manually
|
||||
declare const IS_CI: boolean;
|
||||
declare const IS_DEV_MODE: boolean;
|
||||
declare const IS_PRODUCT_MODE: boolean;
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ export function DemoComponent(props: { name: string }): JSX.Element {
|
|||
const [foo] = useState('hello world');
|
||||
const { name } = props;
|
||||
return (
|
||||
// font-bold 来自 taiwindcss
|
||||
// 建议优先使用 taiwindcss
|
||||
// Font-bold from taiwindcss
|
||||
// It is recommended to use taiwindcss first.
|
||||
<div className={classNames(s.foo, 'font-bold')}>
|
||||
{foo} {name}!
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|||
import { CliOptions } from '../types/config';
|
||||
|
||||
/**
|
||||
* 创建命令行程序
|
||||
* Create a command line program
|
||||
*/
|
||||
export const createProgram = (): Command => {
|
||||
const program = new Command();
|
||||
|
|
@ -32,7 +32,7 @@ export const createProgram = (): Command => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 解析命令行选项
|
||||
* Parse command line options
|
||||
*/
|
||||
export const parseOptions = (program: Command): CliOptions => {
|
||||
const options = program.opts();
|
||||
|
|
@ -53,7 +53,7 @@ export const parseOptions = (program: Command): CliOptions => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 显示帮助信息
|
||||
* Display help information
|
||||
*/
|
||||
export const showHelp = (): void => {
|
||||
console.log(`
|
||||
|
|
@ -101,7 +101,7 @@ export const showHelp = (): void => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 显示版本信息
|
||||
* Show version information
|
||||
*/
|
||||
export const showVersion = (): void => {
|
||||
console.log('ai-translate version 1.0.0');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { AppConfig, CliOptions, TranslationConfig, ProcessingConfig } from '../t
|
|||
import { deepMerge } from '../utils/fp';
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
* default configuration
|
||||
*/
|
||||
const DEFAULT_CONFIG: AppConfig = {
|
||||
translation: {
|
||||
|
|
@ -32,7 +32,7 @@ const DEFAULT_CONFIG: AppConfig = {
|
|||
};
|
||||
|
||||
/**
|
||||
* 从文件加载配置
|
||||
* Load configuration from file
|
||||
*/
|
||||
export const loadConfigFromFile = async (configPath: string): Promise<Partial<AppConfig>> => {
|
||||
try {
|
||||
|
|
@ -46,12 +46,12 @@ export const loadConfigFromFile = async (configPath: string): Promise<Partial<Ap
|
|||
};
|
||||
|
||||
/**
|
||||
* 从命令行选项创建配置
|
||||
* Create configuration from command line options
|
||||
*/
|
||||
export const createConfigFromOptions = (options: CliOptions): Partial<AppConfig> => {
|
||||
const config: Partial<AppConfig> = {};
|
||||
|
||||
// 翻译配置
|
||||
// translation configuration
|
||||
if (options.accessKeyId || options.secretAccessKey || options.region || options.sourceLanguage || options.targetLanguage) {
|
||||
config.translation = {} as Partial<TranslationConfig>;
|
||||
if (options.accessKeyId) {
|
||||
|
|
@ -71,10 +71,10 @@ export const createConfigFromOptions = (options: CliOptions): Partial<AppConfig>
|
|||
}
|
||||
}
|
||||
|
||||
// 处理配置
|
||||
// handle configuration
|
||||
if (options.output) {
|
||||
config.processing = {} as Partial<ProcessingConfig>;
|
||||
// 根据输出文件扩展名推断格式
|
||||
// Infer format based on output file extension
|
||||
const ext = options.output.toLowerCase().split('.').pop();
|
||||
if (ext === 'json') {
|
||||
config.processing!.outputFormat = 'json';
|
||||
|
|
@ -87,7 +87,7 @@ export const createConfigFromOptions = (options: CliOptions): Partial<AppConfig>
|
|||
};
|
||||
|
||||
/**
|
||||
* 合并配置
|
||||
* merge configuration
|
||||
*/
|
||||
export const mergeConfigs = (...configs: Partial<AppConfig>[]): AppConfig => {
|
||||
return configs.reduce(
|
||||
|
|
@ -97,18 +97,18 @@ export const mergeConfigs = (...configs: Partial<AppConfig>[]): AppConfig => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 加载完整配置
|
||||
* Load full configuration
|
||||
*/
|
||||
export const loadConfig = async (options: CliOptions): Promise<AppConfig> => {
|
||||
const configs: Partial<AppConfig>[] = [DEFAULT_CONFIG];
|
||||
|
||||
// 加载配置文件
|
||||
// Load configuration file
|
||||
if (options.config) {
|
||||
const fileConfig = await loadConfigFromFile(options.config);
|
||||
configs.push(fileConfig);
|
||||
}
|
||||
|
||||
// 加载命令行选项配置
|
||||
// Load command line options configuration
|
||||
const optionsConfig = createConfigFromOptions(options);
|
||||
configs.push(optionsConfig);
|
||||
|
||||
|
|
@ -116,28 +116,28 @@ export const loadConfig = async (options: CliOptions): Promise<AppConfig> => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 验证配置
|
||||
* verify configuration
|
||||
*/
|
||||
export const validateConfig = (config: AppConfig): { valid: boolean; errors: string[] } => {
|
||||
const errors: string[] = [];
|
||||
|
||||
// 验证火山引擎 Access Key ID
|
||||
// Verify Volcano Engine Access Key ID
|
||||
if (!config.translation.accessKeyId) {
|
||||
errors.push('火山引擎 Access Key ID 未设置,请通过环境变量VOLC_ACCESS_KEY_ID或--access-key-id参数提供');
|
||||
}
|
||||
|
||||
// 验证火山引擎 Secret Access Key
|
||||
// Verify Volcano Engine Secret Access Key
|
||||
if (!config.translation.secretAccessKey) {
|
||||
errors.push('火山引擎 Secret Access Key 未设置,请通过环境变量VOLC_SECRET_ACCESS_KEY或--secret-access-key参数提供');
|
||||
}
|
||||
|
||||
// 验证区域
|
||||
// validation area
|
||||
const validRegions = ['cn-beijing', 'ap-southeast-1', 'us-east-1'];
|
||||
if (!validRegions.includes(config.translation.region)) {
|
||||
console.warn(`未知的区域: ${config.translation.region},建议使用: ${validRegions.join(', ')}`);
|
||||
}
|
||||
|
||||
// 验证语言代码
|
||||
// Verify language code
|
||||
const validLanguages = ['zh', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'pt', 'ru'];
|
||||
if (!validLanguages.includes(config.translation.sourceLanguage)) {
|
||||
console.warn(`未知的源语言: ${config.translation.sourceLanguage},建议使用: ${validLanguages.join(', ')}`);
|
||||
|
|
@ -146,12 +146,12 @@ export const validateConfig = (config: AppConfig): { valid: boolean; errors: str
|
|||
console.warn(`未知的目标语言: ${config.translation.targetLanguage},建议使用: ${validLanguages.join(', ')}`);
|
||||
}
|
||||
|
||||
// 验证并发数
|
||||
// validation concurrency
|
||||
if (config.translation.concurrency < 1 || config.translation.concurrency > 10) {
|
||||
errors.push('并发数应该在1-10之间');
|
||||
}
|
||||
|
||||
// 验证超时时间
|
||||
// verification timeout
|
||||
if (config.translation.timeout < 1000 || config.translation.timeout > 300000) {
|
||||
errors.push('超时时间应该在1000-300000毫秒之间');
|
||||
}
|
||||
|
|
@ -160,7 +160,7 @@ export const validateConfig = (config: AppConfig): { valid: boolean; errors: str
|
|||
};
|
||||
|
||||
/**
|
||||
* 打印配置信息
|
||||
* Print configuration information
|
||||
*/
|
||||
export const printConfigInfo = (config: AppConfig, verbose: boolean = false): void => {
|
||||
console.log('🔧 当前配置:');
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {
|
|||
import { FileScanConfig } from './types/index';
|
||||
|
||||
/**
|
||||
* 主处理函数
|
||||
* main processing function
|
||||
*/
|
||||
async function processRepository(
|
||||
rootPath: string,
|
||||
|
|
@ -36,7 +36,7 @@ async function processRepository(
|
|||
printConfigInfo(config, true);
|
||||
}
|
||||
|
||||
// 1. 扫描源文件
|
||||
// 1. Scan source files
|
||||
console.log('\n📁 扫描源文件...');
|
||||
const scanConfig: FileScanConfig = {
|
||||
root: rootPath,
|
||||
|
|
@ -58,7 +58,7 @@ async function processRepository(
|
|||
return;
|
||||
}
|
||||
|
||||
// 2. 检测中文注释
|
||||
// 2. Detect Chinese annotations
|
||||
console.log('\n🔍 检测中文注释...');
|
||||
const filesWithComments = detectChineseInFiles(sourceFiles);
|
||||
|
||||
|
|
@ -76,11 +76,11 @@ async function processRepository(
|
|||
return;
|
||||
}
|
||||
|
||||
// 3. 初始化翻译服务
|
||||
// 3. Initialize the translation service
|
||||
console.log('\n🤖 初始化翻译服务...');
|
||||
const translationService = new TranslationService(config.translation);
|
||||
|
||||
// 4. 处理文件
|
||||
// 4. Processing documents
|
||||
console.log('\n🔄 开始翻译处理...');
|
||||
const progressDisplay = new ProgressDisplay(filesWithComments.length);
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ async function processRepository(
|
|||
reportCollector.recordFileStart(file.path);
|
||||
|
||||
try {
|
||||
// 翻译注释
|
||||
// Translation annotations
|
||||
const translations = await translationService.batchTranslate(
|
||||
chineseComments,
|
||||
config.translation.concurrency,
|
||||
|
|
@ -107,7 +107,7 @@ async function processRepository(
|
|||
});
|
||||
}
|
||||
|
||||
// 如果不是干运行模式,则替换文件内容
|
||||
// If not in dry running mode, replace the file content
|
||||
if (!dryRun) {
|
||||
const replacements = createReplacements(
|
||||
file,
|
||||
|
|
@ -140,7 +140,7 @@ async function processRepository(
|
|||
|
||||
progressDisplay.complete();
|
||||
|
||||
// 5. 生成报告
|
||||
// 5. Generate reports
|
||||
console.log('\n📊 生成处理报告...');
|
||||
const report = reportCollector.generateReport();
|
||||
|
||||
|
|
@ -148,11 +148,11 @@ async function processRepository(
|
|||
console.log('\n🔍 预览模式 - 未实际修改文件');
|
||||
}
|
||||
|
||||
// 显示报告
|
||||
// Show report
|
||||
const reportText = generateReport(report, 'console');
|
||||
console.log(reportText);
|
||||
|
||||
// 保存报告到文件(如果指定了输出路径)
|
||||
// Save the report to a file (if output path is specified)
|
||||
if (config.outputFile) {
|
||||
await saveReportToFile(
|
||||
report,
|
||||
|
|
@ -168,20 +168,20 @@ async function processRepository(
|
|||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
* main function
|
||||
*/
|
||||
async function main(): Promise<void> {
|
||||
try {
|
||||
const program = createProgram();
|
||||
|
||||
// 解析命令行参数
|
||||
// Parsing command line arguments
|
||||
program.parse();
|
||||
const options = parseOptions(program);
|
||||
|
||||
// 加载配置
|
||||
// load configuration
|
||||
const config = await loadConfig(options);
|
||||
|
||||
// 验证配置
|
||||
// verify configuration
|
||||
const validation = validateConfig(config);
|
||||
if (!validation.valid) {
|
||||
console.error('❌ 配置验证失败:');
|
||||
|
|
@ -190,18 +190,18 @@ async function main(): Promise<void> {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
// 解析文件扩展名
|
||||
// Parse file extension
|
||||
const extensions = options.exts
|
||||
? options.exts.split(',').map(ext => ext.trim())
|
||||
: config.processing.defaultExtensions;
|
||||
|
||||
// 添加输出文件配置
|
||||
// Add output file configuration
|
||||
const fullConfig = {
|
||||
...config,
|
||||
outputFile: options.output,
|
||||
};
|
||||
|
||||
// 执行处理
|
||||
// execution processing
|
||||
await processRepository(
|
||||
options.root,
|
||||
extensions,
|
||||
|
|
@ -215,7 +215,7 @@ async function main(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
// 处理未捕获的异常
|
||||
// Handling uncaught exceptions
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
console.error('未处理的Promise拒绝:', reason);
|
||||
process.exit(1);
|
||||
|
|
@ -226,7 +226,7 @@ process.on('uncaughtException', error => {
|
|||
process.exit(1);
|
||||
});
|
||||
|
||||
// 运行主函数
|
||||
// Run the main function
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
main();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { getCommentPatterns } from '../utils/language';
|
|||
import { containsChinese, cleanCommentText } from '../utils/chinese';
|
||||
|
||||
/**
|
||||
* 检查指定位置是否在字符串字面量内部
|
||||
* Checks if the specified location is inside a string literal
|
||||
*/
|
||||
const isInsideStringLiteral = (line: string, position: number): boolean => {
|
||||
let insideDoubleQuote = false;
|
||||
|
|
@ -44,7 +44,7 @@ const isInsideStringLiteral = (line: string, position: number): boolean => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 解析单行注释
|
||||
* Parsing single-line comments
|
||||
*/
|
||||
const parseSingleLineComments = (
|
||||
content: string,
|
||||
|
|
@ -54,34 +54,34 @@ const parseSingleLineComments = (
|
|||
const comments: ParsedComment[] = [];
|
||||
const lines = content.split('\n');
|
||||
|
||||
// 添加安全检查
|
||||
const maxLines = 5000; // 降低到5000行
|
||||
// Add a security check
|
||||
const maxLines = 5000; // Down to 5000 lines
|
||||
if (lines.length > maxLines) {
|
||||
console.warn(`⚠️ 文件行数过多 (${lines.length}行),跳过单行注释解析`);
|
||||
return comments;
|
||||
}
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
pattern.lastIndex = 0; // 重置正则表达式索引
|
||||
pattern.lastIndex = 0; // Reset regular expression index
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
// 查找所有匹配,但只保留不在字符串内的
|
||||
// Find all matches, but keep only those not in the string
|
||||
let matchCount = 0;
|
||||
const maxMatches = 100; // 限制每行最多匹配100次
|
||||
const maxMatches = 100; // Limit each line to a maximum of 100 matches
|
||||
let lastIndex = 0;
|
||||
|
||||
while ((match = pattern.exec(line)) !== null) {
|
||||
// 防止无限循环的多重保护
|
||||
// Multiple protections against infinite loops
|
||||
matchCount++;
|
||||
if (matchCount > maxMatches) {
|
||||
console.warn(`⚠️ 单行匹配次数过多,中断处理: ${line.substring(0, 50)}...`);
|
||||
break;
|
||||
}
|
||||
|
||||
// 检查 lastIndex 是否前进,防止无限循环
|
||||
// Check if lastIndex is advancing to prevent an infinite loop
|
||||
if (pattern.global) {
|
||||
if (pattern.lastIndex <= lastIndex) {
|
||||
// 如果 lastIndex 没有前进,手动前进一位避免无限循环
|
||||
// If lastIndex does not advance, manually advance one bit to avoid infinite loops
|
||||
pattern.lastIndex = lastIndex + 1;
|
||||
if (pattern.lastIndex >= line.length) {
|
||||
break;
|
||||
|
|
@ -93,9 +93,9 @@ const parseSingleLineComments = (
|
|||
if (match[1]) {
|
||||
const commentContent = match[1];
|
||||
let commentStartIndex = match.index!;
|
||||
let commentLength = 2; // 默认为 //
|
||||
let commentLength = 2; // Default is//
|
||||
|
||||
// 根据语言确定注释符号
|
||||
// Determine annotation symbols based on language
|
||||
if (
|
||||
language === 'yaml' ||
|
||||
language === 'toml' ||
|
||||
|
|
@ -104,9 +104,9 @@ const parseSingleLineComments = (
|
|||
language === 'ruby'
|
||||
) {
|
||||
commentStartIndex = line.indexOf('#', match.index!);
|
||||
commentLength = 1; // # 长度为 1
|
||||
commentLength = 1; // #length is 1
|
||||
} else if (language === 'ini') {
|
||||
// INI 文件可能使用 # 或 ;
|
||||
// INI files may use #or;
|
||||
const hashIndex = line.indexOf('#', match.index!);
|
||||
const semicolonIndex = line.indexOf(';', match.index!);
|
||||
if (
|
||||
|
|
@ -120,7 +120,7 @@ const parseSingleLineComments = (
|
|||
commentLength = 1;
|
||||
}
|
||||
} else if (language === 'php') {
|
||||
// PHP 可能使用 // 或 #
|
||||
// PHP may use//or #
|
||||
const slashIndex = line.indexOf('//', match.index!);
|
||||
const hashIndex = line.indexOf('#', match.index!);
|
||||
if (slashIndex >= 0 && (hashIndex < 0 || slashIndex < hashIndex)) {
|
||||
|
|
@ -139,7 +139,7 @@ const parseSingleLineComments = (
|
|||
const startColumn = commentStartIndex;
|
||||
const endColumn = startColumn + commentLength + commentContent.length;
|
||||
|
||||
// 检查注释开始位置是否在字符串内部
|
||||
// Check if the comment starts inside the string
|
||||
if (
|
||||
commentStartIndex >= 0 &&
|
||||
!isInsideStringLiteral(line, commentStartIndex)
|
||||
|
|
@ -155,7 +155,7 @@ const parseSingleLineComments = (
|
|||
}
|
||||
}
|
||||
|
||||
// 防止无限循环
|
||||
// Prevent infinite loops
|
||||
if (!pattern.global) break;
|
||||
}
|
||||
});
|
||||
|
|
@ -164,7 +164,7 @@ const parseSingleLineComments = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 解析多行注释
|
||||
* Parse multiline comments
|
||||
*/
|
||||
const parseMultiLineComments = (
|
||||
content: string,
|
||||
|
|
@ -177,21 +177,21 @@ const parseMultiLineComments = (
|
|||
let commentStart: { line: number; column: number } | null = null;
|
||||
let commentLines: string[] = [];
|
||||
|
||||
// 添加安全检查
|
||||
const maxLines = 5000; // 降低到5000行
|
||||
// Add a security check
|
||||
const maxLines = 5000; // Down to 5000 lines
|
||||
if (lines.length > maxLines) {
|
||||
console.warn(`⚠️ 文件行数过多 (${lines.length}行),跳过多行注释解析`);
|
||||
return comments;
|
||||
}
|
||||
|
||||
// 添加处理计数器,防止无限循环
|
||||
// Add processing counters to prevent infinite loops
|
||||
let processedLines = 0;
|
||||
const maxProcessedLines = 10000;
|
||||
|
||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
||||
const line = lines[lineIndex];
|
||||
|
||||
// 防止无限处理
|
||||
// Prevent unlimited processing
|
||||
processedLines++;
|
||||
if (processedLines > maxProcessedLines) {
|
||||
console.warn(`⚠️ 处理行数超限,中断解析`);
|
||||
|
|
@ -206,12 +206,12 @@ const parseMultiLineComments = (
|
|||
inComment = true;
|
||||
commentStart = { line: lineIndex + 1, column: startMatch.index! };
|
||||
|
||||
// 检查是否在同一行结束
|
||||
// Check if they end on the same line
|
||||
endPattern.lastIndex = startMatch.index! + startMatch[0].length;
|
||||
const endMatch = endPattern.exec(line);
|
||||
|
||||
if (endMatch) {
|
||||
// 单行多行注释
|
||||
// single-line multi-line comment
|
||||
const commentContent = line.substring(
|
||||
startMatch.index! + startMatch[0].length,
|
||||
endMatch.index!,
|
||||
|
|
@ -229,7 +229,7 @@ const parseMultiLineComments = (
|
|||
inComment = false;
|
||||
commentStart = null;
|
||||
} else {
|
||||
// 多行注释开始
|
||||
// Start with a multi-line comment
|
||||
const commentContent = line.substring(
|
||||
startMatch.index! + startMatch[0].length,
|
||||
);
|
||||
|
|
@ -237,12 +237,12 @@ const parseMultiLineComments = (
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// 在多行注释中
|
||||
// In a multi-line comment
|
||||
endPattern.lastIndex = 0;
|
||||
const endMatch = endPattern.exec(line);
|
||||
|
||||
if (endMatch) {
|
||||
// 多行注释结束
|
||||
// End of multiline comment
|
||||
const commentContent = line.substring(0, endMatch.index!);
|
||||
commentLines.push(commentContent);
|
||||
|
||||
|
|
@ -260,7 +260,7 @@ const parseMultiLineComments = (
|
|||
commentStart = null;
|
||||
commentLines = [];
|
||||
} else {
|
||||
// 继续多行注释
|
||||
// Continue with multi-line comments
|
||||
commentLines.push(line);
|
||||
}
|
||||
}
|
||||
|
|
@ -270,7 +270,7 @@ const parseMultiLineComments = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 解析文件中的所有注释
|
||||
* Parse all comments in the file
|
||||
*/
|
||||
export const parseComments = (file: SourceFile): ParsedComment[] => {
|
||||
const patterns = getCommentPatterns(file.language);
|
||||
|
|
@ -291,7 +291,7 @@ export const parseComments = (file: SourceFile): ParsedComment[] => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 过滤包含中文的注释,对多行注释进行逐行处理
|
||||
* Filter comments containing Chinese and process multi-line comments line by line
|
||||
*/
|
||||
export const filterChineseComments = (
|
||||
comments: ParsedComment[],
|
||||
|
|
@ -301,11 +301,11 @@ export const filterChineseComments = (
|
|||
|
||||
for (const comment of comments) {
|
||||
if (comment.type === 'multi-line' && comment.content.includes('\n')) {
|
||||
// 多行注释:逐行处理
|
||||
// Multi-line comments: line-by-line processing
|
||||
const multiLineResults = processMultiLineCommentForChinese(comment, language);
|
||||
result.push(...multiLineResults);
|
||||
} else if (containsChinese(comment.content)) {
|
||||
// 单行注释或单行多行注释
|
||||
// Single-line comments or single-line multi-line comments
|
||||
result.push({
|
||||
...comment,
|
||||
content: cleanCommentText(
|
||||
|
|
@ -321,7 +321,7 @@ export const filterChineseComments = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 处理多行注释,提取含中文的行作为独立的注释单元
|
||||
* Processing multi-line comments, extracting lines containing Chinese as independent comment units
|
||||
*/
|
||||
const processMultiLineCommentForChinese = (
|
||||
comment: ParsedComment,
|
||||
|
|
@ -334,18 +334,18 @@ const processMultiLineCommentForChinese = (
|
|||
const cleanedLine = cleanCommentText(line, 'multi-line', language);
|
||||
|
||||
if (containsChinese(cleanedLine)) {
|
||||
// 计算这一行在原始文件中的位置
|
||||
// Calculate the position of this line in the original file
|
||||
const actualLineNumber = comment.startLine + lineIndex;
|
||||
|
||||
// 创建一个表示这一行的注释对象
|
||||
// Create a comment object representing this line
|
||||
const lineComment: ChineseComment = {
|
||||
content: cleanedLine,
|
||||
startLine: actualLineNumber,
|
||||
endLine: actualLineNumber,
|
||||
startColumn: 0, // 这个值需要更精确计算,但对于多行注释内的行处理暂时用0
|
||||
startColumn: 0, // This value needs to be calculated more precisely, but for line processing within a multi-line comment, use 0 for the time being.
|
||||
endColumn: line.length,
|
||||
type: 'multi-line',
|
||||
// 添加多行注释的元数据,用于后续处理
|
||||
// Add metadata with multi-line comments for subsequent processing
|
||||
multiLineContext: {
|
||||
isPartOfMultiLine: true,
|
||||
originalComment: comment,
|
||||
|
|
@ -362,11 +362,11 @@ const processMultiLineCommentForChinese = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 检测文件中的中文注释
|
||||
* Detect Chinese comments in files
|
||||
*/
|
||||
export const detectChineseInFile = (file: SourceFile): ChineseComment[] => {
|
||||
try {
|
||||
// 简单防护:跳过大文件
|
||||
// Simple protection: skipping large files
|
||||
if (file.content.length > 500000) {
|
||||
// 500KB
|
||||
console.warn(
|
||||
|
|
@ -375,7 +375,7 @@ export const detectChineseInFile = (file: SourceFile): ChineseComment[] => {
|
|||
return [];
|
||||
}
|
||||
|
||||
// 简单防护:跳过行数过多的文件
|
||||
// Simple protection: skip files with too many lines
|
||||
const lines = file.content.split('\n');
|
||||
if (lines.length > 10000) {
|
||||
console.warn(`⚠️ 跳过多行文件: ${file.path} (${lines.length} 行)`);
|
||||
|
|
@ -391,7 +391,7 @@ export const detectChineseInFile = (file: SourceFile): ChineseComment[] => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 批量检测多个文件中的中文注释
|
||||
* Batch detection of Chinese comments in multiple files
|
||||
*/
|
||||
export const detectChineseInFiles = (files: SourceFile[]): FileWithComments[] => {
|
||||
const results: FileWithComments[] = [];
|
||||
|
|
@ -417,7 +417,7 @@ export const detectChineseInFiles = (files: SourceFile[]): FileWithComments[] =>
|
|||
);
|
||||
} catch (error) {
|
||||
console.error(`❌ 处理文件失败: ${fileName} - ${error}`);
|
||||
// 继续处理其他文件
|
||||
// Continue working on other documents
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -426,7 +426,7 @@ export const detectChineseInFiles = (files: SourceFile[]): FileWithComments[] =>
|
|||
};
|
||||
|
||||
/**
|
||||
* 获取注释统计信息
|
||||
* Get annotation statistics
|
||||
*/
|
||||
export const getCommentStats = (files: SourceFile[]): {
|
||||
totalFiles: number;
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ import { tryCatch } from '../utils/fp';
|
|||
|
||||
|
||||
/**
|
||||
* 检查字符串是否包含中文字符
|
||||
* Check if string contains Chinese characters
|
||||
*/
|
||||
const containsChinese = (text: string): boolean => {
|
||||
return /[\u4e00-\u9fff]/.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* 保持注释的原始格式,支持逐行翻译多行注释
|
||||
* Maintain the original format of comments and support line-by-line translation of multi-line comments
|
||||
*/
|
||||
export const preserveCommentFormat = (
|
||||
originalComment: string,
|
||||
|
|
@ -24,7 +24,7 @@ export const preserveCommentFormat = (
|
|||
commentType: 'single-line' | 'multi-line',
|
||||
): string => {
|
||||
if (commentType === 'single-line') {
|
||||
// 保持单行注释的前缀空格和注释符 - 支持多种语言
|
||||
// Keep single-line comments prefixed with spaces and comment characters - supports multiple languages
|
||||
let match = originalComment.match(/^(\s*\/\/\s*)/); // JavaScript/TypeScript style
|
||||
if (match) {
|
||||
return match[1] + translatedComment.trim();
|
||||
|
|
@ -40,13 +40,13 @@ export const preserveCommentFormat = (
|
|||
return match[1] + translatedComment.trim();
|
||||
}
|
||||
|
||||
// 如果无法识别,尝试从原始内容推断
|
||||
// If not recognized, try to infer from the original content
|
||||
if (originalComment.includes('#')) {
|
||||
const hashMatch = originalComment.match(/^(\s*#\s*)/);
|
||||
return (hashMatch ? hashMatch[1] : '# ') + translatedComment.trim();
|
||||
}
|
||||
|
||||
// 默认使用 JavaScript 风格
|
||||
// JavaScript style is used by default
|
||||
return '// ' + translatedComment.trim();
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ export const preserveCommentFormat = (
|
|||
const lines = originalComment.split('\n');
|
||||
|
||||
if (lines.length === 1) {
|
||||
// 单行多行注释 /* ... */ 或 /** ... */
|
||||
// Single-line multi-line comment/*... */or/**... */
|
||||
const startMatch = originalComment.match(/^(\s*\/\*\*?\s*)/);
|
||||
const endMatch = originalComment.match(/(\s*\*\/\s*)$/);
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ export const preserveCommentFormat = (
|
|||
|
||||
return prefix + translatedComment.trim() + suffix;
|
||||
} else {
|
||||
// 多行注释 - 需要逐行处理
|
||||
// Multi-line comments - requires line-by-line processing
|
||||
return processMultiLineComment(originalComment, translatedComment);
|
||||
}
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ export const preserveCommentFormat = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 处理多行注释,逐行翻译含中文的行,保持其他行原样
|
||||
* Process multi-line comments, translate lines containing Chinese line by line, and keep other lines as they are
|
||||
*/
|
||||
export const processMultiLineComment = (
|
||||
originalComment: string,
|
||||
|
|
@ -88,54 +88,54 @@ export const processMultiLineComment = (
|
|||
): string => {
|
||||
const originalLines = originalComment.split('\n');
|
||||
|
||||
// 提取每行的注释内容(去除 /** * 等前缀)
|
||||
// Extract comments for each line (remove prefixes such as /** * )other prefixes)
|
||||
const extractedLines = originalLines.map(line => {
|
||||
// 匹配不同类型的注释行
|
||||
Match different types of comment linesifferent types of comment lines
|
||||
if (line.match(/^\s*\/\*\*?\s*/)) {
|
||||
// 开始行: /** 或 /*
|
||||
// Start line:/** or/*
|
||||
return { prefix: line.match(/^\s*\/\*\*?\s*/)![0], content: line.replace(/^\s*\/\*\*?\s*/, '') };
|
||||
} else if (line.match(/^\s*\*\/\s*$/)) {
|
||||
// 结束行: */
|
||||
// End line: */
|
||||
return { prefix: line.match(/^\s*\*\/\s*$/)![0], content: '' };
|
||||
} else if (line.match(/^\s*\*\s*/)) {
|
||||
// 中间行: * content
|
||||
// Middle line: * content
|
||||
const match = line.match(/^(\s*\*\s*)(.*)/);
|
||||
return { prefix: match![1], content: match![2] };
|
||||
} else {
|
||||
// 其他情况
|
||||
// Other situations
|
||||
return { prefix: '', content: line };
|
||||
}
|
||||
});
|
||||
|
||||
// 收集需要翻译的行
|
||||
// Collect lines that need to be translated
|
||||
const linesToTranslate = extractedLines
|
||||
.map((line, index) => ({ index, content: line.content }))
|
||||
.filter(item => containsChinese(item.content));
|
||||
|
||||
// 如果没有中文内容,返回原始注释
|
||||
// If there is no Chinese content, return the original comment
|
||||
if (linesToTranslate.length === 0) {
|
||||
return originalComment;
|
||||
}
|
||||
|
||||
// 解析翻译结果 - 假设翻译服务按顺序返回翻译后的行
|
||||
// Parse translation results - assuming the translation service returns translated rows in order
|
||||
const translatedLines = translatedContent.split('\n');
|
||||
const translations = new Map<number, string>();
|
||||
|
||||
// 将翻译结果映射到对应的行
|
||||
// Map the translation result to the corresponding line
|
||||
linesToTranslate.forEach((item, transIndex) => {
|
||||
if (transIndex < translatedLines.length) {
|
||||
translations.set(item.index, translatedLines[transIndex].trim());
|
||||
}
|
||||
});
|
||||
|
||||
// 重建注释,保持原始结构
|
||||
// Rebuild the annotation, maintaining the original structure
|
||||
return extractedLines
|
||||
.map((line, index) => {
|
||||
if (translations.has(index)) {
|
||||
// 使用翻译内容,保持原始前缀
|
||||
// Use translated content, keeping the original prefix
|
||||
return line.prefix + translations.get(index);
|
||||
} else {
|
||||
// 保持原样
|
||||
// Leave it as it is
|
||||
return originalLines[index];
|
||||
}
|
||||
})
|
||||
|
|
@ -143,7 +143,7 @@ export const processMultiLineComment = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 创建替换操作
|
||||
* Create replacement operation
|
||||
*/
|
||||
export const createReplacements = (
|
||||
file: SourceFile,
|
||||
|
|
@ -157,13 +157,13 @@ export const createReplacements = (
|
|||
if (!translation) return;
|
||||
|
||||
if (comment.multiLineContext?.isPartOfMultiLine) {
|
||||
// 处理多行注释中的单行
|
||||
// Handling single lines in multi-line comments
|
||||
const replacement = createMultiLineReplacement(file, comment, translation);
|
||||
if (replacement) {
|
||||
replacements.push(replacement);
|
||||
}
|
||||
} else {
|
||||
// 处理普通注释(单行注释或整个多行注释)
|
||||
// Processing normal comments (single-line comments or entire multi-line comments)
|
||||
const replacement = createRegularReplacement(file, comment, translation);
|
||||
if (replacement) {
|
||||
replacements.push(replacement);
|
||||
|
|
@ -175,7 +175,7 @@ export const createReplacements = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 为多行注释中的单行创建替换操作
|
||||
* Create a replacement operation for a single line in a multi-line comment
|
||||
*/
|
||||
const createMultiLineReplacement = (
|
||||
file: SourceFile,
|
||||
|
|
@ -189,10 +189,10 @@ const createMultiLineReplacement = (
|
|||
|
||||
const originalLine = lines[lineIndex];
|
||||
|
||||
// 查找这一行中中文内容的位置
|
||||
// Find the location of Chinese content in this line
|
||||
const cleanedContent = comment.content;
|
||||
|
||||
// 更精确地查找中文内容在原始行中的位置
|
||||
// Find the position of Chinese content in the original line more accurately
|
||||
const commentContentRegex = new RegExp(cleanedContent.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
||||
const contentMatch = originalLine.match(commentContentRegex);
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ const createMultiLineReplacement = (
|
|||
const chineseStart = contentMatch.index!;
|
||||
const chineseEnd = chineseStart + contentMatch[0].length;
|
||||
|
||||
// 计算在整个文件中的位置
|
||||
// Calculate the position in the entire file
|
||||
let start = 0;
|
||||
for (let i = 0; i < lineIndex; i++) {
|
||||
start += lines[i].length + 1; // +1 for newline
|
||||
|
|
@ -221,7 +221,7 @@ const createMultiLineReplacement = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 为普通注释创建替换操作
|
||||
* Create a replacement operation for a normal comment
|
||||
*/
|
||||
const createRegularReplacement = (
|
||||
file: SourceFile,
|
||||
|
|
@ -232,7 +232,7 @@ const createRegularReplacement = (
|
|||
const startLineIndex = comment.startLine - 1;
|
||||
const endLineIndex = comment.endLine - 1;
|
||||
|
||||
// 计算原始注释在文件中的精确位置
|
||||
// Calculate the exact location of the original comment in the file
|
||||
let start = 0;
|
||||
for (let i = 0; i < startLineIndex; i++) {
|
||||
start += lines[i].length + 1; // +1 for newline
|
||||
|
|
@ -241,10 +241,10 @@ const createRegularReplacement = (
|
|||
|
||||
let end = start;
|
||||
if (comment.startLine === comment.endLine) {
|
||||
// 同一行
|
||||
// same line
|
||||
end = start + (comment.endColumn - comment.startColumn);
|
||||
} else {
|
||||
// 跨行 - 重新计算end位置
|
||||
// Interline recalculation of end position
|
||||
end = 0;
|
||||
for (let i = 0; i < endLineIndex; i++) {
|
||||
end += lines[i].length + 1; // +1 for newline
|
||||
|
|
@ -252,10 +252,10 @@ const createRegularReplacement = (
|
|||
end += comment.endColumn;
|
||||
}
|
||||
|
||||
// 获取原始注释文本
|
||||
// Get original comment text
|
||||
const originalText = file.content.substring(start, end);
|
||||
|
||||
// 应用格式保持
|
||||
// application format retention
|
||||
const formattedTranslation = preserveCommentFormat(
|
||||
originalText,
|
||||
translation.translated,
|
||||
|
|
@ -271,13 +271,13 @@ const createRegularReplacement = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 应用替换操作到文本内容
|
||||
* Apply a replacement operation to text content
|
||||
*/
|
||||
export const applyReplacements = (
|
||||
content: string,
|
||||
replacements: Replacement[],
|
||||
): string => {
|
||||
// 按位置倒序排列,避免替换后位置偏移
|
||||
// Arrange in reverse order to avoid position shift after replacement
|
||||
const sortedReplacements = [...replacements].sort(
|
||||
(a, b) => b.start - a.start,
|
||||
);
|
||||
|
|
@ -294,7 +294,7 @@ export const applyReplacements = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 替换文件中的注释
|
||||
* Replace comments in the file
|
||||
*/
|
||||
export const replaceCommentsInFile = async (
|
||||
file: SourceFile,
|
||||
|
|
@ -303,13 +303,13 @@ export const replaceCommentsInFile = async (
|
|||
return tryCatch(async () => {
|
||||
const fs = await import('fs/promises');
|
||||
|
||||
// 应用替换
|
||||
// Application Replacement
|
||||
const newContent = applyReplacements(
|
||||
file.content,
|
||||
operation.replacements,
|
||||
);
|
||||
|
||||
// 写入文件
|
||||
// Write file
|
||||
await fs.writeFile(file.path, newContent, 'utf-8');
|
||||
|
||||
return { success: true };
|
||||
|
|
@ -329,7 +329,7 @@ export const replaceCommentsInFile = async (
|
|||
};
|
||||
|
||||
/**
|
||||
* 批量替换多个文件
|
||||
* Batch replacement of multiple files
|
||||
*/
|
||||
export const batchReplaceFiles = async (
|
||||
operations: ReplacementOperation[],
|
||||
|
|
@ -347,7 +347,7 @@ export const batchReplaceFiles = async (
|
|||
const sourceFile: SourceFile = {
|
||||
path: operation.file,
|
||||
content,
|
||||
language: 'other', // 临时值,实际应该检测
|
||||
language: 'other', // Temporary value, which should actually be checked
|
||||
};
|
||||
|
||||
const result = await replaceCommentsInFile(
|
||||
|
|
@ -375,7 +375,7 @@ export const batchReplaceFiles = async (
|
|||
};
|
||||
|
||||
/**
|
||||
* 验证替换操作
|
||||
* Verify replacement operation
|
||||
*/
|
||||
export const validateReplacements = (
|
||||
content: string,
|
||||
|
|
@ -383,7 +383,7 @@ export const validateReplacements = (
|
|||
): { valid: boolean; errors: string[] } => {
|
||||
const errors: string[] = [];
|
||||
|
||||
// 检查位置是否有效
|
||||
// Check if the location is valid
|
||||
replacements.forEach((replacement, index) => {
|
||||
if (replacement.start < 0 || replacement.end > content.length) {
|
||||
errors.push(`Replacement ${index}: Invalid position range`);
|
||||
|
|
@ -395,14 +395,14 @@ export const validateReplacements = (
|
|||
);
|
||||
}
|
||||
|
||||
// 检查原文是否匹配
|
||||
// Check if the original text matches
|
||||
const actualText = content.substring(replacement.start, replacement.end);
|
||||
if (actualText !== replacement.original) {
|
||||
errors.push(`Replacement ${index}: Original text mismatch`);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查是否有重叠
|
||||
// Check for overlap
|
||||
const sortedReplacements = [...replacements].sort(
|
||||
(a, b) => a.start - b.start,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { getGitTrackedFiles, getAllGitFiles } from '../utils/git';
|
|||
import { tryCatch } from '../utils/fp';
|
||||
|
||||
/**
|
||||
* 读取文件内容并创建SourceFile对象
|
||||
* Read the file contents and create a SourceFile object
|
||||
*/
|
||||
export const readSourceFile = async (filePath: string): Promise<Result<SourceFile>> => {
|
||||
return tryCatch(async () => {
|
||||
|
|
@ -21,7 +21,7 @@ export const readSourceFile = async (filePath: string): Promise<Result<SourceFil
|
|||
};
|
||||
|
||||
/**
|
||||
* 批量读取源文件
|
||||
* Batch reading of source files
|
||||
*/
|
||||
export const readSourceFiles = async (filePaths: string[]): Promise<SourceFile[]> => {
|
||||
const results = await Promise.allSettled(
|
||||
|
|
@ -36,13 +36,13 @@ export const readSourceFiles = async (filePaths: string[]): Promise<SourceFile[]
|
|||
};
|
||||
|
||||
/**
|
||||
* 获取Git仓库中的源码文件
|
||||
* Get the source code file in the Git repository
|
||||
*/
|
||||
export const getSourceFiles = async (config: FileScanConfig): Promise<Result<string[]>> => {
|
||||
const { root, extensions, includeUntracked } = config;
|
||||
|
||||
return tryCatch(async () => {
|
||||
// 获取Git文件列表
|
||||
// Get a list of Git files
|
||||
const gitFilesResult = includeUntracked
|
||||
? await getAllGitFiles(root)
|
||||
: await getGitTrackedFiles(root);
|
||||
|
|
@ -53,10 +53,10 @@ export const getSourceFiles = async (config: FileScanConfig): Promise<Result<str
|
|||
|
||||
let files = gitFilesResult.data;
|
||||
|
||||
// 过滤文本文件
|
||||
// Filter text files
|
||||
files = files.filter(isTextFile);
|
||||
|
||||
// 根据扩展名过滤
|
||||
// Filter by extension
|
||||
files = filterFilesByExtensions(files, extensions);
|
||||
|
||||
return files;
|
||||
|
|
@ -64,7 +64,7 @@ export const getSourceFiles = async (config: FileScanConfig): Promise<Result<str
|
|||
};
|
||||
|
||||
/**
|
||||
* 扫描并读取所有源码文件
|
||||
* Scan and read all source code files
|
||||
*/
|
||||
export const scanSourceFiles = async (config: FileScanConfig): Promise<Result<SourceFile[]>> => {
|
||||
return tryCatch(async () => {
|
||||
|
|
@ -80,7 +80,7 @@ export const scanSourceFiles = async (config: FileScanConfig): Promise<Result<So
|
|||
};
|
||||
|
||||
/**
|
||||
* 检查文件是否存在且可读
|
||||
* Check if the file exists and is readable
|
||||
*/
|
||||
export const isFileAccessible = async (filePath: string): Promise<boolean> => {
|
||||
try {
|
||||
|
|
@ -92,7 +92,7 @@ export const isFileAccessible = async (filePath: string): Promise<boolean> => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 获取文件统计信息
|
||||
* Get file statistics
|
||||
*/
|
||||
export const getFileStats = async (filePaths: string[]): Promise<{
|
||||
total: number;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from '../types/index.js';
|
||||
|
||||
/**
|
||||
* 报告收集器类
|
||||
* report collector class
|
||||
*/
|
||||
export class ReportCollector {
|
||||
private stats: ProcessingStats = {
|
||||
|
|
@ -21,7 +21,7 @@ export class ReportCollector {
|
|||
private fileDetails: Map<string, FileProcessingDetail> = new Map();
|
||||
|
||||
/**
|
||||
* 记录文件处理开始
|
||||
* Record file processing begins
|
||||
*/
|
||||
recordFileStart(filePath: string): void {
|
||||
this.stats.totalFiles++;
|
||||
|
|
@ -34,7 +34,7 @@ export class ReportCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* 记录文件处理完成
|
||||
* Record file processing completed
|
||||
*/
|
||||
recordFileComplete(filePath: string, commentCount: number): void {
|
||||
const detail = this.fileDetails.get(filePath);
|
||||
|
|
@ -48,7 +48,7 @@ export class ReportCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* 记录文件跳过
|
||||
* log file skip
|
||||
*/
|
||||
recordFileSkipped(filePath: string, reason?: string): void {
|
||||
const detail = this.fileDetails.get(filePath);
|
||||
|
|
@ -61,7 +61,7 @@ export class ReportCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* 记录处理错误
|
||||
* Log processing errors
|
||||
*/
|
||||
recordError(filePath: string, error: Error): void {
|
||||
const detail = this.fileDetails.get(filePath);
|
||||
|
|
@ -74,28 +74,28 @@ export class ReportCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* 完成统计
|
||||
* Complete statistics
|
||||
*/
|
||||
finalize(): void {
|
||||
this.stats.endTime = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
* Obtain statistical information
|
||||
*/
|
||||
getStats(): ProcessingStats {
|
||||
return { ...this.stats };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件详情
|
||||
* Get file details
|
||||
*/
|
||||
getFileDetails(): FileProcessingDetail[] {
|
||||
return Array.from(this.fileDetails.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成完整报告
|
||||
* Generate a full report
|
||||
*/
|
||||
generateReport(): ProcessingReport {
|
||||
this.finalize();
|
||||
|
|
@ -109,7 +109,7 @@ export class ReportCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* 重置收集器
|
||||
* Reset collector
|
||||
*/
|
||||
reset(): void {
|
||||
this.stats = {
|
||||
|
|
@ -126,7 +126,7 @@ export class ReportCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* 生成控制台报告
|
||||
* Generate console reports
|
||||
*/
|
||||
export const generateConsoleReport = (report: ProcessingReport): string => {
|
||||
const { stats, duration } = report;
|
||||
|
|
@ -160,7 +160,7 @@ export const generateConsoleReport = (report: ProcessingReport): string => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 生成Markdown报告
|
||||
* Generating Markdown Reports
|
||||
*/
|
||||
export const generateMarkdownReport = (report: ProcessingReport): string => {
|
||||
const { stats, details, duration } = report;
|
||||
|
|
@ -217,14 +217,14 @@ export const generateMarkdownReport = (report: ProcessingReport): string => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 生成JSON报告
|
||||
* Generate JSON reports
|
||||
*/
|
||||
export const generateJsonReport = (report: ProcessingReport): string => {
|
||||
return JSON.stringify(report, null, 2);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据格式生成报告
|
||||
* Generate reports according to the format
|
||||
*/
|
||||
export const generateReport = (
|
||||
report: ProcessingReport,
|
||||
|
|
@ -242,7 +242,7 @@ export const generateReport = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 保存报告到文件
|
||||
* Save report to file
|
||||
*/
|
||||
export const saveReportToFile = async (
|
||||
report: ProcessingReport,
|
||||
|
|
@ -255,7 +255,7 @@ export const saveReportToFile = async (
|
|||
};
|
||||
|
||||
/**
|
||||
* 在控制台显示实时进度
|
||||
* Display real-time progress on the console
|
||||
*/
|
||||
export class ProgressDisplay {
|
||||
private total: number = 0;
|
||||
|
|
@ -267,7 +267,7 @@ export class ProgressDisplay {
|
|||
}
|
||||
|
||||
/**
|
||||
* 更新进度
|
||||
* update progress
|
||||
*/
|
||||
update(current: number, currentFile?: string): void {
|
||||
this.current = current;
|
||||
|
|
@ -286,7 +286,7 @@ export class ProgressDisplay {
|
|||
line += ` | 当前: ${currentFile}`;
|
||||
}
|
||||
|
||||
// 清除当前行并输出新进度
|
||||
// Clear the current line and output the new progress
|
||||
process.stdout.write(
|
||||
'\r' + ' '.repeat(process.stdout.columns || 80) + '\r',
|
||||
);
|
||||
|
|
@ -294,7 +294,7 @@ export class ProgressDisplay {
|
|||
}
|
||||
|
||||
/**
|
||||
* 完成进度显示
|
||||
* completion progress display
|
||||
*/
|
||||
complete(): void {
|
||||
process.stdout.write('\n');
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { isValidTranslation } from '../utils/chinese';
|
|||
import { translate as volcTranslate, TranslateConfig as VolcTranslateConfig } from '../volc/translate';
|
||||
|
||||
/**
|
||||
* 翻译服务类
|
||||
* Translation services
|
||||
*/
|
||||
export class TranslationService {
|
||||
private config: TranslationConfig;
|
||||
|
|
@ -21,7 +21,7 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 转换为火山引擎翻译配置
|
||||
* Convert to Volcano Engine Translation Configuration
|
||||
*/
|
||||
private toVolcConfig(): VolcTranslateConfig {
|
||||
return {
|
||||
|
|
@ -34,17 +34,17 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 计算翻译置信度(简单实现)
|
||||
* Calculate translation confidence level (simple implementation)
|
||||
*/
|
||||
private calculateConfidence(translated: string, original: string): number {
|
||||
// 基于长度比例和有效性的简单置信度计算
|
||||
// Simple confidence level calculation based on length ratio and validity
|
||||
const lengthRatio = translated.length / original.length;
|
||||
|
||||
if (!isValidTranslation(original, translated)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 理想的长度比例在0.8-2.0之间
|
||||
// The ideal length ratio is between 0.8-2
|
||||
let confidence = 0.8;
|
||||
if (lengthRatio >= 0.8 && lengthRatio <= 2.0) {
|
||||
confidence = 0.9;
|
||||
|
|
@ -54,7 +54,7 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 调用火山引擎API进行翻译
|
||||
* Call Volcano Engine API for translation
|
||||
*/
|
||||
private async callVolcTranslate(texts: string[]): Promise<string[]> {
|
||||
const volcConfig = this.toVolcConfig();
|
||||
|
|
@ -64,13 +64,13 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 翻译单个注释
|
||||
* Translate a single comment
|
||||
*/
|
||||
async translateComment(
|
||||
comment: string,
|
||||
context?: TranslationContext,
|
||||
): Promise<TranslationResult> {
|
||||
// 检查缓存
|
||||
// Check cache
|
||||
const cacheKey = this.getCacheKey(comment, context);
|
||||
const cached = this.cache.get(cacheKey);
|
||||
if (cached) {
|
||||
|
|
@ -95,7 +95,7 @@ export class TranslationService {
|
|||
confidence: this.calculateConfidence(translated, comment),
|
||||
};
|
||||
|
||||
// 缓存结果
|
||||
// cache results
|
||||
this.cache.set(cacheKey, result);
|
||||
|
||||
return result;
|
||||
|
|
@ -108,7 +108,7 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 生成缓存键
|
||||
* generate cache key
|
||||
*/
|
||||
private getCacheKey(comment: string, context?: TranslationContext): string {
|
||||
const contextStr = context
|
||||
|
|
@ -118,17 +118,17 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 批量翻译注释
|
||||
* batch translation annotations
|
||||
*/
|
||||
async batchTranslate(
|
||||
comments: ChineseComment[],
|
||||
concurrency: number = this.config.concurrency,
|
||||
): Promise<TranslationResult[]> {
|
||||
// 提取未缓存的注释
|
||||
// Extract uncached comments
|
||||
const uncachedComments: { comment: ChineseComment; index: number }[] = [];
|
||||
const results: TranslationResult[] = new Array(comments.length);
|
||||
|
||||
// 检查缓存
|
||||
// Check cache
|
||||
comments.forEach((comment, index) => {
|
||||
const cacheKey = this.getCacheKey(comment.content);
|
||||
const cached = this.cache.get(cacheKey);
|
||||
|
|
@ -139,12 +139,12 @@ export class TranslationService {
|
|||
}
|
||||
});
|
||||
|
||||
// 如果所有注释都已缓存,直接返回
|
||||
// If all comments are cached, return directly
|
||||
if (uncachedComments.length === 0) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// 分批翻译未缓存的注释
|
||||
// Batch translation of uncached comments
|
||||
const chunks = chunk(uncachedComments, concurrency);
|
||||
|
||||
for (const chunkItems of chunks) {
|
||||
|
|
@ -156,7 +156,7 @@ export class TranslationService {
|
|||
1000,
|
||||
);
|
||||
|
||||
// 处理翻译结果
|
||||
// Processing translation results
|
||||
chunkItems.forEach((item, chunkIndex) => {
|
||||
const translated = translations[chunkIndex];
|
||||
if (translated) {
|
||||
|
|
@ -166,26 +166,26 @@ export class TranslationService {
|
|||
confidence: this.calculateConfidence(translated, item.comment.content),
|
||||
};
|
||||
|
||||
// 缓存结果
|
||||
// cache results
|
||||
const cacheKey = this.getCacheKey(item.comment.content);
|
||||
this.cache.set(cacheKey, result);
|
||||
|
||||
results[item.index] = result;
|
||||
} else {
|
||||
// 如果翻译失败,创建一个错误结果
|
||||
// If the translation fails, an error result is created
|
||||
results[item.index] = {
|
||||
original: item.comment.content,
|
||||
translated: item.comment.content, // 翻译失败时保持原文
|
||||
translated: item.comment.content, // Keep the original text when translation fails
|
||||
confidence: 0,
|
||||
};
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// 如果整个批次翻译失败,为这个批次的所有注释创建错误结果
|
||||
// If the entire batch translation fails, an error result is created for all comments in that batch
|
||||
chunkItems.forEach(item => {
|
||||
results[item.index] = {
|
||||
original: item.comment.content,
|
||||
translated: item.comment.content, // 翻译失败时保持原文
|
||||
translated: item.comment.content, // Keep the original text when translation fails
|
||||
confidence: 0,
|
||||
};
|
||||
});
|
||||
|
|
@ -198,7 +198,7 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 保存翻译缓存到文件
|
||||
* Save translation cache to file
|
||||
*/
|
||||
async saveCache(filePath: string): Promise<void> {
|
||||
const cacheData = Object.fromEntries(this.cache);
|
||||
|
|
@ -207,7 +207,7 @@ export class TranslationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 从文件加载翻译缓存
|
||||
* Load translation cache from file
|
||||
*/
|
||||
async loadCache(filePath: string): Promise<void> {
|
||||
try {
|
||||
|
|
@ -216,24 +216,24 @@ export class TranslationService {
|
|||
const cacheData = JSON.parse(data);
|
||||
this.cache = new Map(Object.entries(cacheData));
|
||||
} catch {
|
||||
// 缓存文件不存在或损坏,忽略
|
||||
// The cache file does not exist or is corrupted, ignore it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存
|
||||
* clear cache
|
||||
*/
|
||||
clearCache(): void {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存统计
|
||||
* Get cache statistics
|
||||
*/
|
||||
getCacheStats(): { size: number; hitRate: number } {
|
||||
return {
|
||||
size: this.cache.size,
|
||||
hitRate: 0, // 需要实际统计命中率
|
||||
hitRate: 0, // Actual statistical hit rate is required
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* 翻译配置
|
||||
* translation configuration
|
||||
*/
|
||||
export interface TranslationConfig {
|
||||
accessKeyId: string;
|
||||
|
|
@ -13,7 +13,7 @@ export interface TranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* 文件扫描配置
|
||||
* File Scan Configuration
|
||||
*/
|
||||
export interface FileScanConfig {
|
||||
root: string;
|
||||
|
|
@ -23,7 +23,7 @@ export interface FileScanConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* 处理配置
|
||||
* handle configuration
|
||||
*/
|
||||
export interface ProcessingConfig {
|
||||
defaultExtensions: string[];
|
||||
|
|
@ -31,7 +31,7 @@ export interface ProcessingConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Git配置
|
||||
* Git Configuration
|
||||
*/
|
||||
export interface GitConfig {
|
||||
ignorePatterns: string[];
|
||||
|
|
@ -39,7 +39,7 @@ export interface GitConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* 应用配置
|
||||
* application configuration
|
||||
*/
|
||||
export interface AppConfig {
|
||||
translation: TranslationConfig;
|
||||
|
|
@ -48,7 +48,7 @@ export interface AppConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* 命令行选项
|
||||
* command line options
|
||||
*/
|
||||
export interface CliOptions {
|
||||
root: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* 源文件语言类型
|
||||
* Source file language type
|
||||
*/
|
||||
export type SourceFileLanguage =
|
||||
| 'typescript'
|
||||
|
|
@ -27,12 +27,12 @@ export type SourceFileLanguage =
|
|||
| 'other';
|
||||
|
||||
/**
|
||||
* 注释类型
|
||||
* comment type
|
||||
*/
|
||||
export type CommentType = 'single-line' | 'multi-line' | 'documentation';
|
||||
|
||||
/**
|
||||
* 源文件信息
|
||||
* source file information
|
||||
*/
|
||||
export interface SourceFile {
|
||||
path: string;
|
||||
|
|
@ -41,7 +41,7 @@ export interface SourceFile {
|
|||
}
|
||||
|
||||
/**
|
||||
* 多行注释上下文信息
|
||||
* multiline comment context information
|
||||
*/
|
||||
export interface MultiLineContext {
|
||||
isPartOfMultiLine: boolean;
|
||||
|
|
@ -51,7 +51,7 @@ export interface MultiLineContext {
|
|||
}
|
||||
|
||||
/**
|
||||
* 中文注释信息
|
||||
* Chinese annotation information
|
||||
*/
|
||||
export interface ChineseComment {
|
||||
content: string;
|
||||
|
|
@ -64,7 +64,7 @@ export interface ChineseComment {
|
|||
}
|
||||
|
||||
/**
|
||||
* 包含中文注释的文件
|
||||
* Files containing Chinese annotations
|
||||
*/
|
||||
export interface FileWithComments {
|
||||
file: SourceFile;
|
||||
|
|
@ -72,7 +72,7 @@ export interface FileWithComments {
|
|||
}
|
||||
|
||||
/**
|
||||
* 翻译结果
|
||||
* translation result
|
||||
*/
|
||||
export interface TranslationResult {
|
||||
original: string;
|
||||
|
|
@ -81,7 +81,7 @@ export interface TranslationResult {
|
|||
}
|
||||
|
||||
/**
|
||||
* 翻译上下文
|
||||
* translation context
|
||||
*/
|
||||
export interface TranslationContext {
|
||||
language: string;
|
||||
|
|
@ -90,7 +90,7 @@ export interface TranslationContext {
|
|||
}
|
||||
|
||||
/**
|
||||
* 替换操作
|
||||
* replace operation
|
||||
*/
|
||||
export interface Replacement {
|
||||
start: number;
|
||||
|
|
@ -100,7 +100,7 @@ export interface Replacement {
|
|||
}
|
||||
|
||||
/**
|
||||
* 文件替换操作
|
||||
* file replacement operation
|
||||
*/
|
||||
export interface ReplacementOperation {
|
||||
file: string;
|
||||
|
|
@ -108,7 +108,7 @@ export interface ReplacementOperation {
|
|||
}
|
||||
|
||||
/**
|
||||
* 文件处理详情
|
||||
* Document processing details
|
||||
*/
|
||||
export interface FileProcessingDetail {
|
||||
file: string;
|
||||
|
|
@ -120,7 +120,7 @@ export interface FileProcessingDetail {
|
|||
}
|
||||
|
||||
/**
|
||||
* 处理统计信息
|
||||
* Processing statistics
|
||||
*/
|
||||
export interface ProcessingStats {
|
||||
totalFiles: number;
|
||||
|
|
@ -133,7 +133,7 @@ export interface ProcessingStats {
|
|||
}
|
||||
|
||||
/**
|
||||
* 处理报告
|
||||
* processing report
|
||||
*/
|
||||
export interface ProcessingReport {
|
||||
stats: ProcessingStats;
|
||||
|
|
@ -142,7 +142,7 @@ export interface ProcessingReport {
|
|||
}
|
||||
|
||||
/**
|
||||
* 文件扫描配置
|
||||
* File Scan Configuration
|
||||
*/
|
||||
export interface FileScanConfig {
|
||||
root: string;
|
||||
|
|
@ -152,7 +152,7 @@ export interface FileScanConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* 解析的注释
|
||||
* Parsed annotations
|
||||
*/
|
||||
export interface ParsedComment {
|
||||
content: string;
|
||||
|
|
@ -164,7 +164,7 @@ export interface ParsedComment {
|
|||
}
|
||||
|
||||
/**
|
||||
* 注释模式配置
|
||||
* Comment mode configuration
|
||||
*/
|
||||
export interface CommentPattern {
|
||||
single: RegExp;
|
||||
|
|
@ -173,14 +173,14 @@ export interface CommentPattern {
|
|||
}
|
||||
|
||||
/**
|
||||
* 函数式编程结果类型
|
||||
* Functional programming result type
|
||||
*/
|
||||
export type Result<T, E = Error> =
|
||||
| { success: true; data: T }
|
||||
| { success: false; error: E };
|
||||
|
||||
/**
|
||||
* 翻译错误
|
||||
* translation error
|
||||
*/
|
||||
export class TranslationError extends Error {
|
||||
constructor(
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
/**
|
||||
* 中文字符的Unicode范围正则表达式
|
||||
* Unicode Range Regular Expressions for Chinese Characters
|
||||
*/
|
||||
const CHINESE_REGEX = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]/;
|
||||
const CHINESE_EXTRACT_REGEX = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3000-\u303f\uff00-\uffef]+/g;
|
||||
|
||||
/**
|
||||
* 检测文本是否包含中文字符
|
||||
* Detect whether the text contains Chinese characters
|
||||
*/
|
||||
export const containsChinese = (text: string): boolean => {
|
||||
return CHINESE_REGEX.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* 提取文本中的中文部分
|
||||
* Extract the Chinese part of the text
|
||||
*/
|
||||
export const extractChineseParts = (text: string): string[] => {
|
||||
return text.match(CHINESE_EXTRACT_REGEX) || [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 计算文本中中文字符的数量
|
||||
* Count the number of Chinese characters in a text
|
||||
*/
|
||||
export const countChineseCharacters = (text: string): number => {
|
||||
const matches = text.match(CHINESE_EXTRACT_REGEX);
|
||||
|
|
@ -29,7 +29,7 @@ export const countChineseCharacters = (text: string): number => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 检测文本是否主要由中文组成
|
||||
* Detect whether the text is mainly composed of Chinese
|
||||
*/
|
||||
export const isPrimarilyChinese = (text: string, threshold: number = 0.5): boolean => {
|
||||
const totalLength = text.length;
|
||||
|
|
@ -40,7 +40,7 @@ export const isPrimarilyChinese = (text: string, threshold: number = 0.5): boole
|
|||
};
|
||||
|
||||
/**
|
||||
* 清理注释文本,移除注释符号和多余空格
|
||||
* Clean up comment text, remove comment symbols and extra spaces
|
||||
*/
|
||||
export const cleanCommentText = (
|
||||
text: string,
|
||||
|
|
@ -50,7 +50,7 @@ export const cleanCommentText = (
|
|||
let cleaned = text;
|
||||
|
||||
if (commentType === 'single-line') {
|
||||
// 根据语言类型移除不同的单行注释符号
|
||||
// Remove different single-line comment symbols based on language type
|
||||
switch (language) {
|
||||
case 'yaml':
|
||||
case 'toml':
|
||||
|
|
@ -70,7 +70,7 @@ export const cleanCommentText = (
|
|||
cleaned = cleaned.replace(/^\/\/\s*/, '');
|
||||
}
|
||||
} else if (commentType === 'multi-line') {
|
||||
// 根据语言类型移除不同的多行注释符号
|
||||
// Remove different multi-line comment symbols based on language type
|
||||
switch (language) {
|
||||
case 'html':
|
||||
case 'xml':
|
||||
|
|
@ -86,32 +86,32 @@ export const cleanCommentText = (
|
|||
default:
|
||||
// JavaScript/TypeScript/Go/Java/C/C++/C#/CSS style
|
||||
cleaned = cleaned.replace(/^\/\*\s*/, '').replace(/\s*\*\/$/, '');
|
||||
// 移除每行开头的 * 符号
|
||||
// Remove the * symbol at the beginning of each line
|
||||
cleaned = cleaned.replace(/^\s*\*\s?/gm, '');
|
||||
}
|
||||
}
|
||||
|
||||
// 移除多余的空格和换行
|
||||
// Remove extra spaces and newlines
|
||||
cleaned = cleaned.trim();
|
||||
|
||||
return cleaned;
|
||||
};
|
||||
|
||||
/**
|
||||
* 验证翻译结果是否有效
|
||||
* Verify whether the translation result is valid.
|
||||
*/
|
||||
export const isValidTranslation = (original: string, translated: string): boolean => {
|
||||
// 基本验证
|
||||
// basic verification
|
||||
if (!translated || translated.trim().length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否还包含中文(可能翻译失败)
|
||||
// Check if Chinese is also included (translation may fail)
|
||||
if (containsChinese(translated)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查长度是否合理(翻译后的文本不应该比原文长太多)
|
||||
// Check if the length is reasonable (the translated text should not be much longer than the original).
|
||||
if (translated.length > original.length * 3) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Result } from '../types/index';
|
||||
|
||||
/**
|
||||
* 函数组合 - 从左到右执行
|
||||
* Function composition - executed from left to right
|
||||
*/
|
||||
export const pipe =
|
||||
<T>(...fns: Function[]) =>
|
||||
|
|
@ -9,7 +9,7 @@ export const pipe =
|
|||
fns.reduce((acc, fn) => fn(acc), value);
|
||||
|
||||
/**
|
||||
* 函数组合 - 从右到左执行
|
||||
* Function composition - executed from right to left
|
||||
*/
|
||||
export const compose =
|
||||
<T>(...fns: Function[]) =>
|
||||
|
|
@ -17,7 +17,7 @@ export const compose =
|
|||
fns.reduceRight((acc, fn) => fn(acc), value);
|
||||
|
||||
/**
|
||||
* 柯里化函数
|
||||
* currying function
|
||||
*/
|
||||
export const curry =
|
||||
(fn: Function) =>
|
||||
|
|
@ -27,7 +27,7 @@ export const curry =
|
|||
: (...more: any[]) => curry(fn)(...args, ...more);
|
||||
|
||||
/**
|
||||
* 异步映射
|
||||
* asynchronous mapping
|
||||
*/
|
||||
export const asyncMap = curry(
|
||||
async <T, U>(fn: (item: T) => Promise<U>, items: T[]): Promise<U[]> =>
|
||||
|
|
@ -35,7 +35,7 @@ export const asyncMap = curry(
|
|||
);
|
||||
|
||||
/**
|
||||
* 异步过滤
|
||||
* asynchronous filtering
|
||||
*/
|
||||
export const asyncFilter = curry(
|
||||
async <T>(
|
||||
|
|
@ -48,7 +48,7 @@ export const asyncFilter = curry(
|
|||
);
|
||||
|
||||
/**
|
||||
* 异步归约
|
||||
* asynchronous reduction
|
||||
*/
|
||||
export const asyncReduce = curry(
|
||||
async <T, U>(
|
||||
|
|
@ -65,12 +65,12 @@ export const asyncReduce = curry(
|
|||
);
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
* Create a successful result
|
||||
*/
|
||||
export const success = <T>(data: T): Result<T> => ({ success: true, data });
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
* Create failed result
|
||||
*/
|
||||
export const failure = <E>(error: E): Result<never, E> => ({
|
||||
success: false,
|
||||
|
|
@ -78,7 +78,7 @@ export const failure = <E>(error: E): Result<never, E> => ({
|
|||
});
|
||||
|
||||
/**
|
||||
* 安全的异步操作包装
|
||||
* Safe asynchronous packaging
|
||||
*/
|
||||
export const tryCatch = async <T>(fn: () => Promise<T>): Promise<Result<T>> => {
|
||||
try {
|
||||
|
|
@ -90,7 +90,7 @@ export const tryCatch = async <T>(fn: () => Promise<T>): Promise<Result<T>> => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 同步版本的安全操作包装
|
||||
* Synchronized version of the security operation wrapper
|
||||
*/
|
||||
export const tryCatchSync = <T>(fn: () => T): Result<T> => {
|
||||
try {
|
||||
|
|
@ -102,7 +102,7 @@ export const tryCatchSync = <T>(fn: () => T): Result<T> => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 数组分块
|
||||
* Array chunking
|
||||
*/
|
||||
export const chunk = <T>(array: T[], size: number): T[][] => {
|
||||
const chunks: T[][] = [];
|
||||
|
|
@ -113,13 +113,13 @@ export const chunk = <T>(array: T[], size: number): T[][] => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 延迟执行
|
||||
* delayed execution
|
||||
*/
|
||||
export const delay = (ms: number): Promise<void> =>
|
||||
new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* 重试机制
|
||||
* retry mechanism
|
||||
*/
|
||||
export const retry = async <T>(
|
||||
fn: () => Promise<T>,
|
||||
|
|
@ -135,7 +135,7 @@ export const retry = async <T>(
|
|||
lastError = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
if (attempt < maxAttempts) {
|
||||
await delay(delayMs * attempt); // 指数退避
|
||||
await delay(delayMs * attempt); // exponential backoff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ export const retry = async <T>(
|
|||
};
|
||||
|
||||
/**
|
||||
* 深度合并对象
|
||||
* deep merge object
|
||||
*/
|
||||
export const deepMerge = <T extends Record<string, any>>(
|
||||
target: T,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { tryCatch } from './fp';
|
|||
import { Result } from '../types/index';
|
||||
|
||||
/**
|
||||
* 获取Git仓库中的所有已跟踪文件
|
||||
* Get all tracked files in the Git repository
|
||||
*/
|
||||
export const getGitTrackedFiles = async (
|
||||
root: string,
|
||||
|
|
@ -21,7 +21,7 @@ export const getGitTrackedFiles = async (
|
|||
};
|
||||
|
||||
/**
|
||||
* 获取Git仓库中的所有文件(包括未跟踪的)
|
||||
* Get all files in the Git repository (including untracked ones)
|
||||
*/
|
||||
export const getAllGitFiles = async (
|
||||
root: string,
|
||||
|
|
@ -29,20 +29,20 @@ export const getAllGitFiles = async (
|
|||
return tryCatch(async () => {
|
||||
const git = simpleGit(root);
|
||||
|
||||
// 获取已跟踪的文件
|
||||
// Get tracked files
|
||||
const trackedFiles = await git.raw(['ls-files']);
|
||||
const trackedFilesArray = trackedFiles
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.map(file => path.resolve(root, file));
|
||||
|
||||
// 获取未跟踪的文件
|
||||
// Get untracked files
|
||||
const status = await git.status();
|
||||
const untrackedFiles = status.not_added.map(file =>
|
||||
path.resolve(root, file),
|
||||
);
|
||||
|
||||
// 合并并去重
|
||||
// Merge and deduplicate
|
||||
const allFiles = [...new Set([...trackedFilesArray, ...untrackedFiles])];
|
||||
|
||||
return allFiles;
|
||||
|
|
@ -50,7 +50,7 @@ export const getAllGitFiles = async (
|
|||
};
|
||||
|
||||
/**
|
||||
* 检查目录是否是Git仓库
|
||||
* Check if the directory is a Git repository
|
||||
*/
|
||||
export const isGitRepository = async (
|
||||
root: string,
|
||||
|
|
@ -63,7 +63,7 @@ export const isGitRepository = async (
|
|||
};
|
||||
|
||||
/**
|
||||
* 获取Git仓库的根目录
|
||||
* Get the root directory of the Git repository
|
||||
*/
|
||||
export const getGitRoot = async (cwd: string): Promise<Result<string>> => {
|
||||
return tryCatch(async () => {
|
||||
|
|
@ -74,7 +74,7 @@ export const getGitRoot = async (cwd: string): Promise<Result<string>> => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 检查文件是否被Git忽略
|
||||
* Check if the file is ignored by Git
|
||||
*/
|
||||
export const isIgnoredByGit = async (
|
||||
root: string,
|
||||
|
|
@ -86,9 +86,9 @@ export const isIgnoredByGit = async (
|
|||
|
||||
try {
|
||||
await git.raw(['check-ignore', relativePath]);
|
||||
return true; // 文件被忽略
|
||||
return true; // File is ignored
|
||||
} catch {
|
||||
return false; // 文件未被忽略
|
||||
return false; // File is not ignored
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { SourceFileLanguage, CommentPattern } from '../types/index';
|
||||
|
||||
/**
|
||||
* 根据文件扩展名识别编程语言
|
||||
* Identify programming languages by file extension
|
||||
*/
|
||||
export const detectLanguage = (filePath: string): SourceFileLanguage => {
|
||||
const ext = filePath.toLowerCase().split('.').pop();
|
||||
|
|
@ -51,14 +51,14 @@ export const detectLanguage = (filePath: string): SourceFileLanguage => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 根据文件扩展名过滤文件
|
||||
* Filter files by file extension
|
||||
*/
|
||||
export const filterFilesByExtensions = (
|
||||
files: string[],
|
||||
extensions: string[]
|
||||
): string[] => {
|
||||
if (extensions.length === 0) {
|
||||
// 默认支持的文本文件扩展名
|
||||
// Default supported text file extensions
|
||||
const defaultExtensions = [
|
||||
'.ts', '.tsx', '.js', '.jsx', '.go', '.md', '.txt', '.json',
|
||||
'.yaml', '.yml', '.toml', '.ini', '.conf', '.config',
|
||||
|
|
@ -75,7 +75,7 @@ export const filterFilesByExtensions = (
|
|||
const lowerFile = file.toLowerCase();
|
||||
return extensions.some(ext => {
|
||||
const lowerExt = ext.toLowerCase();
|
||||
// 如果扩展名已经有点号,直接使用;否则添加点号
|
||||
// If the extension is already numbered, use it directly; otherwise, add a dot.
|
||||
const extWithDot = lowerExt.startsWith('.') ? lowerExt : `.${lowerExt}`;
|
||||
return lowerFile.endsWith(extWithDot);
|
||||
});
|
||||
|
|
@ -83,7 +83,7 @@ export const filterFilesByExtensions = (
|
|||
};
|
||||
|
||||
/**
|
||||
* 获取不同编程语言的注释模式
|
||||
* Obtain comment modes for different programming languages
|
||||
*/
|
||||
export const getCommentPatterns = (language: SourceFileLanguage): CommentPattern | null => {
|
||||
const commentPatterns: Record<SourceFileLanguage, CommentPattern> = {
|
||||
|
|
@ -108,33 +108,33 @@ export const getCommentPatterns = (language: SourceFileLanguage): CommentPattern
|
|||
multiEnd: /-->/g
|
||||
},
|
||||
text: {
|
||||
single: /^(.*)$/gm, // 文本文件每行都可能是注释
|
||||
single: /^(.*)$/gm, // Every line of a text file can be a comment
|
||||
multiStart: /^/g,
|
||||
multiEnd: /$/g
|
||||
},
|
||||
json: {
|
||||
single: /\/\/(.*)$/gm, // JSON通常不支持注释,但一些工具支持
|
||||
single: /\/\/(.*)$/gm, // JSON usually doesn't support comments, but some tools do
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
yaml: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /^$/g, // YAML不支持多行注释
|
||||
multiStart: /^$/g, // YAML does not support multi-line comments
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
toml: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /^$/g, // TOML不支持多行注释
|
||||
multiStart: /^$/g, // TOML does not support multi-line comments
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
ini: {
|
||||
single: /[;#](.*)$/gm, // INI文件支持 ; 和 # 作为注释
|
||||
multiStart: /^$/g, // INI不支持多行注释
|
||||
single: /[;#](.*)$/gm, // INI file support; and #as comments
|
||||
multiStart: /^$/g, // INI does not support multi-line comments
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
shell: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /^$/g, // Shell脚本不支持多行注释
|
||||
multiStart: /^$/g, // Shell scripts do not support multi-line comments
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
python: {
|
||||
|
|
@ -143,22 +143,22 @@ export const getCommentPatterns = (language: SourceFileLanguage): CommentPattern
|
|||
multiEnd: /[\s\S]*?"""/gm
|
||||
},
|
||||
css: {
|
||||
single: /^$/g, // CSS不支持单行注释
|
||||
single: /^$/g, // CSS does not support single-line comments
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
html: {
|
||||
single: /^$/g, // HTML不支持单行注释
|
||||
single: /^$/g, // HTML does not support single-line comments
|
||||
multiStart: /<!--/g,
|
||||
multiEnd: /-->/g
|
||||
},
|
||||
xml: {
|
||||
single: /^$/g, // XML不支持单行注释
|
||||
single: /^$/g, // XML does not support single-line comments
|
||||
multiStart: /<!--/g,
|
||||
multiEnd: /-->/g
|
||||
},
|
||||
php: {
|
||||
single: /(?:\/\/|#)(.*)$/gm, // PHP支持 // 和 # 作为单行注释
|
||||
single: /(?:\/\/|#)(.*)$/gm, // PHP supports//and #as single-line comments
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
|
|
@ -208,7 +208,7 @@ export const getCommentPatterns = (language: SourceFileLanguage): CommentPattern
|
|||
};
|
||||
|
||||
/**
|
||||
* 检查文件是否支持处理
|
||||
* Check if the file supports processing
|
||||
*/
|
||||
export const isSupportedFile = (filePath: string): boolean => {
|
||||
const language = detectLanguage(filePath);
|
||||
|
|
@ -216,7 +216,7 @@ export const isSupportedFile = (filePath: string): boolean => {
|
|||
};
|
||||
|
||||
/**
|
||||
* 获取文件的MIME类型(用于判断是否为文本文件)
|
||||
* Get the MIME type of the file (used to determine whether it is a text file)
|
||||
*/
|
||||
export const isTextFile = (filePath: string): boolean => {
|
||||
const textExtensions = [
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* 信号量并发控制类
|
||||
* semaphore concurrency control class
|
||||
*/
|
||||
export class Semaphore {
|
||||
private permits: number;
|
||||
|
|
@ -10,7 +10,7 @@ export class Semaphore {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取许可
|
||||
* Get permission
|
||||
*/
|
||||
async acquire(): Promise<void> {
|
||||
if (this.permits > 0) {
|
||||
|
|
@ -24,7 +24,7 @@ export class Semaphore {
|
|||
}
|
||||
|
||||
/**
|
||||
* 释放许可
|
||||
* release permission
|
||||
*/
|
||||
release(): void {
|
||||
this.permits++;
|
||||
|
|
@ -36,14 +36,14 @@ export class Semaphore {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取当前可用许可数
|
||||
* Get the number of currently available licenses
|
||||
*/
|
||||
available(): number {
|
||||
return this.permits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取等待队列长度
|
||||
* Get the waiting queue length
|
||||
*/
|
||||
waitingCount(): number {
|
||||
return this.waiting.length;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { URLSearchParams } from 'url';
|
|||
const debuglog = util.debuglog('signer');
|
||||
|
||||
/**
|
||||
* 签名参数接口
|
||||
* signature parameter interface
|
||||
*/
|
||||
export interface SignParams {
|
||||
headers?: Record<string, string>;
|
||||
|
|
@ -36,95 +36,95 @@ export interface SignParams {
|
|||
}
|
||||
|
||||
/**
|
||||
* 查询参数类型
|
||||
* query parameter type
|
||||
*/
|
||||
export type QueryParams = Record<string, string | string[] | undefined | null>;
|
||||
|
||||
/**
|
||||
* 请求头类型
|
||||
* request header type
|
||||
*/
|
||||
export type Headers = Record<string, string>;
|
||||
|
||||
/**
|
||||
* 翻译请求参数接口
|
||||
* translation request parameter interface
|
||||
*/
|
||||
export interface TranslateRequest {
|
||||
/** 源语言代码 */
|
||||
/** source language code */
|
||||
SourceLanguage: string;
|
||||
/** 目标语言代码 */
|
||||
/** target language code */
|
||||
TargetLanguage: string;
|
||||
/** 要翻译的文本列表 */
|
||||
/** List of texts to be translated */
|
||||
TextList: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译结果中的额外信息
|
||||
* Additional information in the translation results
|
||||
*/
|
||||
export interface TranslationExtra {
|
||||
/** 输入字符数 */
|
||||
/** Number of characters entered */
|
||||
input_characters: string;
|
||||
/** 源语言 */
|
||||
/** source language */
|
||||
source_language: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个翻译结果
|
||||
* Single translation result
|
||||
*/
|
||||
export interface TranslationItem {
|
||||
/** 翻译结果 */
|
||||
/** translation result */
|
||||
Translation: string;
|
||||
/** 检测到的源语言 */
|
||||
/** Detected source language */
|
||||
DetectedSourceLanguage: string;
|
||||
/** 额外信息 */
|
||||
/** Additional information */
|
||||
Extra: TranslationExtra;
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应元数据
|
||||
* Response metadata
|
||||
*/
|
||||
export interface ResponseMetadata {
|
||||
/** 请求ID */
|
||||
/** Request ID */
|
||||
RequestId: string;
|
||||
/** 操作名称 */
|
||||
/** operation name */
|
||||
Action: string;
|
||||
/** API版本 */
|
||||
/** API version */
|
||||
Version: string;
|
||||
/** 服务名称 */
|
||||
/** service name */
|
||||
Service: string;
|
||||
/** 区域 */
|
||||
/** area */
|
||||
Region: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 火山引擎翻译API响应接口
|
||||
* Volcano Engine Translation API Response Interface
|
||||
*/
|
||||
export interface VolcTranslateResponse {
|
||||
/** 翻译结果列表 */
|
||||
/** List of translation results */
|
||||
TranslationList: TranslationItem[];
|
||||
/** 响应元数据 */
|
||||
/** Response metadata */
|
||||
ResponseMetadata: ResponseMetadata;
|
||||
/** 响应元数据(备用字段) */
|
||||
/** Response metadata (alternate field) */
|
||||
ResponseMetaData?: ResponseMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译配置参数
|
||||
* translation configuration parameters
|
||||
*/
|
||||
export interface TranslateConfig {
|
||||
/** 访问密钥ID */
|
||||
/** Access Key ID */
|
||||
accessKeyId: string;
|
||||
/** 秘密访问密钥 */
|
||||
/** secret access key */
|
||||
secretAccessKey: string;
|
||||
/** 服务区域 */
|
||||
/** service area */
|
||||
region?: string;
|
||||
/** 源语言代码 */
|
||||
/** source language code */
|
||||
sourceLanguage?: string;
|
||||
/** 目标语言代码 */
|
||||
/** target language code */
|
||||
targetLanguage?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不参与加签过程的 header key
|
||||
* Header key that does not participate in the signature process
|
||||
*/
|
||||
const HEADER_KEYS_TO_IGNORE = new Set([
|
||||
'authorization',
|
||||
|
|
@ -136,10 +136,10 @@ const HEADER_KEYS_TO_IGNORE = new Set([
|
|||
]);
|
||||
|
||||
/**
|
||||
* 火山引擎翻译接口
|
||||
* @param textArray 要翻译的文本数组
|
||||
* @param config 翻译配置参数,如果不提供则使用默认配置
|
||||
* @returns 翻译响应结果
|
||||
* Volcano Engine Translation Interface
|
||||
* @Param textArray Array of text to translate
|
||||
* @Param config Translate configuration parameters, use default configuration if not provided
|
||||
* @Returns translation response result
|
||||
*/
|
||||
export async function translate(
|
||||
textArray: string[],
|
||||
|
|
@ -163,7 +163,7 @@ export async function translate(
|
|||
const requestBodyString = JSON.stringify(requestBody);
|
||||
const signParams: SignParams = {
|
||||
headers: {
|
||||
// x-date header 是必传的
|
||||
// The x-date header is required
|
||||
'X-Date': getDateTimeNow(),
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
|
|
@ -179,7 +179,7 @@ export async function translate(
|
|||
bodySha: getBodySha(requestBodyString),
|
||||
};
|
||||
|
||||
// 正规化 query object, 防止串化后出现 query 值为 undefined 情况
|
||||
// Normalize the query object to prevent the query value from being undefined after serialization
|
||||
if (signParams.query) {
|
||||
for (const [key, val] of Object.entries(signParams.query)) {
|
||||
if (val === undefined || val === null) {
|
||||
|
|
@ -216,7 +216,7 @@ export async function translate(
|
|||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* generate signature
|
||||
*/
|
||||
function sign(params: SignParams): string {
|
||||
const {
|
||||
|
|
@ -233,7 +233,7 @@ function sign(params: SignParams): string {
|
|||
} = params;
|
||||
const datetime = headers['X-Date'];
|
||||
const date = datetime.substring(0, 8); // YYYYMMDD
|
||||
// 创建正规化请求
|
||||
// Create a regularization request
|
||||
const [signedHeaders, canonicalHeaders] = getSignHeaders(
|
||||
headers,
|
||||
needSignHeaderKeys,
|
||||
|
|
@ -247,14 +247,14 @@ function sign(params: SignParams): string {
|
|||
bodySha || hash(''),
|
||||
].join('\n');
|
||||
const credentialScope = [date, region, serviceName, 'request'].join('/');
|
||||
// 创建签名字符串
|
||||
// Create signature string
|
||||
const stringToSign = [
|
||||
'HMAC-SHA256',
|
||||
datetime,
|
||||
credentialScope,
|
||||
hash(canonicalRequest),
|
||||
].join('\n');
|
||||
// 计算签名
|
||||
// Compute signature
|
||||
const kDate = hmac(secretAccessKey, date);
|
||||
const kRegion = hmac(kDate, region);
|
||||
const kService = hmac(kRegion, serviceName);
|
||||
|
|
@ -275,21 +275,21 @@ function sign(params: SignParams): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* HMAC-SHA256 加密
|
||||
* HMAC-SHA256 encryption
|
||||
*/
|
||||
function hmac(secret: string | Buffer, s: string): Buffer {
|
||||
return crypto.createHmac('sha256', secret).update(s, 'utf8').digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA256 哈希
|
||||
* SHA256 hash
|
||||
*/
|
||||
function hash(s: string): string {
|
||||
return crypto.createHash('sha256').update(s, 'utf8').digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询参数转字符串
|
||||
* Query parameter to string
|
||||
*/
|
||||
function queryParamsToString(params: QueryParams): string {
|
||||
return Object.keys(params)
|
||||
|
|
@ -313,7 +313,7 @@ function queryParamsToString(params: QueryParams): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取签名头
|
||||
* Get signature header
|
||||
*/
|
||||
function getSignHeaders(
|
||||
originHeaders: Headers,
|
||||
|
|
@ -324,14 +324,14 @@ function getSignHeaders(
|
|||
}
|
||||
|
||||
let h = Object.keys(originHeaders);
|
||||
// 根据 needSignHeaders 过滤
|
||||
// Filter by needSignHeaders
|
||||
if (Array.isArray(needSignHeaders)) {
|
||||
const needSignSet = new Set(
|
||||
[...needSignHeaders, 'x-date', 'host'].map(k => k.toLowerCase()),
|
||||
);
|
||||
h = h.filter(k => needSignSet.has(k.toLowerCase()));
|
||||
}
|
||||
// 根据 ignore headers 过滤
|
||||
// Filter by ignoring headers
|
||||
h = h.filter(k => !HEADER_KEYS_TO_IGNORE.has(k.toLowerCase()));
|
||||
const signedHeaderKeys = h
|
||||
.slice()
|
||||
|
|
@ -346,7 +346,7 @@ function getSignHeaders(
|
|||
}
|
||||
|
||||
/**
|
||||
* URI 转义
|
||||
* URI escape
|
||||
*/
|
||||
function uriEscape(str: string): string {
|
||||
try {
|
||||
|
|
@ -364,7 +364,7 @@ function uriEscape(str: string): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间格式化字符串
|
||||
* Get the current time format string
|
||||
*/
|
||||
export function getDateTimeNow(): string {
|
||||
const now = new Date();
|
||||
|
|
@ -372,7 +372,7 @@ export function getDateTimeNow(): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取 body 的 SHA256 值
|
||||
* Get the SHA256 value of the body
|
||||
*/
|
||||
export function getBodySha(body: string | URLSearchParams | Buffer): string {
|
||||
const hashInstance = crypto.createHash('sha256');
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const { getChangedPackages } = require('./utils')
|
|||
|
||||
|
||||
/**
|
||||
* 针对不同类型的 commit prefix message
|
||||
* For different types of commit prefix messages
|
||||
*/
|
||||
const typesConfig = [
|
||||
{ value: 'feat', name: 'A new feature' },
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function getChangedPackages(changedFiles) {
|
|||
const lookup = rushConfiguration.getProjectLookupForRoot(rushJsonFolder)
|
||||
for (const file of changedFiles) {
|
||||
const project = lookup.findChildPath(file)
|
||||
// 如果没找到注册的包信息,则认为是通用文件更改
|
||||
// If the registered package information is not found, it is considered a generic file change
|
||||
const packageName = project?.packageName || 'misc'
|
||||
if (!changedPackages.has(packageName)) {
|
||||
changedPackages.add(packageName)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ module.exports = {
|
|||
.map(f => path.relative(projectFolder, f))
|
||||
.join(' ');
|
||||
// TSESTREE_SINGLE_RUN doc https://typescript-eslint.io/packages/parser/#allowautomaticsingleruninference
|
||||
// 切换到项目文件夹,并运行 ESLint 命令
|
||||
// Switch to the project folder and run the ESLint command
|
||||
const cmd = [
|
||||
`cd ${projectFolder}`,
|
||||
`TSESTREE_SINGLE_RUN=true eslint --fix --cache ${filesToCheck} --no-error-on-unmatched-pattern`,
|
||||
|
|
@ -47,8 +47,8 @@ module.exports = {
|
|||
}
|
||||
|
||||
return [
|
||||
// 这里不能直接返回 eslintCmds 数组,因为 lint-staged 会依次串行执行每个命令
|
||||
// 而 concurrently 会并行执行多个命令
|
||||
// The eslintCmds array cannot be returned directly here, because lint-staged executes each command in sequence
|
||||
// And concurrently execute multiple commands in parallel
|
||||
`concurrently --max-process 8 --names ${eslintCmds
|
||||
.map(r => `${r.name}`)
|
||||
.join(',')} --kill-others-on-fail ${eslintCmds
|
||||
|
|
@ -57,7 +57,7 @@ module.exports = {
|
|||
];
|
||||
},
|
||||
'**/*.{less,scss,css}': files => {
|
||||
// 暂时只修复,不报错卡点
|
||||
// It is only repaired for the time being, and no errors are reported.
|
||||
return [`stylelint ${files.join(' ')} --fix || exit 0`];
|
||||
},
|
||||
'**/package.json': async files => {
|
||||
|
|
@ -68,7 +68,7 @@ module.exports = {
|
|||
if (!filesToLint) return [];
|
||||
return [
|
||||
// https://eslint.org/docs/latest/flags/#enable-feature-flags-with-the-cli
|
||||
// eslint v9默认从cwd找配置,这里需要使用 unstable_config_lookup_from_file 配置,否则会报错
|
||||
// Eslint v9 finds the configuration from cwd by default. You need to use unstable_config_lookup_from_file configuration here, otherwise an error will be reported.
|
||||
`eslint --cache ${filesToLint} --flag unstable_config_lookup_from_file`,
|
||||
`prettier ${filesToLint} --write`,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const getRushConfiguration = (function () {
|
|||
};
|
||||
})();
|
||||
|
||||
// 获取变更文件所在的项目路径
|
||||
// Get the project path where the change file is located
|
||||
function withProjectFolder(changedFiles) {
|
||||
const projectFolders = [];
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ function withProjectFolder(changedFiles) {
|
|||
|
||||
for (const file of changedFiles) {
|
||||
const project = lookup.findChildPath(path.relative(rushJsonFolder, file));
|
||||
// 忽略不在 rush.json 内定义的项目
|
||||
// Ignore items not defined in rush.json
|
||||
if (project) {
|
||||
const projectFolder = project?.projectFolder ?? rushJsonFolder;
|
||||
const packageName = project?.packageName;
|
||||
|
|
@ -72,7 +72,7 @@ async function excludeIgnoredFiles(changedFiles) {
|
|||
}
|
||||
}
|
||||
|
||||
// 获取发生变更的项目路径
|
||||
// Get the project path that changed
|
||||
function getChangedProjects(changedFiles) {
|
||||
const changedProjectFolders = new Set();
|
||||
const changedProjects = new Set();
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
"$schema": "https://json-schema.bytedance.net/rush-plugins/rush-build-cache-plugin-options.schema.json",
|
||||
"key": "Wek6C-xcBtNNroagxV305NFAcmXOriQRps64",
|
||||
"envList": [
|
||||
// BUILD_TYPE 在 SCM 里面应该是 offline、test、online [Witty]
|
||||
// BUILD_TYPE in SCM should be offline, test, online [Witty]
|
||||
"BUILD_TYPE",
|
||||
// bot studio 构建时需要根据 region 环境区分打包
|
||||
// When building bot studio, it needs to be packaged according to the region environment
|
||||
"REGION",
|
||||
// 用于区分 inhouse/release 环境
|
||||
// Used to differentiate inhouse/release environments
|
||||
"CUSTOM_VERSION",
|
||||
"CI",
|
||||
"CI_LIGHTING",
|
||||
// coze cli 构建时需要区分 inhouse/release 环境
|
||||
// Coze cli builds need to differentiate between inhouse/release environments
|
||||
"PUBLIC_INHOUSE"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
brokerClusterName = DefaultCluster
|
||||
# 禁用磁盘使用率告警
|
||||
# Disable Disk Usage Alerts
|
||||
# min:1, max:95
|
||||
diskMaxUsedSpaceRatio=95.0
|
||||
|
||||
|
|
@ -24,5 +24,5 @@ deleteWhen = 04
|
|||
fileReservedTime = 48
|
||||
brokerRole = ASYNC_MASTER
|
||||
flushDiskType = ASYNC_FLUSH
|
||||
# 使用宿主机IP地址,确保客户端可以直接访问
|
||||
# Use the host Internet Protocol Address to ensure direct access from the client side
|
||||
brokerIP1 = 127.0.0.1
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ BUILD_BRANCH=opencoze-local rush rebuild -o @coze-studio/app --verbose
|
|||
|
||||
popd
|
||||
|
||||
# 复制构建产物到后端静态目录
|
||||
# Copy bundle to backend static directory
|
||||
echo -e "${YELLOW}正在复制构建产物到后端静态目录...${NC}"
|
||||
BACKEND_STATIC_DIR="${SCRIPT_DIR}/../backend/static"
|
||||
BIN_STATIC_DIR="${SCRIPT_DIR}/../bin/resources/static"
|
||||
|
|
@ -27,7 +27,7 @@ rm -rf "${BIN_STATIC_DIR}"
|
|||
mkdir -p "${BACKEND_STATIC_DIR}"
|
||||
mkdir -p "${BIN_STATIC_DIR}"
|
||||
|
||||
# 清空目标目录并复制新的构建产物
|
||||
# Clear the target directory and copy the new bundle
|
||||
rm -rf "${BACKEND_STATIC_DIR}"/*
|
||||
cp -r "${FRONTEND_DIST_DIR}"/* "${BACKEND_STATIC_DIR}/"
|
||||
cp -r "${FRONTEND_DIST_DIR}"/* "${BIN_STATIC_DIR}/"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ FRONTEND_DIR="${1:-${SCRIPT_DIR}/../frontend}"
|
|||
|
||||
set -ex
|
||||
|
||||
# 设置颜色变量
|
||||
# Set color variables
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
|
|
@ -14,7 +14,7 @@ NC='\033[0m' # No Color
|
|||
pushd "${FRONTEND_DIR}"
|
||||
echo "正在进入前端目录: ${FRONTEND_DIR}"
|
||||
|
||||
# 检查 Node.js 是否安装
|
||||
# Check if Node.js is installed
|
||||
echo -e "正在检查 Node.js 是否已安装..."
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo -e "${RED}错误: 未检测到 Node.js${NC}"
|
||||
|
|
@ -28,7 +28,7 @@ else
|
|||
fi
|
||||
|
||||
|
||||
# 检查 Rush 是否安装
|
||||
# Check if Rush is installed
|
||||
echo -e "正在检查 Rush 是否已安装..."
|
||||
if ! command -v rush &> /dev/null; then
|
||||
echo -e "${YELLOW}未检测到 Rush,正在为您安装...${NC}"
|
||||
|
|
@ -48,6 +48,6 @@ echo -e "${GREEN}依赖安装完成!${NC}"
|
|||
|
||||
|
||||
# echo -e "${NC}"
|
||||
# echo -e "${GREEN}构建完成!${NC}"
|
||||
# Echo -e "${GREEN} build complete! ${NC}"
|
||||
|
||||
popd
|
||||
|
|
@ -144,7 +144,7 @@ func CreateMySQLWhiteList(mysqlInstanceID, ts string) (string, error) {
|
|||
ProjectName: volcengine.String(projectName),
|
||||
}
|
||||
|
||||
// 复制代码运行示例,请自行打印API返回值。
|
||||
// Copy the code to run the example, please print the API return value yourself.
|
||||
resp, err := svc.CreateAllowList(createAllowListInput)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
@ -164,7 +164,7 @@ func AssociateMySQLWhiteList(mysqlInstanceID, whitelistID string) error {
|
|||
InstanceIds: volcengine.StringSlice([]string{mysqlInstanceID}),
|
||||
}
|
||||
|
||||
// 复制代码运行示例,请自行打印API返回值。
|
||||
// Copy the code to run the example, please print the API return value yourself.
|
||||
_, err := svc.AssociateAllowList(associateAllowListInput)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ func CheckSafeGroupStatus(sgID string) {
|
|||
SecurityGroupId: volcengine.String(sgID),
|
||||
}
|
||||
|
||||
// 复制代码运行示例,请自行打印API返回值。
|
||||
// Copy the code to run the example, please print the API return value yourself.
|
||||
resp, err := svc.DescribeSecurityGroupAttributes(describeSecurityGroupAttributesInput)
|
||||
if err != nil {
|
||||
fmt.Printf("[SafeGroup] will retry get safe group = %s failed, err= %s\n", sgID, err.Error())
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ func CreateRocketMQAccessKey(instanceID string) (string, string, error) {
|
|||
InstanceId: volcengine.String(instanceID),
|
||||
}
|
||||
|
||||
// 复制代码运行示例,请自行打印API返回值。
|
||||
// Copy the code to run the example, please print the API return value yourself.
|
||||
_, err = svc.CreateAccessKey(createAccessKeyInput)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
|
|
@ -245,7 +245,7 @@ func CreateRocketMQTopic(ak, instanceID string) error {
|
|||
TopicName: volcengine.String(topicName),
|
||||
}
|
||||
|
||||
// 复制代码运行示例,请自行打印API返回值。
|
||||
// Copy the code to run the example, please print the API return value yourself.
|
||||
_, err = svc.CreateTopic(createTopicInput)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
Loading…
Reference in New Issue