<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arthur Dufour</title>
    <description>The latest articles on DEV Community by Arthur Dufour (@adufr).</description>
    <link>https://dev.to/adufr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2928266%2Ffab6f035-caa3-4961-9489-ad5abba829f6.jpeg</url>
      <title>DEV Community: Arthur Dufour</title>
      <link>https://dev.to/adufr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adufr"/>
    <language>en</language>
    <item>
      <title>Automating GitBook translations with GitHub Actions: A complete guide</title>
      <dc:creator>Arthur Dufour</dc:creator>
      <pubDate>Mon, 10 Mar 2025 14:07:07 +0000</pubDate>
      <link>https://dev.to/365talents/automating-gitbook-translations-with-github-actions-a-complete-guide-3maf</link>
      <guid>https://dev.to/365talents/automating-gitbook-translations-with-github-actions-a-complete-guide-3maf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.gitbook.com/" rel="noopener noreferrer"&gt;GitBook&lt;/a&gt; is a modern documentation platform that makes it easy to create, edit, and organize documentation. With its clean interface, version control, and collaboration features, it's no wonder it's become so popular among development teams.&lt;/p&gt;

&lt;p&gt;But if you're managing documentation with GitBook and need to support multiple languages, you've probably hit a major roadblock: GitBook doesn't offer native translation capabilities. While it's fantastic for creating and managing documentation, this lack of built-in translation features can be a real headache for international teams.&lt;/p&gt;

&lt;p&gt;In this article, I'll share a complete solution for automating GitBook translations using GitHub Actions and a custom Node.js script. As a fullstack developer who recently implemented this for the startup I work at ; &lt;a href="https://365talents.com" rel="noopener noreferrer"&gt;365Talents&lt;/a&gt;, I discovered that while GitBook does provide &lt;a href="https://docs.gitbook.com/developers/getting-started/guides/use-github-actions-to-translate-gitbook-pages" rel="noopener noreferrer"&gt;some documentation on translation workflows&lt;/a&gt;, it lacks the concrete implementation details you need to actually get it working.&lt;/p&gt;

&lt;p&gt;This guide is for you if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use GitBook for documentation&lt;/li&gt;
&lt;li&gt;Need to maintain content in multiple languages&lt;/li&gt;
&lt;li&gt;Want to automate the translation process&lt;/li&gt;
&lt;li&gt;Have basic familiarity with GitHub Actions &amp;amp; Node.js&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you'll have everything you need to build a fully automated system that detects changes to your primary language content and automatically translates it to your target languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining your requirements
&lt;/h2&gt;

&lt;p&gt;Before diving into implementation, you need to make two important decisions about your workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Translation review process&lt;/strong&gt;: While automated translation has improved dramatically, it's still far from perfect. You'll need to decide how translations will be reviewed and by whom. GitBook's documentation points out something crucial: when a reviewed translation exists and the source document is updated, automatic re-translation will overwrite any manual improvements made during review. Your team needs a clear process to handle these situations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Update strategy&lt;/strong&gt;: You must decide how to handle updates to existing content. &lt;br&gt;
For our implementation, we took a conservative approach: we only automatically translate new documents, not updates to existing ones. This preserves any manual edits made to translations but means content authors need to manually update translations when they modify the primary language version. While this approach isn't perfect for multilingual content management, it worked best for our specific needs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can enhance your translation process in many ways. For instance, you could just retranslate the specific phrases that changed instead of the entire file, though this would require more development work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution Overview: Automated translation workflow
&lt;/h2&gt;

&lt;p&gt;The solution I'm sharing creates an automated pipeline that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detects &lt;strong&gt;new files&lt;/strong&gt; added in your primary language&lt;/li&gt;
&lt;li&gt;Translates them using a translation service (Azure Translator)&lt;/li&gt;
&lt;li&gt;Creates the corresponding content in your target language spaces&lt;/li&gt;
&lt;li&gt;Commits the changes back to your repository, automatically updating GitBook&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1rgo1vkmgkeh5cx4f11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1rgo1vkmgkeh5cx4f11.png" alt="Diagram of the workflow" width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Setting up GitBook with GitSync
&lt;/h3&gt;

&lt;p&gt;Before we can automate translations, we need to set up GitBook properly and connect it to GitHub using GitSync.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating a Collection with language Spaces
&lt;/h4&gt;

&lt;p&gt;In GitBook, collections let you group related spaces together. For our translation workflow, we'll create a collection with a separate space for each language:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new collection in GitBook&lt;/li&gt;
&lt;li&gt;Create a space for your primary language (e.g., "English")&lt;/li&gt;
&lt;li&gt;Create an additional space for your target language (e.g., "French")&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Configuring GitSync
&lt;/h4&gt;

&lt;p&gt;Next, we need to connect each space to our GitHub repository:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your space settings&lt;/li&gt;
&lt;li&gt;Navigate to the "Integrations" tab&lt;/li&gt;
&lt;li&gt;Select "GitHub" and follow the authentication process&lt;/li&gt;
&lt;li&gt;Configure the sync settings:

&lt;ul&gt;
&lt;li&gt;Select your account and repository&lt;/li&gt;
&lt;li&gt;Choose the branch (usually "main" or "master")&lt;/li&gt;
&lt;li&gt;Use the monorepo approach and specify the folder path for your language (e.g., &lt;code&gt;./en&lt;/code&gt; for the "English" space, and &lt;code&gt;./fr&lt;/code&gt; for the French space)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Repeat this process for your target language space, making sure it's connected to the appropriate folder in your repository.&lt;/p&gt;

&lt;h4&gt;
  
  
  Repository structure
&lt;/h4&gt;

&lt;p&gt;After setting up GitSync, your repository structure should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/
├── en/           # Primary language content (English)
├── fr/           # French translations
└── .github/
    └── workflows/
        └── translate.yml  # Our future GitHub Action workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure makes it easy to manage content for each language separately while keeping the automation in a central location.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Creating the GitHub Action workflow
&lt;/h3&gt;

&lt;p&gt;Now, let's create the GitHub Action workflow that will detect changes and trigger our translation process.&lt;/p&gt;

&lt;p&gt;Create a file at &lt;code&gt;.github/workflows/translate.yml&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Translate GitBook Content&lt;/span&gt;

&lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;source_language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;language&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(e.g.:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;en)"&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;target_language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;language&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(e.g.:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;fr)"&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;translator_api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The Azure Translator API key&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;runs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;using&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composite&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get all changed markdown files&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changed-markdown-files&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tj-actions/changed-files@v45&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;${{ inputs.source_language }}/**/*.md&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get all changes in .gitbook/&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changed-gitbook-files&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tj-actions/changed-files@v45&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;${{ inputs.source_language }}/.gitbook/**&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Copy .gitbook files to target language directory&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.changed-gitbook-files.outputs.any_changed == 'true'&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;rm -rf ${{ inputs.target_language }}/.gitbook&lt;/span&gt;
        &lt;span class="s"&gt;mkdir -p ${{ inputs.target_language }}/.gitbook&lt;/span&gt;
        &lt;span class="s"&gt;cp -r ${{ inputs.source_language }}/.gitbook/* ${{ inputs.target_language }}/.gitbook/&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.changed-markdown-files.outputs.any_changed == 'true'&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;23.6.0&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up pnpm&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.changed-markdown-files.outputs.any_changed == 'true'&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm/action-setup@v4&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;9.14.2&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.changed-markdown-files.outputs.any_changed == 'true'&lt;/span&gt;
      &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;365talents-actions/actions/gitbook-translator/translator-script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm install&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run translation script&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.changed-markdown-files.outputs.any_changed == 'true'&lt;/span&gt;
      &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;365talents-actions/actions/gitbook-translator/translator-script&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;SOURCE_LANGUAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.source_language }}&lt;/span&gt;
        &lt;span class="na"&gt;TARGET_LANGUAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.target_language }}&lt;/span&gt;
        &lt;span class="na"&gt;NEW_FILES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.changed-markdown-files.outputs.all_changed_files }}&lt;/span&gt;
        &lt;span class="na"&gt;TRANSLATOR_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.translator_api_key }}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node index.ts&lt;/span&gt; 

    &lt;span class="c1"&gt;# Make sure that deleting a file in the source_language also deletes it in target_language&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cleanup deleted files&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;cd ${{ inputs.target_language }}&lt;/span&gt;
        &lt;span class="s"&gt;find . -type f -name "*.md" | while read file; do&lt;/span&gt;
          &lt;span class="s"&gt;if [ ! -f "../${{ inputs.source_language }}/$file" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;rm "$file"&lt;/span&gt;
            &lt;span class="s"&gt;echo "Removed ${{ inputs.target_language }}/$file as it no longer exists in ${{ inputs.source_language }}/"&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
        &lt;span class="s"&gt;done&lt;/span&gt;

    &lt;span class="c1"&gt;# Commit translated content&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit changes&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stefanzweifel/git-auto-commit-action@v5&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;commit_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Translate files&lt;/span&gt;
        &lt;span class="na"&gt;file_pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;inputs.target_language&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/**'&lt;/span&gt;
        &lt;span class="na"&gt;add_options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Triggers on file changes&lt;/li&gt;
&lt;li&gt;Uses tj-actions/changed-files to detect which files have changed&lt;/li&gt;
&lt;li&gt;Checks out the repository and sets up Node.js&lt;/li&gt;
&lt;li&gt;Runs our translation script (which we'll create next)&lt;/li&gt;
&lt;li&gt;Commits the translated files back to the repository&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Implementing the translation script
&lt;/h3&gt;

&lt;p&gt;Now we need to develop the Node.js script that will perform the actual translation work. This script is the heart of our automation system, responsible for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Processing the detected file changes&lt;/li&gt;
&lt;li&gt;Communicating with the translation API&lt;/li&gt;
&lt;li&gt;Saving the translated content&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've created a complete implementation of this translation script and shared it in a GitHub Gist for you to reference: &lt;a href="https://gist.github.com/adufr/64635aadcb3c94a45d0e9d7fde06bc41" rel="noopener noreferrer"&gt;GitBook Translation Script&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The script uses Azure Translator API to convert content between languages, but you can easily modify it to work with other translation services like DeepL, Google Cloud Translation, or OpenAI if those better suit your needs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One critical aspect of the translation script is how it handles Markdown syntax. The Azure Translator API sometimes loses Markdown formatting during translation. To fix this, the script uses regular expressions and string replacements to preserve all Markdown syntax elements after translation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Remember that this implementation reflects my team's specific requirements and workflow. You should review and customize the code to match your team's translation strategy, especially regarding how you want to handle content updates and translation reviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Committing translated content back to the repository
&lt;/h3&gt;

&lt;p&gt;The GitHub Action workflow automatically commits the translated files back to your repository. GitSync then updates your GitBook spaces with the new content. You have nothing more to do! 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Customizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Supporting multiple target languages
&lt;/h3&gt;

&lt;p&gt;My script currently only supports one target language, but it can easily be adapted to handle multiple target languages.&lt;/p&gt;

&lt;p&gt;You would need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add more GitBook spaces and configure GitSync correctly for each language&lt;/li&gt;
&lt;li&gt;Update the translation script to loop through all target languages&lt;/li&gt;
&lt;li&gt;Modify the GitHub Action to support multiple target languages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementing manual review workflows
&lt;/h3&gt;

&lt;p&gt;Currently, the described setup doesn't include a manual review process, meaning all new files are automatically translated and instantly added to GitBook.&lt;/p&gt;

&lt;p&gt;To implement a manual review process, you could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a pull request instead of directly committing translations&lt;/li&gt;
&lt;li&gt;Add reviewers to the pull request&lt;/li&gt;
&lt;li&gt;Only merge after approval&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Optimizing for large content bases
&lt;/h3&gt;

&lt;p&gt;Our setup only translates new files, which isn't ideal for frequently updated documentation. If you regularly make changes to your documentation, you'd need to implement incremental translation to process only the content that has changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Translation quality considerations
&lt;/h3&gt;

&lt;p&gt;Azure Translator, like other machine translation services, works well for most general content but may struggle with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical terminology specific to your product or industry&lt;/li&gt;
&lt;li&gt;Complex sentence structures&lt;/li&gt;
&lt;li&gt;Idiomatic expressions&lt;/li&gt;
&lt;li&gt;Cultural nuances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For technical documentation, you might notice certain specialized terms being translated incorrectly or inconsistently across documents.&lt;/p&gt;

&lt;p&gt;Beyond manual review, here are several strategies to enhance your translation quality:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Custom Dictionary/Glossary&lt;/strong&gt;: Create a glossary of technical terms and their approved translations. Some translation APIs (including Azure Translator with Custom Translator) allow you to provide custom dictionaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-processing Content&lt;/strong&gt;: Simplify complex sentences in your source content. Clear, concise writing translates better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-processing Scripts&lt;/strong&gt;: Develop scripts that automatically correct known translation issues, such as consistently replacing certain terms with their correct translations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation Memory&lt;/strong&gt;: Implement a translation memory system that stores previously approved translations, allowing you to reuse them for similar content.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cost considerations
&lt;/h3&gt;

&lt;p&gt;When implementing an automated translation system, it's important to consider the costs involved, especially as your documentation grows.&lt;/p&gt;

&lt;p&gt;A few things to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare service costs before committing to one provider&lt;/li&gt;
&lt;li&gt;Implement cost optimization strategies (only translate sections that have changed, store and reuse translations for repeated content)&lt;/li&gt;
&lt;li&gt;Monitor your usage and set up alerts to avoid unexpected costs as your documentation grows&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Setting up this GitBook translation system was honestly not that simple to do. GitBook really should just build this in. In my opinion, documentation is global, and teams shouldn't have to hack together custom solutions for something so fundamental.&lt;/p&gt;

&lt;p&gt;Until then, this solution works well enough to save you from manual translation headaches. It's not perfect, but it's definitely better than copying and pasting content between languages!&lt;/p&gt;

</description>
      <category>gitbook</category>
    </item>
  </channel>
</rss>
