UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

261 lines (259 loc) 10.9 kB
import { of, throwError, zip } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { LogLevel } from '../diagnostics/log-level'; import { Logging } from '../diagnostics/logging'; import { Http } from './http'; import { LanguageInventory } from './language'; /** * Class to retrieve localized resources based on the user locale * This class lets you load resources from a json file in an * arbitrary location and determines what locale resources to return * on the user preference */ export class LocalizationManager { options; static defaultResourcesStringsFolder = 'assets/strings'; static resourcesStringsFile = '/strings.json'; static resourcesStringsFileFormat = '/{0}/strings.json'; static languageManager = new LanguageInventory(); static localStorageLocaleKey = 'locale:@msft-sme/shell'; static localStorageLocaleSetKey = 'localeSet:@msft-sme/shell'; static neutralCultures = LocalizationManager.languageManager.neutralCultures; static regionalCultures = LocalizationManager.languageManager.regionalCultures; defaultLocaleId = LocalizationManager.neutralCultures[0].id; resourcesStringFormat; resourcesStringDefaultFile; http = new Http(); localeIdInternal; resourcesPath; /** * Initializes a new instance of the LocalizationManager class that reads the localized assets from * the given locations. * @param options? The options to initialize the localization manager. */ constructor(options) { this.options = options; const resourcesPath = options && options.resourcesPath; if (resourcesPath) { this.resourcesPath = MsftSme.trimEnd(resourcesPath.trim(), '/'); } else { this.resourcesPath = LocalizationManager.defaultResourcesStringsFolder; } const azureLocale = options && options.azureLocale; if (azureLocale) { const azureLocaleSet = this.normalizeAzureLocaleId(azureLocale); if (azureLocaleSet) { this.localeIdInternal = azureLocaleSet; } } this.initializeResources(); } /** * Gets current locale. * @return string the locale string. */ get localeId() { return this.localeIdInternal; } /** * Gets the navigator language */ getNavigatorLanguage() { return navigator.language; } /** * Creates a LocaleSet from a string */ createLocaleSet(id, neutral) { return { id: id, neutral: neutral || id }; } /** * Sets the current locale in persistent storage * @param localeId the string representing the locale selected by the user. Ex: 'es' or 'en' */ saveLocale(localeSet) { if (!this.checkBothAvailable(localeSet)) { throw new Error(`The locale specified, ${localeSet.id} and ${localeSet.neutral} were not recognized.`); } this.localeIdInternal = localeSet; MsftSme.LocalStorageHandler.setItem(LocalizationManager.localStorageLocaleSetKey, JSON.stringify(localeSet)); MsftSme.LocalStorageHandler.setItem(LocalizationManager.localStorageLocaleKey, localeSet.neutral); this.updateDocumentLanguage(); } /** * Updates the lang attribute of the document to reflect the current locale. */ updateDocumentLanguage() { const locale = this.getLocaleId(); document.documentElement.setAttribute('lang', locale.id); } /** * Ensures Resources are Initialized */ initializeResources() { const self = MsftSme.self(); const locale = this.getLocaleId(); self.Resources = { strings: {}, localeId: locale.neutral, localeRegionalId: locale.id }; } /** * Gets the current locale. * Throughout code, locale code is using <lower case>-<upper case> format which is standard on both Microsoft Edge and Google Chrome. * ex) en-US, de-DE, ja-JP and so on. * @returns The current locale selected by the user */ getLocaleId() { if (this.localeIdInternal) { return this.localeIdInternal; } const storageLocaleSet = MsftSme.LocalStorageHandler.getItem(LocalizationManager.localStorageLocaleSetKey); let localeSet; if (storageLocaleSet) { localeSet = JSON.parse(storageLocaleSet); if (this.checkBothAvailable(localeSet)) { return this.localeIdInternal = localeSet; } } // Now we read the browser locales and if it's supported, then we use that otherwise use default locale const navigatorLanguage = this.getNavigatorLanguage(); if (!navigatorLanguage) { return this.localeIdInternal = { id: this.defaultLocaleId, neutral: this.defaultLocaleId }; } localeSet = { id: navigatorLanguage, neutral: navigatorLanguage }; if (this.checkBothAvailable(localeSet)) { return this.localeIdInternal = localeSet; } // Try with the neutral part only const neutral = navigatorLanguage.split('-')[0].toLowerCase(); const found = LocalizationManager.neutralCultures.find(item => item.id.split('-')[0] === neutral); if (found) { // adjust default string language. localeSet.neutral = found.id; if (this.checkIdAvailable(localeSet)) { return this.localeIdInternal = localeSet; } // cannot found the regional code. return this.localeIdInternal = { id: found.id, neutral: found.id }; } return this.localeIdInternal = { id: this.defaultLocaleId, neutral: this.defaultLocaleId }; } /** * Fetches the localized strings from the server based on the current culture. * @returns an observable with object the localized strings. */ fetchLocalizedStrings() { this.configureFetchFiles(); this.getLocaleId(); if (this.localeId.neutral === this.defaultLocaleId) { // Only fetch the default locale return this.fetchDefaultStrings(); } return zip(this.fetchDefaultStrings(), this.fetchLocaleStrings()) .pipe(catchError(() => of(null)), map(([fetchDefault, fetchLocale]) => { // get the english strings and replace those properties of the localized json if (fetchLocale) { return MsftSme.deepAssign({}, fetchDefault, fetchLocale); } return fetchDefault; })); } configureFetchFiles() { const version = MsftSme.self() && MsftSme.self().Environment && MsftSme.self().Environment.version || 'no-version'; this.resourcesStringFormat = `${this.resourcesPath}${LocalizationManager.resourcesStringsFileFormat}?v=${version}`; this.resourcesStringDefaultFile = `${this.resourcesPath}${LocalizationManager.resourcesStringsFile}?v=${version}`; } checkBothAvailable(localeSet) { if (!localeSet || !localeSet.neutral || !localeSet.id) { return false; } const checkNeutral = LocalizationManager.neutralCultures.find(item => item.id === localeSet.neutral); const checkId = LocalizationManager.regionalCultures.find(item => item.id === localeSet.id); return !!checkNeutral && !!checkId; } checkIdAvailable(localeSet) { const checkId = LocalizationManager.regionalCultures.find(item => item.id === localeSet.id); return !!checkId; } fetchDefaultStrings() { return this.http.get(this.resourcesStringDefaultFile) .pipe(catchError((error) => { if (error.status >= 400) { // If we got any error, we just reply with that error and the map function will handle it Logging.log({ source: 'LocalizationManager', level: LogLevel.Error, message: `Error ${error.status} received when getting default localized strings for the ` + `${this.defaultLocaleId} locale. The error was: ${error.message}` }); } return throwError(() => error); }), map((result) => { return result.response.Strings; })); } fetchLocaleStrings() { return this.http.get(this.resourcesStringFormat.format(this.localeId.neutral)) .pipe(catchError((error) => { if (error.status >= 400) { // If we got any error, we just reply with that error and the map function will handle it Logging.log({ source: 'LocalizationManager', level: LogLevel.Warning, message: `Error ${error.status} received when getting localized strings for the ` + `user specified locale: '${this.localeId.neutral}'. The error was: ${error.message}` }); return of({}); } }), map((result) => { // If response has body, use that if (!result || !result.response) { return null; } return result.response.Strings; })); } normalizeAzureLocaleId(azureLocale) { if (MsftSme.isNullOrWhiteSpace(azureLocale)) { return null; } const neutralCultures = {}; neutralCultures['de'] = 'de-DE'; neutralCultures['en'] = 'en-US'; neutralCultures['es'] = 'es-ES'; neutralCultures['fr'] = 'fr-FR'; neutralCultures['it'] = 'it-IT'; neutralCultures['hu'] = 'hu-HU'; neutralCultures['nl'] = 'nl-NL'; neutralCultures['pl'] = 'pl-PL'; neutralCultures['pt'] = 'pt-BR'; neutralCultures['sv'] = 'sv-SE'; neutralCultures['tr'] = 'tr-TR'; neutralCultures['cs'] = 'cs-CZ'; neutralCultures['ru'] = 'ru-RU'; neutralCultures['zh'] = 'zh-CN'; neutralCultures['zh-hans'] = 'zh-CN'; neutralCultures['zh-chs'] = 'zh-CN'; neutralCultures['zh-hant'] = 'zh-TW'; neutralCultures['zh-cht'] = 'zh-TW'; neutralCultures['ja'] = 'ja-JP'; neutralCultures['ko'] = 'ko-KR'; const languages = azureLocale.split('.'); const segments = languages[1].split('-'); const id = `${segments[0]}-${segments[1].toUpperCase()}`; const neutral = neutralCultures[languages[0]]; if (!neutral) { return null; } const localeSet = { id, neutral }; if (this.checkBothAvailable(localeSet)) { return localeSet; } // cannot found the regional code. return { id: localeSet.neutral, neutral: localeSet.neutral }; } } //# sourceMappingURL=localization-manager.js.map