UNPKG

systelab-translate

Version:

The internationalization (i18n) library for Systelab

759 lines (748 loc) 28.9 kB
import { of, forkJoin } from 'rxjs'; import * as i0 from '@angular/core'; import { Injectable, Pipe, NgModule } from '@angular/core'; import { format, setHours, setMinutes, setSeconds, setMilliseconds } from 'date-fns'; import { es, fr, it, enUS, enGB, pt, ca, gl, lt, pl, sk, ru, zhCN, de, th, ja, ko, nl } from 'date-fns/locale'; import * as i1 from '@ngx-translate/core'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import * as i2 from '@angular/common'; import { DecimalPipe, Location, LocationStrategy, PathLocationStrategy } from '@angular/common'; import { catchError, map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; // Code derived from: https://gist.github.com/oskansavli/822382 class DecimalFormat { constructor(formatStr) { this.prefix = ''; this.suffix = ''; this.comma = 0; this.minInt = 1; this.minFrac = 0; this.maxFrac = 0; // get prefix for (var i = 0; i < formatStr.length; i++) { if (formatStr.charAt(i) == '#' || formatStr.charAt(i) == '0') { this.prefix = formatStr.substring(0, i); formatStr = formatStr.substring(i); break; } } // get suffix this.suffix = formatStr.replace(/[#]|[0]|[,]|[.]/g, ''); var numberStr = formatStr.replace(/[^0#,.]/g, ''); var intStr = ''; var fracStr = ''; var point = numberStr.indexOf('.'); if (point != -1) { intStr = numberStr.substring(0, point); fracStr = numberStr.substring(point + 1); } else { intStr = numberStr; } var commaPos = intStr.lastIndexOf(','); if (commaPos != -1) { this.comma = intStr.length - 1 - commaPos; } intStr = intStr.replace(/[,]/g, ''); // remove commas fracStr = fracStr.replace(/[,]|[.]+/g, ''); this.maxFrac = fracStr.length; var tmp = intStr.replace(/[^0]/g, ''); // remove all except zero if (tmp.length > this.minInt) { this.minInt = tmp.length; } tmp = fracStr.replace(/[^0]/g, ''); this.minFrac = tmp.length; } format(numStr) { // remove prefix, suffix and commas var numberStr = this.formatBack(numStr) .toLowerCase(); // do not format if not a number if (isNaN(numberStr) || numberStr.length == 0) { return numStr; } //scientific numbers var positione = numberStr.indexOf("e"); if (positione != -1) { var n = Number(numberStr); // if (n == "Infinity" || n == "-Infinity") { // return numberStr; // } numberStr = n + ""; if (numberStr.indexOf('e') != -1) { return numberStr; } } var negative = false; // remove sign if (numberStr.charAt(0) == '-') { negative = true; numberStr = numberStr.substring(1); } else if (numberStr.charAt(0) == '+') { numberStr = numberStr.substring(1); } var point = numberStr.indexOf('.'); // position of point character var intStr = ''; var fracStr = ''; if (point != -1) { intStr = numberStr.substring(0, point); fracStr = numberStr.substring(point + 1); } else { intStr = numberStr; } fracStr = fracStr.replace(/[.]/, ''); // remove other point characters var isPercentage = this.suffix && this.suffix.charAt(0) === '%'; // if percentage, number will be multiplied by 100. var minInt = this.minInt, minFrac = this.minFrac, maxFrac = this.maxFrac; if (isPercentage) { minInt -= 2; minFrac += 2; maxFrac += 2; } if (fracStr.length > maxFrac) { // round //case 6143 var num2 = new Number('0.' + fracStr); var num = (maxFrac == 0) ? Math.round(num2.valueOf()) : num2.toFixed(maxFrac); // toFixed method has bugs on IE (0.7 --> 0) fracStr = num.toString(10) .substr(2); var c = (Number(num) >= 1) ? 1 : 0; //carry var x, i = intStr.length - 1; while (c) { //increment intStr if (i == -1) { intStr = '1' + intStr; break; } else { x = intStr.charAt(i); if (x == 9) { x = '0'; c = 1; } else { x = (++x) + ''; c = 0; } intStr = intStr.substring(0, i) + x + intStr.substring(i + 1, intStr.length); i--; } } } for (var i = fracStr.length; i < minFrac; i++) { // if minFrac=4 then 1.12 --> 1.1200 fracStr = fracStr + '0'; } while (fracStr.length > minFrac && fracStr.charAt(fracStr.length - 1) == '0') { // if minInt=4 then 00034 --> 0034) fracStr = fracStr.substring(0, fracStr.length - 1); } for (var i = intStr.length; i < minInt; i++) { // if minInt=4 then 034 --> 0034 intStr = '0' + intStr; } while (intStr.length > minInt && intStr.charAt(0) == '0') { // if minInt=4 then 00034 --> 0034) intStr = intStr.substring(1); } if (isPercentage) { // multiply by 100 intStr += fracStr.substring(0, 2); fracStr = fracStr.substring(2); } var j = 0; for (var i = intStr.length; i > 0; i--) { // add commas if (j != 0 && j % this.comma == 0) { intStr = intStr.substring(0, i) + ',' + intStr.substring(i); j = 0; } j++; } var formattedValue; if (fracStr.length > 0) { formattedValue = this.prefix + intStr + '.' + fracStr + this.suffix; } else { formattedValue = this.prefix + intStr + this.suffix; } if (negative) { formattedValue = '-' + formattedValue; } return formattedValue; } /** * @description Converts formatted value back to non-formatted value * @methodOf DecimalFormat * @param {String} fNumberStr Formatted number * @return {String} Original number * @author Oskan Savli */ formatBack(fNumStr) { fNumStr += ''; //ensure it is string if (!fNumStr) { return ''; } //do not return undefined or null if (!isNaN(fNumStr)) { return this.getNumericString(fNumStr); } var fNumberStr = fNumStr; var negative = false; if (fNumStr.charAt(0) == '-') { fNumberStr = fNumberStr.substr(1); negative = true; } var pIndex = fNumberStr.indexOf(this.prefix); var sIndex = (this.suffix == '') ? fNumberStr.length : fNumberStr.indexOf(this.suffix, this.prefix.length + 1); if (pIndex == 0 && sIndex > 0) { // remove suffix fNumberStr = fNumberStr.substr(0, sIndex); // remove prefix fNumberStr = fNumberStr.substr(this.prefix.length); // remove commas fNumberStr = fNumberStr.replace(/,/g, ''); if (negative) { fNumberStr = '-' + fNumberStr; } if (!isNaN(fNumberStr)) { return this.getNumericString(fNumberStr); } } return fNumStr; } /** * @description We shouldn't return strings like 1.000 in formatBack method. * However, using only Number(str) is not enough, because it omits . in big numbers * like 23423423423342234.34 => 23423423423342236 . There's a conflict in cases * 6143 and 6541. * @methodOf DecimalFormat * @param {String} str Numberic string * @return {String} Corrected numeric string * @author Serdar Bicer */ getNumericString(str) { //first convert to number var num = new Number(str); //check if there is a missing dot var numStr = num + ''; if (str.indexOf('.') > -1 && numStr.indexOf('.') < 0) { //check if original string has all zeros after dot or not for (var i = str.indexOf('.') + 1; i < str.length; i++) { //if not, this means we lost precision if (str.charAt(i) !== '0') { return str; } } return numStr; } return str; } } class DateUtil { constructor(locale) { this.locale = locale; } setLocale(lang) { this.locale = lang; } formatDate(date) { if (!date) { return undefined; } return format(date, this.getDateFormat()); } formatDateFullYear(date) { if (!date) { return undefined; } return format(date, this.getDateFormat(true)); } formatTime(date, withSeconds) { if (!date) { return undefined; } return format(date, this.getTimeFormat(withSeconds)); } formatDateTime(date, fullYear, withSeconds) { if (!date) { return undefined; } const formattedDate = (fullYear) ? this.formatDateFullYear(date) : this.formatDate(date); const formattedHour = this.formatTime(date, withSeconds); return formattedDate + ' ' + formattedHour; } formatMonthAndYear(date) { if (!date) { return undefined; } return format(date, 'MMMM, yyyy', { locale: this.convertSystelabLocaleToDateFnsLocale(this.locale) }); } formatDateAndShortMonth(date) { if (!date) { return undefined; } return format(date, 'd MMM', { locale: this.convertSystelabLocaleToDateFnsLocale(this.locale) }); } getDateFrom(date) { let d = setHours(date, 0); d = setMinutes(d, 0); d = setSeconds(d, 0); d = setMilliseconds(d, 0); return d; } getDateTo(date) { let d = setHours(date, 23); d = setMinutes(d, 59); d = setSeconds(d, 59); d = setMilliseconds(d, 999); return d; } getDateMidDay(date) { let d = setHours(date, 12); d = setMinutes(d, 0); d = setSeconds(d, 0); d = setMilliseconds(d, 0); return d; } getTimeFormat(withSeconds = false) { if (withSeconds) { return this.locale === 'en-US' ? 'hh:mm:ss a' : 'HH:mm:ss'; } else { return this.locale === 'en-US' ? 'hh:mm a' : 'HH:mm'; } } getDateFormat(fullDateFormat = false) { return this.getLocalizedStringDateFormat(this.locale, fullDateFormat); } getDateFormatForDatePicker(fullDateFormat = false) { return this.getLocalizedDateFormatForDatePicker(this.locale, fullDateFormat); } getFirstDayOfWeek() { switch (this.locale) { case 'en-US': case 'zh-CN': case 'th-TH': case 'ja-JA': return 0; // Sunday default: return 1; // Monday } } getSeparator(locale) { switch (locale) { case 'pl-PL': case 'lt-LT': case 'pt-PT': case 'pt-BR': case 'nl-NL': return '-'; case 'sk-SK': case 'ru-RU': case 'de-DE': return '.'; default: return '/'; } } parseDate(currentDateValue, locale) { if (!locale) { locale = this.locale; } if (!currentDateValue) { return undefined; } const auxArray = currentDateValue.split(this.getSeparator(locale)); if (auxArray.indexOf('') > -1) { return undefined; } switch (locale) { case 'pl-PL': case 'lt-LT': return new Date(auxArray.join('/')); case 'zh-CN': case 'ja-JA': case 'en-US': return new Date(currentDateValue); default: return new Date(auxArray[1] + '/' + auxArray[0] + '/' + auxArray[2]); } } convertSystelabLocaleToDateFnsLocale(localeString) { const localeForSystelabLocale = new Map([ ['es', es], ['es-ES', es], ['es-CL', es], ['es-MX', es], ['es-UY', es], ['es-AR', es], ['es-BO', es], ['es-CO', es], ['es-CR', es], ['es-DO', es], ['es-EC', es], ['es-SV', es], ['es-GT', es], ['es-HN', es], ['es-NI', es], ['es-PA', es], ['es-PY', es], ['es-PE', es], ['es-PR', es], ['es-VE', es], ['fr', fr], ['fr-FR', fr], ['it', it], ['it-IT', it], ['en', enUS], ['en-US', enUS], ['en-GB', enGB], ['pt', pt], ['pt-BR', pt], ['ca', ca], ['gl', gl], ['lt', lt], ['lt-LT', lt], ['pl', pl], ['pl-PL', pl], ['sk', sk], ['sk-SK', sk], ['ru', ru], ['ru-RU', ru], ['zh', zhCN], ['zh-CN', zhCN], ['de', de], ['de-DE', de], ['th', th], ['th-TH', th], ['ja', ja], ['ja-JA', ja], ['ko', ko], ['ko-KR', ko], ['nl', nl], ['nl-NL', nl], ]); const locale = localeForSystelabLocale.get(localeString); return locale ? locale : enUS; } getLocalizedStringDateFormat(locale, fullDateFormat = false) { const year = fullDateFormat ? 'yyyy' : 'yy'; switch (locale) { case 'en-US': return fullDateFormat ? `MM/dd/${year}` : `M/d/${year}`; case 'ko-KO': return `${year}. M. d`; case 'pl-PL': case 'lt-LT': return `${year}-MM-dd`; case 'pt-PT': case 'pt-BR': case 'nl-NL': return `dd-MM-${year}`; case 'sk-SK': case 'ru-RU': return `d.M.${year}`; case 'zh-ZH': return `${year}-M-d`; case 'de-DE': return `dd.MM.${year}`; case 'th-TH': return `d/M/${year}`; case 'ja-JA': return `${year}/MM/dd`; default: return `dd/MM/${year}`; } } getLocalizedDateFormatForDatePicker(locale, fullDateFormat = false) { const year = fullDateFormat ? 'yy' : 'y'; switch (locale) { case 'en-US': return fullDateFormat ? `mm/dd/${year}` : `m/d/${year}`; case 'ko-KO': return `${year}. m. d`; case 'pl-PL': case 'lt-LT': return `${year}-mm-dd`; case 'pt-PT': case 'pt-BR': case 'nl-NL': return `dd-mm-${year}`; case 'sk-SK': case 'ru-RU': return `d.m.${year}`; case 'zh-CN': return `${year}-m-d`; case 'de-DE': return `dd.mm.${year}`; case 'th-TH': return `d/m/${year}`; case 'ja-JA': return `${year}/mm/dd`; default: return `dd/mm/${year}`; } } } class I18nService { constructor(translateService) { this.translateService = translateService; this.staticBundles = {}; this.translateService.setDefaultLang('en-US'); this.dateUtil = new DateUtil('en-US'); } use(locale) { this.locale = locale; this.dateUtil.setLocale(locale); return this.translateService.use(locale); } getLocale() { return this.locale; } getCurrentLanguage() { return this.translateService.currentLang; } getBrowserLang() { return this.translateService.getBrowserLang(); } reloadLanguage(lang) { return this.translateService.reloadLang(lang); } setTranslation(locale, translations) { this.translateService.setTranslation(locale, translations, false); } appendTranslation(locale, translations) { this.translateService.setTranslation(locale, translations, true); } get(bundle) { if (typeof bundle === 'string' && this.staticBundles[bundle]) { return of(this.staticBundles[bundle]); } return this.translateService.get(bundle); } instant(key, interpolateParams) { if (typeof key === 'string' && this.staticBundles[key]) { let bundleValue = ''; if (typeof interpolateParams === 'object') { Object.keys(interpolateParams) .forEach((paramKey) => { bundleValue = this.staticBundles[key].replace('{{' + paramKey + '}}', interpolateParams[paramKey]); }); return bundleValue; } else if (this.staticBundles[key].indexOf('{') > -1 && !interpolateParams) { const regEx = /{{([^]*)}}/g; return this.staticBundles[key].replace(regEx, ''); } return this.staticBundles[key]; } return this.translateService.instant(key, interpolateParams); } setStaticBundles(staticBundles) { if (staticBundles) { this.staticBundles = staticBundles; } } getDateFormat(isFullYear = false) { return this.dateUtil.getDateFormat(isFullYear); } getDateFormatForDatePicker(isFullYear = false) { return this.dateUtil.getDateFormatForDatePicker(isFullYear); } getTimeFormat(withSeconds = false) { return this.dateUtil.getTimeFormat(withSeconds); } formatDate(date) { return this.dateUtil.formatDate(date); } formatDateFullYear(date) { return this.dateUtil.formatDateFullYear(date); } formatTime(date, withSeconds) { return this.dateUtil.formatTime(date, withSeconds); } formatDateTime(date, fullYear, withSeconds) { return this.dateUtil.formatDateTime(date, fullYear, withSeconds); } formatMonthAndYear(date) { return this.dateUtil.formatMonthAndYear(date); } formatDateAndShortMonth(date) { return this.dateUtil.formatDateAndShortMonth(date); } getDateFrom(date) { return this.dateUtil.getDateFrom(date); } getDateTo(date) { return this.dateUtil.getDateTo(date); } getDateMidDay(date) { return this.dateUtil.getDateMidDay(date); } getFirstDayOfWeek() { return this.dateUtil.getFirstDayOfWeek(); } parseDate(currentDateValue, locale) { return this.dateUtil.parseDate(currentDateValue, locale); } formatNumber(numberToFormat, decimalFormat, applyLocale) { const df = new DecimalFormat(decimalFormat); const sNumber = df.format(numberToFormat, decimalFormat); if (applyLocale) { let minimumFractionDigits = 0; if (sNumber.split('.')[1]) { minimumFractionDigits = sNumber.split('.')[1].length; } return Number(sNumber) .toLocaleString(this.locale, { minimumFractionDigits }); } return sNumber; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: I18nService, deps: [{ token: i1.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: I18nService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: I18nService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.TranslateService }] }); class NumberFormatPipe { constructor(i18nService, decimalPipe) { this.i18nService = i18nService; this.decimalPipe = decimalPipe; } transform(value, precision, units, priorSymbol, defaultSymbolWhenNull, ...args) { if (value || value === 0) { if (!precision) { precision = '1.0-2'; } try { let roundedValue = this.decimalPipe.transform(value, precision, this.i18nService.getLocale()); if (units) { roundedValue = roundedValue + units; } if (priorSymbol) { roundedValue = priorSymbol + ' ' + roundedValue; } return roundedValue; } catch (error) { console.error(error); return ''; } } else { return defaultSymbolWhenNull ? defaultSymbolWhenNull : ''; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: NumberFormatPipe, deps: [{ token: I18nService }, { token: i2.DecimalPipe }], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.1.1", ngImport: i0, type: NumberFormatPipe, name: "numberformat" }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: NumberFormatPipe, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: NumberFormatPipe, decorators: [{ type: Pipe, args: [{ name: 'numberformat' }] }, { type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: I18nService }, { type: i2.DecimalPipe }] }); class GeneralTranslatePipe { constructor(i18nService) { this.i18nService = i18nService; } transform(query, ...args) { return this.i18nService.get(query); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: GeneralTranslatePipe, deps: [{ token: I18nService }], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.1.1", ngImport: i0, type: GeneralTranslatePipe, name: "translate" }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: GeneralTranslatePipe, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: GeneralTranslatePipe, decorators: [{ type: Pipe, args: [{ name: 'translate' }] }, { type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: I18nService }] }); class LocalizableTranslateStaticLoader { constructor(http, location) { this.http = http; this.location = location; this.prefix = ''; if (!(window.location.pathname === '/' || window.location.pathname === '/context.html')) { this.prefix = window.location.pathname; if (this.prefix.endsWith('index.html')) { // That's the case of Electron when starting from local file. this.prefix = this.prefix.substr(0, this.prefix.length - 10); } if (this.prefix.endsWith('/')) { this.prefix = this.prefix.substr(0, this.prefix.length - 1); } if (this.prefix.endsWith(this.location.path())) { // When starting from an Angular application route this.prefix = this.prefix.substr(0, this.prefix.length - this.location.path().length); } this.prefix = (this.prefix !== '') ? this.prefix + '/' : ''; } } getTranslation(locale) { // If the execution is from testing the http access can be avoided with mock translations if (globalThis.jasmine && globalThis.jasmine['translations']) { return of(globalThis.jasmine['translations']); } const language = locale.split('-')[0]; const country = locale.split('-')[1]; const languageAndCountry = language + '_' + country; return forkJoin([ this.http.get(`${this.prefix}i18n/language/MessagesBundle_${language}.json`).pipe(catchError(() => of({}))), this.http.get(`${this.prefix}i18n/language/MessagesBundle_${languageAndCountry}.json`).pipe(catchError(() => of({}))), this.http.get(`${this.prefix}i18n/error/ErrorsBundle_${language}.json`).pipe(catchError(() => of({}))), this.http.get(`${this.prefix}i18n/error/ErrorsBundle_${languageAndCountry}.json`).pipe(catchError(() => of({}))), ]).pipe(map((translations) => { if (translations.length > 0) { let bundles = translations[0]; for (let i = 1; i < translations.length; i++) { bundles = this.mergeRecursive(bundles, translations[i]); } return bundles; } else { return of(undefined); } })); } mergeRecursive(obj1, obj2) { for (const p in obj2) { try { // Property in destination object set; update its value. if (obj2[p].constructor === Object) { obj1[p] = this.mergeRecursive(obj1[p], obj2[p]); } else { obj1[p] = obj2[p]; } } catch (e) { // Property in destination object not set; create it and set its value. obj1[p] = obj2[p]; } } return obj1; } } // AoT requires an exported function for factories function httpLoaderFactory(http, location) { return new LocalizableTranslateStaticLoader(http, location); } class SystelabTranslateModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: SystelabTranslateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.1.1", ngImport: i0, type: SystelabTranslateModule, declarations: [GeneralTranslatePipe, NumberFormatPipe], imports: [i1.TranslateModule], exports: [GeneralTranslatePipe, NumberFormatPipe] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: SystelabTranslateModule, providers: [ DecimalPipe, Location, { provide: LocationStrategy, useClass: PathLocationStrategy } ], imports: [TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: (httpLoaderFactory), deps: [HttpClient, Location] } })] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.1", ngImport: i0, type: SystelabTranslateModule, decorators: [{ type: NgModule, args: [{ imports: [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: (httpLoaderFactory), deps: [HttpClient, Location] } }) ], declarations: [ GeneralTranslatePipe, NumberFormatPipe ], exports: [ GeneralTranslatePipe, NumberFormatPipe ], providers: [ DecimalPipe, Location, { provide: LocationStrategy, useClass: PathLocationStrategy } ] }] }] }); /* * Public API Surface of systelab-translate */ /** * Generated bundle index. Do not edit. */ export { GeneralTranslatePipe, I18nService, NumberFormatPipe, SystelabTranslateModule, httpLoaderFactory }; //# sourceMappingURL=systelab-translate.mjs.map