Originally published on php-to-js.learnio.dev — part of the PHP to JavaScript course. View this lesson on the original site.

Port a small PHP utility class to a JavaScript module

This project ties chapter 1 together: variables, strings, arrays, objects, functions, modules, and try/catch. You rewrite a PHP Formatter helper as exported functions in one .js file.

PHP habit: the starting class

<?php
namespace App\Utils;

class Formatter
{
    public function formatCurrency(float $amount, string $currency = 'GBP'): string
    {
        return number_format($amount, 2) . ' ' . strtoupper($currency);
    }

    public function truncate(string $text, int $maxLength = 100, string $ellipsis = '…'): string
    {
        if (strlen($text) <= $maxLength) {
            return $text;
        }
        return substr($text, 0, $maxLength) . $ellipsis;
    }

    public function slugify(string $text): string
    {
        $text = strtolower(trim($text));
        $text = preg_replace('/[^a-z0-9\s-]/', '', $text);
        $text = preg_replace('/[\s-]+/', '-', $text);
        return trim($text, '-');
    }
}

JavaScript equivalent: module shape

export function formatCurrency(amount, currency = 'GBP') {
  return new Intl.NumberFormat('en-GB', {
    style: 'currency',
    currency,
  }).format(amount);
}

export function truncate(text, maxLength = 100, ellipsis = '…') {
  if (text.length <= maxLength) return text;
  return text.slice(0, maxLength) + ellipsis;
}

export function slugify(text) {
  return text
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/[\s-]+/g, '-')
    .replace(/^-+|-+$/g, '');
}
PHP JavaScript Why
class Formatter Named export function One responsibility per function
number_format() Intl.NumberFormat Locale-aware currency
substr($s, 0, $n) text.slice(0, n) Same slicing idea
preg_replace(...) text.replace(/.../g, ...) Regex on strings

Your task

  1. Implement the three functions in the sandbox (or in your editor with a formatter.js file).
  2. Build a small catalog array of products with title and price.
  3. For each product, log: truncated title, formatted price, and slug URL path.
  4. Wrap formatCurrency input in try/catch if you add validation that throws on bad data.

Example output shape:

Running shoes — Trail Ed…    £89.99    /products/running-shoes-trail-edition
Merino Wool Socks            £12.50    /products/merino-wool-socks

GitHub example

The course repo includes formatter.php, a sample formatter.js, and main.js under the chapter 1 example folder. Compare your version after you finish — not before.

Try it

Use the amber Try it panel below as a starter. Extend it with your own product list.

function formatCurrency(amount, currency = 'GBP') {
  return new Intl.NumberFormat('en-GB', { style: 'currency', currency }).format(amount);
}

function truncate(text, maxLength = 30, ellipsis = '…') {
  if (text.length <= maxLength) return text;
  return text.slice(0, maxLength) + ellipsis;
}

function slugify(text) {
  return text
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/[\s-]+/g, '-');
}

const products = [
  { title: 'Running shoes — Trail Edition', price: 89.99 },
  { title: 'Merino Wool Socks', price: 12.5 },
];

for (const { title, price } of products) {
  const label = truncate(title);
  const amount = formatCurrency(price);
  const slug = slugify(title);
  console.log(`${label.padEnd(28)} ${amount.padStart(8)}  /products/${slug}`);
}

When this runs cleanly, you have a working end-to-end port — the chapter 1 outcome.

From PHP to JavaScript — JavaScript Syntax Foundations for PHP Devs