Automated Translations with AI at Pethotel.io

Fully automatic translations

I wrote parser to detect hardcoded text from the views and then replace them with i18n keys, and then generate translations for each key using AI & the context where it's used. It was fun hobby project to see what can be done.

Objectives

Overview

Pethotel.io started as a platform entirely in Finnish and while this served our initial user base, I was curious on my free time about making the site accessible to a broader audience. Rather than an urgent requirement, it felt like a fun challenge to see how far I could push automation in localization using AI.

I had some experience with internationalization (i18n) in Ruby on Rails, but automating the entire process sounded like an interesting challenge. Initially, I thought about translating the site into Swedish and English, but then I figured, why not see how many languages I could support if automation made it easy?

Automated Translation Process Diagram

Experimenting

I started by creating Rake tasks to automate the extraction and replacement of hardcoded text. Using Nokogiri, a Ruby gem for parsing HTML and XML, I wrote a script that could traverse all the HTML.ERB files in the application. This script identified text nodes that needed internationalization and replaced them with i18n keys.

Running this Rake task allowed me to edit nearly all the view files at once. It was exciting to see how much manual work I could bypass. Of course, there were challenges—edge cases where the script didn't work perfectly, especially with complex nested elements or dynamic content. I spent time refining the script to handle these exceptions, which was part of the fun.

Automating Translations

With the text extracted and replaced with i18n keys, the next step was generating translations. I started with Swedish and English but soon thought, if the automation works well, why not add more languages like French, German, and Spanish? It became an interesting experiment to see how far I could push it.

I created a service called ThinkingService that interacts with OpenAI's GPT models via their API. This allowed me to generate translations automatically using Large Language Models. Setting this up involved dealing with API rate limits and ensuring the translations made sense, which was a learning experience in itself.

The service processes translations asynchronously, so it didn't bog down the application. I played around with different parameters like model choice and temperature settings to see how they affected the translations. It was a lot of trial and error, but that's what made it interesting.

Technical Details

The Rake task reads the YAML file for each locale and iterates over every translation key. For each string, it calls the translate_value method, which uses the ThinkingService to get the translated text from the AI model. The translated strings are then written back to the YAML file.

Here's a simplified version of the Rake task:


namespace :translations do
  desc "Process translations in the YAML file"
  task process: :environment do
    # Load the YAML file
    file_path = "config/locales/fr.yml"
    @translations = YAML.load_file(file_path)

    # Configuration for translation
    config = {
      model: "gpt-4",
      max_tokens: 60,
      temperature: 0.0,
      from: "English",
      to: "French"
    }

    # Collect changes to be made
    changes = {}

    # Iterate over each translation
    @translations.each do |locale, keys|
      process_translations(locale, keys, config, changes)
    end

    # Apply collected changes
    apply_changes(changes)

    # Write back to the YAML file
    File.write(file_path, @translations.to_yaml)
  end
end
    

The ThinkingService uses HTTParty to make HTTP requests to the OpenAI API. It constructs prompts and handles responses, extracting the translated content. I had to implement error handling to make sure that if a translation failed, it wouldn't crash the entire process.

Here's a snippet of the ThinkingService:


class ThinkingService
  include HTTParty
  base_uri "https://api.openai.com/v1"

  def answer(selected_model, system_prompt, user_prompt, assistant_prompt, max_tokens, temperature)
    messages = [
      { role: "system", content: system_prompt },
      { role: "user", content: user_prompt },
      { role: "assistant", content: assistant_prompt }
    ]

    body = {
      model: selected_model,
      messages: messages,
      max_tokens: max_tokens,
      temperature: temperature
    }.to_json

    response = self.class.post("/chat/completions", body: body, headers: headers)
    handle_response(response)
  end

  private

  def headers
    { "Authorization" => "Bearer #{api_key}", "Content-Type" => "application/json" }
  end

  def api_key
    ENV["OPENAI_API_KEY"]
  end
end
    

Implementing the code required attention to variables and placeholders within the strings. Placeholders like %{count} needed to be preserved in the translated text. I added logic to detect and protect these during translation.

Challenges Faced

Contextual Accuracy in Translations

Ensuring the AI-generated translations were contextually accurate was sometimes a challenge. Technical terms and idiomatic expressions didn't always translate well. I tweaked the prompts and adjusted parameters to improve the results, which was an interesting part of the experiment.

Handling Variables and Placeholders

Preserving variables and placeholders was crucial to maintain functionality. I had to implement checks to ensure the AI didn't alter these elements during translation. It added complexity but also made the project more engaging.

Cost concerns turned out minimal

Using the OpenAI API came with rate limits and costs. Processing many translations could get expensive. My prompts were short & I had max tokens set to somewhere about 100-200 so the cost was very low for the whole thing.

Error Handling and Edge Cases

There were times when the AI returned incomplete responses. Implementing robust error handling was necessary to keep the process running smoothly. Handling these edge cases was part of the learning experience.

Supported Languages

Results

Through this experiment, I managed to generate translations for multiple languages relatively quickly. It did that in around 3-4 hours once the working, tested code started running. What started as a curiosity turned into a functional multilingual platform with minimal manual effort. It was satisfying to see the platform become more accessible to a wider audience.

The automated system saved a lot of time and maintained consistency across the application. It also simplified maintenance, allowing for easy updates as the platform evolved. While there were challenges, the end result was rewarding.

Learnings

Let AI write your Regex code, it will figure it out much faster & more precisely

Specify logic for detection & let AI figure out the regex for it.

Really realized the power of code that writes itself

I knew about file editing capabilities of programming languages but never really had any use for it. This project made me realize how powerful it can be. It surely makes me think different in the future about everything.

AI has brought more possibilities when it comes to automatic file editing

AI can be used to generate translations, write code, and even refactor code. It's exciting to think about the possibilities it opens up for automation in web development.

Version control helped me to track what changes my automation made each time it ran

Version control was crucial in tracking changes and checking what was edited each time the automation ran. It helped me understand the impact of the changes and revert things if needed.

Looking back, I really enjoyed this experiment. I really thought that this wouldn't be possible but then something in me asked "why not?" and I started working. It was a fun project that ended up to be little meaningful enhancement for Pethotel.io.