IncludeBeer

Création d'un site web multilingue avec CodeIgniter 4 (2e partie)

Publié le 24 juin 2021
Image de l'article
Origine de l'image: Brett Zeck

Dans la première partie nous avons créé une site web multilingue avec les fichiers de traductions. Mais si vous avez un blog, vous ne voulez pas écrire vos articles dans des fichiers de traduction et devoir modifier du code pour les afficher. La solution est de sauvegarder les articles dans la base de données et afficher uniquement les articles dans la langue sélectionnée. C'est très facile. Voici comment faire...

Les fichiers de langues

D'abord, ajoutez les traductions suivantes dans les fichiers de langues de l'application. Elles seront utilisées dans la liste d'articles.

app/Language/de/Blog.php

'noArticles' => "Keine Artikel",
'readMore' => "Mehr lesen",

app/Language/en/Blog.php

'noArticles' => "No articles",
'readMore' => "Read more",

app/Language/es/Blog.php

'noArticles' => "No hay artículos",
'readMore' => "Leer más",

app/Language/fr/Blog.php

'noArticles' => "Aucun articles",
'readMore' => "Lire la suite",

app/Language/ja/Blog.php

'noArticles' => "記事なし",
'readMore' => "続きを読む",

Les routes

Ajoutez la route suivante pour afficher un article à partir de son slug. On définit le placeholder slug de la même façon qu'on a fait dans le tutoriel d'une application de base.

app/Config/Routes.php

$routes->addPlaceholder('slug', '[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*');
$routes->get('{locale}/(:slug)', 'HomeController::article/$1');

Base de données

Pour la base de données, au lieu de créer un script SQL, on va utiliser la classe Migration. La commande spark make:migration permet de créer une classe vide comme point de départ pour notre migration. Il suffit de lui passer le nom de la classe à créer en paramètre.

cd /Applications/MAMP/htdocs/ci4.test
/Applications/MAMP/bin/php/php7.4.2/bin/php spark make:migration CreateBlogTables

Ça crée un fichier dans le répertoire app/Database/Migrations/ avec la date et l'heure actuelle et le nom de la classe : 2021-06-24-080000_Createblogtables.php

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class Createblogtables extends Migration
{
        public function up()
        {
                //
        }

        public function down()
        {
                //
        }
}

Même si on a demandé de créer une classe CreateBlogTables, CodeIgniter nous a créée la classe Createblogtables. Le nom du fichier et de la classe n'est pas important, alors je les renomment pour que ça soit plus lisible. Je met des soulignés dans le nom du fichier et je met des majuscules dans le nom de la classe. La seule chose à faire attention est la date et l'heure. Car si on a plusieurs fichiers de migration, c'est dans cet ordre qu'elles seront appliquées. Voici donc notre classe de migration pour créer une table article. C'est le champ lang qui va nous permettre d'obtenir les articles dans la langue de l'utilisateur.

app/Database/Migrations/2021-06-24-080000_create_blog_tables.php
<?php namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateBlogTables extends Migration
{
    public function up()
    {
        $this->forge->addField([
            'id' => ['type' => 'integer', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
            'slug' => ['type' => 'varchar', 'constraint' => 255],
            'lang' => ['type' => 'varchar', 'constraint' => 5, 'null' => true],
            'title' => ['type' => 'varchar', 'constraint' => 150],
            'text' => ['type' => 'text', 'null' => true],
            'created_at' => ['type' => 'datetime'],
            'updated_at' => ['type' => 'datetime'],
        ]);

        $this->forge->addPrimaryKey('id');
        $this->forge->createTable('article');
    }

    //--------------------------------------------------------------------

    public function down()
    {
        $this->forge->dropTable('article');
    }
}

Ensuite, pour créer nos articles bidons, on va utiliser une classe Seeder. On va simplement faire une boucle de 1 à 5 pour chacune des langues et insérer du texte lorem ipsum. Seuls le titre et les premiers mots des articles sont traduit pour qu'on puisse constater qu'on obtiens bel et bien les articles dans la bonne langue.

app/Database/Seeds/BlogSeeder.php

<?php namespace App\Database\Seeds;

use CodeIgniter\Database\Seeder;

class BlogSeeder extends Seeder
{
    public function run()
    {
        $builder = $this->db->table('article');
        $nb_article = 5;

        // DE
        for ($article_no = 1; $article_no <= $nb_article; $article_no++)
        {
            $row = [
                'slug'          => "article-{$article_no}",
                'lang'          => 'de',
                'title'         => "Artikel in Deutsch {$article_no}",
                'text'          => <<<EOT
Artikel in Deutsch. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi eros, ultrices vitae arcu fringilla, sollicitudin tempus lacus. Curabitur suscipit ex eu nisi volutpat, quis pharetra neque dictum. Sed commodo est blandit facilisis vehicula. Ut justo mauris, ultricies eu massa ac, dignissim porttitor ligula. Quisque nec magna ante. Nullam accumsan nunc sed accumsan ornare. Donec orci mi, lobortis sed purus quis, egestas viverra justo. Cras pulvinar, mauris ut efficitur venenatis, urna justo suscipit tortor, nec sodales lacus quam sed dui. Sed auctor, augue vel scelerisque molestie, ligula odio consequat ipsum, vel facilisis sapien velit non risus. Cras at molestie mauris. Nam a euismod purus. Cras vitae commodo orci, vel tempus tortor. Ut sodales, est ac volutpat congue, dui tortor luctus elit, sit amet accumsan lectus diam a tortor. Aenean convallis, elit vel scelerisque accumsan, enim nisi volutpat lacus, in interdum nulla elit nec nunc. Nullam eleifend elit cursus mauris elementum porta.

Nulla et dui at metus pretium accumsan sed et ligula. Integer sodales cursus elit in commodo. Suspendisse ultrices diam elit, sit amet fermentum dui vulputate sit amet. Aenean a eros nec arcu tempus consectetur id cursus quam. Cras quis arcu pellentesque, fringilla nisl vitae, accumsan elit. Praesent nec semper velit, a posuere diam. Quisque in arcu eros. Vivamus venenatis purus risus, ut vehicula turpis ullamcorper nec. Morbi purus leo, lobortis sollicitudin mi sit amet, blandit aliquet orci. Donec tincidunt consequat felis vitae bibendum. Ut porttitor ante eget dui volutpat venenatis. Mauris eget euismod neque, dapibus blandit eros. Cras condimentum metus ac mi tempor, in commodo orci vestibulum. Aliquam a ultricies leo. Integer quis auctor ligula. Nulla sed congue justo.
EOT
                ,
                'created_at'    => date('Y-m-d H:i:s'),
                'updated_at'    => date('Y-m-d H:i:s'),
            ];
            $builder->insert($row);
        }

        // EN
        for ($article_no = 1; $article_no <= $nb_article; $article_no++)
        {
            $row = [
                'slug'          => "article-{$article_no}",
                'lang'          => 'en',
                'title'         => "Article in English {$article_no}",
                'text'          => <<<EOT
Article in English. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi eros, ultrices vitae arcu fringilla, sollicitudin tempus lacus. Curabitur suscipit ex eu nisi volutpat, quis pharetra neque dictum. Sed commodo est blandit facilisis vehicula. Ut justo mauris, ultricies eu massa ac, dignissim porttitor ligula. Quisque nec magna ante. Nullam accumsan nunc sed accumsan ornare. Donec orci mi, lobortis sed purus quis, egestas viverra justo. Cras pulvinar, mauris ut efficitur venenatis, urna justo suscipit tortor, nec sodales lacus quam sed dui. Sed auctor, augue vel scelerisque molestie, ligula odio consequat ipsum, vel facilisis sapien velit non risus. Cras at molestie mauris. Nam a euismod purus. Cras vitae commodo orci, vel tempus tortor. Ut sodales, est ac volutpat congue, dui tortor luctus elit, sit amet accumsan lectus diam a tortor. Aenean convallis, elit vel scelerisque accumsan, enim nisi volutpat lacus, in interdum nulla elit nec nunc. Nullam eleifend elit cursus mauris elementum porta.

Nulla et dui at metus pretium accumsan sed et ligula. Integer sodales cursus elit in commodo. Suspendisse ultrices diam elit, sit amet fermentum dui vulputate sit amet. Aenean a eros nec arcu tempus consectetur id cursus quam. Cras quis arcu pellentesque, fringilla nisl vitae, accumsan elit. Praesent nec semper velit, a posuere diam. Quisque in arcu eros. Vivamus venenatis purus risus, ut vehicula turpis ullamcorper nec. Morbi purus leo, lobortis sollicitudin mi sit amet, blandit aliquet orci. Donec tincidunt consequat felis vitae bibendum. Ut porttitor ante eget dui volutpat venenatis. Mauris eget euismod neque, dapibus blandit eros. Cras condimentum metus ac mi tempor, in commodo orci vestibulum. Aliquam a ultricies leo. Integer quis auctor ligula. Nulla sed congue justo.
EOT
                ,
                'created_at'    => date('Y-m-d H:i:s'),
                'updated_at'    => date('Y-m-d H:i:s'),
            ];
            $builder->insert($row);
        }

        // ES
        for ($article_no = 1; $article_no <= $nb_article; $article_no++)
        {
            $row = [
                'slug'          => "article-{$article_no}",
                'lang'          => 'es',
                'title'         => "Artículo en español {$article_no}",
                'text'          => <<<EOT
Artículo en español. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi eros, ultrices vitae arcu fringilla, sollicitudin tempus lacus. Curabitur suscipit ex eu nisi volutpat, quis pharetra neque dictum. Sed commodo est blandit facilisis vehicula. Ut justo mauris, ultricies eu massa ac, dignissim porttitor ligula. Quisque nec magna ante. Nullam accumsan nunc sed accumsan ornare. Donec orci mi, lobortis sed purus quis, egestas viverra justo. Cras pulvinar, mauris ut efficitur venenatis, urna justo suscipit tortor, nec sodales lacus quam sed dui. Sed auctor, augue vel scelerisque molestie, ligula odio consequat ipsum, vel facilisis sapien velit non risus. Cras at molestie mauris. Nam a euismod purus. Cras vitae commodo orci, vel tempus tortor. Ut sodales, est ac volutpat congue, dui tortor luctus elit, sit amet accumsan lectus diam a tortor. Aenean convallis, elit vel scelerisque accumsan, enim nisi volutpat lacus, in interdum nulla elit nec nunc. Nullam eleifend elit cursus mauris elementum porta.

Nulla et dui at metus pretium accumsan sed et ligula. Integer sodales cursus elit in commodo. Suspendisse ultrices diam elit, sit amet fermentum dui vulputate sit amet. Aenean a eros nec arcu tempus consectetur id cursus quam. Cras quis arcu pellentesque, fringilla nisl vitae, accumsan elit. Praesent nec semper velit, a posuere diam. Quisque in arcu eros. Vivamus venenatis purus risus, ut vehicula turpis ullamcorper nec. Morbi purus leo, lobortis sollicitudin mi sit amet, blandit aliquet orci. Donec tincidunt consequat felis vitae bibendum. Ut porttitor ante eget dui volutpat venenatis. Mauris eget euismod neque, dapibus blandit eros. Cras condimentum metus ac mi tempor, in commodo orci vestibulum. Aliquam a ultricies leo. Integer quis auctor ligula. Nulla sed congue justo.
EOT
                ,
                'created_at'    => date('Y-m-d H:i:s'),
                'updated_at'    => date('Y-m-d H:i:s'),
            ];
            $builder->insert($row);
        }

        // FR
        for ($article_no = 1; $article_no <= $nb_article; $article_no++)
        {
            $row = [
                'slug'          => "article-{$article_no}",
                'lang'          => 'fr',
                'title'         => "Article en français {$article_no}",
                'text'          => <<<EOT
Article en français. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi eros, ultrices vitae arcu fringilla, sollicitudin tempus lacus. Curabitur suscipit ex eu nisi volutpat, quis pharetra neque dictum. Sed commodo est blandit facilisis vehicula. Ut justo mauris, ultricies eu massa ac, dignissim porttitor ligula. Quisque nec magna ante. Nullam accumsan nunc sed accumsan ornare. Donec orci mi, lobortis sed purus quis, egestas viverra justo. Cras pulvinar, mauris ut efficitur venenatis, urna justo suscipit tortor, nec sodales lacus quam sed dui. Sed auctor, augue vel scelerisque molestie, ligula odio consequat ipsum, vel facilisis sapien velit non risus. Cras at molestie mauris. Nam a euismod purus. Cras vitae commodo orci, vel tempus tortor. Ut sodales, est ac volutpat congue, dui tortor luctus elit, sit amet accumsan lectus diam a tortor. Aenean convallis, elit vel scelerisque accumsan, enim nisi volutpat lacus, in interdum nulla elit nec nunc. Nullam eleifend elit cursus mauris elementum porta.

Nulla et dui at metus pretium accumsan sed et ligula. Integer sodales cursus elit in commodo. Suspendisse ultrices diam elit, sit amet fermentum dui vulputate sit amet. Aenean a eros nec arcu tempus consectetur id cursus quam. Cras quis arcu pellentesque, fringilla nisl vitae, accumsan elit. Praesent nec semper velit, a posuere diam. Quisque in arcu eros. Vivamus venenatis purus risus, ut vehicula turpis ullamcorper nec. Morbi purus leo, lobortis sollicitudin mi sit amet, blandit aliquet orci. Donec tincidunt consequat felis vitae bibendum. Ut porttitor ante eget dui volutpat venenatis. Mauris eget euismod neque, dapibus blandit eros. Cras condimentum metus ac mi tempor, in commodo orci vestibulum. Aliquam a ultricies leo. Integer quis auctor ligula. Nulla sed congue justo.
EOT
                ,
                'created_at'    => date('Y-m-d H:i:s'),
                'updated_at'    => date('Y-m-d H:i:s'),
            ];
            $builder->insert($row);
        }

        // JA
        for ($article_no = 1; $article_no <= $nb_article; $article_no++)
        {
            $row = [
                'slug'          => "article-{$article_no}",
                'lang'          => 'ja',
                'title'         => "日本語での記事 {$article_no}",
                'text'          => <<<EOT
日本語での記事. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi eros, ultrices vitae arcu fringilla, sollicitudin tempus lacus. Curabitur suscipit ex eu nisi volutpat, quis pharetra neque dictum. Sed commodo est blandit facilisis vehicula. Ut justo mauris, ultricies eu massa ac, dignissim porttitor ligula. Quisque nec magna ante. Nullam accumsan nunc sed accumsan ornare. Donec orci mi, lobortis sed purus quis, egestas viverra justo. Cras pulvinar, mauris ut efficitur venenatis, urna justo suscipit tortor, nec sodales lacus quam sed dui. Sed auctor, augue vel scelerisque molestie, ligula odio consequat ipsum, vel facilisis sapien velit non risus. Cras at molestie mauris. Nam a euismod purus. Cras vitae commodo orci, vel tempus tortor. Ut sodales, est ac volutpat congue, dui tortor luctus elit, sit amet accumsan lectus diam a tortor. Aenean convallis, elit vel scelerisque accumsan, enim nisi volutpat lacus, in interdum nulla elit nec nunc. Nullam eleifend elit cursus mauris elementum porta.

Nulla et dui at metus pretium accumsan sed et ligula. Integer sodales cursus elit in commodo. Suspendisse ultrices diam elit, sit amet fermentum dui vulputate sit amet. Aenean a eros nec arcu tempus consectetur id cursus quam. Cras quis arcu pellentesque, fringilla nisl vitae, accumsan elit. Praesent nec semper velit, a posuere diam. Quisque in arcu eros. Vivamus venenatis purus risus, ut vehicula turpis ullamcorper nec. Morbi purus leo, lobortis sollicitudin mi sit amet, blandit aliquet orci. Donec tincidunt consequat felis vitae bibendum. Ut porttitor ante eget dui volutpat venenatis. Mauris eget euismod neque, dapibus blandit eros. Cras condimentum metus ac mi tempor, in commodo orci vestibulum. Aliquam a ultricies leo. Integer quis auctor ligula. Nulla sed congue justo.
EOT
                ,
                'created_at'    => date('Y-m-d H:i:s'),
                'updated_at'    => date('Y-m-d H:i:s'),
            ];
            $builder->insert($row);
        }
    }
}

Modèle et entité

Le modèle et l'entité pour les articles sont ce qu'il y a de plus simple. Si vous avez passé au travers ma série d'articles Création d'une application web de base avec CodeIgniter 4, c'est du déjà vu.

app/Models/ArticleModel.php

<?php namespace App\Models;

use CodeIgniter\Model;
use App\Entities\Article;

class ArticleModel extends Model
{
    protected $table = 'article';
    protected $primaryKey = 'id';
    protected $returnType = Article::Class;
    protected $allowedFields = ['slug', 'lang', 'title', 'text', 'created_at', 'updated_at'];
    protected $useTimestamps = true;
}

app/Entities/Article.php

<?php namespace App\Entities;

use CodeIgniter\Entity;

Class Article extends Entity
{
    protected $dates = ['created_at', 'updated_at'];
}

Le contrôleur

Dans le contrôleur, remplacez la fonction index() et ajoutez la fonction article(). La fonction index() obtient la liste d'articles dans la langue demandée. On a vu dans la première partie de cet article que la langue est déterminée par le premier segment de l'URL. Par exemple, pour l'URL http://ci4.test:8888/fr, le premier segment est fr. Ce segment est automatiquement extrait grâce au terme {locale} définit dans nos routes. Dans le BaseController on a sauvegardé la langue dans la variable $this->viewData['locale']. C'est cette variable qu'on utilise dans la requête pour obtenir les articles dans la bonne langue.

La fonction article() obtient un article à partir de son slug. On spécifie aussi la langue demandée au cas où le slug serait le même dans plusieurs langues. Comme par exemple, si on a un article sur les iPhone, le slug pourrait être iphone dans toutes les langues, ce qui donnerait les URL suivantes : http://ci4.test:8888/fr/iphone, http://ci4.test:8888/en/iphone, etc. Justement, dans nos données bidons, les slugs sont les mêmes dans toutes les langues (article-1, article-2, article-3, etc).

app/Controllers/HomeController.php

<?php

namespace App\Controllers;

class HomeController extends BaseController
{
    public function index()
    {
        $articleModel = model('articleModel');

        $this->viewData['articles'] = $articleModel
            ->where('lang', $this->viewData['locale'])
            ->orderBy('title', 'asc')
            ->find();

        return view('index', $this->viewData);
    }

    public function article (string $slug)
    {
        $articleModel = model('articleModel');

        $this->viewData['article'] = $articleModel
            ->where('lang', $this->viewData['locale'])
            ->where('slug', $slug)
            ->first();

        if ( ! $this->viewData['article'])
        {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
        }

        return view('article', $this->viewData);
    }
}

Les vues

Remplacez la vue index.php avec le code ci-dessous. Il n'y a rien de bien compliqué. On boucle sur la liste d'articles pour afficher le titre avec un lien vers l'article. On bâtit les liens avec la fonction anchor() :

<?= anchor($article->lang . '/' . $article->slug, lang('Blog.readMore')) ?> &rarr;

Ce qui va donner le résultat suivant :

<a href="http://ci4.test:8888/fr/article-1">Lire la suite</a> &rarr;

Le code &rarr; va afficher une flèche vers la droite.

app/Views/index.php

<?php
/**
 * @var \CodeIgniter\View\View $this
 * @var array $articles
 * @var \App\Entities\Article $article
 */
?>
<?= $this->extend('template') ?>
<?= $this->section('main_content') ?>

        <section class="px-3 py-5">
            <div class="container">
                <div class="mb-5">
                    <h3><?= lang('Blog.aboutTitle') ?></h3>
                    <p><?= lang('Blog.aboutText') ?></p>
                </div>

<?php if (count($articles) != 0) : ?>
<?php   foreach ($articles as $article) : ?>
                <div class="mb-5">
                    <h3><?= $article->title ?></h3>
                    <?= anchor($article->lang . '/' . $article->slug, lang('Blog.readMore')) ?> &rarr;
                </div>
<?php   endforeach; ?>

            </div>
        </section>
<?php else : ?>
        <div class="alert alert-info text-center"><?= lang('Blog.noArticles') ?></div>
<?php endif; ?>

<?= $this->endSection() ?>

Créez la nouvelle vue article.php. Dans cette vue on affiche le texte de l'article. On utilise la fonction nl2br() pour convertir les sauts de lignes en <br>. Pour rendre le visuel plus intéressant, j'ai ajouté une image placeholder grâce au site https://placekitten.com/.

app/Views/article.php

<?php
/**
 * @var \CodeIgniter\View\View $this
 * @var \App\Entities\Article $article
 */
?>
<?= $this->extend('template') ?>
<?= $this->section('main_content') ?>

    <article class="px-3 py-5">
        <div class="container">
            <header>
                <h2 class="title mb-2"><?= $article->title ?></h2>
            </header>
            <div class="m-5">
                <img src="https://placekitten.com/200/287" alt="placekitten.com" class="float-left mr-4 mb-4">
                <p><?= nl2br($article->text) ?></p>
            </div>
        </div>
    </article>

<?= $this->endSection() ?>

Exécuter la migration de base de données et le Seeder

Maintenant que tout est en place, on peux exécuter le script de migration pour créer la table MySQL avec la commande spark migrate -all. On peux ensuite insérer nos données bidons pour tester notre application en exécutant le Seeder. Exécutez les commandes suitantes dans un terminal :

cd /Applications/MAMP/htdocs/ci4.test
/Applications/MAMP/bin/php/php7.4.2/bin/php spark migrate -all
/Applications/MAMP/bin/php/php7.4.2/bin/php spark db:seed BlogSeeder

On peux aussi rafraîchir la base de données avec le paramètre migrate:refresh. Ce qui aura pour effet d'appeller la fonction down() et up() de notre classe de migration. C'est à dire que la table MySQL sera détruite puis recréée. Ensuite il suffit d'appeller le Seeder pour insérer les données de tests. C'est utile si on fait des ajustements à la structure de la table ou si on modifié les données de test et qu'on veut revenir à l'état initial.

cd /Applications/MAMP/htdocs/ci4.test
/Applications/MAMP/bin/php/php7.4.2/bin/php spark migrate:refresh
/Applications/MAMP/bin/php/php7.4.2/bin/php spark db:seed BlogSeeder

Conclusion

Vous avez maintenant un site web en cinq langues, avec la possibilité d'ajouter du contenu dynamique en plusieurs langues dans la base de données ! Et grâce a CodeIgniter 4 c'était super facile !

Beer Si vous appréciez mes tutoriels, vous pouvez me payer l'équivalent d'un café, une bière ou un lunch !

Paypal