Skip to content

HasLocalization

File: app/Traits/Common/HasLocalization.php

A Laravel Eloquent trait that automatically resolves localized JSON columns into the current locale's string value — so $model->name returns the Indonesian name when the app is in id locale, without writing $model->name['id'] everywhere.

Overview

Many models store multilingual content as JSON columns:

json
{"en": "School Name", "id": "Nama Sekolah"}

Without this trait, every access requires $model->name['en'] ?? $model->name['id']. With the trait, $model->name automatically returns the correct locale.


Why It Exists

  • Developer ergonomics: Reading a localized value is $room->name, not $room->name['en']
  • Consistency: Every localization follows the same JSON structure ({en: "...", id: "..."})
  • Fallback chain: If the current locale has no value, it falls back to any available locale, then shows -
  • Backward compatible: Non-localized columns are unaffected — the trait only intercepts columns listed in $localizations

Methods

getAttribute($key): mixed

Overrides Eloquent's getAttribute() to intercept localized columns.

php
public function getAttribute($key): mixed
{
    $value = parent::getAttribute($key);

    // Skip non-string keys or keys not in the localizations array
    if (! is_string($key) || $key === '') return $value;
    if (! property_exists($this, 'localizations')) return $value;

    $localizations = $this->localizations ?? [];
    if (! is_array($localizations) || ! in_array($key, $localizations, true)) return $value;

    return $this->resolveLocalizedValue($value);
}

Algorithm:

  1. Get the raw attribute value from the parent
  2. Check if the key is in the $localizations array
  3. If yes → resolve as localized JSON
  4. If no → return raw value (standard Eloquent behavior)

getLocalizationRaw(string $key): array

Returns the full JSON object for a localized field without resolution, useful for APIs or admin panels.

php
public function getLocalizationRaw(string $key): array
{
    $raw = parent::getAttribute($key);

    if (is_string($raw)) {
        $json = json_decode($raw, true);
        if (json_last_error() === JSON_ERROR_NONE && is_array($json)) {
            return $json;
        }
    }

    return is_array($raw) ? $raw : [];
}

Usage:

php
$model->name;                 // "Nama Sekolah" (resolved)
$model->getLocalizationRaw('name'); // ["en" => "School Name", "id" => "Nama Sekolah"]

resolveLocalizedValue(mixed $value): string

Core resolution algorithm:

php
protected function resolveLocalizedValue(mixed $value): string
{
    // Plain string → return trimmed or '-'
    if (is_string($value)) {
        $value = trim($value);
        return $value !== '' ? $value : '-';
    }

    // Not an array → no localization possible
    if (! is_array($value)) return '-';

    // Try current/base locale first
    $baseLocale = $this->resolveBaseLocale();
    $preferred = Arr::get($value, $baseLocale);
    if (is_string($preferred) && trim($preferred) !== '') {
        return trim($preferred);
    }

    // Fallback: return first non-empty value from any locale
    foreach ($value as $candidate) {
        if (is_string($candidate) && trim($candidate) !== '') {
            return trim($candidate);
        }
    }

    // Nothing found
    return '-';
}

Fallback chain:

Current locale → Any available locale → "-"

resolveBaseLocale(): string

Determines which locale to prefer:

php
protected function resolveBaseLocale(): string
{
    if (is_string($this->localizationBaseLocale) && trim($this->localizationBaseLocale) !== '') {
        return trim($this->localizationBaseLocale);
    }
    return (string) app()->getLocale();
}

Override the locale per-instance by setting $model->localizationBaseLocale.


How to Use

1. Add the trait to your model

php
use App\Traits\Common\HasLocalization;

class Room extends Model
{
    use HasLocalization;

    /**
     * Columns that store localized JSON.
     */
    protected array $localizations = ['name', 'description'];
}

2. Store localized data as JSON

php
$room->name = json_encode(['en' => 'Classroom A', 'id' => 'Ruang Kelas A']);
$room->save();

Or use the getLocalizationRaw() inverse when building the JSON:

php
$room->name = ['en' => 'Classroom A', 'id' => 'Ruang Kelas A'];
$room->save(); // Eloquent casts to JSON automatically

3. Read localized values

php
// App locale: en
$room->name; // "Classroom A"

// App locale: id
$room->name; // "Ruang Kelas A"

// Raw JSON (for API responses)
$room->getLocalizationRaw('name'); // ["en" => "Classroom A", "id" => "Ruang Kelas A"]

4. Override locale per-instance

php
$room->localizationBaseLocale = 'en';
$room->name; // Always returns English, regardless of app locale

Key Files

FilePurpose
app/Traits/Common/HasLocalization.phpThe trait — overrides getAttribute(), resolves localized JSON
app/Models/Any model that uses use HasLocalization and defines $localizations