Add auto correction with hunspell
This commit is contained in:
parent
23cdc3e57c
commit
48a01a3992
@ -8,7 +8,7 @@ DEPS_DIR=$$(ST_DEPS_DIR)
|
||||
isEmpty(DEPS_DIR):DEPS_DIR=$$PWD/../deps
|
||||
INCLUDEPATH += $$DEPS_DIR/include
|
||||
LIBS += -L$$DEPS_DIR/lib
|
||||
LIBS += -ltesseract -lleptonica
|
||||
LIBS += -ltesseract -lleptonica -lhunspell
|
||||
|
||||
win32{
|
||||
LIBS += -lUser32
|
||||
@ -39,6 +39,8 @@ HEADERS += \
|
||||
src/capture/capturer.h \
|
||||
src/commonmodels.h \
|
||||
src/correct/corrector.h \
|
||||
src/correct/correctorworker.h \
|
||||
src/correct/hunspellcorrector.h \
|
||||
src/languagecodes.h \
|
||||
src/manager.h \
|
||||
src/ocr/recognizer.h \
|
||||
@ -72,6 +74,8 @@ SOURCES += \
|
||||
src/capture/capturer.cpp \
|
||||
src/commonmodels.cpp \
|
||||
src/correct/corrector.cpp \
|
||||
src/correct/correctorworker.cpp \
|
||||
src/correct/hunspellcorrector.cpp \
|
||||
src/languagecodes.cpp \
|
||||
src/main.cpp \
|
||||
src/manager.cpp \
|
||||
|
118
share/ci/get_hunspell.py
Normal file
118
share/ci/get_hunspell.py
Normal file
@ -0,0 +1,118 @@
|
||||
import common as c
|
||||
from config import bitness, msvc_version, build_dir, dependencies_dir
|
||||
import os
|
||||
import platform
|
||||
|
||||
c.print('>> Installing hunspell')
|
||||
|
||||
install_dir = dependencies_dir
|
||||
url = 'https://github.com/hunspell/hunspell/files/2573619/hunspell-1.7.0.tar.gz'
|
||||
required_version = '1.7.0'
|
||||
|
||||
|
||||
def check_existing():
|
||||
if platform.system() == "Windows":
|
||||
dll = install_dir + '/bin/hunspell.dll'
|
||||
lib = install_dir + '/lib/hunspell.lib'
|
||||
if not os.path.exists(dll) or not os.path.exists(lib):
|
||||
return False
|
||||
elif platform.system() == "Darwin":
|
||||
lib = install_dir + '/lib/libhunspell.1.7.0.dylib'
|
||||
if not os.path.exists(lib):
|
||||
return False
|
||||
c.symlink(lib, install_dir + '/lib/libhunspell.dylib')
|
||||
else:
|
||||
lib = install_dir + '/lib/libhunspell-1.7.so'
|
||||
if not os.path.exists(lib):
|
||||
return False
|
||||
c.symlink(lib, install_dir + '/lib/libhunspell.so')
|
||||
|
||||
includes_path = install_dir + '/include/hunspell'
|
||||
if len(c.get_folder_files(includes_path)) == 0:
|
||||
return False
|
||||
|
||||
version_file = install_dir + '/lib/pkgconfig/hunspell.pc'
|
||||
if not os.path.exists(version_file):
|
||||
return False
|
||||
|
||||
with open(version_file, 'rt') as f:
|
||||
lines = f.readlines()
|
||||
for l in lines:
|
||||
if not l.startswith('Version'):
|
||||
continue
|
||||
existing_version = l[9:14] # Version: 1.7.0
|
||||
if existing_version != required_version:
|
||||
return False
|
||||
break
|
||||
return True
|
||||
|
||||
|
||||
if check_existing():
|
||||
c.print('>> Using cached')
|
||||
exit(0)
|
||||
|
||||
archive = os.path.basename(url)
|
||||
c.download(url, archive)
|
||||
|
||||
src_dir = os.path.abspath('hunspell_src')
|
||||
c.extract(archive, '.')
|
||||
c.symlink(c.get_archive_top_dir(archive), src_dir)
|
||||
|
||||
c.ensure_got_path(install_dir)
|
||||
|
||||
c.recreate_dir(build_dir)
|
||||
os.chdir(build_dir)
|
||||
|
||||
c.set_make_threaded()
|
||||
|
||||
if platform.system() != "Windows":
|
||||
c.run('autoreconf -i {}'.format(src_dir))
|
||||
c.run('{}/configure --prefix={}'.format(src_dir, install_dir))
|
||||
c.run('make')
|
||||
c.run('make install')
|
||||
else:
|
||||
lib_src = os.path.join(src_dir, 'src', 'hunspell')
|
||||
sources = []
|
||||
with os.scandir(lib_src) as it:
|
||||
for f in it:
|
||||
if not f.is_file() or not f.name.endswith('.cxx'):
|
||||
continue
|
||||
sources.append('${SRC_DIR}/' + f.name)
|
||||
|
||||
headers = ['${SRC_DIR}/atypes.hxx', '${SRC_DIR}/hunspell.h', '${SRC_DIR}/hunspell.hxx',
|
||||
'${SRC_DIR}/hunvisapi.h', '${SRC_DIR}/w_char.hxx']
|
||||
|
||||
cmake_file = os.path.join(build_dir, 'CMakeLists.txt')
|
||||
with open(cmake_file, 'w') as f:
|
||||
f.write('project(hunspell)\n')
|
||||
f.write('cmake_minimum_required(VERSION 3.11)\n')
|
||||
f.write('set(SRC_DIR "{}")\n'.format(lib_src).replace('\\', '/'))
|
||||
f.write('\n')
|
||||
f.write('add_library(hunspell SHARED {})\n'.format(' '.join(sources)))
|
||||
f.write('\n')
|
||||
f.write('add_compile_definitions(HAVE_CONFIG_H _WIN32 BUILDING_LIBHUNSPELL)\n')
|
||||
f.write('\n')
|
||||
f.write('install(FILES {} \
|
||||
DESTINATION include/hunspell)\n'.format(' '.join(headers)))
|
||||
f.write('\n')
|
||||
f.write('install(TARGETS hunspell \
|
||||
RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)\n')
|
||||
f.write('\n')
|
||||
f.write('set(prefix "${CMAKE_INSTALL_PREFIX}")\n')
|
||||
f.write('set(VERSION "{}")\n'.format(required_version))
|
||||
f.write('configure_file({}/hunspell.pc.in \
|
||||
${{CMAKE_CURRENT_BINARY_DIR}}/hunspell.pc @ONLY)\n'.format(src_dir.replace('\\', '/')))
|
||||
f.write('install(FILES ${CMAKE_CURRENT_BINARY_DIR}/hunspell.pc \
|
||||
DESTINATION lib/pkgconfig)\n')
|
||||
|
||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
||||
c.apply_cmd_env(env_cmd)
|
||||
cmake_args = '"{}" -DCMAKE_INSTALL_PREFIX="{}" {}'.format(
|
||||
build_dir, install_dir, c.get_cmake_arch_args(bitness=bitness))
|
||||
c.run('cmake {}'.format(cmake_args))
|
||||
c.run('cmake --build . --config Release --verbose')
|
||||
c.run('cmake --build . --target install --config Release')
|
||||
|
||||
if not check_existing(): # create links
|
||||
c.print('>> Build failed')
|
||||
exit(1)
|
@ -14,12 +14,17 @@
|
||||
<translation>Сохранить (можно будет захватывать горячими клавишами)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/capture/captureareaeditor.cpp" line="25"/>
|
||||
<location filename="../../src/capture/captureareaeditor.cpp" line="18"/>
|
||||
<source>Use auto corrections</source>
|
||||
<translation>Использовать автокоррекцию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/capture/captureareaeditor.cpp" line="26"/>
|
||||
<source>Recognize:</source>
|
||||
<translation>Распознать:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/capture/captureareaeditor.cpp" line="27"/>
|
||||
<location filename="../../src/capture/captureareaeditor.cpp" line="28"/>
|
||||
<source>⇵</source>
|
||||
<translation>⇵</translation>
|
||||
</message>
|
||||
@ -50,6 +55,14 @@ Ctrl - продолжить выделять</translation>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CorrectorWorker</name>
|
||||
<message>
|
||||
<location filename="../../src/correct/correctorworker.cpp" line="24"/>
|
||||
<source>Failed to init hunspell engine: %1</source>
|
||||
<translation>Ошибка инициализации hunspell: %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
@ -589,38 +602,43 @@ Ctrl - продолжить выделять</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="44"/>
|
||||
<source>hunspell</source>
|
||||
<translation>hunspell</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="45"/>
|
||||
<source>translators</source>
|
||||
<translation>перевод</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="59"/>
|
||||
<location filename="../../src/manager.cpp" line="60"/>
|
||||
<source>Screen translator started</source>
|
||||
<translation>Экранный переводчик запущен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="67"/>
|
||||
<location filename="../../src/manager.cpp" line="68"/>
|
||||
<source>Update completed</source>
|
||||
<translation>Обновление завершено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="71"/>
|
||||
<location filename="../../src/manager.cpp" line="72"/>
|
||||
<source>Updates available</source>
|
||||
<translation>Доступны обновления</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="97"/>
|
||||
<location filename="../../src/manager.cpp" line="98"/>
|
||||
<source>Current version might be outdated.
|
||||
Check for updates to silence this warning</source>
|
||||
<translation>Текущая версия может быть устаревшей.
|
||||
Проверьте обновления, чтобы отключить это сообщение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="181"/>
|
||||
<location filename="../../src/manager.cpp" line="184"/>
|
||||
<source>Failed to set log file: %1</source>
|
||||
<translation>Ошибка установки лог-файла: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="187"/>
|
||||
<location filename="../../src/manager.cpp" line="190"/>
|
||||
<source>Started logging to file: %1</source>
|
||||
<translation>Начата запись в лог-файл: %1</translation>
|
||||
</message>
|
||||
@ -630,7 +648,7 @@ Check for updates to silence this warning</source>
|
||||
<p>Version: %1</p>
|
||||
<p>Author: Gres (<a href="mailto:%2">%2</a>)</p>
|
||||
<p>Issues: <a href="%3">%3</a></p></source>
|
||||
<translation type="unfinished"><p>Инструмент оптического распознавания текста (OCR) и перевода</p>
|
||||
<translation><p>Инструмент оптического распознавания текста (OCR) и перевода</p>
|
||||
<p>Версия: %1</p>
|
||||
<p>Автор: Gres (<a href="mailto:%2">%2</a>)</p>
|
||||
<p>Поддержка: <a href="%3">%3</a></p></translation>
|
||||
@ -650,6 +668,20 @@ Check for updates to silence this warning</source>
|
||||
<source>Failed to recognize text or no text selected</source>
|
||||
<translation>Ошибка распознавания текста или нет текста в выделенной зоне</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/correct/hunspellcorrector.cpp" line="72"/>
|
||||
<source>Hunspell path not exists
|
||||
%1</source>
|
||||
<translation>Путь к словарям hunspell не существует
|
||||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/correct/hunspellcorrector.cpp" line="90"/>
|
||||
<source>No .aff or .dic files for hunspell
|
||||
in %1</source>
|
||||
<translation>Нет .aff или .dic файлов для hunspell
|
||||
в %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>RecognizeWorker</name>
|
||||
@ -790,17 +822,37 @@ Check for updates to silence this warning</source>
|
||||
<translation>сохранять пароль (небезопасно)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="371"/>
|
||||
<location filename="../../src/settingseditor.ui" line="292"/>
|
||||
<source>User substitutions</source>
|
||||
<translation>Пользовательская коррекция</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="312"/>
|
||||
<source>Use auto corrections (hunspell)</source>
|
||||
<translation>Использовать автокоррекцию (hunspell)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="319"/>
|
||||
<source>Use user substitutions</source>
|
||||
<translation>Использовать пользовательскую коррекцию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="326"/>
|
||||
<source>Hunspell dictionaries path:</source>
|
||||
<translation>Путь к словарям Hunspell:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="392"/>
|
||||
<source>Language:</source>
|
||||
<translation>Язык:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="354"/>
|
||||
<location filename="../../src/settingseditor.ui" line="375"/>
|
||||
<source> secs</source>
|
||||
<translation> сек</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="337"/>
|
||||
<location filename="../../src/settingseditor.ui" line="358"/>
|
||||
<source>Ignore SSL errors</source>
|
||||
<translation>Игнорировать ошибки SSL</translation>
|
||||
</message>
|
||||
@ -840,102 +892,92 @@ Check for updates to silence this warning</source>
|
||||
<translation>Путь к языкам (tessdata):</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="292"/>
|
||||
<source>Substitutions</source>
|
||||
<translation>Замены</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="302"/>
|
||||
<source>Substitute recognized text</source>
|
||||
<translation>Заменять распознанный текст</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="323"/>
|
||||
<location filename="../../src/settingseditor.ui" line="344"/>
|
||||
<source>Translators path:</source>
|
||||
<translation>Путь к переводчикам:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="344"/>
|
||||
<location filename="../../src/settingseditor.ui" line="365"/>
|
||||
<source>Translators</source>
|
||||
<translation>Переводчики</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="452"/>
|
||||
<location filename="../../src/settingseditor.ui" line="473"/>
|
||||
<source>Result window</source>
|
||||
<translation>Окно результата</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="458"/>
|
||||
<location filename="../../src/settingseditor.ui" line="479"/>
|
||||
<source>Font:</source>
|
||||
<translation>Шрифт:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="468"/>
|
||||
<location filename="../../src/settingseditor.ui" line="489"/>
|
||||
<source>Font size:</source>
|
||||
<translation>Размер шрифта:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="485"/>
|
||||
<location filename="../../src/settingseditor.ui" line="506"/>
|
||||
<source>Font color:</source>
|
||||
<translation>Цвет шрифта:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="502"/>
|
||||
<location filename="../../src/settingseditor.ui" line="523"/>
|
||||
<source>Background:</source>
|
||||
<translation>Фон:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="519"/>
|
||||
<location filename="../../src/settingseditor.ui" line="540"/>
|
||||
<source>Show image</source>
|
||||
<translation>Показывать изображение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="526"/>
|
||||
<location filename="../../src/settingseditor.ui" line="547"/>
|
||||
<source>Show recognized</source>
|
||||
<translation>Показывать распознанное</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="595"/>
|
||||
<location filename="../../src/settingseditor.ui" line="616"/>
|
||||
<source>Update check interval (days):</source>
|
||||
<translation>Интервал проверки обновления (дней):</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="602"/>
|
||||
<location filename="../../src/settingseditor.ui" line="623"/>
|
||||
<source>0 - disabled</source>
|
||||
<translation>- отключено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="635"/>
|
||||
<location filename="../../src/settingseditor.ui" line="656"/>
|
||||
<source>Apply updates</source>
|
||||
<translation>Применить изменения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="330"/>
|
||||
<location filename="../../src/settingseditor.ui" line="351"/>
|
||||
<source>Translate text</source>
|
||||
<translation>Переводить текст</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="364"/>
|
||||
<location filename="../../src/settingseditor.ui" line="385"/>
|
||||
<source>Single translator timeout:</source>
|
||||
<translation>Переходить к следующему переводчику после:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="414"/>
|
||||
<location filename="../../src/settingseditor.ui" line="435"/>
|
||||
<source>Result type</source>
|
||||
<translation>Тип результата</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="426"/>
|
||||
<location filename="../../src/settingseditor.ui" line="447"/>
|
||||
<source>Tray</source>
|
||||
<translation>Трей</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="439"/>
|
||||
<location filename="../../src/settingseditor.ui" line="460"/>
|
||||
<source>Window</source>
|
||||
<translation>Окно</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="618"/>
|
||||
<location filename="../../src/settingseditor.ui" line="639"/>
|
||||
<source>Check now</source>
|
||||
<translation>Проверить сейчас</translation>
|
||||
</message>
|
||||
@ -1000,7 +1042,7 @@ Check for updates to silence this warning</source>
|
||||
<translation>Текст для проверки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="317"/>
|
||||
<location filename="../../src/settingseditor.cpp" line="321"/>
|
||||
<source>Portable changed. Apply settings first</source>
|
||||
<translation>Portable режиме изменени. Сначала применить настройки</translation>
|
||||
</message>
|
||||
|
101
share/updates/hunspell.py
Normal file
101
share/updates/hunspell.py
Normal file
@ -0,0 +1,101 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
|
||||
def parse_language_names():
|
||||
root = os.path.abspath(os.path.basename(__file__) + '/../../..')
|
||||
lines = []
|
||||
with open(root + '/src/languagecodes.cpp', 'r') as d:
|
||||
lines = d.readlines()
|
||||
result = {}
|
||||
for line in lines:
|
||||
if line.startswith('//'):
|
||||
continue
|
||||
all = re.findall(r'"(.*?)"', line)
|
||||
if len(all) != 6:
|
||||
continue
|
||||
result[all[2]] = all[5]
|
||||
return result
|
||||
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage:", sys.argv[0], "<dict_dir> [<download_url>]")
|
||||
exit(1)
|
||||
|
||||
dict_dir = sys.argv[1]
|
||||
|
||||
download_url = "https://cgit.freedesktop.org/libreoffice/dictionaries/plain"
|
||||
if len(sys.argv) > 2:
|
||||
download_url = sys.argv[2]
|
||||
|
||||
language_names = parse_language_names()
|
||||
|
||||
preferred = ['sr.aff', 'sv_FI.aff',
|
||||
'en_US.aff', 'de_DE_frami.aff', 'nb_NO.aff']
|
||||
|
||||
files = {}
|
||||
it = os.scandir(dict_dir)
|
||||
for d in it:
|
||||
if not d.is_dir():
|
||||
continue
|
||||
|
||||
lang = d.name
|
||||
if '_' in lang:
|
||||
lang = lang[0:lang.index('_')]
|
||||
|
||||
affs = []
|
||||
fit = os.scandir(os.path.join(dict_dir, d.name))
|
||||
for f in fit:
|
||||
if not f.is_file or not f.name.endswith('.aff'):
|
||||
continue
|
||||
affs.append(f.name)
|
||||
|
||||
aff = ''
|
||||
if len(affs) == 0:
|
||||
continue
|
||||
if len(affs) == 1:
|
||||
aff = affs[0]
|
||||
else:
|
||||
for p in preferred:
|
||||
if p in affs:
|
||||
aff = p
|
||||
break
|
||||
|
||||
if len(aff) == 0:
|
||||
print('no aff for', lang, affs)
|
||||
continue
|
||||
|
||||
aff = os.path.join(d.name, aff)
|
||||
dic = aff[:aff.rindex('.')] + '.dic'
|
||||
if not os.path.exists(os.path.join(dict_dir, dic)):
|
||||
print('no dic exists', dic)
|
||||
|
||||
files[lang] = [aff, dic]
|
||||
|
||||
|
||||
print(',"hunspell": {')
|
||||
comma = ''
|
||||
unknown_names = []
|
||||
for lang, file_names in files.items():
|
||||
if not lang in language_names:
|
||||
unknown_names.append(lang)
|
||||
continue
|
||||
lang_name = language_names[lang]
|
||||
print(' {}"{}":{{"files":['.format(comma, lang_name))
|
||||
comma = ', '
|
||||
lang_comma = ''
|
||||
for file_name in file_names:
|
||||
git_cmd = ['git', 'log', '-1', '--pretty=format:%cI', file_name]
|
||||
date = subprocess.run(git_cmd, cwd=dict_dir, universal_newlines=True,
|
||||
stdout=subprocess.PIPE, check=True).stdout
|
||||
size = os.path.getsize(os.path.join(dict_dir, file_name))
|
||||
installed = lang + file_name[file_name.index('/'):]
|
||||
print(' {}{{"url":"{}/{}", "path":"$hunspell$/{}", "date":"{}", "size":{}}}'.format(
|
||||
lang_comma, download_url, file_name, installed, date, size))
|
||||
lang_comma = ','
|
||||
print(' ]}')
|
||||
print('}')
|
||||
|
||||
print('unknown names', unknown_names)
|
@ -5,6 +5,7 @@
|
||||
CaptureArea::CaptureArea(const QRect &rect, const Settings &settings)
|
||||
: rect_(rect)
|
||||
, doTranslation_(settings.doTranslation)
|
||||
, useHunspell_(settings.useHunspell)
|
||||
, sourceLanguage_(settings.sourceLanguage)
|
||||
, targetLanguage_(settings.targetLanguage)
|
||||
, translators_(settings.translators)
|
||||
@ -18,6 +19,7 @@ TaskPtr CaptureArea::task(const QPixmap &pixmap) const
|
||||
|
||||
auto task = std::make_shared<Task>();
|
||||
task->generation = generation_;
|
||||
task->useHunspell = useHunspell_;
|
||||
task->captured = pixmap.copy(rect_);
|
||||
task->capturePoint = rect_.topLeft();
|
||||
task->sourceLanguage = sourceLanguage_;
|
||||
|
@ -28,6 +28,7 @@ private:
|
||||
QRect rect_;
|
||||
bool doTranslation_;
|
||||
bool isLocked_{false};
|
||||
bool useHunspell_{false};
|
||||
LanguageId sourceLanguage_;
|
||||
LanguageId targetLanguage_;
|
||||
QStringList translators_;
|
||||
|
@ -15,6 +15,7 @@ CaptureAreaEditor::CaptureAreaEditor(const CommonModels &models,
|
||||
: QWidget(parent)
|
||||
, doTranslation_(new QCheckBox(tr("Translate:"), this))
|
||||
, isLocked_(new QCheckBox(tr("Save (can capture via hotkey)"), this))
|
||||
, useHunspell_(new QCheckBox(tr("Use auto corrections"), this))
|
||||
, sourceLanguage_(new QComboBox(this))
|
||||
, targetLanguage_(new QComboBox(this))
|
||||
{
|
||||
@ -31,6 +32,9 @@ CaptureAreaEditor::CaptureAreaEditor(const CommonModels &models,
|
||||
layout->addWidget(doTranslation_, row, 0);
|
||||
layout->addWidget(targetLanguage_, row, 1);
|
||||
|
||||
++row;
|
||||
layout->addWidget(useHunspell_, row, 0, 1, 2);
|
||||
|
||||
++row;
|
||||
layout->addWidget(isLocked_, row, 0, 1, 2);
|
||||
|
||||
@ -64,6 +68,7 @@ void CaptureAreaEditor::swapLanguages()
|
||||
void CaptureAreaEditor::set(const CaptureArea &area)
|
||||
{
|
||||
isLocked_->setChecked(area.isLocked());
|
||||
useHunspell_->setChecked(area.useHunspell_);
|
||||
doTranslation_->setChecked(area.doTranslation_);
|
||||
sourceLanguage_->setCurrentText(LanguageCodes::name(area.sourceLanguage_));
|
||||
targetLanguage_->setCurrentText(LanguageCodes::name(area.targetLanguage_));
|
||||
@ -72,6 +77,7 @@ void CaptureAreaEditor::set(const CaptureArea &area)
|
||||
void CaptureAreaEditor::apply(CaptureArea &area) const
|
||||
{
|
||||
area.isLocked_ = isLocked_->isChecked();
|
||||
area.useHunspell_ = useHunspell_->isChecked();
|
||||
area.doTranslation_ = doTranslation_->isChecked();
|
||||
area.sourceLanguage_ =
|
||||
LanguageCodes::idForName(sourceLanguage_->currentText());
|
||||
|
@ -23,6 +23,7 @@ private:
|
||||
|
||||
QCheckBox* doTranslation_;
|
||||
QCheckBox* isLocked_;
|
||||
QCheckBox* useHunspell_;
|
||||
QComboBox* sourceLanguage_;
|
||||
QComboBox* targetLanguage_;
|
||||
};
|
||||
|
@ -1,13 +1,37 @@
|
||||
#include "corrector.h"
|
||||
#include "correctorworker.h"
|
||||
#include "debug.h"
|
||||
#include "manager.h"
|
||||
#include "settings.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
Corrector::Corrector(Manager &manager, const Settings &settings)
|
||||
: manager_(manager)
|
||||
, settings_(settings)
|
||||
, workerThread_(new QThread(this))
|
||||
{
|
||||
auto worker = new CorrectorWorker;
|
||||
connect(this, &Corrector::resetAuto, //
|
||||
worker, &CorrectorWorker::reset);
|
||||
connect(this, &Corrector::correctAuto, //
|
||||
worker, &CorrectorWorker::handle);
|
||||
connect(worker, &CorrectorWorker::finished, //
|
||||
this, &Corrector::finishCorrection);
|
||||
connect(workerThread_, &QThread::finished, //
|
||||
worker, &QObject::deleteLater);
|
||||
|
||||
workerThread_->start();
|
||||
worker->moveToThread(workerThread_);
|
||||
}
|
||||
|
||||
Corrector::~Corrector()
|
||||
{
|
||||
workerThread_->quit();
|
||||
const auto timeoutMs = 2000;
|
||||
if (!workerThread_->wait(timeoutMs))
|
||||
workerThread_->terminate();
|
||||
}
|
||||
|
||||
void Corrector::correct(const TaskPtr &task)
|
||||
@ -20,11 +44,26 @@ void Corrector::correct(const TaskPtr &task)
|
||||
return;
|
||||
}
|
||||
|
||||
task->corrected = task->recognized;
|
||||
|
||||
if (!settings_.userSubstitutions.empty())
|
||||
task->corrected = substituteUser(task->recognized, task->sourceLanguage);
|
||||
else
|
||||
task->corrected = task->recognized;
|
||||
|
||||
if (task->useHunspell) {
|
||||
emit correctAuto(task);
|
||||
return;
|
||||
}
|
||||
|
||||
finishCorrection(task);
|
||||
}
|
||||
|
||||
void Corrector::updateSettings()
|
||||
{
|
||||
emit resetAuto(settings_.hunspellDir);
|
||||
}
|
||||
|
||||
void Corrector::finishCorrection(const TaskPtr &task)
|
||||
{
|
||||
manager_.corrected(task);
|
||||
}
|
||||
|
||||
|
@ -2,17 +2,28 @@
|
||||
|
||||
#include "stfwd.h"
|
||||
|
||||
class Corrector
|
||||
#include <QObject>
|
||||
|
||||
class Corrector : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Corrector(Manager &manager, const Settings &settings);
|
||||
~Corrector();
|
||||
|
||||
void correct(const TaskPtr &task);
|
||||
void updateSettings();
|
||||
|
||||
signals:
|
||||
void correctAuto(const TaskPtr &task);
|
||||
void resetAuto(const QString &tessdataPath);
|
||||
|
||||
private:
|
||||
void finishCorrection(const TaskPtr &task);
|
||||
QString substituteUser(const QString &source,
|
||||
const LanguageId &language) const;
|
||||
|
||||
Manager &manager_;
|
||||
const Settings &settings_;
|
||||
QThread *workerThread_;
|
||||
};
|
||||
|
66
src/correct/correctorworker.cpp
Normal file
66
src/correct/correctorworker.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "correctorworker.h"
|
||||
#include "debug.h"
|
||||
#include "hunspellcorrector.h"
|
||||
#include "task.h"
|
||||
|
||||
CorrectorWorker::CorrectorWorker() = default;
|
||||
|
||||
CorrectorWorker::~CorrectorWorker() = default;
|
||||
|
||||
void CorrectorWorker::handle(const TaskPtr &task)
|
||||
{
|
||||
SOFT_ASSERT(task, return );
|
||||
SOFT_ASSERT(task->isValid(), return );
|
||||
SOFT_ASSERT(!hunspellDir_.isEmpty(), return );
|
||||
|
||||
auto result = task;
|
||||
|
||||
if (!bundles_.count(task->sourceLanguage)) {
|
||||
auto engine =
|
||||
std::make_unique<HunspellCorrector>(task->sourceLanguage, hunspellDir_);
|
||||
|
||||
if (!engine->isValid()) {
|
||||
LWARNING()
|
||||
<< tr("Failed to init hunspell engine: %1").arg(engine->error());
|
||||
emit finished(result);
|
||||
return;
|
||||
}
|
||||
|
||||
bundles_.emplace(task->sourceLanguage, Bundle{std::move(engine), 0});
|
||||
}
|
||||
|
||||
auto &bundle = bundles_[task->sourceLanguage];
|
||||
SOFT_ASSERT(bundle.hunspell->isValid(), return );
|
||||
|
||||
result->corrected = bundle.hunspell->correct(task->corrected);
|
||||
|
||||
const auto keepGenerations = 10;
|
||||
bundle.usesLeft = keepGenerations;
|
||||
removeUnused(task->generation);
|
||||
lastGeneration_ = task->generation;
|
||||
|
||||
emit finished(result);
|
||||
}
|
||||
|
||||
void CorrectorWorker::reset(const QString &hunspellDir)
|
||||
{
|
||||
if (hunspellDir_ == hunspellDir)
|
||||
return;
|
||||
|
||||
hunspellDir_ = hunspellDir;
|
||||
bundles_.clear();
|
||||
}
|
||||
|
||||
void CorrectorWorker::removeUnused(Generation current)
|
||||
{
|
||||
if (lastGeneration_ == current)
|
||||
return;
|
||||
|
||||
for (auto it = bundles_.begin(), end = bundles_.end(); it != end;) {
|
||||
if (it->second.usesLeft >= 0) {
|
||||
++it;
|
||||
} else {
|
||||
it = bundles_.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
32
src/correct/correctorworker.h
Normal file
32
src/correct/correctorworker.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "stfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class HunspellCorrector;
|
||||
|
||||
class CorrectorWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CorrectorWorker();
|
||||
~CorrectorWorker();
|
||||
|
||||
void handle(const TaskPtr &task);
|
||||
void reset(const QString &hunspellDir);
|
||||
|
||||
signals:
|
||||
void finished(const TaskPtr &task);
|
||||
|
||||
private:
|
||||
struct Bundle {
|
||||
std::unique_ptr<HunspellCorrector> hunspell;
|
||||
int usesLeft;
|
||||
};
|
||||
void removeUnused(Generation current);
|
||||
|
||||
std::map<QString, Bundle> bundles_;
|
||||
Generation lastGeneration_{};
|
||||
QString hunspellDir_;
|
||||
};
|
169
src/correct/hunspellcorrector.cpp
Normal file
169
src/correct/hunspellcorrector.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
#include "hunspellcorrector.h"
|
||||
#include "debug.h"
|
||||
#include "languagecodes.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <hunspell/hunspell.hxx>
|
||||
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextCodec>
|
||||
|
||||
static int levenshteinDistance(const QString &source, const QString &target)
|
||||
{
|
||||
if (source == target)
|
||||
return 0;
|
||||
|
||||
const auto sourceCount = source.size();
|
||||
const auto targetCount = target.size();
|
||||
|
||||
if (sourceCount == 0)
|
||||
return targetCount;
|
||||
|
||||
if (targetCount == 0)
|
||||
return sourceCount;
|
||||
|
||||
if (sourceCount > targetCount)
|
||||
return levenshteinDistance(target, source);
|
||||
|
||||
QVector<int> previousColumn;
|
||||
previousColumn.reserve(targetCount + 1);
|
||||
for (auto i = 0; i < targetCount + 1; ++i) previousColumn.append(i);
|
||||
|
||||
QVector<int> column(targetCount + 1, 0);
|
||||
for (auto i = 0; i < sourceCount; ++i) {
|
||||
column[0] = i + 1;
|
||||
for (auto j = 0; j < targetCount; ++j) {
|
||||
column[j + 1] = std::min(
|
||||
{1 + column.at(j), 1 + previousColumn.at(1 + j),
|
||||
previousColumn.at(j) + ((source.at(i) == target.at(j)) ? 0 : 1)});
|
||||
}
|
||||
column.swap(previousColumn);
|
||||
}
|
||||
|
||||
return previousColumn.at(targetCount);
|
||||
}
|
||||
|
||||
HunspellCorrector::HunspellCorrector(const LanguageId &language,
|
||||
const QString &dictPath)
|
||||
{
|
||||
const auto name = LanguageCodes::iso639_1(language);
|
||||
init(dictPath + QLatin1Char('/') + name);
|
||||
}
|
||||
|
||||
HunspellCorrector::~HunspellCorrector() = default;
|
||||
|
||||
const QString &HunspellCorrector::error() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
|
||||
bool HunspellCorrector::isValid() const
|
||||
{
|
||||
return engine_.get();
|
||||
}
|
||||
|
||||
void HunspellCorrector::init(const QString &path)
|
||||
{
|
||||
SOFT_ASSERT(!engine_, return );
|
||||
|
||||
QDir dir(path);
|
||||
if (!dir.exists()) {
|
||||
error_ = QObject::tr("Hunspell path not exists\n%1").arg(path);
|
||||
return;
|
||||
}
|
||||
|
||||
QString aff;
|
||||
QStringList dics;
|
||||
for (const auto &file : dir.entryList(QDir::Filter::Files)) {
|
||||
if (file.endsWith(".aff")) {
|
||||
aff = dir.absoluteFilePath(file);
|
||||
continue;
|
||||
}
|
||||
if (file.endsWith(".dic")) {
|
||||
dics.append(dir.absoluteFilePath(file));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (aff.isEmpty() || dics.isEmpty()) {
|
||||
error_ = QObject::tr("No .aff or .dic files for hunspell\nin %1").arg(path);
|
||||
return;
|
||||
}
|
||||
|
||||
engine_ =
|
||||
std::make_unique<Hunspell>(qPrintable(aff), qPrintable(dics.first()));
|
||||
|
||||
dics.pop_front();
|
||||
if (!dics.isEmpty()) {
|
||||
for (const auto &dic : dics) engine_->add_dic(qPrintable(dic));
|
||||
}
|
||||
}
|
||||
|
||||
QString HunspellCorrector::correct(const QString &original)
|
||||
{
|
||||
SOFT_ASSERT(engine_, return original);
|
||||
|
||||
const auto codec =
|
||||
QTextCodec::codecForName(engine_->get_dict_encoding().c_str());
|
||||
SOFT_ASSERT(codec, return original);
|
||||
|
||||
QString result;
|
||||
|
||||
QString word;
|
||||
QString separator;
|
||||
for (auto i = 0, end = original.size(); i < end; ++i) {
|
||||
const auto ch = original[i];
|
||||
if (ch.isPunct() || ch.isSpace()) {
|
||||
if (!word.isEmpty()) {
|
||||
correctWord(word, *codec);
|
||||
result += word;
|
||||
word.clear();
|
||||
}
|
||||
separator += ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ch.isLetter() && word.isEmpty()) {
|
||||
separator += ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!separator.isEmpty()) {
|
||||
result += separator;
|
||||
separator.clear();
|
||||
}
|
||||
word += ch;
|
||||
}
|
||||
|
||||
if (!word.isEmpty()) {
|
||||
correctWord(word, *codec);
|
||||
result += word;
|
||||
}
|
||||
result += separator;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HunspellCorrector::correctWord(QString &word, QTextCodec &codec) const
|
||||
{
|
||||
if (word.isEmpty())
|
||||
return;
|
||||
|
||||
const auto stdWord = codec.fromUnicode(word).toStdString();
|
||||
if (engine_->spell(stdWord))
|
||||
return;
|
||||
|
||||
const auto suggestions = engine_->suggest(stdWord);
|
||||
if (suggestions.empty())
|
||||
return;
|
||||
|
||||
const auto suggestion = QByteArray::fromStdString(suggestions.front());
|
||||
const auto distance = levenshteinDistance(word, suggestion);
|
||||
const auto maxDistance = std::max(int(word.size() * 0.2), 1);
|
||||
LTRACE() << "hunspell" << word << suggestion << "distances" << distance
|
||||
<< maxDistance;
|
||||
|
||||
if (distance <= maxDistance)
|
||||
word = suggestion;
|
||||
}
|
26
src/correct/hunspellcorrector.h
Normal file
26
src/correct/hunspellcorrector.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "stfwd.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
class Hunspell;
|
||||
|
||||
class HunspellCorrector
|
||||
{
|
||||
public:
|
||||
explicit HunspellCorrector(const LanguageId& language,
|
||||
const QString& dictPath);
|
||||
~HunspellCorrector();
|
||||
|
||||
const QString& error() const;
|
||||
bool isValid() const;
|
||||
QString correct(const QString& original);
|
||||
|
||||
private:
|
||||
void init(const QString& path);
|
||||
void correctWord(QString& word, QTextCodec& codec) const;
|
||||
|
||||
std::unique_ptr<Hunspell> engine_;
|
||||
QString error_;
|
||||
};
|
@ -41,6 +41,7 @@ Manager::Manager()
|
||||
// updater components
|
||||
(void)QT_TRANSLATE_NOOP("QObject", "app");
|
||||
(void)QT_TRANSLATE_NOOP("QObject", "recognizers");
|
||||
(void)QT_TRANSLATE_NOOP("QObject", "hunspell");
|
||||
(void)QT_TRANSLATE_NOOP("QObject", "translators");
|
||||
|
||||
tray_ = std::make_unique<TrayIcon>(*this, *settings_);
|
||||
@ -111,6 +112,7 @@ void Manager::updateSettings()
|
||||
tray_->updateSettings();
|
||||
capturer_->updateSettings();
|
||||
recognizer_->updateSettings();
|
||||
corrector_->updateSettings();
|
||||
translator_->updateSettings();
|
||||
representer_->updateSettings();
|
||||
|
||||
@ -147,6 +149,7 @@ void Manager::setupUpdates(const Settings &settings)
|
||||
updater_->model()->setExpansions({
|
||||
{"$translators$", settings.translatorsDir},
|
||||
{"$tessdata$", settings.tessdataPath},
|
||||
{"$hunspell$", settings.hunspellDir},
|
||||
});
|
||||
|
||||
SOFT_ASSERT(updateAutoChecker_, return );
|
||||
|
@ -34,6 +34,7 @@ const QString qs_ocrLanguage = "language";
|
||||
const QString qs_correctionGroup = "Correction";
|
||||
const QString qs_userSubstitutions = "userSubstitutions";
|
||||
const QString qs_useUserSubstitutions = "useUserSubstitutions";
|
||||
const QString qs_useHunspell = "useHunspell";
|
||||
|
||||
const QString qs_translationGroup = "Translation";
|
||||
const QString qs_doTranslation = "doTranslation";
|
||||
@ -173,6 +174,7 @@ void Settings::save() const
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(qs_correctionGroup);
|
||||
settings.setValue(qs_useHunspell, useHunspell);
|
||||
settings.setValue(qs_useUserSubstitutions, useUserSubstitutions);
|
||||
settings.setValue(qs_userSubstitutions, packSubstitutions(userSubstitutions));
|
||||
settings.endGroup();
|
||||
@ -258,6 +260,7 @@ void Settings::load()
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup(qs_correctionGroup);
|
||||
useHunspell = settings.value(qs_useHunspell, useHunspell).toBool();
|
||||
useUserSubstitutions =
|
||||
settings.value(qs_useUserSubstitutions, useUserSubstitutions).toBool();
|
||||
userSubstitutions =
|
||||
@ -328,4 +331,5 @@ void Settings::setPortable(bool isPortable)
|
||||
: QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
tessdataPath = baseDataPath + "/tessdata";
|
||||
translatorsDir = baseDataPath + "/translators";
|
||||
hunspellDir = baseDataPath + "/hunspell";
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ public:
|
||||
int autoUpdateIntervalDays{0};
|
||||
QDateTime lastUpdateCheck;
|
||||
|
||||
bool useHunspell{false};
|
||||
QString hunspellDir;
|
||||
Substitutions userSubstitutions;
|
||||
bool useUserSubstitutions{true};
|
||||
|
||||
|
@ -145,6 +145,7 @@ Settings SettingsEditor::settings() const
|
||||
settings.sourceLanguage =
|
||||
LanguageCodes::idForName(ui->tesseractLangCombo->currentText());
|
||||
|
||||
settings.useHunspell = ui->useHunspell->isChecked();
|
||||
settings.useUserSubstitutions = ui->useUserSubstitutions->isChecked();
|
||||
settings.userSubstitutions = ui->userSubstitutionsTable->substitutions();
|
||||
|
||||
@ -207,6 +208,8 @@ void SettingsEditor::setSettings(const Settings &settings)
|
||||
ui->tesseractLangCombo->setCurrentText(
|
||||
LanguageCodes::name(settings.sourceLanguage));
|
||||
|
||||
ui->useHunspell->setChecked(settings.useHunspell);
|
||||
ui->hunspellDir->setText(settings.hunspellDir);
|
||||
ui->useUserSubstitutions->setChecked(settings.useUserSubstitutions);
|
||||
ui->userSubstitutionsTable->setSubstitutions(settings.userSubstitutions);
|
||||
|
||||
@ -308,6 +311,7 @@ void SettingsEditor::handlePortableChanged()
|
||||
settings.setPortable(ui->portable->isChecked());
|
||||
ui->tessdataPath->setText(settings.tessdataPath);
|
||||
ui->translatorsPath->setText(settings.translatorsDir);
|
||||
ui->hunspellDir->setText(settings.hunspellDir);
|
||||
updateModels(settings.tessdataPath);
|
||||
updateTranslators();
|
||||
|
||||
|
@ -286,24 +286,17 @@
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageCorrect">
|
||||
<layout class="QGridLayout" name="gridLayout_10">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Substitutions</string>
|
||||
<string>User substitutions</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="useUserSubstitutions">
|
||||
<property name="text">
|
||||
<string>Substitute recognized text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="SubstitutionsTable" name="userSubstitutionsTable">
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
@ -313,6 +306,34 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="useHunspell">
|
||||
<property name="text">
|
||||
<string>Use auto corrections (hunspell)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="useUserSubstitutions">
|
||||
<property name="text">
|
||||
<string>Use user substitutions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="text">
|
||||
<string>Hunspell dictionaries path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="hunspellDir">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageTranslate">
|
||||
|
@ -19,6 +19,8 @@ public:
|
||||
QString corrected;
|
||||
QString translated;
|
||||
|
||||
bool useHunspell{false};
|
||||
|
||||
LanguageId sourceLanguage;
|
||||
LanguageId targetLanguage;
|
||||
|
||||
|
190
updates.json
190
updates.json
@ -382,6 +382,196 @@
|
||||
]}
|
||||
}
|
||||
|
||||
|
||||
,"hunspell": {
|
||||
"Thai":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/th_TH/th_TH.aff", "path":"$hunspell$/th/th_TH.aff", "date":"2019-04-30T09:35:45+02:00", "size":156}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/th_TH/th_TH.dic", "path":"$hunspell$/th/th_TH.dic", "date":"2019-06-04T14:18:16+02:00", "size":1251425}
|
||||
]}
|
||||
, "Gujarati":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gu_IN/gu_IN.aff", "path":"$hunspell$/gu/gu_IN.aff", "date":"2012-10-16T11:09:27-05:00", "size":174}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gu_IN/gu_IN.dic", "path":"$hunspell$/gu/gu_IN.dic", "date":"2012-10-16T11:09:27-05:00", "size":3792870}
|
||||
]}
|
||||
, "Afrikaans":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/af_ZA/af_ZA.aff", "path":"$hunspell$/af/af_ZA.aff", "date":"2020-02-16T20:22:16+01:00", "size":5027}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/af_ZA/af_ZA.dic", "path":"$hunspell$/af/af_ZA.dic", "date":"2020-02-16T20:22:16+01:00", "size":1262203}
|
||||
]}
|
||||
, "Norwegian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/no/nb_NO.aff", "path":"$hunspell$/no/nb_NO.aff", "date":"2013-05-23T11:54:36+01:00", "size":17259}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/no/nb_NO.dic", "path":"$hunspell$/no/nb_NO.dic", "date":"2018-09-05T10:30:32+02:00", "size":5274030}
|
||||
]}
|
||||
, "Albanian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sq_AL/sq_AL.aff", "path":"$hunspell$/sq/sq_AL.aff", "date":"2017-10-23T23:57:24+02:00", "size":7555}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sq_AL/sq_AL.dic", "path":"$hunspell$/sq/sq_AL.dic", "date":"2017-10-23T23:57:24+02:00", "size":2605147}
|
||||
]}
|
||||
, "Italian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/it_IT/it_IT.aff", "path":"$hunspell$/it/it_IT.aff", "date":"2012-10-16T11:09:27-05:00", "size":80216}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/it_IT/it_IT.dic", "path":"$hunspell$/it/it_IT.dic", "date":"2012-10-16T11:09:27-05:00", "size":1290681}
|
||||
]}
|
||||
, "Nepali":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ne_NP/ne_NP.aff", "path":"$hunspell$/ne/ne_NP.aff", "date":"2012-10-16T11:09:27-05:00", "size":14162}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ne_NP/ne_NP.dic", "path":"$hunspell$/ne/ne_NP.dic", "date":"2012-10-16T11:09:27-05:00", "size":874372}
|
||||
]}
|
||||
, "Danish":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/da_DK/da_DK.aff", "path":"$hunspell$/da/da_DK.aff", "date":"2019-07-14T00:13:36+02:00", "size":55779}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/da_DK/da_DK.dic", "path":"$hunspell$/da/da_DK.dic", "date":"2019-07-14T00:13:36+02:00", "size":2915525}
|
||||
]}
|
||||
, "Ukrainian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/uk_UA/uk_UA.aff", "path":"$hunspell$/uk/uk_UA.aff", "date":"2012-10-16T11:09:27-05:00", "size":159599}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/uk_UA/uk_UA.dic", "path":"$hunspell$/uk/uk_UA.dic", "date":"2012-10-16T11:09:27-05:00", "size":2584267}
|
||||
]}
|
||||
, "Gaelic":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gd_GB/gd_GB.aff", "path":"$hunspell$/gd/gd_GB.aff", "date":"2017-06-22T00:27:25+02:00", "size":8228}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gd_GB/gd_GB.dic", "path":"$hunspell$/gd/gd_GB.dic", "date":"2017-06-22T00:27:25+02:00", "size":4806682}
|
||||
]}
|
||||
, "Estonian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/et_EE/et_EE.aff", "path":"$hunspell$/et/et_EE.aff", "date":"2012-10-16T11:09:27-05:00", "size":236336}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/et_EE/et_EE.dic", "path":"$hunspell$/et/et_EE.dic", "date":"2012-10-16T11:09:27-05:00", "size":4383841}
|
||||
]}
|
||||
, "Greek":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/el_GR/el_GR.aff", "path":"$hunspell$/el/el_GR.aff", "date":"2015-09-21T17:56:43+02:00", "size":15647}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/el_GR/el_GR.dic", "path":"$hunspell$/el/el_GR.dic", "date":"2015-09-21T17:56:43+02:00", "size":10125390}
|
||||
]}
|
||||
, "Bulgarian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bg_BG/bg_BG.aff", "path":"$hunspell$/bg/bg_BG.aff", "date":"2018-06-29T12:25:29+02:00", "size":58189}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bg_BG/bg_BG.dic", "path":"$hunspell$/bg/bg_BG.dic", "date":"2018-06-29T12:25:29+02:00", "size":1566331}
|
||||
]}
|
||||
, "Croatian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hr_HR/hr_HR.aff", "path":"$hunspell$/hr/hr_HR.aff", "date":"2018-05-29T22:11:06+02:00", "size":95802}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hr_HR/hr_HR.dic", "path":"$hunspell$/hr/hr_HR.dic", "date":"2018-05-29T22:11:06+02:00", "size":731819}
|
||||
]}
|
||||
, "Polish":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pl_PL/pl_PL.aff", "path":"$hunspell$/pl/pl_PL.aff", "date":"2017-05-05T15:26:38+02:00", "size":246842}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pl_PL/pl_PL.dic", "path":"$hunspell$/pl/pl_PL.dic", "date":"2017-05-21T10:58:59+02:00", "size":4539105}
|
||||
]}
|
||||
, "Galician":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gl/gl_ES.aff", "path":"$hunspell$/gl/gl_ES.aff", "date":"2018-12-28T04:42:44+01:00", "size":1159910}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gl/gl_ES.dic", "path":"$hunspell$/gl/gl_ES.dic", "date":"2018-12-28T04:42:44+01:00", "size":8636406}
|
||||
]}
|
||||
, "Bengali":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bn_BD/bn_BD.aff", "path":"$hunspell$/bn/bn_BD.aff", "date":"2012-10-16T11:09:27-05:00", "size":195}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bn_BD/bn_BD.dic", "path":"$hunspell$/bn/bn_BD.dic", "date":"2012-10-16T11:09:27-05:00", "size":2596038}
|
||||
]}
|
||||
, "Dutch":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/nl_NL/nl_NL.aff", "path":"$hunspell$/nl/nl_NL.aff", "date":"2013-07-22T17:41:01+00:00", "size":27835}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/nl_NL/nl_NL.dic", "path":"$hunspell$/nl/nl_NL.dic", "date":"2013-07-22T17:41:01+00:00", "size":1881063}
|
||||
]}
|
||||
, "Bosnian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bs_BA/bs_BA.aff", "path":"$hunspell$/bs/bs_BA.aff", "date":"2013-01-22T17:32:09+01:00", "size":17468}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bs_BA/bs_BA.dic", "path":"$hunspell$/bs/bs_BA.dic", "date":"2013-01-22T17:32:09+01:00", "size":339863}
|
||||
]}
|
||||
, "Swahili":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sw_TZ/sw_TZ.aff", "path":"$hunspell$/sw/sw_TZ.aff", "date":"2012-10-16T11:09:27-05:00", "size":974}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sw_TZ/sw_TZ.dic", "path":"$hunspell$/sw/sw_TZ.dic", "date":"2012-10-16T11:09:27-05:00", "size":630844}
|
||||
]}
|
||||
, "Slovenian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sl_SI/sl_SI.aff", "path":"$hunspell$/sl/sl_SI.aff", "date":"2012-10-16T11:09:27-05:00", "size":14730}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sl_SI/sl_SI.dic", "path":"$hunspell$/sl/sl_SI.dic", "date":"2012-10-16T11:09:27-05:00", "size":2967766}
|
||||
]}
|
||||
, "French":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fr_FR/fr.aff", "path":"$hunspell$/fr/fr.aff", "date":"2018-08-24T15:00:59+02:00", "size":256857}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fr_FR/fr.dic", "path":"$hunspell$/fr/fr.dic", "date":"2018-08-24T15:00:59+02:00", "size":1100397}
|
||||
]}
|
||||
, "Latvian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lv_LV/lv_LV.aff", "path":"$hunspell$/lv/lv_LV.aff", "date":"2012-10-16T11:09:27-05:00", "size":69597}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lv_LV/lv_LV.dic", "path":"$hunspell$/lv/lv_LV.dic", "date":"2012-10-16T11:09:27-05:00", "size":2226834}
|
||||
]}
|
||||
, "German":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/de/de_DE_frami.aff", "path":"$hunspell$/de/de_DE_frami.aff", "date":"2017-01-22T19:03:05+00:00", "size":18991}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/de/de_DE_frami.dic", "path":"$hunspell$/de/de_DE_frami.dic", "date":"2017-01-22T19:03:05+00:00", "size":4356858}
|
||||
]}
|
||||
, "Tibetan":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bo/bo.aff", "path":"$hunspell$/bo/bo.aff", "date":"2016-11-22T22:23:34+00:00", "size":1706}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bo/bo.dic", "path":"$hunspell$/bo/bo.dic", "date":"2017-10-23T18:37:13+02:00", "size":4637}
|
||||
]}
|
||||
, "Telugu":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/te_IN/te_IN.aff", "path":"$hunspell$/te/te_IN.aff", "date":"2012-10-16T11:09:27-05:00", "size":160}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/te_IN/te_IN.dic", "path":"$hunspell$/te/te_IN.dic", "date":"2012-10-16T11:09:27-05:00", "size":3402272}
|
||||
]}
|
||||
, "Czech":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/cs_CZ/cs_CZ.aff", "path":"$hunspell$/cs/cs_CZ.aff", "date":"2015-04-07T12:40:39+02:00", "size":97286}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/cs_CZ/cs_CZ.dic", "path":"$hunspell$/cs/cs_CZ.dic", "date":"2019-09-16T06:45:26+02:00", "size":2209232}
|
||||
]}
|
||||
, "Slovak":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sk_SK/sk_SK.aff", "path":"$hunspell$/sk/sk_SK.aff", "date":"2013-05-09T10:20:05+02:00", "size":99414}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sk_SK/sk_SK.dic", "path":"$hunspell$/sk/sk_SK.dic", "date":"2013-05-09T10:20:05+02:00", "size":3289769}
|
||||
]}
|
||||
, "Hebrew":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/he_IL/he_IL.aff", "path":"$hunspell$/he/he_IL.aff", "date":"2017-09-05T18:11:31+02:00", "size":78883}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/he_IL/he_IL.dic", "path":"$hunspell$/he/he_IL.dic", "date":"2017-09-05T18:11:31+02:00", "size":7796259}
|
||||
]}
|
||||
, "Russian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ru_RU/ru_RU.aff", "path":"$hunspell$/ru/ru_RU.aff", "date":"2012-10-16T11:09:27-05:00", "size":53019}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ru_RU/ru_RU.dic", "path":"$hunspell$/ru/ru_RU.dic", "date":"2012-10-16T11:09:27-05:00", "size":1969349}
|
||||
]}
|
||||
, "Arabic":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ar/ar.aff", "path":"$hunspell$/ar/ar.aff", "date":"2018-02-04T21:34:12+01:00", "size":86949}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ar/ar.dic", "path":"$hunspell$/ar/ar.dic", "date":"2019-03-07T11:32:58+01:00", "size":7217161}
|
||||
]}
|
||||
, "Vietnamese":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/vi/vi_VN.aff", "path":"$hunspell$/vi/vi_VN.aff", "date":"2012-10-16T11:09:27-05:00", "size":788}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/vi/vi_VN.dic", "path":"$hunspell$/vi/vi_VN.dic", "date":"2012-10-16T11:09:27-05:00", "size":39852}
|
||||
]}
|
||||
, "Portuguese":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pt_PT/pt_PT.aff", "path":"$hunspell$/pt/pt_PT.aff", "date":"2016-07-07T10:15:29+02:00", "size":95089}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pt_PT/pt_PT.dic", "path":"$hunspell$/pt/pt_PT.dic", "date":"2016-10-03T18:05:09+00:00", "size":1473077}
|
||||
]}
|
||||
, "Turkish":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/tr_TR/tr_TR.aff", "path":"$hunspell$/tr/tr_TR.aff", "date":"2018-08-27T16:55:14+02:00", "size":235315}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/tr_TR/tr_TR.dic", "path":"$hunspell$/tr/tr_TR.dic", "date":"2018-08-27T16:55:14+02:00", "size":9061155}
|
||||
]}
|
||||
, "Indonesian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/id/id_ID.aff", "path":"$hunspell$/id/id_ID.aff", "date":"2018-02-28T01:40:08+01:00", "size":14957}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/id/id_ID.dic", "path":"$hunspell$/id/id_ID.dic", "date":"2018-02-28T01:40:08+01:00", "size":315384}
|
||||
]}
|
||||
, "Hindi":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hi_IN/hi_IN.aff", "path":"$hunspell$/hi/hi_IN.aff", "date":"2012-10-16T11:09:27-05:00", "size":210}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hi_IN/hi_IN.dic", "path":"$hunspell$/hi/hi_IN.dic", "date":"2012-10-16T11:09:27-05:00", "size":303963}
|
||||
]}
|
||||
, "Lithuanian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lt_LT/lt.aff", "path":"$hunspell$/lt/lt.aff", "date":"2013-01-23T11:35:37+00:00", "size":92208}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lt_LT/lt.dic", "path":"$hunspell$/lt/lt.dic", "date":"2013-01-23T11:35:37+00:00", "size":1085291}
|
||||
]}
|
||||
, "Spanish":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/es/es_ANY.aff", "path":"$hunspell$/es/es_ANY.aff", "date":"2019-10-22T21:26:40+02:00", "size":169377}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/es/es_ANY.dic", "path":"$hunspell$/es/es_ANY.dic", "date":"2019-10-22T21:26:40+02:00", "size":804058}
|
||||
]}
|
||||
, "Icelandic":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/is/is.aff", "path":"$hunspell$/is/is.aff", "date":"2016-03-14T09:05:09+00:00", "size":309734}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/is/is.dic", "path":"$hunspell$/is/is.dic", "date":"2016-03-14T09:05:09+00:00", "size":2454138}
|
||||
]}
|
||||
, "Lao":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lo_LA/lo_LA.aff", "path":"$hunspell$/lo/lo_LA.aff", "date":"2013-11-24T19:21:08+01:00", "size":10}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lo_LA/lo_LA.dic", "path":"$hunspell$/lo/lo_LA.dic", "date":"2013-11-24T19:21:08+01:00", "size":203209}
|
||||
]}
|
||||
, "Romanian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ro/ro_RO.aff", "path":"$hunspell$/ro/ro_RO.aff", "date":"2013-03-28T11:26:45+01:00", "size":55181}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ro/ro_RO.dic", "path":"$hunspell$/ro/ro_RO.dic", "date":"2013-03-28T11:26:45+01:00", "size":2196348}
|
||||
]}
|
||||
, "Serbian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sr/sr.aff", "path":"$hunspell$/sr/sr.aff", "date":"2019-04-20T11:24:57+02:00", "size":901060}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sr/sr.dic", "path":"$hunspell$/sr/sr.dic", "date":"2019-04-20T11:24:57+02:00", "size":5878745}
|
||||
]}
|
||||
, "Belarusian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/be_BY/be_BY.aff", "path":"$hunspell$/be/be_BY.aff", "date":"2012-10-16T11:09:27-05:00", "size":23968}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/be_BY/be_BY.dic", "path":"$hunspell$/be/be_BY.dic", "date":"2012-10-16T11:09:27-05:00", "size":1707840}
|
||||
]}
|
||||
, "English":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/en/en_US.aff", "path":"$hunspell$/en/en_US.aff", "date":"2018-05-15T00:49:14+02:00", "size":3090}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/en/en_US.dic", "path":"$hunspell$/en/en_US.dic", "date":"2019-11-13T00:44:49+01:00", "size":551260}
|
||||
]}
|
||||
, "Hungarian":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hu_HU/hu_HU.aff", "path":"$hunspell$/hu/hu_HU.aff", "date":"2018-05-22T22:26:58+02:00", "size":2106214}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hu_HU/hu_HU.dic", "path":"$hunspell$/hu/hu_HU.dic", "date":"2018-05-22T22:26:58+02:00", "size":1653155}
|
||||
]}
|
||||
, "Swedish":{"files":[
|
||||
{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sv_SE/sv_FI.aff", "path":"$hunspell$/sv/sv_FI.aff", "date":"2015-09-08T21:02:20+00:00", "size":18583}
|
||||
,{"url":"https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sv_SE/sv_FI.dic", "path":"$hunspell$/sv/sv_FI.dic", "date":"2016-08-16T20:00:33+00:00", "size":2317112}
|
||||
]}
|
||||
}
|
||||
|
||||
|
||||
|
||||
,"translators":{
|
||||
"bing": {"files":[
|
||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/bing.js", "path":"$translators$/bing.js", "md5":"fc69c1e05a3462a88131ee0a8422ad89"}
|
||||
|
Loading…
Reference in New Issue
Block a user