Multi language application on startup of the app to redirect to default language if no language is set along with saving data to storage

So one of my questions is there a way to set fetching data from the database as default fetchMode such as FETCH_LAZY or FETCH_OBJ without using the setFetchMode() method each time a statement is executed.

Also I am using the session container to save the data of the available languages coming from my database, and using it as a namespace like FrontEnd in this case, now i have checked in the session component Container class that you have a method to check if the Namespace exists, previously in the Zend Framework I was using the registry method to save and check them if they exist with static methods, my question here is there a way to get the saved session data or should I check the Namespace each time in different actions in modules?

So my next step is to make on my application visit that if no language in route is set will redirect the user to default language in this case EN as i have the following in my Application Module:

'router' => [
    'routes' => [
        'app' => [
            'type' => Segment::class,
            'options' => [
                'route' => '/[:lang]',
                'constraints' => [
                    'lang' => '[a-z]{2}'
                ],
                'defaults' => [
                    'lang' => 'en',
                    'controller' => Controller\IndexController::class,
                    'action' => 'index',
                ],
            ],
            'may_terminate' => true,
            'child_routes' => [
                'plugin' => [
                    'type'      => Literal::class,
                    'options'   => [
                        'route'    => '/plugin',
                        'defaults' => [
                            'controller' => Controller\IndexController::class,
                            'action'     => 'plugin',
                        ],
                    ],
                    'may_terminate' => true,
                ],
            ],
        ],
    ],
],

Here is my Application class:

namespace Nanocord\Mvc;

// Load classes
use Laminas\Db\Adapter\Adapter;
use Laminas\Db\Sql\Sql;

use Laminas\Mvc\Application as LaminasApplication;

use Laminas\Session\Config\SessionConfig;
use Laminas\Session\Container;
use Laminas\Session\SessionManager;
use Laminas\Session\Storage\SessionArrayStorage;
use Laminas\Session\Validator\HttpUserAgent;
use Laminas\Session\Validator\RemoteAddr;

/**
 * Application
 * 
 * @category    Nanocord
 * @package     Mvc
 * @subpackage  Application
 */
class Application extends LaminasApplication 
{
    private $config;
    private $db;
    private $dbIsConnected;
    private $session;
    
    public function __construct($config) 
    {
        $this->config = $config;
    }

    public function start() 
    {
        $application = parent::init($this->config)->run();
        
        $db_config = $application->getConfig()['db'];
        $db_adapter = new Adapter($db_config);
        $db_adapter->driver->getConnection()->connect();
        
        if ($db_adapter->driver->getConnection()->isConnected() === true) {
            
            $this->db = $db_adapter;
            $this->dbIsConnected = true;
            
            $this->initSession();
            $this->initLanguage();
            
        }
    }
    
    public function initSession() 
    {
        $session_config = new SessionConfig();
        $session_storage = new SessionArrayStorage();
        $session_manager = new SessionManager($session_config, $session_storage, null, [HttpUserAgent::class, RemoteAddr::class]);
        
        $session_manager->getValidatorChain()
            ->attach('session.validate', [new HttpUserAgent(), 'isValid']);
        $session_manager->getValidatorChain()
            ->attach('session.validate', [new RemoteAddr(), 'isValid']);

        $session = new Container('FrontEnd', $session_manager);
        $this->session = $session;


        //echo '<pre>' . var_export($session, true) . '</pre>';
    }
    
    public function initLanguage() 
    {
        $statement = $this->db->createStatement('SELECT * FROM application_language WHERE active = ?', [1]);
        $languages = $statement->execute();
        //$languages->setFetchMode(1);
        
        $languages_array = array();
        $languages_default = null;
        
        foreach($languages as $language) {

            $languages_array[] = $language['code'];
            
            if ($language['predefined'] == 1) {
                
                $languages_default = $language['code'];
                
            }

        }
        
        $this->session->languages_default = $languages_default;
        $this->session->languages_available = $languages_array;
        
        var_dump($this->session->languages_default);
        var_dump($this->session->languages_available);
    }
}

Ok I was able to setup the language parameter by using the following in my initLanguage() method:

public function initLanguage() 
{
    $sql = new Sql($this->db);
    $select = $sql->select();
    $select->from('application_language');
    
    $statement = $sql->prepareStatementForSqlObject($select);
    $languages = $statement->execute();
    //$languages->setFetchMode(\PDO::FETCH_LAZY);
    
    $languages_array = array();
    $languages_default = null;
    $languages_available = array();
    
    foreach($languages as $language) {

        $languages_array[$language['code']] = $language;
        
        if ($language['predefined'] == 1) {
            
            $languages_default = $language['code'];
            
        }
        
        $languages_available[] = $language['code'];

    }
    
    $this->session->languages = $languages_array;
    $this->session->languages_default = $languages_default;
    $this->session->languages_available = $languages_available;
    
    $service_manager = $this->application->getServiceManager();
    $router = $service_manager->get('router');
    $request = $service_manager->get('request');
    $route_current = $router->match($request);
    $params = $route_current->getParams();
    
    if (isset($params['lang']) && $params['lang'] !== '') {
        
        if (in_array($params['lang'], $this->session->languages_available)) {
            
            $this->session->lang = $params['lang'];
            
        } else {
            
            $this->session->lang = $languages_default;
            
        }
        
    } else {
        
        $this->session->lang = $languages_default;
        
    }

    $translator = \Laminas\I18n\Translator\Translator::factory([
        'locale' => [
            $this->session->languages[$this->session->lang]['locale'], // Default locale
            $this->session->languages[$this->session->languages_default]['locale'], // Fallback locale
        ],
        'cache' => [
            'adapter' => [
                'name' => \Laminas\Cache\Storage\Adapter\Filesystem::class,
                'options' => [
                    'cache_dir' => ROOT_PATH . '/cache',
                ],
            ],
        ],
    ]);
    
    $router->setDefaultParam('lang', $this->session->lang);

    var_dump($translator->getLocale());
    var_dump($translator->getFallbackLocale());
}

So what this does is execute a query to my database for the languages that I have defined in my table and sets all the languages that are active and saves them in session for future information along the application. Is this the best approach to save this kind of data? Cause I am in doubt since I used to use the Zend_Registry from the version before Laminas but now it is removed.

Afterwards I get the service manager to retrieve my router and request instances and check for the current route if has a lang parameter and run the conditions of having it or not. After that I setup a translator with the locale also set in my database with a cache option located in my root folder to save the strings which I would like to save in a file for future use without checking the database.

At the end if the language is in session I set it as a default parameter for the language in the routing.

My next challenge is to setup a translator class where it retrieves the strings from my database if no cache file exists and only queries the database if the string isn’t found in the file itself. Now the way i understand is to use a remote loader for this type of strings but been trying to do that with no luck. Can anyone help on this part?

I have a question: if all your routes have the language parameter including a fallback, why do you need the language identifier in the session?

My suggestion would be here to use PHP’s Locale class to set the language tag:

Locale::setDefault('de-DE');

laminas-i18n uses per default this option and you can retrieve the locale with Locale::getDefault();.

I am saving it in session for multi language purposes as I have tables ending in _lang suffix so that later I can retrieve the correct data from the language that the user is in, in this is case using the session way.

Using the Locale class as you suggest returns as like so en_US_POSIX and not the language code it.
But Locale normally uses the format lowercase_UPPERCASE two letter right in this case I would only use the lowercase for the router part.

Or just by using the $translator->setLocale(‘en’) would be enough?