(function (root) {

'use strict';

/**
 * charMap
 * @type {Object}
 */
var charMap = {

    // latin
    'À': 'A',
    'Á': 'A',
    'Â': 'A',
    'Ã': 'A',
    'Ä': 'Ae',
    'Å': 'A',
    'Æ': 'AE',
    'Ç': 'C',
    'È': 'E',
    'É': 'E',
    'Ê': 'E',
    'Ë': 'E',
    'Ì': 'I',
    'Í': 'I',
    'Î': 'I',
    'Ï': 'I',
    'Ð': 'D',
    'Ñ': 'N',
    'Ò': 'O',
    'Ó': 'O',
    'Ô': 'O',
    'Õ': 'O',
    'Ö': 'Oe',
    'Ő': 'O',
    'Ø': 'O',
    'Ù': 'U',
    'Ú': 'U',
    'Û': 'U',
    'Ü': 'Ue',
    'Ű': 'U',
    'Ý': 'Y',
    'Þ': 'TH',
    'ß': 'ss',
    'à': 'a',
    'á': 'a',
    'â': 'a',
    'ã': 'a',
    'ä': 'ae',
    'å': 'a',
    'æ': 'ae',
    'ç': 'c',
    'è': 'e',
    'é': 'e',
    'ê': 'e',
    'ë': 'e',
    'ì': 'i',
    'í': 'i',
    'î': 'i',
    'ï': 'i',
    'ð': 'd',
    'ñ': 'n',
    'ò': 'o',
    'ó': 'o',
    'ô': 'o',
    'õ': 'o',
    'ö': 'oe',
    'ő': 'o',
    'ø': 'o',
    'ù': 'u',
    'ú': 'u',
    'û': 'u',
    'ü': 'ue',
    'ű': 'u',
    'ý': 'y',
    'þ': 'th',
    'ÿ': 'y',
    'ẞ': 'SS',

    // language specific

    // Arabic
    'ا': 'a',
    'أ': 'a',
    'إ': 'i',
    'آ': 'aa',
    'ؤ': 'u',
    'ئ': 'e',
    'ء': 'a',
    'ب': 'b',
    'ت': 't',
    'ث': 'th',
    'ج': 'j',
    'ح': 'h',
    'خ': 'kh',
    'د': 'd',
    'ذ': 'th',
    'ر': 'r',
    'ز': 'z',
    'س': 's',
    'ش': 'sh',
    'ص': 's',
    'ض': 'dh',
    'ط': 't',
    'ظ': 'z',
    'ع': 'a',
    'غ': 'gh',
    'ف': 'f',
    'ق': 'q',
    'ك': 'k',
    'ل': 'l',
    'م': 'm',
    'ن': 'n',
    'ه': 'h',
    'و': 'w',
    'ي': 'y',
    'ى': 'a',
    'ة': 'h',
    'ﻻ': 'la',
    'ﻷ': 'laa',
    'ﻹ': 'lai',
    'ﻵ': 'laa',

    // Persian additional characters than Arabic
    'گ': 'g',
    'چ': 'ch',
    'پ': 'p',
    'ژ': 'zh',
    'ک': 'k',
    'ی': 'y',

    // Arabic diactrics
    'َ': 'a',
    'ً': 'an',
    'ِ': 'e',
    'ٍ': 'en',
    'ُ': 'u',
    'ٌ': 'on',
    'ْ': '',

    // Arabic numbers
    '٠': '0',
    '١': '1',
    '٢': '2',
    '٣': '3',
    '٤': '4',
    '٥': '5',
    '٦': '6',
    '٧': '7',
    '٨': '8',
    '٩': '9',

    // Persian numbers
    '۰': '0',
    '۱': '1',
    '۲': '2',
    '۳': '3',
    '۴': '4',
    '۵': '5',
    '۶': '6',
    '۷': '7',
    '۸': '8',
    '۹': '9',

    // Burmese consonants
    'က': 'k',
    'ခ': 'kh',
    'ဂ': 'g',
    'ဃ': 'ga',
    'င': 'ng',
    'စ': 's',
    'ဆ': 'sa',
    'ဇ': 'z',
    'စျ': 'za',
    'ည': 'ny',
    'ဋ': 't',
    'ဌ': 'ta',
    'ဍ': 'd',
    'ဎ': 'da',
    'ဏ': 'na',
    'တ': 't',
    'ထ': 'ta',
    'ဒ': 'd',
    'ဓ': 'da',
    'န': 'n',
    'ပ': 'p',
    'ဖ': 'pa',
    'ဗ': 'b',
    'ဘ': 'ba',
    'မ': 'm',
    'ယ': 'y',
    'ရ': 'ya',
    'လ': 'l',
    'ဝ': 'w',
    'သ': 'th',
    'ဟ': 'h',
    'ဠ': 'la',
    'အ': 'a',
    // consonant character combos
    'ြ': 'y',
    'ျ': 'ya',
    'ွ': 'w',
    'ြွ': 'yw',
    'ျွ': 'ywa',
    'ှ': 'h',
    // independent vowels
    'ဧ': 'e',
    '၏': '-e',
    'ဣ': 'i',
    'ဤ': '-i',
    'ဉ': 'u',
    'ဦ': '-u',
    'ဩ': 'aw',
    'သြော': 'aw',
    'ဪ': 'aw',
    // numbers
    '၀': '0',
    '၁': '1',
    '၂': '2',
    '၃': '3',
    '၄': '4',
    '၅': '5',
    '၆': '6',
    '၇': '7',
    '၈': '8',
    '၉': '9',
    // virama and tone marks which are silent in transliteration
    '္': '',
    '့': '',
    'း': '',

    // Czech
    'č': 'c',
    'ď': 'd',
    'ě': 'e',
    'ň': 'n',
    'ř': 'r',
    'š': 's',
    'ť': 't',
    'ů': 'u',
    'ž': 'z',
    'Č': 'C',
    'Ď': 'D',
    'Ě': 'E',
    'Ň': 'N',
    'Ř': 'R',
    'Š': 'S',
    'Ť': 'T',
    'Ů': 'U',
    'Ž': 'Z',

    // Dhivehi
    'ހ': 'h',
    'ށ': 'sh',
    'ނ': 'n',
    'ރ': 'r',
    'ބ': 'b',
    'ޅ': 'lh',
    'ކ': 'k',
    'އ': 'a',
    'ވ': 'v',
    'މ': 'm',
    'ފ': 'f',
    'ދ': 'dh',
    'ތ': 'th',
    'ލ': 'l',
    'ގ': 'g',
    'ޏ': 'gn',
    'ސ': 's',
    'ޑ': 'd',
    'ޒ': 'z',
    'ޓ': 't',
    'ޔ': 'y',
    'ޕ': 'p',
    'ޖ': 'j',
    'ޗ': 'ch',
    'ޘ': 'tt',
    'ޙ': 'hh',
    'ޚ': 'kh',
    'ޛ': 'th',
    'ޜ': 'z',
    'ޝ': 'sh',
    'ޞ': 's',
    'ޟ': 'd',
    'ޠ': 't',
    'ޡ': 'z',
    'ޢ': 'a',
    'ޣ': 'gh',
    'ޤ': 'q',
    'ޥ': 'w',
    'ަ': 'a',
    'ާ': 'aa',
    'ި': 'i',
    'ީ': 'ee',
    'ު': 'u',
    'ޫ': 'oo',
    'ެ': 'e',
    'ޭ': 'ey',
    'ޮ': 'o',
    'ޯ': 'oa',
    'ް': '',

    // Georgian https://en.wikipedia.org/wiki/Romanization_of_Georgian
    // National system (2002)
    'ა': 'a',
    'ბ': 'b',
    'გ': 'g',
    'დ': 'd',
    'ე': 'e',
    'ვ': 'v',
    'ზ': 'z',
    'თ': 't',
    'ი': 'i',
    'კ': 'k',
    'ლ': 'l',
    'მ': 'm',
    'ნ': 'n',
    'ო': 'o',
    'პ': 'p',
    'ჟ': 'zh',
    'რ': 'r',
    'ს': 's',
    'ტ': 't',
    'უ': 'u',
    'ფ': 'p',
    'ქ': 'k',
    'ღ': 'gh',
    'ყ': 'q',
    'შ': 'sh',
    'ჩ': 'ch',
    'ც': 'ts',
    'ძ': 'dz',
    'წ': 'ts',
    'ჭ': 'ch',
    'ხ': 'kh',
    'ჯ': 'j',
    'ჰ': 'h',

    // Greek
    'α': 'a',
    'β': 'v',
    'γ': 'g',
    'δ': 'd',
    'ε': 'e',
    'ζ': 'z',
    'η': 'i',
    'θ': 'th',
    'ι': 'i',
    'κ': 'k',
    'λ': 'l',
    'μ': 'm',
    'ν': 'n',
    'ξ': 'ks',
    'ο': 'o',
    'π': 'p',
    'ρ': 'r',
    'σ': 's',
    'τ': 't',
    'υ': 'y',
    'φ': 'f',
    'χ': 'x',
    'ψ': 'ps',
    'ω': 'o',
    'ά': 'a',
    'έ': 'e',
    'ί': 'i',
    'ό': 'o',
    'ύ': 'y',
    'ή': 'i',
    'ώ': 'o',
    'ς': 's',
    'ϊ': 'i',
    'ΰ': 'y',
    'ϋ': 'y',
    'ΐ': 'i',
    'Α': 'A',
    'Β': 'B',
    'Γ': 'G',
    'Δ': 'D',
    'Ε': 'E',
    'Ζ': 'Z',
    'Η': 'I',
    'Θ': 'TH',
    'Ι': 'I',
    'Κ': 'K',
    'Λ': 'L',
    'Μ': 'M',
    'Ν': 'N',
    'Ξ': 'KS',
    'Ο': 'O',
    'Π': 'P',
    'Ρ': 'R',
    'Σ': 'S',
    'Τ': 'T',
    'Υ': 'Y',
    'Φ': 'F',
    'Χ': 'X',
    'Ψ': 'PS',
    'Ω': 'O',
    'Ά': 'A',
    'Έ': 'E',
    'Ί': 'I',
    'Ό': 'O',
    'Ύ': 'Y',
    'Ή': 'I',
    'Ώ': 'O',
    'Ϊ': 'I',
    'Ϋ': 'Y',

    // Latvian
    'ā': 'a',
    // 'č': 'c', // duplicate
    'ē': 'e',
    'ģ': 'g',
    'ī': 'i',
    'ķ': 'k',
    'ļ': 'l',
    'ņ': 'n',
    // 'š': 's', // duplicate
    'ū': 'u',
    // 'ž': 'z', // duplicate
    'Ā': 'A',
    // 'Č': 'C', // duplicate
    'Ē': 'E',
    'Ģ': 'G',
    'Ī': 'I',
    'Ķ': 'k',
    'Ļ': 'L',
    'Ņ': 'N',
    // 'Š': 'S', // duplicate
    'Ū': 'U',
    // 'Ž': 'Z', // duplicate

    // Macedonian
    'Ќ': 'Kj',
    'ќ': 'kj',
    'Љ': 'Lj',
    'љ': 'lj',
    'Њ': 'Nj',
    'њ': 'nj',
    'Тс': 'Ts',
    'тс': 'ts',

    // Polish
    'ą': 'a',
    'ć': 'c',
    'ę': 'e',
    'ł': 'l',
    'ń': 'n',
    // 'ó': 'o', // duplicate
    'ś': 's',
    'ź': 'z',
    'ż': 'z',
    'Ą': 'A',
    'Ć': 'C',
    'Ę': 'E',
    'Ł': 'L',
    'Ń': 'N',
    'Ś': 'S',
    'Ź': 'Z',
    'Ż': 'Z',

    // Ukranian
    'Є': 'Ye',
    'І': 'I',
    'Ї': 'Yi',
    'Ґ': 'G',
    'є': 'ye',
    'і': 'i',
    'ї': 'yi',
    'ґ': 'g',

    // Romanian
    'ă': 'a',
    'Ă': 'A',
    'ș': 's',
    'Ș': 'S',
    // 'ş': 's', // duplicate
    // 'Ş': 'S', // duplicate
    'ț': 't',
    'Ț': 'T',
    'ţ': 't',
    'Ţ': 'T',

    // Russian https://en.wikipedia.org/wiki/Romanization_of_Russian
    // ICAO

    'а': 'a',
    'б': 'b',
    'в': 'v',
    'г': 'g',
    'д': 'd',
    'е': 'e',
    'ё': 'yo',
    'ж': 'zh',
    'з': 'z',
    'и': 'i',
    'й': 'i',
    'к': 'k',
    'л': 'l',
    'м': 'm',
    'н': 'n',
    'о': 'o',
    'п': 'p',
    'р': 'r',
    'с': 's',
    'т': 't',
    'у': 'u',
    'ф': 'f',
    'х': 'kh',
    'ц': 'c',
    'ч': 'ch',
    'ш': 'sh',
    'щ': 'sh',
    'ъ': '',
    'ы': 'y',
    'ь': '',
    'э': 'e',
    'ю': 'yu',
    'я': 'ya',
    'А': 'A',
    'Б': 'B',
    'В': 'V',
    'Г': 'G',
    'Д': 'D',
    'Е': 'E',
    'Ё': 'Yo',
    'Ж': 'Zh',
    'З': 'Z',
    'И': 'I',
    'Й': 'I',
    'К': 'K',
    'Л': 'L',
    'М': 'M',
    'Н': 'N',
    'О': 'O',
    'П': 'P',
    'Р': 'R',
    'С': 'S',
    'Т': 'T',
    'У': 'U',
    'Ф': 'F',
    'Х': 'Kh',
    'Ц': 'C',
    'Ч': 'Ch',
    'Ш': 'Sh',
    'Щ': 'Sh',
    'Ъ': '',
    'Ы': 'Y',
    'Ь': '',
    'Э': 'E',
    'Ю': 'Yu',
    'Я': 'Ya',

    // Serbian
    'ђ': 'dj',
    'ј': 'j',
    // 'љ': 'lj',  // duplicate
    // 'њ': 'nj', // duplicate
    'ћ': 'c',
    'џ': 'dz',
    'Ђ': 'Dj',
    'Ј': 'j',
    // 'Љ': 'Lj', // duplicate
    // 'Њ': 'Nj', // duplicate
    'Ћ': 'C',
    'Џ': 'Dz',

    // Slovak
    'ľ': 'l',
    'ĺ': 'l',
    'ŕ': 'r',
    'Ľ': 'L',
    'Ĺ': 'L',
    'Ŕ': 'R',

    // Turkish
    'ş': 's',
    'Ş': 'S',
    'ı': 'i',
    'İ': 'I',
    // 'ç': 'c', // duplicate
    // 'Ç': 'C', // duplicate
    // 'ü': 'u', // duplicate, see langCharMap
    // 'Ü': 'U', // duplicate, see langCharMap
    // 'ö': 'o', // duplicate, see langCharMap
    // 'Ö': 'O', // duplicate, see langCharMap
    'ğ': 'g',
    'Ğ': 'G',

    // Vietnamese
    'ả': 'a',
    'Ả': 'A',
    'ẳ': 'a',
    'Ẳ': 'A',
    'ẩ': 'a',
    'Ẩ': 'A',
    'đ': 'd',
    'Đ': 'D',
    'ẹ': 'e',
    'Ẹ': 'E',
    'ẽ': 'e',
    'Ẽ': 'E',
    'ẻ': 'e',
    'Ẻ': 'E',
    'ế': 'e',
    'Ế': 'E',
    'ề': 'e',
    'Ề': 'E',
    'ệ': 'e',
    'Ệ': 'E',
    'ễ': 'e',
    'Ễ': 'E',
    'ể': 'e',
    'Ể': 'E',
    'ỏ': 'o',
    'ọ': 'o',
    'Ọ': 'o',
    'ố': 'o',
    'Ố': 'O',
    'ồ': 'o',
    'Ồ': 'O',
    'ổ': 'o',
    'Ổ': 'O',
    'ộ': 'o',
    'Ộ': 'O',
    'ỗ': 'o',
    'Ỗ': 'O',
    'ơ': 'o',
    'Ơ': 'O',
    'ớ': 'o',
    'Ớ': 'O',
    'ờ': 'o',
    'Ờ': 'O',
    'ợ': 'o',
    'Ợ': 'O',
    'ỡ': 'o',
    'Ỡ': 'O',
    'Ở': 'o',
    'ở': 'o',
    'ị': 'i',
    'Ị': 'I',
    'ĩ': 'i',
    'Ĩ': 'I',
    'ỉ': 'i',
    'Ỉ': 'i',
    'ủ': 'u',
    'Ủ': 'U',
    'ụ': 'u',
    'Ụ': 'U',
    'ũ': 'u',
    'Ũ': 'U',
    'ư': 'u',
    'Ư': 'U',
    'ứ': 'u',
    'Ứ': 'U',
    'ừ': 'u',
    'Ừ': 'U',
    'ự': 'u',
    'Ự': 'U',
    'ữ': 'u',
    'Ữ': 'U',
    'ử': 'u',
    'Ử': 'ư',
    'ỷ': 'y',
    'Ỷ': 'y',
    'ỳ': 'y',
    'Ỳ': 'Y',
    'ỵ': 'y',
    'Ỵ': 'Y',
    'ỹ': 'y',
    'Ỹ': 'Y',
    'ạ': 'a',
    'Ạ': 'A',
    'ấ': 'a',
    'Ấ': 'A',
    'ầ': 'a',
    'Ầ': 'A',
    'ậ': 'a',
    'Ậ': 'A',
    'ẫ': 'a',
    'Ẫ': 'A',
    // 'ă': 'a', // duplicate
    // 'Ă': 'A', // duplicate
    'ắ': 'a',
    'Ắ': 'A',
    'ằ': 'a',
    'Ằ': 'A',
    'ặ': 'a',
    'Ặ': 'A',
    'ẵ': 'a',
    'Ẵ': 'A',
    "⓪": "0",
    "①": "1",
    "②": "2",
    "③": "3",
    "④": "4",
    "⑤": "5",
    "⑥": "6",
    "⑦": "7",
    "⑧": "8",
    "⑨": "9",
    "⑩": "10",
    "⑪": "11",
    "⑫": "12",
    "⑬": "13",
    "⑭": "14",
    "⑮": "15",
    "⑯": "16",
    "⑰": "17",
    "⑱": "18",
    "⑲": "18",
    "⑳": "18",

    "⓵": "1",
    "⓶": "2",
    "⓷": "3",
    "⓸": "4",
    "⓹": "5",
    "⓺": "6",
    "⓻": "7",
    "⓼": "8",
    "⓽": "9",
    "⓾": "10",

    "⓿": "0",
    "⓫": "11",
    "⓬": "12",
    "⓭": "13",
    "⓮": "14",
    "⓯": "15",
    "⓰": "16",
    "⓱": "17",
    "⓲": "18",
    "⓳": "19",
    "⓴": "20",

    "Ⓐ": "A",
    "Ⓑ": "B",
    "Ⓒ": "C",
    "Ⓓ": "D",
    "Ⓔ": "E",
    "Ⓕ": "F",
    "Ⓖ": "G",
    "Ⓗ": "H",
    "Ⓘ": "I",
    "Ⓙ": "J",
    "Ⓚ": "K",
    "Ⓛ": "L",
    "Ⓜ": "M",
    "Ⓝ": "N",
    "Ⓞ": "O",
    "Ⓟ": "P",
    "Ⓠ": "Q",
    "Ⓡ": "R",
    "Ⓢ": "S",
    "Ⓣ": "T",
    "Ⓤ": "U",
    "Ⓥ": "V",
    "Ⓦ": "W",
    "Ⓧ": "X",
    "Ⓨ": "Y",
    "Ⓩ": "Z",

    "ⓐ": "a",
    "ⓑ": "b",
    "ⓒ": "c",
    "ⓓ": "d",
    "ⓔ": "e",
    "ⓕ": "f",
    "ⓖ": "g",
    "ⓗ": "h",
    "ⓘ": "i",
    "ⓙ": "j",
    "ⓚ": "k",
    "ⓛ": "l",
    "ⓜ": "m",
    "ⓝ": "n",
    "ⓞ": "o",
    "ⓟ": "p",
    "ⓠ": "q",
    "ⓡ": "r",
    "ⓢ": "s",
    "ⓣ": "t",
    "ⓤ": "u",
    "ⓦ": "v",
    "ⓥ": "w",
    "ⓧ": "x",
    "ⓨ": "y",
    "ⓩ": "z",

    // symbols
    '“': '"',
    '”': '"',
    '‘': "'",
    '’': "'",
    '∂': 'd',
    'ƒ': 'f',
    '™': '(TM)',
    '©': '(C)',
    'œ': 'oe',
    'Œ': 'OE',
    '®': '(R)',
    '†': '+',
    '℠': '(SM)',
    '…': '...',
    '˚': 'o',
    'º': 'o',
    'ª': 'a',
    '•': '*',
    '၊': ',',
    '။': '.',

    // currency
    '$': 'USD',
    '€': 'EUR',
    '₢': 'BRN',
    '₣': 'FRF',
    '£': 'GBP',
    '₤': 'ITL',
    '₦': 'NGN',
    '₧': 'ESP',
    '₩': 'KRW',
    '₪': 'ILS',
    '₫': 'VND',
    '₭': 'LAK',
    '₮': 'MNT',
    '₯': 'GRD',
    '₱': 'ARS',
    '₲': 'PYG',
    '₳': 'ARA',
    '₴': 'UAH',
    '₵': 'GHS',
    '¢': 'cent',
    '¥': 'CNY',
    '元': 'CNY',
    '円': 'YEN',
    '﷼': 'IRR',
    '₠': 'EWE',
    '฿': 'THB',
    '₨': 'INR',
    '₹': 'INR',
    '₰': 'PF',
    '₺': 'TRY',
    '؋': 'AFN',
    '₼': 'AZN',
    'лв': 'BGN',
    '៛': 'KHR',
    '₡': 'CRC',
    '₸': 'KZT',
    'ден': 'MKD',
    'zł': 'PLN',
    '₽': 'RUB',
    '₾': 'GEL'

};

/**
 * special look ahead character array
 * These characters form with consonants to become 'single'/consonant combo
 * @type [Array]
 */
var lookAheadCharArray = [
    // burmese
    '်',

    // Dhivehi
    'ް'
];

/**
 * diatricMap for languages where transliteration changes entirely as more diatrics are added
 * @type {Object}
 */
var diatricMap = {
    // Burmese
    // dependent vowels
    'ာ': 'a',
    'ါ': 'a',
    'ေ': 'e',
    'ဲ': 'e',
    'ိ': 'i',
    'ီ': 'i',
    'ို': 'o',
    'ု': 'u',
    'ူ': 'u',
    'ေါင်': 'aung',
    'ော': 'aw',
    'ော်': 'aw',
    'ေါ': 'aw',
    'ေါ်': 'aw',
    '်': '်', // this is special case but the character will be converted to latin in the code
    'က်': 'et',
    'ိုက်': 'aik',
    'ောက်': 'auk',
    'င်': 'in',
    'ိုင်': 'aing',
    'ောင်': 'aung',
    'စ်': 'it',
    'ည်': 'i',
    'တ်': 'at',
    'ိတ်': 'eik',
    'ုတ်': 'ok',
    'ွတ်': 'ut',
    'ေတ်': 'it',
    'ဒ်': 'd',
    'ိုဒ်': 'ok',
    'ုဒ်': 'ait',
    'န်': 'an',
    'ာန်': 'an',
    'ိန်': 'ein',
    'ုန်': 'on',
    'ွန်': 'un',
    'ပ်': 'at',
    'ိပ်': 'eik',
    'ုပ်': 'ok',
    'ွပ်': 'ut',
    'န်ုပ်': 'nub',
    'မ်': 'an',
    'ိမ်': 'ein',
    'ုမ်': 'on',
    'ွမ်': 'un',
    'ယ်': 'e',
    'ိုလ်': 'ol',
    'ဉ်': 'in',
    'ံ': 'an',
    'ိံ': 'ein',
    'ုံ': 'on',

    // Dhivehi
    'ައް': 'ah',
    'ަށް': 'ah'
};

/**
 * langCharMap language specific characters translations
 * @type   {Object}
 */
var langCharMap = {
    'en': {}, // default language

    'az': { // Azerbaijani
        'ç': 'c',
        'ə': 'e',
        'ğ': 'g',
        'ı': 'i',
        'ö': 'o',
        'ş': 's',
        'ü': 'u',
        'Ç': 'C',
        'Ə': 'E',
        'Ğ': 'G',
        'İ': 'I',
        'Ö': 'O',
        'Ş': 'S',
        'Ü': 'U'
    },

    'cs': { // Czech
        'č': 'c',
        'ď': 'd',
        'ě': 'e',
        'ň': 'n',
        'ř': 'r',
        'š': 's',
        'ť': 't',
        'ů': 'u',
        'ž': 'z',
        'Č': 'C',
        'Ď': 'D',
        'Ě': 'E',
        'Ň': 'N',
        'Ř': 'R',
        'Š': 'S',
        'Ť': 'T',
        'Ů': 'U',
        'Ž': 'Z'
    },

    'fi': { // Finnish
        // 'å': 'a', duplicate see charMap/latin
        // 'Å': 'A', duplicate see charMap/latin
        'ä': 'a', // ok
        'Ä': 'A', // ok
        'ö': 'o', // ok
        'Ö': 'O' // ok
    },

    'hu': { // Hungarian
        'ä': 'a', // ok
        'Ä': 'A', // ok
        // 'á': 'a', duplicate see charMap/latin
        // 'Á': 'A', duplicate see charMap/latin
        'ö': 'o', // ok
        'Ö': 'O', // ok
        // 'ő': 'o', duplicate see charMap/latin
        // 'Ő': 'O', duplicate see charMap/latin
        'ü': 'u',
        'Ü': 'U',
        'ű': 'u',
        'Ű': 'U'
    },

    'lt': { // Lithuanian
        'ą': 'a',
        'č': 'c',
        'ę': 'e',
        'ė': 'e',
        'į': 'i',
        'š': 's',
        'ų': 'u',
        'ū': 'u',
        'ž': 'z',
        'Ą': 'A',
        'Č': 'C',
        'Ę': 'E',
        'Ė': 'E',
        'Į': 'I',
        'Š': 'S',
        'Ų': 'U',
        'Ū': 'U'
    },

    'lv': { // Latvian
        'ā': 'a',
        'č': 'c',
        'ē': 'e',
        'ģ': 'g',
        'ī': 'i',
        'ķ': 'k',
        'ļ': 'l',
        'ņ': 'n',
        'š': 's',
        'ū': 'u',
        'ž': 'z',
        'Ā': 'A',
        'Č': 'C',
        'Ē': 'E',
        'Ģ': 'G',
        'Ī': 'i',
        'Ķ': 'k',
        'Ļ': 'L',
        'Ņ': 'N',
        'Š': 'S',
        'Ū': 'u',
        'Ž': 'Z'
    },

    'pl': { // Polish
        'ą': 'a',
        'ć': 'c',
        'ę': 'e',
        'ł': 'l',
        'ń': 'n',
        'ó': 'o',
        'ś': 's',
        'ź': 'z',
        'ż': 'z',
        'Ą': 'A',
        'Ć': 'C',
        'Ę': 'e',
        'Ł': 'L',
        'Ń': 'N',
        'Ó': 'O',
        'Ś': 'S',
        'Ź': 'Z',
        'Ż': 'Z'
    },

    'sv': { // Swedish
        // 'å': 'a', duplicate see charMap/latin
        // 'Å': 'A', duplicate see charMap/latin
        'ä': 'a', // ok
        'Ä': 'A', // ok
        'ö': 'o', // ok
        'Ö': 'O' // ok
    },

    'sk': { // Slovak
        'ä': 'a',
        'Ä': 'A'
    },

    'sr': { // Serbian
        'љ': 'lj',
        'њ': 'nj',
        'Љ': 'Lj',
        'Њ': 'Nj',
        'đ': 'dj',
        'Đ': 'Dj'
    },

    'tr': { // Turkish
        'Ü': 'U',
        'Ö': 'O',
        'ü': 'u',
        'ö': 'o'
    }
};

/**
 * symbolMap language specific symbol translations
 * translations must be transliterated already
 * @type   {Object}
 */
var symbolMap = {
    'ar': {
        '∆': 'delta',
        '∞': 'la-nihaya',
        '♥': 'hob',
        '&': 'wa',
        '|': 'aw',
        '<': 'aqal-men',
        '>': 'akbar-men',
        '∑': 'majmou',
        '¤': 'omla'
    },

    'az': {},

    'ca': {
        '∆': 'delta',
        '∞': 'infinit',
        '♥': 'amor',
        '&': 'i',
        '|': 'o',
        '<': 'menys que',
        '>': 'mes que',
        '∑': 'suma dels',
        '¤': 'moneda'
    },

    'cs': {
        '∆': 'delta',
        '∞': 'nekonecno',
        '♥': 'laska',
        '&': 'a',
        '|': 'nebo',
        '<': 'mensi nez',
        '>': 'vetsi nez',
        '∑': 'soucet',
        '¤': 'mena'
    },

    'de': {
        '∆': 'delta',
        '∞': 'unendlich',
        '♥': 'Liebe',
        '&': 'und',
        '|': 'oder',
        '<': 'kleiner als',
        '>': 'groesser als',
        '∑': 'Summe von',
        '¤': 'Waehrung'
    },

    'dv': {
        '∆': 'delta',
        '∞': 'kolunulaa',
        '♥': 'loabi',
        '&': 'aai',
        '|': 'noonee',
        '<': 'ah vure kuda',
        '>': 'ah vure bodu',
        '∑': 'jumula',
        '¤': 'faisaa'
    },

    'en': {
        '∆': 'delta',
        '∞': 'infinity',
        '♥': 'love',
        '&': 'and',
        '|': 'or',
        '<': 'less than',
        '>': 'greater than',
        '∑': 'sum',
        '¤': 'currency'
    },

    'es': {
        '∆': 'delta',
        '∞': 'infinito',
        '♥': 'amor',
        '&': 'y',
        '|': 'u',
        '<': 'menos que',
        '>': 'mas que',
        '∑': 'suma de los',
        '¤': 'moneda'
    },

    'fa': {
        '∆': 'delta',
        '∞': 'bi-nahayat',
        '♥': 'eshgh',
        '&': 'va',
        '|': 'ya',
        '<': 'kamtar-az',
        '>': 'bishtar-az',
        '∑': 'majmooe',
        '¤': 'vahed'
    },

    'fi': {
        '∆': 'delta',
        '∞': 'aarettomyys',
        '♥': 'rakkaus',
        '&': 'ja',
        '|': 'tai',
        '<': 'pienempi kuin',
        '>': 'suurempi kuin',
        '∑': 'summa',
        '¤': 'valuutta'
    },

    'fr': {
        '∆': 'delta',
        '∞': 'infiniment',
        '♥': 'Amour',
        '&': 'et',
        '|': 'ou',
        '<': 'moins que',
        '>': 'superieure a',
        '∑': 'somme des',
        '¤': 'monnaie'
    },

    'ge': {
        '∆': 'delta',
        '∞': 'usasruloba',
        '♥': 'siqvaruli',
        '&': 'da',
        '|': 'an',
        '<': 'naklebi',
        '>': 'meti',
        '∑': 'jami',
        '¤': 'valuta'
    },

    'gr': {},

    'hu': {
        '∆': 'delta',
        '∞': 'vegtelen',
        '♥': 'szerelem',
        '&': 'es',
        '|': 'vagy',
        '<': 'kisebb mint',
        '>': 'nagyobb mint',
        '∑': 'szumma',
        '¤': 'penznem'
    },

    'it': {
        '∆': 'delta',
        '∞': 'infinito',
        '♥': 'amore',
        '&': 'e',
        '|': 'o',
        '<': 'minore di',
        '>': 'maggiore di',
        '∑': 'somma',
        '¤': 'moneta'
    },

    'lt': {
        '∆': 'delta',
        '∞': 'begalybe',
        '♥': 'meile',
        '&': 'ir',
        '|': 'ar',
        '<': 'maziau nei',
        '>': 'daugiau nei',
        '∑': 'suma',
        '¤': 'valiuta'
    },

    'lv': {
        '∆': 'delta',
        '∞': 'bezgaliba',
        '♥': 'milestiba',
        '&': 'un',
        '|': 'vai',
        '<': 'mazak neka',
        '>': 'lielaks neka',
        '∑': 'summa',
        '¤': 'valuta'
    },

    'my': {
        '∆': 'kwahkhyaet',
        '∞': 'asaonasme',
        '♥': 'akhyait',
        '&': 'nhin',
        '|': 'tho',
        '<': 'ngethaw',
        '>': 'kyithaw',
        '∑': 'paungld',
        '¤': 'ngwekye'
    },

    'mk': {},

    'nl': {
        '∆': 'delta',
        '∞': 'oneindig',
        '♥': 'liefde',
        '&': 'en',
        '|': 'of',
        '<': 'kleiner dan',
        '>': 'groter dan',
        '∑': 'som',
        '¤': 'valuta'
    },

    'pl': {
        '∆': 'delta',
        '∞': 'nieskonczonosc',
        '♥': 'milosc',
        '&': 'i',
        '|': 'lub',
        '<': 'mniejsze niz',
        '>': 'wieksze niz',
        '∑': 'suma',
        '¤': 'waluta'
    },

    'pt': {
        '∆': 'delta',
        '∞': 'infinito',
        '♥': 'amor',
        '&': 'e',
        '|': 'ou',
        '<': 'menor que',
        '>': 'maior que',
        '∑': 'soma',
        '¤': 'moeda'
    },

    'ro': {
        '∆': 'delta',
        '∞': 'infinit',
        '♥': 'dragoste',
        '&': 'si',
        '|': 'sau',
        '<': 'mai mic ca',
        '>': 'mai mare ca',
        '∑': 'suma',
        '¤': 'valuta'
    },

    'ru': {
        '∆': 'delta',
        '∞': 'beskonechno',
        '♥': 'lubov',
        '&': 'i',
        '|': 'ili',
        '<': 'menshe',
        '>': 'bolshe',
        '∑': 'summa',
        '¤': 'valjuta'
    },

    'sk': {
        '∆': 'delta',
        '∞': 'nekonecno',
        '♥': 'laska',
        '&': 'a',
        '|': 'alebo',
        '<': 'menej ako',
        '>': 'viac ako',
        '∑': 'sucet',
        '¤': 'mena'
    },

    'sr': {},

    'tr': {
        '∆': 'delta',
        '∞': 'sonsuzluk',
        '♥': 'ask',
        '&': 've',
        '|': 'veya',
        '<': 'kucuktur',
        '>': 'buyuktur',
        '∑': 'toplam',
        '¤': 'para birimi'
    },

    'uk': {
        '∆': 'delta',
        '∞': 'bezkinechnist',
        '♥': 'lubov',
        '&': 'i',
        '|': 'abo',
        '<': 'menshe',
        '>': 'bilshe',
        '∑': 'suma',
        '¤': 'valjuta'
    },

    'vn': {
        '∆': 'delta',
        '∞': 'vo cuc',
        '♥': 'yeu',
        '&': 'va',
        '|': 'hoac',
        '<': 'nho hon',
        '>': 'lon hon',
        '∑': 'tong',
        '¤': 'tien te'
    }
};

var uricChars = [';', '?', ':', '@', '&', '=', '+', '$', ',', '/'].join('');

var uricNoSlashChars = [';', '?', ':', '@', '&', '=', '+', '$', ','].join('');

var markChars = ['.', '!', '~', '*', "'", '(', ')'].join('');

/**
 * getSlug
 * @param  {string} input input string
 * @param  {object|string} opts config object or separator string/char
 * @api    public
 * @return {string}  sluggified string
 */
var getSlug = function getSlug(input, opts) {
    var separator = '-';
    var result = '';
    var diatricString = '';
    var convertSymbols = true;
    var customReplacements = {};
    var maintainCase;
    var titleCase;
    var truncate;
    var uricFlag;
    var uricNoSlashFlag;
    var markFlag;
    var symbol;
    var langChar;
    var lucky;
    var i;
    var ch;
    var l;
    var lastCharWasSymbol;
    var lastCharWasDiatric;
    var allowedChars = '';

    if (typeof input !== 'string') {
        return '';
    }

    if (typeof opts === 'string') {
        separator = opts;
    }

    symbol = symbolMap.en;
    langChar = langCharMap.en;

    if (typeof opts === 'object') {
        maintainCase = opts.maintainCase || false;
        customReplacements = (opts.custom && typeof opts.custom === 'object') ? opts.custom : customReplacements;
        truncate = (+opts.truncate > 1 && opts.truncate) || false;
        uricFlag = opts.uric || false;
        uricNoSlashFlag = opts.uricNoSlash || false;
        markFlag = opts.mark || false;
        convertSymbols = (opts.symbols === false || opts.lang === false) ? false : true;
        separator = opts.separator || separator;

        if (uricFlag) {
            allowedChars += uricChars;
        }

        if (uricNoSlashFlag) {
            allowedChars += uricNoSlashChars;
        }

        if (markFlag) {
            allowedChars += markChars;
        }

        symbol = (opts.lang && symbolMap[opts.lang] && convertSymbols) ?
            symbolMap[opts.lang] : (convertSymbols ? symbolMap.en : {});

        langChar = (opts.lang && langCharMap[opts.lang]) ?
            langCharMap[opts.lang] :
            opts.lang === false || opts.lang === true ? {} : langCharMap.en;

        // if titleCase config is an Array, rewrite to object format
        if (opts.titleCase && typeof opts.titleCase.length === 'number' && Array.prototype.toString.call(opts.titleCase)) {
            opts.titleCase.forEach(function (v) {
                customReplacements[v + ''] = v + '';
            });

            titleCase = true;
        } else {
            titleCase = !!opts.titleCase;
        }

        // if custom config is an Array, rewrite to object format
        if (opts.custom && typeof opts.custom.length === 'number' && Array.prototype.toString.call(opts.custom)) {
            opts.custom.forEach(function (v) {
                customReplacements[v + ''] = v + '';
            });
        }

        // custom replacements
        Object.keys(customReplacements).forEach(function (v) {
            var r;

            if (v.length > 1) {
                r = new RegExp('\\b' + escapeChars(v) + '\\b', 'gi');
            } else {
                r = new RegExp(escapeChars(v), 'gi');
            }

            input = input.replace(r, customReplacements[v]);
        });

        // add all custom replacement to allowed charlist
        for (ch in customReplacements) {
            allowedChars += ch;
        }
    }

    allowedChars += separator;

    // escape all necessary chars
    allowedChars = escapeChars(allowedChars);

    // trim whitespaces
    input = input.replace(/(^\s+|\s+$)/g, '');

    lastCharWasSymbol = false;
    lastCharWasDiatric = false;

    for (i = 0, l = input.length; i < l; i++) {
        ch = input[i];

        if (isReplacedCustomChar(ch, customReplacements)) {
            // don't convert a already converted char
            lastCharWasSymbol = false;
        } else if (langChar[ch]) {
            // process language specific diactrics chars conversion
            ch = lastCharWasSymbol && langChar[ch].match(/[A-Za-z0-9]/) ? ' ' + langChar[ch] : langChar[ch];

            lastCharWasSymbol = false;
        } else if (ch in charMap) {
            // the transliteration changes entirely when some special characters are added
            if (i + 1 < l && lookAheadCharArray.indexOf(input[i + 1]) >= 0) {
                diatricString += ch;
                ch = '';
            } else if (lastCharWasDiatric === true) {
                ch = diatricMap[diatricString] + charMap[ch];
                diatricString = '';
            } else {
                // process diactrics chars
                ch = lastCharWasSymbol && charMap[ch].match(/[A-Za-z0-9]/) ? ' ' + charMap[ch] : charMap[ch];
            }

            lastCharWasSymbol = false;
            lastCharWasDiatric = false;
        } else if (ch in diatricMap) {
            diatricString += ch;
            ch = '';
            // end of string, put the whole meaningful word
            if (i === l - 1) {
                ch = diatricMap[diatricString];
            }
            lastCharWasDiatric = true;
        } else if (
            // process symbol chars
            symbol[ch] && !(uricFlag && uricChars
                .indexOf(ch) !== -1) && !(uricNoSlashFlag && uricNoSlashChars
                // .indexOf(ch) !== -1) && !(markFlag && markChars
                .indexOf(ch) !== -1)) {
            ch = lastCharWasSymbol || result.substr(-1).match(/[A-Za-z0-9]/) ? separator + symbol[ch] : symbol[ch];
            ch += input[i + 1] !== void 0 && input[i + 1].match(/[A-Za-z0-9]/) ? separator : '';

            lastCharWasSymbol = true;
        } else {
            if (lastCharWasDiatric === true) {
                ch = diatricMap[diatricString] + ch;
                diatricString = '';
                lastCharWasDiatric = false;
            } else if (lastCharWasSymbol && (/[A-Za-z0-9]/.test(ch) || result.substr(-1).match(/A-Za-z0-9]/))) {
                // process latin chars
                ch = ' ' + ch;
            }
            lastCharWasSymbol = false;
        }

        // add allowed chars
        result += ch.replace(new RegExp('[^\\w\\s' + allowedChars + '_-]', 'g'), separator);
    }

    if (titleCase) {
        result = result.replace(/(\w)(\S*)/g, function (_, i, r) {
            var j = i.toUpperCase() + (r !== null ? r : '');
            return (Object.keys(customReplacements).indexOf(j.toLowerCase()) < 0) ? j : j.toLowerCase();
        });
    }

    // eliminate duplicate separators
    // add separator
    // trim separators from start and end
    result = result.replace(/\s+/g, separator)
        .replace(new RegExp('\\' + separator + '+', 'g'), separator)
        .replace(new RegExp('(^\\' + separator + '+|\\' + separator + '+$)', 'g'), '');

    if (truncate && result.length > truncate) {
        lucky = result.charAt(truncate) === separator;
        result = result.slice(0, truncate);

        if (!lucky) {
            result = result.slice(0, result.lastIndexOf(separator));
        }
    }

    if (!maintainCase && !titleCase) {
        result = result.toLowerCase();
    }

    return result;
};

/**
 * createSlug curried(opts)(input)
 * @param   {object|string} opts config object or input string
 * @return  {Function} function getSlugWithConfig()
 **/
var createSlug = function createSlug(opts) {

    /**
     * getSlugWithConfig
     * @param   {string} input string
     * @return  {string} slug string
     */
    return function getSlugWithConfig(input) {
        return getSlug(input, opts);
    };
};

/**
 * escape Chars
 * @param   {string} input string
 */
var escapeChars = function escapeChars(input) {
    return input.replace(/[-\\^$*+?.()|[\]{}\/]/g, '\\$&');
};

/**
 * check if the char is an already converted char from custom list
 * @param   {char} ch character to check
 * @param   {object} customReplacements custom translation map
 */
var isReplacedCustomChar = function (ch, customReplacements) {
    for (var c in customReplacements) {
        if (customReplacements[c] === ch) {
            return true;
        }
    }
};

if (typeof module !== 'undefined' && module.exports) {

    // export functions for use in Node
    module.exports = getSlug;
    module.exports.createSlug = createSlug;
} else if (typeof define !== 'undefined' && define.amd) {

    // export function for use in AMD
    define([], function () {
        return getSlug;
    });
} else {

    // don't overwrite global if exists
    try {
        if (root.getSlug || root.createSlug) {
            throw 'speakingurl: globals exists /(getSlug|createSlug)/';
        } else {
            root.getSlug = getSlug;
            root.createSlug = createSlug;
        }
    } catch (e) {}
}

})(this);