mphpmaster / laravel-translatable
一个用于多语言模型的Laravel包。轻松设置、管理和使用本地化路由!
Requires
- php: >=7.2
- codezero/laravel-localizer: ^1.1
- illuminate/contracts: 5.8.* || ^6.0 || ^7.0 || ^8.0
- illuminate/database: 5.8.* || ^6.0 || ^7.0 || ^8.0
- illuminate/support: 5.8.* || ^6.0 || ^7.0 || ^8.0
Requires (Dev)
- orchestra/testbench: 3.8.* || ^4.0 || ^5.0 || ^6.0
- phpunit/phpunit: ^8.0 || ^9.0
This package is auto-updated.
Last update: 2024-09-26 20:19:48 UTC
README
如果您想将模型的翻译存储到数据库中,这个包就是为您准备的。
这是一个用于可翻译模型的Laravel包。其目标是简化检索和存储多语言模型实例的复杂性。使用此包,您可以编写更少的代码,因为翻译将在您检索/保存实例时自动获取/保存。
完整的文档可以在GitHub上找到。
安装
composer require mphpmaster/laravel-translatable
快速示例
获取已翻译的属性
$book = Book::first(); echo $book->translate('en')->title; // English App::setLocale('en'); echo $book->title; // English App::setLocale('fr'); echo $book->title; // French
保存已翻译的属性
$book = Book::first(); echo $book->translate('en')->title; // English $book->translate('en')->title = 'English Lang'; $book->save(); $book = Book::first(); echo $book->translate('en')->title; // English Lang
填充多个翻译
$data = [ 'author' => 'name', 'en' => ['title' => 'English'], 'fr' => ['title' => 'French'], ]; $book = Book::create($data); echo $book->translate('fr')->title; // French
路由
?? 配置
?? 发布配置文件
php artisan vendor:publish --provider="mPhpMaster\Translatable\LocalizedRoutesServiceProvider" --tag="config"
现在您将在config文件夹中找到一个laravel-translatable.php文件。
?? 支持的语言
使用别名
将您希望支持的语言添加到已发布的config/laravel-translatable.php文件中
'supported-locales' => ['en', 'nl', 'fr'],
这将自动在您的本地化路由前添加一个别名。有关更多信息,请参阅以下内容此处。
使用域名
或者,您可以通过配置supported-locales使用不同的域名或子域名来为每个语言设置
'supported-locales' => [ 'en' => 'example.com', 'nl' => 'nl.example.com', 'fr' => 'fr.example.com', ],
?? 主语言不使用别名
如果您想从URL中省略主语言的别名,请指定您的默认语言
'omit_url_prefix_for_locale' => null
将此选项设置为'en'将导致URL如下所示
- 英语:
/some-url而不是默认的/en/some-url - 荷兰语:
/nl/some-url如常 - 法语:
/fr/some-url如常
如果使用域名而不是别名,则此选项不起作用。
?? 使用中间件更新应用程序语言
默认情况下,应用程序语言将始终是您在config/app.php中配置的语言。要自动更新访问本地化路由时的应用程序语言,您需要使用中间件。
?? 对于Laravel 6+的重要说明
为了使Laravel 6+中的路由模型绑定工作,您还必须在app/Http/Kernel.php中的$middlewarePriority数组中添加中间件,以确保它在SubstituteBindings之前运行
protected $middlewarePriority = [ \Illuminate\Session\Middleware\StartSession::class, // <= after this //... \mPhpMaster\Translatable\Middleware\SetLocale::class, //... \Illuminate\Routing\Middleware\SubstituteBindings::class, // <= before this ];
您可以通过以下几种方式启用中间件
为每个本地化路由,通过我们的配置文件
只需将选项设置为true即可将中间件添加到每个本地化路由
'use_locale_middleware' => true
这不会将中间件添加到非本地化路由!
或者,为每个使用web中间件组的路由
您可以在app/Http/Kernel.php中的$middlewareGroups数组中手动添加中间件
protected $middlewareGroups = [ 'web' => [ \Illuminate\Session\Middleware\StartSession::class, // <= after this //... \mPhpMaster\Translatable\Middleware\SetLocale::class, //... \Illuminate\Routing\Middleware\SubstituteBindings::class, // <= before this ], ];
或者,为特定路由
或者,您可以将中间件添加到特定路由或路由组
Route::localized(function () { Route::get('about', AboutController::class.'@index') ->name('about') ->middleware(\mPhpMaster\Translatable\Middleware\SetLocale::class); Route::group([ 'as' => 'admin.', 'middleware' => [\mPhpMaster\Translatable\Middleware\SetLocale::class], ], function () { Route::get('admin/reports', ReportsController::class.'@index') ->name('reports.index'); }); });
?? 使用Localizer检测并设置语言
此包可以使用codezero/laravel-localizer来自动检测和设置语言。
禁用此选项时,应用程序语言仅在访问本地化路由时更新。
启用此选项后,在访问未本地化的路由时,应用程序的区域设置也可以更新。对于未本地化的路由,它将在会话、cookie或浏览器中寻找首选的区域设置。此外,它还会将应用程序的区域设置存储在会话和cookie中。
如果您有一个通用主页并希望了解首选的区域设置,启用此选项将很有用。
要启用此选项,请将发布配置文件中的设置设置为true。
'use_localizer' => true
此选项仅对使用我们的SetLocale中间件的路线有效。
您可以查看codezero/laravel-localizer,发布其配置文件并根据需要调整。我们将覆盖的唯一选项是
supported-locales,以匹配我们自己的配置文件中的选项。
?? 设置当前本地化路由组的选项
要为单个本地化路由组设置选项,您可以将它指定为本地化路由宏的第二个参数。这将覆盖配置文件设置。
Route::localized(function () { Route::get('about', AboutController::class.'@index') ->name('about'); }, [ 'supported-locales' => ['en', 'nl', 'fr'], 'omit_url_prefix_for_locale' => null, 'use_locale_middleware' => false, ]);
? 注册路由
示例
// Not localized Route::get('home', HomeController::class.'@index') ->name('home'); // Localized Route::localized(function () { Route::get('about', AboutController::class.'@index') ->name('about'); Route::name('admin.')->group(function () { Route::get('admin/reports', ReportsController::class.'@index') ->name('reports.index'); }); });
在上面的示例中,有5个路由正在注册。在Route::localized闭包中定义的路由将自动为每个配置的区域设置注册。这将把区域设置添加到路由的URI和名称前面。如果您配置了自定义域名,它将使用这些域名而不是slug。
如果您在配置文件中将omit_url_prefix_for_locale设置为'en',则生成的路由看起来像这样
注意:省略区域设置时不要重复注册相同的URL。在这种情况下,您不能同时有一个本地化的/about路由和非本地化的/about路由。同样的想法也适用于/(根)路由!请注意,路由名称仍然有区域设置前缀。
? 本地化404页面
默认情况下,Laravel的404页面不会通过中间件,并且没有与它关联的Route::current()。即使您创建了自定义的errors.404视图,也是如此。因此,无法通过中间件自动将区域设置设置为与请求的URL匹配。
要启用本地化404页面,您需要注册一个fallback路由并确保它具有SetLocale中间件。这基本上是一个捕获所有非现有URL的通配符路由。
Route::fallback(function () { return response()->view('errors.404', [], 404); })->middleware(\mPhpMaster\Translatable\Middleware\SetLocale::class);
需要注意的是,默认情况下,fallback路由返回一个200状态。因此,要使其成为一个真正的404,您需要自己返回一个404响应。
以下情况下不会触发回退路由
- 您的现有路由抛出
404错误(如abort(404)) - 您的现有路由抛出
ModelNotFoundException(如与路由模型绑定一起使用) - 您的现有路由抛出任何其他异常
因为这些路由实际上是注册的,所以404页面将具有正确的App::getLocale()设置。
? 生成路由URL
您可以使用route()辅助函数像往常一样获取命名路由的URL。
? 可怕的方式...
通常,您必须在生成URL时始终包含区域设置。
$url = route(app()->getLocale().'.admin.reports.index');
? 更好的方式...
因为前者相当丑陋,所以这个包重写了route()函数和底层的UrlGenerator类,并添加了一个可选的$locale参数,并为您处理区域设置前缀。如果您未指定区域设置,则返回一个正常的、非本地化的路由或当前区域设置的路由。
$url = route('admin.reports.index'); // current locale $url = route('admin.reports.index', [], true, 'nl'); // dutch URL
这是新的路由辅助函数签名
route($name, $parameters = [], $absolute = true, $locale = null)
一些示例(假设我们上面注册的示例路由)
app()->setLocale('en'); app()->getLocale(); // 'en' $url = route('home'); // /home (normal routes have priority) $url = route('about'); // /en/about (current locale) // Get specific locales... // This is most useful if you want to generate a URL to switch language. $url = route('about', [], true, 'en'); // /en/about $url = route('about', [], true, 'nl'); // /nl/about // You could also do this, but it kinda defeats the purpose... $url = route('en.about'); // /en/about $url = route('en.about', [], true, 'nl'); // /nl/about
注意:在大多数实际场景中,您会注册本地化或非本地化路由,但不会同时两者都注册。如果您这样做,在调用
route()函数时,非本地化路由总是具有优先权,因此您始终需要指定一个区域来获取URL。
重定向到路由
Laravel的 Redirector 在幕后使用与 route() 函数相同的 UrlGenerator。因为我们在重写这个类,所以您可以轻松地将路由重定向到您的路由。
return redirect()->route('home'); // non-localized route, redirects to /home return redirect()->route('about'); // localized route, redirects to /en/about (current locale)
您不能以这种方式将URL重定向到特定区域的URL,但如果您需要,当然可以使用 route() 函数。
return redirect(route('about', [], true, 'nl')); // localized route, redirects to /nl/about
?? 生成当前URL的本地化版本
要生成任何区域当前路由的URL,您可以使用这个 Route 宏。
带有路由模型绑定
如果您的路由使用本地化键(如缩略名),并且您正在使用 路由模型绑定,则键将自动本地化。
$current = \Route::localizedUrl(); // /en/books/en-slug $en = \Route::localizedUrl('en'); // /en/books/en-slug $nl = \Route::localizedUrl('nl'); // /nl/books/nl-slug
如果您有一个具有多个键的路由,例如 /en/books/{id}/{slug},那么您可以自己传递参数(如下面的示例中不使用路由模型绑定那样),或者您可以在您的模型中实现此接口。
use mPhpMaster\Translatable\ProvidesRouteParameters; use Illuminate\Database\Eloquent\Model; class Book extends Model implements ProvidesRouteParameters { public function getRouteParameters($locale = null) { return [ $this->id, $this->getSlug($locale) // Add this method yourself of course :) ]; } }
现在,只要您使用路由模型绑定,您仍然可以这样做:
$current = \Route::localizedUrl(); // /en/books/en-slug $en = \Route::localizedUrl('en'); // /en/books/en-slug $nl = \Route::localizedUrl('nl'); // /nl/books/nl-slug
不使用路由模型绑定
如果您不使用 路由模型绑定 并且您需要在URL中使用本地化缩略名,那么您将不得不手动传递它。
例如
$nl = \Route::localizedUrl('nl'); // Wrong: /nl/books/en-slug $nl = \Route::localizedUrl('nl', [$book->getSlug('nl')]); // Right: /nl/books/nl-slug
getSlug() 方法只是为了说明,因此您当然需要自己实现它。
?? 生成签名路由URL
生成 签名路由URL 与此类似。
传递路由名称、必要的参数,您将获得当前区域的URL。
$signedUrl = URL::signedRoute('reset.password', ['user' => $id], now()->addMinutes(30));
您也可以为特定区域生成签名URL。
$signedUrl = URL::signedRoute($name, $parameters, $expiration, true, 'nl');
有关签名路由的更多信息,请查看 Laravel文档。
? 翻译路由
如果您想翻译URI的段,为每个您 配置 的区域创建一个 routes.php 语言文件。
resources
??? lang
??? en
? ??? routes.php
??? nl
??? routes.php
在这些文件中,为每个段添加翻译。
// lang/nl/routes.php return [ 'about' => 'over', 'us' => 'ons', ];
现在您可以在路由注册期间使用我们的 Lang::uri() 宏。
Route::localized(function () { Route::get(Lang::uri('about/us'), AboutController::class.'@index') ->name('about.us'); });
请注意,为了找到路由的翻译版本,您需要给您的路由命名。如果您没有命名您的路由,则只有参数(模型路由键)会被翻译,而不是“硬编码”的缩略名。
上面将生成:
- /en/about/us
- /nl/over/ons
如果没有找到翻译,则使用原始段。
? 路由参数
参数占位符不会通过语言文件进行翻译。这些是通过 route() 函数提供的值。 Lang::uri() 宏将跳过任何参数占位符段。
如果您有一个使用当前区域中翻译的路由键的路由模型,那么您仍然可以简单地通过将模型传递给 route() 函数来获取翻译的URL。
示例...
假设我们有一个这样的模型
class Book extends \Illuminate\Database\Eloquent\Model { public function getRouteKey() { $slugs = [ 'en' => 'en-slug', 'nl' => 'nl-slug', ]; return $slugs[app()->getLocale()]; } }
提示:查看 spatie/laravel-translatable 了解可翻译模型。
如果我们有一个本地化路由如下所示
Route::localized(function () { Route::get('books/{book}', BooksController::class.'@show') ->name('books.show'); });
现在我们可以获取带有适当缩略名的URL
app()->setLocale('en'); app()->getLocale(); // 'en' $book = new Book; $url = route('books.show', [$book]); // /en/books/en-slug $url = route('books.show', [$book], true, 'nl'); // /nl/books/nl-slug
? 路由模型绑定
如果您启用此包中包含的 中间件,您可以使用 Laravel的路由模型绑定 在控制器中自动注入具有本地化路由键的模型。
您需要做的只是将一个resolveRouteBinding()方法添加到您的模型中。请参阅Laravel的文档,了解启用路由模型绑定替代方法的更多信息。
public function resolveRouteBinding($value) { // Perform the logic to find the given slug in the database... return $this->where('slug->'.app()->getLocale(), $value)->firstOrFail(); }
提示:查看 spatie/laravel-translatable 了解可翻译模型。
? 缓存路由
在生产环境中,您可以像往常一样安全地缓存您的路由。
php artisan route:cache
? 测试
composer test
致谢
- hlaCk 作者