Check line existence in laravel's trans() - laravel

Lets say in my lang/en/general.php there are multiple translation lines for example:
"token" => "This password reset token is invalid.",
"sent" => "Password reminder sent!",
"reset" => "Password has been reset!",
But in my lang/de/general.php these lines are missing.
So later, when I use the Lang::get('general.token') or simply trans('general.token')
The english version will return
This password reset token is invalid.
And the german (de) version will return
general.token
Is there any way I can handle a 'translation not found' function, like a filter but not creating a special class for it? For example, when a line has no translation, I want to throw an Exception.
Thanks in advance!

In Laravel 4 only, you can use Lang::has() as below, here is the doc
if (\Lang::has('general.token')) {
// line exists.
} else {
// line not exist.
}

In current Laravel versions you can just use trans helper like so:
#if (trans()->has('general.token'))
{{ trans('general.token') }}
#endif

This question is getting a little bit old but as per version 5.8 you can simply check as this :
array_key_exists('your-word-key', trans('your-file'))
or
array_key_exists('your-word-key', trans('your-file.array_key'))
for nested translations

You might want to write a helper similar to the one below to help with fallbacks:
/**
* Makes translation fall back to specified value if definition does not exist
*
* #param string $key
* #param null|string $fallback
* #param null|string $locale
* #param array|null $replace
*
* #return array|\Illuminate\Contracts\Translation\Translator|null|string
*/
function trans_fb(string $key, ?string $fallback = null, ?string $locale = null, ?array $replace = [])
{
if (\Illuminate\Support\Facades\Lang::has($key, $locale)) {
return trans($key, $replace, $locale);
}
return $fallback;
}
Note: The helper only works on PHP 7.1 (which has support nullable types). Adjust it to your PHP version if it's lower than 7.1.

You can create your own TranslationServiceProvider and Translator and override the get() method in translator to throw an exception when the parent::get() returns a translation string that is equal to the translation key that was passed in. Both #lang() and trans() functions call the get() method of the translator.
Seems like a whole lot of trouble only to get another reason for a "Whoops! something went wrong!" on your site. You will only get the exception when the translation is encountered.
Another solution: you can use the barryvdh/laravel-translation-manager package, it has a translation service provider that logs missing translation keys and a web interface for managing translations. It will log missing translation keys per locale and let you edit them through a web interface.
It is simple to setup and easy to modify. So you can replace the logging with throwing an exception.

Related

Laravel Get the translations of the application from a package

I am trying to develop a small package in Laravel, but I have the following problem. From my package I need to get the translations that are present in the languages directory:
Example:
myapp/resources/lang
When I make a call as follows to get the translations of a file:
private function getTranslationsFromFile()
{
return Lang::get('auth');
}
It does not return the translations in that file. It only returns auth. In the Laravel languages directory there is that file, but inside the directory en.
So I have defined before getting the translations the default 'locale' so that I can load them from there in this way:
private function getTranslationsFromFile()
{
app()->setLocale('en');
return Lang::get('auth');
}
this way it works fine, but my question is the following, is there no way to load the translations regardless of whether or not the 'locale' is set? I mean, is it not possible to get the translations just by giving it a file path?
Example:
private function getTranslationsFromFile()
{
return Lang::get('en/auth');
}
Thank you very much in advance.
You can certainly do that by leveraging the third and fourth parameter of Lang::get():
This is the method's signature:
get(string $key, array $replace = [], string|null $locale = null, bool $fallback = true)
The third parameter specifies the locale you'd wish to get the translation string for:
Lang::get('auth.failed', [], 'en')
To set a fallback language in case that the translation is not available in the chosen locale, use the fourth parameter:
Lang::get('auth.failed', [], 'en', 'es')

Add extra question to Laravel forgotten password form and custom its error messages

I'd like to customize the forgotten password form in Laravel.
When asking to reset the password, the user will have to answer a simple question (the name your first pet, the name of your childhood best friend, etc) besides inserting his/her email. This is to avoid other people asking password reset if they know the account's email, but are not the owner of the account.
I also would like to custom the errors messages to, actually, not show errors. For example, if an invalid email is inserted, it would not show the error message "We can't find a user with that e-mail address." I don't like it because someone may guess the email of a user by trying different emails until she/he stops getting the error message. Instead, I would like to show the message "If the information provided is correct, you will receive an email with the link to reset your password."
How to add these functionalities to Laravel auth?
I am looking for a solution that I don't have to create an entire login system from scratch (I think that if I try to design everything from scratch I'd probably miss something and create security vulnerabilities). I'd like to keep the Laravel auth system and just add these two features.
Feel free to suggest other ways to achieve the desired result and to make my question clearer. I'll appreciate that.
The good news is you don't need to rewrite everything.
The bad news is, you need to understand traits and how to extend/override them, which can be a little confusing.
The default controller that Laravel creates ForgotPasswordController doesn't do much. Everything it does is in the trait. The trait SendsPasswordResetEmails contains a few methods, most importantly for the validation in validateEmail method.
You can override this validateEmail method with one that checks for an answered question. You override traits by altering the 'use' statement.
For example change;
use SendsPasswordResetEmails
to:
use SendsPasswordResetEmails {
validateEmail as originValidateEmail
}
This will tell the code to re-name the original method validateEmail to originValidateEmail allowing you to create a new validateEmail in your own ForgotPasswordController.
You can then, inside ForgotPasswordController add a replacement which will be called by the default reset password code:
protected function validateEmail(Request $request)
{
// add in your own validation rules, etc.
$request->validate(['email' => 'required|email', 'questionfield' => 'required']);
}
To alter the error message, you can simply edit the language file found in resources/lang/en/passwords.php
Hope that helps.
Thanks to the user #Darryl E. Clarke, I managed to solve the problem. Here is what I did:
Add this line at the top of the file ForgotPasswordController, after namespace:
use App\User;
Add these 3 methods in the same file:
/**
* Send a reset link to the given user.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateRequest($request);
// We will send the password reset link to this user. Regardless if that
// worked, we will send the same response. We won't display error messages
// That is because we do not want people guessing the users' email. If we
// send an error message telling that the email is wrong, then a malicious
// person may guess a user' email by trying until he/she stops getting that
// error message.
$user = User::whereEmail($request->email)->first();
if ($user == null) {
return $this->sendResponse();
}
if ($user->secrete_question != $request->secrete_question) {
return $this->sendResponse();
}
$this->broker()->sendResetLink(
$this->credentials($request)
);
return $this->sendResponse();
}
/**
* Validate the given request.
*
* #param \Illuminate\Http\Request $request
* #return void
*/
protected function validateRequest(Request $request)
{
$request->validate(['email' => 'required|email', 'secrete_question' => 'required|string']);
}
/**
* Get the response for a password reset link.
*
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResponse()
{
$response = 'If the information provided is correct, you will receive an email with a link to reset your password.';
return back()->with('status', $response);
}
Customize it the way you want.
Hope that it will helps others!!

How to set possible date input formats for Typo3 6.2 Extbase Extension?

I made an extension with the Extension Builder in Typo3 6.2 using Fluid 6.2 and Extbase 6.2.
I made Appointment-Objects with a date property.
I want to enter the date in the format "dd.mm.yyyy".
So I tried this:
And it gives this error:
I'm clueless as I'm not familiar with this and I want to solve this in a nice way.
My createAction code is simply generated by the extension builder and therefore looks like this:
/**
* action create
*
* #param \JH\Appmgmt\Domain\Model\Appointment $newAppointment
* #return void
*/
public function createAction(\JH\Appmgmt\Domain\Model\Appointment $newAppointment) {
$this->addFlashMessage('The object was created. Please be aware that this action is publicly accessible unless you implement an access check. See Wiki', '', \TYPO3\CMS\Core\Messaging\AbstractMessage::ERROR);
$this->appointmentRepository->add($newAppointment);
$this->redirect('list');
}
Now I realize that if I change something here in order for the format to work I would have to do the same thing in the updateAction and maybe others that I don't know about yet.
I also desperately tried to format it to the desired format somehow in the partial but that was bound to fail - same result:
<f:form.textfield property="appointmentDate" value="{appointment.appointmentDate->f:format.date(format:'Y-m-d\TH:i:sP')}" /><br />
So that's where I need your help - I don't know where and how to globally allow this date format since I will be needing it for other fields as well.
The only other thing I can think of is changing something in the domain model:
/**
* appointmentDate
*
* #var \DateTime
*/
protected $appointmentDate = NULL;
but I don't know how I should approach this. :(
Anyone an idea?
You send a date that is not formatted correctly as a date Object.
that's exactly what the error says.
What you can do is re-format the date you send so that it arrives at your controller action as a valid argument for your object. this is done with an initialize action that invokes a property mapping.
a clear example can be found here:
http://www.kalpatech.in/blog/detail/article/typo3-extbase-datetime-property-converter.html
the part that you need is:
// Here we convert the property mapping using the property mapper
public function initializeAction() {
if ($this->arguments->hasArgument('newCalendar')) {
$this->arguments->getArgument('newCalendar')->getPropertyMappingConfiguration()->forProperty('startdate')->setTypeConverterOption('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\DateTimeConverter',\TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::CONFIGURATION_DATE_FORMAT,'d-m-Y');
}
}
You could also disable the validation of the date arguments to your controller and create a valid date Object from your 'date' and then use setAppointmentDate($yourNewDateObject).
You then go round the extbase validation, what is not a good practise.

Magento translations ok in online program but not run as cronjob

I created a module (extends Mage_Core_Model_Abstract) and an admin controller.
When I run this module online translations are going right.
When I run this module as cronjob, everything goes allright but translations are not done, I specified translation file in config.xml in as well frontend as adminhtml.
What I am doing wrong?
I see this is a very old question. I've posted here to future reference and others.
Quick and dirty solution
// Fix unitialized translator
Mage::app()->getTranslator()->init('frontend', true);
just after
$initialEnvironmentInfo = $appEmulation>startEnvironmentEmulation($storeId);
for instance. Or in a foreach loop of your own, which is called via cron/admin. Since you're talking about crons, I assume that you know what you are doing.
The real problem
In Magento 1.9 in Mage_Core_Model_App_Emulation (in app/code/core/Mage/Core/Model/App/Emulation.php), there's this function:
/**
* Apply locale of the specified store
*
* #param integer $storeId
* #param string $area
*
* #return string initial locale code
*/
protected function _emulateLocale($storeId, $area = Mage_Core_Model_App_Area::AREA_FRONTEND)
{
$initialLocaleCode = $this->_app->getLocale()->getLocaleCode();
$newLocaleCode = $this->_getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $storeId);
if ($initialLocaleCode != $newLocaleCode) {
$this->_app->getLocale()->setLocaleCode($newLocaleCode);
$this->_factory->getSingleton('core/translate')->setLocale($newLocaleCode)->init($area, true);
}
return $initialLocaleCode;
}
The $initialLocaleCode != $newLocaleCode seems to be the issue here. When iteration orders/customers/subscribers/*, the locale could stay the same, which then prevents executing the code in the statement. And the locale is thus not set in the Translator (Mage::app()->getTranslator()).
We've yet to fix the issue, but you could change if ($initialLocaleCode != $newLocaleCode) { to if (true) { straight in the core source. Off course, this is ugly. I suggest something like extending the class and then :
/**
* Apply locale of the specified store. Extended
* to fix Magento's uninitialized translator.
*
* #see http://stackoverflow.com/questions/19940733/magento-translations-ok-in-online-program-but-not-run-as-cronjob#
* #param integer $storeId
* #param string $area
*
* #return string initial locale code
*/
protected function _emulateLocale($storeId, $area = Mage_Core_Model_App_Area::AREA_FRONTEND)
{
$initialLocaleCode = $this->_app->getLocale()->getLocaleCode();
$newLocaleCode = $this->_getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $storeId);
$this->_app
->getLocale()
->setLocaleCode($newLocaleCode);
$this->_factory
->getSingleton('core/translate')
->setLocale($newLocaleCode)
->init($area, true);
return $initialLocaleCode;
}
Magento 2
I guess the developers became aware it was borked and they changed the code in Magento 2. The _emulateLocale() function is gone all together and they added this line to the startEnvironmentEmulation() function, without any conditional around it:
$this->_localeResolver->setLocale($newLocaleCode);
It's a bug even with CE 1.9.0.1!
See what I've done about it:
https://magento.stackexchange.com/questions/25612/cron-job-template-block-not-being-translated-but-testobserver-is/25920#25920

Translate controller class variables in zend framework 2

Let's say I have a controller and I want to define some const variables that hold some messages (eg error messages etc).
Is there a way to make it so they are translated?
An example class is defined bellow:
<?php
namespace Test\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class AccountController extends AbstractActionController
{
protected $testError = 'There was an error while testing some stuff';
public function testAction(){
// I know i can use the following approach but I don't want to
// since I want to use a variable for readability issues.
// $testError = $this->getTranslator()->translate('There was an error..');
return new ViewModel();
}
/**
* Retrieve the translator
*
* #return \Zend\I18n\Translator\Translator
*/
public function getTranslator()
{
if (!$this->translator) {
$this->setTranslator($this->getServiceLocator()->get('translator'));
}
return $this->translator;
}
/**
* Set the translator
*
* #param $translator
*/
public function setTranslator($translator)
{
$this->translator = $translator;
}
}
So I want to have the testError translated. I know I can just use the message and translate it via the zend translator without using a variable, but still I want to store it in a variable for readability issues. Any help or other approaches to this?
Simply create a translations.phtml file in any directory in your project root and fill it something like that:
<?php
// Colors
_('Black');
_('White');
_('Green');
_('Light Green');
_('Blue');
_('Orange');
_('Red');
_('Pink');
In poedit, check Catalog Properties > Source keywords list an be sure _ character is exists. (Alias of the gettext method). In application, use $this->translate($colorName) for example.
When poedit scanning your project directory to find the keywords which needs to be translated, translations.phtml file will be scanned too.
Another handy approach is using _ method (gettext alias) to improve code readability. Example:
$this->errorMsg = _('There was an error..');
But don't forget to set the global Locale object's default locale value too when you initialising your translator instance first time in a TranslatorServiceFactory or onBootstrap method of the module:
...
$translator = \Zend\Mvc\I18n\Translator\Translator::factory($config['translator']);
$locale = 'en_US';
$translator->setLocale($locale);
\Locale::setDefault($translator->getLocale());
return $translator;
...
I don't quite understand what you mean:
$errorMessage = 'FooBarBazBat";
return new ViewModel(array(
'error' => $this->getTranslator()->translate($errorMessage)
));
would be a way to store the message inside a variable. But i really don't understand where your problem is.
Or do you mean having the translator as variable?
$translator = $this->getServiceLocator()->get('viewhelpermanager')->get('translate');
$errorMessage = $translator('FooBarBazBat');

Resources