Currency Converter

Configuration

Configurer un projet de tutoriel

Introduction

Il s’agit d’un guide 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 jusqu’à sa traduction. Au fil de ce tutoriel, nous progresserons de concepts simples vers des notions plus avancées. Ce tutoriel suppose que vous avez une compréhension générale de TypeScript, Next.js et React.

Voici la liste des éléments que nous aborderons dans ce tutoriel :

  • Configurer un nouveau projet Next.js
  • Utiliser le composant <T> pour traduire une application
  • Utiliser des composants de variables comme <Var>, <Currency>, <DateTime> et <Num> pour traduire du contenu dynamique
  • Utiliser des composants conditionnels 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 utiliserons uniquement des styles en ligne et la bibliothèque gt-next afin de rester aussi minimalistes que possible. Cet exemple est basé sur le tutoriel Currency Converter de GeeksforGeeks.

Configurez votre application Next.js

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

npx create-next-app@latest

Cela ouvre l’assistant de configuration, où vous pouvez choisir le nom de votre application et le modèle à utiliser. Pour ce tutoriel, utilisez le nom currencies et sélectionnez « Yes » lorsque vous êtes invité à indiquer 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 démarrera 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 files src/app/page.tsx et src/app/layout.tsx.

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

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

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

// A map between two currencies and their exchange rate (from -> to)
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 },
};

// some styles for button
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 API pour obtenir le taux de change actuel.
 * Attend 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() {
  // exch rates
  const [info, setInfo] = useState<ExchTable>({});
  const options = ["usd", "inr", "eur", "jpy", "gbp"];

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

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

  // Function to convert the currency
  const convert = useCallback(() => {
    if (info?.[from]?.[to]) {
      const rate = info[from][to];
      setOutput(input * rate);
    } else {
      setOutput(0);
    }
  }, [info, input, to, from]);

  // Calling the api whenever from or to currency changes
  useEffect(() => {
    // If the exchange rate is already present, then convert
    if (info?.[from]?.[to]) {
      convert();
      return;
    }
    // Fetch the exchange rate
    (async () => {
      const response = await fetchExchangeRate(from, to);
      // Enter new response without overwriting old info
      setInfo((prevInfo) => ({
        ...prevInfo,
        [from]: {
          ...(prevInfo?.[from] || undefined),
          [to]: response,
        },
      }));
    })();
  }, [from, to, convert, info]);

  // Call convert whenever a user switches the currency
  useEffect(() => {
    convert();
  }, [info, convert]);

  // Function to switch between two currency
  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: "Un convertisseur de devises simple",
};

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

Conclusion

Comment trouvez-vous ce guide ?