UNPKG

@sodacore/i18n

Version:

Sodacore i18n is a plugin that offers internationalisation and localisation support for the Sodacore framework.

108 lines (107 loc) 4.66 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { BaseService, Service } from '@sodacore/core'; import { Inject } from '@sodacore/di'; import { Glob } from 'bun'; import { resolve } from 'node:path'; import process from 'node:process'; let I18nService = class I18nService extends BaseService { constructor() { super(...arguments); this._hasTranslations = false; this.translations = {}; } async init() { this.logger.info('[I18N]: Initialising the i18n service.'); // Set the translations path based on the config or default to './translations'. this.translationsPath = this.config?.translationsPath ? this.config.translationsPath.startsWith('./') ? resolve(process.cwd(), this.config.translationsPath) : this.config.translationsPath : resolve(process.cwd(), './translations'); // Get the file paths. const tFilePaths = await this.getFilePaths(); if (!tFilePaths || tFilePaths.length === 0) { this.logger.error('[I18N]: No translation files found or unable to access the translations directory.'); return; } // Let's load the contents of the translation files. this.translations = await this.loadTranslations(tFilePaths); // Log the found translation files. this.logger.info(`[I18N]: Found ${tFilePaths.length} translation file(s).`); } hasTranslations() { return this._hasTranslations; } translate(query, languageCode, fallback) { if (!this._hasTranslations) throw new Error('[I18N]: No translations available.'); if (this.translations[languageCode]) { return this.translations[languageCode][query] || (fallback ?? query); // Return the translation or the original query if not found. } return (fallback ?? query); // Return the original query if the language code is not found. } getAvailableLanguages(lower) { if (!this._hasTranslations) return []; const defaultLang = this.config?.defaultLang || 'en-GB'; const availableLanguages = lower ? Object.keys(this.translations).map(lang => lang.toLowerCase()) : Object.keys(this.translations); return [defaultLang, ...availableLanguages]; } async getFilePaths() { const files = []; const glob = new Glob(`*.json`); try { // Scan and add the files. for await (const file of glob.scan({ cwd: this.translationsPath, onlyFiles: true, absolute: true, })) { files.push(file); this._hasTranslations = true; } return files; } catch { return null; } } async loadTranslations(filePaths) { const translations = {}; for (const filePath of filePaths) { try { const content = await Bun.file(filePath).json(); const fileName = filePath.split('/').pop()?.replace('.json', ''); if (!fileName) throw new Error(`Invalid file name: ${filePath}`); const [lang, country] = fileName.toLowerCase().split('-') || []; translations[fileName.toLowerCase()] = { _lang: lang ?? 'en', _code: fileName, _country: country ?? 'gb', ...content, }; } catch (error) { this.logger.error(`[I18N]: Error loading translation file "${filePath}": ${error}`); } } return translations; } }; __decorate([ Inject('@i18n:config'), __metadata("design:type", Object) ], I18nService.prototype, "config", void 0); I18nService = __decorate([ Service() ], I18nService); export default I18nService;