verify-translation-prompt.ts 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. /** Verify that the committed translation prompt renders and parses as documented. */
  2. import { readFileSync } from 'node:fs'
  3. import { join, resolve } from 'node:path'
  4. import {
  5. documentedTranslationPromptPlaceholders,
  6. parseTranslationResponse,
  7. renderTranslationPrompt,
  8. renderTranslationResponse,
  9. TRANSLATION_PROMPT_PLACEHOLDERS,
  10. } from './translation-prompt.ts'
  11. const root = resolve(import.meta.dirname, '..')
  12. function read(path: string): string {
  13. return readFileSync(join(root, path), 'utf8')
  14. }
  15. try {
  16. const document = read('docs/i18n/translation-prompt.md')
  17. const translationRules = read('docs/i18n/translation-rules.md')
  18. const terminology = read('docs/i18n/terminology.md')
  19. const documented = documentedTranslationPromptPlaceholders(document)
  20. if (documented.join('\n') !== TRANSLATION_PROMPT_PLACEHOLDERS.join('\n')) {
  21. throw new Error(`placeholder table must list exactly: ${TRANSLATION_PROMPT_PLACEHOLDERS.join(', ')}`)
  22. }
  23. const englishSource = renderTranslationPrompt(document, {
  24. sourceLanguage: 'English',
  25. sourceFilename: 'example.md',
  26. translationRules,
  27. terminology,
  28. })
  29. const chineseSource = renderTranslationPrompt(document, {
  30. sourceLanguage: 'Chinese',
  31. sourceFilename: 'example.zh.md',
  32. translationRules,
  33. terminology,
  34. })
  35. if (!englishSource.includes('[English](example.md) | 中文')) throw new Error('English-source render does not carry the Chinese switcher instruction')
  36. if (!chineseSource.includes('English | [中文](example.zh.md)')) throw new Error('Chinese-source render does not carry the English switcher instruction')
  37. const example = /```xml\n([\s\S]*?)\n```/.exec(englishSource)?.[1]
  38. if (example === undefined) throw new Error('rendered prompt has no XML response example')
  39. parseTranslationResponse(example)
  40. const roundTrip = { translation: 'first ]]> pass', review: '- [None] No corrections.', final: 'final ]]> text' }
  41. const parsed = parseTranslationResponse(renderTranslationResponse(roundTrip))
  42. if (JSON.stringify(parsed) !== JSON.stringify(roundTrip)) throw new Error('CDATA split rule does not round-trip response content')
  43. console.log('verify-translation-prompt: both directions render and the XML response contract parses.')
  44. } catch (error) {
  45. const message = error instanceof Error ? error.message : String(error)
  46. console.error(`verify-translation-prompt: ${message}`)
  47. process.exit(1)
  48. }