Tienus McVinger
Tienus McVinger

Reputation: 477

htmlspecialchars error when using multiple translation files in laravel

I'm currently working on translations for my laravel project. It's all working fine, but only when I'm working with just one translation file.

I'm trying to keep things organised, and for that purpose I had the idea to have one translation file for the side-wide components (login/logout links, back buttons, stuff like that) and then introduce other translation files for more specific things, such as translating components found on your profile's dashboard.

Here are, for example, my English translation files:

general.php

return [
    "login" => "Login",
    "register" => "Register",
    "logout" => "Logout",
    "back" => "Back",
    "postBy" => "Posted by",
    "name" => "Name",
    "email" => "E-Mail Address",
    "pass" => "Password",
    "confirmPass" => "Confirm Password",
    "rememberMe" => "Remember Me", 
    "forgotPass" => "Forgot Your Password?",
];

dashboard.php

<?php

return [
    "title" => "Your Messages",
    "header" => "Message",
    "create" => "Add Message",
    "edit" => "Edit",
    "delete" => "Delete",
];

I'm already getting errors by doing this, without even using it in my dashboard.blade.php file. The error I get is:

ErrorException (E_ERROR) htmlspecialchars() expects parameter 1 to be string, array given (View: C:\xampp\htdocs\messageboard\resources\views\layouts\app.blade.php) (View: C:\xampp\htdocs\messageboard\resources\views\layouts\app.blade.php)

while I haven't even attempted to call it with something like {{ __('dashboard.title') }}. I'm at a loss as to what's causing this error.

As requested, here is the view causing the error. I'm getting the same error no matter what page is loaded, so I'm assuming that it'll be this very view as it's basically included and expanded upon in every other view.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">

</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    <i class="far fa-envelope"></i>
                    {{ config('app.name', 'Messageboard') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <!-- Authentication Links -->
                        @guest
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('login') }}">{{ __('general.login') }}</a>
                            </li>
                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('general.register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="/dashboard">{{ __('Dashboard') }}</a>
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('general.logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <div class="container">
            <main class="py-4">
                @include('inc.statusmessages')
                @yield('content')
            </main>
        </div>    
    </div>
</body>
</html>

The controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Message;
use App\User;
use Auth;

class MessagesController extends Controller
{

    public function __construct(){
        $this->middleware('auth', [
            'except' => [
                'index',
                'show'
            ]
        ]);
    }
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $messages = Message::orderBy('created_at', 'desc')->get();
        return view('messages')->with('messages', $messages);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('createmessage');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $this->validate($request, [
            'title' => 'required',
            'body' => 'required|min:15|max:500'
        ]);


        // Create Message
        $message = new Message;
        $message->title = $request->input('title');
        $message->body = $request->input('body');
        $message->status = 100;
        $message->user_id = auth()->user()->id;

        $message->save();

        return redirect('/dashboard')->with('success', 'Message Created');
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $message = Message::findOrFail($id);       
        $user = User::findOrFail($message->user_id); 

        $messageData = [
            'message' => $message,
            'user' => $user
        ];

        return view('showmessage')->with($messageData);
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $userId = 0;
        $message = Message::findOrFail($id);

        if (Auth::check())
        {
            // The user is logged in...
            $userId = Auth::user()->id;
        }

        if((int)$userId !== (int)$message->user_id) {
            return "Poster ID: ".$message->user_id.", User ID: ".$userId;
        }

        return view('editmessage')->with('message', $message);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'title' => 'required',
            'body' => 'required|min:15|max:500'
        ]);

        $message = Message::find($id);
        $message->title = $request->input('title');
        $message->body = $request->input('body');
        $message->status = 100;
        $message->user_id = auth()->user()->id;

        $message->save();

        return redirect('/dashboard')->with('success', 'Message Updated');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $message = Message::find($id);
        $message->delete();

        return redirect('/dashboard')->with('success', 'Message Removed');
    }
}

Any help would be appreciated. I hope I'm clear enough, and if not please let me know.

Upvotes: 0

Views: 617

Answers (1)

Travis Britz
Travis Britz

Reputation: 5552

As @Magnus Eriksson pointed out, this is because you're using {{ __('Dashboard') }} in your blade view and have a dashboard.php translation file.

The __() translation helper will normally fall back to returning the string it's given when there are no matching translation keys, but in this case it does find the translation file. Since you're not using a specific key from the file such as dashboard.title, it's returning the full translation array from the file, which is then given to htmlentities() when the blade view is rendered.

Upvotes: 1

Related Questions