UNPKG

d2

Version:

Javascript library for DHIS2

179 lines (158 loc) 5.42 kB
/** * @module i18n */ import Api from '../api/Api' /** * I18n class for dealing with translations */ class I18n { constructor(sources = [], api = Api.getApi()) { this.sources = sources this.api = api this.strings = new Set() this.translations = undefined } /** * Adds a .properties file to the list of sources to load translations from * * Files are loaded in the order they're added, and the first translation of each string that's encountered will be * used. * * @param {String} path */ addSource(path) { this.sources.push(path) } /** * Adds one or more strings to the list of strings to translate * * @param {(String[]|String)} strings */ addStrings(strings) { if (typeof strings === 'string' && strings.trim().length > 0) { this.strings.add(strings.trim()) } else { Array.from(strings) .filter(string => string && `${string}`.trim().length > 0) .forEach(string => this.strings.add(string)) } } /** * Load translations * * First, all properties files (specified with addSource) are loaded. * Then, if any untranslated strings remain, these are POSTed to the i18n endpoint of the DHIS2 API. * * @returns {Promise} */ load() { const i18n = this i18n.translations = {} function parseProperties(text) { return text.split('\n').reduce((props, line) => { const [key, value] = line.split('=').map(out => out.trim()) if ( key !== undefined && value !== undefined && !props.hasOwnProperty(key) ) { props[key] = value.replace( /\\u([0-9a-f]{4})/gi, (match, grp) => String.fromCharCode(parseInt(grp, 16)) ) } return props }, {}) } const propFiles = [] this.sources.forEach(source => { propFiles.push( i18n.api.request('GET', source).then( data => Promise.resolve(parseProperties(data)), // Resolve errors to an empty object, so that one missing file doesn't prevent // the rest from being loaded () => Promise.resolve({}) ) ) }) return Promise.all(propFiles).then(propFile => { propFile.forEach(props => { Object.keys(props).forEach(str => { if (!i18n.translations.hasOwnProperty(str)) { i18n.translations[str] = props[str] } this.strings.delete(str) }) }) if (this.strings.size > 0) { return i18n.api .post('i18n', Array.from(i18n.strings)) .then(res => { Object.keys(res) .filter(str => str !== res[str]) .forEach(str => { i18n.translations[str] = res[str] i18n.strings.delete(str) }) return Promise.resolve(i18n.translations) }) } return Promise.resolve(i18n.translations) }) } /** * Gets the translated version of the specified string * * If no translation exists for the specified string, the string is returned as is with two asterisks on each side, * in order to easily identify missing translations in the UI * * @param string * @returns {String} */ getTranslation(string, variables = {}) { if (this.translations === undefined) { throw new Error('Tried to translate before loading translations!') } const translatedString = this.translations.hasOwnProperty(string) ? this.translations[string] : `** ${string} **` if (Object.keys(variables).length) { return translatedString.replace( /\$\$(.+?)\$\$/gi, (match, partial) => variables[partial] || '' ) } return translatedString } /** * Check if a translation exists for the specified string * * @param string * @returns {boolean} True if a translation exists, false otherwise */ isTranslated(string) { if (this.translations === undefined) { throw new Error('Tried to translate before loading translations!') } return this.translations.hasOwnProperty(string) } /** * Get the list of strings that don't have translations * * If no translations have been loaded yet, `undefined` is returned in stead. * * @returns {Array|undefined} Array of untranslated strings, or undefined if translations haven't been loaded */ getUntranslatedStrings() { return this.translations ? Array.from(this.strings) : undefined } /** * Return a new instance of this class * * @returns {I18n} */ static getI18n() { return new I18n() } } export default I18n