@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
261 lines (259 loc) • 10.9 kB
JavaScript
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