/**
 * A type encapsulating localization logic for strings
 * returned by localization server.
 *
 * Replacement for 'its' namespace from @onyx/localization package.
 */
export class AmpLocalization {
    constructor() {
        // region Properties
        /**
         * Localization JSON dictionary loaded from localization file.
         */
        this.locData = {};
        /**
         * The 2-letter code for current device language.
         */
        this.language = "en";
        // endregion
    }
    // endregion
    // region API
    /**
     * Updates the localization data for the device.
     * @param localizations A JSON dictionary representing the localized strings.
     * @param language Language code for current device language.
     */
    updateLocalizationData(localizations, language) {
        this.locData = localizations;
        this.language = language;
    }
    /**
     * Localizes a string replacing placehoders in key with values in the parameters dictionary
     * @param key The loc key to look up
     * @param params Parameters to replace in the loc string
     * @return The localized string
     */
    localize(key, params) {
        let value = this.locData[key];
        if (value === undefined || typeof value !== "string") {
            value = key;
        }
        if (params) {
            value = this.replaceTokens(value, params);
        }
        value = this.replaceMarkupTokens(value, params);
        return value;
    }
    /**
     * Localize with appropriate plural form based on the count.
     *
     * Some languages have more plural forms than others.
     * The full set of categories is "zero", "one", "two", "few", "many", and "other".
     *
     * The base loc key is used for "other" (the default).
     * Otherwise the category is appended to the base loc key with a "." separator.
     *
     * http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
     * http://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories
     * http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-numbers.html#Language_Plural_Rules
     *
     * English loc keys
     * key = "@@count@@ dogs";      // default, aka "plural"
     * key.zero = "no dogs";        // used when count is 0
     * key.one = "@@count@@ dog";   // used when count is 1, aka "singular"
     *
     * @param key     base loc key
     * @param count   number used to determine the plural form
     * @param params  (optional) substitution keys and values, "count" will be added if it's not already present
     * @return        localized string
     */
    localizeWithCount(objectGraph, key, count, params) {
        let keyToUse = null;
        let pluralFormKey;
        let category;
        if (count === 0) {
            // Special case zero for all languages so we can have strings like
            // "you have no messages" instead of "you have 0 messages".
            pluralFormKey = `${key}.zero`;
            if (this.isLocalized(objectGraph, pluralFormKey)) {
                keyToUse = pluralFormKey;
            }
        }
        if (keyToUse === null) {
            keyToUse = key;
            category = this.pluralCategory(objectGraph, count);
            if (category !== "other") {
                pluralFormKey = `${key}.${category}`;
                if (this.isLocalized(objectGraph, pluralFormKey)) {
                    keyToUse = pluralFormKey;
                }
            }
        }
        if (!params) {
            params = {};
        }
        // @@count@@ is the standard, but @@number@@ is frequently used too.
        if (params.count === undefined) {
            params.count = this.formatNumber(count.toString());
        }
        if (params.number === undefined) {
            params.number = this.formatNumber(count.toString());
        }
        return this.localize(keyToUse !== null && keyToUse !== void 0 ? keyToUse : key, params);
    }
    // endregion
    // region Private Methods
    replaceTokens(text, values) {
        Object.entries(values).forEach(([key, value]) => {
            const subKey = "@@" + key + "@@";
            text = this.replaceSubstring(text, subKey, value);
        });
        return text;
    }
    replaceMarkupTokens(text, values) {
        if (text.indexOf("##") <= -1) {
            return text;
        }
        // Resolve token properties.
        let markupParams;
        if (values) {
            // Create a copy to avoid mutating defaults.
            markupParams = { ...AmpLocalization.MARKUP_PARAMS };
            Object.entries(values).forEach(([key, value]) => {
                markupParams[key] = value;
            });
        }
        else {
            markupParams = AmpLocalization.MARKUP_PARAMS;
        }
        Object.entries(markupParams).forEach(([key, value]) => {
            const token = "##" + key + "##";
            text = text.replace(new RegExp(token, "gi"), value);
        });
        // Replace any remaining standard markup tags like <br> etc.
        text = text.replace(/##([^##]+)##/gi, "<$1>");
        return text;
    }
    /**
     * Searches "str" for "substr" and replaces each occurrence with "replacement"
     * @param str input string
     * @param substr the string to search for in the input string
     * @param replacement value to use for the replacement
     *
     * JavaScript String.replace has a misfeature where "$" in the replacement is always interpreted as a meta char,
     * i.e. you have to use "$$" in the replacement to get a single "$" in the resulting string
     * which lead to <rdar://problem/16848063> tv episode title containing "$&" not displaying correctly
     * Using split and join is faster than substituting $$ for $ in the replacement.
     */
    replaceSubstring(str, substr, replacement) {
        return str.split(substr).join(replacement);
    }
    formatNumber(value) {
        let decimalSeparator = this.locData["_decimalSeparator"];
        if (decimalSeparator === undefined || typeof decimalSeparator !== "string") {
            decimalSeparator = ".";
        }
        let thousandsSeparator = this.locData["_thousandsSeparator"];
        if (thousandsSeparator === undefined || typeof thousandsSeparator !== "string") {
            thousandsSeparator = ".";
        }
        const parts = parseFloat(value).toString().split(".");
        const chars = parts[0].split("");
        for (let i = chars.length - 3; i > 0; i -= 3) {
            chars.splice(i, 0, thousandsSeparator);
        }
        parts[0] = chars.join("");
        return parts.join(decimalSeparator);
    }
    /**
     * Check whether the given key is localized or not.
     * @param key The key to check.
     */
    isLocalized(objectGraph, key) {
        const value = this.locData[key];
        if (value === undefined || typeof value !== "string") {
            return false;
        }
        else if (key.indexOf(".") === -1) {
            // Simple localization keys such as "OK".
            return true;
        }
        else if (value === key || (value.indexOf("**") === 0 && value.lastIndexOf("**") === value.length - 2)) {
            objectGraph.console.error("Unlocalized key in keys dictionary", key);
            return false;
        }
        return true;
    }
    /**
     * Returns localization plural category for given number.
     * @param num The number to return plural category for.
     * @returns Plural category for specified number or "other"
     * if there's no plural category function available for current language.
     *
     * @see:
     * - http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
     * - http://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories
     * - http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-numbers.html#Language_Plural_Rules
     */
    pluralCategory(objectGraph, num) {
        const categoryFn = AmpLocalization.pluralCategoryFnByLanguage[this.language];
        if (categoryFn !== undefined) {
            return categoryFn(num);
        }
        else {
            objectGraph.console.warn("Missing plural category function for: " + this.language);
            return "other";
        }
    }
}
/**
 * Markup parameters used for replacing markup tokens in text.
 */
AmpLocalization.MARKUP_PARAMS = { nbsp: "&nbsp;", gt: "&gt;", lt: "&lt;", copy: "\u00a9" };
// endregion
// regions Plurals
AmpLocalization.pluralCategoryDefault = function (num) {
    return "other";
};
AmpLocalization.pluralCategoryOne = function (num) {
    if (num === 1) {
        return "one";
    }
    return "other";
};
AmpLocalization.pluralCategoryArabic = function (num) {
    const n = num >> 0;
    if (n !== num) {
        // non integer
        return "other";
    }
    if (n === 0) {
        return "zero";
    }
    if (n === 1) {
        return "one";
    }
    if (n === 2) {
        return "two";
    }
    const m100 = n % 100;
    if (m100 >= 11) {
        // n mod 100 in 11..99
        return "many";
    }
    if (m100 >= 3) {
        // n mod 100 in 3..10
        return "few";
    }
    return "other";
};
AmpLocalization.pluralCategoryFrench = function (num) {
    // Do not check for integer, French includes fractional values!
    if (num < 2 && num >= 0) {
        return "one";
    }
    return "other";
};
AmpLocalization.pluralCategoryHebrew = function (num) {
    const n = num >> 0;
    if (n !== num) {
        // non integer
        return "other";
    }
    if (n === 1) {
        return "one";
    }
    if (n === 2) {
        return "two";
    }
    const m10 = n % 10;
    if (m10 === 0 && n > 10) {
        // n mod 10 is 0 and n != 0..10
        return "many";
    }
    return "other";
};
AmpLocalization.pluralCategoryPolish = function (num) {
    const n = num >> 0;
    if (n !== num) {
        // non integer
        return "other";
    }
    if (n === 1) {
        return "one";
    }
    const m10 = n % 10;
    if (m10 <= 4 && m10 >= 2) {
        const m100 = n % 100;
        if (m100 > 14 || m100 < 12) {
            // n mod 10 in 2..4 and n mod 100 not in 12..14
            return "few";
        }
    }
    return "many";
};
AmpLocalization.pluralCategoryRomanian = function (num) {
    const n = num >> 0;
    if (n !== num) {
        // non integer
        return "few";
    }
    if (n === 0) {
        return "few";
    }
    if (n === 1) {
        return "one";
    }
    const m100 = num % 100;
    if (m100 <= 19 && m100 >= 1) {
        // n mod 100 in 1..19
        return "few";
    }
    return "other";
};
AmpLocalization.pluralCategoryRussian = function (num) {
    const n = num >> 0;
    if (n !== num) {
        // non integer
        return "other";
    }
    const m10 = n % 10;
    if (m10 >= 5 || m10 === 0) {
        // n mod 10 is 0 or n mod 10 in 5..9
        return "many";
    }
    const m100 = n % 100;
    if (m100 <= 14 && m100 >= 11) {
        // n mod 100 in 11..14
        return "many";
    }
    if (m10 === 1) {
        // n mod 10 is 1 and n mod 100 is not 11
        return "one";
    }
    // n mod 10 in 2..4 and n mod 100 not in 12..14
    return "few";
};
/**
 * Mapping language code to plural category function.
 */
AmpLocalization.pluralCategoryFnByLanguage = {
    zh: AmpLocalization.pluralCategoryDefault,
    id: AmpLocalization.pluralCategoryDefault,
    ja: AmpLocalization.pluralCategoryDefault,
    ko: AmpLocalization.pluralCategoryDefault,
    ms: AmpLocalization.pluralCategoryDefault,
    th: AmpLocalization.pluralCategoryDefault,
    vi: AmpLocalization.pluralCategoryDefault,
    en: AmpLocalization.pluralCategoryOne,
    ca: AmpLocalization.pluralCategoryOne,
    da: AmpLocalization.pluralCategoryOne,
    nl: AmpLocalization.pluralCategoryOne,
    de: AmpLocalization.pluralCategoryOne,
    el: AmpLocalization.pluralCategoryOne,
    fi: AmpLocalization.pluralCategoryOne,
    hu: AmpLocalization.pluralCategoryOne,
    it: AmpLocalization.pluralCategoryOne,
    nb: AmpLocalization.pluralCategoryOne,
    no: AmpLocalization.pluralCategoryOne,
    pt: AmpLocalization.pluralCategoryOne,
    es: AmpLocalization.pluralCategoryOne,
    sv: AmpLocalization.pluralCategoryOne,
    tr: AmpLocalization.pluralCategoryOne,
    ar: AmpLocalization.pluralCategoryArabic,
    fr: AmpLocalization.pluralCategoryFrench,
    iw: AmpLocalization.pluralCategoryHebrew,
    pl: AmpLocalization.pluralCategoryPolish,
    ro: AmpLocalization.pluralCategoryRomanian,
    ru: AmpLocalization.pluralCategoryRussian, // Russian
};
//# sourceMappingURL=amp-localization.js.map