Darkdawg's avatar

Every translation helper has a broad return type?

I noticed the translation helpers __() and trans() return array|string|null.

This means you cannot print/cast these to string without getting errors from PHPStan.

Shouldn't Laravel come with at least a string return type translation helper out of the box? Or am I missing something?

0 likes
11 replies
tykus's avatar

It needs to be able to return multiple types. The translation helper will return an array, a string or null because in certain circumstances you might be retrieving an array of translation messages, e.g.

__('validation.between')
/**
    'between' => [
        'array' => 'The :attribute field must have between :min and :max items.',
        'file' => 'The :attribute field must be between :min and :max kilobytes.',
        'numeric' => 'The :attribute field must be between :min and :max.',
        'string' => 'The :attribute field must be between :min and :max characters.',
    ],
*/

...or, you might be retrieving a specific message:

__('validation.between.string')
// 'The :attribute field must be between :min and :max characters.'

...or, a translation message might not exist at all:

__('validation.between.foobar)
// null
Darkdawg's avatar

So you'd just ignore the error then, instead of creating a new helper that ensures string?

tykus's avatar

I would not write a separate helper just to satisfy the tool.

Depending on where/how you are experiencing the error, I would suggest a PHPStan-specific comment which identifies the offended rule, e.g.

/* @phpstan-ignore echo.nonString */
echo __('validation.required');

However if you take this approach, then you should take steps to mitigate for situations where an array is returned.

Darkdawg's avatar

To be honest, I think that stinks. You're using something that can return an array as a string, so you're not just trying to satisfy the tool.

I don't see any reason the framework shouldn't come with trans()->string('...') or trans()->array('...') (or for the __() shorthand if you wish).

If you look at the new typed accessor APIs from Laravel 12, where they introduced exactly these things to the config() and request() helpers. Now instead of doing config('app.something') which would return mixed, you instead do config()->string('app.something') which returns string.

This should be included within the framework imo.

Darkdawg's avatar

While I agree that trans() and __() need to be able to return multiple types depending on the translation structure (string or array), that doesn’t mean a typed accessor like trans()->string() cannot exist.

This pattern already exists elsewhere in the framework: both request() and config() support strict, typed accessors (like request()->string() and config()->integer()), which throw an exception when the value doesn't match the expected type.

In fact, the trans() helper already returns the underlying Translator instance when called without arguments (trans() => app('translator')), so the API is fully capable of supporting this extension naturally:

trans()->string('messages.welcome'); // returns string or throws InvalidArgumentException
trans()->array('validation');        // returns array or throws InvalidArgumentException

Adding these typed accessors would bring Translator in line with the Config\Repository and Http\Request classes, creating a consistent, type-safe developer experience across Laravel's core repositories.

1 like
tykus's avatar

To be honest, I think that stinks

I was simply trying to help... if you don't like the suggestion, then find a better way to give that feedback.

I don't see any reason the framework shouldn't come with trans()->string('...') or trans()->array('...') (or for the __() shorthand if you wish).

The Translator class is macroable; so you can write your own implementation of string, array or whatever else you please, e.g.

\Illuminate\Translation\Translator::macro('string', function ($key): string {
    // fetch the translation (if available) using the key
    // and return an appropriate string 
});
Darkdawg's avatar

I'm sorry, that first part was rude of me, I didn't mean it that way. I appreciate the help. I'm somewhat in a learning bubble, as I started using Laravel less than a year ago.

I'll look into macros for it. I think last time I used macros for something I ran into issues with either my IDE (VS Code) or PHPStan not aknowledging the methods. I believe IDE helper solved the former, but not the latter. It's very annoying when you have to fight the tools 😅

I think the framework could really benefit from having it by default. It would be a very lightweight addition, but I'm not familiar with the process for that. PR would be the way, I guess?

Snapey's avatar

I would question why/where you are using echo? That 'smells' more to me.

Darkdawg's avatar

I'm not using echo literally, I meant when you print it as a string.

<h1>{{ __('Hello world!') }}</h1>

Your static analysis would give an error here because you're trying to implicitly cast (array|string) to string.

Snapey's avatar

is phpstan evaluating your blade files?

Darkdawg's avatar

No, but there's apparently packages for that: https://github.com/bladestan/bladestan

But it was just an example, I use the translation helpers several other places as well. I found the new property getters quite nifty, for example:

public string $label = 'Category' {
        get => __($this->label);
    }

Please or to participate in this conversation.