_getRPLocalizedString(appid, tokens, language) {
		return new Promise(async (resolve, reject) => {
			if (!tokens.steam_display) {
				// Nothing to do here
				return reject();
			}

			let localizationTokens;
			try {
				localizationTokens = (await this.getAppRichPresenceLocalization(appid, language || this.options.language)).tokens;
				// Normalize all localization tokens to lowercase
				for (let i in localizationTokens) {
					localizationTokens[i.toLowerCase()] = localizationTokens[i];
				}
			} catch (ex) {
				// Oh well
				return reject(ex);
			}

			let rpTokens = JSON.parse(JSON.stringify(tokens)); // So we don't modify the original objects
			for (let i in rpTokens) {
				if (Object.hasOwnProperty.call(rpTokens, i) && localizationTokens[rpTokens[i].toLowerCase()]) {
					rpTokens[i] = localizationTokens[rpTokens[i].toLowerCase()];
				}
			}

			let rpString = rpTokens.steam_display;
			// eslint-disable-next-line
			while (true) {
				let newRpString = rpString;
				for (let i in rpTokens) {
					if (Object.hasOwnProperty.call(rpTokens, i)) {
						newRpString = newRpString.replace(new RegExp('%' + i + '%', 'gi'), rpTokens[i]);
					}
				}

				(newRpString.match(/{#[^}]+}/g) || []).forEach((token) => {
					token = token.substring(1, token.length - 1);
					if (localizationTokens[token.toLowerCase()]) {
						newRpString = newRpString.replace(new RegExp('{' + token + '}', 'gi'), localizationTokens[token.toLowerCase()]);
					}
				});

				if (newRpString == rpString) {
					break;
				} else {
					rpString = newRpString;
				}
			}
			return resolve(rpString);
		});
	}
@param {number} appid
@param {object} tokens
@param {string} [language]
@returns {Promise}
@protected