禁用给定请求的翻译(laravel)

I'm currently building a Vue.js SPA with laravel as the backend where I need to support multiple locales. I'm therefore considering using Lang.js alongside laravel's built-in translation system to manage the translated stings. I basicly want to manage all the strings in laravel (on the backend) but let the frontend handle the actual translations, to allow the user to switch language dynamicly with a language switcher ui component.

The problem is that when I submit a form request that is validated on the server, the error messages are already translated, thus not allowing the frontend to pick a language on-the-fly.

My question is how I can disable the translation for a given request (preferably using headers in middleware or something like that) and instead print the raw translation keys

For example for the given validation

<?php
function store() {
    request()->validate(['test' => 'required'])
}

The server returns

{
    'message': 'The given data was invalid',
    'errors': {
        'test': 'The test field is required'
    }
}

instead of

{
    'message': 'The given data was invalid',
    'errors': {
        'test': 'validation.required',
    }
}

I have already tried using \App::setLocale('somethinginvalidandrandom') which should technically try to translate the key into a language that doesn't exist, thus returning the key, but besides feeling wrong and hacky, this approach won't work if I want to preserve the fallback language funcionality of laravel, since the key would be found in the fallback language (en).

I actually just solved it myself, and thought to reply here in case others where trying to solve the same problem as me in the future.

Solution

I added an invalidJson method to my App\Exceptions\Handler class, that Laravel will automaticly call whenever an ValidationException is thrown and $request->wantsJson() is true.

In that method i just checked if my custom header (X-WITH-UNTRANSLATED-VALIDATION) is present in the request with the value of "yes". If that is the case i get the validator instance from the exception, and grab all the rules.

However laravel represent these rules internally in "studly caps case" (StartsWith) and we want to get them in a snake case format ("starts_with"). To fix this I used the laravel collection helpers to map over the nested array and convert the rule keys to snake case.

Code

Here is the code from app\Exceptions\Handler.php.

/**
 * Convert a validation exception into a JSON response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Validation\ValidationException  $exception
 * @return \Illuminate\Http\Response
 */
protected function invalidJson($request, ValidationException $exception)
{
    //If the header is not present in the request 'no' wil be provided as the fallback value, thus not being equal with "yes"
    if($request->header('X-WITH-UNTRANSLATED-VALIDATION', 'no') === "yes") {
        $failed = $this->makeUntranslatedMessagesIntoSnakeCase($exception);

        return response([
            // Get the original exception message
            'message' => $exception->getMessage(),
            'errors' => $failed,
            // Add the original translated error messages under a diffrent key to help with debugging
            'translated_errors' => $exception->errors(),
        ], $exception->status);
    }

    //If the header is not present (with the right value) we return the default JSON response instead
    return parent::invalidJson($request, $exception);
}

/**
 * Convert the untranslated rule names of a validation exception into snake case.
 * 
 * @param \Illuminate\Validation\ValidationException  $exception
 * @return array
 */
protected function makeUntranslatedMessagesIntoSnakeCase(ValidationException $exception) : array
{
    return collect($exception->validator->failed())->map(function ($item) {
        return collect($item)->mapWithKeys(function ($values, $rule) {
            return [Str::snake($rule) => $values];
        });
    })->toArray();
}

Further improvements

If you need to disable translation for normal "non-json" requests, you can also override the invalid method in the Handler.php and do a similar thing, but just add the messages to the session instead. Likewise you could also add the translated messages alongside the untranslated messages.

Edit

I also added 'translated_errors' => $exception->errors(), to the response to help with debugging

Maybe not the answer you're looking for but you could implement an endpoint for switching languages in the backend. You can call this endpoint whenever you change language on the frontend. Additionally it allows you to save the selected language, so if a user were to exit the SPA and come back later the preferred language is not reset.