Stop Hardcoding Translations in Laravel - Use Translatable

Published: (December 14, 2025 at 01:11 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

🤔 The Problem

Building a multilingual Laravel app? You might end up with messy code like this:

// ❌ The nightmare approach
switch ($status) {
    case 'draft':
        return $locale === 'es' ? 'Borrador' :
               ($locale === 'fr' ? 'Brouillon' : 'Draft');
    // ... more chaos
}

There’s a much cleaner way.

✨ The Solution: Translatable Enums

Install It

composer require osama/laravel-enum-translatable

Create Your Enum

// Example enum (Laravel Enum Translatable)
enum ArticleStatus: string
{
    use EnumTranslatable;

    case DRAFT     = 'draft';
    case PUBLISHED = 'published';
    case REJECTED  = 'rejected';
}
// lang/en/enums.php
return [
    'article_statuses' => [
        'draft'     => 'Draft',
        'published' => 'Published',
        'rejected'  => 'Rejected',
    ],
];
// lang/es/enums.php
return [
    'article_statuses' => [
        'draft'     => 'Borrador',
        'published' => 'Publicado',
        'rejected'  => 'Rechazado',
    ],
];
// lang/fr/enums.php
return [
    'article_statuses' => [
        'draft'     => 'Brouillon',
        'published' => 'Publié',
        'rejected'  => 'Rejeté',
    ],
];

Use It Everywhere

$status = ArticleStatus::DRAFT;

// Current locale
$label = $status->trans(); // "Draft" (en) or "Borrador" (es) or "Brouillon" (fr)

// Specific locale
$spanish = $status->trans('es'); // "Borrador"

// All translations at once
$all = $status->allTrans(); // ['en' => 'Draft', 'es' => 'Borrador', 'fr' => 'Brouillon']

🚀 Real Example: Blog API

The Model

use App\Enums\ArticleStatus;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected function casts(): array
    {
        return [
            'status' => ArticleStatus::class,
        ];
    }
}

The Controller

public function show(Article $article)
{
    return response()->json([
        'id'    => $article->id,
        'title' => $article->title,
        'status'=> [
            'value'            => $article->status->value,
            'label'            => $article->status->trans(),
            'all_translations' => $article->status->allTrans(),
        ],
    ]);
}

The Response

{
  "id": 1,
  "title": "Getting Started with Laravel",
  "status": {
    "value": "published",
    "label": "Published",
    "all_translations": {
      "en": "Published",
      "es": "Publicado",
      "fr": "Publié"
    }
  }
}

Perfect for multi‑language frontends! 🌍

💪 Advanced: Combine with Business Logic

Enum Definition

enum OrderStatus: string
{
    use EnumTranslatable;

    case PENDING   = 'pending';
    case PAID      = 'paid';
    case SHIPPED   = 'shipped';
    case DELIVERED = 'delivered';

    public function badge(): string
    {
        return match ($this) {
            self::PENDING   => 'warning',
            self::PAID      => 'info',
            self::SHIPPED   => 'primary',
            self::DELIVERED => 'success',
        };
    }

    public function canBeCancelled(): bool
    {
        return in_array($this, [self::PENDING, self::PAID]);
    }
}

In Your Blade View

<span class="badge badge-{{ $order->status->badge() }}">
    {{ $order->status->trans() }}
</span>

@if ($order->status->canBeCancelled())
    <button>{{ __('Cancel Order') }}</button>
@endif

Clean, readable, maintainable! ✨

🎯 Why This Rocks

  • Type‑Safe – PHP 8.1+ enum power
  • DRY – Write translation once, use everywhere
  • Clean Code – No scattered conditionals
  • Scalable – Add languages without code changes
  • Testable – Easy to unit test

🧪 Quick Test Example

public function test_translations_exist_for_all_locales()
{
    $locales = ['en', 'es', 'fr'];

    foreach (ArticleStatus::cases() as $status) {
        foreach ($locales as $locale) {
            $translation = $status->trans($locale);

            $this->assertNotNull($translation);
            $this->assertNotEmpty($translation);
        }
    }
}

📦 Quick Reference

// Get current locale translation
$status->trans();

// Get specific locale
$status->trans('es');

// Get all translations
$status->allTrans();

// In models (automatic casting)
protected $casts = ['status' => ArticleStatus::class];

💡 Pro Tips

Always set a fallback

// config/app.php
'fallback_locale' => 'en',

Dynamic form selects

$options = collect(ArticleStatus::cases())
    ->map(fn($s) => [
        'value' => $s->value,
        'label' => $s->trans(),
    ]);

Localized notifications

$locale  = $user->locale ?? 'en';
$message = "Status changed to: " . $order->status->trans($locale);

🌟 Common Use Cases

  • E‑commerce: Order statuses, payment statuses
  • CMS: Article statuses, content types
  • SaaS: Subscription tiers, user roles
  • Task Management: Priority levels, task states
  • Multi‑tenant Apps: Per‑tenant translations
Back to Blog

Related posts

Read more »