# -*- coding: utf-8 -*-
# vim:et sts=4 sw=4
#
# ibus-typing-booster - A completion input method for IBus
#
# Copyright (c) 2013-2018 Mike FABIAN <mfabian@redhat.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>
'''
Utility functions used in ibus-typing-booster
'''

import sys
import os
import re
import string
import unicodedata
import gettext
from gi import require_version
require_version('GLib', '2.0')
from gi.repository import GLib
require_version('Gdk', '3.0')
from gi.repository import Gdk
require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GObject

import version

IMPORT_XDG_BASEDIRECTORY_SUCCESSFUL = False
try:
    import xdg.BaseDirectory
    IMPORT_XDG_BASEDIRECTORY_SUCCESSFUL = True
except (ImportError,):
    IMPORT_XDG_BASEDIRECTORY_SUCCESSFUL = False

DOMAINNAME = 'ibus-typing-booster'
_ = lambda a: gettext.dgettext(DOMAINNAME, a)
N_ = lambda a: a

MAXIMUM_NUMBER_OF_INPUT_METHODS = 10

NORMALIZATION_FORM_INTERNAL = 'NFD'

# maximum possible value for the INTEGER datatype in SQLite3
SQLITE_MAXINT = 2**63-1
# user frequency used for a user defined shortcut
SHORTCUT_USER_FREQ = 1000000

# If a character ending a sentence is committed (possibly
# followed by whitespace) remove trailing white space
# before the committed string. For example if
# commit_phrase is “!”, and the context before is “word ”,
# make the result “word!”.  And if the commit_phrase is “!
# ” and the context before is “word ” make the result
# “word! ”.
SENTENCE_END_CHARACTERS = '.,;:?!)'

CATEGORIES_TO_STRIP_FROM_TOKENS = (
    'Po', 'Pi', 'Pf', 'Ps', 'Pe', 'Pc', 'Pd'
)

LOCALE_DEFAULTS = {
    # Contains the default input methods and dictionaries which should
    # be used if ibus-typing-booster is started for the very first
    # time. In that case, no previous settings can be found from dconf
    # and a reasonable default should be used depending on the current
    # locale.
    'af_NA': {'inputmethods': ['NoIme'], 'dictionaries': ['af_NA']},
    'af_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['af_ZA']},
    'ak_GH': {'inputmethods': ['NoIme'], 'dictionaries': ['ak_GH']},
    'am_ET': {'inputmethods': ['am-sera'], 'dictionaries': ['am_ET']},
    'ar_AE': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_AE']},
    'ar_BH': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_BH']},
    'ar_DJ': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_DJ']},
    'ar_DZ': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_DZ']},
    'ar_EG': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_EG']},
    'ar_ER': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_ER']},
    'ar_IL': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_IL']},
    'ar_IN': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_IN']},
    'ar_IQ': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_IQ']},
    'ar_JO': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_JO']},
    'ar_KM': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_KM']},
    'ar_KW': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_KW']},
    'ar_LB': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_LB']},
    'ar_LY': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_LY']},
    'ar_MA': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_MA']},
    'ar_MR': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_MR']},
    'ar_OM': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_OM']},
    'ar_PS': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_PS']},
    'ar_QA': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_QA']},
    'ar_SA': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_SA']},
    'ar_SD': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_SD']},
    'ar_SO': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_SO']},
    'ar_SY': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_SY']},
    'ar_TD': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_TD']},
    'ar_TN': {'inputmethods':['NoIme'], 'dictionaries': ['ar_TN']},
    'ar_YE': {'inputmethods': ['NoIme'], 'dictionaries': ['ar_YE']},
    'as_IN': {'inputmethods': ['as-inscript2', 'NoIme'], 'dictionaries': ['as_IN', 'en_GB']},
    'ast_ES': {'inputmethods': ['NoIme'], 'dictionaries': ['ast_ES']},
    'az_AZ': {'inputmethods': ['NoIme'], 'dictionaries': ['az_AZ']},
    'be_BY': {'inputmethods': ['NoIme'], 'dictionaries': ['be_BY']},
    'ber_MA': {'inputmethods': ['NoIme'], 'dictionaries': ['ber_MA']},
    'bg_BG': {'inputmethods': ['NoIme'], 'dictionaries': ['bg_BG']},
    'bn_IN': {'inputmethods': ['bn-inscript2', 'NoIme'], 'dictionaries': ['bn_IN', 'en_GB']},
    'br_FR': {'inputmethods': ['NoIme'], 'dictionaries': ['br_FR']},
    'bs_BA': {'inputmethods': ['NoIme'], 'dictionaries': ['bs_BA']},
    'ca_AD': {'inputmethods': ['NoIme'], 'dictionaries': ['ca_AD']},
    'ca_ES': {'inputmethods': ['NoIme'], 'dictionaries': ['ca_ES']},
    'ca_FR': {'inputmethods': ['NoIme'], 'dictionaries': ['ca_FR']},
    'ca_IT': {'inputmethods': ['NoIme'], 'dictionaries': ['ca_IT']},
    'cop_EG': {'inputmethods': ['NoIme'], 'dictionaries': ['cop_EG']},
    'cs_CZ': {'inputmethods': ['NoIme'], 'dictionaries': ['cs_CZ']},
    'csb_PL': {'inputmethods': ['NoIme'], 'dictionaries': ['csb_PL']},
    'cv_RU': {'inputmethods': ['NoIme'], 'dictionaries': ['cv_RU']},
    'cy_GB': {'inputmethods': ['NoIme'], 'dictionaries': ['cy_GB']},
    'da_DK': {'inputmethods': ['NoIme'], 'dictionaries': ['da_DK']},
    'de_AT': {'inputmethods': ['NoIme'], 'dictionaries': ['de_AT']},
    'de_BE': {'inputmethods': ['NoIme'], 'dictionaries': ['de_BE']},
    'de_CH': {'inputmethods': ['NoIme'], 'dictionaries': ['de_CH']},
    'de_DE': {'inputmethods':['NoIme'], 'dictionaries': ['de_DE']},
    'de_LI': {'inputmethods': ['NoIme'], 'dictionaries': ['de_LI']},
    'de_LU': {'inputmethods': ['NoIme'], 'dictionaries': ['de_LU']},
    'dsb_DE': {'inputmethods': ['NoIme'], 'dictionaries': ['dsb_DE']},
    'el_CY': {'inputmethods': ['NoIme'], 'dictionaries': ['el_CY']},
    'el_GR': {'inputmethods': ['NoIme'], 'dictionaries': ['el_GR']},
    'en_AG': {'inputmethods': ['NoIme'], 'dictionaries': ['en_AG']},
    'en_AU': {'inputmethods': ['NoIme'], 'dictionaries': ['en_AU']},
    'en_BS': {'inputmethods': ['NoIme'], 'dictionaries': ['en_BS']},
    'en_BW': {'inputmethods': ['NoIme'], 'dictionaries': ['en_BW']},
    'en_BZ': {'inputmethods': ['NoIme'], 'dictionaries': ['en_BZ']},
    'en_CA': {'inputmethods': ['NoIme'], 'dictionaries': ['en_CA']},
    'en_DK': {'inputmethods': ['NoIme'], 'dictionaries': ['en_DK']},
    'en_GB': {'inputmethods': ['NoIme'], 'dictionaries': ['en_GB']},
    'en_GH': {'inputmethods': ['NoIme'], 'dictionaries': ['en_GH']},
    'en_HK': {'inputmethods': ['NoIme'], 'dictionaries': ['en_HK']},
    'en_IE': {'inputmethods': ['NoIme'], 'dictionaries': ['en_IE']},
    'en_IN': {'inputmethods': ['NoIme'], 'dictionaries': ['en_IN']},
    'en_JM': {'inputmethods': ['NoIme'], 'dictionaries': ['en_JM']},
    'en_MW': {'inputmethods': ['NoIme'], 'dictionaries': ['en_MW']},
    'en_NA': {'inputmethods': ['NoIme'], 'dictionaries': ['en_NA']},
    'en_NG': {'inputmethods': ['NoIme'], 'dictionaries': ['en_NG']},
    'en_NZ': {'inputmethods': ['NoIme'], 'dictionaries': ['en_NZ']},
    'en_PH': {'inputmethods': ['NoIme'], 'dictionaries': ['en_PH']},
    'en_SG': {'inputmethods': ['NoIme'], 'dictionaries': ['en_SG']},
    'en_TT': {'inputmethods': ['NoIme'], 'dictionaries': ['en_TT']},
    'en_US': {'inputmethods': ['NoIme'], 'dictionaries': ['en_US']},
    'en_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['en_ZA']},
    'en_ZM': {'inputmethods': ['NoIme'], 'dictionaries': ['en_ZM']},
    'en_ZW': {'inputmethods': ['NoIme'], 'dictionaries': ['en_ZW']},
    'eo': {'inputmethods': ['NoIme'], 'dictionaries': ['eo']},
    'es_AR': {'inputmethods': ['NoIme'], 'dictionaries': ['es_AR']},
    'es_BO': {'inputmethods': ['NoIme'], 'dictionaries': ['es_BO']},
    'es_CL': {'inputmethods': ['NoIme'], 'dictionaries': ['es_CL']},
    'es_CO': {'inputmethods': ['NoIme'], 'dictionaries': ['es_CO']},
    'es_CR': {'inputmethods': ['NoIme'], 'dictionaries': ['es_CR']},
    'es_CU': {'inputmethods': ['NoIme'], 'dictionaries': ['es_CU']},
    'es_DO': {'inputmethods': ['NoIme'], 'dictionaries': ['es_DO']},
    'es_EC': {'inputmethods': ['NoIme'], 'dictionaries': ['es_EC']},
    'es_ES': {'inputmethods': ['NoIme'], 'dictionaries': ['es_ES']},
    'es_GT': {'inputmethods': ['NoIme'], 'dictionaries': ['es_GT']},
    'es_HN': {'inputmethods': ['NoIme'], 'dictionaries': ['es_HN']},
    'es_MX': {'inputmethods': ['NoIme'], 'dictionaries': ['es_MX']},
    'es_NI': {'inputmethods': ['NoIme'], 'dictionaries': ['es_NI']},
    'es_PA': {'inputmethods': ['NoIme'], 'dictionaries': ['es_PA']},
    'es_PE': {'inputmethods': ['NoIme'], 'dictionaries': ['es_PE']},
    'es_PR': {'inputmethods': ['NoIme'], 'dictionaries': ['es_PR']},
    'es_PY': {'inputmethods': ['NoIme'], 'dictionaries': ['es_PY']},
    'es_SV': {'inputmethods': ['NoIme'], 'dictionaries': ['es_SV']},
    'es_US': {'inputmethods': ['NoIme'], 'dictionaries': ['es_US']},
    'es_UY': {'inputmethods': ['NoIme'], 'dictionaries': ['es_UY']},
    'es_VE': {'inputmethods': ['NoIme'], 'dictionaries': ['es_VE']},
    'et_EE': {'inputmethods': ['NoIme'], 'dictionaries': ['et_EE']},
    'eu_ES': {'inputmethods': ['NoIme'], 'dictionaries': ['eu_ES']},
    'fa_IR': {'inputmethods': ['NoIme'], 'dictionaries': ['fa_IR']},
    'fil_PH': {'inputmethods': ['NoIme'], 'dictionaries': ['fil_PH']},
    'fj': {'inputmethods': ['NoIme'], 'dictionaries': ['fj']},
    'fo_FO': {'inputmethods': ['NoIme'], 'dictionaries': ['fo_FO']},
    'fr_BE': {'inputmethods': ['NoIme'], 'dictionaries': ['fr_BE']},
    'fr_CA': {'inputmethods': ['NoIme'], 'dictionaries': ['fr_CA']},
    'fr_CH': {'inputmethods': ['NoIme'], 'dictionaries': ['fr_CH']},
    'fr_FR': {'inputmethods': ['NoIme'], 'dictionaries': ['fr_FR']},
    'fr_LU': {'inputmethods': ['NoIme'], 'dictionaries': ['fr_LU']},
    'fr_MC': {'inputmethods': ['NoIme'], 'dictionaries': ['fr_MC']},
    'fur_IT': {'inputmethods': ['NoIme'], 'dictionaries': ['fur_IT']},
    'fy_DE': {'inputmethods': ['NoIme'], 'dictionaries': ['fy_DE']},
    'fy_NL': {'inputmethods': ['NoIme'], 'dictionaries': ['fy_NL']},
    'ga_IE': {'inputmethods': ['NoIme'], 'dictionaries': ['ga_IE']},
    'gd_GB': {'inputmethods': ['NoIme'], 'dictionaries': ['gd_GB']},
    'gl_ES': {'inputmethods': ['NoIme'], 'dictionaries': ['gl_ES']},
    'grc': {'inputmethods': ['NoIme'], 'dictionaries': ['grc']},
    'gu_IN': {'inputmethods': ['gu-inscript2', 'NoIme'], 'dictionaries': ['gu_IN', 'en_GB']},
    'gv_GB': {'inputmethods': ['NoIme'], 'dictionaries': ['gv_GB']},
    'haw': {'inputmethods': ['NoIme'], 'dictionaries': ['haw']},
    'he_IL': {'inputmethods': ['NoIme'], 'dictionaries': ['he_IL']},
    'hi_IN': {'inputmethods': ['hi-inscript2', 'NoIme'], 'dictionaries': ['hi_IN', 'en_GB']},
    'hil_PH': {'inputmethods': ['NoIme'], 'dictionaries': ['hil_PH']},
    'hr_HR': {'inputmethods': ['NoIme'], 'dictionaries': ['hr_HR']},
    'hsb_DE': {'inputmethods': ['NoIme'], 'dictionaries': ['hsb_DE']},
    'ht_HT': {'inputmethods': ['NoIme'], 'dictionaries': ['ht_HT']},
    'hu_HU': {'inputmethods': ['NoIme'], 'dictionaries': ['hu_HU']},
    'hy_AM': {'inputmethods': ['NoIme'], 'dictionaries': ['hy_AM']},
    'ia': {'inputmethods': ['NoIme'], 'dictionaries': ['ia']},
    'id_ID': {'inputmethods': ['NoIme'], 'dictionaries': ['id_ID']},
    'is_IS': {'inputmethods': ['NoIme'], 'dictionaries': ['is_IS']},
    'it_CH': {'inputmethods': ['NoIme'], 'dictionaries': ['it_CH']},
    'it_IT': {'inputmethods': ['NoIme'], 'dictionaries': ['it_IT']},
    'kk_KZ': {'inputmethods': ['NoIme'], 'dictionaries': ['kk_KZ']},
    'km_KH': {'inputmethods': ['NoIme'], 'dictionaries': ['km_KH']},
    'kn_IN': {'inputmethods': ['kn-inscript2', 'NoIme'], 'dictionaries': ['kn_IN', 'en_GB']},
    'ko_KR': {'inputmethods': ['ko-han2', 'NoIme'], 'dictionaries': ['ko_KR', 'en_GB']},
    'ku_SY': {'inputmethods': ['NoIme'], 'dictionaries': ['ku_SY']},
    'ku_TR': {'inputmethods': ['NoIme'], 'dictionaries': ['ku_TR']},
    'ky_KG': {'inputmethods': ['NoIme'], 'dictionaries': ['ky_KG']},
    'la': {'inputmethods': ['NoIme'], 'dictionaries': ['la']},
    'lb_LU': {'inputmethods': ['NoIme'], 'dictionaries': ['lb_LU']},
    'ln_CD': {'inputmethods': ['NoIme'], 'dictionaries': ['ln_CD']},
    'lt_LT': {'inputmethods': ['NoIme'], 'dictionaries': ['lt_LT']},
    'lv_LV': {'inputmethods': ['NoIme'], 'dictionaries': ['lv_LV']},
    'mai_IN': {'inputmethods': ['mai-inscript2', 'NoIme'], 'dictionaries': ['mai_IN', 'en_GB']},
    'mg': {'inputmethods': ['NoIme'], 'dictionaries': ['mg']},
    'mi_NZ': {'inputmethods': ['NoIme'], 'dictionaries': ['mi_NZ']},
    'mk_MK': {'inputmethods': ['NoIme'], 'dictionaries': ['mk_MK']},
    'ml_IN': {'inputmethods': ['ml-inscript2', 'NoIme'], 'dictionaries': ['ml_IN', 'en_GB']},
    'mn_MN': {'inputmethods': ['NoIme'], 'dictionaries': ['mn_MN']},
    'mos_BF': {'inputmethods': ['NoIme'], 'dictionaries': ['mos_BF']},
    'mr_IN': {'inputmethods': ['mr-inscript2', 'NoIme'], 'dictionaries': ['mr_IN', 'en_GB']},
    'ms_BN': {'inputmethods': ['NoIme'], 'dictionaries': ['ms_BN']},
    'ms_MY': {'inputmethods': ['NoIme'], 'dictionaries': ['ms_MY']},
    'mt_MT': {'inputmethods': ['NoIme'], 'dictionaries': ['mt_MT']},
    'nb_NO': {'inputmethods': ['NoIme'], 'dictionaries': ['nb_NO']},
    'nds_DE': {'inputmethods': ['NoIme'], 'dictionaries': ['nds_DE']},
    'nds_NL': {'inputmethods': ['NoIme'], 'dictionaries': ['nds_NL']},
    'ne_IN': {'inputmethods': ['ne-rom', 'NoIme'], 'dictionaries': ['ne_IN', 'en_GB']},
    'ne_NP': {'inputmethods': ['ne-rom', 'NoIme'], 'dictionaries': ['ne_NP', 'en_GB']},
    'nl_AW': {'inputmethods': ['NoIme'], 'dictionaries': ['nl_AW']},
    'nl_BE': {'inputmethods': ['NoIme'], 'dictionaries': ['nl_BE']},
    'nl_NL': {'inputmethods': ['NoIme'], 'dictionaries': ['nl_NL']},
    'nn_NO': {'inputmethods': ['NoIme'], 'dictionaries': ['nn_NO']},
    'nr_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['nr_ZA']},
    'nso_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['nso_ZA']},
    'ny_MW': {'inputmethods': ['NoIme'], 'dictionaries': ['ny_MW']},
    'oc_FR': {'inputmethods': ['NoIme'], 'dictionaries': ['oc_FR']},
    'om_ET': {'inputmethods': ['NoIme'], 'dictionaries': ['om_ET']},
    'om_KE': {'inputmethods': ['NoIme'], 'dictionaries': ['om_KE']},
    'or_IN': {'inputmethods': ['or-inscript2', 'NoIme'], 'dictionaries': ['or_IN', 'en_GB']},
    'pa_IN': {'inputmethods': ['pa-inscript2', 'NoIme'], 'dictionaries': ['pa_IN', 'en_GB']},
    'pl_PL': {'inputmethods': ['NoIme'], 'dictionaries': ['pl_PL']},
    'plt': {'inputmethods': ['NoIme'], 'dictionaries': ['plt']},
    'pt_AO': {'inputmethods': ['NoIme'], 'dictionaries': ['pt_AO']},
    'pt_BR': {'inputmethods': ['NoIme'], 'dictionaries': ['pt_BR']},
    'pt_PT': {'inputmethods': ['NoIme'], 'dictionaries': ['pt_PT']},
    'qu_EC': {'inputmethods': ['NoIme'], 'dictionaries': ['qu_EC']},
    'quh_BO': {'inputmethods': ['NoIme'], 'dictionaries': ['quh_BO']},
    'ro_RO': {'inputmethods': ['NoIme'], 'dictionaries': ['ro_RO']},
    'ru_RU': {'inputmethods': ['NoIme'], 'dictionaries': ['ru_RU']},
    'ru_UA': {'inputmethods': ['NoIme'], 'dictionaries': ['ru_UA']},
    'rw_RW': {'inputmethods': ['NoIme'], 'dictionaries': ['rw_RW']},
    'sc_IT': {'inputmethods': ['NoIme'], 'dictionaries': ['sc_IT']},
    'se_FI': {'inputmethods': ['NoIme'], 'dictionaries': ['se_FI']},
    'se_NO': {'inputmethods': ['NoIme'], 'dictionaries': ['se_NO']},
    'se_SE': {'inputmethods': ['NoIme'], 'dictionaries': ['se_SE']},
    'sh_ME': {'inputmethods': ['NoIme'], 'dictionaries': ['sh_ME']},
    'sh_RS': {'inputmethods': ['NoIme'], 'dictionaries': ['sh_RS']},
    'sh_YU': {'inputmethods': ['NoIme'], 'dictionaries': ['sh_YU']},
    'shs_CA': {'inputmethods': ['NoIme'], 'dictionaries': ['shs_CA']},
    'si_LK': {'inputmethods': ['NoIme'], 'dictionaries': ['si_LK', 'en_GB']},
    'sk_SK': {'inputmethods': ['NoIme'], 'dictionaries': ['sk_SK']},
    'sl_SI': {'inputmethods': ['NoIme'], 'dictionaries': ['sl_SI']},
    'smj_NO': {'inputmethods': ['NoIme'], 'dictionaries': ['smj_NO']},
    'smj_SE': {'inputmethods': ['NoIme'], 'dictionaries': ['smj_SE']},
    'so_DJ': {'inputmethods': ['NoIme'], 'dictionaries': ['so_DJ']},
    'so_ET': {'inputmethods': ['NoIme'], 'dictionaries': ['so_ET']},
    'so_KE': {'inputmethods': ['NoIme'], 'dictionaries': ['so_KE']},
    'so_SO': {'inputmethods': ['NoIme'], 'dictionaries': ['so_SO']},
    'sq_AL': {'inputmethods': ['NoIme'], 'dictionaries': ['sq_AL']},
    'sr_ME': {'inputmethods': ['NoIme'], 'dictionaries': ['sr_ME']},
    'sr_RS': {'inputmethods': ['NoIme'], 'dictionaries': ['sr_RS']},
    'sr_YU': {'inputmethods': ['NoIme'], 'dictionaries': ['sr_YU']},
    'ss_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['ss_ZA']},
    'st_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['st_ZA']},
    'sv_FI': {'inputmethods': ['NoIme'], 'dictionaries': ['sv_FI']},
    'sv_SE': {'inputmethods': ['NoIme'], 'dictionaries': ['sv_SE']},
    'sw_KE': {'inputmethods': ['NoIme'], 'dictionaries': ['sw_KE']},
    'sw_TZ': {'inputmethods': ['NoIme'], 'dictionaries': ['sw_TZ']},
    'ta_IN': {'inputmethods': ['ta-inscript2', 'NoIme'], 'dictionaries': ['ta_IN', 'en_GB']},
    'te_IN': {'inputmethods': ['te-inscript2', 'NoIme'], 'dictionaries': ['te_IN', 'en_GB']},
    'tet_ID': {'inputmethods': ['NoIme'], 'dictionaries': ['tet_ID']},
    'tet_TL': {'inputmethods': ['NoIme'], 'dictionaries': ['tet_TL']},
    'th_TH': {'inputmethods': ['NoIme'], 'dictionaries': ['th_TH']},
    'ti_ER': {'inputmethods': ['NoIme'], 'dictionaries': ['ti_ER']},
    'ti_ET': {'inputmethods': ['NoIme'], 'dictionaries': ['ti_ET']},
    'tk_TM': {'inputmethods': ['NoIme'], 'dictionaries': ['tk_TM']},
    'tl_PH': {'inputmethods': ['NoIme'], 'dictionaries': ['tl_PH']},
    'tn_BW': {'inputmethods': ['NoIme'], 'dictionaries': ['tn_BW']},
    'tn_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['tn_ZA']},
    'tpi_PG': {'inputmethods': ['NoIme'], 'dictionaries': ['tpi_PG']},
    'ts_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['ts_ZA']},
    'uk_UA': {'inputmethods': ['NoIme'], 'dictionaries': ['uk_UA']},
    'ur_IN': {'inputmethods': ['NoIme'], 'dictionaries': ['ur_IN']},
    'ur_PK': {'inputmethods': ['NoIme'], 'dictionaries': ['ur_PK']},
    'uz_UZ': {'inputmethods': ['NoIme'], 'dictionaries': ['uz_UZ']},
    've_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['ve_ZA']},
    'vi_VN': {'inputmethods': ['NoIme'], 'dictionaries': ['vi_VN']},
    'wa_BE': {'inputmethods': ['NoIme'], 'dictionaries': ['wa_BE']},
    'xh_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['xh_ZA']},
    'yi_US': {'inputmethods': ['NoIme', 'yi-yivo'], 'dictionaries': ['yi_US', 'en_US']},
    'zu_ZA': {'inputmethods': ['NoIme'], 'dictionaries': ['zu_ZA', 'en_GB']},
}

def get_default_input_methods(locale):
    '''
    Gets the default input methods for a locale

    :param locale:
    :type locale: String
    :rtype: List of  Strings

    Examples:

    >>> get_default_input_methods('te_IN')
    ['te-inscript2', 'NoIme']

    >>> get_default_input_methods('xx_YY')
    ['NoIme']
    '''
    if locale in LOCALE_DEFAULTS:
        default_input_methods = LOCALE_DEFAULTS[locale]['inputmethods']
    else:
        default_input_methods = ['NoIme']
    return default_input_methods

def get_default_dictionaries(locale):
    '''
    Gets the default dictionaries for a locale

    :param locale:
    :type locale: String
    :rtype: List of  Strings

    Examples:

    >>> get_default_dictionaries('te_IN')
    ['te_IN', 'en_GB']

    >>> get_default_dictionaries('xx_YY')
    ['en_US']
    '''
    if locale in LOCALE_DEFAULTS:
        default_dictionaries = LOCALE_DEFAULTS[locale]['dictionaries']
    else:
        default_dictionaries = ['en_US']
    return default_dictionaries

HUNSPELL_DICTIONARIES = {
    # List of all locales/languages where hunspell dictionaries exist.
    # They do not necessary need to be installed on the system at the
    # moment.  But it is known that a hunspell dictionary for that
    # locale/language exists, so usually one just needs to install the
    # right package to make it available.
    'af_NA',
    'af_ZA',
    'ak_GH',
    'am_ET',
    'ar_AE',
    'ar_BH',
    'ar_DJ',
    'ar_DZ',
    'ar_EG',
    'ar_ER',
    'ar_IL',
    'ar_IN',
    'ar_IQ',
    'ar_JO',
    'ar_KM',
    'ar_KW',
    'ar_LB',
    'ar_LY',
    'ar_MA',
    'ar_MR',
    'ar_OM',
    'ar_PS',
    'ar_QA',
    'ar_SA',
    'ar_SD',
    'ar_SO',
    'ar_SY',
    'ar_TD',
    'ar_TN',
    'ar_YE',
    'as_IN',
    'ast_ES',
    'az_AZ',
    'be_BY',
    'ber_MA',
    'bg_BG',
    'bn_IN',
    'br_FR',
    'bs_BA',
    'ca_AD',
    'ca_ES',
    'ca_FR',
    'ca_IT',
    'cop_EG',
    'cs_CZ',
    'csb_PL',
    'cv_RU',
    'cy_GB',
    'da_DK',
    'de_AT',
    'de_BE',
    'de_CH',
    'de_DE',
    'de_LI',
    'de_LU',
    'dsb_DE',
    'el_CY',
    'el_GR',
    'en_AG',
    'en_AU',
    'en_BS',
    'en_BW',
    'en_BZ',
    'en_CA',
    'en_DK',
    'en_GB',
    'en_GH',
    'en_HK',
    'en_IE',
    'en_IN',
    'en_JM',
    'en_MW',
    'en_NA',
    'en_NG',
    'en_NZ',
    'en_PH',
    'en_SG',
    'en_TT',
    'en_US',
    'en_ZA',
    'en_ZM',
    'en_ZW',
    'eo',
    'es_AR',
    'es_BO',
    'es_CL',
    'es_CO',
    'es_CR',
    'es_CU',
    'es_DO',
    'es_EC',
    'es_ES',
    'es_GT',
    'es_HN',
    'es_MX',
    'es_NI',
    'es_PA',
    'es_PE',
    'es_PR',
    'es_PY',
    'es_SV',
    'es_US',
    'es_UY',
    'es_VE',
    'et_EE',
    'eu_ES',
    'fa_IR',
    'fil_PH',
    'fj',
    'fo_FO',
    'fr_BE',
    'fr_CA',
    'fr_CH',
    'fr_FR',
    'fr_LU',
    'fr_MC',
    'fur_IT',
    'fy_DE',
    'fy_NL',
    'ga_IE',
    'gd_GB',
    'gl_ES',
    'grc',
    'gu_IN',
    'gv_GB',
    'haw',
    'he_IL',
    'hi_IN',
    'hil_PH',
    'hr_HR',
    'hsb_DE',
    'ht_HT',
    'hu_HU',
    'hy_AM',
    'ia',
    'id_ID',
    'is_IS',
    'it_CH',
    'it_IT',
    'kk_KZ',
    'km_KH',
    'kn_IN',
    'ko_KR',
    'ku_SY',
    'ku_TR',
    'ky_KG',
    'la',
    'lb_LU',
    'ln_CD',
    'lt_LT',
    'lv_LV',
    'mai_IN',
    'mg',
    'mi_NZ',
    'mk_MK',
    'ml_IN',
    'mn_MN',
    'mos_BF',
    'mr_IN',
    'ms_BN',
    'ms_MY',
    'mt_MT',
    'nb_NO',
    'nds_DE',
    'nds_NL',
    'ne_IN',
    'ne_NP',
    'nl_AW',
    'nl_BE',
    'nl_NL',
    'nn_NO',
    'nr_ZA',
    'nso_ZA',
    'ny_MW',
    'oc_FR',
    'om_ET',
    'om_KE',
    'or_IN',
    'pa_IN',
    'pl_PL',
    'plt',
    'pt_AO',
    'pt_BR',
    'pt_PT',
    'qu_EC',
    'quh_BO',
    'ro_RO',
    'ru_RU',
    'ru_UA',
    'rw_RW',
    'sc_IT',
    'se_FI',
    'se_NO',
    'se_SE',
    'sh_ME',
    'sh_RS',
    'sh_YU',
    'shs_CA',
    'si_LK',
    'sk_SK',
    'sl_SI',
    'smj_NO',
    'smj_SE',
    'so_DJ',
    'so_ET',
    'so_KE',
    'so_SO',
    'sq_AL',
    'sr_ME',
    'sr_RS',
    'sr_YU',
    'ss_ZA',
    'st_ZA',
    'sv_FI',
    'sv_SE',
    'sw_KE',
    'sw_TZ',
    'ta_IN',
    'te_IN',
    'tet_ID',
    'tet_TL',
    'th_TH',
    'ti_ER',
    'ti_ET',
    'tk',
    'tl_PH',
    'tn_BW',
    'tn_ZA',
    'tpi_PG',
    'ts_ZA',
    'uk_UA',
    'ur_IN',
    'ur_PK',
    'uz_UZ',
    've_ZA',
    'vi_VN',
    'wa_BE',
    'xh_ZA',
    'yi_US',
    'zu',
}

CLDR_ANNOTATION_FILES = {
    # List of all locales/languages where CLDR annotation files currently exist.
    # Not all of these are necessarily available at the moment.
    # That depends what is currently installed on the system.
    'af',
    'af_NA',
    'af_ZA',
    'agq',
    'agq_CM',
    'ak',
    'ak_GH',
    'am',
    'am_ET',
    'ar',
    'ar_001',
    'ar_AE',
    'ar_BH',
    'ar_DJ',
    'ar_DZ',
    'ar_EG',
    'ar_EH',
    'ar_ER',
    'ar_IL',
    'ar_IQ',
    'ar_JO',
    'ar_KM',
    'ar_KW',
    'ar_LB',
    'ar_LY',
    'ar_MA',
    'ar_MR',
    'ar_OM',
    'ar_PS',
    'ar_QA',
    'ar_SA',
    'ar_SD',
    'ar_SO',
    'ar_SS',
    'ar_SY',
    'ar_TD',
    'ar_TN',
    'ar_YE',
    'as',
    'as_IN',
    'asa',
    'asa_TZ',
    'ast',
    'ast_ES',
    'az',
    'az_Cyrl',
    'az_Cyrl_AZ',
    'az_Latn',
    'az_Latn_AZ',
    'bas',
    'bas_CM',
    'be',
    'be_BY',
    'bem',
    'bem_ZM',
    'bez',
    'bez_TZ',
    'bg',
    'bg_BG',
    'bm',
    'bm_ML',
    'bn',
    'bn_BD',
    'bn_IN',
    'bo',
    'bo_CN',
    'bo_IN',
    'br',
    'br_FR',
    'brx',
    'brx_IN',
    'bs',
    'bs_Cyrl',
    'bs_Cyrl_BA',
    'bs_Latn',
    'bs_Latn_BA',
    'ca',
    'ca_AD',
    'ca_ES',
    'ca_ES_VALENCIA',
    'ca_FR',
    'ca_IT',
    'ccp',
    'ccp_BD',
    'ccp_IN',
    'ce',
    'ce_RU',
    'cgg',
    'cgg_UG',
    'chr',
    'chr_US',
    'ckb',
    'ckb_IQ',
    'ckb_IR',
    'cs',
    'cs_CZ',
    'cu',
    'cu_RU',
    'cy',
    'cy_GB',
    'da',
    'da_DK',
    'da_GL',
    'dav',
    'dav_KE',
    'de',
    'de_AT',
    'de_BE',
    'de_CH',
    'de_DE',
    'de_IT',
    'de_LI',
    'de_LU',
    'dje',
    'dje_NE',
    'dsb',
    'dsb_DE',
    'dua',
    'dua_CM',
    'dyo',
    'dyo_SN',
    'dz',
    'dz_BT',
    'ebu',
    'ebu_KE',
    'ee',
    'ee_GH',
    'ee_TG',
    'el',
    'el_CY',
    'el_GR',
    'en',
    'en_001',
    'en_150',
    'en_AG',
    'en_AI',
    'en_AS',
    'en_AT',
    'en_AU',
    'en_BB',
    'en_BE',
    'en_BI',
    'en_BM',
    'en_BS',
    'en_BW',
    'en_BZ',
    'en_CA',
    'en_CC',
    'en_CH',
    'en_CK',
    'en_CM',
    'en_CX',
    'en_CY',
    'en_DE',
    'en_DG',
    'en_DK',
    'en_DM',
    'en_ER',
    'en_FI',
    'en_FJ',
    'en_FK',
    'en_FM',
    'en_GB',
    'en_GD',
    'en_GG',
    'en_GH',
    'en_GI',
    'en_GM',
    'en_GU',
    'en_GY',
    'en_HK',
    'en_IE',
    'en_IL',
    'en_IM',
    'en_IN',
    'en_IO',
    'en_JE',
    'en_JM',
    'en_KE',
    'en_KI',
    'en_KN',
    'en_KY',
    'en_LC',
    'en_LR',
    'en_LS',
    'en_MG',
    'en_MH',
    'en_MO',
    'en_MP',
    'en_MS',
    'en_MT',
    'en_MU',
    'en_MW',
    'en_MY',
    'en_NA',
    'en_NF',
    'en_NG',
    'en_NL',
    'en_NR',
    'en_NU',
    'en_NZ',
    'en_PG',
    'en_PH',
    'en_PK',
    'en_PN',
    'en_PR',
    'en_PW',
    'en_RW',
    'en_SB',
    'en_SC',
    'en_SD',
    'en_SE',
    'en_SG',
    'en_SH',
    'en_SI',
    'en_SL',
    'en_SS',
    'en_SX',
    'en_SZ',
    'en_TC',
    'en_TK',
    'en_TO',
    'en_TT',
    'en_TV',
    'en_TZ',
    'en_UG',
    'en_UM',
    'en_US',
    'en_US_POSIX',
    'en_VC',
    'en_VG',
    'en_VI',
    'en_VU',
    'en_WS',
    'en_ZA',
    'en_ZM',
    'en_ZW',
    'eo',
    'eo_001',
    'es',
    'es_419',
    'es_AR',
    'es_BO',
    'es_BR',
    'es_BZ',
    'es_CL',
    'es_CO',
    'es_CR',
    'es_CU',
    'es_DO',
    'es_EA',
    'es_EC',
    'es_ES',
    'es_GQ',
    'es_GT',
    'es_HN',
    'es_IC',
    'es_MX',
    'es_NI',
    'es_PA',
    'es_PE',
    'es_PH',
    'es_PR',
    'es_PY',
    'es_SV',
    'es_US',
    'es_UY',
    'es_VE',
    'et',
    'et_EE',
    'eu',
    'eu_ES',
    'ewo',
    'ewo_CM',
    'fa',
    'fa_AF',
    'fa_IR',
    'ff',
    'ff_CM',
    'ff_GN',
    'ff_MR',
    'ff_SN',
    'fi',
    'fi_FI',
    'fil',
    'fil_PH',
    'fo',
    'fo_DK',
    'fo_FO',
    'fr',
    'fr_BE',
    'fr_BF',
    'fr_BI',
    'fr_BJ',
    'fr_BL',
    'fr_CA',
    'fr_CD',
    'fr_CF',
    'fr_CG',
    'fr_CH',
    'fr_CI',
    'fr_CM',
    'fr_DJ',
    'fr_DZ',
    'fr_FR',
    'fr_GA',
    'fr_GF',
    'fr_GN',
    'fr_GP',
    'fr_GQ',
    'fr_HT',
    'fr_KM',
    'fr_LU',
    'fr_MA',
    'fr_MC',
    'fr_MF',
    'fr_MG',
    'fr_ML',
    'fr_MQ',
    'fr_MR',
    'fr_MU',
    'fr_NC',
    'fr_NE',
    'fr_PF',
    'fr_PM',
    'fr_RE',
    'fr_RW',
    'fr_SC',
    'fr_SN',
    'fr_SY',
    'fr_TD',
    'fr_TG',
    'fr_TN',
    'fr_VU',
    'fr_WF',
    'fr_YT',
    'fur',
    'fur_IT',
    'fy',
    'fy_NL',
    'ga',
    'ga_IE',
    'gd',
    'gd_GB',
    'gl',
    'gl_ES',
    'gsw',
    'gsw_CH',
    'gsw_FR',
    'gsw_LI',
    'gu',
    'gu_IN',
    'guz',
    'guz_KE',
    'gv',
    'gv_IM',
    'ha',
    'ha_GH',
    'ha_NE',
    'ha_NG',
    'haw',
    'haw_US',
    'he',
    'he_IL',
    'hi',
    'hi_IN',
    'hr',
    'hr_BA',
    'hr_HR',
    'hsb',
    'hsb_DE',
    'hu',
    'hu_HU',
    'hy',
    'hy_AM',
    'id',
    'id_ID',
    'ig',
    'ig_NG',
    'ii',
    'ii_CN',
    'is',
    'is_IS',
    'it',
    'it_CH',
    'it_IT',
    'it_SM',
    'it_VA',
    'ja',
    'ja_JP',
    'jgo',
    'jgo_CM',
    'jmc',
    'jmc_TZ',
    'ka',
    'ka_GE',
    'kab',
    'kab_DZ',
    'kam',
    'kam_KE',
    'kde',
    'kde_TZ',
    'kea',
    'kea_CV',
    'khq',
    'khq_ML',
    'ki',
    'ki_KE',
    'kk',
    'kk_KZ',
    'kkj',
    'kkj_CM',
    'kl',
    'kl_GL',
    'kln',
    'kln_KE',
    'km',
    'km_KH',
    'kn',
    'kn_IN',
    'ko',
    'ko_KP',
    'ko_KR',
    'kok',
    'kok_IN',
    'ks',
    'ks_IN',
    'ksb',
    'ksb_TZ',
    'ksf',
    'ksf_CM',
    'ksh',
    'ksh_DE',
    'kw',
    'kw_GB',
    'ky',
    'ky_KG',
    'lag',
    'lag_TZ',
    'lb',
    'lb_LU',
    'lg',
    'lg_UG',
    'lkt',
    'lkt_US',
    'ln',
    'ln_AO',
    'ln_CD',
    'ln_CF',
    'ln_CG',
    'lo',
    'lo_LA',
    'lrc',
    'lrc_IQ',
    'lrc_IR',
    'lt',
    'lt_LT',
    'lu',
    'lu_CD',
    'luo',
    'luo_KE',
    'luy',
    'luy_KE',
    'lv',
    'lv_LV',
    'mas',
    'mas_KE',
    'mas_TZ',
    'mer',
    'mer_KE',
    'mfe',
    'mfe_MU',
    'mg',
    'mg_MG',
    'mgh',
    'mgh_MZ',
    'mgo',
    'mgo_CM',
    'mk',
    'mk_MK',
    'ml',
    'ml_IN',
    'mn',
    'mn_MN',
    'mr',
    'mr_IN',
    'ms',
    'ms_BN',
    'ms_MY',
    'ms_SG',
    'mt',
    'mt_MT',
    'mua',
    'mua_CM',
    'my',
    'my_MM',
    'mzn',
    'mzn_IR',
    'naq',
    'naq_NA',
    'nb',
    'nb_NO',
    'nb_SJ',
    'nd',
    'nd_ZW',
    'nds',
    'nds_DE',
    'nds_NL',
    'ne',
    'ne_IN',
    'ne_NP',
    'nl',
    'nl_AW',
    'nl_BE',
    'nl_BQ',
    'nl_CW',
    'nl_NL',
    'nl_SR',
    'nl_SX',
    'nmg',
    'nmg_CM',
    'nn',
    'nn_NO',
    'nnh',
    'nnh_CM',
    'nus',
    'nus_SS',
    'nyn',
    'nyn_UG',
    'om',
    'om_ET',
    'om_KE',
    'or',
    'or_IN',
    'os',
    'os_GE',
    'os_RU',
    'pa',
    'pa_Arab',
    'pa_Arab_PK',
    'pa_Guru',
    'pa_Guru_IN',
    'pl',
    'pl_PL',
    'prg',
    'prg_001',
    'ps',
    'ps_AF',
    'pt',
    'pt_AO',
    'pt_BR',
    'pt_CH',
    'pt_CV',
    'pt_GQ',
    'pt_GW',
    'pt_LU',
    'pt_MO',
    'pt_MZ',
    'pt_PT',
    'pt_ST',
    'pt_TL',
    'qu',
    'qu_BO',
    'qu_EC',
    'qu_PE',
    'rm',
    'rm_CH',
    'rn',
    'rn_BI',
    'ro',
    'ro_MD',
    'ro_RO',
    'rof',
    'rof_TZ',
    'root',
    'ru',
    'ru_BY',
    'ru_KG',
    'ru_KZ',
    'ru_MD',
    'ru_RU',
    'ru_UA',
    'rw',
    'rw_RW',
    'rwk',
    'rwk_TZ',
    'sah',
    'sah_RU',
    'saq',
    'saq_KE',
    'sbp',
    'sbp_TZ',
    'sd',
    'sd_PK',
    'se',
    'se_FI',
    'se_NO',
    'se_SE',
    'seh',
    'seh_MZ',
    'ses',
    'ses_ML',
    'sg',
    'sg_CF',
    'shi',
    'shi_Latn',
    'shi_Latn_MA',
    'shi_Tfng',
    'shi_Tfng_MA',
    'si',
    'si_LK',
    'sk',
    'sk_SK',
    'sl',
    'sl_SI',
    'smn',
    'smn_FI',
    'sn',
    'sn_ZW',
    'so',
    'so_DJ',
    'so_ET',
    'so_KE',
    'so_SO',
    'sq',
    'sq_AL',
    'sq_MK',
    'sq_XK',
    'sr',
    'sr_Cyrl',
    'sr_Cyrl_BA',
    'sr_Cyrl_ME',
    'sr_Cyrl_RS',
    'sr_Cyrl_XK',
    'sr_Latn',
    'sr_Latn_BA',
    'sr_Latn_ME',
    'sr_Latn_RS',
    'sr_Latn_XK',
    'sv',
    'sv_AX',
    'sv_FI',
    'sv_SE',
    'sw',
    'sw_CD',
    'sw_KE',
    'sw_TZ',
    'sw_UG',
    'ta',
    'ta_IN',
    'ta_LK',
    'ta_MY',
    'ta_SG',
    'te',
    'te_IN',
    'teo',
    'teo_KE',
    'teo_UG',
    'tg',
    'tg_TJ',
    'th',
    'th_TH',
    'ti',
    'ti_ER',
    'ti_ET',
    'tk',
    'tk_TM',
    'to',
    'to_TO',
    'tr',
    'tr_CY',
    'tr_TR',
    'tt',
    'tt_RU',
    'twq',
    'twq_NE',
    'tzm',
    'tzm_MA',
    'ug',
    'ug_CN',
    'uk',
    'uk_UA',
    'ur',
    'ur_IN',
    'ur_PK',
    'uz',
    'uz_Arab',
    'uz_Arab_AF',
    'uz_Cyrl',
    'uz_Cyrl_UZ',
    'uz_Latn',
    'uz_Latn_UZ',
    'vai',
    'vai_Latn',
    'vai_Latn_LR',
    'vai_Vaii',
    'vai_Vaii_LR',
    'vi',
    'vi_VN',
    'vo',
    'vo_001',
    'vun',
    'vun_TZ',
    'wae',
    'wae_CH',
    'wo',
    'wo_SN',
    'xog',
    'xog_UG',
    'yav',
    'yav_CM',
    'yi',
    'yi_001',
    'yo',
    'yo_BJ',
    'yo_NG',
    'yue',
    'yue_Hans',
    'yue_Hans_CN',
    'yue_Hant',
    'yue_Hant_HK',
    'zgh',
    'zgh_MA',
    'zh',
    'zh_Hans',
    'zh_Hans_CN',
    'zh_Hans_HK',
    'zh_Hans_MO',
    'zh_Hans_SG',
    'zh_Hant',
    'zh_Hant_HK',
    'zh_Hant_MO',
    'zh_Hant_TW',
    'zu',
    'zu_ZA',
}

SUPPORTED_DICTIONARIES = set()
SUPPORTED_DICTIONARIES.update(HUNSPELL_DICTIONARIES)
SUPPORTED_DICTIONARIES.update(CLDR_ANNOTATION_FILES)

M17N_INPUT_METHODS = {
    # Dictionary of all the m17n input methods which are
    # useful with ibus-typing-booster.
    #
    # Key: name of input method.
    # Value: file name where the input method is implemented.
    'am-sera': 'am-sera.mim',
    'ar-kbd': 'ar-kbd.mim',
    'ar-translit': 'ar-translit.mim',
    'as-inscript': 'as-inscript.mim',
    'as-inscript2': 'as-inscript2.mim',
    'as-itrans': 'as-itrans.mim',
    'as-phonetic': 'as-phonetic.mim',
    'ath-phonetic': 'ath-phonetic.mim',
    'be-kbd': 'be-kbd.mim',
    'bla-phonetic': 'bla-phonetic.mim',
    'bn-disha': 'bn-disha.mim',
    'bn-inscript': 'bn-inscript.mim',
    'bn-inscript2': 'bn-inscript2.mim',
    'bn-itrans': 'bn-itrans.mim',
    'bn-probhat': 'bn-probhat.mim',
    'bo-ewts': 'bo-ewts.mim',
    'bo-tcrc': 'bo-tcrc.mim',
    'bo-wylie': 'bo-wylie.mim',
    'brx-inscript2': 'brx-inscript2-deva.mim',
    # 't-nil-cjk-util': 'cjk-util.mim', # not useful
    'cmc-kbd': 'cmc-kbd.mim',
    'cr-western': 'cr-western.mim',
    'cs-kbd': 'cs-kbd.mim',
    'da-post': 'da-post.mim',
    'doi-inscript2': 'doi-inscript2-deva.mim',
    'dv-phonetic': 'dv-phonetic.mim',
    'el-kbd': 'el-kbd.mim',
    'eo-h-fundamente': 'eo-h-f.mim',
    'eo-h-sistemo': 'eo-h.mim',
    'eo-plena': 'eo-plena.mim',
    'eo-q-sistemo': 'eo-q.mim',
    'eo-vi-sistemo': 'eo-vi.mim',
    'eo-x-sistemo': 'eo-x.mim',
    'fa-isiri': 'fa-isiri.mim',
    'fr-azerty': 'fr-azerty.mim',
    # 't-nil-global': 'global.mim', # not useful
    'grc-mizuochi': 'grc-mizuochi.mim',
    'gu-inscript': 'gu-inscript.mim',
    'gu-inscript2': 'gu-inscript2.mim',
    'gu-itrans': 'gu-itrans.mim',
    'gu-phonetic': 'gu-phonetic.mim',
    'he-kbd': 'he-kbd.mim',
    'hi-inscript': 'hi-inscript.mim',
    'hi-inscript2': 'hi-inscript2.mim',
    'hi-itrans': 'hi-itrans.mim',
    'hi-optitransv2': 'hi-optitransv2.mim',
    'hi-phonetic': 'hi-phonetic.mim',
    'hi-remington': 'hi-remington.mim',
    'hi-typewriter': 'hi-typewriter.mim',
    'hi-vedmata': 'hi-vedmata.mim',
    'hr-kbd': 'hr-kbd.mim',
    'hu-rovas-post': 'hu-rovas-post.mim',
    'hy-kbd': 'hy-kbd.mim',
    'ii-phonetic': 'ii-phonetic.mim',
    'iu-phonetic': 'iu-phonetic.mim',
    'ja-anthy': 'ja-anthy.mim',
    'ja-tcode': 'ja-tcode.mim',
    'ja-trycode': 'ja-trycode.mim',
    'ka-kbd': 'ka-kbd.mim',
    'kk-arabic': 'kk-arabic.mim',
    'kk-kbd': 'kk-kbd.mim',
    'km-yannis': 'km-yannis.mim',
    'kn-inscript': 'kn-inscript.mim',
    'kn-inscript2': 'kn-inscript2.mim',
    'kn-itrans': 'kn-itrans.mim',
    'kn-kgp': 'kn-kgp.mim',
    'kn-optitransv2': 'kn-optitransv2.mim',
    'kn-typewriter': 'kn-typewriter.mim',
    'ko-han2': 'ko-han2.mim',
    'ko-romaja': 'ko-romaja.mim',
    'kok-inscript2': 'kok-inscript2-deva.mim',
    'ks-inscript': 'ks-inscript.mim',
    'ks-kbd': 'ks-kbd.mim',
    't-latn-post': 'latn-post.mim',
    't-latn-pre': 'latn-pre.mim',
    't-latn1-pre': 'latn1-pre.mim',
    'lo-kbd': 'lo-kbd.mim',
    'lo-lrt': 'lo-lrt.mim',
    't-lsymbol': 'lsymbol.mim',
    'mai-inscript': 'mai-inscript.mim',
    'mai-inscript2': 'mai-inscript2.mim',
    't-math-latex': 'math-latex.mim',
    'mr-minglish': 'minglish.mim',
    'ml-enhanced-inscript': 'ml-enhanced-inscript.mim',
    'ml-inscript': 'ml-inscript.mim',
    'ml-inscript2': 'ml-inscript2.mim',
    'ml-itrans': 'ml-itrans.mim',
    'ml-mozhi': 'ml-mozhi.mim',
    'ml-remington': 'ml-remington.mim',
    'ml-swanalekha': 'ml-swanalekha.mim',
    'mni-inscript2': 'mni-inscript2-beng.mim',
    'mni-inscript2': 'mni-inscript2-mtei.mim',
    'mr-inscript': 'mr-inscript.mim',
    'mr-inscript2': 'mr-inscript2.mim',
    'mr-itrans': 'mr-itrans.mim',
    'mr-phonetic': 'mr-phonetic.mim',
    'mr-remington': 'mr-remington.mim',
    'mr-typewriter': 'mr-typewriter.mim',
    'my-kbd': 'my-kbd.mim',
    'ne-inscript2': 'ne-inscript2-deva.mim',
    'ne-rom-translit': 'ne-rom-translit.mim',
    'ne-rom': 'ne-rom.mim',
    'ne-trad-ttf': 'ne-trad-ttf.mim',
    'ne-trad': 'ne-trad.mim',
    'nsk-phonetic': 'nsk-phonetic.mim',
    'oj-phonetic': 'oj-phonetic.mim',
    'or-inscript': 'or-inscript.mim',
    'or-inscript2': 'or-inscript2.mim',
    'or-itrans': 'or-itrans.mim',
    'or-phonetic': 'or-phonetic.mim',
    'pa-anmollipi': 'pa-anmollipi.mim',
    'pa-inscript': 'pa-inscript.mim',
    'pa-inscript2': 'pa-inscript2-guru.mim',
    'pa-itrans': 'pa-itrans.mim',
    'pa-jhelum': 'pa-jhelum.mim',
    'pa-phonetic': 'pa-phonetic.mim',
    'ps-phonetic': 'ps-phonetic.mim',
    't-rfc1345': 'rfc1345.mim',
    'ru-kbd': 'ru-kbd.mim',
    'ru-phonetic': 'ru-phonetic.mim',
    'ru-translit': 'ru-translit.mim',
    'ru-yawerty': 'ru-yawerty.mim',
    'sa-harvard-kyoto': 'sa-harvard-kyoto.mim',
    'sa-IAST': 'sa-iast.mim',
    'sa-inscript2': 'sa-inscript2.mim',
    'sa-itrans': 'sa-itrans.mim',
    'sat-inscript2': 'sat-inscript2-deva.mim',
    'sat-inscript2': 'sat-inscript2-olck.mim',
    'sd-inscript': 'sd-inscript.mim',
    'sd-inscript2': 'sd-inscript2-deva.mim',
    'si-phonetic-dynamic': 'si-phonetic-dynamic.mim',
    'si-samanala': 'si-samanala.mim',
    'si-singlish': 'si-singlish.mim',
    'si-sumihiri': 'si-sumihiri.mim',
    'si-transliteration': 'si-trans.mim',
    'si-wijesekera': 'si-wijesekera.mim',
    'sk-kbd': 'sk-kbd.mim',
    'sr-kbd': 'sr-kbd.mim',
    't-ssymbol': 'ssymbol.mim',
    'sv-post': 'sv-post.mim',
    't-syrc-phonetic': 'syrc-phonetic.mim',
    'ta-inscript': 'ta-inscript.mim',
    'ta-inscript2': 'ta-inscript2.mim',
    'ta-itrans': 'ta-itrans.mim',
    'ta-lk-renganathan': 'ta-lk-renganathan.mim',
    'ta-phonetic': 'ta-phonetic.mim',
    'ta-tamil99': 'ta-tamil99.mim',
    'ta-typewriter': 'ta-typewriter.mim',
    'ta-vutam': 'ta-vutam.mim',
    'tai-sonla-kbd': 'tai-sonla.mim',
    'te-apple': 'te-apple.mim',
    'te-inscript': 'te-inscript.mim',
    'te-inscript2': 'te-inscript2.mim',
    'te-itrans': 'te-itrans.mim',
    'te-pothana': 'te-pothana.mim',
    'te-rts': 'te-rts.mim',
    'te-sarala': 'te-sarala.mim',
    'th-kesmanee': 'th-kesmanee.mim',
    'th-pattachote': 'th-pattachote.mim',
    'th-tis820': 'th-tis820.mim',
    'ug-kbd': 'ug-kbd.mim',
    'uk-kbd': 'uk-kbd.mim',
    't-unicode': 'unicode.mim',
    'ur-phonetic': 'ur-phonetic.mim',
    'uz-kbd': 'uz-kbd.mim',
    # 't-nil vi-base': 'vi-base.mim', # not useful
    'vi-han': 'vi-han.mim',
    'vi-nomvni': 'vi-nom-vni.mim',
    'vi-nomtelex': 'vi-nom.mim',
    'vi-tcvn': 'vi-tcvn.mim',
    'vi-telex': 'vi-telex.mim',
    'vi-viqr': 'vi-viqr.mim',
    'vi-vni': 'vi-vni.mim',
    'yi-yivo': 'yi-yivo.mim',
    'zh-bopomofo': 'zh-bopomofo.mim',
    'zh-cangjie': 'zh-cangjie.mim',
    'zh-pinyin-vi': 'zh-pinyin-vi.mim',
    'zh-pinyin': 'zh-pinyin.mim',
    'zh-py-b5': 'zh-py-b5.mim',
    'zh-py-gb': 'zh-py-gb.mim',
    'zh-py': 'zh-py.mim',
    'zh-quick': 'zh-quick.mim',
    'zh-tonepy-b5': 'zh-tonepy-b5.mim',
    'zh-tonepy-gb': 'zh-tonepy-gb.mim',
    'zh-tonepy': 'zh-tonepy.mim',
    # 't-nil-zh-util': 'zh-util.mim', # not useful
    'zh-zhuyin': 'zh-zhuyin.mim',
}

SPANISH_419_LOCALES = (
    'es_AR', 'es_MX', 'es_BO', 'es_CL', 'es_CO', 'es_CR',
    'es_CU', 'es_DO', 'es_EC', 'es_GT', 'es_HN', 'es_NI',
    'es_PA', 'es_PE', 'es_PR', 'es_PY', 'es_SV', 'es_US',
    'es_UY', 'es_VE',)

def expand_languages(languages):
    '''Expands the given list of languages by including fallbacks.

    Returns a possibly longer list of languages by adding
    aliases and fallbacks.

    :param languages: A list of languages (or locale names)
    :type languages: List of strings
    :rtype: List  of strings

    Examples:

    >>> expand_languages(['es_MX', 'es_ES', 'ja_JP'])
    ['es_MX', 'es_419', 'es', 'es_ES', 'es', 'ja_JP', 'ja', 'en']

    >>> expand_languages(['zh_Hant', 'zh_CN', 'zh_TW', 'zh_SG', 'zh_HK', 'zh_MO'])
    ['zh_Hant', 'zh_CN', 'zh', 'zh_TW', 'zh_Hant', 'zh_SG', 'zh', 'zh_HK', 'zh_Hant', 'zh_MO', 'zh_Hant', 'en']

    >>> expand_languages(['en_GB', 'en'])
    ['en_GB', 'en_001', 'en', 'en', 'en_001']
    '''
    expanded_languages = []
    for language in languages:
        expanded_languages.append(language)
        if language in SPANISH_419_LOCALES:
            expanded_languages.append('es_419')
        if language in ('zh_TW', 'zh_HK', 'zh_MO'):
            expanded_languages.append('zh_Hant')
        if language[:2] == 'en':
            expanded_languages.append('en_001')
        if (language not in ('zh_TW', 'zh_HK', 'zh_MO', 'zh_Hant')
                and language.split('_')[:1] != [language]):
            expanded_languages += language.split('_')[:1]
    if 'en' not in expanded_languages:
        expanded_languages.append('en')
    return expanded_languages

def lstrip_token(token):
    '''Strips some characters from the left side of a token

    Characters which have a type listed in CATEGORIES_TO_STRIP_FROM_TOKENS
    are stripped from the left side of a token.

    The stripped token is returned.

    :param token: The token where characters may be stripped from
    :type token: String
    :rtype: String

    Examples:

    >>> lstrip_token(".'foo'.")
    "foo'."
    '''
    token = token.lstrip()
    while (len(token) > 0
           and
           unicodedata.category(token[0]) in CATEGORIES_TO_STRIP_FROM_TOKENS):
        token = token[1:]
    return token

def rstrip_token(token):
    '''Strips some characters from the right side of a token

    Characters which have a type listed in CATEGORIES_TO_STRIP_FROM_TOKENS
    are stripped from the right side of a token.

    The stripped token is returned.

    :param token: The token where characters may be stripped from
    :type token: String
    :rtype: String

    Examples:

    >>> rstrip_token(".'foo'.")
    ".'foo"
    '''
    token = token.rstrip()
    while (len(token) > 0
           and
           unicodedata.category(token[-1]) in CATEGORIES_TO_STRIP_FROM_TOKENS):
        token = token[0:-1]
    return token

def strip_token(token):
    '''Strips some characters from both sides of a token

    Characters which have a type listed in CATEGORIES_TO_STRIP_FROM_TOKENS
    are stripped from both sides of a token.

    The stripped token is returned.

    :param token: The token where characters may be stripped from
    :type token: String
    :rtype: String

    Examples:

    >>> strip_token(".'foo'.")
    'foo'
    '''
    return rstrip_token(lstrip_token(token))

def tokenize(text):
    '''Splits a text into tokens

    Returns a list tokens

    :param text: The text to tokenize
    :type text: String
    :rtype: List of strings
    '''
    pattern = re.compile(r'[\s]+')
    tokens = []
    for word in pattern.split(text.strip()):
        tokens.append(strip_token(word))
    return tokens

def is_ascii(text):
    '''Checks whether all characters in text are ASCII characters

    Returns “True” if the text is all ASCII, “False” if not.

    :param text: The text to check
    :type text: string
    :rtype: bool

    Examples:

    >>> is_ascii('Abc')
    True

    >>> is_ascii('Naïve')
    False
    '''
    try:
        text.encode('ascii')
    except UnicodeEncodeError:
        return False
    else:
        return True

# Mapping of Unicode ordinals to Unicode ordinals, strings, or None.
# Unmapped characters are left untouched. Characters mapped to None
# are deleted.

TRANS_TABLE = {
    ord('ẞ'): 'SS',
    ord('ß'): 'ss',
    ord('Ø'): 'O',
    ord('ø'): 'o',
    ord('Æ'): 'AE',
    ord('æ'): 'ae',
    ord('Œ'): 'OE',
    ord('œ'): 'oe',
    ord('Ł'): 'L',
    ord('ł'): 'l',
}

def remove_accents(text):
    '''Removes accents from the text

    Returns the text with all accents removed

    Using “from unidecode import unidecode” is more
    sophisticated, but I am not sure whether I can require
    “unidecode”.

    :param text: The text to change
    :type text: string
    :rtype: string

    Examples:

    >>> remove_accents('Ångstrøm')
    'Angstrom'

    >>> remove_accents('ÅÆæŒœĳøßẞü')
    'AAEaeOEoeijossSSu'

    '''
    return ''.join([
        x for x in unicodedata.normalize('NFKD', text)
        if unicodedata.category(x) != 'Mn']).translate(TRANS_TABLE)

def is_right_to_left(text):
    '''Check whether a text is right-to-left text or not

    :param text: The text to check
    :type text: string
    :rtype: boolean

    See: http://unicode.org/reports/tr9/#P2

    TR9> In each paragraph, find the first character of type L, AL, or R
    TR9> while skipping over any characters between an isolate initiator
    TR9> and its matching PDI or, if it has no matching PDI, the end of the
    TR9> paragraph

    Examples:

    >>> is_right_to_left('Hallo!')
    False

    >>> is_right_to_left('﷼')
    True

    >>> is_right_to_left('⁨﷼⁩')
    False

    >>> is_right_to_left('⁨﷼⁩﷼')
    True

    >>> is_right_to_left('a⁨﷼⁩﷼')
    False

    >>> is_right_to_left('⁨a⁩⁨﷼⁩﷼')
    True
    '''
    skip = False
    for char in text:
        bidi_cat = unicodedata.bidirectional(char)
        if skip and bidi_cat != 'PDI':
            continue
        skip = False
        if bidi_cat in ('AL', 'R'):
            return True
        if bidi_cat == 'L':
            return False
        if bidi_cat in ('LRI', 'RLI', 'FSI'):
            skip = True
    return False

def bidi_embed(text):
    '''Embed the text using explicit directional embedding

    Returns “RLE + text + PDF” if the text is right-to-left,
    if not it returns “LRE + text + PDF”.

    :param text: The text to embed
    :type text: string
    :rtype: string

    See: http://unicode.org/reports/tr9/#Explicit_Directional_Embeddings

    Examples:

    >>> bidi_embed('a')
    '‪a‬'

    >>> bidi_embed('﷼')
    '‫﷼‬'
    '''
    if is_right_to_left(text):
        return chr(0x202B) + text + chr(0x202C) # RLE + text + PDF
    else:
        return chr(0x202A) + text + chr(0x202C) # LRE + text + PDF

def contains_letter(text):
    '''Returns whether “text” contains a “letter” type character

    :param text: The text to check
    :type text: string
    :rtype: boolean

    Examples:

    >>> contains_letter('Hi!')
    True

    >>> contains_letter(':-)')
    False
    '''
    for char in text:
        category = unicodedata.category(char)
        if category in ('Ll', 'Lu', 'Lo',):
            return True
    return False

def variant_to_value(variant):
    '''
    Convert a GLib variant to a value
    '''
    # pylint: disable=unidiomatic-typecheck
    if type(variant) != GLib.Variant:
        return variant
    type_string = variant.get_type_string()
    if type_string == 's':
        return variant.get_string()
    elif type_string == 'i':
        return variant.get_int32()
    elif type_string == 'b':
        return variant.get_boolean()
    elif type_string == 'as':
        # In the latest pygobject3 3.3.4 or later, g_variant_dup_strv
        # returns the allocated strv but in the previous release,
        # it returned the tuple of (strv, length)
        if type(GLib.Variant.new_strv([]).dup_strv()) == tuple:
            return variant.dup_strv()[0]
        else:
            return variant.dup_strv()
    else:
        print('error: unknown variant type: %s' %type_string)
    return variant

def find_hunspell_dictionary(language):
    '''
    Find the hunspell dictionary file for a language


    :param language: The language of the dictionary to search for
    :type language: String
    :rtype: tuple of the form (dic_path, aff_path) where
            dic_path is the full path of the .dic file found
            and aff_path is the full path of the .aff file found.
            If no dictionary can be found for the requested language,
            the return value is ('', '').
    '''
    dirnames = [
        '/usr/share/hunspell',
        '/usr/share/myspell',
        '/usr/share/myspell/dicts',
        '/usr/local/share/hunspell', # On FreeBSD the dictionaries are here
        '/usr/local/share/myspell',
        '/usr/local/share/myspell/dicts',
    ]
    dic_path = ''
    aff_path = ''
    for language in expand_languages([language]):
        for dirname in dirnames:
            if os.path.isfile(os.path.join(dirname, language + '.dic')):
                dic_path = os.path.join(dirname, language + '.dic')
                aff_path = os.path.join(dirname, language + '.aff')
                return (dic_path, aff_path)
    sys.stderr.write(
        'find_hunspell_dictionary(): '
        + 'No file %s.dic found in %s\n'
        %(language, dirnames))
    return ('', '')

def get_hunspell_dictionary_wordlist(language):
    '''
    Open the hunspell dictionary file for a language

    :param language: The language of the dictionary to open
    :type language: String
    :rtype: tuple of the form (dic_path, dictionary_encoding, wordlist) where
            dic_path is the full path of the dictionary file found,
            dictionary_encoding is the encoding of that dictionary file,
            and wordlist is a list of words found in that file.
            If no dictionary can be found for the requested language,
            the return value is ('', '', []).
    '''
    (dic_path, aff_path) = find_hunspell_dictionary(language)
    if not dic_path:
        return ('', '', [])
    sys.stderr.write(
        'get_hunspell_dictionary_wordlist(): '
        + '%s file found.\n'
        %dic_path)
    dictionary_encoding = 'UTF-8'
    if os.path.isfile(aff_path):
        aff_buffer = ''
        try:
            aff_buffer = open(
                aff_path,
                mode='r',
                encoding='ISO-8859-1',
                errors='ignore').read().replace('\r\n', '\n')
        except (FileNotFoundError, PermissionError):
            traceback.print_exc()
        except:
            sys.stderr.write(
                'get_hunspell_dictionary_wordlist() '
                + 'Unexpected error loading .aff File: %s\n'
                %aff_path)
            traceback.print_exc()
        if aff_buffer:
            encoding_pattern = re.compile(
                r'^[\s]*SET[\s]+(?P<encoding>[-a-zA-Z0-9_]+)[\s]*$',
                re.MULTILINE)
            match = encoding_pattern.search(aff_buffer)
            if match:
                dictionary_encoding = match.group('encoding')
                sys.stderr.write(
                    'get_hunspell_dictionary_wordlist(): '
                    + 'dictionary encoding=%s found in %s\n'
                    %(dictionary_encoding, aff_path))
            else:
                sys.stderr.write(
                    'get_hunspell_dictionary_wordlist(): '
                    + 'No encoding found in %s\n'
                    %aff_path)
    else:
        sys.stderr.write(
            'get_hunspell_dictionary_wordlist(): '
            + '%s file missing. Trying to open %s using %s encoding\n'
            %(aff_path, dic_path, dictionary_encoding))
    dic_buffer = ''
    try:
        dic_buffer = open(
            dic_path, encoding=dictionary_encoding).readlines()
    except (UnicodeDecodeError, FileNotFoundError, PermissionError):
        sys.stderr.write(
            'get_hunspell_dictionary_wordlist(): '
            + 'loading %s as %s encoding failed, '
            %(dic_path, dictionary_encoding)
            + 'fall back to ISO-8859-1.\n')
        dictionary_encoding = 'ISO-8859-1'
        try:
            dic_buffer = open(
                dic_path,
                encoding=dictionary_encoding).readlines()
        except (UnicodeDecodeError, FileNotFoundError, PermissionError):
            sys.stderr.write(
                'get_hunspell_dictionary_wordlist(): '
                + 'loading %s as %s encoding failed, '
                %(dic_path, dictionary_encoding)
                + 'giving up.\n')
            traceback.print_exc()
            return ('', '', [])
        except:
            sys.stderr.write(
                'get_hunspell_dictionary_wordlist(): '
                + 'Unexpected error loading .dic File: %s\n' %dic_path)
            traceback.print_exc()
            return ('', '', [])
    except:
        sys.stderr.write(
            'get_hunspell_dictionary_wordlist(): '
            + 'Unexpected error loading .dic File: %s\n' %dic_path)
        traceback.print_exc()
        return ('', '', [])
    if not dic_buffer:
        return ('', '', [])
    sys.stderr.write(
        'get_hunspell_dictionary_wordlist(): '
        + 'Successfully loaded %s using %s encoding.\n'
        %(dic_path, dictionary_encoding))
    # http://pwet.fr/man/linux/fichiers_speciaux/hunspell says:
    #
    # > A dictionary file (*.dic) contains a list of words, one per
    # > line. The first line of the dictionaries (except personal
    # > dictionaries) contains the word count. Each word may
    # > optionally be followed by a slash ("/") and one or more
    # > flags, which represents affixes or special attributes.
    #
    # Some dictionaries, like fr_FR.dic and pt_PT.dic also contain
    # some lines where words are followed by a tab and some stuff.
    # For example, pt_PT.dic contains lines like:
    #
    # abaixo	[CAT=adv,SUBCAT=lugar]
    # abalada/p	[CAT=nc,G=f,N=s]
    #
    # and fr_FR.dic contains lines like:
    #
    # différemment	8
    # différence/1	2
    #
    # Therefore, remove everthing following a '/' or a tab from a line
    # to make the memory use of the word list a bit smaller and the
    # regular expressions we use later to match words in the
    # dictionary slightly simpler and maybe a tiny bit faster:
    word_list = [
        unicodedata.normalize(
            NORMALIZATION_FORM_INTERNAL,
            re.sub(r'[/\t].*', '', x.replace('\n', '')))
        for x in dic_buffer
    ]
    return (dic_path, dictionary_encoding, word_list)

def get_ime_help(ime_name):
    '''
    Get help for an input method.

    :param ime_name: Name of the input method
    :type ime_name: String
    :rtype: tuple of the form (path, title, description, full_contents, error)
            where “path” is the full path of the file implementing the input method,
            “title” is title of the input method,
            “description” is the description of the input method,
            and full_contents is the full content of the file implementing
            the input method.
            “error” is empty if getting help for the input method was successfull,
            otherwise it might contain an error message.

    “NoIme” is handled as a special case.

    Examples:

    >>> get_ime_help('latn-post')[1]
    'Latin-post'

    >>> get_ime_help('latn-post')[4]
    ''

    >>> get_ime_help('mr-itrans')[1]
    'क'

    >>> get_ime_help('ko-romaja')[1]
    '로마자'
    '''
    path = ''
    title = ''
    description = ''
    full_contents = ''
    error = ''
    if not ime_name:
        return ('', '', '', '', '')
    if ime_name == 'NoIme':
        title = _('Native Keyboard')
        description = _(
            'Direct keyboard input. This is not really an input method, '
            + 'it uses directly whatever comes from the current keyboard layout '
            + 'without any further changes. So no transliteration or composing '
            + 'is done here.')
        return (path, title, description, full_contents, error)
    if ime_name in M17N_INPUT_METHODS:
        mim_file = M17N_INPUT_METHODS[ime_name]
    else:
        mim_file = ime_name+'.mim'
    dirnames = [
        '/usr/share/m17n',
        '/usr/local/share/m17n', # On FreeBSD, the .mim files are here
    ]
    m17n_dir = ''
    for dirname in dirnames:
        if os.path.isdir(dirname):
            m17n_dir = dirname
    if not m17n_dir:
        # Maybe don’t mark this error message as translatable, it should
        # never happen in practice:
        return ('', '', '', '', ('m17n dir not found, tried: %s' %dirnames))
    path = os.path.join(m17n_dir, mim_file)
    try:
        with open(path, mode='r', encoding='UTF-8', errors='ignore') as ime_file:
            full_contents = ime_file.read()
    except FileNotFoundError:
        return ('', '', '', '', _('File not found'))
    except PermissionError:
        return ('', '', '', '', _('Permission error'))
    except UnicodeDecodeError:
        return ('', '', '', '', _('Unicode decoding error'))
    except:
        return ('', '', '', '', _('Unknown error'))
    if full_contents:
        title_pattern = re.compile(
            r'\([\s]*title[\s]*"(?P<title>.+?)(?<!\\)"[\s]*\)',
            re.DOTALL|re.MULTILINE|re.UNICODE)
        match = title_pattern.search(full_contents)
        if match:
            title = match.group('title').replace('\\"', '"')
        description_pattern = re.compile(
            r'\([\s]*description[\s]*"(?P<description>.+?)(?<!\\)"[\s]*\)',
            re.DOTALL|re.MULTILINE|re.UNICODE)
        match = description_pattern.search(full_contents)
        if match:
            description = match.group('description').replace('\\"', '"')
    return (path, title, description, full_contents, error)


def xdg_save_data_path(*resource):
    '''
    Compatibility function for systems which do not have pyxdg.
    (For example openSUSE Leap 42.1)
    '''
    if IMPORT_XDG_BASEDIRECTORY_SUCCESSFUL:
        return xdg.BaseDirectory.save_data_path(*resource)
    else:
        # Replicate implementation of xdg.BaseDirectory.save_data_path
        # here:
        xdg_data_home = os.environ.get('XDG_DATA_HOME') or os.path.join(
            os.path.expanduser('~'), '.local', 'share')
        resource = os.path.join(*resource)
        assert not resource.startswith('/')
        path = os.path.join(xdg_data_home, resource)
        if not os.path.isdir(path):
            os.makedirs(path)
        return path

class ItbAboutDialog(Gtk.AboutDialog):
    def  __init__(self):
        Gtk.AboutDialog.__init__(self)
        self.set_modal(True)
        # An empty string in aboutdialog.set_logo_icon_name('')
        # prevents an ugly default icon to be shown. We don’t yet
        # have nice icons for ibus-typing-booster.
        self.set_logo_icon_name('')
        self.set_title(
            '🚀 ibus-typing-booster %s' %version.get_version())
        self.set_program_name(
            '🚀 ibus-typing-booster')
        self.set_version(version.get_version())
        self.set_comments(
            _('A completion input method to speedup typing.'))
        self.set_copyright(
            'Copyright © 2017 Mike FABIAN')
        self.set_authors([
            'Mike FABIAN <maiku.fabian@gmail.com>',
            'Anish Patil <anish.developer@gmail.com>',
            ])
        self.set_translator_credits(
            # Translators: put your names here, one name per line.
            _('translator-credits'))
        # self.set_artists('')
        self.set_documenters([
            'Mike FABIAN <maiku.fabian@gmail.com>',
            ])
        self.set_website(
            'http://mike-fabian.github.io/ibus-typing-booster')
        self.set_website_label(
            _('Online documentation:')
            + ' ' + 'http://mike-fabian.github.io/ibus-typing-booster')
        self.set_license('''
        This program is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.

        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with this program.  If not, see <http://www.gnu.org/licenses/>
        ''')
        self.set_wrap_license(True)
        # overrides the above .set_license()
        self.set_license_type(Gtk.License.GPL_3_0)
        self.connect('response', self.on_close_aboutdialog)
        self.set_transient_for(self)
        self.show()

    def on_close_aboutdialog( # pylint: disable=no-self-use
            self, dummy_about_dialog, dummy_response):
        '''
        The “About” dialog has been closed by the user

        :param dummy_about_dialog: The “About” dialog
        :type dummy_about_dialog: GtkDialog object
        :param dummy_response: The response when the “About” dialog was closed
        :type dummy_response: Gtk.ResponseType enum
        '''
        self.destroy()

if __name__ == "__main__":
    import doctest
    (FAILED, ATTEMPTED) = doctest.testmod()
    if FAILED:
        sys.exit(1)
    else:
        sys.exit(0)
