forked from Mirrors/apostrophe
Updated pygtkspellcheck to 4.0.5
parent
ac1774c848
commit
db427bf1fb
|
@ -16,29 +16,25 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Python 2/3 unicode
|
||||
import sys
|
||||
if sys.version_info.major == 3:
|
||||
u = lambda x: x
|
||||
else:
|
||||
u = lambda x: x.decode('utf-8')
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Metadata
|
||||
__version__ = '3.0'
|
||||
__version__ = '4.0.5'
|
||||
__project__ = 'Python GTK Spellcheck'
|
||||
__short_name__ = 'pygtkspellcheck'
|
||||
__authors__ = u('Maximilian Köhl & Carlos Jenkins')
|
||||
__emails__ = u('linuxmaxi@googlemail.com & carlos@jenkins.co.cr')
|
||||
__authors__ = 'Maximilian Köhl & Carlos Jenkins'
|
||||
__emails__ = 'linuxmaxi@googlemail.com & carlos@jenkins.co.cr'
|
||||
__website__ = 'http://koehlma.github.com/projects/pygtkspellcheck.html'
|
||||
__download_url__ = 'https://github.com/koehlma/pygtkspellcheck/tarball/master'
|
||||
__source__ = 'https://github.com/koehlma/pygtkspellcheck/'
|
||||
__vcs__ = 'git://github.com/koehlma/pygtkspellcheck.git'
|
||||
__copyright__ = u('2012, Maximilian Köhl & Carlos Jenkins')
|
||||
__desc_short__ = 'A simple but quite powerful Python spell checking library for GtkTextViews based on Enchant.'
|
||||
__desc_long__ = ('It supports both Gtk\'s Python bindings, PyGObject and'
|
||||
'PyGtk, and for both Python 2 and 3 with automatic switching'
|
||||
'and binding autodetection. For automatic translation of the'
|
||||
'user interface it can use GEdit\'s translation files.')
|
||||
__copyright__ = '2012, Maximilian Köhl & Carlos Jenkins'
|
||||
__desc_short__ = ('a simple but quite powerful Python spell checking library '
|
||||
'for GtkTextViews based on Enchant')
|
||||
__desc_long__ = ('A simple but quite powerful spellchecking library written in '
|
||||
'pure Python for Gtk based on Enchant. It supports PyGObject '
|
||||
'as well as PyGtk for Python 2 and 3 with automatic switching '
|
||||
'and binding detection. For automatic translation of the user '
|
||||
'interface it can use Gedit’s translation files.')
|
||||
|
||||
__metadata__ = {'__version__' : __version__,
|
||||
'__project__' : __project__,
|
||||
|
@ -53,5 +49,5 @@ __metadata__ = {'__version__' : __version__,
|
|||
'__desc_short__' : __desc_short__,
|
||||
'__desc_long__' : __desc_long__}
|
||||
|
||||
# import SpellChecker class
|
||||
from . spellcheck import SpellChecker
|
||||
from gtkspellcheck.spellcheck import (SpellChecker, NoDictionariesFound,
|
||||
NoGtkBindingFound)
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2012, Carlos Jenkins <carlos@jenkins.co.cr>
|
||||
# Copyright (C) 2012-2016, Maximilian Köhl <mail@koehlma.de>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
This module extracts the .dic and .aff (Hunspell) dictionaries from any given
|
||||
.oxt extension.
|
||||
|
||||
Extensions could be found at:
|
||||
|
||||
http://extensions.services.openoffice.org/dictionary
|
||||
"""
|
||||
|
||||
import functools
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import warnings
|
||||
import xml.dom.minidom
|
||||
import xml.parsers.expat
|
||||
import zipfile
|
||||
|
||||
# enable deprecation warnings
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
|
||||
# public objects
|
||||
__all__ = ['extract_oxt', 'batch_extract', 'BadXml', 'BadExtensionFile',
|
||||
'ExtractPathIsNoDirectory', 'BATCH_SUCCESS', 'BATCH_ERROR',
|
||||
'BATCH_WARNING']
|
||||
|
||||
# logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# translation
|
||||
locale_name = 'py{}gtkspellcheck'.format(sys.version_info.major)
|
||||
_ = gettext.translation(locale_name, fallback=True).gettext
|
||||
|
||||
class BadXml(Exception):
|
||||
"""
|
||||
The XML dictionary registry is not valid XML.
|
||||
"""
|
||||
|
||||
class BadExtensionFile(Exception):
|
||||
"""
|
||||
The extension has a wrong file format, should be a ZIP file.
|
||||
"""
|
||||
|
||||
class ExtractPathIsNoDirectory(Exception):
|
||||
"""
|
||||
The given `extract_path` is no directory.
|
||||
"""
|
||||
|
||||
|
||||
def find_dictionaries(registry):
|
||||
def oor_name(name, element):
|
||||
return element.attributes['oor:name'].value.lower() == name
|
||||
|
||||
def get_property(name, properties):
|
||||
property = list(filter(functools.partial(oor_name, name),
|
||||
properties))
|
||||
if property:
|
||||
return property[0].getElementsByTagName('value')[0]
|
||||
|
||||
result = []
|
||||
|
||||
# find all "node" elements which have "dictionaries" as "oor:name" attribute
|
||||
for dictionaries in filter(functools.partial(oor_name, 'dictionaries'),
|
||||
registry.getElementsByTagName('node')):
|
||||
# for all "node" elements in this dictionary nodes
|
||||
for dictionary in dictionaries.getElementsByTagName('node'):
|
||||
# get all "prop" elements
|
||||
properties = dictionary.getElementsByTagName('prop')
|
||||
# get the format property as text
|
||||
format = get_property('format', properties).firstChild.data.strip()
|
||||
if format and format == 'DICT_SPELL':
|
||||
# find the locations property
|
||||
locations = get_property('locations', properties)
|
||||
# if the location property is text:
|
||||
# %origin%/dictionary.aff %origin%/dictionary.dic
|
||||
if locations.firstChild.nodeType == xml.dom.Node.TEXT_NODE:
|
||||
locations = locations.firstChild.data
|
||||
locations = locations.replace('%origin%/', '').strip()
|
||||
result.append(locations.split())
|
||||
# otherwise:
|
||||
# <i>%origin%/dictionary.aff</i> <i>%origin%/dictionary.dic</i>
|
||||
else:
|
||||
locations = [item.firshChild.data.replace('%origin%/', '') \
|
||||
.strip() for item in
|
||||
locations.getElementsByTagName('it')]
|
||||
result.append(locations)
|
||||
|
||||
return result
|
||||
|
||||
def extract(filename, target, override=False):
|
||||
"""
|
||||
Extract Hunspell dictionaries out of LibreOffice ``.oxt`` extensions.
|
||||
|
||||
:param filename: path to the ``.oxt`` extension
|
||||
:param target: path to extract Hunspell dictionaries to
|
||||
:param override: override existing files in the target directory
|
||||
:rtype: list of the extracted dictionaries
|
||||
|
||||
This function extracts the Hunspell dictionaries (``.dic`` and ``.aff``
|
||||
files) from the given ``.oxt`` extension found to ``target``.
|
||||
|
||||
Extensions could be found at:
|
||||
|
||||
http://extensions.services.openoffice.org/dictionary
|
||||
"""
|
||||
# TODO 5.0: remove this function
|
||||
warnings.warn(('call to deprecated function "{}", '
|
||||
'moved to separate package "oxt_extract", '
|
||||
'will be removed in pygtkspellcheck 5.0').format(extract.__name__),
|
||||
category=DeprecationWarning)
|
||||
try:
|
||||
with zipfile.ZipFile(filename, 'r') as extension:
|
||||
files = extension.namelist()
|
||||
|
||||
registry = 'dictionaries.xcu'
|
||||
if not registry in files:
|
||||
for filename in files:
|
||||
if filename.lower().endswith(registry):
|
||||
registry = filename
|
||||
|
||||
if registry in files:
|
||||
registry = xml.dom.minidom.parse(extension.open(registry))
|
||||
dictionaries = find_dictionaries(registry)
|
||||
extracted = []
|
||||
for dictionary in dictionaries:
|
||||
for filename in dictionary:
|
||||
dict_file = os.path.join(target,
|
||||
os.path.basename(filename))
|
||||
if (not os.path.exists(dict_file)
|
||||
or (override and os.path.isfile(dict_file))):
|
||||
if filename in files:
|
||||
with open(dict_file, 'wb') as _target:
|
||||
with extension.open(filename, 'r') as _source:
|
||||
extracted.append(os.path.basename(filename))
|
||||
_target.write(_source.read())
|
||||
else:
|
||||
logger.warning('dictionary exists in registry '
|
||||
'but not in the extension zip')
|
||||
else:
|
||||
logging.warning(('dictionary file "{}" already exists '
|
||||
'and not overriding it'
|
||||
).format(dict_file))
|
||||
return extracted
|
||||
except zipfile.BadZipfile:
|
||||
raise BadExtensionFile('extension is not a valid ZIP file')
|
||||
except xml.parsers.expat.ExpatError:
|
||||
raise BadXml('dictionary registry is not valid XML')
|
||||
|
||||
BATCH_SUCCESS = 'success'
|
||||
BATCH_ERROR = 'error'
|
||||
BATCH_WARNING = 'warning'
|
||||
|
||||
def batch_extract(oxt_path, extract_path, override=False, move_path=None):
|
||||
"""
|
||||
Uncompress, read and install LibreOffice ``.oxt`` dictionaries extensions.
|
||||
|
||||
:param oxt_path: path to a directory containing the ``.oxt`` extensions
|
||||
:param extract_path: path to extract Hunspell dictionaries files to
|
||||
:param override: override already existing files
|
||||
:param move_path: optional path to move the ``.oxt`` files after processing
|
||||
:rtype: generator over all extensions, yielding result, extension name,
|
||||
error, extracted dictionaries and translated error message - result
|
||||
would be :const:`BATCH_SUCCESS` for success, :const:`BATCH_ERROR` if
|
||||
some error happened or :const:`BATCH_WARNING` which contain some warning
|
||||
messages instead of errors
|
||||
|
||||
This function extracts the Hunspell dictionaries (``.dic`` and ``.aff``
|
||||
files) from all the ``.oxt`` extensions found on ``oxt_path`` directory to
|
||||
the ``extract_path`` directory.
|
||||
|
||||
Extensions could be found at:
|
||||
|
||||
http://extensions.services.openoffice.org/dictionary
|
||||
|
||||
In detail, this functions does the following:
|
||||
|
||||
1. find all the ``.oxt`` extension files within ``oxt_path``
|
||||
2. open (unzip) each extension
|
||||
3. find the dictionary definition file within (*dictionaries.xcu*)
|
||||
4. parse the dictionary definition file and locate the dictionaries files
|
||||
5. uncompress those files to ``extract_path``
|
||||
|
||||
|
||||
By default file overriding is disabled, set ``override`` parameter to True
|
||||
if you want to enable it. As additional option, each processed extension can
|
||||
be moved to ``move_path``.
|
||||
|
||||
Example::
|
||||
|
||||
for result, name, error, dictionaries, message in oxt_extract.batch_extract(...):
|
||||
if result == oxt_extract.BATCH_SUCCESS:
|
||||
print('successfully extracted extension "{}"'.format(name))
|
||||
elif result == oxt_extract.BATCH_ERROR:
|
||||
print('could not extract extension "{}"'.format(name))
|
||||
print(message)
|
||||
print('error {}'.format(error))
|
||||
elif result == oxt_extract.BATCH_WARNING:
|
||||
print('warning during processing extension "{}"'.format(name))
|
||||
print(message)
|
||||
print(error)
|
||||
|
||||
"""
|
||||
|
||||
# TODO 5.0: remove this function
|
||||
warnings.warn(('call to deprecated function "{}", '
|
||||
'moved to separate package "oxt_extract", '
|
||||
'will be removed in pygtkspellcheck 5.0').format(extract.__name__),
|
||||
category=DeprecationWarning)
|
||||
|
||||
# get the real, absolute and normalized path
|
||||
oxt_path = os.path.normpath(os.path.abspath(os.path.realpath(oxt_path)))
|
||||
|
||||
# check that the input directory exists
|
||||
if not os.path.isdir(oxt_path):
|
||||
return
|
||||
|
||||
# create extract directory if not exists
|
||||
if not os.path.exists(extract_path):
|
||||
os.makedirs(extract_path)
|
||||
|
||||
# check that the extract path is a directory
|
||||
if not os.path.isdir(extract_path):
|
||||
raise ExtractPathIsNoDirectory('extract path is not a valid directory')
|
||||
|
||||
# get all .oxt extension at given path
|
||||
oxt_files = [extension for extension in os.listdir(oxt_path)
|
||||
if extension.lower().endswith('.oxt')]
|
||||
|
||||
for extension_name in oxt_files:
|
||||
extension_path = os.path.join(oxt_path, extension_name)
|
||||
|
||||
try:
|
||||
dictionaries = extract(extension_path, extract_path, override)
|
||||
yield BATCH_SUCCESS, extension_name, None, dictionaries, ''
|
||||
except BadExtensionFile as error:
|
||||
logger.error(('extension "{}" is not a valid ZIP file'
|
||||
).format(extension_name))
|
||||
yield (BATCH_ERROR, extension_name, error, [],
|
||||
_('extension "{}" is not a valid ZIP file'
|
||||
).format(extension_name))
|
||||
except BadXml as error:
|
||||
logger.error(('extension "{}" has no valid XML dictionary registry'
|
||||
).format(extension_name))
|
||||
yield (BATCH_ERROR, extension_name, error, [],
|
||||
_('extension "{}" has no valid XML dictionary registry'
|
||||
).format(extension_name))
|
||||
|
||||
# move the extension after processing if user requires it
|
||||
if move_path is not None:
|
||||
# create move path if it doesn't exists
|
||||
if not os.path.exists(move_path):
|
||||
os.makedirs(move_path)
|
||||
# move to the given path only if it is a directory and target
|
||||
# doesn't exists
|
||||
if os.path.isdir(move_path):
|
||||
if (not os.path.exists(os.path.join(move_path, extension_name))
|
||||
or override):
|
||||
shutil.move(extension_path, move_path)
|
||||
else:
|
||||
logger.warning(('unable to move extension, file with same '
|
||||
'name exists within move_path'))
|
||||
yield (BATCH_WARNING, extension_name,
|
||||
('unable to move extension, file with same name '
|
||||
'exists within move_path'), [],
|
||||
_('unable to move extension, file with same name '
|
||||
'exists within move_path'))
|
||||
else:
|
||||
logger.warning(('unable to move extension, move_path is not a '
|
||||
'directory'))
|
||||
yield (BATCH_WARNING, extension_name,
|
||||
('unable to move extension, move_path is not a '
|
||||
'directory'), [],
|
||||
_('unable to move extension, move_path is not a '
|
||||
'directory'))
|
|
@ -28,13 +28,9 @@ import gettext
|
|||
import logging
|
||||
import re
|
||||
import sys
|
||||
import pickle
|
||||
import os
|
||||
|
||||
from ..helpers import get_media_path
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from ..pylocales import code_to_name
|
||||
from pylocales import code_to_name as _code_to_name
|
||||
from pylocales import LanguageNotFound, CountryNotFound
|
||||
|
||||
# public objects
|
||||
__all__ = ['SpellChecker', 'NoDictionariesFound', 'NoGtkBindingFound']
|
||||
|
@ -53,15 +49,25 @@ class NoGtkBindingFound(Exception):
|
|||
Could not find any loaded Gtk binding.
|
||||
"""
|
||||
|
||||
# find any loaded gtk binding
|
||||
if 'gi.repository.Gtk' in sys.modules:
|
||||
gtk = sys.modules['gi.repository.Gtk']
|
||||
_pygobject = True
|
||||
elif 'gtk' in sys.modules:
|
||||
gtk = sys.modules['gtk']
|
||||
_pygobject = False
|
||||
if sys.version_info.major == 3:
|
||||
_py3k = True
|
||||
else:
|
||||
raise NoGtkBindingFound('could not find any loaded Gtk binding')
|
||||
_py3k = False
|
||||
|
||||
if _py3k:
|
||||
# there is only the gi binding for Python 3
|
||||
from gi.repository import Gtk as gtk
|
||||
_pygobject = True
|
||||
else:
|
||||
# find any loaded gtk binding
|
||||
if 'gi.repository.Gtk' in sys.modules:
|
||||
gtk = sys.modules['gi.repository.Gtk']
|
||||
_pygobject = True
|
||||
elif 'gtk' in sys.modules:
|
||||
gtk = sys.modules['gtk']
|
||||
_pygobject = False
|
||||
else:
|
||||
raise NoGtkBindingFound('could not find any loaded Gtk binding')
|
||||
|
||||
# select base list class
|
||||
try:
|
||||
|
@ -70,10 +76,7 @@ try:
|
|||
except ImportError:
|
||||
_list = list
|
||||
|
||||
if sys.version_info.major == 3:
|
||||
_py3k = True
|
||||
else:
|
||||
_py3k = False
|
||||
|
||||
|
||||
# select base string
|
||||
if _py3k:
|
||||
|
@ -84,7 +87,8 @@ _GEDIT_MAP = {'Languages' : 'Languages',
|
|||
'Ignore All' : 'Ignore _All',
|
||||
'Suggestions' : 'Suggestions',
|
||||
'(no suggestions)' : '(no suggested words)',
|
||||
'Add "{}" to Dictionary' : 'Add w_ord'}
|
||||
'Add "{}" to Dictionary' : 'Add w_ord',
|
||||
'Unknown' : 'Unknown'}
|
||||
|
||||
# translation
|
||||
if gettext.find('gedit'):
|
||||
|
@ -94,6 +98,12 @@ else:
|
|||
locale_name = 'py{}gtkspellcheck'.format(sys.version_info.major)
|
||||
_ = gettext.translation(locale_name, fallback=True).gettext
|
||||
|
||||
def code_to_name(code, separator='_'):
|
||||
try:
|
||||
return _code_to_name(code, separator)
|
||||
except (LanguageNotFound, CountryNotFound):
|
||||
return '{} ({})'.format(_('Unknown'), code)
|
||||
|
||||
class SpellChecker(object):
|
||||
"""
|
||||
Main spellchecking class, everything important happens here.
|
||||
|
@ -173,7 +183,6 @@ class SpellChecker(object):
|
|||
|
||||
def __init__(self, view, language='en', prefix='gtkspellchecker',
|
||||
collapse=True, params={}):
|
||||
|
||||
self._view = view
|
||||
self.collapse = collapse
|
||||
self._view.connect('populate-popup',
|
||||
|
@ -181,12 +190,6 @@ class SpellChecker(object):
|
|||
self._view.connect('popup-menu', self._click_move_popup)
|
||||
self._view.connect('button-press-event', self._click_move_button)
|
||||
self._prefix = prefix
|
||||
if _pygobject:
|
||||
self._misspelled = gtk.TextTag.new('{}-misspelled'\
|
||||
.format(self._prefix))
|
||||
else:
|
||||
self._misspelled = gtk.TextTag('{}-misspelled'.format(self._prefix))
|
||||
self._misspelled.set_property('underline', 4)
|
||||
self._broker = enchant.Broker()
|
||||
for param, value in params.items(): self._broker.set_param(param, value)
|
||||
self.languages = SpellChecker._LanguageList.from_broker(self._broker)
|
||||
|
@ -219,26 +222,6 @@ class SpellChecker(object):
|
|||
self._enabled = True
|
||||
self.buffer_initialize()
|
||||
|
||||
self.notify_language_change_functions = []
|
||||
|
||||
self.frequency_dict = {}
|
||||
pp_pickled = 'pickled_dict'
|
||||
if pp_pickled and os.path.isfile(pp_pickled):
|
||||
f = open(pp_pickled, 'rb')
|
||||
self.frequency_dict = pickle.load(f)
|
||||
f.close()
|
||||
else:
|
||||
pp = get_media_path('wordlists/en_us_wordlist.xml')
|
||||
frequencies = ET.parse(pp)
|
||||
root = frequencies.getroot()
|
||||
for child in root:
|
||||
self.frequency_dict[child.text] = int(child.attrib['f'])
|
||||
f = open('pickled_dict', 'wb+')
|
||||
pickle.dump(self.frequency_dict, f)
|
||||
f.close()
|
||||
# print(self.frequency_dict)
|
||||
|
||||
|
||||
@property
|
||||
def language(self):
|
||||
"""
|
||||
|
@ -273,6 +256,12 @@ class SpellChecker(object):
|
|||
have associated a new GtkTextBuffer with the GtkTextView call this
|
||||
method.
|
||||
"""
|
||||
if _pygobject:
|
||||
self._misspelled = gtk.TextTag.new('{}-misspelled'\
|
||||
.format(self._prefix))
|
||||
else:
|
||||
self._misspelled = gtk.TextTag('{}-misspelled'.format(self._prefix))
|
||||
self._misspelled.set_property('underline', 4)
|
||||
self._buffer = self._view.get_buffer()
|
||||
self._buffer.connect('insert-text', self._before_text_insert)
|
||||
self._buffer.connect_after('insert-text', self._after_text_insert)
|
||||
|
@ -451,24 +440,15 @@ class SpellChecker(object):
|
|||
else:
|
||||
self._check_word(word_start, word_end)
|
||||
self._deferred_check = False
|
||||
|
||||
word_end.forward_word_end()
|
||||
word_end.backward_word_start()
|
||||
word_start = word_end.copy()
|
||||
|
||||
|
||||
if word_start.equal(word_end):
|
||||
break
|
||||
|
||||
def connect_language_change(self, fn):
|
||||
self.notify_language_change_functions.append(fn)
|
||||
word_start = word_end.copy()
|
||||
|
||||
def _languages_menu(self):
|
||||
def _set_language(item, code):
|
||||
self.language = code
|
||||
for fn in self.notify_language_change_functions:
|
||||
fn(code)
|
||||
|
||||
if _pygobject:
|
||||
menu = gtk.Menu.new()
|
||||
group = []
|
||||
|
@ -508,21 +488,6 @@ class SpellChecker(object):
|
|||
item.add(label)
|
||||
menu.append(item)
|
||||
else:
|
||||
# add sorting here
|
||||
# suggestions_map = []
|
||||
# print(suggestions)
|
||||
# for suggestion in suggestions:
|
||||
# if suggestion in self.frequency_dict:
|
||||
# suggestions_map.append({'suggestion': suggestion, 'freq': self.frequency_dict[suggestion]})
|
||||
# else:
|
||||
# suggestions_map.append({'suggestion': suggestion, 'freq': 0})
|
||||
|
||||
# suggestions_map.sort(key= lambda x: x['freq'])
|
||||
# suggestions_map.reverse()
|
||||
# print(suggestions_map)
|
||||
# suggestions = []
|
||||
# for suggestion in suggestions_map:
|
||||
# suggestions.append(suggestion["suggestion"])
|
||||
for suggestion in suggestions:
|
||||
if _pygobject:
|
||||
item = gtk.MenuItem.new()
|
||||
|
@ -609,7 +574,10 @@ class SpellChecker(object):
|
|||
if self._deferred_check: self._check_deferred_range(True)
|
||||
x, y = self._view.window_to_buffer_coords(2, int(event.x),
|
||||
int(event.y))
|
||||
self._marks['click'].move(self._view.get_iter_at_location(x, y))
|
||||
iter = self._view.get_iter_at_location(x, y)
|
||||
if isinstance(iter, tuple):
|
||||
iter = iter[1]
|
||||
self._marks['click'].move(iter)
|
||||
return False
|
||||
|
||||
def _before_text_insert(self, textbuffer, location, text, length):
|
||||
|
@ -651,6 +619,8 @@ class SpellChecker(object):
|
|||
word = self._buffer.get_text(start, end, False).strip()
|
||||
else:
|
||||
word = self._buffer.get_text(start, end, False).decode('utf-8').strip()
|
||||
if not word:
|
||||
return
|
||||
if len(self._filters[SpellChecker.FILTER_WORD]):
|
||||
if self._regexes[SpellChecker.FILTER_WORD].match(word):
|
||||
return
|
||||
|
@ -684,9 +654,5 @@ class SpellChecker(object):
|
|||
end = self._buffer.get_iter_at_offset(match.end())
|
||||
self._buffer.remove_tag(self._misspelled, start, end)
|
||||
return
|
||||
# Somehow needed now, checking for zero length (happens on double space)
|
||||
if len(word) == 0:
|
||||
return
|
||||
if not self._dictionary.check(word):
|
||||
self._buffer.apply_tag(self._misspelled, start, end)
|
||||
|
||||
|
|
|
@ -15,29 +15,23 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Python 2/3 unicode
|
||||
import sys
|
||||
if sys.version_info.major == 3:
|
||||
u = lambda x: x
|
||||
else:
|
||||
u = lambda x: x.decode('utf-8')
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Metadata
|
||||
__version__ = '1.1'
|
||||
__project__ = 'PyLocales'
|
||||
__version__ = '1.2'
|
||||
__project__ = 'Python Locales'
|
||||
__short_name__ = 'pylocales'
|
||||
__authors__ = u('Maximilian Köhl & Carlos Jenkins')
|
||||
__emails__ = u('linuxmaxi@googlemail.com & carlos@jenkins.co.cr')
|
||||
__website__ = 'http://pygtkspellcheck.readthedocs.org/'
|
||||
__authors__ = 'Maximilian Köhl'
|
||||
__emails__ = 'linuxmaxi@googlemail.com'
|
||||
__website__ = 'http://koehlma.github.com/projects/pygtkspellcheck.html'
|
||||
__source__ = 'https://github.com/koehlma/pygtkspellcheck/'
|
||||
__vcs__ = 'git://github.com/koehlma/pygtkspellcheck.git'
|
||||
__copyright__ = u('2012, Maximilian Köhl & Carlos Jenkins')
|
||||
__desc_short__ = 'Query the ISO 639/3166 database about a country or a language.'
|
||||
__copyright__ = '2012, Maximilian Köhl'
|
||||
__desc_short__ = 'query the ISO 639/3166 database about a country or a language.'
|
||||
__desc_long__ = ('Query the ISO 639/3166 database about a country or a'
|
||||
'language. The locales database contains ISO 639 languages'
|
||||
'definitions and ISO 3166 countries definitions. This package'
|
||||
'provides translation for countries and languages names if'
|
||||
'iso-codes package is installed (Ubuntu/Debian).')
|
||||
'language. The locales database contains ISO 639 language'
|
||||
'definitions and ISO 3166 country definitions. This package'
|
||||
'provides translation for country and language names if'
|
||||
'the iso-code messages are installed on your system.')
|
||||
|
||||
__metadata__ = {'__version__' : __version__,
|
||||
'__project__' : __project__,
|
||||
|
@ -51,5 +45,5 @@ __metadata__ = {'__version__' : __version__,
|
|||
'__desc_short__' : __desc_short__,
|
||||
'__desc_long__' : __desc_long__}
|
||||
|
||||
# Should only import Public Objects
|
||||
from .locales import *
|
||||
from pylocales.locales import (Country, Language, LanguageNotFound,
|
||||
CountryNotFound, code_to_name)
|
||||
|
|
|
@ -18,23 +18,22 @@
|
|||
|
||||
"""
|
||||
Query the ISO 639/3166 database about a country or a language. The locales
|
||||
database contains ISO 639 languages definitions and ISO 3166 countries
|
||||
definitions. This package provides translation for countries and languages names
|
||||
if iso-codes package is installed (Ubuntu/Debian).
|
||||
|
||||
@see utils/locales/build.py to know the database tables and structure.
|
||||
database contains ISO 639 language definitions and ISO 3166 country definitions.
|
||||
This package provides translation for country and language names if the
|
||||
iso-code messages are installed on your system.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
|
||||
# Public Objects
|
||||
# public objects
|
||||
__all__ = ['Country', 'Language', 'LanguageNotFound',
|
||||
'CountryNotFound', 'code_to_name']
|
||||
|
||||
# Translation
|
||||
# translation
|
||||
_translator_language = gettext.translation('iso_639', fallback=True).gettext
|
||||
_translator_country = gettext.translation('iso_3166', fallback=True).gettext
|
||||
|
||||
|
@ -48,14 +47,23 @@ if hasattr(os.path, 'get_module_path'):
|
|||
if not os.path.isfile(os.path.join(__path__, 'locales.db')):
|
||||
__path__ = None
|
||||
if __path__ is None:
|
||||
__path__ = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
|
||||
frozen = getattr(sys, 'frozen', None)
|
||||
if frozen in ('dll', 'console_exe', 'windows_exe'):
|
||||
__path__ = os.path.abspath(os.path.dirname(sys.executable))
|
||||
elif frozen == 'macosx_app':
|
||||
__path__ = os.path.abspath(os.environ['RESOURCEPATH'])
|
||||
elif frozen is True:
|
||||
# Handle executables produced by PyInstaller.
|
||||
__path__ = sys._MEIPASS
|
||||
else:
|
||||
__path__ = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
# Loading the Database
|
||||
# loading the database
|
||||
_database = sqlite3.connect(os.path.join(__path__, 'locales.db'))
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Exceptions
|
||||
class LanguageNotFound(Exception):
|
||||
"""
|
||||
The specified language wasn't found in the database.
|
||||
|
@ -68,7 +76,8 @@ class CountryNotFound(Exception):
|
|||
|
||||
class Country(object):
|
||||
def __init__(self, rowid):
|
||||
country = _database.execute('SELECT * FROM countries WHERE rowid == ?', (rowid,)).fetchone()
|
||||
country = _database.execute('SELECT * FROM countries WHERE rowid == ?',
|
||||
(rowid,)).fetchone()
|
||||
self.name = country[0]
|
||||
self.official_name = country[1]
|
||||
self.alpha_2 = country[2]
|
||||
|
@ -78,7 +87,9 @@ class Country(object):
|
|||
|
||||
@classmethod
|
||||
def get_country(cls, code, codec):
|
||||
country = _database.execute('SELECT rowid FROM countries WHERE %s == ?' % (codec), (code,)).fetchone()
|
||||
country = _database.execute(
|
||||
'SELECT rowid FROM countries WHERE %s == ?' % (codec),
|
||||
(code,)).fetchone()
|
||||
if country:
|
||||
return cls(country[0])
|
||||
raise CountryNotFound('code: %s, codec: %s' % (code, codec))
|
||||
|
@ -97,7 +108,8 @@ class Country(object):
|
|||
|
||||
class Language(object):
|
||||
def __init__(self, rowid):
|
||||
language = _database.execute('SELECT * FROM languages WHERE rowid == ?', (rowid,)).fetchone()
|
||||
language = _database.execute('SELECT * FROM languages WHERE rowid == ?',
|
||||
(rowid,)).fetchone()
|
||||
self.name = language[0]
|
||||
self.iso_639_2B = language[1]
|
||||
self.iso_639_2T = language[2]
|
||||
|
@ -106,7 +118,9 @@ class Language(object):
|
|||
|
||||
@classmethod
|
||||
def get_language(cls, code, codec):
|
||||
language = _database.execute('SELECT rowid FROM languages WHERE %s == ?' % (codec), (code,)).fetchone()
|
||||
language = _database.execute(
|
||||
'SELECT rowid FROM languages WHERE %s == ?' % (codec),
|
||||
(code,)).fetchone()
|
||||
if language:
|
||||
return cls(language[0])
|
||||
raise LanguageNotFound('code: %s, codec: %s' % (code, codec))
|
||||
|
@ -125,14 +139,18 @@ class Language(object):
|
|||
|
||||
|
||||
def code_to_name(code, separator='_'):
|
||||
"""
|
||||
Get the natural name of a language based on it's code.
|
||||
"""
|
||||
Get the human readable and translated name of a language based on it's code.
|
||||
|
||||
:param code: the code of the language (e.g. de_DE, en_US)
|
||||
:param target: separator used to separate language from country
|
||||
:rtype: human readable and translated language name
|
||||
"""
|
||||
logger.debug('requesting name for code "{}"'.format(code))
|
||||
code = code.split(separator)
|
||||
if len(code) > 1:
|
||||
lang = Language.by_iso_639_1(code[0]).translation
|
||||
country = Country.by_alpha_2(code[1]).translation
|
||||
return '{lang} ({country})'.format(lang=lang, country=country)
|
||||
return '{} ({})'.format(lang, country)
|
||||
else:
|
||||
return Language.by_iso_639_1(code[0]).translation
|
||||
return Language.by_iso_639_1(code[0]).translation
|
||||
|
|
Loading…
Reference in New Issue