Currency Converter

Configuración

Configura un proyecto de tutorial

Introducción

Esta es una guía/tutorial más completa sobre cómo configurar un proyecto muy sencillo de Next.js usando gt-next. Te acompañaremos de principio a fin, desde la configuración del proyecto hasta su traducción. A lo largo de este tutorial avanzaremos desde conceptos simples hasta otros más avanzados. Este tutorial asume que tienes un entendimiento general de TypeScript, Next.js y React.

A continuación, la lista de temas que cubriremos en este tutorial:

  • Configurar un nuevo proyecto de Next.js
  • Usar el componente <T> para traducir una aplicación
  • Usar componentes de variables como <Var>, <Currency>, <DateTime> y <Num> para traducir contenido dinámico
  • Usar componentes condicionales como <Plural> y <Branch> para traducir contenido según condiciones
  • Usar enrutamiento i18n en tu aplicación

Nuestra aplicación será sencilla y nos permitirá consultar la tasa de conversión entre monedas. Solo usaremos estilos en línea y únicamente la biblioteca gt-next para mantener todo lo más simple posible. Este ejemplo se basó en el tutorial Currency Converter de GeeksforGeeks.

Configura tu app de Next.js

Primero, creemos una nueva app de Next.js. Puedes hacerlo ejecutando el siguiente comando:

npx create-next-app@latest

Esto te llevará al asistente de configuración, donde puedes elegir el nombre de tu app y la plantilla que quieres usar. Para este tutorial, usa el nombre currencies y selecciona Yes cuando se te pregunte si quieres usar TypeScript.

Navega al directorio del proyecto y ¡pongamos en marcha la app!

cd currencies
npm install
npm run dev

Esto iniciará la app en http://localhost:3000.

¡Agreguemos contenido!

Ahora que tenemos nuestra app configurada, reemplacemos su contenido para mostrar un conversor de divisas sencillo. Copia y pega el siguiente código en los archivos src/app/page.tsx y src/app/layout.tsx.

Por ahora, no te preocupes demasiado por cómo funciona. Este código simplemente simula una solicitud a una API de tipos de cambio y muestra el tipo de cambio entre dos monedas.

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

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

// Un mapa entre dos monedas y su tipo de cambio (de -> a)
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 },
};

// algunos estilos para el botón
const buttonStyle = {
  backgroundColor: "#007bff",
  color: "white",
  border: "none",
  padding: "10px 20px",
  cursor: "pointer",
  borderRadius: "5px",
  fontSize: "16px",
  margin: "20px",
};

/**
 * Esta función simula una solicitud de API para obtener el tipo de cambio actual.
 * Espera 1 segundo antes de devolver el tipo de cambio.
 * @returns el tipo de cambio entre dos monedas
 */
async function fetchExchangeRate(from: string, to: string): Promise<number> {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  return EXCH_RATES[from][to];
}

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

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

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

  // Función para convertir la moneda
  const convert = useCallback(() => {
    if (info?.[from]?.[to]) {
      const rate = info[from][to];
      setOutput(input * rate);
    } else {
      setOutput(0);
    }
  }, [info, input, to, from]);

  // Llamar a la API cada vez que cambia la moneda de origen o destino
  useEffect(() => {
    // Si el tipo de cambio ya está disponible, entonces convertir
    if (info?.[from]?.[to]) {
      convert();
      return;
    }
    // Obtener el tipo de cambio
    (async () => {
      const response = await fetchExchangeRate(from, to);
      // Agregar nueva respuesta sin sobrescribir la información anterior
      setInfo((prevInfo) => ({
        ...prevInfo,
        [from]: {
          ...(prevInfo?.[from] || undefined),
          [to]: response,
        },
      }));
    })();
  }, [from, to, convert, info]);

  // Llamar a convert cada vez que el usuario cambia la moneda
  useEffect(() => {
    convert();
  }, [info, convert]);

  // Función para intercambiar entre dos monedas
  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" }}>
          Conversor de Monedas
        </h1>
      </div>
      <div style={{ flex: 2, textAlign: "center", margin: "20px 0" }}>
        <h3>Cantidad</h3>
        <input
          type="text"
          placeholder="Ingrese la cantidad"
          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}
          >
            Intercambiar
          </button>
        </div>
        <div style={{ flex: 1, textAlign: "center" }}>
          <label htmlFor="to">
            <h3>A</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>Cantidad Convertida:</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: "Conversor de Monedas",
  description: "Un conversor de monedas sencillo",
};

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

Conclusión

¿Qué te ha parecido esta guía?

Configuración