Skip to content
RSFlowHub

How to Add AI Translation to a Laravel App

How to Add AI Translation to a Laravel App

Quick Answer

Create a small service class that sends your text to POST /api/v1/ai/translation with target_languages, and cache each result so the same text is never translated twice. Pass protected_keys for placeholders such as user_name and a glossary for brand terms, then read data.result.translations from the response.

Translating a Laravel app by hand does not scale: every new message in every language is another file to edit. In this tutorial you will add AI-powered translation to a Laravel 10 or 11 application. You will build one service class, cache the results, protect placeholders and brand names, and translate user-generated content on a queue. You need PHP 8.1 or newer and an RSFlowHub API key.

1. When to use AI translation

AI translation works best for content that changes often or that you do not control: product descriptions, user reviews, support messages, help-centre articles and notifications. For a short list of fixed interface labels, Laravel's normal language files are still the right tool, because those strings are few, reviewed once and reused everywhere.

Content Better approach
Menu labels, button textLanguage files, translated once and reviewed
Product descriptions, articlesAPI translation, cached and stored
User reviews and commentsAPI translation on demand
Legal text, contractsA professional human translator

2. Add the configuration

Store the key in .env and read it through a config file, never directly with env() in application code.

config/services.php PHP
'rsflowhub' => [
    'key' => env('RSFLOWHUB_KEY'),
    'url' => env('RSFLOWHUB_URL', 'https://rsflowhub.com/api/v1'),
],
.env Env
RSFLOWHUB_KEY=your_api_key_here

3. Write the translation service

The service sends one request for every language you ask for (up to 10), caches the answer and returns a simple array keyed by language. Caching matters twice over: translations are faster, and you never pay to translate the same text again.

app/Services/Translator.php PHP
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class Translator
{
    /**
     * @param  array<int, string>  $languages  e.g. ['Spanish', 'French']
     * @param  array<int, string>  $protectedKeys  placeholders that must stay as they are
     * @param  array<string, string>  $glossary  fixed translations for brand or product terms
     * @return array<string, string>  language => translated text (empty if translation failed)
     */
    public function translate(string $text, array $languages, array $protectedKeys = [], array $glossary = [], string $tone = 'friendly'): array
    {
        $cacheKey = 'translation:' . hash('sha256', json_encode([$text, $languages, $protectedKeys, $glossary, $tone]));

        if ($cached = Cache::get($cacheKey)) {
            return $cached;
        }

        $payload = array_filter([
            'text' => $text,
            'target_languages' => $languages,
            'protected_keys' => $protectedKeys,
            'glossary' => $glossary,
            'tone' => $tone,
        ]);

        $response = Http::withHeaders(['X-API-Key' => config('services.rsflowhub.key')])
            ->timeout(30)
            ->post(config('services.rsflowhub.url') . '/ai/translation', $payload);

        if ($response->failed()) {
            Log::warning('Translation failed', [
                'status' => $response->status(),
                'code' => $response->json('error.code'),
            ]);

            return []; // not cached, so the next request tries again
        }

        $translations = collect($response->json('data.result.translations', []))
            ->pluck('translated_text', 'target_language')
            ->all();

        if ($translations) {
            Cache::put($cacheKey, $translations, now()->addDays(30));
        }

        return $translations;
    }
}

Failed calls are logged and returned as an empty array without being cached, so a temporary problem never hides translations for 30 days. Callers should treat an empty result as "show the original text".

4. Protect placeholders and brand names

Laravel messages contain placeholders such as :name or {user_name}, and your product has terms that must not drift between languages. Two request fields handle this:

  • protected_keys is a list (up to 30) of variable names that must never be translated or altered.
  • glossary is an object (up to 30 entries) of fixed translations, for example {"Wallet": "Billetera"}, applied exactly.

Also set tone to one of formal, casual, friendly or professional. preserve_formatting defaults to true, so HTML tags and Markdown symbols in your text are kept as they are.

Example call PHP
$result = app(Translator::class)->translate(
    'Hello <b>{user_name}</b>, welcome to Acme! Please check your Wallet balance.',
    ['Spanish', 'French'],
    protectedKeys: ['user_name'],
    glossary: ['Wallet' => 'Billetera'],
    tone: 'friendly',
);

// ['Spanish' => '¡Hola <b>{user_name}</b>, ...', 'French' => 'Bonjour <b>{user_name}</b>, ...']

Always run a few of your real messages through the glossary and read the result in each language you support, ideally with a native speaker, before you ship.

5. Use it in a controller

Here a visitor reads a product description in their own language. The controller asks for the visitor's language, translates only when it differs from your source language and falls back to the original text on any failure, so the page always renders.

app/Http/Controllers/ProductController.php PHP
public function show(Request $request, Product $product, Translator $translator)
{
    $language = $request->query('lang', 'English');
    $description = $product->description;

    if ($language !== 'English') {
        $translated = $translator->translate($description, [$language], glossary: ['Acme Cloud' => 'Acme Cloud']);
        $description = $translated[$language] ?? $description;
    }

    return view('products.show', compact('product', 'description', 'language'));
}

Validate lang against a short allow-list in real code. Otherwise anyone can send arbitrary strings that become API requests and cache entries.

6. Translate stored content on a queue

For content you edit rarely and read often, translate once when it changes and store the result. A queued job keeps the editing screen fast.

app/Jobs/TranslateProduct.php PHP
<?php

namespace App\Jobs;

use App\Models\Product;
use App\Services\Translator;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class TranslateProduct implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $backoff = 30;

    public function __construct(public Product $product)
    {
    }

    public function handle(Translator $translator): void
    {
        $translations = $translator->translate(
            $this->product->description,
            ['Spanish', 'French', 'German'],
        );

        foreach ($translations as $language => $text) {
            $this->product->translations()->updateOrCreate(
                ['language' => $language],
                ['description' => $text],
            );
        }
    }
}

Dispatch it from your model's saved event or the controller that updates the product, and only when the description actually changed. Three languages are sent as one request. The limits and credits guide explains how translation is billed.

7. Common mistakes to avoid

  • Translating on every page view. Cache or store results. Translating the same text repeatedly wastes credits and time.
  • Leaving out protected_keys. A translated {user_name} breaks your template.
  • Sending untrusted language names. Check the requested language against a list you support.
  • Calling the API from JavaScript. The key must stay on the server. Call your own route, which calls RSFlowHub.
  • Showing an error instead of the original. If translation fails, show the source text. A page in the wrong language beats a broken page.
  • Shipping without a native review. Machine translation is good, not perfect. Have a speaker check important pages, pricing and legal text.

Translate Into Many Languages with One Call

The Translation API translates one text into up to 10 languages per request, with placeholder protection, a custom glossary and tone control. It works from Laravel, Node.js, Python or any backend.

View Translation API →
Check translation quality first
Before you build, test your own sentences in the free Hinglish and Hindi translator, including Hinglish and Indian languages. Free, no signup.

Key Takeaways

  • Wrap POST /api/v1/ai/translation in one service class and call it from controllers and jobs.
  • Request up to 10 target_languages in a single call and read data.result.translations.
  • Use protected_keys for placeholders and glossary for brand terms.
  • Cache every result and never cache a failed call.
  • Fall back to the original text when translation fails.
  • Use language files for fixed interface labels and the API for changing content.

Frequently Asked Questions

Up to 10 in the target_languages array. Use target_language for a single language.

List the variable names in protected_keys. They are left exactly as written in every translation.

Yes. Pass a glossary object of up to 30 entries, such as Wallet mapped to Billetera, and those terms are translated exactly as given.

Up to 15,000 characters per request. Split longer content on paragraph boundaries and translate each part.

By default yes. preserve_formatting is true unless you turn it off, so tags and Markdown symbols stay in place.

No. Keep language files for fixed interface labels. Use the API for content that changes often or that you do not control.

Reviews & Discussion (0)

0

Based on 0 ratings

5 ★
0%
4 ★
0%
3 ★
0%
2 ★
0%
1 ★
0%
Write a Review or Comment
No rating selected
No comments yet

Be the first to share your thoughts on this article!

Unified AI Access ⚡

One API Key. Infinite Possibilities.

Get access to OpenAI, Anthropic, Gemini, and more through a single, secure gateway. Start building in minutes with zero setup friction.

RSFlowHub

Sign in to continue

You'll come straight back to this page.

Forgot password?

New to RSFlowHub? Create a free account