Language & Locale Value Objects
The Language and Locale value objects provide type-safe handling of language codes and locale identifiers, with localized names powered by Symfony Intl. These work together to enable proper internationalization support in your Laravel application.
Language Value Object
Section titled “Language Value Object”The Language value object represents a language using ISO 639-1 language codes.
Creating Language Instances
Section titled “Creating Language Instances”Create a language instance from an ISO 639-1 language code:
use Cline\Intl\ValueObjects\Language;
$language = Language::createFromString('en');The factory method accepts 2-letter ISO 639-1 language codes (e.g., en, fr, de, es).
Language Properties
Section titled “Language Properties”$language = Language::createFromString('en');
// Language codeecho $language->value; // "en"
// Localized language nameecho $language->localized; // "English"When you change your application’s locale, the localized property will reflect the language name in that locale:
app()->setLocale('fr');$language = Language::createFromString('en');echo $language->localized; // "anglais"
app()->setLocale('de');$language = Language::createFromString('en');echo $language->localized; // "Englisch"String Representation
Section titled “String Representation”The Language object implements Stringable and returns the language code:
$language = Language::createFromString('fr');
echo (string) $language; // "fr"echo $language->toString(); // "fr"Equality Comparison
Section titled “Equality Comparison”$language1 = Language::createFromString('en');$language2 = Language::createFromString('en');$language3 = Language::createFromString('fr');
$language1->isEqualTo($language2); // true$language1->isEqualTo($language3); // falseLocale Value Object
Section titled “Locale Value Object”The Locale value object represents a full locale identifier including language, region, script, and variant.
Creating Locale Instances
Section titled “Creating Locale Instances”Create a locale instance from a locale identifier:
use Cline\Intl\ValueObjects\Locale;
$locale = Locale::createFromString('en_US');The factory method accepts locale identifiers in various formats:
en_US(language + region)fr_FRde_DEpt_BRzh_CN
Locale Properties
Section titled “Locale Properties”$locale = Locale::createFromString('en_US');
// Locale identifierecho $locale->value; // "en_US"
// Localized locale nameecho $locale->localized; // "English (United States)"The localized property respects your application’s locale:
app()->setLocale('fr');$locale = Locale::createFromString('en_US');echo $locale->localized; // "anglais (États-Unis)"String Representation
Section titled “String Representation”$locale = Locale::createFromString('fr_FR');
echo (string) $locale; // "fr_FR"echo $locale->toString(); // "fr_FR"Equality Comparison
Section titled “Equality Comparison”$locale1 = Locale::createFromString('en_US');$locale2 = Locale::createFromString('en_US');$locale3 = Locale::createFromString('en_GB');
$locale1->isEqualTo($locale2); // true$locale1->isEqualTo($locale3); // falseEloquent Model Integration
Section titled “Eloquent Model Integration”Using Language and Locale Casts
Section titled “Using Language and Locale Casts”use Illuminate\Database\Eloquent\Model;use Cline\Intl\Data\Cast\LanguageCast;use Cline\Intl\Data\Cast\LocaleCast;
class User extends Model{ protected function casts(): array { return [ 'language' => LanguageCast::class, 'locale' => LocaleCast::class, ]; }}Working with the Casts
Section titled “Working with the Casts”// Store as strings, retrieve as value objects$user = new User();$user->language = 'en';$user->locale = 'en_US';$user->save();
// Automatically cast to value objectsecho $user->language->localized; // "English"echo $user->locale->localized; // "English (United States)"
// Update with strings$user->language = 'fr';$user->locale = 'fr_FR';$user->save();Database Schema
Section titled “Database Schema”Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('language', 2); // ISO 639-1 code $table->string('locale', 10); // Locale identifier $table->timestamps();});Validation
Section titled “Validation”Language Validation
Section titled “Language Validation”use Cline\Intl\Rules\LanguageRule;
class UpdateProfileRequest extends FormRequest{ public function rules(): array { return [ 'language' => ['required', 'string', new LanguageRule()], ]; }}Locale Validation
Section titled “Locale Validation”use Cline\Intl\Rules\LocaleRule;
class UpdatePreferencesRequest extends FormRequest{ public function rules(): array { return [ 'locale' => ['required', 'string', new LocaleRule()], ]; }}Common Use Cases
Section titled “Common Use Cases”User Language Preference
Section titled “User Language Preference”use Cline\Intl\ValueObjects\Language;use Cline\Intl\Data\Cast\LanguageCast;
class User extends Model{ protected function casts(): array { return [ 'language' => LanguageCast::class, ]; }
public function getPreferredLanguage(): string { return $this->language->value; }
public function setAppLocale(): void { app()->setLocale($this->language->value); }}
// Usage$user = auth()->user();$user->setAppLocale(); // Set application locale to user's preferenceContent Localization
Section titled “Content Localization”use Cline\Intl\ValueObjects\Language;use Cline\Intl\Data\Cast\LanguageCast;
class Article extends Model{ protected function casts(): array { return [ 'language' => LanguageCast::class, ]; }
public function scopeInLanguage($query, Language $language) { return $query->where('language', $language->value); }
public function hasTranslation(Language $language): bool { return $this->translations() ->where('language', $language->value) ->exists(); }}
// Usage$language = Language::createFromString('fr');$articles = Article::inLanguage($language)->get();Locale-Based Formatting
Section titled “Locale-Based Formatting”use Cline\Intl\ValueObjects\Locale;use Cline\Intl\Data\Cast\LocaleCast;
class User extends Model{ protected function casts(): array { return [ 'locale' => LocaleCast::class, ]; }
public function formatDate(\DateTime $date): string { $formatter = new \IntlDateFormatter( $this->locale->value, \IntlDateFormatter::LONG, \IntlDateFormatter::NONE );
return $formatter->format($date); }
public function formatNumber(float $number): string { $formatter = new \NumberFormatter( $this->locale->value, \NumberFormatter::DECIMAL );
return $formatter->format($number); }}
// Usage$user = User::find(1);$user->locale = 'fr_FR';
echo $user->formatDate(new \DateTime()); // "15 décembre 2025"echo $user->formatNumber(1234.56); // "1 234,56"Language Selector
Section titled “Language Selector”use Symfony\Component\Intl\Languages;
class LanguageController extends Controller{ public function index() { // Get all languages for a dropdown $languages = collect(Languages::getNames()) ->map(fn($name, $code) => [ 'code' => $code, 'name' => $name, ]) ->sortBy('name') ->values();
return view('languages.index', compact('languages')); }}Multi-Language Application
Section titled “Multi-Language Application”use Cline\Intl\ValueObjects\Language;
class LocalizationMiddleware{ public function handle($request, Closure $next) { // Set locale from authenticated user if ($user = auth()->user()) { app()->setLocale($user->language->value); } // Or from session elseif ($language = session('language')) { app()->setLocale($language); } // Or from Accept-Language header else { $language = $request->getPreferredLanguage(['en', 'fr', 'de', 'es']); app()->setLocale($language); }
return $next($request); }}Translation Management
Section titled “Translation Management”use Cline\Intl\ValueObjects\Language;use Cline\Intl\Data\Cast\LanguageCast;
class Translation extends Model{ protected function casts(): array { return [ 'language' => LanguageCast::class, ]; }
public function scopeForLanguage($query, Language $language) { return $query->where('language', $language->value); }}
class TranslatableModel extends Model{ public function translations() { return $this->hasMany(Translation::class); }
public function getTranslation(Language $language): ?string { return $this->translations() ->forLanguage($language) ->first() ?->content; }
public function translate(Language $language, string $content): Translation { return $this->translations()->create([ 'language' => $language->value, 'content' => $content, ]); }}Language vs. Locale: When to Use Which
Section titled “Language vs. Locale: When to Use Which”Use Language When:
Section titled “Use Language When:”- You only need to identify the language without regional specifics
- Managing content translations
- Simple language preferences
- Language-specific content filtering
// Simple language preference$user->language = 'en'; // Just English, no regional variantUse Locale When:
Section titled “Use Locale When:”- You need regional formatting (dates, numbers, currency)
- Full internationalization with regional differences
- Supporting multiple variants of the same language (en_US vs. en_GB)
- Using PHP’s Intl extension for formatting
// Full locale with regional formatting$user->locale = 'en_US'; // English with US formatting$user->locale = 'en_GB'; // English with UK formattingException Handling
Section titled “Exception Handling”Both value objects throw MissingResourceException for invalid codes:
use Symfony\Component\Intl\Exception\MissingResourceException;use Cline\Intl\ValueObjects\Language;use Cline\Intl\ValueObjects\Locale;
try { $language = Language::createFromString('invalid');} catch (MissingResourceException $e) { // Handle invalid language code}
try { $locale = Locale::createFromString('invalid_LOCALE');} catch (MissingResourceException $e) { // Handle invalid locale identifier}Always validate user input using validation rules to prevent exceptions.
Best Practices
Section titled “Best Practices”- Validate input - Use
LanguageRuleandLocaleRulein form requests - Store codes, not names - Store language codes and locale identifiers, not localized names
- Use middleware - Set application locale based on user preferences or request headers
- Consider fallbacks - Have a default language/locale for users who haven’t set preferences
- Respect regional differences - Use full locales when regional formatting matters
- Cache language lists - Cache the list of available languages for dropdowns
- Test with RTL languages - If supporting Arabic or Hebrew, test right-to-left layouts