| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- /** Canonical paths, parsing, and rendering for bilingual pairing records. */
- import { basename } from 'node:path'
- /** The three repository-relative paths that form one bilingual pair. */
- export interface TranslationPairPaths {
- /** English document path. */
- source: string
- /** Simplified Chinese document path. */
- zh: string
- /** Generated consistency-record path. */
- meta: string
- }
- /** The two content hashes recorded for a bilingual pair. */
- export interface TranslationPairingRecord {
- /** Git blob hash of the English document. */
- sourceHash: string
- /** Git blob hash of the Simplified Chinese document. */
- zhHash: string
- }
- const META_LINE = /^([^:#]+\.md): ([0-9a-f]{40})$/
- /**
- * Derive the counterpart and consistency-record paths from an English document.
- *
- * @param source - Repository-relative English Markdown path.
- * @returns The complete three-path pair.
- */
- export function translationPairPaths(source: string): TranslationPairPaths {
- if (!source.endsWith('.md') || source.endsWith('.zh.md')) {
- throw new Error(`expected an English Markdown path, received ${JSON.stringify(source)}`)
- }
- return {
- source,
- zh: source.replace(/\.md$/, '.zh.md'),
- meta: source.replace(/\.md$/, '.i18n.yaml'),
- }
- }
- /**
- * Derive one pair from its consistency-record path.
- *
- * @param meta - Repository-relative `foo.i18n.yaml` path.
- * @returns The complete three-path pair.
- */
- export function translationPairPathsFromMeta(meta: string): TranslationPairPaths {
- if (!meta.endsWith('.i18n.yaml')) {
- throw new Error(`expected a bilingual consistency-record path, received ${JSON.stringify(meta)}`)
- }
- return translationPairPaths(meta.replace(/\.i18n\.yaml$/, '.md'))
- }
- /**
- * Parse a consistency record for its expected sibling names.
- *
- * @param content - Complete sidecar text.
- * @param paths - Expected sibling paths.
- * @returns The two hashes, or `undefined` for malformed, duplicate, or unexpected keys.
- */
- export function parseTranslationPairingRecord(
- content: string,
- paths: TranslationPairPaths,
- ): TranslationPairingRecord | undefined {
- const hashes = new Map<string, string>()
- for (const line of content.split('\n')) {
- if (line === '' || line.startsWith('#')) continue
- const match = META_LINE.exec(line)
- if (!match?.[1] || !match[2] || hashes.has(match[1])) return undefined
- hashes.set(match[1], match[2])
- }
- const sourceHash = hashes.get(basename(paths.source))
- const zhHash = hashes.get(basename(paths.zh))
- if (hashes.size !== 2 || sourceHash === undefined || zhHash === undefined) return undefined
- return { sourceHash, zhHash }
- }
- /**
- * Render the canonical consistency record for a pair.
- *
- * @param paths - Pair paths written into the record and its recovery command.
- * @param record - Confirmed content hashes.
- * @returns Canonical YAML text with exactly one trailing newline.
- */
- export function renderTranslationPairingRecord(
- paths: TranslationPairPaths,
- record: TranslationPairingRecord,
- ): string {
- return [
- '# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each',
- '# side as of the last confirmed-consistent state. Both languages carry equal authority;',
- '# after editing either side, bring the other along and re-record with:',
- `# pnpm run verify-translation-pairing --write ${paths.source}`,
- `${basename(paths.source)}: ${record.sourceHash}`,
- `${basename(paths.zh)}: ${record.zhHash}`,
- '',
- ].join('\n')
- }
|