Update zh-cn to zh-hans, and update the icon when switch language Change-Id: I3e6d3e8aebec4584f863282082a11b36d7cee193
261 lines
8.0 KiB
JavaScript
261 lines
8.0 KiB
JavaScript
// Copyright 2021 99cloud
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import React from 'react';
|
|
import IntlMessageFormat from 'intl-messageformat';
|
|
import escapeHtml from 'escape-html';
|
|
import cookie from 'cookie';
|
|
import queryParser from 'querystring';
|
|
import invariant from 'invariant';
|
|
import { getLocalStorageItem } from 'utils/local-storage';
|
|
// import * as constants from './constants';
|
|
import { merge } from 'lodash';
|
|
|
|
// eslint-disable-next-line no-extend-native
|
|
String.prototype.defaultMessage = function (msg) {
|
|
return this || msg || '';
|
|
};
|
|
|
|
class SLI18n {
|
|
constructor() {
|
|
this.options = {
|
|
currentLocale: null, // Current locale such as 'en'
|
|
locales: {}, // app locale data like {"en":{"key1":"value1"},"zh-hans":{"key1":"值1"}}
|
|
// eslint-disable-next-line no-console
|
|
warningHandler: function warn(...msg) {
|
|
console.warn(...msg);
|
|
}, // ability to accumulate missing messages using third party services
|
|
escapeHtml: true, // disable escape html in variable mode
|
|
// commonLocaleDataUrls: COMMON_LOCALE_DATA_URLS,
|
|
fallbackLocale: null, // Locale to use if a key is not found in the current locale
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get the formatted message by key
|
|
* @param {string} key The string representing key in locale data file
|
|
* @param {Object} variables Variables in message
|
|
* @returns {string} message
|
|
*/
|
|
get(key, variables) {
|
|
invariant(key, 'key is required');
|
|
const { locales, currentLocale, formats } = this.options;
|
|
|
|
if (!locales || !locales[currentLocale]) {
|
|
this.options.warningHandler(
|
|
`translate locales data "${currentLocale}" not exists.`
|
|
);
|
|
return '';
|
|
}
|
|
let msg = this.getDescendantProp(locales[currentLocale], key);
|
|
if (msg == null || msg === '') {
|
|
if (this.options.fallbackLocale) {
|
|
msg = this.getDescendantProp(locales[this.options.fallbackLocale], key);
|
|
if (msg == null) {
|
|
this.options.warningHandler(
|
|
`translate key "${key}" not defined in ${currentLocale}`
|
|
);
|
|
// this.options.warningHandler(
|
|
// `translate key "${key}" not defined in ${currentLocale} or the fallback locale, ${this.options.fallbackLocale}`
|
|
// );
|
|
msg = key;
|
|
}
|
|
} else {
|
|
this.options.warningHandler(
|
|
`translate key "${key}" not defined in ${currentLocale}`
|
|
);
|
|
msg = key;
|
|
}
|
|
}
|
|
if (variables) {
|
|
variables = { ...variables };
|
|
// HTML message with variables. Escape it to avoid XSS attack.
|
|
Object.keys(variables).forEach((i) => {
|
|
let value = variables[i];
|
|
if (
|
|
this.options.escapeHtml === true &&
|
|
(typeof value === 'string' || value instanceof String) &&
|
|
value.indexOf('<') >= 0 &&
|
|
value.indexOf('>') >= 0
|
|
) {
|
|
value = escapeHtml(value);
|
|
}
|
|
variables[i] = value;
|
|
});
|
|
}
|
|
|
|
try {
|
|
const msgFormatter = new IntlMessageFormat(msg, currentLocale, formats);
|
|
return msgFormatter.format(variables);
|
|
} catch (err) {
|
|
this.options.warningHandler(
|
|
`translate format message failed for key='${key}'.`,
|
|
err.message
|
|
);
|
|
return msg;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the formatted html message by key.
|
|
* @param {string} key The string representing key in locale data file
|
|
* @param {Object} variables Variables in message
|
|
* @returns {React.Element} message
|
|
*/
|
|
getHTML(key, variables) {
|
|
const msg = this.get(key, variables);
|
|
if (msg) {
|
|
const el = React.createElement('span', {
|
|
dangerouslySetInnerHTML: {
|
|
__html: msg,
|
|
},
|
|
});
|
|
// when key exists, it should still return element if there's defaultMessage() after getHTML()
|
|
const defaultMessage = () => el;
|
|
return {
|
|
defaultMessage,
|
|
d: defaultMessage,
|
|
...el,
|
|
};
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* As same as get(...) API
|
|
* @param {Object} options
|
|
* @param {string} options.id
|
|
* @param {string} options.defaultMessage
|
|
* @param {Object} variables Variables in message
|
|
* @returns {string} message
|
|
*/
|
|
formatMessage(messageDescriptor, variables) {
|
|
const { id, defaultMessage } = messageDescriptor;
|
|
return this.get(id, variables).defaultMessage(defaultMessage);
|
|
}
|
|
|
|
/**
|
|
* As same as getHTML(...) API
|
|
* @param {Object} options
|
|
* @param {string} options.id
|
|
* @param {React.Element} options.defaultMessage
|
|
* @param {Object} variables Variables in message
|
|
* @returns {React.Element} message
|
|
*/
|
|
formatHTMLMessage(messageDescriptor, variables) {
|
|
const { id, defaultMessage } = messageDescriptor;
|
|
return this.getHTML(id, variables).defaultMessage(defaultMessage);
|
|
}
|
|
|
|
/**
|
|
* Helper: determine user's locale via URL, cookie, localStorage, and browser's language.
|
|
* You may not this API, if you have other rules to determine user's locale.
|
|
* @param {string} options.urlLocaleKey URL's query Key to determine locale. Example: if URL=http://localhost?lang=en, then set it 'lang'
|
|
* @param {string} options.cookieLocaleKey Cookie's Key to determine locale. Example: if cookie=lang:en, then set it 'lang'
|
|
* @param {string} options.localStorageLocaleKey LocalStorage's Key to determine locale such as 'lang'
|
|
* @returns {string} determined locale such as 'en'
|
|
*/
|
|
determineLocale(options = {}) {
|
|
return (
|
|
this.getLocaleFromLocalStorage(options) ||
|
|
this.getLocaleFromURL(options) ||
|
|
this.getLocaleFromCookie(options) ||
|
|
this.getLocaleFromBrowser()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Initialize properties and load CLDR locale data according to currentLocale
|
|
* @param {Object} options
|
|
* @param {string} options.currentLocale Current locale such as 'en'
|
|
* @param {string} options.locales App locale data like {"en":{"key1":"value1"},"zh-hans":{"key1":"值1"}}
|
|
* @returns {Promise}
|
|
*/
|
|
init(options = {}) {
|
|
invariant(options.currentLocale, 'options.currentLocale is required');
|
|
invariant(options.locales, 'options.locales is required');
|
|
|
|
Object.assign(this.options, options);
|
|
|
|
this.options.formats = {
|
|
...this.options.formats,
|
|
// constants.defaultFormats
|
|
};
|
|
|
|
return new Promise((resolve) => {
|
|
// init() will not load external common locale data anymore.
|
|
// But, it still return a Promise for backward compatibility.
|
|
resolve();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the initial options
|
|
*/
|
|
getInitOptions() {
|
|
return this.options;
|
|
}
|
|
|
|
/**
|
|
* Load more locales after init
|
|
*/
|
|
load(locales) {
|
|
merge(this.options.locales, locales);
|
|
}
|
|
|
|
getLocaleFromCookie(options) {
|
|
const { cookieLocaleKey } = options;
|
|
if (cookieLocaleKey) {
|
|
const params = cookie.parse(document.cookie);
|
|
return params && params[cookieLocaleKey];
|
|
}
|
|
}
|
|
|
|
getLocaleFromLocalStorage(options) {
|
|
const { localStorageLocaleKey } = options;
|
|
if (localStorageLocaleKey && window.localStorage) {
|
|
return getLocalStorageItem(localStorageLocaleKey);
|
|
}
|
|
}
|
|
|
|
getLocaleFromURL(options) {
|
|
const { urlLocaleKey } = options;
|
|
if (urlLocaleKey) {
|
|
const query = window.location.search.split('?');
|
|
if (query.length >= 2) {
|
|
const params = queryParser.parse(query[1]);
|
|
return params && params[urlLocaleKey];
|
|
}
|
|
}
|
|
}
|
|
|
|
getDescendantProp(locale, key) {
|
|
if (locale[key]) {
|
|
return locale[key];
|
|
}
|
|
|
|
const msg = key
|
|
.split('.')
|
|
.reduce((a, b) => (a !== undefined ? a[b] : a), locale);
|
|
|
|
return msg;
|
|
}
|
|
|
|
getLocaleFromBrowser() {
|
|
return navigator.language || navigator.userLanguage;
|
|
}
|
|
}
|
|
|
|
export default SLI18n;
|