Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- **Sync Mode**: Runs in SOURCE repo, creates translation PRs in target repo
- **Review Mode**: Runs in TARGET repo, posts quality review comments on translation PRs

**Current Version**: v0.12.3 | **Tests**: 934 (39 suites) | **Glossary**: 357 terms (zh-cn, fa)
**Current Version**: v0.12.3 | **Tests**: 935 (39 suites) | **Glossary**: 357 terms (zh-cn, fa)

---

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- **CJK–MyST spacing rule for zh-cn**: New language-config rule instructs Claude to insert a space between Chinese characters and inline MyST directives (`{doc}`, `{ref}`, etc.) or Markdown links, preventing rendering failures (e.g. `请参阅 {doc}` not `请参阅{doc}`)
- **MyST target-label blank-line cleanup**: `reconstructFromComponents` now strips blank lines between MyST target labels (`(label)=`) and headings in post-processing, so targets always attach to their heading correctly
- **1 test** for target-label blank-line removal (934 → 935 total)

## [0.12.3] - 2026-03-24

### Fixed
Expand Down
89 changes: 89 additions & 0 deletions src/__tests__/component-reconstruction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1615,4 +1615,93 @@ heading-map:
expect(result).toContain('Section B: B部分');
expect(result).not.toContain('Section A:');
});

it('should remove blank lines between MyST target labels and headings', async () => {
const oldContent = `---
config: test
---

# Advanced Features

Overview paragraph.

## Iterables

Content about iterables.

(iterators)=
### Iterators

Iterator details.

(descriptors)=
## Decorators

Decorator content.`;

const newContent = `---
config: test
---

# Advanced Features

Overview paragraph updated.

## Iterables

Content about iterables.

(iterators)=
### Iterators

Iterator details.

(descriptors)=
## Decorators

Decorator content.`;

const targetContent = `---
config: test
---

# 高级特性

概述段落。

## 可迭代对象

关于可迭代对象的内容。

(iterators)=
### 迭代器

迭代器详情。

(descriptors)=
## 装饰器

装饰器内容。`;

// Mock translation for intro change
mockTranslator.translateSection.mockResolvedValue({
success: true,
translatedSection: '概述段落已更新。',
});

const result = await processor.processSectionBased(
oldContent,
newContent,
targetContent,
'test.md',
'en',
'zh-cn'
);

// Target labels should be directly above headings with no blank line
expect(result).toContain('(iterators)=\n### 迭代器');
expect(result).not.toContain('(iterators)=\n\n### 迭代器');
expect(result).toContain('(descriptors)=\n## 装饰器');
expect(result).not.toContain('(descriptors)=\n\n## 装饰器');
});
});
3 changes: 2 additions & 1 deletion src/__tests__/language-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ describe('Language Configuration', () => {
const config = getLanguageConfig('zh-cn');
expect(config.code).toBe('zh-cn');
expect(config.name).toBe('Chinese (Simplified)');
expect(config.additionalRules).toHaveLength(1);
expect(config.additionalRules).toHaveLength(2);
expect(config.additionalRules[0]).toContain('full-width Chinese punctuation');
expect(config.additionalRules[1]).toContain('space between Chinese characters and inline MyST directives');
});

it('should handle case insensitive language codes', () => {
Expand Down
7 changes: 6 additions & 1 deletion src/file-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,12 @@ export class FileProcessor {
parts.push(''); // Empty line between sections
}

return parts.join('\n').trim() + '\n';
const joined = parts.join('\n').trim() + '\n';

// Post-process: remove blank lines between MyST target labels and headings.
// Target labels like (some_id)= must be directly above their heading with no
// intervening blank line, otherwise MyST won't attach the target to the heading.
return joined.replace(/(\([a-zA-Z0-9_.:-]+\)=)\n\n(#+\s)/g, '$1\n$2');
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/language-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const LANGUAGE_CONFIGS: Record<string, LanguageConfig> = {
name: 'Chinese (Simplified)',
additionalRules: [
'Use proper full-width Chinese punctuation marks (,:。!?) not ASCII punctuation (,.!?) in prose text',
'Always insert a space between Chinese characters and inline MyST directives ({doc}, {ref}, {any}, {term}, etc.) or Markdown links ([text](url)), e.g., "请参阅 {doc}\`介绍 <intro>\`" not "请参阅{doc}\`介绍 <intro>\`"',
],
},
'fa': {
Expand Down
Loading