mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 00:45:41 +00:00
Refactored the whole app based on the requirements shared
This commit is contained in:
9
apps/edr-passenger-api/src/common/i18n/i18n.module.ts
Normal file
9
apps/edr-passenger-api/src/common/i18n/i18n.module.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Module, Global } from '@nestjs/common';
|
||||
import { I18nService } from './i18n.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [I18nService],
|
||||
exports: [I18nService],
|
||||
})
|
||||
export class I18nModule {}
|
||||
49
apps/edr-passenger-api/src/common/i18n/i18n.service.spec.ts
Normal file
49
apps/edr-passenger-api/src/common/i18n/i18n.service.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { I18nService } from './i18n.service';
|
||||
|
||||
describe('I18nService', () => {
|
||||
let service: I18nService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [I18nService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<I18nService>(I18nService);
|
||||
});
|
||||
|
||||
it('should translate English keys', () => {
|
||||
expect(service.translate('common.welcome', 'en')).toBe('Welcome');
|
||||
expect(service.translate('booking.created', 'en')).toBe('Booking created successfully');
|
||||
});
|
||||
|
||||
it('should translate Amharic keys', () => {
|
||||
expect(service.translate('common.welcome', 'am')).toBe('እንኳን ደህና መጡ');
|
||||
});
|
||||
|
||||
it('should translate French keys', () => {
|
||||
expect(service.translate('common.welcome', 'fr')).toBe('Bienvenue');
|
||||
});
|
||||
|
||||
it('should translate Oromo keys', () => {
|
||||
expect(service.translate('common.welcome', 'om')).toBe('Baga nagaan dhuftan');
|
||||
});
|
||||
|
||||
it('should fallback to English for unsupported locale', () => {
|
||||
expect(service.translate('common.welcome', 'de')).toBe('Welcome');
|
||||
});
|
||||
|
||||
it('should return key if translation not found', () => {
|
||||
expect(service.translate('nonexistent.key', 'en')).toBe('nonexistent.key');
|
||||
});
|
||||
|
||||
it('should interpolate parameters', () => {
|
||||
const result = service.translate('common.welcome', 'en', { name: 'John' });
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return supported locales', () => {
|
||||
const locales = service.getSupportedLocales();
|
||||
expect(locales).toEqual(['en', 'am', 'fr', 'om']);
|
||||
});
|
||||
});
|
||||
63
apps/edr-passenger-api/src/common/i18n/i18n.service.ts
Normal file
63
apps/edr-passenger-api/src/common/i18n/i18n.service.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
type TranslationMap = Record<string, any>;
|
||||
|
||||
@Injectable()
|
||||
export class I18nService {
|
||||
private translations: Map<string, TranslationMap> = new Map();
|
||||
private readonly supportedLocales = ['en', 'am', 'fr', 'om'];
|
||||
private readonly defaultLocale = 'en';
|
||||
|
||||
constructor() {
|
||||
this.loadTranslations();
|
||||
}
|
||||
|
||||
private loadTranslations() {
|
||||
for (const locale of this.supportedLocales) {
|
||||
const filePath = path.join(__dirname, 'translations', `${locale}.json`);
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
this.translations.set(locale, JSON.parse(content));
|
||||
} catch (err) {
|
||||
console.warn(`Failed to load translation file for locale: ${locale}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
translate(key: string, locale: string = this.defaultLocale, params?: Record<string, string>): string {
|
||||
const normalizedLocale = this.normalizeLocale(locale);
|
||||
const translations = this.translations.get(normalizedLocale) || this.translations.get(this.defaultLocale);
|
||||
|
||||
if (!translations) return key;
|
||||
|
||||
const keys = key.split('.');
|
||||
let value: any = translations;
|
||||
|
||||
for (const k of keys) {
|
||||
value = value?.[k];
|
||||
if (value === undefined) return key;
|
||||
}
|
||||
|
||||
if (typeof value !== 'string') return key;
|
||||
|
||||
if (params) {
|
||||
return Object.entries(params).reduce(
|
||||
(text, [param, val]) => text.replace(new RegExp(`{{${param}}}`, 'g'), val),
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private normalizeLocale(locale: string): string {
|
||||
const normalized = locale.toLowerCase().split('-')[0];
|
||||
return this.supportedLocales.includes(normalized) ? normalized : this.defaultLocale;
|
||||
}
|
||||
|
||||
getSupportedLocales(): string[] {
|
||||
return this.supportedLocales;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
import { LOCALE_KEY } from './locale.middleware';
|
||||
|
||||
export const Locale = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext): string => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
return request[LOCALE_KEY] || 'en';
|
||||
},
|
||||
);
|
||||
16
apps/edr-passenger-api/src/common/i18n/locale.middleware.ts
Normal file
16
apps/edr-passenger-api/src/common/i18n/locale.middleware.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||
|
||||
export const LOCALE_KEY = 'locale';
|
||||
|
||||
@Injectable()
|
||||
export class LocaleMiddleware implements NestMiddleware {
|
||||
use(req: any, res: any, next: () => void) {
|
||||
const locale =
|
||||
req.query.lang as string ||
|
||||
req.headers['accept-language']?.split(',')[0]?.split('-')[0] ||
|
||||
'en';
|
||||
|
||||
req[LOCALE_KEY] = locale;
|
||||
next();
|
||||
}
|
||||
}
|
||||
23
apps/edr-passenger-api/src/common/i18n/translations/am.json
Normal file
23
apps/edr-passenger-api/src/common/i18n/translations/am.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"common": {
|
||||
"welcome": "እንኳን ደህና መጡ",
|
||||
"error": "ስህተት ተከስቷል",
|
||||
"success": "ተሳክቷል"
|
||||
},
|
||||
"booking": {
|
||||
"created": "ቦታ ማስያዝ በተሳካ ሁኔታ ተፈጥሯል",
|
||||
"notFound": "ቦታ ማስያዝ አልተገኘም",
|
||||
"cancelled": "ቦታ ማስያዝ ተሰርዟል",
|
||||
"confirmed": "ቦታ ማስያዝ ተረጋግጧል"
|
||||
},
|
||||
"payment": {
|
||||
"succeeded": "ክፍያ ተሳክቷል",
|
||||
"failed": "ክፍያ አልተሳካም",
|
||||
"pending": "ክፍያ በመጠባበቅ ላይ"
|
||||
},
|
||||
"ticket": {
|
||||
"issued": "ትኬት ተሰጥቷል",
|
||||
"validated": "ትኬት ተረጋግጧል",
|
||||
"alreadyValidated": "ትኬት ቀድሞውኑ ተረጋግጧል"
|
||||
}
|
||||
}
|
||||
23
apps/edr-passenger-api/src/common/i18n/translations/en.json
Normal file
23
apps/edr-passenger-api/src/common/i18n/translations/en.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"common": {
|
||||
"welcome": "Welcome",
|
||||
"error": "An error occurred",
|
||||
"success": "Success"
|
||||
},
|
||||
"booking": {
|
||||
"created": "Booking created successfully",
|
||||
"notFound": "Booking not found",
|
||||
"cancelled": "Booking cancelled",
|
||||
"confirmed": "Booking confirmed"
|
||||
},
|
||||
"payment": {
|
||||
"succeeded": "Payment successful",
|
||||
"failed": "Payment failed",
|
||||
"pending": "Payment pending"
|
||||
},
|
||||
"ticket": {
|
||||
"issued": "Ticket issued",
|
||||
"validated": "Ticket validated",
|
||||
"alreadyValidated": "Ticket already validated"
|
||||
}
|
||||
}
|
||||
23
apps/edr-passenger-api/src/common/i18n/translations/fr.json
Normal file
23
apps/edr-passenger-api/src/common/i18n/translations/fr.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"common": {
|
||||
"welcome": "Bienvenue",
|
||||
"error": "Une erreur s'est produite",
|
||||
"success": "Succès"
|
||||
},
|
||||
"booking": {
|
||||
"created": "Réservation créée avec succès",
|
||||
"notFound": "Réservation introuvable",
|
||||
"cancelled": "Réservation annulée",
|
||||
"confirmed": "Réservation confirmée"
|
||||
},
|
||||
"payment": {
|
||||
"succeeded": "Paiement réussi",
|
||||
"failed": "Échec du paiement",
|
||||
"pending": "Paiement en attente"
|
||||
},
|
||||
"ticket": {
|
||||
"issued": "Billet émis",
|
||||
"validated": "Billet validé",
|
||||
"alreadyValidated": "Billet déjà validé"
|
||||
}
|
||||
}
|
||||
23
apps/edr-passenger-api/src/common/i18n/translations/om.json
Normal file
23
apps/edr-passenger-api/src/common/i18n/translations/om.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"common": {
|
||||
"welcome": "Baga nagaan dhuftan",
|
||||
"error": "Dogongora uumame",
|
||||
"success": "Milkaa'ina"
|
||||
},
|
||||
"booking": {
|
||||
"created": "Bakka qabachuu milkaa'inaan uumame",
|
||||
"notFound": "Bakka qabachuu hin argamne",
|
||||
"cancelled": "Bakka qabachuu haqame",
|
||||
"confirmed": "Bakka qabachuu mirkaneeffame"
|
||||
},
|
||||
"payment": {
|
||||
"succeeded": "Kaffaltiin milkaa'e",
|
||||
"failed": "Kaffaltiin hin milkoofne",
|
||||
"pending": "Kaffaltiin eegaa jira"
|
||||
},
|
||||
"ticket": {
|
||||
"issued": "Tiikeetiin kenname",
|
||||
"validated": "Tiikeetiin mirkaneeffame",
|
||||
"alreadyValidated": "Tiikeetiin duraan mirkaneeffame"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user