Currency Converter

Mise en place

Configurer un projet de tutoriel

Introduction

Il s’agit d’un tutoriel plus approfondi expliquant comment configurer un projet Next.js très simple avec gt-next. Nous vous accompagnerons de bout en bout, de la configuration du projet à sa traduction. Au fil de ce tutoriel, nous progresserons de concepts simples vers des concepts plus avancés. Ce tutoriel suppose que vous avez une compréhension générale de TypeScript, Next.js et React.

Voici la liste des sujets que nous aborderons dans ce tutoriel :

  • Configurer un nouveau projet Next.js
  • Utiliser le composant <T> pour traduire une application
  • Utiliser des composants variables comme <Var>, <Currency>, <DateTime> et <Num> pour traduire du contenu dynamique
  • Utiliser des composants de branchement comme <Plural> et <Branch> pour traduire du contenu conditionnel
  • Utiliser le routage i18n dans votre application

Notre application sera une application simple qui nous permettra de vérifier le taux de conversion entre des devises. Nous n’utiliserons que des styles en ligne et uniquement la bibliothèque gt-next afin de rester aussi minimalistes que possible. Cet exemple a été conçu à partir du tutoriel Currency Converter sur GeeksforGeeks.

Configurer votre application Next

Commencez par créer une nouvelle application Next.js. Pour cela, exécutez la commande suivante :

npx create-next-app@latest

Cela vous amènera à l’assistant de configuration, où vous pourrez choisir le nom de votre application et le modèle que vous souhaitez utiliser. Pour ce tutoriel, utilisez le nom currencies et sélectionnez « Oui » lorsqu’on vous demandera si vous souhaitez utiliser TypeScript.

Accédez au répertoire du projet et lançons l’application !

cd currencies
npm install
npm run dev

Cela lancera l’application sur http://localhost:3000.

Ajoutons du contenu !

Maintenant que notre application est configurée, remplaçons le contenu de notre app pour afficher un simple convertisseur de devises. Copiez puis collez le code suivant dans les fichiers src/app/page.tsx et src/app/layout.tsx.

Ne vous inquiétez pas trop de son fonctionnement pour l’instant. Ce code se contente de simuler une requête vers une API de change et d’afficher le taux de change entre deux devises.

src/app/page.tsx
"use client";

import { useEffect, useState, useCallback } from "react";

// Table de correspondance entre deux devises et leur taux de change (de → vers)
type ExchTable = Record<string, Record<string, number>>;

const EXCH_RATES: ExchTable = {
  usd: { usd: 1, inr: 73.5, eur: 0.85, jpy: 105.45, gbp: 0.72 },
  inr: { usd: 0.014, inr: 1, eur: 0.012, jpy: 1.46, gbp: 0.01 },
  eur: { usd: 1.18, inr: 85.5, eur: 1, jpy: 123.5, gbp: 0.85 },
  jpy: { usd: 0.0095, inr: 0.68, eur: 0.0081, jpy: 1, gbp: 0.0068 },
  gbp: { usd: 1.39, inr: 99.5, eur: 1.17, jpy: 146.5, gbp: 1 },
};

// Quelques styles pour le bouton
const buttonStyle = {
  backgroundColor: "#007bff",
  color: "white",
  border: "none",
  padding: "10px 20px",
  cursor: "pointer",
  borderRadius: "5px",
  fontSize: "16px",
  margin: "20px",
};

/**
 * Cette fonction simule une requête à l’API pour obtenir le taux de change actuel.
 * Patiente 1 seconde avant de renvoyer le taux de change.
 * @returns le taux de change entre deux devises
 */
async function fetchExchangeRate(from: string, to: string): Promise<number> {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  return EXCH_RATES[from][to];
}

function Page() {
  // Taux de change
  const [info, setInfo] = useState<ExchTable>({});
  const options = ["usd", "inr", "eur", "jpy", "gbp"];

  // Devises
  const [from, setFrom] = useState("usd");
  const [to, setTo] = useState("inr");

  // Valeurs
  const [input, setInput] = useState(0);
  const [output, setOutput] = useState(0);

  // Fonction de conversion de devise
  const convert = useCallback(() => {
    if (info?.[from]?.[to]) {
      const rate = info[from][to];
      setOutput(input * rate);
    } else {
      setOutput(0);
    }
  }, [info, input, to, from]);

  // Appel de l’API à chaque changement de devise source ou cible
  useEffect(() => {
    // Si le taux de change est déjà présent, effectuer la conversion
    if (info?.[from]?.[to]) {
      convert();
      return;
    }
    // Récupérer le taux de change
    (async () => {
      const response = await fetchExchangeRate(from, to);
      // Ajouter la nouvelle réponse sans écraser les informations existantes
      setInfo((prevInfo) => ({
        ...prevInfo,
        [from]: {
          ...(prevInfo?.[from] || undefined),
          [to]: response,
        },
      }));
    })();
  }, [from, to, convert, info]);

  // Appeler convert à chaque fois que l’utilisateur change de devise
  useEffect(() => {
    convert();
  }, [info, convert]);

  // Fonction pour permuter les deux devises
  function flip() {
    const temp = from;
    setFrom(to);
    setTo(temp);
  }

  return (
    <div style={{ margin: "0 auto", width: "50%", textAlign: "center" }}>
      <div style={{ margin: "20px 0", paddingBottom: "20px" }}>
        <h1 style={{ fontSize: "2.5em", fontWeight: "bold" }}>
          Convertisseur de devises
        </h1>
      </div>
      <div style={{ flex: 2, textAlign: "center", margin: "20px 0" }}>
        <h3>Montant</h3>
        <input
          type="text"
          placeholder="Saisissez le montant"
          style={{ textAlign: "center" }}
          onChange={(e) => {
            setInput(Number(e.target.value));
          }}
        />
      </div>
      <div
        style={{ display: "flex", justifyContent: "center", margin: "20px 0" }}
      >
        <div style={{ flex: 1, textAlign: "center" }}>
          <label htmlFor="from">
            <h3>De</h3>
          </label>
          <select
            name="from"
            id="from"
            value={from}
            onChange={(e) => setFrom(e.target.value)}
          >
            {options.map((option) => (
              <option key={option} value={option}>
                {option.toUpperCase()}
              </option>
            ))}
          </select>
        </div>
        <div style={{ flex: 1, textAlign: "center" }}>
          <button
            onClick={() => {
              flip();
            }}
            style={buttonStyle}
          >
            Inverser
          </button>
        </div>
        <div style={{ flex: 1, textAlign: "center" }}>
          <label htmlFor="to">
            <h3>Vers</h3>
          </label>
          <select
            name="to"
            id="to"
            value={to}
            onChange={(e) => setTo(e.target.value)}
          >
            {options.map((option) => (
              <option key={option} value={option}>
                {option.toUpperCase()}
              </option>
            ))}
          </select>
        </div>
      </div>
      <div style={{ margin: "0 auto", width: "50%", textAlign: "center" }}>
        <button
          onClick={() => {
            convert();
          }}
          style={buttonStyle}
        >
          Convertir
        </button>
        <h2>Montant converti :</h2>
        <p>{input + " " + from + " = " + output.toFixed(2) + " " + to}</p>
      </div>
    </div>
  );
}

export default Page;
src/app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Convertisseur de devises",
  description: "Convertisseur de devises simple",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="fr">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        {children}
      </body>
    </html>
  );
}

Conclusion

Que pensez-vous de ce guide ?

Mise en place