mphpmaster/laravel-translatable

一个用于多语言模型的Laravel包。轻松设置、管理和使用本地化路由!

1.0.2 2021-03-26 11:48 UTC

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

致谢

版本