@apicart/vue-components
Version:
Apicart Vue.Js components for simple e-commerce platform development
206 lines (164 loc) • 5.72 kB
text/typescript
// Dependencies
import Apicart from '@apicart/core-sdk';
import Vue from 'vue';
import VueI18n from 'vue-i18n';
// Translations
import fallbackTranslations from '@apicart/web-components-localization/localization/en.json';
Vue.use(VueI18n);
class Translator {
private _config: Record<string, any> = {
actualLocale: null,
fallbackLocale: 'en',
localization: {},
// eslint-disable-next-line max-len
localizationFilesUrl: 'https://cdn.jsdelivr.net/npm/@apicart/web-components-localization@__APICART_PACKAGES_VERSION__/localization',
currencyFormats: {
en: {
currency: {
style: 'currency', currency: 'EUR', currencyDisplay: 'symbol', code: 'EUR'
}
}
}
};
private _i18n: VueI18n = null;
private _loadedLanguages: string[] = ['en'];
private _processedLanguageFiles: Record<string, Promise<any>> = {};
private _downloadUrlsWithError: string[] = [];
private _loadingLanguagePromise: Promise<any> = null;
private _configurationPromises: Promise<any> = null;
constructor()
{
this.configure();
Apicart.Utils.EventDispatcher.addListener(
'apicart-vueComponentsTranslator-configure',
'apicart:configure', () => {
this.configure();
}
);
}
public async configure(config: Record<string, any> = null): Promise<void>
{
await this._configurationPromises;
config = config || Apicart.getConfigParameter('vueComponentsTranslator') || {};
this._config = Apicart.Utils.Objects.merge(this._config, config);
if (!this._config.actualLocale) {
this._config.actualLocale = this.detectLocale();
}
const configurationPromises = [];
configurationPromises.push(await this.loadLocalizationFiles(this._config.actualLocale));
if (this._config.localization) {
configurationPromises.push(this.addLocalization(this._config.localization));
}
this._configurationPromises = Promise.all(configurationPromises);
await this._configurationPromises;
if (this._config.currencyFormats) {
this.addNumberFormat(this._config.currencyFormats);
}
}
public getActualCurrencyConfig(): Record<string, any>
{
const currencyConfig = typeof this.getI18n().numberFormats[this.getActualLocale()] === 'undefined'
? this.getI18n().numberFormats[this.getFallbackLocale()]
: this.getI18n().numberFormats[this.getActualLocale()];
return typeof currencyConfig === 'undefined' ? {} : currencyConfig.currency;
}
public getActualLocale(): string
{
return this.getI18n().locale;
}
public getFallbackLocale(): string
{
return this.getI18n().fallbackLocale ? this.getI18n().fallbackLocale : this._config.fallbackLocale;
}
public getI18n(): VueI18n
{
if (!this._i18n) {
const messages = Apicart.Storage.getItem('apicart-localization') || {};
messages[this._config.fallbackLocale] = fallbackTranslations;
this._i18n = new VueI18n({
locale: this.detectLocale(),
fallbackLocale: this._config.fallbackLocale,
messages: messages,
numberFormats: this._config.currencyFormats || this._config.defaultCurrencyFormats
});
}
return this._i18n;
}
public async loadLocalizationFiles(lang: string, url: string = null): Promise<string>
{
await this._loadingLanguagePromise;
this._loadingLanguagePromise = this.downloadLocalizationFilesContent(
lang,
url || this._config.localizationFilesUrl + '/' + lang + '.json'
);
await this._loadingLanguagePromise;
return this.setI18nLanguage(lang);
}
public addLocalization(localization: Record<string, string>): void
{
Apicart.Utils.Loops.forEach(localization, (translationsOrUrl, locale) => {
if (typeof translationsOrUrl === 'string') {
this.loadLocalizationFiles(locale, translationsOrUrl);
} else {
this.mergeLocaleMessages(locale, translationsOrUrl);
}
});
}
public addNumberFormat(numberFormat: Record<string, string>): void
{
Apicart.Utils.Loops.forEach(numberFormat, (numberFormat, locale: string) => {
this.getI18n().mergeNumberFormat(locale, numberFormat);
});
}
private async downloadLocalizationFilesContent(locale: string, url: string): Promise<boolean>
{
if (this._loadedLanguages.includes(locale)) {
return true;
}
if (this._downloadUrlsWithError.includes(url)) {
return false;
}
if ( ! (url in this._processedLanguageFiles)) {
this._processedLanguageFiles[url] = Apicart.Utils.Ajax.get(url);
}
const response = await this._processedLanguageFiles[url];
const loaded = response && Apicart.Utils.Objects.isObject(response.data);
if (loaded) {
const localeObject = {};
localeObject[locale] = response.data;
Apicart.Storage.updateItem('apicart-localization', localeObject);
this.mergeLocaleMessages(locale, response.data);
} else {
Apicart.Utils.Console.error('Localization file "' + url + '" could not be loaded.');
this._downloadUrlsWithError.push(url);
}
this._loadedLanguages.push(locale);
return loaded;
}
private setI18nLanguage(lang: string): string
{
this.getI18n().locale = lang;
if (this.isInBrowser()) {
document.querySelector('html').setAttribute('lang', lang);
}
return lang;
}
private isInBrowser(): boolean
{
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
private detectLocale(): string
{
if (!this.isInBrowser()) {
return this._config.fallbackLocale;
}
const documentLang = document.documentElement.getAttribute('lang');
return documentLang ? documentLang : navigator.language || (<any>window).navigator.userLanguage;
}
private mergeLocaleMessages(locale: string, messages: Record<string, any>): void
{
this.getI18n().mergeLocaleMessage(locale, messages);
Apicart.Utils.EventDispatcher.dispatchEvent('apicart:translations:updated');
}
}
export default new Translator();