支持Laravel.io的持续开发 →

将翻译系统集成到您的Laravel项目中使用Inertia和Vue

12 Feb, 2024 9 min read

如何在Laravel项目中快速设置使用Inertia和Vue的翻译系统。

Capsules Translations Image 0

一个示例Laravel项目可以在本Github仓库找到。更多内容请访问CapsulesX

虽然Laravel框架提供了一个默认的本地化系统,但它需要一些额外的添加来实现使用Laravel Inertia和Vue技术的网页工具的适当功能。本文将讨论这个话题。

从基本的Laravel Inertia Vue Tailwind项目开始,它尚未适配国际化。这一点可以通过简单地看到缺失的lang文件夹来证实。以下步骤为多语言工具奠定基础。

首先,将默认的Laravel lang文件夹及其翻译文件添加到template项目中。例如,Molière的语言

cd template
mkdir lang

lang/fr.json

{
    "Hello world!" : "Bonjour le monde!",
	"This is a translation" : "Ceci est une traduction",
    "Maintenance mode activated" : "Le mode maintenance est activé"
}

有三种翻译可供访问。在Vue组件中可见的键为英文翻译,而法文翻译为值。

在文件config/app.php中添加网站将包含的不同语言。本文中涉及enfr

config/app.php

/*
|--------------------------------------------------------------------------
| Application Available Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the available locales that will be used
| by the translation service provider. You are free to set this array
| to any of the locales which will be supported by the application.
|
*/

'available_locales' => [ 'en', 'fr' ],
  • 此配置将在实现语言更改按钮时对我们很有用。

现在可以将新信息注入到Inertia的HandleInertiaRequest中间件中的共享数据中。

app/Http/Middleware/HandleInertiaRequests.php

<?php

namespace App\Http\Middleware;

use Inertia\Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\File;

class HandleInertiaRequests extends Middleware
{
    public function share( Request $request ) : array
    {
        $file = lang_path( App::currentLocale() . ".json" );

        return array_merge( parent::share( $request ), [
            'csrf' => csrf_token(),
            'locale' => App::currentLocale(),
            'locales' => config( 'app.available_locales' ),
            'translations' => File::exists( $file ) ? File::json( $file ) : []
        ] );
    }
}
  • locale代表当前语言。
  • 《locales》表示不同的可用的语言,如 config( 'app.available_locales' ) 所示。
  • translations 将位于 lang 目录中并与当前语言链接的 JSON 文件中的可用翻译进行分组。如果没有文件存在,则返回的翻译数组将为空。

以下是检查共享数据内容的客户端方法

routes/web.php

<?php

use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

App::setLocale( 'fr' );

Route::get( '/', fn() => dd( Inertia::getShared() ) );
array:5 [▼ // routes/web.php:10
  "errors" => Closure() {#307 ▶}
  "csrf" => "QTGHRkM83KysIS7htTNEWfZ9sC6Cs7U20i6kSSeF"
  "locale" => "fr"
  "locales" => array:2 [▼
    0 => "en"
    1 => "fr"
  ]
  "translations" => array:2 [▼
    "Hello world!" => "Bonjour le monde!"
    "This is a translation" => "Ceci est une traduction"
  ]
]
  • 使用 App::setLocale('fr') 修改语言以识别不同的翻译。在这种情况下,其他可能性将为 translations 返回一个空数组。

现在可以正确配置 web.php 文件了。

routes/web.php

<?php

use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

App::setLocale( 'fr' );

Route::get( '/', fn() => Inertia::render( 'Welcome' ) )->name( 'welcome' );

在客户端,特别是 Vue 中,您需要设置一个 composable,它考虑了当前 locale 以显示从服务器提供并位于翻译数组中的正确 translation

mkdir resources/js/composables
cd resources/js/composables

resources/js/composables/trans.js

import { usePage } from '@inertiajs/vue3';

export function useTrans( value )
{
    const array = usePage().props.translations;

    return array[ value ] != null ? array[ value ] : value;
}
  • useTrans 如果存在则返回翻译,否则返回默认英文短语。

现在可以在 Welcome.vue 文件中实施文章开始时添加的翻译,通过将 "Capsules Codes" 替换为 "Hello world!" 并导入 useTrans 来实现。

resources/js/pages/Welcome.vue

<script setup>

import { useTrans } from '/resources/js/composables/trans';

import logotype from '/public/assets/capsules-logotype.svg';

</script>

<template>

    <div class="w-screen h-screen flex flex-col items-center justify-center text-center">

        <img class="w-24 h-24" v-bind:src="logotype" v-bind:alt="'Capsules Codes Logotype'">

        <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="useTrans( 'Hello world!' )" />

    </div>

</template>

Capsules Translations Image 1

现在是时候在 Welcome.vue 文件中实现导航栏了,直接列出不同的语言选项。

resources/js/pages/Welcome.vue

<script setup>

import { computed } from 'vue';
import { usePage } from '@inertiajs/vue3';
import { useTrans } from '/resources/js/composables/trans';

import logotype from '/public/assets/capsules-logotype.svg';

const locales = computed( () => usePage().props.locales );
const index = computed( () => locales.value.findIndex( value => value == usePage().props.locale ) + 1 );
const language = computed( () => locales.value[ index.value % locales.value.length ] );

</script>

<template>

    <div class="absolute h-12 w-full flex items-center justify-center">

        <a v-if=" locales.length > 1 " class="rounded-md outline-none hover:bg-slate-50 text-sm font-medium" v-bind:href="`/${language}`" v-text="`/ ${language}`" />

    </div>

    <div class="w-screen h-screen flex flex-col items-center justify-center text-center">

        <img class="w-24 h-24" v-bind:src="logotype" v-bind:alt="'Capsules Codes Logotype'">

        <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="useTrans( 'Hello world!' )" />

    </div>

</template>
  • 计算常量 locales 通过 Inertia 返回可用的语言。
  • 计算常量 index 代表当前 locale 后的索引。
  • 计算常量 language 代表当前语言后的语言。在这种情况下,如果我们有 frlanguage 将代表 en。如果只有一个语言,则不显示。如果有三个语言,则每个语言将依次滚动。

顶部栏中显示的语言是,然后是不在页面中使用的语言。现在的目标是将该选择应用于服务器端的 locale。然后 <a> 标签发送一个 GET 请求到 /fr/en,取决于 language

为了允许服务器理解这一点并通过此过程更改 locale,需要必要的中间件:SetLocale

app/Http/Middleware/SetLocale.php

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\URL;

class SetLocale
{
    public function handle( Request $request, Closure $next ) : Response
    {
        if( in_array( $request->segment( 1 ), config( 'app.available_locales' ) ) && $request->segment( 1 ) !== App::currentLocale() ) Session::put( 'locale', $request->segment( 1 ) );

        App::setLocale( Session::get( 'locale', App::currentLocale() ) );

        URL::defaults( [ 'locale' => App::currentLocale() ] );

        return $next( $request );
    }
}
  • 第一个条件检查 locale 是否在可用 locale 中。
  • 第二个条件检查给定的 locale 是否不同于当前 locale。
  • URL::defaults( [ 'locale' => App::currentLocale() ] ); 允许添加 locale 到 URL。

《SetLocale》中间件的作用是初始化或更改 locale,并将它添加到 URL。

然后将此中间件添加到 Kernel 文件中。中间件的位置很重要,但这只取决于其用途。将其放在 PreventRequestsDuringMaintenance 维护中间件之前很有用,以便在维护期间也能从翻译中受益。

app/Http/Kernel.php

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    protected $middleware = [
        ...
        \App\Http\Middleware\SetLocale::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        ...
    ];
    ...

web.php 文件中,需要新的前缀、新的路由和回退。

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::prefix( '{locale}' )->where( [ 'locale' => '[a-zA-Z]{2}' ] )->group( function()
{
	Route::get( '', fn() => redirect()->route( Route::getRoutes()->match( Request::create( URL::previous() ) )->getName() ) ?? 'welcome' );

	Route::get( 'welcome', fn() => Inertia::render( 'Welcome' ) )->name( 'welcome' ); 
} );

Route::fallback( fn() => redirect()->route( 'welcome' ) );
  • 如其名称所示,Route::prefix( '{locale}' ) 向每个路由添加前缀。这里,它将是 locale。
  • 此 locale 必须遵循 where(['locale' => '[a-zA-Z]{2}']),这相当于两个介于 aZ 之间的字母。
  • 由于路由 '''/' 是相同的,因此需要将初始路由 welcome 重定向到 'welcome'
  • 现在可以删除 App::setLocale( 'fr' );
  • Route::fallback( fn() => redirect()->route('welcome') ); 表示如果请求没有匹配到任何路由,将会重定向到 'welcome' 路由。这是一种处理错误并避免在这种情况下出现 404 页面的方法。
  • 在更改语言环境时不要指定路由名称,否则可能会发生重定向的无限循环。

翻译系统现在已经可用。🎉

为了避免在每一个 href 引用中加入语言环境,等其它方法,可以向 trans.js 的可组合中添加另一个函数: useRoute

resources/js/composables/trans.js

import { usePage } from '@inertiajs/vue3';

...

export function useRoute( value = null )
{
    return `/${usePage().props.lang}${value ?? ''}`;
}
import { useRoute, useTrans } from '~/composables/trans';

<a v-bind:href="useRoute( `/welcome` )"><span v-text="useTrans( 'Welcome' )" />

由于路由有了前缀,现在可以从它们的闭包中访问它们。

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::prefix( '{locale}' )->where( [ 'locale' => '[a-zA-Z]{2}' ] )->group( function()
{
    ...

    Route::get( 'translate', fn( string $locale ) => dd( __( "This is a translation", [], $locale ) ) );

    ...
} );

...
"Ceci est une traduction" // routes/web.php:13

在维护期间,如前所述,虽然确实分配了语言环境,但由于在调用 HandleInertiaRequest 中间件之前将调用 PreventRequestDuringMaintenance 中间件,因此不会发送翻译。因此,您需要手动将它们注入到 Handler 中。

app/exceptions/handler.php

use Symfony\Component\HttpFoundation\Response;
use Inertia\Response as InertiaResponse;
use Inertia\Inertia;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\File;

public function render( $request, Throwable $exception ) : Response | InertiaResponse
{
    $response = parent::render( $request, $exception );

    if( $response->status() === 503 )
    {
        Inertia::share( 'locale', App::currentLocale() );
        Inertia::share( 'translations', File::exists( lang_path( App::currentLocale() . ".json" ) ) ? File::json( lang_path( App::currentLocale() . ".json" ) ) : [] );

		return Inertia::render( 'Error' )->toResponse( $request )->setStatusCode( $response->status() );
    }

    return $response;
}

resources/js/pages/Error.vue

<script setup>

import { useTrans } from '/resources/js/composables/trans';

</script>

<template>

    <div class="w-screen h-screen flex items-center justify-center text-center space-y-8">

        <h1 class="text-6xl font-bold select-none header-mode" v-text="useTrans( 'Maintenance mode activated' )" />

    </div>

</template>
php artisan down

Capsules Translations Image 2

很高兴这有帮助。

最后更新 5 个月前。

driesvints, fanatp, antoniputra 喜欢这篇文章

3
喜欢这篇文章吗? 让作者知道并给予他们掌声!
mho (MHO) 全职副业全栈 web 开发者 | 设计师 工作于 http://capsules.codes

你可能还喜欢其他文章

2024年3月11日

如何使用 Larastan 将 Laravel 应用从0到9进行改进

在 Laravel 应用执行之前找到错误是可能的,这要归功于 Larastan,它...

阅读文章
2024年7月19日

在不使用 traits 的情况下标准化 API 响应

我注意到的许多用于 API 响应的库...

阅读文章
2024年7月17日

如何在Laravel项目中创建反馈模块,并在收到消息时获取Discord通知

在Laravel项目中创建反馈模块以及在收到消息时获得Discord通知的方法...

阅读文章

我们感谢以下这些 令人惊叹的公司 对我们的支持

您的标志在这里?

Laravel.io

用于问题解决、知识共享和社区建设的Laravel门户。

© 2024 Laravel.io - 版权所有。