Add internationalization (i18n) with next-intl and Italian translations

- Integrated `next-intl` for server-side and client-side i18n support.
- Added English (`en.json`) and Italian (`it.json`) localization files.
- Configured routing with locale-based subdirectories (`/[locale]/path`) using `next-intl`.
- Introduced type-safe i18n utilities and TypeScript definitions for translation keys.
- Updated middleware to handle locale detection and routing.
- Implemented dynamic translation loading to reduce bundle size.
- Enhanced developer experience with auto-complete and compile-time validation for i18n keys.
This commit is contained in:
Felipe Cardoso
2025-11-17 20:27:09 +01:00
parent b7c1191335
commit fe6a98c379
11 changed files with 873 additions and 13 deletions

View File

@@ -0,0 +1,109 @@
// src/lib/i18n/utils.ts
/**
* Utility functions for internationalization.
*
* This file demonstrates type-safe translation usage.
*/
import { useTranslations } from 'next-intl';
/**
* Get the display name for a locale code.
*
* @param locale - The locale code ('en' or 'it')
* @returns The human-readable locale name
*/
export function getLocaleName(locale: string): string {
const names: Record<string, string> = {
en: 'English',
it: 'Italiano',
};
return names[locale] || names.en;
}
/**
* Get the native display name for a locale code.
* This shows the language name in its own language.
*
* @param locale - The locale code ('en' or 'it')
* @returns The native language name
*/
export function getLocaleNativeName(locale: string): string {
const names: Record<string, string> = {
en: 'English',
it: 'Italiano',
};
return names[locale] || names.en;
}
/**
* Get the flag emoji for a locale.
*
* @param locale - The locale code ('en' or 'it')
* @returns The flag emoji
*/
export function getLocaleFlag(locale: string): string {
// Map to country flags (note: 'en' uses US flag, could be GB)
const flags: Record<string, string> = {
en: '🇺🇸', // or '🇬🇧' for British English
it: '🇮🇹',
};
return flags[locale] || flags.en;
}
/**
* Hook to get common translations.
* This demonstrates type-safe usage of useTranslations.
*
* @returns Object with commonly used translation functions
*/
export function useCommonTranslations() {
const t = useTranslations('common');
return {
loading: () => t('loading'),
error: () => t('error'),
success: () => t('success'),
cancel: () => t('cancel'),
save: () => t('save'),
delete: () => t('delete'),
edit: () => t('edit'),
close: () => t('close'),
confirm: () => t('confirm'),
};
}
/**
* Format a relative time string (e.g., "2 hours ago").
* This is a placeholder for future implementation with next-intl's date/time formatting.
*
* @param date - The date to format
* @param locale - The locale to use for formatting
* @returns Formatted relative time string
*/
export function formatRelativeTime(date: Date, locale: string = 'en'): string {
const now = new Date();
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
if (diffInSeconds < 60) {
return locale === 'it' ? 'proprio ora' : 'just now';
} else if (diffInSeconds < 3600) {
const minutes = Math.floor(diffInSeconds / 60);
return locale === 'it'
? `${minutes} ${minutes === 1 ? 'minuto' : 'minuti'} fa`
: `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`;
} else if (diffInSeconds < 86400) {
const hours = Math.floor(diffInSeconds / 3600);
return locale === 'it'
? `${hours} ${hours === 1 ? 'ora' : 'ore'} fa`
: `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;
} else {
const days = Math.floor(diffInSeconds / 86400);
return locale === 'it'
? `${days} ${days === 1 ? 'giorno' : 'giorni'} fa`
: `${days} ${days === 1 ? 'day' : 'days'} ago`;
}
}