javascript

exercises

exercises.js
/**
 * 9.3 Internationalization (Intl) - Exercises
 */

/**
 * Exercise 1: Currency Formatter
 * Create a function that formats amounts in different currencies
 */
function formatCurrency(amount, currencyCode, locale) {
  // TODO: Format the amount as currency
  // Use appropriate locale for the currency if locale not provided
  // Return formatted string
}

// Test:
// console.log(formatCurrency(1234.56, 'USD'));        // $1,234.56
// console.log(formatCurrency(1234.56, 'EUR', 'de-DE')); // 1.234,56 €
// console.log(formatCurrency(1234.56, 'JPY'));        // ¥1,235

/**
 * Exercise 2: Multi-locale Number Formatter
 * Create a number formatter with multiple output formats
 */
function createNumberFormatter(locale) {
  // TODO: Return object with methods:
  // - format(n): basic number formatting
  // - percent(n): percentage formatting
  // - compact(n): compact notation
  // - scientific(n): scientific notation
  // - currency(n, code): currency formatting
}

// Test:
// const fmt = createNumberFormatter('en-US');
// console.log(fmt.format(1234567));     // "1,234,567"
// console.log(fmt.percent(0.75));       // "75%"
// console.log(fmt.compact(1234567));    // "1.2M"

/**
 * Exercise 3: Smart Date Formatter
 * Format dates based on how recent they are
 */
function smartFormatDate(date, locale = 'en-US') {
  // TODO: Format date intelligently:
  // - Same day: time only (2:30 PM)
  // - Yesterday: "Yesterday at 2:30 PM"
  // - This week: "Tuesday at 2:30 PM"
  // - This year: "Mar 15 at 2:30 PM"
  // - Older: "Mar 15, 2023"
}

// Test:
// console.log(smartFormatDate(new Date()));
// console.log(smartFormatDate(new Date(Date.now() - 86400000)));

/**
 * Exercise 4: Relative Time Calculator
 * Calculate and format relative time differences
 */
function relativeTime(date, baseDate = new Date(), locale = 'en') {
  // TODO: Calculate difference and return relative time string
  // Handle: seconds, minutes, hours, days, weeks, months, years
  // Use numeric: 'auto' for natural language
}

// Test:
// console.log(relativeTime(new Date(Date.now() - 60000))); // "1 minute ago"
// console.log(relativeTime(new Date(Date.now() + 86400000))); // "tomorrow"

/**
 * Exercise 5: Duration Formatter
 * Format durations in a locale-aware way
 */
function formatDuration(seconds, locale = 'en', style = 'long') {
  // TODO: Format duration like "2 hours, 30 minutes, and 15 seconds"
  // Use Intl.ListFormat and Intl.NumberFormat with units
}

// Test:
// console.log(formatDuration(9015)); // "2 hours, 30 minutes, and 15 seconds"
// console.log(formatDuration(90, 'en', 'narrow')); // "1 min 30 sec"

/**
 * Exercise 6: Pluralization System
 * Create a comprehensive pluralization system
 */
class Pluralizer {
  // TODO: Implement pluralization for different locales
  // constructor(locale)
  // addRule(key, forms): Add pluralization forms
  // forms = { zero, one, two, few, many, other }
  // format(key, count): Return pluralized string
  // e.g., format('item', 5) => "5 items"
}

// Test:
// const pl = new Pluralizer('en');
// pl.addRule('apple', { one: '# apple', other: '# apples' });
// console.log(pl.format('apple', 1));  // "1 apple"
// console.log(pl.format('apple', 5));  // "5 apples"

/**
 * Exercise 7: Locale-Aware Sorting
 * Create a sorting utility with locale support
 */
function createSorter(locale, options = {}) {
  // TODO: Return a function that sorts arrays
  // Support options: numeric, caseFirst, sensitivity
  // Handle arrays of strings or objects with a key
}

// Test:
// const sort = createSorter('en', { numeric: true });
// console.log(sort(['item10', 'item2', 'item1']));
// // ['item1', 'item2', 'item10']

/**
 * Exercise 8: Display Names Utility
 * Create a utility for displaying localized names
 */
class LocaleDisplayNames {
  // TODO: Implement
  // constructor(displayLocale)
  // region(code): Get region name
  // language(code): Get language name
  // currency(code): Get currency name
  // script(code): Get script name
  // all(code): Try to determine type and return name
}

// Test:
// const names = new LocaleDisplayNames('en');
// console.log(names.region('DE'));    // "Germany"
// console.log(names.language('de'));  // "German"
// console.log(names.currency('EUR')); // "Euro"

/**
 * Exercise 9: Invoice Formatter
 * Create a locale-aware invoice formatter
 */
class InvoiceFormatter {
  // TODO: Create invoice formatter
  // constructor(locale, currency)
  // formatLineItem(item): { description, quantity, unitPrice, total }
  // formatSubtotal(amount)
  // formatTax(amount, rate)
  // formatTotal(amount)
  // formatDate(date)
  // formatInvoiceNumber(number)
}

// Test:
// const inv = new InvoiceFormatter('de-DE', 'EUR');
// console.log(inv.formatLineItem({
//     description: 'Widget',
//     quantity: 10,
//     unitPrice: 99.99
// }));

/**
 * Exercise 10: Multi-Locale Application
 * Create a localization system
 */
class I18n {
  // TODO: Implement internationalization system
  // constructor(defaultLocale)
  // setLocale(locale): Change current locale
  // getLocale(): Get current locale
  // addTranslations(locale, translations): Add translation strings
  // t(key, params): Translate key with parameters
  // Params can include count for pluralization
  // formatNumber(n, options)
  // formatCurrency(n, currency)
  // formatDate(date, options)
  // formatRelativeTime(date)
  // formatList(items)
}

// Test:
// const i18n = new I18n('en');
// i18n.addTranslations('en', {
//     'greeting': 'Hello, {name}!',
//     'items': { one: '{count} item', other: '{count} items' }
// });
// console.log(i18n.t('greeting', { name: 'World' }));
// console.log(i18n.t('items', { count: 5 }));

/**
 * BONUS CHALLENGES
 */

/**
 * Bonus 1: Calendar System Support
 * Support different calendar systems
 */
function formatWithCalendar(date, locale, calendar) {
  // TODO: Format date with specific calendar system
  // Calendars: 'gregory', 'islamic', 'hebrew', 'chinese', etc.
}

/**
 * Bonus 2: Smart Number Input
 * Parse numbers from locale-specific formats
 */
function parseLocalizedNumber(str, locale) {
  // TODO: Parse "1.234,56" (de-DE) or "1,234.56" (en-US) to number
  // Handle currency symbols, percent signs, etc.
}

/**
 * Bonus 3: Locale Negotiation
 * Find best matching locale from available options
 */
function negotiateLocale(requestedLocales, availableLocales, defaultLocale) {
  // TODO: Find best match between requested and available
  // Consider language, region, and fallbacks
}

// ============================================
// SOLUTION KEY (for reference)
// ============================================

/*
// Exercise 1 Solution:
function formatCurrency(amount, currencyCode, locale) {
    const localeMap = {
        USD: 'en-US',
        EUR: 'de-DE',
        GBP: 'en-GB',
        JPY: 'ja-JP',
        CNY: 'zh-CN'
    };
    
    const useLocale = locale || localeMap[currencyCode] || 'en-US';
    
    return new Intl.NumberFormat(useLocale, {
        style: 'currency',
        currency: currencyCode
    }).format(amount);
}

// Exercise 2 Solution:
function createNumberFormatter(locale) {
    return {
        format(n) {
            return new Intl.NumberFormat(locale).format(n);
        },
        percent(n) {
            return new Intl.NumberFormat(locale, {
                style: 'percent',
                minimumFractionDigits: 0
            }).format(n);
        },
        compact(n) {
            return new Intl.NumberFormat(locale, {
                notation: 'compact',
                compactDisplay: 'short'
            }).format(n);
        },
        scientific(n) {
            return new Intl.NumberFormat(locale, {
                notation: 'scientific'
            }).format(n);
        },
        currency(n, code) {
            return new Intl.NumberFormat(locale, {
                style: 'currency',
                currency: code
            }).format(n);
        }
    };
}

// Exercise 4 Solution:
function relativeTime(date, baseDate = new Date(), locale = 'en') {
    const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
    const diffMs = date.getTime() - baseDate.getTime();
    const diffSeconds = Math.round(diffMs / 1000);
    
    const units = [
        ['year', 60 * 60 * 24 * 365],
        ['month', 60 * 60 * 24 * 30],
        ['week', 60 * 60 * 24 * 7],
        ['day', 60 * 60 * 24],
        ['hour', 60 * 60],
        ['minute', 60],
        ['second', 1]
    ];
    
    for (const [unit, seconds] of units) {
        if (Math.abs(diffSeconds) >= seconds || unit === 'second') {
            const value = Math.round(diffSeconds / seconds);
            return rtf.format(value, unit);
        }
    }
}

// Exercise 6 Solution:
class Pluralizer {
    constructor(locale) {
        this.locale = locale;
        this.rules = new Map();
        this.pr = new Intl.PluralRules(locale);
    }
    
    addRule(key, forms) {
        this.rules.set(key, forms);
    }
    
    format(key, count) {
        const forms = this.rules.get(key);
        if (!forms) return `${count} ${key}`;
        
        const category = this.pr.select(count);
        const template = forms[category] || forms.other;
        return template.replace('#', count.toString());
    }
}

// Exercise 7 Solution:
function createSorter(locale, options = {}) {
    const collator = new Intl.Collator(locale, options);
    
    return function(array, key = null) {
        return [...array].sort((a, b) => {
            const valA = key ? a[key] : a;
            const valB = key ? b[key] : b;
            return collator.compare(String(valA), String(valB));
        });
    };
}
*/

console.log('Complete the exercises above!');
console.log('Check the solution key at the bottom for guidance.');
Exercises - JavaScript Tutorial | DeepML