Compare commits

...

41 Commits

Author SHA1 Message Date
dependabot[bot]
6efc473859
Bump actions/download-artifact from 1 to 4.1.7 in /.github/workflows (#191)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 1 to 4.1.7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v1...v4.1.7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 10:51:26 +03:00
Gres
e1ec86f298 Update deepl translation 2023-09-02 21:09:27 +03:00
Gres
29ee5dda90 Update deepl translation 2023-05-19 23:30:44 +03:00
Gres
b8cd2dff54 Update project state 2023-05-05 21:50:57 +03:00
Gres
fb3f32f050 Update deepl translation 2023-04-08 22:09:35 +03:00
Gres
41f1f56fe5 Update deepl translation 2023-02-12 14:17:17 +03:00
Gres
5be5def820 Update deepl translation 2023-02-06 23:40:07 +03:00
Gres
12997389ff Add Hebrew translation 2023-01-28 12:29:12 +03:00
Gres
ab15cc8c79 Update Yandex translator 2023-01-28 12:29:12 +03:00
Gres
6cb9199a6c Fix repeated 'check for updates warning' 2023-01-28 12:29:12 +03:00
Gres
57b1cf8865 Add note about the app translaion 2023-01-03 21:51:56 +03:00
Gres
d328026356 Update correction resource names 2022-10-24 13:07:58 +03:00
Gres
8d4bcb8605 Update translation script 2022-10-14 21:20:42 +03:00
Gres
5a2c52e4c5 Update deepl supported languages 2022-10-07 21:09:43 +03:00
Gres
8c918e14a4 Update readme 2022-08-14 12:29:45 +03:00
Gres
936aaa90ff Update version file 2022-08-14 12:07:35 +03:00
Gres
efa9fab49f Update version file 2022-07-30 13:27:46 +03:00
Gres
cad1e83d44 Bump version 2022-07-30 13:10:43 +03:00
Gres
7be070744b Use single tesseract library
Link with it during compilation.
Bump to 5.2.0.
2022-07-29 23:09:56 +03:00
Gres
cb203b912e Improve image preprocessing 2022-07-29 23:05:49 +03:00
Gres
5f53aaec23 Rescent gcc fix 2022-07-03 22:34:16 +03:00
Gres
b71c67dd1b Respect "use user substitutions" option 2022-07-03 22:34:02 +03:00
Gres
5df1f52a68 Update version file 2022-05-22 15:06:14 +03:00
Gres
59d7f1d4f5 Update changelog 2022-05-22 14:53:33 +03:00
Gres
bf5d4efc6b Bump version 2022-05-22 14:37:50 +03:00
Gres
6cb853d804 Yet another typo 2022-05-18 23:13:51 +03:00
Gres
ac24d909d1 Typo in gcc version 2022-05-18 23:00:58 +03:00
Gres
9b6657318b Bump gcc version 2022-05-18 22:44:53 +03:00
Gres
ccfbf843dc Add info about Linux crash workaround 2022-05-18 22:44:21 +03:00
Gres
00e3f90430 Add workaround for tesseract crash on Linux 2022-05-18 22:44:21 +03:00
Gres
60f115acd1 Set proper flags for tesseract builds 2022-05-18 22:43:20 +03:00
Gres
1f6fff0050 Improve result window placement on screen borders
See #74
2022-05-04 19:36:56 +03:00
Gres
030c99d0fe Typo 2022-04-07 23:18:38 +03:00
Gres
a4a3f44806 Fixed avx support detection 2022-04-07 22:56:22 +03:00
Gres
ce9c47c3ea Update tesseract version 2022-04-03 12:19:50 +03:00
Gres
07b520d10b Change msvc version 2022-03-27 22:04:59 +03:00
Gres
260a10bea3 Close result widgets with escape button
For #86
2022-03-27 11:37:32 +03:00
Gres
4f195f5629 Fix multi-monitor result and editor positions
Fixes #90
2022-03-26 16:19:33 +03:00
Gres
7d84bd2f7b Fixed translators order persistence
fixes #91
2022-03-24 20:39:39 +03:00
Gres
a4e09d88c6 Update version file 2022-02-05 22:18:19 +03:00
Gres
ad39d98858 Disable sourceforge upload 2022-02-05 22:14:56 +03:00
37 changed files with 1746 additions and 578 deletions

View File

@ -33,7 +33,7 @@ jobs:
runs-on: ${{ matrix.config.os }}
env:
OS: ${{ matrix.config.name }}
MSVC_VERSION: 2019/Enterprise
MSVC_VERSION: C:/Program Files/Microsoft Visual Studio/2022/Enterprise
strategy:
matrix:
config:
@ -55,7 +55,7 @@ jobs:
if: runner.os == 'Linux'
run: |
sudo apt-get install libgl1-mesa-dev libxkbcommon-x11-0 libxcb-*
echo "QMAKE_FLAGS=QMAKE_CXX=g++-9 QMAKE_CC=gcc-9" >> $GITHUB_ENV
echo "QMAKE_FLAGS=QMAKE_CXX=g++-10 QMAKE_CC=gcc-10 QMAKE_LINK=g++-10" >> $GITHUB_ENV
- name: Cache dependencies
uses: actions/cache@v2
@ -78,7 +78,7 @@ jobs:
- name: Download release url
if: contains(github.ref, '/tags/')
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4.1.7
with:
name: release_upload_url
path: ./
@ -98,13 +98,3 @@ jobs:
asset_path: ./${{ env.artifact }}
asset_name: ${{ env.artifact }}
asset_content_type: application/zip
- name: Upload sourceforge
if: contains(github.ref, '/tags/')
env:
SF_PKEY: ${{ secrets.SF_PKEY }}
SF_API: ${{ secrets.SF_API }}
run: |
python -m pip install --upgrade pip
pip install paramiko
python ./share/ci/sourceforge.py ./${{ env.artifact }}

View File

@ -1,5 +1,7 @@
# Screen Translator
**The project is almost abandoned. I don't have time for it and I can only fix minor issues**
## Introduction
This software allows you to translate any text on screen.
@ -16,6 +18,12 @@ If the app fails to start complaining about missing dlls or there are any update
**OS X**: currently not supported.
### App translation
To install Hebrew translation of the app (thanks to [Y-PLONI](https://github.com/Y-PLONI)),
download [this](https://github.com/OneMoreGres/ScreenTranslator/releases/download/3.3.0/screentranslator_he.qm)
file and place it into the `translations` folder next to `screen-translator.exe`.
## Setup
The app doesn't have a main window.
@ -53,8 +61,7 @@ Then click `Ok` to close settings.
By default resources are downloaded to the one of the user's folders.
If `Portable` setting in `General` section is checked, then resources will be downloaded to the app's folder.
If you are experiencing crashes during the app usage, then try changing `tesseract library version` from `Optimized` to `Compatible`.
It provides the same functionality, but should work on the bigger variety of hardware.
Set `QTWEBENGINE_DISABLE_SANDBOX=1` environment variable when fail to start due to crash.
Answers to some frequently asked questions can be found in issues or
[wiki](https://github.com/OneMoreGres/ScreenTranslator/wiki/FAQ)

View File

@ -11,5 +11,6 @@
</qresource>
<qresource prefix="/translations">
<file alias="screentranslator_ru.qm">share/translations/screentranslator_ru.qm</file>
<file alias="screentranslator_he.qm">share/translations/screentranslator_he.qm</file>
</qresource>
</RCC>

View File

@ -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 += -lhunspell -lleptonica
LIBS += -lhunspell -lleptonica -ltesseract
win32{
LIBS += -lUser32
@ -21,7 +21,7 @@ linux{
SOURCES += $$PWD/external/miniz/miniz.c
INCLUDEPATH += $$PWD/external
VER=3.2.2
VER=3.3.0
DEFINES += VERSION="$$VER"
VERSION = $$VER.0
QMAKE_TARGET_COMPANY = Gres
@ -117,7 +117,8 @@ OTHER_FILES += \
updates.json
TRANSLATIONS += \
share/translations/screentranslator_ru.ts
share/translations/screentranslator_ru.ts \
share/translations/screentranslator_he.ts
linux {
PREFIX = /usr

View File

@ -1,5 +1,17 @@
# Changes
## 3.3.0
* Use single tesseract library (not optimized and compatible versions)
* Improved recognition
## 3.2.3
* Fixed translators order persistance
* Fixed multi-monitor support
* Improves result representation near monitor borders
* Updated recognition library
## 3.2.2
* Disabled hotkeys with several consecutive combinations

View File

@ -1,5 +1,17 @@
# Изменения
## 3.3.0
* Использование единой библиотеки распознавания (без оптимизированной и совместимой версий)
* Улучшено распознавание
## 3.2.3
* Исправлено сохранение порядка переводчиков в настройках
* Исправлена работа с несколькими мониторами
* Улучшено отображение результата на границе монитора
* Обновлена версия библиотеки распознавания
## 3.2.2
* Исключено задание горячих клавиш из нескольких последовательных комбинаций

View File

@ -47,8 +47,7 @@ os.environ['VERSION'] = app_version
flags = '' if os.getenv("DEBUG") is None else '-unsupported-allow-new-glibc'
additional_files = glob(ssl_dir + '/lib/lib*.so.*') + \
glob('/usr/lib/x86_64-linux-gnu/nss/*') + \
glob(dependencies_dir + '/lib/libtesseract-*.so')
glob('/usr/lib/x86_64-linux-gnu/nss/*')
out_lib_dir = install_dir + '/usr/lib'
os.makedirs(out_lib_dir, exist_ok=True)
for f in additional_files:

View File

@ -150,15 +150,7 @@ def get_msvc_env_cmd(bitness='64', msvc_version=''):
if platform.system() != "Windows":
return None
msvc_path = 'C:/Program Files (x86)/Microsoft Visual Studio'
if len(msvc_version) == 0:
with os.scandir(msvc_path) as ver_it:
version = next(ver_it, '')
with os.scandir(msvc_path + '/' + version) as ed_it:
msvc_version = version + '/' + next(ed_it, '')
env_script = msvc_path + '/{}/VC/Auxiliary/Build/vcvars{}.bat'.format(
msvc_version, bitness)
env_script = msvc_version + '/VC/Auxiliary/Build/vcvars{}.bat'.format(bitness)
return '"' + env_script + '"'

View File

@ -30,6 +30,6 @@ os_name = getenv('OS', 'linux')
app_version += {'linux': '', 'macos': '-experimental',
'win32': '', 'win64': ''}[os_name]
bitness = '32' if os_name == 'win32' else '64'
msvc_version = getenv('MSVC_VERSION', '2019/Community')
msvc_version = getenv('MSVC_VERSION', 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community')
build_type = 'release' # 'debug'

View File

@ -6,38 +6,12 @@ import platform
c.print('>> Installing tesseract')
install_dir = dependencies_dir
url = 'https://github.com/tesseract-ocr/tesseract/archive/4.1.3.tar.gz'
required_version = '4.1.3'
required_version = '5.2.0'
url = 'https://github.com/tesseract-ocr/tesseract/archive/{}.tar.gz'.format(required_version)
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
# compatibility flags
compat_flags = ''
if os.environ.get('NO_AVX2', '0') == '1':
compat_flags += ' -D USE_AVX2=OFF '
if os.environ.get('NO_AVX512', '0') == '1':
compat_flags += ' -D USE_AVX512BW=OFF -D USE_AVX512CD=OFF \
-D USE_AVX512DQ=OFF -D USE_AVX512ER=OFF -D USE_AVX512F=OFF -D USE_AVX512IFMA=OFF \
-D USE_AVX512PF=OFF -D USE_AVX512VBMI=OFF -D USE_AVX512VL=OFF '
if os.environ.get('NO_AVX', '0') == '1':
compat_flags += ' -D USE_AVX=OFF '
if os.environ.get('NO_FMA', '0') == '1':
compat_flags += ' -D USE_FMA=OFF '
if os.environ.get('NO_BMI2', '0') == '1':
compat_flags += ' -D USE_BMI2=OFF '
if os.environ.get('NO_SSE4', '0') == '1':
compat_flags += ' -D USE_SSE4_1=OFF -D USE_SSE4_2=OFF '
if os.environ.get('NO_OPT', '0') == '1':
compat_flags += ' -D CMAKE_CXX_FLAGS_RELEASE="/MD /Od /Od0 /DNDEBUG" '
compat_flags += ' -D CMAKE_C_FLAGS_RELEASE="/MD /Od /Od0 /DNDEBUG" '
if len(os.environ.get('MARCH', '')) > 0:
compat_flags += ' -D TARGET_ARCHITECTURE={} '.format(os.environ['MARCH'])
lib_suffix = os.environ.get('TAG', '')
if len(lib_suffix) > 0:
lib_suffix = '-' + lib_suffix
cache_file = install_dir + '/tesseract{}.cache'.format(lib_suffix)
cache_file = install_dir + '/tesseract.cache'
cache_file_data = required_version + build_type_flag
def check_existing():
@ -53,22 +27,25 @@ def check_existing():
return False
if platform.system() == "Windows":
lib = install_dir + '/bin/tesseract{}.dll'.format(lib_suffix)
orig_lib = install_dir + '/bin/tesseract41.dll'
elif platform.system() == "Darwin":
lib = install_dir + '/lib/libtesseract{}.dylib'.format(lib_suffix)
orig_lib = install_dir + '/lib/libtesseract.{}.dylib'.format(required_version)
else:
lib = install_dir + '/lib/libtesseract{}.so'.format(lib_suffix)
orig_lib = install_dir + '/lib/libtesseract.so.{}'.format(required_version)
if os.path.exists(lib):
return True
if os.path.exists(orig_lib):
os.rename(orig_lib, lib)
return True
file_name_ver = required_version[0] + required_version[2]
dll = install_dir + '/bin/tesseract{}.dll'.format(file_name_ver)
lib = install_dir + '/lib/tesseract{}.lib'.format(file_name_ver)
if not os.path.exists(dll) or not os.path.exists(lib):
return False
c.symlink(dll, install_dir + '/bin/tesseract.dll')
c.symlink(lib, install_dir + '/lib/tesseract.lib')
elif platform.system() == "Darwin":
lib = install_dir + '/lib/libtesseract.{}.dylib'.format(required_version)
if not os.path.exists(lib):
return False
c.symlink(lib, install_dir + '/lib/libtesseract.dylib')
else:
lib = install_dir + '/lib/libtesseract.so.{}'.format(required_version)
if not os.path.exists(lib):
return False
c.symlink(lib, install_dir + '/lib/libtesseract.so')
return True
if check_existing() and not 'FORCE' in os.environ:
@ -82,44 +59,24 @@ src_dir = os.path.abspath('tesseract_src')
c.extract(archive, '.')
c.symlink(c.get_archive_top_dir(archive), src_dir)
if platform.system() == "Windows":
# workaround for not found 'max'
modify_data = ''
modify_file = '{}/src/ccmain/thresholder.cpp'.format(src_dir)
with open(modify_file, 'r') as f:
modify_data = f.read()
if modify_data.find('<algorithm>') == -1:
modify_data = modify_data.replace(
'''<tuple>''',
'''<tuple>\n#include <algorithm>''')
with open(modify_file, 'w') as f:
f.write(modify_data)
# ignore libtiff
modify_data = ''
modify_file = '{}/CMakeLists.txt'.format(src_dir)
with open(modify_file, 'r') as f:
modify_data = f.read()
if modify_data.find('#pkg_check_modules(TIFF libtiff-4)') == -1:
modify_data = modify_data.replace(
'''pkg_check_modules(TIFF libtiff-4)''',
'''#pkg_check_modules(TIFF libtiff-4)''')
with open(modify_file, 'w') as f:
f.write(modify_data)
c.ensure_got_path(install_dir)
c.recreate_dir(build_dir)
os.chdir(build_dir)
cmake_args = '"{0}" -DCMAKE_INSTALL_PREFIX="{1}" -DLeptonica_DIR="{1}/cmake" \
-DBUILD_TRAINING_TOOLS=OFF -DBUILD_TESTS=OFF -DBUILD_SHARED_LIBS=ON -DSW_BUILD=OFF \
cmake_args = '"{0}" \
-DCMAKE_INSTALL_PREFIX="{1}" \
-DLeptonica_DIR="{1}/cmake" \
-DSW_BUILD=OFF \
-DBUILD_TRAINING_TOOLS=OFF \
-DBUILD_TESTS=OFF \
-DBUILD_SHARED_LIBS=ON \
-DDISABLE_CURL=ON \
-DDISABLE_ARCHIVE=ON \
-DUSE_SYSTEM_ICU=ON \
-DENABLE_LTO=ON \
-DGRAPHICS_DISABLED=ON \
-DDISABLED_LEGACY_ENGINE=ON \
'.format(src_dir, install_dir)
if platform.system() == "Windows":
@ -130,10 +87,6 @@ if platform.system() == "Windows":
c.set_make_threaded()
c.run('cmake {}'.format(cmake_args))
if len(compat_flags) > 0:
c.run('cmake {} .'.format(compat_flags))
c.run('cmake {} .'.format(compat_flags)) # for sure :)
c.run('cmake --build . --config {}'.format(build_type_flag))
c.run('cmake --build . --target install --config {}'.format(build_type_flag))

View File

@ -29,18 +29,8 @@ def r(script):
r('get_qt.py')
r('get_qt_ssl.py')
r('get_leptonica.py')
os.environ['MARCH'] = 'sandy-bridge'
os.environ['TAG'] = 'optimized'
r('get_tesseract.py')
os.environ['MARCH'] = 'nehalem'
os.environ['TAG'] = 'compatible'
r('get_tesseract.py')
del os.environ['MARCH']
del os.environ['TAG']
r('get_hunspell.py')
r('test.py')
r('build.py')

File diff suppressed because it is too large Load Diff

View File

@ -655,7 +655,7 @@ Check for updates to silence this warning</source>
<message>
<location filename="../../src/manager.cpp" line="129"/>
<source>Incorrect settings found. Go to Settings</source>
<translation>Обнвружены некорректные настройки. Перейдите в Настройки</translation>
<translation>Обнаружены некорректные настройки. Перейдите в Настройки</translation>
</message>
<message>
<location filename="../../src/manager.cpp" line="194"/>
@ -673,12 +673,12 @@ Check for updates to silence this warning</source>
<translation>неизвестные языки для перевода: %1 или %2</translation>
</message>
<message>
<location filename="../../src/ocr/tesseract.cpp" line="255"/>
<location filename="../../src/ocr/tesseract.cpp" line="234"/>
<source>init failed</source>
<translation>ошибка инициалиизации</translation>
</message>
<message>
<location filename="../../src/ocr/tesseract.cpp" line="306"/>
<location filename="../../src/ocr/tesseract.cpp" line="294"/>
<source>Failed to recognize text or no text selected</source>
<translation>Ошибка распознавания текста или нет текста в выделенной зоне</translation>
</message>
@ -697,12 +697,12 @@ in %1</source>
в %1</translation>
</message>
<message>
<location filename="../../src/capture/capturearea.cpp" line="27"/>
<location filename="../../src/capture/capturearea.cpp" line="28"/>
<source>No source language set</source>
<translation>Не задан исходный язык</translation>
</message>
<message>
<location filename="../../src/capture/capturearea.cpp" line="34"/>
<location filename="../../src/capture/capturearea.cpp" line="35"/>
<source>No target language set</source>
<translation>Не задан язык результата</translation>
</message>
@ -712,37 +712,37 @@ in %1</source>
<translation>Не восстанавливать интерфейс пользователя (размер и положения окна и т.д.)</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="197"/>
<location filename="../../src/settingseditor.cpp" line="189"/>
<source>&lt;p&gt;Optical character recognition (OCR) and translation tool&lt;/p&gt;</source>
<translation>&lt;p&gt;Инструмент оптического распознавания текста (OCR) и перевода&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="199"/>
<location filename="../../src/settingseditor.cpp" line="191"/>
<source>&lt;p&gt;Version: %1&lt;/p&gt;</source>
<translation>&lt;p&gt;Версия: %1&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="201"/>
<location filename="../../src/settingseditor.cpp" line="193"/>
<source>&lt;p&gt;Setup instructions: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;&lt;/p&gt;</source>
<translation>&lt;p&gt;Инструкции по установке: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="202"/>
<location filename="../../src/settingseditor.cpp" line="195"/>
<source>&lt;p&gt;Changelog: &lt;a href=&quot;%1&quot;&gt;%2&lt;/a&gt;&lt;/p&gt;</source>
<translation>&lt;p&gt;Список изменений: &lt;a href=&quot;%1&quot;&gt;%2&lt;/a&gt;&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="204"/>
<location filename="../../src/settingseditor.cpp" line="197"/>
<source>&lt;p&gt;License: &lt;a href=&quot;%3&quot;&gt;MIT&lt;/a&gt;&lt;/p&gt;</source>
<translation>&lt;p&gt;Лицензия: &lt;a href=&quot;%3&quot;&gt;MIT&lt;/a&gt;&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="205"/>
<location filename="../../src/settingseditor.cpp" line="198"/>
<source>&lt;p&gt;Author: Gres (&lt;a href=&quot;mailto:%1&quot;&gt;%1&lt;/a&gt;)&lt;/p&gt;</source>
<translation>&lt;p&gt;Автор: Gres (&lt;a href=&quot;mailto:%1&quot;&gt;%1&lt;/a&gt;)&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="207"/>
<location filename="../../src/settingseditor.cpp" line="200"/>
<source>&lt;p&gt;Issues: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;&lt;/p&gt;</source>
<translation>&lt;p&gt;Поддержка: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;&lt;/p&gt;</translation>
</message>
@ -844,7 +844,7 @@ in %1</source>
<translation>Повторить захват</translation>
</message>
<message>
<location filename="../../src/represent/resultwidget.cpp" line="93"/>
<location filename="../../src/represent/resultwidget.cpp" line="95"/>
<source>Without correction:
</source>
<translation>Без коррекции:
@ -919,42 +919,41 @@ in %1</source>
<translation>сохранять пароль (небезопасно)</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="355"/>
<source>Library version</source>
<translation>Версия</translation>
<translation type="vanished">Версия</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="369"/>
<location filename="../../src/settingseditor.ui" line="359"/>
<source>User substitutions</source>
<translation>Пользовательская коррекция</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="392"/>
<location filename="../../src/settingseditor.ui" line="382"/>
<source>Use auto corrections (hunspell)</source>
<translation>Использовать автокоррекцию (hunspell)</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="399"/>
<location filename="../../src/settingseditor.ui" line="389"/>
<source>Use user substitutions</source>
<translation>Использовать пользовательскую коррекцию</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="406"/>
<location filename="../../src/settingseditor.ui" line="396"/>
<source>Hunspell dictionaries path:</source>
<translation>Путь к словарям Hunspell:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="468"/>
<location filename="../../src/settingseditor.ui" line="458"/>
<source>Language:</source>
<translation>Язык:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="504"/>
<location filename="../../src/settingseditor.ui" line="494"/>
<source> secs</source>
<translation> сек</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="444"/>
<location filename="../../src/settingseditor.ui" line="434"/>
<source>Ignore SSL errors</source>
<translation>Игнорировать ошибки SSL</translation>
</message>
@ -984,102 +983,102 @@ in %1</source>
<translation>Писать логи в файл (отладка)</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="342"/>
<location filename="../../src/settingseditor.ui" line="345"/>
<source>Default language:</source>
<translation>Язык по умолчанию:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="329"/>
<location filename="../../src/settingseditor.ui" line="303"/>
<source>Tessdata path:</source>
<translation>Путь к языкам (tessdata):</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="379"/>
<location filename="../../src/settingseditor.ui" line="369"/>
<source>\\ for \ symbol, \n for newline</source>
<translation>\\ для символа \ , \n для символа новой строки</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="427"/>
<location filename="../../src/settingseditor.ui" line="417"/>
<source>Translators path:</source>
<translation>Путь к переводчикам:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="434"/>
<location filename="../../src/settingseditor.ui" line="424"/>
<source>Translators</source>
<translation>Переводчики</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="569"/>
<location filename="../../src/settingseditor.ui" line="559"/>
<source>Result window</source>
<translation>Окно результата</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="575"/>
<location filename="../../src/settingseditor.ui" line="565"/>
<source>Font:</source>
<translation>Шрифт:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="585"/>
<location filename="../../src/settingseditor.ui" line="575"/>
<source>Font size:</source>
<translation>Размер шрифта:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="602"/>
<location filename="../../src/settingseditor.ui" line="592"/>
<source>Font color:</source>
<translation>Цвет шрифта:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="616"/>
<location filename="../../src/settingseditor.ui" line="606"/>
<source>Background:</source>
<translation>Фон:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="630"/>
<location filename="../../src/settingseditor.ui" line="620"/>
<source>Show image</source>
<translation>Показывать изображение</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="637"/>
<location filename="../../src/settingseditor.ui" line="627"/>
<source>Show recognized</source>
<translation>Показывать распознанное</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="687"/>
<location filename="../../src/settingseditor.ui" line="677"/>
<source>Update check interval (days):</source>
<translation>Интервал проверки обновления (дней):</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="694"/>
<location filename="../../src/settingseditor.ui" line="684"/>
<source>0 - disabled</source>
<translation>- отключено</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="454"/>
<location filename="../../src/settingseditor.ui" line="444"/>
<source>Translate text</source>
<translation>Переводить текст</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="461"/>
<location filename="../../src/settingseditor.ui" line="451"/>
<source>Single translator timeout:</source>
<translation>Переходить к следующему переводчику после:</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="531"/>
<location filename="../../src/settingseditor.ui" line="521"/>
<source>Result type</source>
<translation>Тип результата</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="543"/>
<location filename="../../src/settingseditor.ui" line="533"/>
<source>Tray</source>
<translation>Трей</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="556"/>
<location filename="../../src/settingseditor.ui" line="546"/>
<source>Window</source>
<translation>Окно</translation>
</message>
<message>
<location filename="../../src/settingseditor.ui" line="710"/>
<location filename="../../src/settingseditor.ui" line="700"/>
<source>Check now</source>
<translation>Проверить сейчас</translation>
</message>
@ -1175,32 +1174,29 @@ Hunspell ищет в своем словаре слова, похожие на
<translation>HTTP</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="126"/>
<source>Optimized</source>
<translation>Оптимизированная</translation>
<translation type="vanished">Оптимизированная</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="127"/>
<source>Compatible</source>
<translation>Совместимая</translation>
<translation type="vanished">Совместимая</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="131"/>
<source>Use compatible version if you are experiencing crashes during recognition</source>
<translation>Используйте совместимую версию если программа неожиданно завершается во время распознавания</translation>
<translation type="vanished">Используйте совместимую версию если программа неожиданно завершается во время распознавания</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="143"/>
<location filename="../../src/settingseditor.cpp" line="135"/>
<source>&lt;b&gt;NOTE! Some translators might require the translation window to be visible. You can make it using the &quot;Show translator&quot; entry in the tray icon&apos;s context menu&lt;/b&gt;</source>
<translation>&lt;b&gt;ПРИМЕЧАНИЕ! Для работы некоторых переводчиков может потребоваться активное окно перевода. Его можно отобразить при помощи пункта &quot;Показать окно перевода&quot; контекстного меню иконки в трее&lt;/b&gt;</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="151"/>
<location filename="../../src/settingseditor.cpp" line="143"/>
<source>Sample text</source>
<translation>Текст для проверки</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="215"/>
<location filename="../../src/settingseditor.cpp" line="208"/>
<source>The program workflow consists of the following steps:
1. Selection on the screen area
2. Recognition of the selected area
@ -1223,7 +1219,7 @@ Then set default recognition and translation languages, enable some (or all) tra
Далее установите языки распознавания и перевода по умолчанию, активируйте некоторые (или все) переводчики и настройку &quot;переводить текст&quot;, если нужно.</translation>
</message>
<message>
<location filename="../../src/settingseditor.cpp" line="380"/>
<location filename="../../src/settingseditor.cpp" line="371"/>
<source>Portable changed. Apply settings first</source>
<translation>Portable режим изменен. Сначала примените настройки</translation>
</message>

View File

@ -12,7 +12,8 @@ CaptureArea::CaptureArea(const QRect &rect, const Settings &settings)
{
}
TaskPtr CaptureArea::task(const QPixmap &pixmap) const
TaskPtr CaptureArea::task(const QPixmap &pixmap,
const QPoint &pixmapOffset) const
{
if (pixmap.isNull() || !isValid())
return {};
@ -21,7 +22,7 @@ TaskPtr CaptureArea::task(const QPixmap &pixmap) const
task->generation = generation_;
task->useHunspell = useHunspell_;
task->captured = pixmap.copy(rect_);
task->capturePoint = rect_.topLeft();
task->capturePoint = pixmapOffset + rect_.topLeft();
task->sourceLanguage = sourceLanguage_;
if (task->sourceLanguage.isEmpty())
task->error += QObject::tr("No source language set");

View File

@ -11,7 +11,7 @@ class CaptureArea
{
public:
CaptureArea(const QRect& rect, const Settings& settings);
TaskPtr task(const QPixmap& pixmap) const;
TaskPtr task(const QPixmap& pixmap, const QPoint& pixmapOffset) const;
void setGeneration(uint generation);
bool isValid() const;

View File

@ -339,7 +339,7 @@ void CaptureAreaSelector::customize(const std::shared_ptr<CaptureArea> &area)
edited_ = area;
editor_->show();
const auto topLeft = service::geometry::cornerAtPoint(
area->rect().center(), editor_->size(), geometry());
area->rect().center(), editor_->size(), QRect({}, size()));
editor_->move(topLeft);
update();
}

View File

@ -91,7 +91,7 @@ void Capturer::selected(const CaptureArea &area)
selector_->hide();
SOFT_ASSERT(!pixmap_.isNull(), return manager_.captureCanceled())
auto task = area.task(pixmap_);
auto task = area.task(pixmap_, pixmapOffset_);
if (task)
manager_.captured(task);
else

View File

@ -51,7 +51,7 @@ void Corrector::correct(const TaskPtr &task)
task->corrected = task->recognized;
if (!settings_.userSubstitutions.empty()) {
if (settings_.useUserSubstitutions && !settings_.userSubstitutions.empty()) {
task->corrected = substituteUser(task->recognized, task->sourceLanguage);
LTRACE() << "Corrected with user data";
}

View File

@ -4,6 +4,7 @@
#include <optional>
#include <unordered_map>
#include <vector>
using LanguageId = QString;

View File

@ -79,9 +79,5 @@ void Recognizer::updateSettings()
SOFT_ASSERT(!settings_.tessdataPath.isEmpty(), return );
queue_.clear();
const auto libName =
(settings_.tesseractVersion == TesseractVersion::Optimized
? "tesseract-optimized"
: "tesseract-compatible");
emit reset(settings_.tessdataPath, libName);
emit reset(settings_.tessdataPath);
}

View File

@ -18,7 +18,7 @@ public:
signals:
void recognizeImpl(const TaskPtr &task);
void reset(const QString &tessdataPath, const QString &tesseractLibrary);
void reset(const QString &tessdataPath);
private:
void recognized(const TaskPtr &task);

View File

@ -17,8 +17,8 @@ void RecognizeWorker::handle(const TaskPtr &task)
if (!engines_.count(task->sourceLanguage)) {
LTRACE() << "Create OCR engine" << task->sourceLanguage;
auto engine = std::make_unique<Tesseract>(task->sourceLanguage,
tessdataPath_, tesseractLibrary_);
auto engine =
std::make_unique<Tesseract>(task->sourceLanguage, tessdataPath_);
if (!engine->isValid()) {
result->error = tr("Failed to init OCR engine: %1").arg(engine->error());
@ -43,14 +43,12 @@ void RecognizeWorker::handle(const TaskPtr &task)
emit finished(result);
}
void RecognizeWorker::reset(const QString &tessdataPath,
const QString &tesseractLibrary)
void RecognizeWorker::reset(const QString &tessdataPath)
{
if (tessdataPath_ == tessdataPath && tesseractLibrary_ == tesseractLibrary)
if (tessdataPath_ == tessdataPath)
return;
tessdataPath_ = tessdataPath;
tesseractLibrary_ = tesseractLibrary;
engines_.clear();
LTRACE() << "Cleared OCR engines";
}

View File

@ -13,7 +13,7 @@ public:
~RecognizeWorker();
void handle(const TaskPtr &task);
void reset(const QString &tessdataPath, const QString &tesseractLibrary);
void reset(const QString &tessdataPath);
signals:
void finished(const TaskPtr &task);
@ -24,5 +24,4 @@ private:
std::map<QString, std::unique_ptr<Tesseract>> engines_;
std::map<QString, Generation> lastGenerations_;
QString tessdataPath_;
QString tesseractLibrary_;
};

View File

@ -4,6 +4,7 @@
#include "task.h"
#include <leptonica/allheaders.h>
#include <tesseract/baseapi.h>
#include <QBuffer>
#include <QDir>
@ -90,145 +91,121 @@ static double getScale(Pix *source)
return scale;
}
static Pix *prepareImage(const QImage &image)
// Smart pointer for Pix
class PixGuard
{
auto pix = convertImage(image);
SOFT_ASSERT(pix, return nullptr);
LTRACE() << "Converted Pix" << pix;
auto gray = pixConvertRGBToGray(pix, 0.0, 0.0, 0.0);
LTRACE() << "Created gray Pix" << gray;
SOFT_ASSERT(gray, return nullptr);
pixDestroy(&pix);
LTRACE() << "Removed converted Pix";
auto scaleSource = gray;
auto scaled = scaleSource;
if (const auto scale = getScale(scaleSource); scale > 1.0) {
scaled = pixScale(scaleSource, scale, scale);
LTRACE() << "Scaled Pix for OCR" << LARG(scale) << LARG(scaled);
if (!scaled)
scaled = scaleSource;
}
if (scaled != scaleSource) {
pixDestroy(&scaleSource);
LTRACE() << "Removed unscaled Pix";
}
return scaled;
}
static void cleanupImage(Pix **image)
{
pixDestroy(image);
}
// do not include capi.h from tesseract because it defined BOOL that breaks msvc
struct TessBaseAPI;
class Tesseract::Wrapper
{
using CreateApi = TessBaseAPI *(*)();
using DeleteApi = void (*)(TessBaseAPI *);
using InitApi = int (*)(TessBaseAPI *, const char *, const char *, int);
using SetImage = void (*)(TessBaseAPI *, struct Pix *);
using GetUtf8 = char *(*)(TessBaseAPI *);
using ClearApi = void (*)(TessBaseAPI *);
using DeleteUtf8 = void (*)(const char *);
using SetPageMode = void (*)(TessBaseAPI *, int);
public:
explicit Wrapper(const QString &libraryName)
: lib(libraryName)
explicit PixGuard(Pix *pix = nullptr)
: pix_(pix)
{
if (!lib.load()) {
LERROR() << "Failed to load tesseract library" << libraryName;
}
~PixGuard()
{
if (pix_)
pixDestroy(&pix_);
}
void operator=(Pix *pix)
{
if (!pix)
return;
if (pix_)
pixDestroy(&pix_);
pix_ = pix;
}
LTRACE() << "Loaded tesseract library" << lib.fileName();
auto ok = true;
ok &= bool(createApi_ = (CreateApi)lib.resolve("TessBaseAPICreate"));
ok &= bool(deleteApi_ = (DeleteApi)lib.resolve("TessBaseAPIDelete"));
ok &= bool(initApi_ = (InitApi)lib.resolve("TessBaseAPIInit2"));
ok &= bool(setImage_ = (SetImage)lib.resolve("TessBaseAPISetImage2"));
ok &= bool(getUtf8_ = (GetUtf8)lib.resolve("TessBaseAPIGetUTF8Text"));
ok &= bool(clearApi_ = (ClearApi)lib.resolve("TessBaseAPIClear"));
ok &= bool(deleteUtf8_ = (DeleteUtf8)lib.resolve("TessDeleteText"));
ok &= bool(setPageMode_ =
(SetPageMode)lib.resolve("TessBaseAPISetPageSegMode"));
if (!ok) {
LERROR() << "Failed to resolve tesseract functions from" << libraryName;
operator Pix *() { return pix_; }
Pix *operator->() { return pix_; }
Pix *&get() { return pix_; }
Pix *take()
{
auto ret = pix_;
pix_ = nullptr;
return ret;
}
void trace(const QString &name) const
{
LTRACE() << qPrintable(name) << pix_;
#if 0
if (!pix_)
return;
}
handle_ = createApi_();
}
~Wrapper()
{
if (handle_ && deleteApi_) {
deleteApi_(handle_);
}
lib.unload();
}
int Init(const char *datapath, const char *language)
{
SOFT_ASSERT(handle_, return -1);
SOFT_ASSERT(initApi_, return -1);
const auto mode = 3; // TessOcrEngineMode::OEM_DEFAULT
return initApi_(handle_, datapath, language, mode);
}
QString GetText(Pix *pix)
{
SOFT_ASSERT(handle_, return {});
SOFT_ASSERT(setPageMode_, return {});
setPageMode_(handle_, 3); // PSM_AUTO
SOFT_ASSERT(setImage_, return {});
setImage_(handle_, pix);
LTRACE() << "Set Pix to engine";
char *outText = nullptr;
SOFT_ASSERT(getUtf8_, return {});
outText = getUtf8_(handle_);
LTRACE() << "Received recognized text";
SOFT_ASSERT(clearApi_, return {});
clearApi_(handle_);
LTRACE() << "Cleared engine";
const auto result = QString(outText).trimmed();
SOFT_ASSERT(deleteUtf8_, return {});
deleteUtf8_(outText);
LTRACE() << "Cleared recognized text buffer";
return result;
auto fileName = name + ".png";
fileName.replace(' ', "_");
convertImage(*pix_).save(fileName);
#endif
}
private:
QLibrary lib;
CreateApi createApi_{nullptr};
DeleteApi deleteApi_{nullptr};
InitApi initApi_{nullptr};
SetImage setImage_{nullptr};
GetUtf8 getUtf8_{nullptr};
ClearApi clearApi_{nullptr};
DeleteUtf8 deleteUtf8_{nullptr};
SetPageMode setPageMode_{nullptr};
TessBaseAPI *handle_{nullptr};
Pix *pix_;
Q_DISABLE_COPY(PixGuard);
};
Tesseract::Tesseract(const LanguageId &language, const QString &tessdataPath,
const QString &tesseractLibrary)
: tesseractLibrary_(tesseractLibrary)
static Pix *prepareImage(const QImage &image)
{
auto pix = PixGuard(convertImage(image));
SOFT_ASSERT(pix, return nullptr);
pix.trace("Pix 1 Converted");
{
pix = pixConvertRGBToGray(pix, 0.0, 0.0, 0.0);
pix.trace("Pix 2 Gray");
}
if (const auto scale = getScale(pix); scale > 1.0) {
pix = pixScaleGrayLI(pix, scale, scale);
pix.trace("Pix 3 Scaled");
}
l_int32 otsuSx = 5000;
l_int32 otsuSy = 5000;
l_int32 otsuSmoothx = 0;
l_int32 otsuSmoothy = 0;
l_float32 otsuScorefract = 0.1f;
{
PixGuard otsu;
pixOtsuAdaptiveThreshold(pix, otsuSx, otsuSy, otsuSmoothx, otsuSmoothy,
otsuScorefract, nullptr, &otsu.get());
pix.trace("Pix 4 Test Color Otsu");
// Get the average intensity of the border pixels,
// with average of 0.0 being completely white and 1.0 being completely black
// Top
auto avg = pixAverageOnLine(otsu, 0, 0, otsu->w - 1, 0, 1);
// Bottom
avg += pixAverageOnLine(otsu, 0, otsu->h - 1, otsu->w - 1, otsu->h - 1, 1);
// Left
avg += pixAverageOnLine(otsu, 0, 0, 0, otsu->h - 1, 1);
// Right
avg += pixAverageOnLine(otsu, otsu->w - 1, 0, otsu->w - 1, otsu->h - 1, 1);
avg /= 4.0f;
// If background is dark
l_float32 threshold = 0.5f;
if (avg > threshold) {
pix = pixInvert(nullptr, pix);
pix.trace("Pix 5 Inverted");
}
}
{
l_int32 usm_halfwidth = 5;
l_float32 usm_fract = 2.5f;
pix = pixUnsharpMaskingGray(pix, usm_halfwidth, usm_fract);
pix.trace("Pix 6 Unshapred");
}
{
pixOtsuAdaptiveThreshold(pix, otsuSx, otsuSy, otsuSmoothx, otsuSmoothy, 0.0,
nullptr, &pix.get());
pix.trace("Pix 7 Binarized");
}
pix.trace("Pix 8 Result");
return pix.take();
}
Tesseract::Tesseract(const LanguageId &language, const QString &tessdataPath)
{
SOFT_ASSERT(!tessdataPath.isEmpty(), return );
SOFT_ASSERT(!language.isEmpty(), return );
@ -240,20 +217,22 @@ Tesseract::~Tesseract() = default;
void Tesseract::init(const LanguageId &language, const QString &tessdataPath)
{
SOFT_ASSERT(!engine_, return );
SOFT_ASSERT(!api_, return );
engine_ = std::make_unique<Wrapper>(tesseractLibrary_);
LTRACE() << "Created Tesseract api" << engine_.get();
api_ = std::make_unique<tesseract::TessBaseAPI>();
LTRACE() << "Created Tesseract api" << api_.get();
const auto tesseractName = LanguageCodes::tesseract(language);
auto result =
engine_->Init(qPrintable(tessdataPath), qPrintable(tesseractName));
auto result = api_->Init(qPrintable(tessdataPath), qPrintable(tesseractName),
tesseract::OcrEngineMode::OEM_DEFAULT);
LTRACE() << "Inited Tesseract api" << result;
if (result == 0)
return;
api_->SetPageSegMode(tesseract::PageSegMode::PSM_AUTO);
error_ = QObject::tr("init failed");
engine_.reset();
api_.reset();
LTRACE() << "Cleared Tesseract api";
}
@ -288,19 +267,28 @@ QStringList Tesseract::availableLanguageNames(const QString &path)
QString Tesseract::recognize(const QPixmap &source)
{
SOFT_ASSERT(engine_, return {});
SOFT_ASSERT(api_, return {});
SOFT_ASSERT(!source.isNull(), return {});
error_.clear();
Pix *image = prepareImage(source.toImage());
PixGuard image(prepareImage(source.toImage()));
SOFT_ASSERT(image, return {});
LTRACE() << "Preprocessed Pix for OCR" << image;
auto result = engine_->GetText(image);
api_->SetImage(image);
LTRACE() << "Set Pix to engine";
cleanupImage(&image);
LTRACE() << "Cleared preprocessed Pix";
const auto outText = api_->GetUTF8Text();
LTRACE() << "Received recognized text";
api_->Clear();
LTRACE() << "Cleared engine";
const auto result = QString(outText).trimmed();
delete[] outText;
LTRACE() << "Cleared recognized text buffer";
if (result.isEmpty())
error_ = QObject::tr("Failed to recognize text or no text selected");
@ -309,5 +297,5 @@ QString Tesseract::recognize(const QPixmap &source)
bool Tesseract::isValid() const
{
return engine_.get();
return api_.get();
}

View File

@ -8,12 +8,15 @@
class QPixmap;
class Task;
namespace tesseract
{
class TessBaseAPI;
}
class Tesseract
{
public:
Tesseract(const LanguageId& language, const QString& tessdataPath,
const QString& tesseractLibrary);
Tesseract(const LanguageId& language, const QString& tessdataPath);
~Tesseract();
QString recognize(const QPixmap& source);
@ -23,10 +26,8 @@ public:
static QStringList availableLanguageNames(const QString& path);
private:
class Wrapper;
void init(const LanguageId& language, const QString& tessdataPath);
const QString tesseractLibrary_;
std::unique_ptr<Wrapper> engine_;
std::unique_ptr<tesseract::TessBaseAPI> api_;
QString error_;
};

View File

@ -135,6 +135,10 @@ bool Representer::eventFilter(QObject * /*watched*/, QEvent *event)
const auto casted = static_cast<QMouseEvent *>(event);
if (casted->button() == Qt::LeftButton)
hide();
} else if (event->type() == QEvent::KeyPress) {
const auto casted = static_cast<QKeyEvent *>(event);
if (casted->key() == Qt::Key_Escape)
hide();
}
return false;
}

View File

@ -11,13 +11,15 @@
#include <QLabel>
#include <QMenu>
#include <QMouseEvent>
#include <QScreen>
ResultWidget::ResultWidget(Manager &manager, Representer &representer,
const Settings &settings, QWidget *parent)
: QFrame(parent)
, representer_(representer)
, settings_(settings)
, image_(new QLabel(this))
, imagePlaceholder_(new QWidget(this))
, image_(new QLabel(imagePlaceholder_))
, recognized_(new QLabel(this))
, separator_(new QLabel(this))
, translated_(new QLabel(this))
@ -30,8 +32,6 @@ ResultWidget::ResultWidget(Manager &manager, Representer &representer,
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Plain);
image_->setAlignment(Qt::AlignCenter);
recognized_->setAlignment(Qt::AlignCenter);
recognized_->setWordWrap(true);
@ -65,7 +65,7 @@ ResultWidget::ResultWidget(Manager &manager, Representer &representer,
installEventFilter(this);
auto layout = new QVBoxLayout(this);
layout->addWidget(image_);
layout->addWidget(imagePlaceholder_);
layout->addWidget(recognized_);
layout->addWidget(separator_);
layout->addWidget(translated_);
@ -86,6 +86,8 @@ void ResultWidget::show(const TaskPtr &task)
task_ = task;
image_->setPixmap(task->captured);
image_->resize(task->captured.size());
imagePlaceholder_->setMinimumSize(image_->size());
recognized_->setText(task->corrected);
const auto tooltip = task->recognized == task->corrected
@ -106,20 +108,37 @@ void ResultWidget::show(const TaskPtr &task)
show();
adjustSize();
if (!image_->isVisible())
// window should not be smaller than selected image
if (!imagePlaceholder_->isVisible())
resize(std::max(width(), task->captured.width()),
std::max(height(), task->captured.height()));
QDesktopWidget *desktop = QApplication::desktop();
Q_CHECK_PTR(desktop);
const auto correction =
QPoint((width() - task->captured.width()) / 2, lineWidth());
auto rect = QRect(task->capturePoint - correction, size());
// if window is wider than image then image should be at horizontal center
const auto correctionToCenterImage =
QPoint((width() - task->captured.width()) / 2, 2 * lineWidth());
auto rect = QRect(task->capturePoint - correctionToCenterImage, size());
const auto screenRect = desktop->screenGeometry(this);
auto screen = QApplication::screenAt(task->capturePoint);
SOFT_ASSERT(screen, return );
const auto screenRect = screen->geometry();
// window should not exceed horizontal borders
if (rect.right() > screenRect.right())
rect.moveRight(screenRect.right());
if (rect.left() < screenRect.left())
rect.moveLeft(screenRect.left());
// image should be where it was selected
if (imagePlaceholder_->isVisible()) {
const auto imageOffset =
task->capturePoint.x() - rect.left() - 2 * lineWidth();
image_->move(imageOffset, image_->y());
}
// window should not exceed vertical borders
const auto shouldTextOnTop = rect.bottom() > screenRect.bottom();
if (shouldTextOnTop)
rect.moveBottom(rect.top() + task->captured.height() + lineWidth());
rect.moveBottom(rect.top() + task->captured.height() + 3 * lineWidth());
auto layout = static_cast<QBoxLayout *>(this->layout());
SOFT_ASSERT(layout, return );
@ -156,7 +175,7 @@ void ResultWidget::updateSettings()
palette.setColor(QPalette::Window, separatorColor);
separator_->setPalette(palette);
image_->setVisible(settings_.showCaptured);
imagePlaceholder_->setVisible(settings_.showCaptured);
}
void ResultWidget::mousePressEvent(QMouseEvent *event)

View File

@ -31,6 +31,7 @@ private:
Representer& representer_;
const Settings& settings_;
TaskPtr task_;
QWidget* imagePlaceholder_;
QLabel* image_;
QLabel* recognized_;
QLabel* separator_;

View File

@ -896,11 +896,6 @@ QDateTime Updater::lastUpdateCheck() const
void Updater::setAutoUpdate(int intervalDays, const QDateTime &lastCheck)
{
if (intervalDays < 1) {
autoChecker_.reset();
return;
}
autoChecker_ = std::make_unique<AutoChecker>(*this, intervalDays, lastCheck);
}

View File

@ -7,6 +7,8 @@
#include <QSettings>
#include <QStandardPaths>
#include <array>
namespace
{
const QString iniFileName()
@ -33,7 +35,6 @@ const QString qs_showMessageOnStart = "showMessageOnStart";
const QString qs_recogntionGroup = "Recognition";
const QString qs_ocrLanguage = "language";
const QString qs_tesseractVersion = "tesseractVersion";
const QString qs_correctionGroup = "Correction";
const QString qs_userSubstitutions = "userSubstitutions";
@ -131,51 +132,6 @@ void cleanupOutdated(QSettings& settings)
settings.endGroup();
}
#ifdef _MSC_VER
#include <intrin.h>
#endif
#ifdef __GNUC__
void __cpuid(int* cpuinfo, int info)
{
__asm__ __volatile__(
"xchg %%ebx, %%edi;"
"cpuid;"
"xchg %%ebx, %%edi;"
: "=a"(cpuinfo[0]), "=D"(cpuinfo[1]), "=c"(cpuinfo[2]), "=d"(cpuinfo[3])
: "0"(info));
}
unsigned long long _xgetbv(unsigned int index)
{
unsigned int eax, edx;
__asm__ __volatile__("xgetbv;" : "=a"(eax), "=d"(edx) : "c"(index));
return ((unsigned long long)edx << 32) | eax;
}
#endif
bool checkOptimizedTesseractSupport()
{
bool sse4_1Supportted = false;
bool sse4_2Supportted = false;
bool avxSupportted = false;
int cpuinfo[4];
__cpuid(cpuinfo, 1);
sse4_1Supportted = cpuinfo[2] & (1 << 19) || false;
sse4_2Supportted = cpuinfo[2] & (1 << 20) || false;
avxSupportted = cpuinfo[2] & (1 << 28) || false;
bool osxsaveSupported = cpuinfo[2] & (1 << 27) || false;
if (osxsaveSupported && avxSupportted) {
// _XCR_XFEATURE_ENABLED_MASK = 0
unsigned long long xcrFeatureMask = _xgetbv(0);
avxSupportted = (xcrFeatureMask & 0x6) == 0x6;
}
return sse4_1Supportted && sse4_2Supportted && avxSupportted;
}
} // namespace
void Settings::save() const
@ -220,7 +176,6 @@ void Settings::save() const
settings.beginGroup(qs_recogntionGroup);
settings.setValue(qs_ocrLanguage, sourceLanguage);
settings.setValue(qs_tesseractVersion, int(tesseractVersion));
settings.endGroup();
settings.beginGroup(qs_correctionGroup);
@ -308,15 +263,6 @@ void Settings::load()
settings.beginGroup(qs_recogntionGroup);
sourceLanguage = settings.value(qs_ocrLanguage, sourceLanguage).toString();
if (!settings.contains(qs_tesseractVersion)) {
tesseractVersion = checkOptimizedTesseractSupport()
? TesseractVersion::Optimized
: TesseractVersion::Compatible;
} else {
tesseractVersion = TesseractVersion(std::clamp(
settings.value(qs_tesseractVersion, int(tesseractVersion)).toInt(),
int(TesseractVersion::Optimized), int(TesseractVersion::Compatible)));
}
settings.endGroup();
settings.beginGroup(qs_correctionGroup);

View File

@ -19,8 +19,6 @@ using Substitutions = std::multimap<LanguageId, Substitution>;
enum class ProxyType { Disabled, System, Socks5, Http };
enum class TesseractVersion { Optimized, Compatible };
class Settings
{
public:
@ -60,7 +58,6 @@ public:
QString tessdataPath;
QString sourceLanguage{"eng"};
TesseractVersion tesseractVersion{TesseractVersion::Optimized};
bool doTranslation{true};
bool ignoreSslErrors{false};

View File

@ -122,14 +122,6 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Updater &updater)
// recognition
ui->tesseractLangCombo->setModel(models_.sourceLanguageModel());
const QMap<TesseractVersion, QString> tesseractVersions{
{TesseractVersion::Optimized, tr("Optimized")},
{TesseractVersion::Compatible, tr("Compatible")},
};
ui->tesseractVersion->addItems(tesseractVersions.values());
ui->tesseractVersion->setToolTip(
tr("Use compatible version if you are experiencing crashes during "
"recognition"));
// correction
ui->userSubstitutionsTable->setEnabled(ui->useUserSubstitutions->isChecked());
@ -198,7 +190,8 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Updater &updater)
R"(<p>Optical character recognition (OCR) and translation tool</p>)"),
QObject::tr(R"(<p>Version: %1</p>)")
.arg(QApplication::applicationVersion()),
QObject::tr(R"(<p>Setup instructions: <a href="%1">%1</a></p>)").arg(help),
QObject::tr(R"(<p>Setup instructions: <a href="%1">%1</a></p>)")
.arg(help),
QObject::tr(R"(<p>Changelog: <a href="%1">%2</a></p>)")
.arg(changelog, QUrl(changelog).fileName()),
QObject::tr(R"(<p>License: <a href="%3">MIT</a></p>)").arg(license),
@ -267,8 +260,6 @@ Settings SettingsEditor::settings() const
settings.sourceLanguage =
LanguageCodes::idForName(ui->tesseractLangCombo->currentText());
settings.tesseractVersion =
TesseractVersion(ui->tesseractVersion->currentIndex());
settings.useHunspell = ui->useHunspell->isChecked();
settings.useUserSubstitutions = ui->useUserSubstitutions->isChecked();
@ -323,10 +314,11 @@ void SettingsEditor::setSettings(const Settings &settings)
ui->proxySaveCheck->setChecked(settings.proxySavePassword);
ui->tessdataPath->setText(settings.tessdataPath);
ui->translatorsPath->setText(settings.translatorsPath);
updateModels();
ui->tesseractLangCombo->setCurrentText(
LanguageCodes::name(settings.sourceLanguage));
ui->tesseractVersion->setCurrentIndex(int(settings.tesseractVersion));
ui->useHunspell->setChecked(settings.useHunspell);
ui->hunspellDir->setText(settings.hunspellPath);
@ -336,7 +328,6 @@ void SettingsEditor::setSettings(const Settings &settings)
ui->doTranslationCheck->setChecked(settings.doTranslation);
ui->ignoreSslCheck->setChecked(settings.ignoreSslErrors);
ui->translateTimeoutSpin->setValue(settings.translationTimeout.count());
ui->translatorsPath->setText(settings.translatorsPath);
ui->translateLangCombo->setCurrentText(
LanguageCodes::name(settings.targetLanguage));
updateTranslators(settings.translators);
@ -408,7 +399,14 @@ void SettingsEditor::updateTranslators(const QStringList &translators)
if (models_.translators().isEmpty())
return;
ui->translatorList->addItems(models_.translators());
QStringList all;
for (const auto &i : translators) {
if (models_.translators().contains(i))
all.append(i);
}
all += models_.translators();
all.removeDuplicates();
ui->translatorList->addItems(all);
for (auto i = 0, end = ui->translatorList->count(); i < end; ++i) {
auto item = ui->translatorList->item(i);

View File

@ -221,7 +221,7 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="1">
<widget class="service::KeySequenceEdit" name="clipboardEdit"/>
<widget class="service::KeySequenceEdit" name="clipboardEdit" native="true"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
@ -231,7 +231,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="service::KeySequenceEdit" name="repeatCaptureEdit"/>
<widget class="service::KeySequenceEdit" name="repeatCaptureEdit" native="true"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
@ -241,7 +241,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="service::KeySequenceEdit" name="captureEdit"/>
<widget class="service::KeySequenceEdit" name="captureEdit" native="true"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
@ -251,7 +251,7 @@
</widget>
</item>
<item row="3" column="1">
<widget class="service::KeySequenceEdit" name="repeatEdit"/>
<widget class="service::KeySequenceEdit" name="repeatEdit" native="true"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
@ -268,7 +268,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="service::KeySequenceEdit" name="captureLockedEdit"/>
<widget class="service::KeySequenceEdit" name="captureLockedEdit" native="true"/>
</item>
</layout>
</widget>
@ -291,7 +291,23 @@
</widget>
<widget class="QWidget" name="pageRecognize">
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="2">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Tessdata path:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="tesseractLangCombo"/>
</item>
<item row="2" column="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -317,19 +333,6 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Tessdata path:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
@ -346,19 +349,6 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="tesseractLangCombo"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Library version</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="tesseractVersion"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageCorrect">

View File

@ -4,8 +4,16 @@ var active = window.location.href !== "about:blank";
function checkFinished() {
if (!active) return;
let area = document.querySelector('textarea[dl-test=translator-target-input]');
let text = area ? area.value : '';
let area = document.querySelector('div#target-dummydiv');
let text = area ? area.innerHTML.trim() : '';
if (area == null) {
area = document.querySelector('d-textarea.lmt__target_textarea p');
text = area ? area.innerText.trim() : '';
}
if (area == null) {
area = document.querySelector('d-textarea[data-testid=translator-target-input] p');
text = area ? area.innerText.trim() : '';
}
if (text === lastText || text === '')
return;
@ -27,7 +35,9 @@ function translate(text, from, to) {
from = from == 'zh-CN' ? 'zh' : from;
to = to == 'zh-CN' ? 'zh' : to;
let supported = ['ru', 'en', 'de', 'fr', 'es', 'pt', 'it', 'nl', 'pl', 'ja', 'zh']
let supported = ['ru', 'en', 'de', 'fr', 'es', 'pt', 'it', 'nl', 'pl', 'ja', 'zh',
'uk', 'bg', 'hu', 'el', 'da', 'id', 'lt', 'pt', 'ro', 'sk', 'sk', 'tr', 'fi', 'cs',
'sv', 'et']
if (supported.indexOf(from) == -1) {
proxy.setFailed('Source language not supported');
return;
@ -39,21 +49,32 @@ function translate(text, from, to) {
active = true;
var singleLineText = text.replace(/(?:\r\n|\r|\n)/g, ' ');
let langs = from + '/' + to + '/';
if (window.location.href.indexOf('www.deepl.com/translator') !== -1
&& window.location.href.indexOf(langs) !== -1) {
var input = document.querySelector('textarea[dl-test=translator-source-input]');
if (input.value == text) {
var input = document.querySelector('d-textarea[dl-test=translator-source-input] p');
if (input == null)
input = document.querySelector('d-textarea.lmt__source_textarea p');
if (input == null)
input = document.querySelector('d-textarea[data-testid=translator-source-input] p');
if (input.innerText == singleLineText) {
console.log('using cached result');
lastText = '';
return;
}
input.value = text;
input.innerText = singleLineText;
if (areaCopy = document.querySelector('div#source-dummydiv'))
areaCopy.innerHTML = singleLineText;
setTimeout(function () {
input.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
}, 300);
return;
}
let url = 'https://www.deepl.com/translator#' + langs + encodeURIComponent(text);
let url = 'https://www.deepl.com/translator#' + langs + encodeURIComponent(singleLineText);
console.log("setting url", url);
window.location = url;
}

View File

@ -4,7 +4,7 @@ var active = window.location.href !== "about:blank";
function checkFinished() {
if (!active) return;
let spans = [].slice.call(document.querySelectorAll('span.VIiyi > span > span'));
let spans = [].slice.call(document.querySelectorAll('span.HwtZe > span > span'));
let text = spans.reduce(function (res, i) {
return res + ' ' + i.innerText;
}, '');

View File

@ -4,9 +4,9 @@ var active = window.location.href !== "about:blank";
function checkFinished() {
if (!active) return;
let spans = [].slice.call(document.querySelectorAll('span.translation-chunk'));
let spans = [].slice.call(document.querySelectorAll('span.translation-word'));
let text = spans.reduce(function (res, i) {
return res + ' ' + i.innerText;
return res + i.innerText;
}, '').trim();
if (text === lastText || text === '')

View File

@ -2,9 +2,9 @@
"version":1
,"app":{
"win32":{"version":"3.2.1", "host":"win32", "files":[{"path":"$appdir$/screen-translator.exe", "md5":"30a87d5ae32c6cb384a021f53566f8a9"}]}
,"win64":{"version":"3.2.1", "host":"win64", "files":[{"path":"$appdir$/screen-translator.exe", "md5":"181a72953ed3bcfa1de41c8f9ca7360c"}]}
,"linux":{"version":"3.2.1", "host":"linux", "files":[{"path":"$appdir$/screen-translator", "md5":"be84c13ee89badfb99a5df579176353a"}]}
"win32":{"version":"3.3.0", "host":"win32", "files":[{"path":"$appdir$/screen-translator.exe", "md5":"414c74c4594e0b90aff3cd86a73f96dd"}]}
,"win64":{"version":"3.3.0", "host":"win64", "files":[{"path":"$appdir$/screen-translator.exe", "md5":"3f2c3c27364f25c239ea63243a8910a3"}]}
,"linux":{"version":"3.3.0", "host":"linux", "files":[{"path":"$appdir$/screen-translator", "md5":"a091be0443fd128a02b01b313e0270bc"}]}
}
@ -387,188 +387,200 @@
,"correction": {
"Afrikaans":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/af_ZA/af_ZA.aff","https://translator.gres.biz/resources/dictionaries/af_ZA/af_ZA.aff.zip"], "path":"$hunspell$/af/af_ZA.aff", "date":"2020-03-17T12:21:16+01:00", "size":5027}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/af_ZA/af_ZA.dic","https://translator.gres.biz/resources/dictionaries/af_ZA/af_ZA.dic.zip"], "path":"$hunspell$/af/af_ZA.dic", "date":"2020-03-17T12:21:16+01:00", "size":1262203}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/af_ZA/af_ZA.aff","https://translator.gres.biz/resources/dictionaries/af_ZA/af_ZA.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/af_ZA/af_ZA.dic.zip"], "path":"$hunspell$/af/af_ZA.dic", "date":"2020-02-16T20:22:16+01:00", "size":1262203}
]}
, "Arabic":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ar/ar.aff","https://translator.gres.biz/resources/dictionaries/ar/ar.aff.zip"], "path":"$hunspell$/ar/ar.aff", "date":"2020-03-17T12:21:16+01:00", "size":86949}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ar/ar.dic","https://translator.gres.biz/resources/dictionaries/ar/ar.dic.zip"], "path":"$hunspell$/ar/ar.dic", "date":"2020-03-17T12:21:16+01:00", "size":7217161}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ar/ar.aff","https://translator.gres.biz/resources/dictionaries/ar/ar.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/ar/ar.dic.zip"], "path":"$hunspell$/ar/ar.dic", "date":"2019-03-07T11:32:58+01:00", "size":7217161}
]}
, "Belarusian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/be_BY/be_BY.aff","https://translator.gres.biz/resources/dictionaries/be_BY/be_BY.aff.zip"], "path":"$hunspell$/be/be_BY.aff", "date":"2020-03-17T12:21:16+01:00", "size":23968}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/be_BY/be_BY.dic","https://translator.gres.biz/resources/dictionaries/be_BY/be_BY.dic.zip"], "path":"$hunspell$/be/be_BY.dic", "date":"2020-03-17T12:21:16+01:00", "size":1707840}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/be_BY/be-official.aff","https://translator.gres.biz/resources/dictionaries/be_BY/be-official.aff.zip"], "path":"$hunspell$/be/be-official.aff", "date":"2021-09-27T10:14:30+02:00", "size":183480}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/be_BY/be-official.dic","https://translator.gres.biz/resources/dictionaries/be_BY/be-official.dic.zip"], "path":"$hunspell$/be/be-official.dic", "date":"2021-09-27T10:14:30+02:00", "size":9355556}
]}
, "Bulgarian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bg_BG/bg_BG.aff","https://translator.gres.biz/resources/dictionaries/bg_BG/bg_BG.aff.zip"], "path":"$hunspell$/bg/bg_BG.aff", "date":"2020-03-17T12:21:16+01:00", "size":58189}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bg_BG/bg_BG.dic","https://translator.gres.biz/resources/dictionaries/bg_BG/bg_BG.dic.zip"], "path":"$hunspell$/bg/bg_BG.dic", "date":"2020-03-17T12:21:16+01:00", "size":1566331}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bg_BG/bg_BG.aff","https://translator.gres.biz/resources/dictionaries/bg_BG/bg_BG.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/bg_BG/bg_BG.dic.zip"], "path":"$hunspell$/bg/bg_BG.dic", "date":"2018-06-29T12:25:29+02:00", "size":1566331}
]}
, "Bengali":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bn_BD/bn_BD.aff","https://translator.gres.biz/resources/dictionaries/bn_BD/bn_BD.aff.zip"], "path":"$hunspell$/bn/bn_BD.aff", "date":"2020-03-17T12:21:16+01:00", "size":195}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bn_BD/bn_BD.dic","https://translator.gres.biz/resources/dictionaries/bn_BD/bn_BD.dic.zip"], "path":"$hunspell$/bn/bn_BD.dic", "date":"2020-03-17T12:21:16+01:00", "size":2596038}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bn_BD/bn_BD.aff","https://translator.gres.biz/resources/dictionaries/bn_BD/bn_BD.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/bn_BD/bn_BD.dic.zip"], "path":"$hunspell$/bn/bn_BD.dic", "date":"2012-10-16T11:09:27-05:00", "size":2596038}
]}
, "Tibetan":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bo/bo.aff","https://translator.gres.biz/resources/dictionaries/bo/bo.aff.zip"], "path":"$hunspell$/bo/bo.aff", "date":"2020-03-17T12:21:16+01:00", "size":1706}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bo/bo.dic","https://translator.gres.biz/resources/dictionaries/bo/bo.dic.zip"], "path":"$hunspell$/bo/bo.dic", "date":"2020-03-17T12:21:16+01:00", "size":4637}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bo/bo.aff","https://translator.gres.biz/resources/dictionaries/bo/bo.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/bo/bo.dic.zip"], "path":"$hunspell$/bo/bo.dic", "date":"2017-10-23T18:37:13+02:00", "size":4637}
]}
, "Bosnian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bs_BA/bs_BA.aff","https://translator.gres.biz/resources/dictionaries/bs_BA/bs_BA.aff.zip"], "path":"$hunspell$/bs/bs_BA.aff", "date":"2020-03-17T12:21:16+01:00", "size":17468}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bs_BA/bs_BA.dic","https://translator.gres.biz/resources/dictionaries/bs_BA/bs_BA.dic.zip"], "path":"$hunspell$/bs/bs_BA.dic", "date":"2020-03-17T12:21:16+01:00", "size":339863}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/bs_BA/bs_BA.aff","https://translator.gres.biz/resources/dictionaries/bs_BA/bs_BA.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/bs_BA/bs_BA.dic.zip"], "path":"$hunspell$/bs/bs_BA.dic", "date":"2013-01-22T17:32:09+01:00", "size":339863}
]}
, "Czech":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/cs_CZ/cs_CZ.aff","https://translator.gres.biz/resources/dictionaries/cs_CZ/cs_CZ.aff.zip"], "path":"$hunspell$/cs/cs_CZ.aff", "date":"2020-03-17T12:21:16+01:00", "size":97286}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/cs_CZ/cs_CZ.dic","https://translator.gres.biz/resources/dictionaries/cs_CZ/cs_CZ.dic.zip"], "path":"$hunspell$/cs/cs_CZ.dic", "date":"2020-03-17T12:21:16+01:00", "size":2209232}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/cs_CZ/cs_CZ.aff","https://translator.gres.biz/resources/dictionaries/cs_CZ/cs_CZ.aff.zip"], "path":"$hunspell$/cs/cs_CZ.aff", "date":"2021-07-01T19:25:44+02:00", "size":111575}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/cs_CZ/cs_CZ.dic","https://translator.gres.biz/resources/dictionaries/cs_CZ/cs_CZ.dic.zip"], "path":"$hunspell$/cs/cs_CZ.dic", "date":"2021-07-28T19:02:59+02:00", "size":3656362}
]}
, "Danish":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/da_DK/da_DK.aff","https://translator.gres.biz/resources/dictionaries/da_DK/da_DK.aff.zip"], "path":"$hunspell$/da/da_DK.aff", "date":"2020-03-17T12:21:16+01:00", "size":55779}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/da_DK/da_DK.dic","https://translator.gres.biz/resources/dictionaries/da_DK/da_DK.dic.zip"], "path":"$hunspell$/da/da_DK.dic", "date":"2020-03-17T12:21:16+01:00", "size":2915525}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/da_DK/da_DK.aff","https://translator.gres.biz/resources/dictionaries/da_DK/da_DK.aff.zip"], "path":"$hunspell$/da/da_DK.aff", "date":"2022-06-09T11:42:30+02:00", "size":79054}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/da_DK/da_DK.dic","https://translator.gres.biz/resources/dictionaries/da_DK/da_DK.dic.zip"], "path":"$hunspell$/da/da_DK.dic", "date":"2022-06-09T11:42:30+02:00", "size":3514463}
]}
, "German":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/de/de_DE_frami.aff","https://translator.gres.biz/resources/dictionaries/de/de_DE_frami.aff.zip"], "path":"$hunspell$/de/de_DE_frami.aff", "date":"2020-03-17T12:21:16+01:00", "size":18991}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/de/de_DE_frami.dic","https://translator.gres.biz/resources/dictionaries/de/de_DE_frami.dic.zip"], "path":"$hunspell$/de/de_DE_frami.dic", "date":"2020-03-17T12:21:16+01:00", "size":4356858}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/de/de_DE_frami.aff","https://translator.gres.biz/resources/dictionaries/de/de_DE_frami.aff.zip"], "path":"$hunspell$/de/de_DE_frami.aff", "date":"2022-09-23T10:52:56+02:00", "size":19067}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/de/de_DE_frami.dic","https://translator.gres.biz/resources/dictionaries/de/de_DE_frami.dic.zip"], "path":"$hunspell$/de/de_DE_frami.dic", "date":"2017-01-22T19:03:05+00:00", "size":4356858}
]}
, "Greek":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/el_GR/el_GR.aff","https://translator.gres.biz/resources/dictionaries/el_GR/el_GR.aff.zip"], "path":"$hunspell$/el/el_GR.aff", "date":"2020-03-17T12:21:16+01:00", "size":15647}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/el_GR/el_GR.dic","https://translator.gres.biz/resources/dictionaries/el_GR/el_GR.dic.zip"], "path":"$hunspell$/el/el_GR.dic", "date":"2020-03-17T12:21:16+01:00", "size":10125390}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/el_GR/el_GR.aff","https://translator.gres.biz/resources/dictionaries/el_GR/el_GR.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/el_GR/el_GR.dic.zip"], "path":"$hunspell$/el/el_GR.dic", "date":"2015-09-21T17:56:43+02:00", "size":10125390}
]}
, "English":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/en/en_US.aff","https://translator.gres.biz/resources/dictionaries/en/en_US.aff.zip"], "path":"$hunspell$/en/en_US.aff", "date":"2020-03-17T12:21:16+01:00", "size":3090}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/en/en_US.dic","https://translator.gres.biz/resources/dictionaries/en/en_US.dic.zip"], "path":"$hunspell$/en/en_US.dic", "date":"2020-03-17T12:21:16+01:00", "size":551260}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/en/en_US.aff","https://translator.gres.biz/resources/dictionaries/en/en_US.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/en/en_US.dic.zip"], "path":"$hunspell$/en/en_US.dic", "date":"2021-05-12T15:36:00+02:00", "size":551762}
]}
, "Spanish":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/es/es_ANY.aff","https://translator.gres.biz/resources/dictionaries/es/es_ANY.aff.zip"], "path":"$hunspell$/es/es_ANY.aff", "date":"2020-03-17T12:21:16+01:00", "size":169377}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/es/es_ANY.dic","https://translator.gres.biz/resources/dictionaries/es/es_ANY.dic.zip"], "path":"$hunspell$/es/es_ANY.dic", "date":"2020-03-17T12:21:16+01:00", "size":804058}
, "Esperanto":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/eo/eo.aff","https://translator.gres.biz/resources/dictionaries/eo/eo.aff.zip"], "path":"$hunspell$/eo/eo.aff", "date":"2021-04-11T10:01:47+02:00", "size":19129}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/eo/eo.dic","https://translator.gres.biz/resources/dictionaries/eo/eo.dic.zip"], "path":"$hunspell$/eo/eo.dic", "date":"2021-04-11T10:01:47+02:00", "size":377989}
]}
, "Estonian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/et_EE/et_EE.aff","https://translator.gres.biz/resources/dictionaries/et_EE/et_EE.aff.zip"], "path":"$hunspell$/et/et_EE.aff", "date":"2020-03-17T12:21:16+01:00", "size":236336}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/et_EE/et_EE.dic","https://translator.gres.biz/resources/dictionaries/et_EE/et_EE.dic.zip"], "path":"$hunspell$/et/et_EE.dic", "date":"2020-03-17T12:21:16+01:00", "size":4383841}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/et_EE/et_EE.aff","https://translator.gres.biz/resources/dictionaries/et_EE/et_EE.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/et_EE/et_EE.dic.zip"], "path":"$hunspell$/et/et_EE.dic", "date":"2012-10-16T11:09:27-05:00", "size":4383841}
]}
, "Persian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fa_IR/fa-IR.aff","https://translator.gres.biz/resources/dictionaries/fa_IR/fa-IR.aff.zip"], "path":"$hunspell$/fa/fa-IR.aff", "date":"2022-08-27T17:55:37+02:00", "size":5439}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fa_IR/fa-IR.dic","https://translator.gres.biz/resources/dictionaries/fa_IR/fa-IR.dic.zip"], "path":"$hunspell$/fa/fa-IR.dic", "date":"2022-08-27T17:55:37+02:00", "size":2575990}
]}
, "French":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fr_FR/fr.aff","https://translator.gres.biz/resources/dictionaries/fr_FR/fr.aff.zip"], "path":"$hunspell$/fr/fr.aff", "date":"2020-03-17T12:21:16+01:00", "size":256857}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fr_FR/fr.dic","https://translator.gres.biz/resources/dictionaries/fr_FR/fr.dic.zip"], "path":"$hunspell$/fr/fr.dic", "date":"2020-03-17T12:21:16+01:00", "size":1100397}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fr_FR/fr.aff","https://translator.gres.biz/resources/dictionaries/fr_FR/fr.aff.zip"], "path":"$hunspell$/fr/fr.aff", "date":"2020-12-22T09:23:57+01:00", "size":201591}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/fr_FR/fr.dic","https://translator.gres.biz/resources/dictionaries/fr_FR/fr.dic.zip"], "path":"$hunspell$/fr/fr.dic", "date":"2020-12-22T09:23:57+01:00", "size":1227095}
]}
, "Gaelic":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gd_GB/gd_GB.aff","https://translator.gres.biz/resources/dictionaries/gd_GB/gd_GB.aff.zip"], "path":"$hunspell$/gd/gd_GB.aff", "date":"2020-03-17T12:21:16+01:00", "size":8228}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gd_GB/gd_GB.dic","https://translator.gres.biz/resources/dictionaries/gd_GB/gd_GB.dic.zip"], "path":"$hunspell$/gd/gd_GB.dic", "date":"2020-03-17T12:21:16+01:00", "size":4806682}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gd_GB/gd_GB.aff","https://translator.gres.biz/resources/dictionaries/gd_GB/gd_GB.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/gd_GB/gd_GB.dic.zip"], "path":"$hunspell$/gd/gd_GB.dic", "date":"2017-06-22T00:27:25+02:00", "size":4806682}
]}
, "Galician":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gl/gl_ES.aff","https://translator.gres.biz/resources/dictionaries/gl/gl_ES.aff.zip"], "path":"$hunspell$/gl/gl_ES.aff", "date":"2020-03-17T12:21:16+01:00", "size":1159910}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gl/gl_ES.dic","https://translator.gres.biz/resources/dictionaries/gl/gl_ES.dic.zip"], "path":"$hunspell$/gl/gl_ES.dic", "date":"2020-03-17T12:21:16+01:00", "size":8636406}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gl/gl_ES.aff","https://translator.gres.biz/resources/dictionaries/gl/gl_ES.aff.zip"], "path":"$hunspell$/gl/gl_ES.aff", "date":"2021-07-26T16:31:04+02:00", "size":1163541}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gl/gl_ES.dic","https://translator.gres.biz/resources/dictionaries/gl/gl_ES.dic.zip"], "path":"$hunspell$/gl/gl_ES.dic", "date":"2021-07-26T16:31:04+02:00", "size":8325262}
]}
, "Gujarati":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gu_IN/gu_IN.aff","https://translator.gres.biz/resources/dictionaries/gu_IN/gu_IN.aff.zip"], "path":"$hunspell$/gu/gu_IN.aff", "date":"2020-03-17T12:21:16+01:00", "size":174}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gu_IN/gu_IN.dic","https://translator.gres.biz/resources/dictionaries/gu_IN/gu_IN.dic.zip"], "path":"$hunspell$/gu/gu_IN.dic", "date":"2020-03-17T12:21:16+01:00", "size":3792870}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/gu_IN/gu_IN.aff","https://translator.gres.biz/resources/dictionaries/gu_IN/gu_IN.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/gu_IN/gu_IN.dic.zip"], "path":"$hunspell$/gu/gu_IN.dic", "date":"2012-10-16T11:09:27-05:00", "size":3792870}
]}
, "Hebrew":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/he_IL/he_IL.aff","https://translator.gres.biz/resources/dictionaries/he_IL/he_IL.aff.zip"], "path":"$hunspell$/he/he_IL.aff", "date":"2020-03-17T12:21:16+01:00", "size":78883}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/he_IL/he_IL.dic","https://translator.gres.biz/resources/dictionaries/he_IL/he_IL.dic.zip"], "path":"$hunspell$/he/he_IL.dic", "date":"2020-03-17T12:21:16+01:00", "size":7796259}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/he_IL/he_IL.aff","https://translator.gres.biz/resources/dictionaries/he_IL/he_IL.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/he_IL/he_IL.dic.zip"], "path":"$hunspell$/he/he_IL.dic", "date":"2017-09-05T18:11:31+02:00", "size":7796259}
]}
, "Hindi":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hi_IN/hi_IN.aff","https://translator.gres.biz/resources/dictionaries/hi_IN/hi_IN.aff.zip"], "path":"$hunspell$/hi/hi_IN.aff", "date":"2020-03-17T12:21:16+01:00", "size":210}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hi_IN/hi_IN.dic","https://translator.gres.biz/resources/dictionaries/hi_IN/hi_IN.dic.zip"], "path":"$hunspell$/hi/hi_IN.dic", "date":"2020-03-17T12:21:16+01:00", "size":303963}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hi_IN/hi_IN.aff","https://translator.gres.biz/resources/dictionaries/hi_IN/hi_IN.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/hi_IN/hi_IN.dic.zip"], "path":"$hunspell$/hi/hi_IN.dic", "date":"2012-10-16T11:09:27-05:00", "size":303963}
]}
, "Croatian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hr_HR/hr_HR.aff","https://translator.gres.biz/resources/dictionaries/hr_HR/hr_HR.aff.zip"], "path":"$hunspell$/hr/hr_HR.aff", "date":"2020-03-17T12:21:16+01:00", "size":95802}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hr_HR/hr_HR.dic","https://translator.gres.biz/resources/dictionaries/hr_HR/hr_HR.dic.zip"], "path":"$hunspell$/hr/hr_HR.dic", "date":"2020-03-17T12:21:16+01:00", "size":731819}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hr_HR/hr_HR.aff","https://translator.gres.biz/resources/dictionaries/hr_HR/hr_HR.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/hr_HR/hr_HR.dic.zip"], "path":"$hunspell$/hr/hr_HR.dic", "date":"2018-05-29T22:11:06+02:00", "size":731819}
]}
, "Hungarian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hu_HU/hu_HU.aff","https://translator.gres.biz/resources/dictionaries/hu_HU/hu_HU.aff.zip"], "path":"$hunspell$/hu/hu_HU.aff", "date":"2020-03-17T12:21:16+01:00", "size":2106214}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hu_HU/hu_HU.dic","https://translator.gres.biz/resources/dictionaries/hu_HU/hu_HU.dic.zip"], "path":"$hunspell$/hu/hu_HU.dic", "date":"2020-03-17T12:21:16+01:00", "size":1653155}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/hu_HU/hu_HU.aff","https://translator.gres.biz/resources/dictionaries/hu_HU/hu_HU.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/hu_HU/hu_HU.dic.zip"], "path":"$hunspell$/hu/hu_HU.dic", "date":"2018-05-22T22:26:58+02:00", "size":1653155}
]}
, "Indonesian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/id/id_ID.aff","https://translator.gres.biz/resources/dictionaries/id/id_ID.aff.zip"], "path":"$hunspell$/id/id_ID.aff", "date":"2020-03-17T12:21:16+01:00", "size":14957}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/id/id_ID.dic","https://translator.gres.biz/resources/dictionaries/id/id_ID.dic.zip"], "path":"$hunspell$/id/id_ID.dic", "date":"2020-03-17T12:21:16+01:00", "size":315384}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/id/id_ID.aff","https://translator.gres.biz/resources/dictionaries/id/id_ID.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/id/id_ID.dic.zip"], "path":"$hunspell$/id/id_ID.dic", "date":"2018-02-28T01:40:08+01:00", "size":315384}
]}
, "Icelandic":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/is/is.aff","https://translator.gres.biz/resources/dictionaries/is/is.aff.zip"], "path":"$hunspell$/is/is.aff", "date":"2020-03-17T12:21:16+01:00", "size":309734}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/is/is.dic","https://translator.gres.biz/resources/dictionaries/is/is.dic.zip"], "path":"$hunspell$/is/is.dic", "date":"2020-03-17T12:21:16+01:00", "size":2454138}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/is/is.aff","https://translator.gres.biz/resources/dictionaries/is/is.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/is/is.dic.zip"], "path":"$hunspell$/is/is.dic", "date":"2016-03-14T09:05:09+00:00", "size":2454138}
]}
, "Italian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/it_IT/it_IT.aff","https://translator.gres.biz/resources/dictionaries/it_IT/it_IT.aff.zip"], "path":"$hunspell$/it/it_IT.aff", "date":"2020-03-17T12:21:16+01:00", "size":80216}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/it_IT/it_IT.dic","https://translator.gres.biz/resources/dictionaries/it_IT/it_IT.dic.zip"], "path":"$hunspell$/it/it_IT.dic", "date":"2020-03-17T12:21:16+01:00", "size":1290681}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/it_IT/it_IT.aff","https://translator.gres.biz/resources/dictionaries/it_IT/it_IT.aff.zip"], "path":"$hunspell$/it/it_IT.aff", "date":"2020-10-28T10:37:21+01:00", "size":70054}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/it_IT/it_IT.dic","https://translator.gres.biz/resources/dictionaries/it_IT/it_IT.dic.zip"], "path":"$hunspell$/it/it_IT.dic", "date":"2021-01-20T09:47:03+01:00", "size":1295078}
]}
, "Korean":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ko_KR/ko_KR.aff","https://translator.gres.biz/resources/dictionaries/ko_KR/ko_KR.aff.zip"], "path":"$hunspell$/ko/ko_KR.aff", "date":"2020-10-28T10:46:18+01:00", "size":11094418}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ko_KR/ko_KR.dic","https://translator.gres.biz/resources/dictionaries/ko_KR/ko_KR.dic.zip"], "path":"$hunspell$/ko/ko_KR.dic", "date":"2020-10-28T10:46:18+01:00", "size":2862610}
]}
, "Lao":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lo_LA/lo_LA.aff","https://translator.gres.biz/resources/dictionaries/lo_LA/lo_LA.aff.zip"], "path":"$hunspell$/lo/lo_LA.aff", "date":"2020-03-17T12:21:16+01:00", "size":10}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lo_LA/lo_LA.dic","https://translator.gres.biz/resources/dictionaries/lo_LA/lo_LA.dic.zip"], "path":"$hunspell$/lo/lo_LA.dic", "date":"2020-03-17T12:21:16+01:00", "size":203209}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lo_LA/lo_LA.aff","https://translator.gres.biz/resources/dictionaries/lo_LA/lo_LA.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/lo_LA/lo_LA.dic.zip"], "path":"$hunspell$/lo/lo_LA.dic", "date":"2021-05-11T15:56:42+02:00", "size":671495}
]}
, "Lithuanian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lt_LT/lt.aff","https://translator.gres.biz/resources/dictionaries/lt_LT/lt.aff.zip"], "path":"$hunspell$/lt/lt.aff", "date":"2020-03-17T12:21:16+01:00", "size":92208}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lt_LT/lt.dic","https://translator.gres.biz/resources/dictionaries/lt_LT/lt.dic.zip"], "path":"$hunspell$/lt/lt.dic", "date":"2020-03-17T12:21:16+01:00", "size":1085291}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lt_LT/lt.aff","https://translator.gres.biz/resources/dictionaries/lt_LT/lt.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/lt_LT/lt.dic.zip"], "path":"$hunspell$/lt/lt.dic", "date":"2013-01-23T11:35:37+00:00", "size":1085291}
]}
, "Latvian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lv_LV/lv_LV.aff","https://translator.gres.biz/resources/dictionaries/lv_LV/lv_LV.aff.zip"], "path":"$hunspell$/lv/lv_LV.aff", "date":"2020-03-17T12:21:16+01:00", "size":69597}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lv_LV/lv_LV.dic","https://translator.gres.biz/resources/dictionaries/lv_LV/lv_LV.dic.zip"], "path":"$hunspell$/lv/lv_LV.dic", "date":"2020-03-17T12:21:16+01:00", "size":2226834}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lv_LV/lv_LV.aff","https://translator.gres.biz/resources/dictionaries/lv_LV/lv_LV.aff.zip"], "path":"$hunspell$/lv/lv_LV.aff", "date":"2020-05-24T12:13:08+02:00", "size":130475}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/lv_LV/lv_LV.dic","https://translator.gres.biz/resources/dictionaries/lv_LV/lv_LV.dic.zip"], "path":"$hunspell$/lv/lv_LV.dic", "date":"2020-05-24T12:13:08+02:00", "size":1844831}
]}
, "Mongolian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/mn_MN/mn_MN.aff","https://translator.gres.biz/resources/dictionaries/mn_MN/mn_MN.aff.zip"], "path":"$hunspell$/mn/mn_MN.aff", "date":"2022-04-18T07:06:27+02:00", "size":398455}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/mn_MN/mn_MN.dic","https://translator.gres.biz/resources/dictionaries/mn_MN/mn_MN.dic.zip"], "path":"$hunspell$/mn/mn_MN.dic", "date":"2022-04-18T07:06:27+02:00", "size":16650918}
]}
, "Nepali":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ne_NP/ne_NP.aff","https://translator.gres.biz/resources/dictionaries/ne_NP/ne_NP.aff.zip"], "path":"$hunspell$/ne/ne_NP.aff", "date":"2020-03-17T12:21:16+01:00", "size":14162}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ne_NP/ne_NP.dic","https://translator.gres.biz/resources/dictionaries/ne_NP/ne_NP.dic.zip"], "path":"$hunspell$/ne/ne_NP.dic", "date":"2020-03-17T12:21:16+01:00", "size":874372}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ne_NP/ne_NP.aff","https://translator.gres.biz/resources/dictionaries/ne_NP/ne_NP.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/ne_NP/ne_NP.dic.zip"], "path":"$hunspell$/ne/ne_NP.dic", "date":"2012-10-16T11:09:27-05:00", "size":874372}
]}
, "Dutch":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/nl_NL/nl_NL.aff","https://translator.gres.biz/resources/dictionaries/nl_NL/nl_NL.aff.zip"], "path":"$hunspell$/nl/nl_NL.aff", "date":"2020-03-17T12:21:16+01:00", "size":27835}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/nl_NL/nl_NL.dic","https://translator.gres.biz/resources/dictionaries/nl_NL/nl_NL.dic.zip"], "path":"$hunspell$/nl/nl_NL.dic", "date":"2020-03-17T12:21:16+01:00", "size":1881063}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/nl_NL/nl_NL.aff","https://translator.gres.biz/resources/dictionaries/nl_NL/nl_NL.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/nl_NL/nl_NL.dic.zip"], "path":"$hunspell$/nl/nl_NL.dic", "date":"2013-07-22T17:41:01+00:00", "size":1881063}
]}
, "Norwegian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/no/nb_NO.aff","https://translator.gres.biz/resources/dictionaries/no/nb_NO.aff.zip"], "path":"$hunspell$/no/nb_NO.aff", "date":"2020-03-17T12:21:16+01:00", "size":17259}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/no/nb_NO.dic","https://translator.gres.biz/resources/dictionaries/no/nb_NO.dic.zip"], "path":"$hunspell$/no/nb_NO.dic", "date":"2020-03-17T12:21:16+01:00", "size":5274030}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/no/nb_NO.aff","https://translator.gres.biz/resources/dictionaries/no/nb_NO.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/no/nb_NO.dic.zip"], "path":"$hunspell$/no/nb_NO.dic", "date":"2018-09-05T10:30:32+02:00", "size":5274030}
]}
, "Polish":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pl_PL/pl_PL.aff","https://translator.gres.biz/resources/dictionaries/pl_PL/pl_PL.aff.zip"], "path":"$hunspell$/pl/pl_PL.aff", "date":"2020-03-17T12:21:16+01:00", "size":246842}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pl_PL/pl_PL.dic","https://translator.gres.biz/resources/dictionaries/pl_PL/pl_PL.dic.zip"], "path":"$hunspell$/pl/pl_PL.dic", "date":"2020-03-17T12:21:16+01:00", "size":4539105}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pl_PL/pl_PL.aff","https://translator.gres.biz/resources/dictionaries/pl_PL/pl_PL.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/pl_PL/pl_PL.dic.zip"], "path":"$hunspell$/pl/pl_PL.dic", "date":"2017-05-21T10:58:59+02:00", "size":4539105}
]}
, "Portuguese":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pt_PT/pt_PT.aff","https://translator.gres.biz/resources/dictionaries/pt_PT/pt_PT.aff.zip"], "path":"$hunspell$/pt/pt_PT.aff", "date":"2020-03-17T12:21:16+01:00", "size":95089}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pt_PT/pt_PT.dic","https://translator.gres.biz/resources/dictionaries/pt_PT/pt_PT.dic.zip"], "path":"$hunspell$/pt/pt_PT.dic", "date":"2020-03-17T12:21:16+01:00", "size":1473077}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pt_BR/pt_BR.aff","https://translator.gres.biz/resources/dictionaries/pt_BR/pt_BR.aff.zip"], "path":"$hunspell$/pt/pt_BR.aff", "date":"2021-11-12T14:13:08+01:00", "size":979792}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/pt_BR/pt_BR.dic","https://translator.gres.biz/resources/dictionaries/pt_BR/pt_BR.dic.zip"], "path":"$hunspell$/pt/pt_BR.dic", "date":"2021-11-12T14:13:08+01:00", "size":4477695}
]}
, "Romanian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ro/ro_RO.aff","https://translator.gres.biz/resources/dictionaries/ro/ro_RO.aff.zip"], "path":"$hunspell$/ro/ro_RO.aff", "date":"2020-03-17T12:21:16+01:00", "size":55181}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ro/ro_RO.dic","https://translator.gres.biz/resources/dictionaries/ro/ro_RO.dic.zip"], "path":"$hunspell$/ro/ro_RO.dic", "date":"2020-03-17T12:21:16+01:00", "size":2196348}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ro/ro_RO.aff","https://translator.gres.biz/resources/dictionaries/ro/ro_RO.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/ro/ro_RO.dic.zip"], "path":"$hunspell$/ro/ro_RO.dic", "date":"2013-03-28T11:26:45+01:00", "size":2196348}
]}
, "Russian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ru_RU/ru_RU.aff","https://translator.gres.biz/resources/dictionaries/ru_RU/ru_RU.aff.zip"], "path":"$hunspell$/ru/ru_RU.aff", "date":"2020-03-17T12:21:16+01:00", "size":53019}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ru_RU/ru_RU.dic","https://translator.gres.biz/resources/dictionaries/ru_RU/ru_RU.dic.zip"], "path":"$hunspell$/ru/ru_RU.dic", "date":"2020-03-17T12:21:16+01:00", "size":1969349}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ru_RU/ru_RU.aff","https://translator.gres.biz/resources/dictionaries/ru_RU/ru_RU.aff.zip"], "path":"$hunspell$/ru/ru_RU.aff", "date":"2020-06-04T15:36:15+02:00", "size":71236}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/ru_RU/ru_RU.dic","https://translator.gres.biz/resources/dictionaries/ru_RU/ru_RU.dic.zip"], "path":"$hunspell$/ru/ru_RU.dic", "date":"2021-07-27T15:41:55+02:00", "size":3473191}
]}
, "Slovak":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sk_SK/sk_SK.aff","https://translator.gres.biz/resources/dictionaries/sk_SK/sk_SK.aff.zip"], "path":"$hunspell$/sk/sk_SK.aff", "date":"2020-03-17T12:21:16+01:00", "size":99414}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sk_SK/sk_SK.dic","https://translator.gres.biz/resources/dictionaries/sk_SK/sk_SK.dic.zip"], "path":"$hunspell$/sk/sk_SK.dic", "date":"2020-03-17T12:21:16+01:00", "size":3289769}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sk_SK/sk_SK.aff","https://translator.gres.biz/resources/dictionaries/sk_SK/sk_SK.aff.zip"], "path":"$hunspell$/sk/sk_SK.aff", "date":"2020-06-10T20:31:32+02:00", "size":195963}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sk_SK/sk_SK.dic","https://translator.gres.biz/resources/dictionaries/sk_SK/sk_SK.dic.zip"], "path":"$hunspell$/sk/sk_SK.dic", "date":"2020-06-10T20:31:32+02:00", "size":4308934}
]}
, "Slovenian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sl_SI/sl_SI.aff","https://translator.gres.biz/resources/dictionaries/sl_SI/sl_SI.aff.zip"], "path":"$hunspell$/sl/sl_SI.aff", "date":"2020-03-17T12:21:16+01:00", "size":14730}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sl_SI/sl_SI.dic","https://translator.gres.biz/resources/dictionaries/sl_SI/sl_SI.dic.zip"], "path":"$hunspell$/sl/sl_SI.dic", "date":"2020-03-17T12:21:16+01:00", "size":2967766}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sl_SI/sl_SI.aff","https://translator.gres.biz/resources/dictionaries/sl_SI/sl_SI.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/sl_SI/sl_SI.dic.zip"], "path":"$hunspell$/sl/sl_SI.dic", "date":"2012-10-16T11:09:27-05:00", "size":2967766}
]}
, "Albanian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sq_AL/sq_AL.aff","https://translator.gres.biz/resources/dictionaries/sq_AL/sq_AL.aff.zip"], "path":"$hunspell$/sq/sq_AL.aff", "date":"2020-03-17T12:21:16+01:00", "size":7555}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sq_AL/sq_AL.dic","https://translator.gres.biz/resources/dictionaries/sq_AL/sq_AL.dic.zip"], "path":"$hunspell$/sq/sq_AL.dic", "date":"2020-03-17T12:21:16+01:00", "size":2605147}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sq_AL/sq_AL.aff","https://translator.gres.biz/resources/dictionaries/sq_AL/sq_AL.aff.zip"], "path":"$hunspell$/sq/sq_AL.aff", "date":"2021-01-08T00:11:15+01:00", "size":7764}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sq_AL/sq_AL.dic","https://translator.gres.biz/resources/dictionaries/sq_AL/sq_AL.dic.zip"], "path":"$hunspell$/sq/sq_AL.dic", "date":"2021-01-08T00:11:15+01:00", "size":2726785}
]}
, "Serbian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sr/sr.aff","https://translator.gres.biz/resources/dictionaries/sr/sr.aff.zip"], "path":"$hunspell$/sr/sr.aff", "date":"2020-03-17T12:21:16+01:00", "size":901060}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sr/sr.dic","https://translator.gres.biz/resources/dictionaries/sr/sr.dic.zip"], "path":"$hunspell$/sr/sr.dic", "date":"2020-03-17T12:21:16+01:00", "size":5878745}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sr/sr.aff","https://translator.gres.biz/resources/dictionaries/sr/sr.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/sr/sr.dic.zip"], "path":"$hunspell$/sr/sr.dic", "date":"2019-04-20T11:24:57+02:00", "size":5878745}
]}
, "Swedish":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sv_SE/sv_FI.aff","https://translator.gres.biz/resources/dictionaries/sv_SE/sv_FI.aff.zip"], "path":"$hunspell$/sv/sv_FI.aff", "date":"2020-03-17T12:21:16+01:00", "size":18583}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sv_SE/sv_FI.dic","https://translator.gres.biz/resources/dictionaries/sv_SE/sv_FI.dic.zip"], "path":"$hunspell$/sv/sv_FI.dic", "date":"2020-03-17T12:21:16+01:00", "size":2317112}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sv_SE/sv_FI.aff","https://translator.gres.biz/resources/dictionaries/sv_SE/sv_FI.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/sv_SE/sv_FI.dic.zip"], "path":"$hunspell$/sv/sv_FI.dic", "date":"2016-08-16T20:00:33+00:00", "size":2317112}
]}
, "Swahili":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sw_TZ/sw_TZ.aff","https://translator.gres.biz/resources/dictionaries/sw_TZ/sw_TZ.aff.zip"], "path":"$hunspell$/sw/sw_TZ.aff", "date":"2020-03-17T12:21:16+01:00", "size":974}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sw_TZ/sw_TZ.dic","https://translator.gres.biz/resources/dictionaries/sw_TZ/sw_TZ.dic.zip"], "path":"$hunspell$/sw/sw_TZ.dic", "date":"2020-03-17T12:21:16+01:00", "size":630844}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/sw_TZ/sw_TZ.aff","https://translator.gres.biz/resources/dictionaries/sw_TZ/sw_TZ.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/sw_TZ/sw_TZ.dic.zip"], "path":"$hunspell$/sw/sw_TZ.dic", "date":"2012-10-16T11:09:27-05:00", "size":630844}
]}
, "Telugu":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/te_IN/te_IN.aff","https://translator.gres.biz/resources/dictionaries/te_IN/te_IN.aff.zip"], "path":"$hunspell$/te/te_IN.aff", "date":"2020-03-17T12:21:16+01:00", "size":160}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/te_IN/te_IN.dic","https://translator.gres.biz/resources/dictionaries/te_IN/te_IN.dic.zip"], "path":"$hunspell$/te/te_IN.dic", "date":"2020-03-17T12:21:16+01:00", "size":3402272}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/te_IN/te_IN.aff","https://translator.gres.biz/resources/dictionaries/te_IN/te_IN.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/te_IN/te_IN.dic.zip"], "path":"$hunspell$/te/te_IN.dic", "date":"2012-10-16T11:09:27-05:00", "size":3402272}
]}
, "Thai":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/th_TH/th_TH.aff","https://translator.gres.biz/resources/dictionaries/th_TH/th_TH.aff.zip"], "path":"$hunspell$/th/th_TH.aff", "date":"2020-03-17T12:21:16+01:00", "size":156}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/th_TH/th_TH.dic","https://translator.gres.biz/resources/dictionaries/th_TH/th_TH.dic.zip"], "path":"$hunspell$/th/th_TH.dic", "date":"2020-03-17T12:21:16+01:00", "size":1251425}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/th_TH/th_TH.aff","https://translator.gres.biz/resources/dictionaries/th_TH/th_TH.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/th_TH/th_TH.dic.zip"], "path":"$hunspell$/th/th_TH.dic", "date":"2019-06-04T14:18:16+02:00", "size":1251425}
]}
, "Turkish":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/tr_TR/tr_TR.aff","https://translator.gres.biz/resources/dictionaries/tr_TR/tr_TR.aff.zip"], "path":"$hunspell$/tr/tr_TR.aff", "date":"2020-03-17T12:21:16+01:00", "size":235315}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/tr_TR/tr_TR.dic","https://translator.gres.biz/resources/dictionaries/tr_TR/tr_TR.dic.zip"], "path":"$hunspell$/tr/tr_TR.dic", "date":"2020-03-17T12:21:16+01:00", "size":9061155}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/tr_TR/tr_TR.aff","https://translator.gres.biz/resources/dictionaries/tr_TR/tr_TR.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/tr_TR/tr_TR.dic.zip"], "path":"$hunspell$/tr/tr_TR.dic", "date":"2018-08-27T16:55:14+02:00", "size":9061155}
]}
, "Ukrainian":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/uk_UA/uk_UA.aff","https://translator.gres.biz/resources/dictionaries/uk_UA/uk_UA.aff.zip"], "path":"$hunspell$/uk/uk_UA.aff", "date":"2020-03-17T12:21:16+01:00", "size":159599}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/uk_UA/uk_UA.dic","https://translator.gres.biz/resources/dictionaries/uk_UA/uk_UA.dic.zip"], "path":"$hunspell$/uk/uk_UA.dic", "date":"2020-03-17T12:21:16+01:00", "size":2584267}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/uk_UA/uk_UA.aff","https://translator.gres.biz/resources/dictionaries/uk_UA/uk_UA.aff.zip"], "path":"$hunspell$/uk/uk_UA.aff", "date":"2022-08-28T03:23:22+02:00", "size":203463}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/uk_UA/uk_UA.dic","https://translator.gres.biz/resources/dictionaries/uk_UA/uk_UA.dic.zip"], "path":"$hunspell$/uk/uk_UA.dic", "date":"2022-08-28T03:23:22+02:00", "size":8355640}
]}
, "Vietnamese":{"files":[
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/vi/vi_VN.aff","https://translator.gres.biz/resources/dictionaries/vi/vi_VN.aff.zip"], "path":"$hunspell$/vi/vi_VN.aff", "date":"2020-03-17T12:21:16+01:00", "size":788}
,{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/vi/vi_VN.dic","https://translator.gres.biz/resources/dictionaries/vi/vi_VN.dic.zip"], "path":"$hunspell$/vi/vi_VN.dic", "date":"2020-03-17T12:21:16+01:00", "size":39852}
{"url":["https://cgit.freedesktop.org/libreoffice/dictionaries/plain/vi/vi_VN.aff","https://translator.gres.biz/resources/dictionaries/vi/vi_VN.aff.zip"], "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","https://translator.gres.biz/resources/dictionaries/vi/vi_VN.dic.zip"], "path":"$hunspell$/vi/vi_VN.dic", "date":"2012-10-16T11:09:27-05:00", "size":39852}
]}
}
@ -582,10 +594,10 @@
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/bing.js", "path":"$translators$/bing.js", "md5":"a982e9aa6cac598f4c9bf4a56386d13e", "size":1481}
]}
,"deepl": {"files":[
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/deepl.js", "path":"$translators$/deepl.js", "md5":"fff5dba9840208cbb98fc85079014b0d", "size":1754}
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/deepl.js", "path":"$translators$/deepl.js", "md5":"76856af9b80c3d0e852ca73f8f1ebbdb", "size":2611}
]}
,"google": {"files":[
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google.js", "path":"$translators$/google.js", "md5":"1ceba6431d757fc2ae028a4ec177542c", "size":1508}
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google.js", "path":"$translators$/google.js", "md5":"793d6628ac9e26a1f3cc00fa9c863495", "size":1508}
]}
,"google_api": {"files":[
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google_api.js", "path":"$translators$/google_api.js", "md5":"90b9b1a5c8dc52fd4a3f28be93442a56", "size":1030}
@ -594,7 +606,7 @@
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/papago.js", "path":"$translators$/papago.js", "md5":"603a56fc23990453942064ec53d1eaa3", "size":2164}
]}
,"yandex": {"files":[
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/yandex.js", "path":"$translators$/yandex.js", "md5":"6bc71c24270ca418b193f073e6155d5f", "size":1177}
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/yandex.js", "path":"$translators$/yandex.js", "md5":"82c10bddde30f3a1dc6675f7eea71986", "size":1170}
]}
}