rdcstarr/laravel-multilanguage
Composer 安装命令:
composer require rdcstarr/laravel-multilanguage
包简介
A simple multilanguage package for Laravel with metadata support.
README 文档
README
Elegant package for managing multilanguage locale data in Laravel — with intelligent per‑language caching and a fluent API.
✨ Features
- 🌍 Multiple languages – easily switch & manage localized content
- 🔑 Key-value locale data – structured, namespaced keys per language
- ⚡ Smart cache – per-language forever cache with auto invalidation
- 📦 Batch operations – set or fetch many keys at once
- 🔄 Fluent API – expressive chaining (
localedata()->lang('en')->set(...)) - 🧩 Blade directives – simple templating helpers & conditionals
- 🗄️ Clean schema – normalized tables with FK constraints
📦 Installation
Install via Composer:
composer require rdcstarr/laravel-multilanguage
- (Optional) Publish migration files:
php artisan vendor:publish --provider="Rdcstarr\Multilanguage\MultilanguageServiceProvider" - Run migrations:
php artisan migrate
- (Recommended) Use the install command (runs migrations & seeds default languages):
php artisan multilanguage:install
Default seeded languages: English (en), Romanian (ro), French (fr).
🛠️ Artisan Commands
Install package (migrations + seed languages)
php artisan multilanguage:install [--force]
- --force : Skip interactive confirmation.
🔑 Usage
Set Values
// Single key per language localedata()->lang('en')->set('site.title', 'Welcome to our website'); localedata()->lang('ro')->set('site.title', 'Bine ai venit pe site-ul nostru'); // Batch (multiple keys) localedata()->lang('en')->setMany([ 'nav.home' => 'Home', 'nav.about' => 'About', 'nav.contact' => 'Contact', 'site.description'=> 'Best website ever', ]);
Get Values
$title = localedata()->lang('en')->get('site.title'); $titleWithDefault = localedata()->lang('es')->get('site.title', 'Default Title'); // Multiple $navigation = localedata()->lang('en')->getMany(['nav.home', 'nav.about', 'nav.contact']); // All for a language $allEnglish = localedata()->lang('en')->all(); // Current app locale (app()->getLocale()) $localized = localedata()->get('site.title', 'Fallback');
Working with Languages
use Rdcstarr\Multilanguage\Models\Language; // Create a language Language::create([ 'name' => 'Spanish', 'code' => 'es', 'flag' => '🇪🇸' ]); // Retrieve $languages = Language::all(); $english = Language::where('code', 'en')->first(); // Seed multiple values dynamically foreach (['en','ro','fr','es'] as $lang) { localedata()->lang($lang)->set("demo.message", "Message for {$lang}"); }
Facade
use Rdcstarr\Multilanguage\Facades\LocaleData; LocaleData::lang('en')->set('app.name', 'My App'); LocaleData::lang('ro')->set('app.name', 'Aplicația Mea'); $appName = LocaleData::lang('en')->get('app.name');
Helper
// Manager instance localedata(); // same as app('localedata') // Direct access (current locale) $title = localedata('site.title', 'Default Title');
Extra Operations
localedata()->lang('en')->has('site.title'); // existence localedata()->lang('en')->forget('old.unused.key'); // delete one localedata()->lang('en')->flushCache(); // clear cache for one language localedata()->flushAllCache(); // clear cache for all languages
🎨 Blade Directives
{{-- Current locale value (with optional default) --}}
@localedata('site.title', 'Default Title')
{{-- Specific language --}}
@localedataForLang('"en"', '"site.title"', '"Default"')
{{-- Conditional (current locale) --}}
@hasLocaledata('site.title')
<h1>{{ localedata('site.title') }}</h1>
@endhasLocaledata
{{-- Conditional (specific language) --}}
@hasLocaledataForLang('"ro"', '"site.title"')
<h1>{{ localedata()->lang('ro')->get('site.title') }}</h1>
@endhasLocaledataForLang
Placeholders
// welcome.user => "Hello :NAME, your :type_plural are ready! :message_limit"; $result = localedata()->get('welcome.user')->placeholders([ 'name' => 'john doe', 'type' => 'order', 'message' => 'This is a very long message that will be truncated at 50 characters automatically', ]); // Result: "Hello JOHN DOE, your orders are ready! This is a very long message that will be truncat..."
💡 Examples
Website Content
localedata()->lang('en')->setMany([ 'home.hero.title' => 'Welcome to Our Platform', 'home.hero.subtitle' => 'The best solution for your business', ]); localedata()->lang('ro')->setMany([ 'home.hero.title' => 'Bine ai venit pe Platforma Noastră', 'home.hero.subtitle' => 'Cea mai bună soluție pentru afacerea ta', ]);
SEO Meta
localedata()->lang('en')->setMany([ 'seo.home.title' => 'Home - Best Platform Ever', 'seo.home.description' => 'Discover our amazing platform features', ]); <title>{{ localedata()->get('seo.' . request()->route()->getName() . '.title', 'Default') }}</title>
User Personalization
$userId = auth()->id(); localedata()->lang('en')->setMany([ "user.{$userId}.welcome" => 'Welcome back!', ]); localedata()->lang('ro')->setMany([ "user.{$userId}.welcome" => 'Bine ai revenit!', ]);
Product Catalog
$productId = 123; localedata()->lang('en')->setMany([ "product.{$productId}.name" => 'Premium Laptop', "product.{$productId}.description" => 'High-performance laptop', ]);
🎯 Placeholders
The package includes powerful placeholder support with default date/time placeholders and the ability to add custom ones.
Default Placeholders (Available Automatically)
Date & Time:
:year- 2025:month- 10:month_name- October:month_short- Oct:day- 20:day_name- Monday:day_short- Mon:quarter- 4:hour- 14 (24h):hour_12- 2 (12h):minute- 30:second- 45:am_pm- AM/PM:am_pm_lower- am/pm
Complete Formats:
:date- 2025-10-20:date_formatted- 20/10/2025:date_us- 10/20/2025:time- 14:30:45:time_short- 14:30:datetime- 2025-10-20 14:30:45:timestamp- Unix timestamp:iso- 2025-10-20T14:30:45+00:00:ago- "1 second ago" (human readable)
App Info:
:app_name- Application name from config:app_env- Environment (production, local, etc.):app_url- Application URL
Other:
:week- Week number:day_of_year- Day of year (1-365):days_in_month- Days in current month:timezone- UTC
Usage in Translations
localedata()->set('footer.copyright', '© :year :app_name. All rights reserved.'); // Output: © 2025 My App. All rights reserved. localedata()->set('report.generated', 'Generated on :date at :time_short'); // Output: Generated on 2025-10-20 at 14:30 localedata()->set('greetings.time', 'It is :time, :day_name :month_name :day, :year'); // Output: It is 14:30:45, Monday October 20, 2025
Custom Placeholders
Method 1: Config File (Recommended for static values)
Publish config:
php artisan vendor:publish --tag=multilanguage-config
Edit config/multilanguage.php:
return [ 'placeholders' => [ 'company_name' => env('COMPANY_NAME', 'My Company'), 'support_email' => 'support@example.com', 'support_phone' => '+40 123 456 789', 'copyright_year' => date('Y'), 'vat_number' => 'RO12345678', ], ];
Usage:
localedata()->set('footer.copyright', '© :copyright_year :company_name. All rights reserved.'); localedata()->set('contact.info', 'Contact us at :support_email or call :support_phone');
Method 2: In AppServiceProvider (For dynamic values)
use Rdcstarr\Multilanguage\LocaleDataPlaceholders; use App\Models\User; public function boot() { // Set multiple placeholders at once LocaleDataPlaceholders::setCustomPlaceholders([ 'total_users' => User::count(), 'active_users' => User::where('active', true)->count(), 'latest_post' => Post::latest()->first()?->title, 'site_status' => Cache::get('site_status', 'online'), ]); // Or add individually LocaleDataPlaceholders::addCustomPlaceholder('active_sessions', Session::where('active', true)->count()); }
Usage:
localedata()->set('dashboard.stats', 'Platform Stats: :total_users users, :active_users active'); // Output: Platform Stats: 1,234 users, 567 active
Method 3: Helper Function
// Single placeholder set_placeholder('username', 'John Doe'); // Multiple placeholders set_placeholder([ 'product_name' => 'Super Widget', 'product_price' => '$99.99', ]);
Method 4: Runtime (Context-specific)
// In controller public function show(User $user) { $greeting = localedata()->get('user.welcome', '', [ 'username' => $user->name, 'last_login' => $user->last_login_at->diffForHumans(), ]); return view('profile', compact('greeting')); }
Translation:
localedata()->set('user.welcome', 'Hello :Username! Last seen: :last_login'); // Output: Hello John Doe! Last seen: 2 hours ago
Placeholder Transformations
All placeholders (custom and default) support automatic transformations:
set_placeholder('company_name', 'my company'); // Available transformations: :company_name // my company :Company_name // My company (ucfirst) :COMPANY_NAME // MY COMPANY (uppercase) :company_name_camel // myCompany :company_name_studly // MyCompany :company_name_snake // my_company :company_name_kebab // my-company :company_name_slug // my-company :company_name_plural // my companies :company_name_singular // my company :company_name_upper // MY COMPANY :company_name_lower // my company :company_name_title // My Company
Placeholder Priority
Placeholders are applied in the following order (last one overrides):
- Default placeholders (date, time, app info)
- Config placeholders (
config/multilanguage.php) - Custom static placeholders (
LocaleDataPlaceholders::setCustomPlaceholders()) - Runtime placeholders (parameter in
get()method)
Safe Placeholder Usage with User Input
⚠️ Important: When using placeholders with user-generated content, always escape the input to prevent unwanted placeholder replacement.
The Problem
// User searches for ":year" $query = ':year'; // Translation localedata()->set('search.meta_title', 'Search results for: :query'); // Without escaping $title = localedata()->get('search.meta_title')->placeholders(['query' => $query]); // Output: "Search results for: 2025" ❌ WRONG! :year in query was replaced
The Solution: Use escape_placeholders()
// In Controller public function search(Request $request) { $query = $request->input('q'); // ESCAPE user query $safeQuery = escape_placeholders($query); $title = localedata()->get('search.meta_title')->placeholders([ 'query' => $safeQuery ]); return view('search.results', compact('title', 'query')); }
Now it works correctly:
$userInput = ':year in review'; $safe = escape_placeholders($userInput); $title = localedata()->get('search.meta_title')->placeholders(['query' => $safe]); // Output: "Search results for: :year in review" ✅ CORRECT!
When to Escape
✅ ALWAYS escape for:
- User input (search queries, comments, usernames)
- User-generated titles/descriptions
- Any user-provided data in placeholders
❌ NO need to escape for:
- Admin-controlled database values
- Code constants
- System-calculated values (counters, dates)
- Default placeholders (
:year,:app_name, etc.)
Alternative Escape Methods
// Method 1: Helper function (recommended) $safe = escape_placeholders($userInput); // Method 2: Static method use Rdcstarr\Multilanguage\LocaleDataPlaceholders; $safe = LocaleDataPlaceholders::escape($userInput); // Method 3: Facade use Rdcstarr\Multilanguage\Facades\Placeholders; $safe = Placeholders::escape($userInput);
Practical Examples
Search Results:
Route::get('/search', function (Request $request) { $query = escape_placeholders($request->input('q')); $title = localedata()->get('search.meta_title')->placeholders([ 'query' => $query, // Safe! ]); return view('search', compact('title')); });
User Profile:
public function show(User $user) { $metaTitle = localedata()->get('profile.meta_title')->placeholders([ 'username' => escape_placeholders($user->name), // Escape username // :app_name and :year are safe (default placeholders) ]); return view('profile.show', compact('user', 'metaTitle')); }
Comment System:
public function store(Request $request, Post $post) { $notification = localedata()->get('comment.notification')->placeholders([ 'username' => escape_placeholders(auth()->user()->name), 'comment_preview' => escape_placeholders(Str::limit($request->content, 50)), ]); }
For more details on safe placeholder usage, see the Safe Placeholder Usage with User Input section above.
Real-World Examples
Footer Copyright
// Config 'placeholders' => [ 'company_name' => 'Acme Corporation', 'year' => date('Y'), ], // Translation localedata()->set('footer.text', '© :year :company_name. Made with ❤️ in Romania'); // Output: © 2025 Acme Corporation. Made with ❤️ in Romania
Live Statistics
// AppServiceProvider LocaleDataPlaceholders::setCustomPlaceholders([ 'total_users' => Cache::remember('stats.users', 3600, fn() => User::count()), 'total_posts' => Cache::remember('stats.posts', 3600, fn() => Post::count()), 'total_comments' => Cache::remember('stats.comments', 3600, fn() => Comment::count()), ]); // Translation localedata()->set('stats.overview', 'We have :total_users users who created :total_posts posts with :total_comments comments!');
Personalized Messages
// In controller $message = localedata()->get('user.welcome', '', [ 'username' => auth()->user()->name, 'unread_count' => auth()->user()->unreadNotifications()->count(), ]); // Translation localedata()->set('user.welcome', 'Welcome back, :Username! You have :unread_count new notifications.'); // Output: Welcome back, John! You have 5 new notifications.
Product Catalog
// AppServiceProvider $productId = 123; LocaleDataPlaceholders::setCustomPlaceholders([ 'site_name' => config('app.name'), 'total_products' => Product::count(), ]); // Translation localedata()->set('home.hero', 'Welcome to :site_name! We have :total_products amazing products.');
Email Subject
// Mailable public function build() { $userName = escape_placeholders($this->user->name); return $this->subject( localedata()->get('email.welcome.subject')->placeholders([ 'username' => $userName, ]) ); } // Translation localedata()->set('email.welcome.subject', 'Welcome :username! Your account on :app_name');
Closure Support
For complex logic, use Closures:
LocaleDataPlaceholders::addCustomPlaceholder('dynamic_greeting', function($content) { $hour = now()->hour; if ($hour < 12) return "Good morning, $content"; if ($hour < 18) return "Good afternoon, $content"; return "Good evening, $content"; }); // In translation with XML tag localedata()->set('greeting', '<dynamic_greeting>:username</dynamic_greeting>, welcome to our site!');
Debugging Placeholders
// View all custom placeholders $custom = LocaleDataPlaceholders::getCustomPlaceholders(); dd($custom); // Clear custom placeholders (useful in tests) LocaleDataPlaceholders::clearCustomPlaceholders();
Best Practices
- Use config for static values (company name, contact info)
- Use static placeholders for cached values (user counts, statistics)
- Use runtime placeholders for context-specific data (user names, IDs)
- Cache expensive queries when using in placeholders
- Always escape user input before using in placeholders
- Test transformations to find the best fit for your use case
⚡ Performance
- Per-language cache buckets
- Forever cache via rememberForever
- Automatic invalidation on writes / deletes
- Minimal queries (one load per language as needed)
- Batch write operations
🧪 Testing
composer test
📖 Resources
- Changelog for more information on what has changed recently. ✍️
👥 Credits
- Rdcstarr 🙌
📜 License
- License for more information. ⚖️
rdcstarr/laravel-multilanguage 适用场景与选型建议
rdcstarr/laravel-multilanguage 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 21 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 09 月 17 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「metadata」 「multilanguage」 「laravel」 「laravel-multilanguage」 「Rdcstarr」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 rdcstarr/laravel-multilanguage 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 rdcstarr/laravel-multilanguage 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 rdcstarr/laravel-multilanguage 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Puts the Symfony Translation Component on steroids
Puts the Symfony Translation Component on steroids
Yii2 Component to manage SEO data and metadata
Extremely simple meta tag generator.
Management package for i18n static texts for TBP
统计信息
- 总下载量: 21
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 24
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-09-17