czon 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/pipeline.js +3 -0
- package/dist/process/translateCategories.js +84 -0
- package/dist/ssg/IndexPage.js +4 -1
- package/dist/ssg/components/ContentMeta.js +2 -1
- package/dist/ssg/components/Navigator.js +2 -1
- package/dist/ssg/utils/getCategoryDisplayName.js +9 -0
- package/dist/utils/git.js +13 -12
- package/package.json +1 -1
package/dist/build/pipeline.js
CHANGED
|
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const metadata_1 = require("../metadata");
|
|
40
40
|
const paths_1 = require("../paths");
|
|
41
41
|
const category_1 = require("../process/category");
|
|
42
|
+
const translateCategories_1 = require("../process/translateCategories");
|
|
42
43
|
const enhanceMarkdownSource_1 = require("../process/enhanceMarkdownSource");
|
|
43
44
|
const extractMetadataByAI_1 = require("../process/extractMetadataByAI");
|
|
44
45
|
const processTranslations_1 = require("../process/processTranslations");
|
|
@@ -103,6 +104,8 @@ async function buildPipeline(options) {
|
|
|
103
104
|
await (0, extractMetadataByAI_1.extractMetadataByAI)();
|
|
104
105
|
// 提取分类信息
|
|
105
106
|
await (0, category_1.processExtractCategory)();
|
|
107
|
+
// 翻译分类
|
|
108
|
+
await (0, translateCategories_1.processTranslateCategories)();
|
|
106
109
|
// 存储母语文件,并进行内容增强预处理
|
|
107
110
|
await (0, enhanceMarkdownSource_1.storeNativeFiles)();
|
|
108
111
|
// 处理翻译
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processTranslateCategories = void 0;
|
|
4
|
+
const metadata_1 = require("../metadata");
|
|
5
|
+
const openai_1 = require("../services/openai");
|
|
6
|
+
const processTranslateCategories = async () => {
|
|
7
|
+
const langs = metadata_1.MetaData.options.langs || [];
|
|
8
|
+
if (langs.length === 0) {
|
|
9
|
+
console.info('ℹ️ No target languages configured, skipping category translation.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (!metadata_1.MetaData.categoryTranslations) {
|
|
13
|
+
metadata_1.MetaData.categoryTranslations = {};
|
|
14
|
+
}
|
|
15
|
+
const allCategories = [
|
|
16
|
+
...new Set(metadata_1.MetaData.files.map(f => f.category).filter(Boolean)),
|
|
17
|
+
];
|
|
18
|
+
if (allCategories.length === 0) {
|
|
19
|
+
console.info('ℹ️ No categories found, skipping category translation.');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const categoriesNeedingTranslation = allCategories.filter(cat => {
|
|
23
|
+
const translations = metadata_1.MetaData.categoryTranslations[cat];
|
|
24
|
+
return !translations || langs.some(lang => !translations[lang]);
|
|
25
|
+
});
|
|
26
|
+
if (categoriesNeedingTranslation.length === 0) {
|
|
27
|
+
console.info('ℹ️ All categories already have translations, skipping.');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.info(`🌐 Translating ${categoriesNeedingTranslation.length} categories to ${langs.length} languages...`);
|
|
31
|
+
const response = await (0, openai_1.completeMessages)([
|
|
32
|
+
{
|
|
33
|
+
role: 'system',
|
|
34
|
+
content: [
|
|
35
|
+
'你是一个专业的翻译助手。',
|
|
36
|
+
'请将给定的分类名称翻译成指定的目标语言。',
|
|
37
|
+
'翻译应该自然、准确,符合目标语言的表达习惯。',
|
|
38
|
+
'分类名称通常是简短的词组,翻译时保持简洁。',
|
|
39
|
+
'',
|
|
40
|
+
'请以 JSON 格式返回,格式如下:',
|
|
41
|
+
'{',
|
|
42
|
+
' "translations": {',
|
|
43
|
+
' "CategoryKey": { "zh-Hans": "中文翻译", "en-US": "English" }',
|
|
44
|
+
' }',
|
|
45
|
+
'}',
|
|
46
|
+
].join('\n'),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
role: 'user',
|
|
50
|
+
content: [
|
|
51
|
+
`需要翻译的分类: ${JSON.stringify(categoriesNeedingTranslation)}`,
|
|
52
|
+
`目标语言: ${JSON.stringify(langs)}`,
|
|
53
|
+
].join('\n'),
|
|
54
|
+
},
|
|
55
|
+
], { response_format: { type: 'json_object' }, task_id: 'translate-categories' });
|
|
56
|
+
const json = response.choices[0].message.content;
|
|
57
|
+
const parsed = JSON.parse(json);
|
|
58
|
+
if (parsed.translations) {
|
|
59
|
+
for (const [category, translations] of Object.entries(parsed.translations)) {
|
|
60
|
+
if (!metadata_1.MetaData.categoryTranslations[category]) {
|
|
61
|
+
metadata_1.MetaData.categoryTranslations[category] = {};
|
|
62
|
+
}
|
|
63
|
+
for (const [lang, translation] of Object.entries(translations)) {
|
|
64
|
+
metadata_1.MetaData.categoryTranslations[category][lang] = translation;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const stillMissingTranslation = allCategories.filter(cat => {
|
|
69
|
+
const translations = metadata_1.MetaData.categoryTranslations[cat];
|
|
70
|
+
return !translations || langs.some(lang => !translations[lang]);
|
|
71
|
+
});
|
|
72
|
+
if (stillMissingTranslation.length > 0) {
|
|
73
|
+
const details = stillMissingTranslation.map(cat => {
|
|
74
|
+
const missing = langs.filter(lang => !metadata_1.MetaData.categoryTranslations[cat]?.[lang]);
|
|
75
|
+
return ` - "${cat}": 缺少 ${missing.join(', ')}`;
|
|
76
|
+
});
|
|
77
|
+
console.error(`❌ 以下分类缺少翻译:\n${details.join('\n')}`);
|
|
78
|
+
console.error('这是一个可重试的错误,请重新运行命令。');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
console.info('✅ Category translations completed.');
|
|
82
|
+
};
|
|
83
|
+
exports.processTranslateCategories = processTranslateCategories;
|
|
84
|
+
//# sourceMappingURL=translateCategories.js.map
|
package/dist/ssg/IndexPage.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.IndexPage = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
8
|
const sortBy_1 = require("../utils/sortBy");
|
|
9
|
+
const getCategoryDisplayName_1 = require("./utils/getCategoryDisplayName");
|
|
9
10
|
const Analytics_1 = require("./components/Analytics");
|
|
10
11
|
const ContentMeta_1 = require("./components/ContentMeta");
|
|
11
12
|
const CZONFooter_1 = require("./components/CZONFooter");
|
|
@@ -54,7 +55,9 @@ const IndexPage = props => {
|
|
|
54
55
|
react_1.default.createElement("div", null,
|
|
55
56
|
react_1.default.createElement("span", { className: "font-bold" }),
|
|
56
57
|
react_1.default.createElement("div", { className: "mb-6 gap-6 flex flex-wrap" }, allCategories.map(category => {
|
|
57
|
-
const title = category
|
|
58
|
+
const title = category
|
|
59
|
+
? (0, getCategoryDisplayName_1.getCategoryDisplayName)(props.ctx.site, category, props.lang)
|
|
60
|
+
: 'All';
|
|
58
61
|
const link = category ? `categories_${category}.html` : 'index.html';
|
|
59
62
|
const isActive = category === props.category;
|
|
60
63
|
const articlesCount = category
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ContentMeta = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const getCategoryDisplayName_1 = require("../utils/getCategoryDisplayName");
|
|
8
9
|
const TagList_1 = require("./TagList");
|
|
9
10
|
const ContentMeta = props => {
|
|
10
11
|
const content = props.ctx.contents.find(c => c.file === props.file && c.lang === props.lang);
|
|
@@ -17,7 +18,7 @@ const ContentMeta = props => {
|
|
|
17
18
|
return (react_1.default.createElement("header", { className: "content-header mb-4 pb-2 border-b" },
|
|
18
19
|
react_1.default.createElement("h2", { className: "text-2xl font-bold mb-2" },
|
|
19
20
|
react_1.default.createElement("a", { href: `${props.file.metadata?.slug}.html` }, title)),
|
|
20
|
-
react_1.default.createElement("p", { className: "font-semibold" }, category),
|
|
21
|
+
react_1.default.createElement("p", { className: "font-semibold" }, (0, getCategoryDisplayName_1.getCategoryDisplayName)(props.ctx.site, category, props.lang)),
|
|
21
22
|
react_1.default.createElement("blockquote", null, summary),
|
|
22
23
|
react_1.default.createElement("div", null,
|
|
23
24
|
"\uD83D\uDCC5 ",
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Navigator = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const getCategoryDisplayName_1 = require("../utils/getCategoryDisplayName");
|
|
8
9
|
const sortBy_1 = require("../../utils/sortBy");
|
|
9
10
|
const Navigator = props => {
|
|
10
11
|
const categories = [...new Set(props.ctx.site.files.map(f => f.category))];
|
|
@@ -19,7 +20,7 @@ const Navigator = props => {
|
|
|
19
20
|
if (filesInCategory.length === 0)
|
|
20
21
|
return null;
|
|
21
22
|
return (react_1.default.createElement("div", { key: categoryKey },
|
|
22
|
-
react_1.default.createElement("li", { className: "nav-item font-bold", key: categoryKey }, category || '--'),
|
|
23
|
+
react_1.default.createElement("li", { className: "nav-item font-bold", key: categoryKey }, (0, getCategoryDisplayName_1.getCategoryDisplayName)(props.ctx.site, category, props.lang) || '--'),
|
|
23
24
|
filesInCategory.map(file => {
|
|
24
25
|
const link = file.metadata.slug + '.html';
|
|
25
26
|
const isActive = props.file === file;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCategoryDisplayName = getCategoryDisplayName;
|
|
4
|
+
function getCategoryDisplayName(site, category, lang) {
|
|
5
|
+
if (!category)
|
|
6
|
+
return '';
|
|
7
|
+
return site.categoryTranslations?.[category]?.[lang] || category;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=getCategoryDisplayName.js.map
|
package/dist/utils/git.js
CHANGED
|
@@ -15,21 +15,22 @@ exports.isUntracked = isUntracked;
|
|
|
15
15
|
const child_process_1 = require("child_process");
|
|
16
16
|
/**
|
|
17
17
|
* Execute a git command and return the output
|
|
18
|
+
* Uses spawnSync to avoid shell parsing issues with special characters
|
|
18
19
|
* @throws Error if the command fails
|
|
19
20
|
*/
|
|
20
21
|
function execGit(args, options) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
const result = (0, child_process_1.spawnSync)('git', args, {
|
|
23
|
+
cwd: options?.cwd ?? process.cwd(),
|
|
24
|
+
encoding: 'utf-8',
|
|
25
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
26
|
+
});
|
|
27
|
+
if (result.error) {
|
|
28
|
+
throw result.error;
|
|
29
|
+
}
|
|
30
|
+
if (result.status !== 0) {
|
|
31
|
+
throw new Error(result.stderr || `git command failed with exit code ${result.status}`);
|
|
32
|
+
}
|
|
33
|
+
return (result.stdout || '').trim();
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
36
|
* Check if the current directory is a Git repository
|