Refactor i18n setup and improve structure for maintainability

- Relocated `i18n` configuration files to `src/lib/i18n` for better organization.
- Removed obsolete `request.ts` and `routing.ts` files, simplifying `i18n` setup within the project.
- Added extensive tests for `i18n/utils` to validate locale-related utilities, including locale name, native name, and flag retrieval.
- Introduced a detailed `I18N_IMPLEMENTATION_PLAN.md` to document implementation phases, decisions, and recommendations for future extensions.
- Enhanced TypeScript definitions and modularity across i18n utilities for improved developer experience.
This commit is contained in:
Felipe Cardoso
2025-11-18 07:23:54 +01:00
parent fe6a98c379
commit 55ae92c460
8 changed files with 776 additions and 29 deletions

View File

@@ -0,0 +1,44 @@
// src/i18n/request.ts
/**
* Server-side i18n request configuration for next-intl.
*
* This file handles:
* - Loading translation messages for the requested locale
* - Server-side locale detection
* - Time zone configuration
*
* Important:
* - This runs on the server only (Next.js App Router)
* - Translation files are NOT sent to the client (zero bundle overhead)
* - Messages are loaded on-demand per request
*/
import { getRequestConfig } from 'next-intl/server';
import { routing } from '@/lib/i18n/routing';
export default getRequestConfig(async ({ locale }) => {
// Validate that the incoming `locale` parameter is valid
// Type assertion: we know locale will be a string from the URL parameter
const requestedLocale = locale as 'en' | 'it';
// Check if the requested locale is supported, otherwise use default
const validLocale = routing.locales.includes(requestedLocale)
? requestedLocale
: routing.defaultLocale;
return {
// Return the validated locale
locale: validLocale,
// Load messages for the requested locale
// Dynamic import ensures only the requested locale is loaded
messages: (await import(`../../messages/${validLocale}.json`)).default,
// Optional: Configure time zone
// This will be used for date/time formatting
// timeZone: 'Europe/Rome', // Example for Italian users
// Optional: Configure now (for relative time formatting)
// now: new Date(),
};
});

View File

@@ -0,0 +1,47 @@
// src/i18n/routing.ts
/**
* Internationalization routing configuration for next-intl.
*
* This file defines:
* - Supported locales (en, it)
* - Default locale (en)
* - Routing strategy (subdirectory pattern: /[locale]/path)
*
* Architecture Decision:
* - Using subdirectory pattern (/en/about, /it/about) for best SEO
* - Only 2 languages (EN, IT) as template showcase
* - Users can extend by adding more locales to this configuration
*/
import { defineRouting } from 'next-intl/routing';
import { createNavigation } from 'next-intl/navigation';
/**
* Routing configuration for next-intl.
*
* Pattern: /[locale]/[pathname]
* Examples:
* - /en/about
* - /it/about
* - /en/auth/login
* - /it/auth/login
*/
export const routing = defineRouting({
// A list of all locales that are supported
locales: ['en', 'it'],
// Used when no locale matches
defaultLocale: 'en',
// Locale prefix strategy
// - "always": Always show locale in URL (/en/about, /it/about)
// - "as-needed": Only show non-default locales (/about for en, /it/about for it)
// We use "always" for clarity and consistency
localePrefix: 'always',
});
// Lightweight wrappers around Next.js' navigation APIs
// that will consider the routing configuration
export const { Link, redirect, usePathname, useRouter } = createNavigation(routing);
export type Locale = (typeof routing.locales)[number];

View File

@@ -2,11 +2,10 @@
/**
* Utility functions for internationalization.
*
* This file demonstrates type-safe translation usage.
* This file provides pure utility functions for i18n without React dependencies.
* For React hooks, see hooks.ts
*/
import { useTranslations } from 'next-intl';
/**
* Get the display name for a locale code.
*
@@ -54,28 +53,6 @@ export function getLocaleFlag(locale: string): string {
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.