@termdock/twlint 1.0.0

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.
Files changed (61) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +185 -0
  3. package/TRADEMARK.md +39 -0
  4. package/bin/twlint +6 -0
  5. package/dist/cli.d.ts +10 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +142 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/core/config-loader.d.ts +3 -0
  10. package/dist/core/config-loader.d.ts.map +1 -0
  11. package/dist/core/config-loader.js +54 -0
  12. package/dist/core/config-loader.js.map +1 -0
  13. package/dist/core/config-schema.d.ts +12 -0
  14. package/dist/core/config-schema.d.ts.map +1 -0
  15. package/dist/core/config-schema.js +64 -0
  16. package/dist/core/config-schema.js.map +1 -0
  17. package/dist/core/dictionary-manager.d.ts +14 -0
  18. package/dist/core/dictionary-manager.d.ts.map +1 -0
  19. package/dist/core/dictionary-manager.js +94 -0
  20. package/dist/core/dictionary-manager.js.map +1 -0
  21. package/dist/core/linter.d.ts +31 -0
  22. package/dist/core/linter.d.ts.map +1 -0
  23. package/dist/core/linter.js +182 -0
  24. package/dist/core/linter.js.map +1 -0
  25. package/dist/core/matching/match-strategies.d.ts +26 -0
  26. package/dist/core/matching/match-strategies.d.ts.map +1 -0
  27. package/dist/core/matching/match-strategies.js +170 -0
  28. package/dist/core/matching/match-strategies.js.map +1 -0
  29. package/dist/core/position-mapper.d.ts +31 -0
  30. package/dist/core/position-mapper.d.ts.map +1 -0
  31. package/dist/core/position-mapper.js +101 -0
  32. package/dist/core/position-mapper.js.map +1 -0
  33. package/dist/core/rules/context-sensitive.d.ts +17 -0
  34. package/dist/core/rules/context-sensitive.d.ts.map +1 -0
  35. package/dist/core/rules/context-sensitive.js +154 -0
  36. package/dist/core/rules/context-sensitive.js.map +1 -0
  37. package/dist/core/rules/mainland-terms.d.ts +11 -0
  38. package/dist/core/rules/mainland-terms.d.ts.map +1 -0
  39. package/dist/core/rules/mainland-terms.js +45 -0
  40. package/dist/core/rules/mainland-terms.js.map +1 -0
  41. package/dist/core/rules/simplified-chars.d.ts +10 -0
  42. package/dist/core/rules/simplified-chars.d.ts.map +1 -0
  43. package/dist/core/rules/simplified-chars.js +51 -0
  44. package/dist/core/rules/simplified-chars.js.map +1 -0
  45. package/dist/dictionaries/academic.json +141 -0
  46. package/dist/dictionaries/core.json +1491 -0
  47. package/dist/dictionaries/extended.json +141 -0
  48. package/dist/dictionaries/index.json +21 -0
  49. package/dist/formatters/json.d.ts +5 -0
  50. package/dist/formatters/json.d.ts.map +1 -0
  51. package/dist/formatters/json.js +18 -0
  52. package/dist/formatters/json.js.map +1 -0
  53. package/dist/formatters/stylish.d.ts +5 -0
  54. package/dist/formatters/stylish.d.ts.map +1 -0
  55. package/dist/formatters/stylish.js +45 -0
  56. package/dist/formatters/stylish.js.map +1 -0
  57. package/dist/types.d.ts +73 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +2 -0
  60. package/dist/types.js.map +1 -0
  61. package/package.json +77 -0
@@ -0,0 +1,31 @@
1
+ export interface TextPosition {
2
+ line: number;
3
+ column: number;
4
+ offset: number;
5
+ }
6
+ export interface PositionMapping {
7
+ originalPosition: TextPosition;
8
+ convertedPosition: TextPosition;
9
+ }
10
+ export declare class PositionMapper {
11
+ private mappings;
12
+ private originalText;
13
+ private convertedText;
14
+ constructor(originalText: string, convertedText: string);
15
+ private buildMappings;
16
+ /**
17
+ * Map position from converted text back to original text
18
+ */
19
+ mapToOriginal(convertedLine: number, convertedColumn: number): TextPosition;
20
+ /**
21
+ * Map position from original text to converted text
22
+ */
23
+ mapToConverted(originalLine: number, originalColumn: number): TextPosition;
24
+ private getOffsetFromPosition;
25
+ /**
26
+ * Simple mapping for cases where text lengths are similar
27
+ * Falls back to this when detailed mapping is not needed
28
+ */
29
+ static simpleMap(originalLine: number, originalColumn: number): TextPosition;
30
+ }
31
+ //# sourceMappingURL=position-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position-mapper.d.ts","sourceRoot":"","sources":["../../src/core/position-mapper.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,YAAY,CAAA;IAC9B,iBAAiB,EAAE,YAAY,CAAA;CAChC;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,aAAa,CAAQ;gBAEjB,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAMvD,OAAO,CAAC,aAAa;IAwCrB;;OAEG;IACH,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,YAAY;IAmB3E;;OAEG;IACH,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY;IAkB1E,OAAO,CAAC,qBAAqB;IAe7B;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY;CAO7E"}
@@ -0,0 +1,101 @@
1
+ export class PositionMapper {
2
+ mappings = [];
3
+ originalText;
4
+ convertedText;
5
+ constructor(originalText, convertedText) {
6
+ this.originalText = originalText;
7
+ this.convertedText = convertedText;
8
+ this.buildMappings();
9
+ }
10
+ buildMappings() {
11
+ // Build character-level mapping between original and converted text
12
+ // This handles cases where character conversion changes text length
13
+ const originalLines = this.originalText.split('\n');
14
+ const convertedLines = this.convertedText.split('\n');
15
+ let originalOffset = 0;
16
+ let convertedOffset = 0;
17
+ for (let lineIndex = 0; lineIndex < Math.max(originalLines.length, convertedLines.length); lineIndex++) {
18
+ const originalLine = originalLines[lineIndex] || '';
19
+ const convertedLine = convertedLines[lineIndex] || '';
20
+ // Map each character position in the line
21
+ const maxLength = Math.max(originalLine.length, convertedLine.length);
22
+ for (let charIndex = 0; charIndex < maxLength; charIndex++) {
23
+ if (charIndex < originalLine.length && charIndex < convertedLine.length) {
24
+ this.mappings.push({
25
+ originalPosition: {
26
+ line: lineIndex + 1,
27
+ column: charIndex + 1,
28
+ offset: originalOffset + charIndex
29
+ },
30
+ convertedPosition: {
31
+ line: lineIndex + 1,
32
+ column: charIndex + 1,
33
+ offset: convertedOffset + charIndex
34
+ }
35
+ });
36
+ }
37
+ }
38
+ // Account for line breaks
39
+ originalOffset += originalLine.length + 1; // +1 for \n
40
+ convertedOffset += convertedLine.length + 1;
41
+ }
42
+ }
43
+ /**
44
+ * Map position from converted text back to original text
45
+ */
46
+ mapToOriginal(convertedLine, convertedColumn) {
47
+ // Find the best matching position in original text
48
+ const convertedOffset = this.getOffsetFromPosition(this.convertedText, convertedLine, convertedColumn);
49
+ // Find closest mapping
50
+ let bestMapping = this.mappings[0];
51
+ let minDistance = Math.abs(bestMapping.convertedPosition.offset - convertedOffset);
52
+ for (const mapping of this.mappings) {
53
+ const distance = Math.abs(mapping.convertedPosition.offset - convertedOffset);
54
+ if (distance < minDistance) {
55
+ minDistance = distance;
56
+ bestMapping = mapping;
57
+ }
58
+ }
59
+ return bestMapping.originalPosition;
60
+ }
61
+ /**
62
+ * Map position from original text to converted text
63
+ */
64
+ mapToConverted(originalLine, originalColumn) {
65
+ const originalOffset = this.getOffsetFromPosition(this.originalText, originalLine, originalColumn);
66
+ // Find closest mapping
67
+ let bestMapping = this.mappings[0];
68
+ let minDistance = Math.abs(bestMapping.originalPosition.offset - originalOffset);
69
+ for (const mapping of this.mappings) {
70
+ const distance = Math.abs(mapping.originalPosition.offset - originalOffset);
71
+ if (distance < minDistance) {
72
+ minDistance = distance;
73
+ bestMapping = mapping;
74
+ }
75
+ }
76
+ return bestMapping.convertedPosition;
77
+ }
78
+ getOffsetFromPosition(text, line, column) {
79
+ const lines = text.split('\n');
80
+ let offset = 0;
81
+ // Add lengths of previous lines
82
+ for (let i = 0; i < line - 1 && i < lines.length; i++) {
83
+ offset += lines[i].length + 1; // +1 for \n
84
+ }
85
+ // Add column position in current line
86
+ offset += Math.min(column - 1, lines[line - 1]?.length || 0);
87
+ return offset;
88
+ }
89
+ /**
90
+ * Simple mapping for cases where text lengths are similar
91
+ * Falls back to this when detailed mapping is not needed
92
+ */
93
+ static simpleMap(originalLine, originalColumn) {
94
+ return {
95
+ line: originalLine,
96
+ column: originalColumn,
97
+ offset: 0 // Not computed in simple mapping
98
+ };
99
+ }
100
+ }
101
+ //# sourceMappingURL=position-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position-mapper.js","sourceRoot":"","sources":["../../src/core/position-mapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Rule, Issue } from '../../types.js';
2
+ import type { DictionaryManager } from '../dictionary-manager.js';
3
+ export declare class ContextSensitiveRule implements Rule {
4
+ readonly name = "context-sensitive";
5
+ private dictManager;
6
+ constructor(dictManager: DictionaryManager);
7
+ check(text: string): Promise<Issue[]>;
8
+ fix(text: string): Promise<string>;
9
+ private getContextSensitiveWords;
10
+ private analyzeSimpleContext;
11
+ private tokenize;
12
+ private calculateBM25Score;
13
+ private extractContext;
14
+ private findTermPositions;
15
+ private escapeRegExp;
16
+ }
17
+ //# sourceMappingURL=context-sensitive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-sensitive.d.ts","sourceRoot":"","sources":["../../../src/core/rules/context-sensitive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAEjE,qBAAa,oBAAqB,YAAW,IAAI;IAC/C,QAAQ,CAAC,IAAI,uBAAsB;IACnC,OAAO,CAAC,WAAW,CAAmB;gBAE1B,WAAW,EAAE,iBAAiB;IAIpC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAmCrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBxC,OAAO,CAAC,wBAAwB;IAwChC,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,YAAY;CAGrB"}
@@ -0,0 +1,154 @@
1
+ export class ContextSensitiveRule {
2
+ name = 'context-sensitive';
3
+ dictManager;
4
+ constructor(dictManager) {
5
+ this.dictManager = dictManager;
6
+ }
7
+ async check(text) {
8
+ const issues = [];
9
+ const lines = text.split('\n');
10
+ // 語境敏感詞彙列表(從詞庫中標記為 context-sensitive 的條目)
11
+ const contextSensitiveWords = this.getContextSensitiveWords();
12
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
13
+ const line = lines[lineIndex];
14
+ for (const word of contextSensitiveWords) {
15
+ const positions = this.findTermPositions(line, word.source);
16
+ for (const position of positions) {
17
+ // 提取上下文(前後各50個字元)
18
+ const context = this.extractContext(text, word.source);
19
+ const contextResult = this.analyzeSimpleContext(context, word);
20
+ if (contextResult.confidence > 0.3) { // 使用適當的信心度門檻
21
+ issues.push({
22
+ line: lineIndex + 1,
23
+ column: position + 1,
24
+ message: `'${word.source}' 在${contextResult.domain}語境建議使用 '${contextResult.suggestion}'`,
25
+ severity: contextResult.confidence > 0.8 ? 'warning' : 'info',
26
+ rule: this.name,
27
+ suggestions: [contextResult.suggestion],
28
+ fixable: contextResult.confidence > 0.8
29
+ });
30
+ }
31
+ }
32
+ }
33
+ }
34
+ return issues;
35
+ }
36
+ async fix(text) {
37
+ let fixedText = text;
38
+ const contextSensitiveWords = this.getContextSensitiveWords();
39
+ for (const word of contextSensitiveWords) {
40
+ if (text.includes(word.source)) {
41
+ const context = this.extractContext(text, word.source);
42
+ const result = this.analyzeSimpleContext(context, word);
43
+ if (result.confidence > 0.8) {
44
+ const regex = new RegExp(this.escapeRegExp(word.source), 'g');
45
+ fixedText = fixedText.replace(regex, result.suggestion);
46
+ }
47
+ }
48
+ }
49
+ return fixedText;
50
+ }
51
+ getContextSensitiveWords() {
52
+ // 硬編碼常見語境敏感詞彙,避免過度複雜化
53
+ return [
54
+ {
55
+ source: '質量', // 使用簡體
56
+ candidates: [
57
+ {
58
+ suggestion: '品質',
59
+ domain: '商業',
60
+ keywords: ['產品', '產品', '控制', '管理', '標準', '標準', '檢驗', '檢驗', '服務', '服務', '客戶', '客戶', '品管'],
61
+ confidence: 0.85
62
+ },
63
+ {
64
+ suggestion: '質量',
65
+ domain: '物理',
66
+ keywords: ['密度', '重量', '公式', '物理', '質點', '質點', '慣性', '慣性', '守恆', '守恆', '定律'],
67
+ confidence: 0.90
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ source: '土豆',
73
+ candidates: [
74
+ {
75
+ suggestion: '馬鈴薯',
76
+ domain: '料理',
77
+ keywords: ['烹飪', '料理', '炒', '煮', '蒸', '炸', '調味', '食材', '菜'],
78
+ confidence: 0.85
79
+ },
80
+ {
81
+ suggestion: '花生',
82
+ domain: '農業',
83
+ keywords: ['農業', '種植', '花生油', '作物', '收成', '田地', '油料'],
84
+ confidence: 0.80
85
+ }
86
+ ]
87
+ }
88
+ ];
89
+ }
90
+ analyzeSimpleContext(context, word) {
91
+ const contextWords = this.tokenize(context);
92
+ const scores = word.candidates.map(candidate => {
93
+ // TF-IDF 風格計算:關鍵詞在上下文中的重要性
94
+ const relevanceScore = this.calculateBM25Score(contextWords, candidate.keywords);
95
+ return {
96
+ suggestion: candidate.suggestion,
97
+ domain: candidate.domain,
98
+ confidence: candidate.confidence * relevanceScore,
99
+ reason: `${candidate.domain}語境相關度: ${(relevanceScore * 100).toFixed(1)}%`
100
+ };
101
+ });
102
+ // 返回信心度最高的建議
103
+ return scores.reduce((best, current) => current.confidence > best.confidence ? current : best);
104
+ }
105
+ tokenize(text) {
106
+ // 簡單中文分詞:去除標點符號,分割為字元
107
+ return text.replace(/[。,!?;:、""''()【】]/g, '').split('')
108
+ .filter(char => char.trim() && !char.match(/\s/));
109
+ }
110
+ calculateBM25Score(contextWords, domainKeywords) {
111
+ const context = contextWords.join('');
112
+ const docLength = context.length;
113
+ const avgDocLength = 100; // 假設平均文件長度
114
+ const k1 = 1.5; // BM25 參數
115
+ const b = 0.75; // BM25 參數
116
+ let score = 0;
117
+ for (const keyword of domainKeywords) {
118
+ if (context.includes(keyword)) {
119
+ // 計算詞頻 (TF)
120
+ const tf = (context.match(new RegExp(this.escapeRegExp(keyword), 'g')) || []).length;
121
+ // 計算 IDF
122
+ const totalDocs = domainKeywords.length;
123
+ const docsWithTerm = 1; // 簡化:假設只在當前候選中出現
124
+ const idf = Math.log((totalDocs - docsWithTerm + 0.5) / (docsWithTerm + 0.5));
125
+ // BM25 的 TF 正規化
126
+ const normalizedTF = (tf * (k1 + 1)) /
127
+ (tf + k1 * (1 - b + b * (docLength / avgDocLength)));
128
+ score += idf * normalizedTF;
129
+ }
130
+ }
131
+ return Math.max(0, score);
132
+ }
133
+ extractContext(text, word) {
134
+ const index = text.indexOf(word);
135
+ if (index === -1)
136
+ return '';
137
+ const start = Math.max(0, index - 50);
138
+ const end = Math.min(text.length, index + word.length + 50);
139
+ return text.slice(start, end);
140
+ }
141
+ findTermPositions(text, term) {
142
+ const positions = [];
143
+ let index = text.indexOf(term);
144
+ while (index !== -1) {
145
+ positions.push(index);
146
+ index = text.indexOf(term, index + 1);
147
+ }
148
+ return positions;
149
+ }
150
+ escapeRegExp(string) {
151
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
152
+ }
153
+ }
154
+ //# sourceMappingURL=context-sensitive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-sensitive.js","sourceRoot":"","sources":["../../../src/core/rules/context-sensitive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Rule, Issue } from '../../types.js';
2
+ import type { DictionaryManager } from '../dictionary-manager.js';
3
+ export declare class MainlandTermsRule implements Rule {
4
+ readonly name = "mainland-terms";
5
+ private dictManager;
6
+ constructor(dictManager: DictionaryManager);
7
+ check(text: string): Promise<Issue[]>;
8
+ fix(text: string): Promise<string>;
9
+ private getLineInfo;
10
+ }
11
+ //# sourceMappingURL=mainland-terms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mainland-terms.d.ts","sourceRoot":"","sources":["../../../src/core/rules/mainland-terms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAEjE,qBAAa,iBAAkB,YAAW,IAAI;IAC5C,QAAQ,CAAC,IAAI,oBAAmB;IAChC,OAAO,CAAC,WAAW,CAAmB;gBAE1B,WAAW,EAAE,iBAAiB;IAIpC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAqBrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBxC,OAAO,CAAC,WAAW;CAOpB"}
@@ -0,0 +1,45 @@
1
+ export class MainlandTermsRule {
2
+ name = 'mainland-terms';
3
+ dictManager;
4
+ constructor(dictManager) {
5
+ this.dictManager = dictManager;
6
+ }
7
+ async check(text) {
8
+ const issues = [];
9
+ const matches = this.dictManager.findMatches(text);
10
+ for (const match of matches) {
11
+ const lineInfo = this.getLineInfo(text, match.start);
12
+ issues.push({
13
+ line: lineInfo.line,
14
+ column: lineInfo.column,
15
+ message: `大陸用語 '${match.term}' 建議使用臺灣用語 '${match.replacement}'`,
16
+ severity: 'warning',
17
+ rule: this.name,
18
+ suggestions: [match.replacement],
19
+ fixable: true
20
+ });
21
+ }
22
+ return issues;
23
+ }
24
+ async fix(text) {
25
+ let fixedText = text;
26
+ const matches = this.dictManager.findMatches(text);
27
+ // Sort by position (from end to start) to avoid offset issues
28
+ const sortedMatches = matches.sort((a, b) => b.start - a.start);
29
+ for (const match of sortedMatches) {
30
+ // Replace using precise position information
31
+ fixedText = fixedText.substring(0, match.start) +
32
+ match.replacement +
33
+ fixedText.substring(match.end);
34
+ }
35
+ return fixedText;
36
+ }
37
+ getLineInfo(text, offset) {
38
+ const lines = text.substring(0, offset).split('\n');
39
+ return {
40
+ line: lines.length,
41
+ column: lines[lines.length - 1].length + 1
42
+ };
43
+ }
44
+ }
45
+ //# sourceMappingURL=mainland-terms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mainland-terms.js","sourceRoot":"","sources":["../../../src/core/rules/mainland-terms.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,iBAAiB;IACnB,IAAI,GAAG,gBAAgB,CAAA;IACxB,WAAW,CAAmB;IAEtC,YAAY,WAA8B;QACxC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,MAAM,GAAY,EAAE,CAAA;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QAElD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;YAEpD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,OAAO,EAAE,SAAS,KAAK,CAAC,IAAI,eAAe,KAAK,CAAC,WAAW,GAAG;gBAC/D,QAAQ,EAAE,SAAS;gBACnB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC;gBAChC,OAAO,EAAE,IAAI;aACd,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,IAAI,SAAS,GAAG,IAAI,CAAA;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QAElD,8DAA8D;QAC9D,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;QAE/D,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,6CAA6C;YAC7C,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;gBACnC,KAAK,CAAC,WAAW;gBACjB,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5C,CAAC;QAED,OAAO,SAAS,CAAA;IAClB,CAAC;IAEO,WAAW,CAAC,IAAY,EAAE,MAAc;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnD,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,MAAM;YAClB,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;SAC3C,CAAA;IACH,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ import type { Rule, Issue } from '../../types.js';
2
+ export declare class SimplifiedCharsRule implements Rule {
3
+ readonly name = "simplified-chars";
4
+ private converter;
5
+ constructor();
6
+ check(text: string): Promise<Issue[]>;
7
+ fix(text: string): Promise<string>;
8
+ private findDifferences;
9
+ }
10
+ //# sourceMappingURL=simplified-chars.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simplified-chars.d.ts","sourceRoot":"","sources":["../../../src/core/rules/simplified-chars.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAEjD,qBAAa,mBAAoB,YAAW,IAAI;IAC9C,QAAQ,CAAC,IAAI,sBAAqB;IAClC,OAAO,CAAC,SAAS,CAA8B;;IAOzC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IA6BrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKxC,OAAO,CAAC,eAAe;CAkBxB"}
@@ -0,0 +1,51 @@
1
+ import { Converter } from 'opencc-js';
2
+ export class SimplifiedCharsRule {
3
+ name = 'simplified-chars';
4
+ converter;
5
+ constructor() {
6
+ // 簡體到繁體的轉換器
7
+ this.converter = Converter({ from: 'cn', to: 'tw' });
8
+ }
9
+ async check(text) {
10
+ const issues = [];
11
+ const lines = text.split('\n');
12
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
13
+ const line = lines[lineIndex];
14
+ const convertedLine = this.converter(line);
15
+ // 如果轉換後的內容不同,表示原文包含簡體字
16
+ if (line !== convertedLine) {
17
+ const differences = this.findDifferences(line, convertedLine);
18
+ for (const diff of differences) {
19
+ issues.push({
20
+ line: lineIndex + 1,
21
+ column: diff.column + 1,
22
+ message: `簡體字 '${diff.simplified}' 建議使用繁體字 '${diff.traditional}'`,
23
+ severity: 'error',
24
+ rule: this.name,
25
+ suggestions: [diff.traditional],
26
+ fixable: true
27
+ });
28
+ }
29
+ }
30
+ }
31
+ return issues;
32
+ }
33
+ async fix(text) {
34
+ // 使用 opencc 進行簡繁轉換
35
+ return this.converter(text);
36
+ }
37
+ findDifferences(original, converted) {
38
+ const differences = [];
39
+ for (let i = 0; i < Math.min(original.length, converted.length); i++) {
40
+ if (original[i] !== converted[i]) {
41
+ differences.push({
42
+ simplified: original[i],
43
+ traditional: converted[i],
44
+ column: i
45
+ });
46
+ }
47
+ }
48
+ return differences;
49
+ }
50
+ }
51
+ //# sourceMappingURL=simplified-chars.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simplified-chars.js","sourceRoot":"","sources":["../../../src/core/rules/simplified-chars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAGrC,MAAM,OAAO,mBAAmB;IACrB,IAAI,GAAG,kBAAkB,CAAA;IAC1B,SAAS,CAA8B;IAE/C;QACE,YAAY;QACZ,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,MAAM,GAAY,EAAE,CAAA;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAE9B,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;YAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAA;YAC7B,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YAE1C,uBAAuB;YACvB,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;gBAC3B,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;gBAE7D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;oBAC/B,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,SAAS,GAAG,CAAC;wBACnB,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC;wBACvB,OAAO,EAAE,QAAQ,IAAI,CAAC,UAAU,cAAc,IAAI,CAAC,WAAW,GAAG;wBACjE,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,WAAW,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;wBAC/B,OAAO,EAAE,IAAI;qBACd,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,mBAAmB;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAEO,eAAe,CACrB,QAAgB,EAChB,SAAiB;QAEjB,MAAM,WAAW,GAAuE,EAAE,CAAA;QAE1F,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACrE,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjC,WAAW,CAAC,IAAI,CAAC;oBACf,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;oBACvB,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;oBACzB,MAAM,EAAE,CAAC;iBACV,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAA;IACpB,CAAC;CACF"}
@@ -0,0 +1,141 @@
1
+ {
2
+ "metadata": {
3
+ "name": "academic",
4
+ "version": "1.0.0",
5
+ "entries": 12
6
+ },
7
+ "lookup": {
8
+ "算法": {
9
+ "taiwan": "演算法",
10
+ "confidence": 0.9,
11
+ "category": "mainland-term",
12
+ "reason": "台灣學術標準用語"
13
+ },
14
+ "数据结构": {
15
+ "taiwan": "資料結構",
16
+ "confidence": 0.95,
17
+ "category": "mainland-term",
18
+ "reason": "台灣計算機科學標準"
19
+ },
20
+ "數據結構": {
21
+ "taiwan": "資料結構",
22
+ "confidence": 0.95,
23
+ "category": "mainland-term",
24
+ "reason": "台灣計算機科學標準"
25
+ },
26
+ "计算机科学": {
27
+ "taiwan": "計算機科學",
28
+ "confidence": 0.85,
29
+ "category": "mainland-term",
30
+ "reason": "台灣學術慣用語"
31
+ },
32
+ "計算機科學": {
33
+ "taiwan": "計算機科學",
34
+ "confidence": 0.85,
35
+ "category": "mainland-term",
36
+ "reason": "台灣學術慣用語"
37
+ },
38
+ "人工智能": {
39
+ "taiwan": "人工智慧",
40
+ "confidence": 0.9,
41
+ "category": "mainland-term",
42
+ "reason": "台灣AI領域標準"
43
+ },
44
+ "机器学习": {
45
+ "taiwan": "機器學習",
46
+ "confidence": 0.8,
47
+ "category": "mainland-term",
48
+ "reason": "台灣ML領域用語"
49
+ },
50
+ "機器學習": {
51
+ "taiwan": "機器學習",
52
+ "confidence": 0.8,
53
+ "category": "mainland-term",
54
+ "reason": "台灣ML領域用語"
55
+ },
56
+ "数据库系统": {
57
+ "taiwan": "資料庫系統",
58
+ "confidence": 0.95,
59
+ "category": "mainland-term",
60
+ "reason": "台灣資訊科學標準"
61
+ },
62
+ "數據庫系統": {
63
+ "taiwan": "資料庫系統",
64
+ "confidence": 0.95,
65
+ "category": "mainland-term",
66
+ "reason": "台灣資訊科學標準"
67
+ },
68
+ "操作系统": {
69
+ "taiwan": "作業系統",
70
+ "confidence": 0.9,
71
+ "category": "mainland-term",
72
+ "reason": "台灣系統軟體標準"
73
+ },
74
+ "操作系統": {
75
+ "taiwan": "作業系統",
76
+ "confidence": 0.9,
77
+ "category": "mainland-term",
78
+ "reason": "台灣系統軟體標準"
79
+ },
80
+ "用户界面": {
81
+ "taiwan": "使用者介面",
82
+ "confidence": 0.85,
83
+ "category": "mainland-term",
84
+ "reason": "台灣HCI標準用語"
85
+ },
86
+ "用戶界面": {
87
+ "taiwan": "使用者介面",
88
+ "confidence": 0.85,
89
+ "category": "mainland-term",
90
+ "reason": "台灣HCI標準用語"
91
+ },
92
+ "面向对象": {
93
+ "taiwan": "物件導向",
94
+ "confidence": 0.8,
95
+ "category": "mainland-term",
96
+ "reason": "台灣程式設計標準"
97
+ },
98
+ "面向對象": {
99
+ "taiwan": "物件導向",
100
+ "confidence": 0.8,
101
+ "category": "mainland-term",
102
+ "reason": "台灣程式設計標準"
103
+ },
104
+ "函数式编程": {
105
+ "taiwan": "函數式程式設計",
106
+ "confidence": 0.75,
107
+ "category": "mainland-term",
108
+ "reason": "台灣程式語言理論"
109
+ },
110
+ "函數式編程": {
111
+ "taiwan": "函數式程式設計",
112
+ "confidence": 0.75,
113
+ "category": "mainland-term",
114
+ "reason": "台灣程式語言理論"
115
+ },
116
+ "计算机视觉": {
117
+ "taiwan": "電腦視覺",
118
+ "confidence": 0.85,
119
+ "category": "mainland-term",
120
+ "reason": "台灣影像處理標準"
121
+ },
122
+ "計算機視覺": {
123
+ "taiwan": "電腦視覺",
124
+ "confidence": 0.85,
125
+ "category": "mainland-term",
126
+ "reason": "台灣影像處理標準"
127
+ },
128
+ "自然语言处理": {
129
+ "taiwan": "自然語言處理",
130
+ "confidence": 0.9,
131
+ "category": "mainland-term",
132
+ "reason": "台灣NLP標準用語"
133
+ },
134
+ "自然語言處理": {
135
+ "taiwan": "自然語言處理",
136
+ "confidence": 0.9,
137
+ "category": "mainland-term",
138
+ "reason": "台灣NLP標準用語"
139
+ }
140
+ }
141
+ }