translate.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /**
  2. * How to use it:
  3. * - cd apps/docs
  4. * - Add apps/docs/.env file, to set OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL
  5. * - Use `git add .` to add all changed docs to git staged changes
  6. * - Run `npm run translate:zh` to translate all zh files to en
  7. */
  8. import * as process from 'process';
  9. import path from 'path';
  10. import fs from 'fs';
  11. import { execSync } from 'child_process';
  12. import { ChatCompletionMessageParam } from 'openai/resources/chat';
  13. import OpenAI from 'openai';
  14. // eslint-disable-next-line import/no-extraneous-dependencies
  15. import 'dotenv/config';
  16. const languages = ['zh', 'en'];
  17. const __dirname = path.dirname(new URL(import.meta.url).pathname);
  18. const ai = new OpenAI({
  19. apiKey: process.env.OPENAI_API_KEY,
  20. baseURL: process.env.OPENAI_BASE_URL,
  21. });
  22. const model = process.env.OPENAI_MODEL!;
  23. const sourceLang = process.argv[2];
  24. if (!languages.includes(sourceLang)) {
  25. console.error(`Invalid language: ${sourceLang}`);
  26. process.exit(1);
  27. }
  28. const targetLangs = languages.filter((_lang) => _lang !== sourceLang);
  29. async function translateContent(
  30. content: string,
  31. targetLang: string,
  32. previousTargetContent?: string
  33. ) {
  34. let systemPrompts = `
  35. You are a translator.
  36. You will translate the following content from ${sourceLang} to ${targetLang}.\n
  37. `;
  38. if (previousTargetContent) {
  39. systemPrompts += `
  40. The target file is translated previously, here is the content:
  41. ${previousTargetContent}
  42. Only translate the content that is different in ${sourceLang}.\n
  43. `;
  44. }
  45. systemPrompts += `
  46. Constraint:
  47. - ONLY RETURN THE TRANSLATED CONTENT, NO OTHER TEXT.
  48. `;
  49. const messages: ChatCompletionMessageParam[] = [
  50. {
  51. role: 'system',
  52. content: systemPrompts,
  53. },
  54. { role: 'user', content },
  55. ];
  56. const response = await ai.chat.completions.create({
  57. model,
  58. messages,
  59. });
  60. return response.choices[0].message.content ?? '';
  61. }
  62. async function translateFiles() {
  63. // 1. Get Stage Changed Documentations for source language
  64. const gitDiffOutput = execSync('git diff --cached --name-only', {
  65. encoding: 'utf-8',
  66. });
  67. const allChangedFiles: string[] = gitDiffOutput.split('\n').filter(Boolean);
  68. console.log('Get Diff files:', allChangedFiles);
  69. const sourceLangFolder = path.join(__dirname, '../src', sourceLang);
  70. const sourceLangFolderAbs = path.join('apps/docs/src', sourceLang);
  71. // Find all *.md, *.mdx files in sourceLangFolder
  72. const diffMarkdownFiles: string[] = allChangedFiles
  73. .filter(
  74. (file) =>
  75. file.includes(sourceLangFolderAbs) && (file.endsWith('.md') || file.endsWith('.mdx'))
  76. )
  77. .map((file) => path.relative(sourceLangFolderAbs, file));
  78. console.log('Files to be translated:', diffMarkdownFiles);
  79. // 2. For each file, translate it to other language
  80. await Promise.all(
  81. diffMarkdownFiles.map(async (file) => {
  82. for (const targetLang of targetLangs) {
  83. const targetLangFolder = path.join(__dirname, '../src', targetLang);
  84. const sourceFile = path.join(sourceLangFolder, file);
  85. const targetFile = path.join(targetLangFolder, file);
  86. // 2.1. Read the file
  87. const sourceContent = fs.readFileSync(sourceFile, 'utf-8');
  88. console.log(`Translate ${sourceFile} to ${targetFile}`);
  89. console.log(sourceContent);
  90. console.log('\n\n\n\n\n');
  91. const previousTargetContent = fs.existsSync(targetFile)
  92. ? fs.readFileSync(targetFile, 'utf-8')
  93. : undefined;
  94. // 2.2. Translate the content
  95. const targetContent = await translateContent(
  96. sourceContent,
  97. targetLang,
  98. previousTargetContent
  99. );
  100. // 2.3. Write the translated file
  101. fs.writeFileSync(targetFile, targetContent);
  102. console.log(`Translated: ${targetFile}`);
  103. console.log(targetContent);
  104. console.log('\n\n\n\n\n');
  105. }
  106. })
  107. );
  108. }
  109. translateFiles().catch((err) => {
  110. console.error('Error generating docs:', err);
  111. });