Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6efc473859 | ||
|
|
e1ec86f298 | ||
|
|
29ee5dda90 | ||
|
|
b8cd2dff54 | ||
|
|
fb3f32f050 | ||
|
|
41f1f56fe5 | ||
|
|
5be5def820 | ||
|
|
12997389ff | ||
|
|
ab15cc8c79 | ||
|
|
6cb9199a6c | ||
|
|
57b1cf8865 | ||
|
|
d328026356 | ||
|
|
8d4bcb8605 | ||
|
|
5a2c52e4c5 | ||
|
|
8c918e14a4 | ||
|
|
936aaa90ff | ||
|
|
efa9fab49f | ||
|
|
cad1e83d44 | ||
|
|
7be070744b | ||
|
|
cb203b912e | ||
|
|
5f53aaec23 | ||
|
|
b71c67dd1b | ||
|
|
5df1f52a68 | ||
|
|
59d7f1d4f5 | ||
|
|
bf5d4efc6b | ||
|
|
6cb853d804 | ||
|
|
ac24d909d1 | ||
|
|
9b6657318b | ||
|
|
ccfbf843dc | ||
|
|
00e3f90430 | ||
|
|
60f115acd1 | ||
|
|
1f6fff0050 | ||
|
|
030c99d0fe | ||
|
|
a4a3f44806 | ||
|
|
ce9c47c3ea | ||
|
|
07b520d10b | ||
|
|
260a10bea3 | ||
|
|
4f195f5629 | ||
|
|
7d84bd2f7b | ||
|
|
a4e09d88c6 | ||
|
|
ad39d98858 | ||
|
|
2369015824 | ||
|
|
156cd7c926 | ||
|
|
ff041facd8 | ||
|
|
5581f385ae | ||
|
|
958b86044e | ||
|
|
032a14380f | ||
|
|
c03a3d2fc2 | ||
|
|
d5eb9f1855 | ||
|
|
7a8e8e510c | ||
|
|
45a36f3222 | ||
|
|
155c3577c3 | ||
|
|
0e72af3770 | ||
|
|
fdfa43c093 | ||
|
|
af15301bf1 | ||
|
|
26c034e75a | ||
|
|
75bfd798c8 | ||
|
|
59a10af57e | ||
|
|
2ce8e0edc3 | ||
|
|
1fa62d8370 | ||
|
|
70f75ae63d | ||
|
|
39d1d6e237 | ||
|
|
3f71c10964 | ||
|
|
e4db31de10 | ||
|
|
d5dfa5786b | ||
|
|
1a153ff5c3 | ||
|
|
e8f4f01d9c | ||
|
|
718bb7314b | ||
|
|
3f2dcbbeb0 | ||
|
|
413cae80c4 | ||
|
|
9706ab1c12 | ||
|
|
d9bff84acd | ||
|
|
c715ed136a | ||
|
|
5b591fa476 | ||
|
|
efee1088f7 | ||
|
|
3aa76f6074 | ||
|
|
bf3c3ad893 | ||
|
|
30894d7689 | ||
|
|
65b61dec56 | ||
|
|
8273b245ef | ||
|
|
49983a772a | ||
|
|
a76ee0f25a | ||
|
|
abdf15aaaf | ||
|
|
75a04beffa | ||
|
|
d46c72fafb | ||
|
|
6936914976 | ||
|
|
eba1b84184 |
77
.github/workflows/build.yml
vendored
77
.github/workflows/build.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Create release
|
name: Create release
|
||||||
if: contains(github.ref, '/tags/')
|
if: contains(github.ref, '/tags/')
|
||||||
runs-on: ubuntu-16.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
@ -33,13 +33,13 @@ jobs:
|
|||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
env:
|
env:
|
||||||
OS: ${{ matrix.config.name }}
|
OS: ${{ matrix.config.name }}
|
||||||
MSVC_VERSION: 2019/Enterprise
|
MSVC_VERSION: C:/Program Files/Microsoft Visual Studio/2022/Enterprise
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
config:
|
config:
|
||||||
- { name: "win64", os: windows-latest }
|
- { name: "win64", os: windows-latest }
|
||||||
- { name: "win32", os: windows-latest }
|
- { name: "win32", os: windows-latest }
|
||||||
- { name: "linux", os: ubuntu-16.04 }
|
- { name: "linux", os: ubuntu-18.04 }
|
||||||
# - { name: "macos", os: macos-latest }
|
# - { name: "macos", os: macos-latest }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@ -55,64 +55,19 @@ jobs:
|
|||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install libgl1-mesa-dev libxkbcommon-x11-0 libxcb-*
|
sudo apt-get install libgl1-mesa-dev libxkbcommon-x11-0 libxcb-*
|
||||||
echo ::set-env name=QMAKE_FLAGS::QMAKE_CXX=g++-9 QMAKE_CC=gcc-9
|
echo "QMAKE_FLAGS=QMAKE_CXX=g++-10 QMAKE_CC=gcc-10 QMAKE_LINK=g++-10" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: deps
|
path: deps
|
||||||
key: ${{ env.OS }}-deps
|
key: ${{ env.OS }}-${{ hashFiles('./share/ci/*.py') }}
|
||||||
|
|
||||||
- name: Get Qt
|
- name: Make a release
|
||||||
run: python ./share/ci/get_qt.py
|
|
||||||
|
|
||||||
- name: Get ssl
|
|
||||||
run: python ./share/ci/get_qt_ssl.py
|
|
||||||
|
|
||||||
- name: Get leptonica
|
|
||||||
run: python ./share/ci/get_leptonica.py
|
|
||||||
|
|
||||||
- name: Get tesseract optimized
|
|
||||||
env:
|
|
||||||
MARCH: sandy-bridge
|
|
||||||
TAG: optimized
|
|
||||||
run: python ./share/ci/get_tesseract.py
|
|
||||||
|
|
||||||
- name: Get tesseract compatible
|
|
||||||
env:
|
|
||||||
MARCH: nehalem
|
|
||||||
TAG: compatible
|
|
||||||
run: python ./share/ci/get_tesseract.py
|
|
||||||
|
|
||||||
- name: Get hunspell
|
|
||||||
run: python ./share/ci/get_hunspell.py
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: python ./share/ci/test.py
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: python ./share/ci/build.py
|
|
||||||
|
|
||||||
- name: Create AppImage
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
python ./share/ci/appimage.py
|
python ./share/ci/release.py
|
||||||
echo ::set-env name=artifact::`python ./share/ci/appimage.py artifact_name`
|
echo "artifact=`python ./share/ci/release.py artifact_name`" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Create win deploy
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
python ./share/ci/windeploy.py
|
|
||||||
echo ::set-env name=artifact::`python ./share/ci/windeploy.py artifact_name`
|
|
||||||
|
|
||||||
- name: Create mac deploy
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
python ./share/ci/macdeploy.py
|
|
||||||
echo ::set-env name=artifact::`python ./share/ci/macdeploy.py artifact_name`
|
|
||||||
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
if: env.artifact != ''
|
if: env.artifact != ''
|
||||||
@ -123,7 +78,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download release url
|
- name: Download release url
|
||||||
if: contains(github.ref, '/tags/')
|
if: contains(github.ref, '/tags/')
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: release_upload_url
|
name: release_upload_url
|
||||||
path: ./
|
path: ./
|
||||||
@ -131,7 +86,7 @@ jobs:
|
|||||||
- name: Set release env
|
- name: Set release env
|
||||||
if: contains(github.ref, '/tags/')
|
if: contains(github.ref, '/tags/')
|
||||||
shell: bash
|
shell: bash
|
||||||
run: echo ::set-env name=upload_url::`cat ./release_upload_url`
|
run: echo "upload_url=`cat ./release_upload_url`" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Upload release artifacts
|
- name: Upload release artifacts
|
||||||
if: contains(github.ref, '/tags/')
|
if: contains(github.ref, '/tags/')
|
||||||
@ -143,13 +98,3 @@ jobs:
|
|||||||
asset_path: ./${{ env.artifact }}
|
asset_path: ./${{ env.artifact }}
|
||||||
asset_name: ${{ env.artifact }}
|
asset_name: ${{ env.artifact }}
|
||||||
asset_content_type: application/zip
|
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 }}
|
|
||||||
|
|||||||
53
README.md
53
README.md
@ -1,40 +1,52 @@
|
|||||||
# Screen Translator
|
# Screen Translator
|
||||||
|
|
||||||
|
**The project is almost abandoned. I don't have time for it and I can only fix minor issues**
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This software allows you to translate any text on screen.
|
This software allows you to translate any text on screen.
|
||||||
Basically it is a combination of screen capture, OCR and translation tools.
|
Basically it is a combination of screen capture, OCR and translation tools.
|
||||||
|
Translation is currently done via online services.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
There are 2 versions of the app: general and `compatible`.
|
|
||||||
They all share the same functionality.
|
|
||||||
The `compatible` version does not use some hardware optimizations,
|
|
||||||
but allows the app to be run on elder hardware.
|
|
||||||
|
|
||||||
At first, download general version (without any suffixes in
|
|
||||||
the downloadable archive's name).
|
|
||||||
If the app will silently crash during its work,
|
|
||||||
then try the `compatible` version.
|
|
||||||
|
|
||||||
**Windows**: download archive from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases) page, extract it and run `.exe` file.
|
**Windows**: download archive from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases) page, extract it and run `.exe` file.
|
||||||
|
|
||||||
If the app fails to start with missing dll's error then install `vs_redist*.exe` from the release archive.
|
If the app fails to start complaining about missing dlls or there are any update errors related to SSL/TLS then install or repair `vs_redist*.exe` from the release archive.
|
||||||
If you have any update errors related to SSL/TLS you should also install or repair `vcredist 2010` (from the Microsoft website).
|
|
||||||
|
|
||||||
**Linux**: download `.AppImage` file from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases), make executable (`chmod +x <file>`) and run it.
|
**Linux**: download `.AppImage` file from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases), make executable (`chmod +x <file>`) and run it.
|
||||||
|
|
||||||
**OS X**: currently not supported.
|
**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
|
## Setup
|
||||||
|
|
||||||
Start the app, open the updates page of the settings window
|
The app doesn't have a main window.
|
||||||
and install required recognition languages, translators and, optionally,
|
After start it shows only the tray icon.
|
||||||
hunspell dictionaries.
|
|
||||||
|
|
||||||
After languages/translators installation set default recognition and
|
If the app detects invalid settings, it will show the error message via system tray.
|
||||||
translation languages, enable some (or all) translators
|
It will also highlight the section name in red on the left panel of the settings window.
|
||||||
and the `translate text` setting if needed.
|
Clicking on that section name will show a more detailed error message in the right panel (also in red).
|
||||||
|
|
||||||
|
The packages downloaded from this site do not include resources, such as recognition language packs or scripts to interact with online translation services.
|
||||||
|
|
||||||
|
To download them, open the settings window and go to the `Update` section.
|
||||||
|
In the right panel, expand the `recognizers` and `translators` sections.
|
||||||
|
Select preferred items, then right click and choose `Install/Update`.
|
||||||
|
After the progress bar reaches `100%`, the resource's state will change to `Up to Date`.
|
||||||
|
|
||||||
|
You must download at least one `recognizers` resource and one `translators` resource.
|
||||||
|
|
||||||
|
After finishing downloads, go to the `Recognition` section and update the default recognition language setting (the source to be translated).
|
||||||
|
Then go to the `Translation` section, update the default translation language setting (the language to be translated into) and enable some or all translation sevices (you may also change their order by dragging).
|
||||||
|
|
||||||
|
After that all sections in the left panel should be black.
|
||||||
|
Then click `Ok` to close settings.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -46,6 +58,11 @@ and the `translate text` setting if needed.
|
|||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
Answers to some frequently asked questions can be found in issues or
|
||||||
[wiki](https://github.com/OneMoreGres/ScreenTranslator/wiki/FAQ)
|
[wiki](https://github.com/OneMoreGres/ScreenTranslator/wiki/FAQ)
|
||||||
|
|
||||||
|
|||||||
@ -11,5 +11,6 @@
|
|||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/translations">
|
<qresource prefix="/translations">
|
||||||
<file alias="screentranslator_ru.qm">share/translations/screentranslator_ru.qm</file>
|
<file alias="screentranslator_ru.qm">share/translations/screentranslator_ru.qm</file>
|
||||||
|
<file alias="screentranslator_he.qm">share/translations/screentranslator_he.qm</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ DEPS_DIR=$$(ST_DEPS_DIR)
|
|||||||
isEmpty(DEPS_DIR):DEPS_DIR=$$PWD/../deps
|
isEmpty(DEPS_DIR):DEPS_DIR=$$PWD/../deps
|
||||||
INCLUDEPATH += $$DEPS_DIR/include
|
INCLUDEPATH += $$DEPS_DIR/include
|
||||||
LIBS += -L$$DEPS_DIR/lib
|
LIBS += -L$$DEPS_DIR/lib
|
||||||
LIBS += -lhunspell -lleptonica
|
LIBS += -lhunspell -lleptonica -ltesseract
|
||||||
|
|
||||||
win32{
|
win32{
|
||||||
LIBS += -lUser32
|
LIBS += -lUser32
|
||||||
@ -21,7 +21,7 @@ linux{
|
|||||||
SOURCES += $$PWD/external/miniz/miniz.c
|
SOURCES += $$PWD/external/miniz/miniz.c
|
||||||
INCLUDEPATH += $$PWD/external
|
INCLUDEPATH += $$PWD/external
|
||||||
|
|
||||||
VER=3.1.0
|
VER=3.3.0
|
||||||
DEFINES += VERSION="$$VER"
|
DEFINES += VERSION="$$VER"
|
||||||
VERSION = $$VER.0
|
VERSION = $$VER.0
|
||||||
QMAKE_TARGET_COMPANY = Gres
|
QMAKE_TARGET_COMPANY = Gres
|
||||||
@ -53,12 +53,14 @@ HEADERS += \
|
|||||||
src/service/debug.h \
|
src/service/debug.h \
|
||||||
src/service/geometryutils.h \
|
src/service/geometryutils.h \
|
||||||
src/service/globalaction.h \
|
src/service/globalaction.h \
|
||||||
|
src/service/keysequenceedit.h \
|
||||||
src/service/runatsystemstart.h \
|
src/service/runatsystemstart.h \
|
||||||
src/service/singleapplication.h \
|
src/service/singleapplication.h \
|
||||||
src/service/updates.h \
|
src/service/updates.h \
|
||||||
src/service/widgetstate.h \
|
src/service/widgetstate.h \
|
||||||
src/settings.h \
|
src/settings.h \
|
||||||
src/settingseditor.h \
|
src/settingseditor.h \
|
||||||
|
src/settingsvalidator.h \
|
||||||
src/stfwd.h \
|
src/stfwd.h \
|
||||||
src/substitutionstable.h \
|
src/substitutionstable.h \
|
||||||
src/task.h \
|
src/task.h \
|
||||||
@ -89,12 +91,14 @@ SOURCES += \
|
|||||||
src/service/debug.cpp \
|
src/service/debug.cpp \
|
||||||
src/service/geometryutils.cpp \
|
src/service/geometryutils.cpp \
|
||||||
src/service/globalaction.cpp \
|
src/service/globalaction.cpp \
|
||||||
|
src/service/keysequenceedit.cpp \
|
||||||
src/service/runatsystemstart.cpp \
|
src/service/runatsystemstart.cpp \
|
||||||
src/service/singleapplication.cpp \
|
src/service/singleapplication.cpp \
|
||||||
src/service/updates.cpp \
|
src/service/updates.cpp \
|
||||||
src/service/widgetstate.cpp \
|
src/service/widgetstate.cpp \
|
||||||
src/settings.cpp \
|
src/settings.cpp \
|
||||||
src/settingseditor.cpp \
|
src/settingseditor.cpp \
|
||||||
|
src/settingsvalidator.cpp \
|
||||||
src/substitutionstable.cpp \
|
src/substitutionstable.cpp \
|
||||||
src/translate/translator.cpp \
|
src/translate/translator.cpp \
|
||||||
src/translate/webpage.cpp \
|
src/translate/webpage.cpp \
|
||||||
@ -113,7 +117,8 @@ OTHER_FILES += \
|
|||||||
updates.json
|
updates.json
|
||||||
|
|
||||||
TRANSLATIONS += \
|
TRANSLATIONS += \
|
||||||
share/translations/screentranslator_ru.ts
|
share/translations/screentranslator_ru.ts \
|
||||||
|
share/translations/screentranslator_he.ts
|
||||||
|
|
||||||
linux {
|
linux {
|
||||||
PREFIX = /usr
|
PREFIX = /usr
|
||||||
|
|||||||
@ -1,5 +1,42 @@
|
|||||||
# Changes
|
# 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
|
||||||
|
* Added the ability to use some service buttons for hotkeys
|
||||||
|
* Fixed multiple monitors support if the main one is not at the top left
|
||||||
|
* Automatic selection of the supported version of tesseract
|
||||||
|
|
||||||
|
## 3.2.1
|
||||||
|
|
||||||
|
* Fixed incorrect update install
|
||||||
|
|
||||||
|
## 3.2.0
|
||||||
|
|
||||||
|
* Improved vertical text recognition
|
||||||
|
* Improved incorrect settings notification
|
||||||
|
* Improved update process
|
||||||
|
|
||||||
|
## 3.1.2
|
||||||
|
|
||||||
|
* Fixed manually corrected text translation
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
* Fixed portable mode detection
|
||||||
|
|
||||||
## 3.1.0
|
## 3.1.0
|
||||||
|
|
||||||
* Added ability to choose a recognition version from the program
|
* Added ability to choose a recognition version from the program
|
||||||
|
|||||||
@ -1,5 +1,42 @@
|
|||||||
# Изменения
|
# Изменения
|
||||||
|
|
||||||
|
## 3.3.0
|
||||||
|
|
||||||
|
* Использование единой библиотеки распознавания (без оптимизированной и совместимой версий)
|
||||||
|
* Улучшено распознавание
|
||||||
|
|
||||||
|
## 3.2.3
|
||||||
|
|
||||||
|
* Исправлено сохранение порядка переводчиков в настройках
|
||||||
|
* Исправлена работа с несколькими мониторами
|
||||||
|
* Улучшено отображение результата на границе монитора
|
||||||
|
* Обновлена версия библиотеки распознавания
|
||||||
|
|
||||||
|
## 3.2.2
|
||||||
|
|
||||||
|
* Исключено задание горячих клавиш из нескольких последовательных комбинаций
|
||||||
|
* Добавлена возможность использования некоторых служебных кнопок для горячих клавиш
|
||||||
|
* Исправлена работа с несколькими мониторами, если главный находится не слева-вверху
|
||||||
|
* Автоматический выбор поддерживаемой версии tesseract
|
||||||
|
|
||||||
|
## 3.2.1
|
||||||
|
|
||||||
|
* Исправлена некорректная установка обновления
|
||||||
|
|
||||||
|
## 3.2.0
|
||||||
|
|
||||||
|
* Улучшено распознавание вертикального текста
|
||||||
|
* Улучшено информирование о некорректных настройках
|
||||||
|
* Упрощена работа с обновлениями
|
||||||
|
|
||||||
|
## 3.1.2
|
||||||
|
|
||||||
|
* Исправлен перевод исправленного вручную текста
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
* Исправлено определение работы в Portable режиме
|
||||||
|
|
||||||
## 3.1.0
|
## 3.1.0
|
||||||
|
|
||||||
* Добавлена возможность выбора версии библиотеки распознавания из программы
|
* Добавлена возможность выбора версии библиотеки распознавания из программы
|
||||||
|
|||||||
@ -22,7 +22,7 @@ c.print('>> Making appimage')
|
|||||||
base_url = 'https://github.com/probonopd/linuxdeployqt/releases/download'
|
base_url = 'https://github.com/probonopd/linuxdeployqt/releases/download'
|
||||||
continuous_url = base_url + '/continuous/linuxdeployqt-continuous-x86_64.AppImage'
|
continuous_url = base_url + '/continuous/linuxdeployqt-continuous-x86_64.AppImage'
|
||||||
tagged_url = base_url + '/6/linuxdeployqt-6-x86_64.AppImage'
|
tagged_url = base_url + '/6/linuxdeployqt-6-x86_64.AppImage'
|
||||||
linuxdeployqt_url = tagged_url
|
linuxdeployqt_url = continuous_url
|
||||||
linuxdeployqt_original = os.path.basename(linuxdeployqt_url)
|
linuxdeployqt_original = os.path.basename(linuxdeployqt_url)
|
||||||
|
|
||||||
c.download(linuxdeployqt_url, linuxdeployqt_original)
|
c.download(linuxdeployqt_url, linuxdeployqt_original)
|
||||||
@ -47,14 +47,15 @@ os.environ['VERSION'] = app_version
|
|||||||
flags = '' if os.getenv("DEBUG") is None else '-unsupported-allow-new-glibc'
|
flags = '' if os.getenv("DEBUG") is None else '-unsupported-allow-new-glibc'
|
||||||
|
|
||||||
additional_files = glob(ssl_dir + '/lib/lib*.so.*') + \
|
additional_files = glob(ssl_dir + '/lib/lib*.so.*') + \
|
||||||
glob('/usr/lib/x86_64-linux-gnu/nss/*') + \
|
glob('/usr/lib/x86_64-linux-gnu/nss/*')
|
||||||
glob(dependencies_dir + '/lib/libtesseract-*.so')
|
|
||||||
out_lib_dir = install_dir + '/usr/lib'
|
out_lib_dir = install_dir + '/usr/lib'
|
||||||
os.makedirs(out_lib_dir, exist_ok=True)
|
os.makedirs(out_lib_dir, exist_ok=True)
|
||||||
for f in additional_files:
|
for f in additional_files:
|
||||||
c.print('>> Copying {} to {}'.format(f, out_lib_dir))
|
c.print('>> Copying {} to {}'.format(f, out_lib_dir))
|
||||||
shutil.copy(f, out_lib_dir)
|
shutil.copy(f, out_lib_dir)
|
||||||
|
|
||||||
|
c.ensure_got_path('{}/usr/share/doc/libc6/copyright'.format(install_dir))
|
||||||
|
|
||||||
c.run('{} {}/usr/share/applications/*.desktop {} -appimage -qmake={}/bin/qmake'.format(
|
c.run('{} {}/usr/share/applications/*.desktop {} -appimage -qmake={}/bin/qmake'.format(
|
||||||
linuxdeployqt_bin, install_dir, flags, qt_dir))
|
linuxdeployqt_bin, install_dir, flags, qt_dir))
|
||||||
|
|
||||||
|
|||||||
@ -150,15 +150,7 @@ def get_msvc_env_cmd(bitness='64', msvc_version=''):
|
|||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
msvc_path = 'C:/Program Files (x86)/Microsoft Visual Studio'
|
env_script = msvc_version + '/VC/Auxiliary/Build/vcvars{}.bat'.format(bitness)
|
||||||
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)
|
|
||||||
return '"' + env_script + '"'
|
return '"' + env_script + '"'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import re
|
|||||||
app_name = 'ScreenTranslator'
|
app_name = 'ScreenTranslator'
|
||||||
|
|
||||||
target_name = app_name
|
target_name = app_name
|
||||||
qt_version = '5.15.0'
|
qt_version = '5.15.2'
|
||||||
qt_modules = ['qtbase', 'qttools', 'icu',
|
qt_modules = ['qtbase', 'qttools', 'icu',
|
||||||
'qttranslations', 'qtx11extras', 'qtwebengine', 'qtwebchannel',
|
'qttranslations', 'qtx11extras', 'qtwebengine', 'qtwebchannel',
|
||||||
'qtdeclarative', 'qtlocation', 'opengl32sw', 'd3dcompiler_47',
|
'qtdeclarative', 'qtlocation', 'opengl32sw', 'd3dcompiler_47',
|
||||||
@ -30,6 +30,6 @@ os_name = getenv('OS', 'linux')
|
|||||||
app_version += {'linux': '', 'macos': '-experimental',
|
app_version += {'linux': '', 'macos': '-experimental',
|
||||||
'win32': '', 'win64': ''}[os_name]
|
'win32': '', 'win64': ''}[os_name]
|
||||||
bitness = '32' if os_name == 'win32' else '64'
|
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'
|
build_type = 'release' # 'debug'
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import platform
|
|||||||
c.print('>> Installing leptonica')
|
c.print('>> Installing leptonica')
|
||||||
|
|
||||||
install_dir = dependencies_dir
|
install_dir = dependencies_dir
|
||||||
url = 'http://www.leptonica.org/source/leptonica-1.78.0.tar.gz'
|
url = 'https://github.com/DanBloomberg/leptonica/releases/download/1.82.0/leptonica-1.82.0.tar.gz'
|
||||||
required_version = '1.78.0'
|
required_version = '1.82.0'
|
||||||
|
|
||||||
|
|
||||||
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
||||||
@ -25,14 +25,14 @@ def check_existing():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
dll = install_dir + '/bin/leptonica-1.78.0.dll'
|
dll = install_dir + '/bin/leptonica-1.82.0.dll'
|
||||||
lib = install_dir + '/lib/leptonica-1.78.0.lib'
|
lib = install_dir + '/lib/leptonica-1.82.0.lib'
|
||||||
if not os.path.exists(dll) or not os.path.exists(lib):
|
if not os.path.exists(dll) or not os.path.exists(lib):
|
||||||
return False
|
return False
|
||||||
c.symlink(dll, install_dir + '/bin/leptonica.dll')
|
c.symlink(dll, install_dir + '/bin/leptonica.dll')
|
||||||
c.symlink(lib, install_dir + '/lib/leptonica.lib')
|
c.symlink(lib, install_dir + '/lib/leptonica.lib')
|
||||||
elif platform.system() == "Darwin":
|
elif platform.system() == "Darwin":
|
||||||
lib = install_dir + '/lib/libleptonica.1.78.0.dylib'
|
lib = install_dir + '/lib/libleptonica.1.82.0.dylib'
|
||||||
if not os.path.exists(lib):
|
if not os.path.exists(lib):
|
||||||
return False
|
return False
|
||||||
c.symlink(lib, install_dir + '/lib/libleptonica.dylib')
|
c.symlink(lib, install_dir + '/lib/libleptonica.dylib')
|
||||||
@ -44,12 +44,12 @@ def check_existing():
|
|||||||
if len(c.get_folder_files(includes_path)) == 0:
|
if len(c.get_folder_files(includes_path)) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
version_file = install_dir + '/cmake/LeptonicaConfig-version.cmake'
|
version_file = install_dir + '/lib/cmake/leptonica/LeptonicaConfig-version.cmake'
|
||||||
if not os.path.exists(version_file):
|
if not os.path.exists(version_file):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
with open(version_file, 'rt') as f:
|
with open(version_file, 'rt') as f:
|
||||||
existing_version = f.readline()[22:28] # set(Leptonica_VERSION 1.78.0)
|
existing_version = f.readline()[22:28] # set(Leptonica_VERSION 1.82.0)
|
||||||
if existing_version != required_version:
|
if existing_version != required_version:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@ -66,12 +66,20 @@ src_dir = os.path.abspath('leptonica_src')
|
|||||||
c.extract(archive, '.')
|
c.extract(archive, '.')
|
||||||
c.symlink(c.get_archive_top_dir(archive), src_dir)
|
c.symlink(c.get_archive_top_dir(archive), src_dir)
|
||||||
|
|
||||||
|
with open('{}/CMakeLists.txt'.format(src_dir), 'r+') as f:
|
||||||
|
data = f.read()
|
||||||
|
data = data.replace('pkg_check_modules(WEBP', '#pkg_check_modules(WEBP')
|
||||||
|
data = data.replace('if(NOT WEBP', 'if(FALSE')
|
||||||
|
f.seek(0, os.SEEK_SET)
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
c.ensure_got_path(install_dir)
|
c.ensure_got_path(install_dir)
|
||||||
|
|
||||||
c.recreate_dir(build_dir)
|
c.recreate_dir(build_dir)
|
||||||
os.chdir(build_dir)
|
os.chdir(build_dir)
|
||||||
|
|
||||||
cmake_args = '"{}" -DCMAKE_INSTALL_PREFIX="{}"'.format(src_dir, install_dir)
|
cmake_args = '"{}" -DCMAKE_INSTALL_PREFIX="{}" -DBUILD_SHARED_LIBS=ON \
|
||||||
|
-DSW_BUILD=OFF'.format(src_dir, install_dir,)
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
||||||
|
|||||||
@ -6,60 +6,46 @@ import platform
|
|||||||
c.print('>> Installing tesseract')
|
c.print('>> Installing tesseract')
|
||||||
|
|
||||||
install_dir = dependencies_dir
|
install_dir = dependencies_dir
|
||||||
url = 'https://github.com/tesseract-ocr/tesseract/archive/4.1.1.tar.gz'
|
required_version = '5.2.0'
|
||||||
required_version = '4.1.1'
|
url = 'https://github.com/tesseract-ocr/tesseract/archive/{}.tar.gz'.format(required_version)
|
||||||
|
|
||||||
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
||||||
|
|
||||||
# compatibility flags
|
cache_file = install_dir + '/tesseract.cache'
|
||||||
compat_flags = ''
|
cache_file_data = required_version + build_type_flag
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def check_existing():
|
def check_existing():
|
||||||
|
if not os.path.exists(cache_file):
|
||||||
|
return False
|
||||||
|
with open(cache_file, 'r') as f:
|
||||||
|
cached = f.read()
|
||||||
|
if cached != cache_file_data:
|
||||||
|
return False
|
||||||
|
|
||||||
includes_path = install_dir + '/include/tesseract'
|
includes_path = install_dir + '/include/tesseract'
|
||||||
if len(c.get_folder_files(includes_path)) == 0:
|
if len(c.get_folder_files(includes_path)) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
lib = install_dir + '/bin/tesseract{}.dll'.format(lib_suffix)
|
file_name_ver = required_version[0] + required_version[2]
|
||||||
orig_lib = install_dir + '/bin/tesseract41.dll'
|
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":
|
elif platform.system() == "Darwin":
|
||||||
lib = install_dir + '/lib/libtesseract{}.dylib'.format(lib_suffix)
|
lib = install_dir + '/lib/libtesseract.{}.dylib'.format(required_version)
|
||||||
orig_lib = install_dir + '/lib/libtesseract.4.1.1.dylib'
|
if not os.path.exists(lib):
|
||||||
|
return False
|
||||||
|
c.symlink(lib, install_dir + '/lib/libtesseract.dylib')
|
||||||
else:
|
else:
|
||||||
lib = install_dir + '/lib/libtesseract{}.so'.format(lib_suffix)
|
lib = install_dir + '/lib/libtesseract.so.{}'.format(required_version)
|
||||||
orig_lib = install_dir + '/lib/libtesseract.so.4.1.1'
|
if not os.path.exists(lib):
|
||||||
|
return False
|
||||||
|
c.symlink(lib, install_dir + '/lib/libtesseract.so')
|
||||||
|
|
||||||
if os.path.exists(lib):
|
return True
|
||||||
return True
|
|
||||||
if os.path.exists(orig_lib):
|
|
||||||
os.rename(orig_lib, lib)
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
if check_existing() and not 'FORCE' in os.environ:
|
if check_existing() and not 'FORCE' in os.environ:
|
||||||
@ -78,8 +64,20 @@ c.ensure_got_path(install_dir)
|
|||||||
c.recreate_dir(build_dir)
|
c.recreate_dir(build_dir)
|
||||||
os.chdir(build_dir)
|
os.chdir(build_dir)
|
||||||
|
|
||||||
cmake_args = '"{0}" -DCMAKE_INSTALL_PREFIX="{1}" -DLeptonica_DIR="{1}/cmake" \
|
cmake_args = '"{0}" \
|
||||||
-DBUILD_TRAINING_TOOLS=OFF -DBUILD_TESTS=OFF'.format(src_dir, install_dir)
|
-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":
|
if platform.system() == "Windows":
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
||||||
@ -89,13 +87,12 @@ if platform.system() == "Windows":
|
|||||||
c.set_make_threaded()
|
c.set_make_threaded()
|
||||||
c.run('cmake {}'.format(cmake_args))
|
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 . --config {}'.format(build_type_flag))
|
||||||
c.run('cmake --build . --target install --config {}'.format(build_type_flag))
|
c.run('cmake --build . --target install --config {}'.format(build_type_flag))
|
||||||
|
|
||||||
|
with open(cache_file, 'w') as f:
|
||||||
|
f.write(cache_file_data)
|
||||||
|
|
||||||
if not check_existing(): # add suffix
|
if not check_existing(): # add suffix
|
||||||
c.print('>> Build failed')
|
c.print('>> Build failed')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|||||||
43
share/ci/release.py
Normal file
43
share/ci/release.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
here = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def r_out(script, args):
|
||||||
|
return subprocess.run([sys.executable, os.path.join(here, script)] + args, check=True, stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
|
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name':
|
||||||
|
artifact_name = ''
|
||||||
|
if platform.system() == "Linux":
|
||||||
|
artifact_name = r_out('appimage.py', ['artifact_name'])
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
artifact_name = r_out('windeploy.py', ['artifact_name'])
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
artifact_name = r_out('macdeploy.py', ['artifact_name'])
|
||||||
|
print(artifact_name)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def r(script):
|
||||||
|
return subprocess.run([sys.executable, os.path.join(here, script)], check=True)
|
||||||
|
|
||||||
|
|
||||||
|
r('get_qt.py')
|
||||||
|
r('get_qt_ssl.py')
|
||||||
|
r('get_leptonica.py')
|
||||||
|
r('get_tesseract.py')
|
||||||
|
|
||||||
|
r('get_hunspell.py')
|
||||||
|
r('test.py')
|
||||||
|
r('build.py')
|
||||||
|
|
||||||
|
if platform.system() == "Linux":
|
||||||
|
r('appimage.py')
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
r('windeploy.py')
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
r('macdeploy.py')
|
||||||
1248
share/translations/screentranslator_he.ts
Normal file
1248
share/translations/screentranslator_he.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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())
|
if (pixmap.isNull() || !isValid())
|
||||||
return {};
|
return {};
|
||||||
@ -21,7 +22,7 @@ TaskPtr CaptureArea::task(const QPixmap &pixmap) const
|
|||||||
task->generation = generation_;
|
task->generation = generation_;
|
||||||
task->useHunspell = useHunspell_;
|
task->useHunspell = useHunspell_;
|
||||||
task->captured = pixmap.copy(rect_);
|
task->captured = pixmap.copy(rect_);
|
||||||
task->capturePoint = rect_.topLeft();
|
task->capturePoint = pixmapOffset + rect_.topLeft();
|
||||||
task->sourceLanguage = sourceLanguage_;
|
task->sourceLanguage = sourceLanguage_;
|
||||||
if (task->sourceLanguage.isEmpty())
|
if (task->sourceLanguage.isEmpty())
|
||||||
task->error += QObject::tr("No source language set");
|
task->error += QObject::tr("No source language set");
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class CaptureArea
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CaptureArea(const QRect& rect, const Settings& settings);
|
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);
|
void setGeneration(uint generation);
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|||||||
@ -22,10 +22,12 @@ static bool notLocked(const std::shared_ptr<CaptureArea> &area)
|
|||||||
CaptureAreaSelector::CaptureAreaSelector(Capturer &capturer,
|
CaptureAreaSelector::CaptureAreaSelector(Capturer &capturer,
|
||||||
const Settings &settings,
|
const Settings &settings,
|
||||||
const CommonModels &models,
|
const CommonModels &models,
|
||||||
const QPixmap &pixmap)
|
const QPixmap &pixmap,
|
||||||
|
const QPoint &pixmapOffset)
|
||||||
: capturer_(capturer)
|
: capturer_(capturer)
|
||||||
, settings_(settings)
|
, settings_(settings)
|
||||||
, pixmap_(pixmap)
|
, pixmap_(pixmap)
|
||||||
|
, pixmapOffset_(pixmapOffset)
|
||||||
, editor_(std::make_unique<CaptureAreaEditor>(models, this))
|
, editor_(std::make_unique<CaptureAreaEditor>(models, this))
|
||||||
, contextMenu_(new QMenu(this))
|
, contextMenu_(new QMenu(this))
|
||||||
{
|
{
|
||||||
@ -56,7 +58,7 @@ CaptureAreaSelector::~CaptureAreaSelector() = default;
|
|||||||
|
|
||||||
void CaptureAreaSelector::activate()
|
void CaptureAreaSelector::activate()
|
||||||
{
|
{
|
||||||
setGeometry(pixmap_.rect());
|
setGeometry(QRect(pixmapOffset_, pixmap_.size()));
|
||||||
show();
|
show();
|
||||||
activateWindow();
|
activateWindow();
|
||||||
}
|
}
|
||||||
@ -337,7 +339,7 @@ void CaptureAreaSelector::customize(const std::shared_ptr<CaptureArea> &area)
|
|||||||
edited_ = area;
|
edited_ = area;
|
||||||
editor_->show();
|
editor_->show();
|
||||||
const auto topLeft = service::geometry::cornerAtPoint(
|
const auto topLeft = service::geometry::cornerAtPoint(
|
||||||
area->rect().center(), editor_->size(), geometry());
|
area->rect().center(), editor_->size(), QRect({}, size()));
|
||||||
editor_->move(topLeft);
|
editor_->move(topLeft);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,8 @@ class CaptureAreaSelector : public QWidget
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
CaptureAreaSelector(Capturer &capturer, const Settings &settings,
|
CaptureAreaSelector(Capturer &capturer, const Settings &settings,
|
||||||
const CommonModels &models, const QPixmap &pixmap);
|
const CommonModels &models, const QPixmap &pixmap,
|
||||||
|
const QPoint &pixmapOffset);
|
||||||
~CaptureAreaSelector();
|
~CaptureAreaSelector();
|
||||||
|
|
||||||
void activate();
|
void activate();
|
||||||
@ -50,6 +51,7 @@ private:
|
|||||||
Capturer &capturer_;
|
Capturer &capturer_;
|
||||||
const Settings &settings_;
|
const Settings &settings_;
|
||||||
const QPixmap &pixmap_;
|
const QPixmap &pixmap_;
|
||||||
|
const QPoint &pixmapOffset_;
|
||||||
Generation generation_{};
|
Generation generation_{};
|
||||||
QPoint startSelectPos_;
|
QPoint startSelectPos_;
|
||||||
QPoint currentSelectPos_;
|
QPoint currentSelectPos_;
|
||||||
|
|||||||
@ -15,7 +15,7 @@ Capturer::Capturer(Manager &manager, const Settings &settings,
|
|||||||
: manager_(manager)
|
: manager_(manager)
|
||||||
, settings_(settings)
|
, settings_(settings)
|
||||||
, selector_(std::make_unique<CaptureAreaSelector>(*this, settings_, models,
|
, selector_(std::make_unique<CaptureAreaSelector>(*this, settings_, models,
|
||||||
pixmap_))
|
pixmap_, pixmapOffset_))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +56,7 @@ void Capturer::updatePixmap()
|
|||||||
|
|
||||||
QPixmap combined(rect.size());
|
QPixmap combined(rect.size());
|
||||||
QPainter p(&combined);
|
QPainter p(&combined);
|
||||||
|
p.translate(-rect.topLeft());
|
||||||
|
|
||||||
for (const auto screen : screens) {
|
for (const auto screen : screens) {
|
||||||
const auto geometry = screen->geometry();
|
const auto geometry = screen->geometry();
|
||||||
@ -66,6 +67,9 @@ void Capturer::updatePixmap()
|
|||||||
|
|
||||||
SOFT_ASSERT(selector_, return );
|
SOFT_ASSERT(selector_, return );
|
||||||
pixmap_ = combined;
|
pixmap_ = combined;
|
||||||
|
pixmapOffset_ = rect.topLeft();
|
||||||
|
|
||||||
|
for (auto &r : screenRects) r.translate(-rect.topLeft());
|
||||||
selector_->setScreenRects(screenRects);
|
selector_->setScreenRects(screenRects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +91,7 @@ void Capturer::selected(const CaptureArea &area)
|
|||||||
selector_->hide();
|
selector_->hide();
|
||||||
|
|
||||||
SOFT_ASSERT(!pixmap_.isNull(), return manager_.captureCanceled())
|
SOFT_ASSERT(!pixmap_.isNull(), return manager_.captureCanceled())
|
||||||
auto task = area.task(pixmap_);
|
auto task = area.task(pixmap_, pixmapOffset_);
|
||||||
if (task)
|
if (task)
|
||||||
manager_.captured(task);
|
manager_.captured(task);
|
||||||
else
|
else
|
||||||
|
|||||||
@ -26,5 +26,6 @@ private:
|
|||||||
Manager &manager_;
|
Manager &manager_;
|
||||||
const Settings &settings_;
|
const Settings &settings_;
|
||||||
QPixmap pixmap_;
|
QPixmap pixmap_;
|
||||||
|
QPoint pixmapOffset_;
|
||||||
std::unique_ptr<CaptureAreaSelector> selector_;
|
std::unique_ptr<CaptureAreaSelector> selector_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,7 +11,8 @@ CommonModels::CommonModels()
|
|||||||
|
|
||||||
CommonModels::~CommonModels() = default;
|
CommonModels::~CommonModels() = default;
|
||||||
|
|
||||||
void CommonModels::update(const QString &tessdataPath)
|
void CommonModels::update(const QString &tessdataPath,
|
||||||
|
const QString &translatorPath)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto names = Tesseract::availableLanguageNames(tessdataPath);
|
auto names = Tesseract::availableLanguageNames(tessdataPath);
|
||||||
@ -19,6 +20,11 @@ void CommonModels::update(const QString &tessdataPath)
|
|||||||
sourceLanguageModel_->setStringList(names);
|
sourceLanguageModel_->setStringList(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
translators_ = Translator::availableTranslators(translatorPath);
|
||||||
|
std::sort(translators_.begin(), translators_.end());
|
||||||
|
}
|
||||||
|
|
||||||
if (targetLanguageModel_->rowCount() > 0)
|
if (targetLanguageModel_->rowCount() > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -38,3 +44,8 @@ QStringListModel *CommonModels::targetLanguageModel() const
|
|||||||
{
|
{
|
||||||
return targetLanguageModel_.get();
|
return targetLanguageModel_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QStringList &CommonModels::translators() const
|
||||||
|
{
|
||||||
|
return translators_;
|
||||||
|
}
|
||||||
|
|||||||
@ -12,12 +12,14 @@ public:
|
|||||||
CommonModels();
|
CommonModels();
|
||||||
~CommonModels();
|
~CommonModels();
|
||||||
|
|
||||||
void update(const QString& tessdataPath);
|
void update(const QString& tessdataPath, const QString& translatorPath);
|
||||||
|
|
||||||
QStringListModel* sourceLanguageModel() const;
|
QStringListModel* sourceLanguageModel() const;
|
||||||
QStringListModel* targetLanguageModel() const;
|
QStringListModel* targetLanguageModel() const;
|
||||||
|
const QStringList& translators() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<QStringListModel> sourceLanguageModel_;
|
std::unique_ptr<QStringListModel> sourceLanguageModel_;
|
||||||
std::unique_ptr<QStringListModel> targetLanguageModel_;
|
std::unique_ptr<QStringListModel> targetLanguageModel_;
|
||||||
|
QStringList translators_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -51,7 +51,7 @@ void Corrector::correct(const TaskPtr &task)
|
|||||||
|
|
||||||
task->corrected = task->recognized;
|
task->corrected = task->recognized;
|
||||||
|
|
||||||
if (!settings_.userSubstitutions.empty()) {
|
if (settings_.useUserSubstitutions && !settings_.userSubstitutions.empty()) {
|
||||||
task->corrected = substituteUser(task->recognized, task->sourceLanguage);
|
task->corrected = substituteUser(task->recognized, task->sourceLanguage);
|
||||||
LTRACE() << "Corrected with user data";
|
LTRACE() << "Corrected with user data";
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ void Corrector::processQueue()
|
|||||||
void Corrector::updateSettings()
|
void Corrector::updateSettings()
|
||||||
{
|
{
|
||||||
queue_.clear();
|
queue_.clear();
|
||||||
emit resetAuto(settings_.hunspellDir);
|
emit resetAuto(settings_.hunspellPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Corrector::finishCorrection(const TaskPtr &task)
|
void Corrector::finishCorrection(const TaskPtr &task)
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const std::unordered_map<LanguageId, LanguageCodes::Bundle>
|
|||||||
{I("ita"), {I("ita"), S("it"), S("ita"), QT_TRANSLATE_NOOP("QObject", "Italian")}},
|
{I("ita"), {I("ita"), S("it"), S("ita"), QT_TRANSLATE_NOOP("QObject", "Italian")}},
|
||||||
// {I("iku"), {I("iku"), S("iu"), S("iku"), QT_TRANSLATE_NOOP("QObject", "Inuktitut")}},
|
// {I("iku"), {I("iku"), S("iu"), S("iku"), QT_TRANSLATE_NOOP("QObject", "Inuktitut")}},
|
||||||
{I("jpn"), {I("jpn"), S("ja"), S("jpn"), QT_TRANSLATE_NOOP("QObject", "Japanese")}},
|
{I("jpn"), {I("jpn"), S("ja"), S("jpn"), QT_TRANSLATE_NOOP("QObject", "Japanese")}},
|
||||||
|
{I("jpn_vert"), {I("jpn_vert"), S("ja"), S("jpn_vert"), QT_TRANSLATE_NOOP("QObject", "Japanese vertical")}},
|
||||||
{I("jav"), {I("jav"), S("jv"), S("jav"), QT_TRANSLATE_NOOP("QObject", "Javanese")}},
|
{I("jav"), {I("jav"), S("jv"), S("jav"), QT_TRANSLATE_NOOP("QObject", "Javanese")}},
|
||||||
// {I("kal"), {I("kal"), S("kl"), S("kal"), QT_TRANSLATE_NOOP("QObject", "Kalaallisut, Greenlandic")}},
|
// {I("kal"), {I("kal"), S("kl"), S("kal"), QT_TRANSLATE_NOOP("QObject", "Kalaallisut, Greenlandic")}},
|
||||||
{I("kan"), {I("kan"), S("kn"), S("kan"), QT_TRANSLATE_NOOP("QObject", "Kannada")}},
|
{I("kan"), {I("kan"), S("kn"), S("kan"), QT_TRANSLATE_NOOP("QObject", "Kannada")}},
|
||||||
@ -93,6 +94,7 @@ const std::unordered_map<LanguageId, LanguageCodes::Bundle>
|
|||||||
// {I("kom"), {I("kom"), S("kv"), S("kom"), QT_TRANSLATE_NOOP("QObject", "Komi")}},
|
// {I("kom"), {I("kom"), S("kv"), S("kom"), QT_TRANSLATE_NOOP("QObject", "Komi")}},
|
||||||
// {I("kon"), {I("kon"), S("kg"), S("kon"), QT_TRANSLATE_NOOP("QObject", "Kongo")}},
|
// {I("kon"), {I("kon"), S("kg"), S("kon"), QT_TRANSLATE_NOOP("QObject", "Kongo")}},
|
||||||
{I("kor"), {I("kor"), S("ko"), S("kor"), QT_TRANSLATE_NOOP("QObject", "Korean")}},
|
{I("kor"), {I("kor"), S("ko"), S("kor"), QT_TRANSLATE_NOOP("QObject", "Korean")}},
|
||||||
|
{I("kor_vert"), {I("kor_vert"), S("ko"), S("kor_vert"), QT_TRANSLATE_NOOP("QObject", "Korean vertical")}},
|
||||||
{I("kur"), {I("kur"), S("ku"), S(""), QT_TRANSLATE_NOOP("QObject", "Kurdish")}},
|
{I("kur"), {I("kur"), S("ku"), S(""), QT_TRANSLATE_NOOP("QObject", "Kurdish")}},
|
||||||
// {I("kua"), {I("kua"), S("kj"), S("kua"), QT_TRANSLATE_NOOP("QObject", "Kuanyama, Kwanyama")}},
|
// {I("kua"), {I("kua"), S("kj"), S("kua"), QT_TRANSLATE_NOOP("QObject", "Kuanyama, Kwanyama")}},
|
||||||
{I("lat"), {I("lat"), S("la"), S("lat"), QT_TRANSLATE_NOOP("QObject", "Latin")}},
|
{I("lat"), {I("lat"), S("la"), S("lat"), QT_TRANSLATE_NOOP("QObject", "Latin")}},
|
||||||
@ -193,8 +195,10 @@ const std::unordered_map<LanguageId, LanguageCodes::Bundle>
|
|||||||
{I("zul"), {I("zul"), S("zu"), S(""), QT_TRANSLATE_NOOP("QObject", "Zulu")}},
|
{I("zul"), {I("zul"), S("zu"), S(""), QT_TRANSLATE_NOOP("QObject", "Zulu")}},
|
||||||
// custom
|
// custom
|
||||||
{I("chi_sim"), {I("chi_sim"), S("zh-CN"), S("chi_sim"), QT_TRANSLATE_NOOP("QObject", "Chinese (Simplified)")}},
|
{I("chi_sim"), {I("chi_sim"), S("zh-CN"), S("chi_sim"), QT_TRANSLATE_NOOP("QObject", "Chinese (Simplified)")}},
|
||||||
|
{I("chi_sim_vert"), {I("chi_sim_vert"), S("zh-CN"), S("chi_sim_vert"), QT_TRANSLATE_NOOP("QObject", "Chinese (Simplified) vertical")}},
|
||||||
{I("chi_tra"), {I("chi_tra"), S("zh-TW"), S("chi_tra"), QT_TRANSLATE_NOOP("QObject", "Chinese (Traditional)")}},
|
{I("chi_tra"), {I("chi_tra"), S("zh-TW"), S("chi_tra"), QT_TRANSLATE_NOOP("QObject", "Chinese (Traditional)")}},
|
||||||
{I("fil"), {I("fil"), S(""), S("fil"), QT_TRANSLATE_NOOP("QObject", "Filipino")}},
|
{I("chi_tra_vert"), {I("chi_tra_vert"), S("zh-TW"), S("chi_tra_vert"), QT_TRANSLATE_NOOP("QObject", "Chinese (Traditional) vertical")}},
|
||||||
|
{I("fil"), {I("fil"), S("tl"), S("fil"), QT_TRANSLATE_NOOP("QObject", "Filipino")}},
|
||||||
{I("chr"), {I("chr"), S(""), S("chr"), QT_TRANSLATE_NOOP("QObject", "Cherokee")}},
|
{I("chr"), {I("chr"), S(""), S("chr"), QT_TRANSLATE_NOOP("QObject", "Cherokee")}},
|
||||||
{I("ceb"), {I("ceb"), S(""), S("ceb"), QT_TRANSLATE_NOOP("QObject", "Cebuano")}},
|
{I("ceb"), {I("ceb"), S(""), S("ceb"), QT_TRANSLATE_NOOP("QObject", "Cebuano")}},
|
||||||
{I("syr"), {I("syr"), S(""), S("syr"), QT_TRANSLATE_NOOP("QObject", "Syriac")}},
|
{I("syr"), {I("syr"), S(""), S("syr"), QT_TRANSLATE_NOOP("QObject", "Syriac")}},
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using LanguageId = QString;
|
using LanguageId = QString;
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "recognizer.h"
|
#include "recognizer.h"
|
||||||
#include "representer.h"
|
#include "representer.h"
|
||||||
#include "settingseditor.h"
|
#include "settingseditor.h"
|
||||||
|
#include "settingsvalidator.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "translator.h"
|
#include "translator.h"
|
||||||
#include "trayicon.h"
|
#include "trayicon.h"
|
||||||
@ -33,8 +34,7 @@ using Loader = update::Loader;
|
|||||||
Manager::Manager()
|
Manager::Manager()
|
||||||
: models_(std::make_unique<CommonModels>())
|
: models_(std::make_unique<CommonModels>())
|
||||||
, settings_(std::make_unique<Settings>())
|
, settings_(std::make_unique<Settings>())
|
||||||
, updater_(std::make_unique<Loader>(Loader::Urls{{updatesUrl}}))
|
, updater_(std::make_unique<update::Updater>(QVector<QUrl>{{updatesUrl}}))
|
||||||
, updateAutoChecker_(std::make_unique<update::AutoChecker>(*updater_))
|
|
||||||
{
|
{
|
||||||
SOFT_ASSERT(settings_, return );
|
SOFT_ASSERT(settings_, return );
|
||||||
|
|
||||||
@ -61,13 +61,9 @@ Manager::Manager()
|
|||||||
|
|
||||||
warnIfOutdated();
|
warnIfOutdated();
|
||||||
|
|
||||||
QObject::connect(updater_.get(), &update::Loader::error, //
|
QObject::connect(updater_.get(), &update::Updater::error, //
|
||||||
tray_.get(), &TrayIcon::showError);
|
tray_.get(), &TrayIcon::showError);
|
||||||
QObject::connect(updater_.get(), &update::Loader::updated, //
|
QObject::connect(updater_.get(), &update::Updater::updatesAvailable, //
|
||||||
tray_.get(), [this] {
|
|
||||||
tray_->showInformation(QObject::tr("Update completed"));
|
|
||||||
});
|
|
||||||
QObject::connect(updater_.get(), &update::Loader::updatesAvailable, //
|
|
||||||
tray_.get(), [this] {
|
tray_.get(), [this] {
|
||||||
tray_->showInformation(QObject::tr("Updates available"));
|
tray_->showInformation(QObject::tr("Updates available"));
|
||||||
});
|
});
|
||||||
@ -76,8 +72,10 @@ Manager::Manager()
|
|||||||
Manager::~Manager()
|
Manager::~Manager()
|
||||||
{
|
{
|
||||||
SOFT_ASSERT(settings_, return );
|
SOFT_ASSERT(settings_, return );
|
||||||
if (updateAutoChecker_ && updateAutoChecker_->isLastCheckDateChanged()) {
|
SOFT_ASSERT(updater_, return );
|
||||||
settings_->lastUpdateCheck = updateAutoChecker_->lastCheckDate();
|
if (updater_->lastUpdateCheck().isValid() &&
|
||||||
|
settings_->lastUpdateCheck != updater_->lastUpdateCheck()) {
|
||||||
|
settings_->lastUpdateCheck = updater_->lastUpdateCheck();
|
||||||
settings_->saveLastUpdateCheck();
|
settings_->saveLastUpdateCheck();
|
||||||
LTRACE() << "saved last update time";
|
LTRACE() << "saved last update time";
|
||||||
}
|
}
|
||||||
@ -111,7 +109,7 @@ void Manager::updateSettings()
|
|||||||
setupProxy(*settings_);
|
setupProxy(*settings_);
|
||||||
setupUpdates(*settings_);
|
setupUpdates(*settings_);
|
||||||
|
|
||||||
models_->update(settings_->tessdataPath);
|
models_->update(settings_->tessdataPath, settings_->translatorsPath);
|
||||||
|
|
||||||
tray_->updateSettings();
|
tray_->updateSettings();
|
||||||
capturer_->updateSettings();
|
capturer_->updateSettings();
|
||||||
@ -122,25 +120,13 @@ void Manager::updateSettings()
|
|||||||
|
|
||||||
tray_->setCaptureLockedEnabled(capturer_->canCaptureLocked());
|
tray_->setCaptureLockedEnabled(capturer_->canCaptureLocked());
|
||||||
|
|
||||||
if (models_->sourceLanguageModel()->rowCount() == 0) {
|
SettingsValidator validator;
|
||||||
fatalError(
|
validator.correct(*settings_, *models_);
|
||||||
QObject::tr("No recognition languages available. Install some via "
|
const auto errors = validator.check(*settings_, *models_);
|
||||||
"Settings->Updates"));
|
if (errors.isEmpty())
|
||||||
}
|
return;
|
||||||
if (settings_->sourceLanguage.isEmpty()) {
|
|
||||||
fatalError(
|
fatalError(QObject::tr("Incorrect settings found. Go to Settings"));
|
||||||
QObject::tr("Recognition language not set. Go to Settings->Recognition "
|
|
||||||
"and set it"));
|
|
||||||
}
|
|
||||||
if (settings_->doTranslation && settings_->translators.isEmpty()) {
|
|
||||||
fatalError(QObject::tr(
|
|
||||||
"No translators enabled. Go to Settings->Translation and select some"));
|
|
||||||
}
|
|
||||||
if (settings_->doTranslation && settings_->targetLanguage.isEmpty()) {
|
|
||||||
fatalError(
|
|
||||||
QObject::tr("Translation language not set. Go to Settings->Translation "
|
|
||||||
"and set it"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::setupProxy(const Settings &settings)
|
void Manager::setupProxy(const Settings &settings)
|
||||||
@ -170,16 +156,15 @@ void Manager::setupProxy(const Settings &settings)
|
|||||||
|
|
||||||
void Manager::setupUpdates(const Settings &settings)
|
void Manager::setupUpdates(const Settings &settings)
|
||||||
{
|
{
|
||||||
updater_->model()->setExpansions({
|
updater_->setExpansions({
|
||||||
{"$translators$", settings.translatorsDir},
|
{"$translators$", settings.translatorsPath},
|
||||||
{"$tessdata$", settings.tessdataPath},
|
{"$tessdata$", settings.tessdataPath},
|
||||||
{"$hunspell$", settings.hunspellDir},
|
{"$hunspell$", settings.hunspellPath},
|
||||||
{"$appdir$", QApplication::applicationDirPath()},
|
{"$appdir$", QApplication::applicationDirPath()},
|
||||||
});
|
});
|
||||||
|
|
||||||
SOFT_ASSERT(updateAutoChecker_, return );
|
updater_->setAutoUpdate(settings.autoUpdateIntervalDays,
|
||||||
updateAutoChecker_->setLastCheckDate(settings.lastUpdateCheck);
|
settings.lastUpdateCheck);
|
||||||
updateAutoChecker_->setCheckIntervalDays(settings.autoUpdateIntervalDays);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::setupTrace(bool isOn)
|
bool Manager::setupTrace(bool isOn)
|
||||||
|
|||||||
@ -43,7 +43,6 @@ private:
|
|||||||
std::unique_ptr<Corrector> corrector_;
|
std::unique_ptr<Corrector> corrector_;
|
||||||
std::unique_ptr<Translator> translator_;
|
std::unique_ptr<Translator> translator_;
|
||||||
std::unique_ptr<Representer> representer_;
|
std::unique_ptr<Representer> representer_;
|
||||||
std::unique_ptr<update::Loader> updater_;
|
std::unique_ptr<update::Updater> updater_;
|
||||||
std::unique_ptr<update::AutoChecker> updateAutoChecker_;
|
|
||||||
int activeTaskCount_{0};
|
int activeTaskCount_{0};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -79,9 +79,5 @@ void Recognizer::updateSettings()
|
|||||||
SOFT_ASSERT(!settings_.tessdataPath.isEmpty(), return );
|
SOFT_ASSERT(!settings_.tessdataPath.isEmpty(), return );
|
||||||
|
|
||||||
queue_.clear();
|
queue_.clear();
|
||||||
const auto libName =
|
emit reset(settings_.tessdataPath);
|
||||||
(settings_.tesseractVersion == TesseractVersion::Optimized
|
|
||||||
? "tesseract-optimized"
|
|
||||||
: "tesseract-compatible");
|
|
||||||
emit reset(settings_.tessdataPath, libName);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void recognizeImpl(const TaskPtr &task);
|
void recognizeImpl(const TaskPtr &task);
|
||||||
void reset(const QString &tessdataPath, const QString &tesseractLibrary);
|
void reset(const QString &tessdataPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recognized(const TaskPtr &task);
|
void recognized(const TaskPtr &task);
|
||||||
|
|||||||
@ -17,8 +17,8 @@ void RecognizeWorker::handle(const TaskPtr &task)
|
|||||||
if (!engines_.count(task->sourceLanguage)) {
|
if (!engines_.count(task->sourceLanguage)) {
|
||||||
LTRACE() << "Create OCR engine" << task->sourceLanguage;
|
LTRACE() << "Create OCR engine" << task->sourceLanguage;
|
||||||
|
|
||||||
auto engine = std::make_unique<Tesseract>(task->sourceLanguage,
|
auto engine =
|
||||||
tessdataPath_, tesseractLibrary_);
|
std::make_unique<Tesseract>(task->sourceLanguage, tessdataPath_);
|
||||||
|
|
||||||
if (!engine->isValid()) {
|
if (!engine->isValid()) {
|
||||||
result->error = tr("Failed to init OCR engine: %1").arg(engine->error());
|
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);
|
emit finished(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecognizeWorker::reset(const QString &tessdataPath,
|
void RecognizeWorker::reset(const QString &tessdataPath)
|
||||||
const QString &tesseractLibrary)
|
|
||||||
{
|
{
|
||||||
if (tessdataPath_ == tessdataPath && tesseractLibrary_ == tesseractLibrary)
|
if (tessdataPath_ == tessdataPath)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tessdataPath_ = tessdataPath;
|
tessdataPath_ = tessdataPath;
|
||||||
tesseractLibrary_ = tesseractLibrary;
|
|
||||||
engines_.clear();
|
engines_.clear();
|
||||||
LTRACE() << "Cleared OCR engines";
|
LTRACE() << "Cleared OCR engines";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ public:
|
|||||||
~RecognizeWorker();
|
~RecognizeWorker();
|
||||||
|
|
||||||
void handle(const TaskPtr &task);
|
void handle(const TaskPtr &task);
|
||||||
void reset(const QString &tessdataPath, const QString &tesseractLibrary);
|
void reset(const QString &tessdataPath);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished(const TaskPtr &task);
|
void finished(const TaskPtr &task);
|
||||||
@ -24,5 +24,4 @@ private:
|
|||||||
std::map<QString, std::unique_ptr<Tesseract>> engines_;
|
std::map<QString, std::unique_ptr<Tesseract>> engines_;
|
||||||
std::map<QString, Generation> lastGenerations_;
|
std::map<QString, Generation> lastGenerations_;
|
||||||
QString tessdataPath_;
|
QString tessdataPath_;
|
||||||
QString tesseractLibrary_;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
|
||||||
#include <leptonica/allheaders.h>
|
#include <leptonica/allheaders.h>
|
||||||
|
#include <tesseract/baseapi.h>
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -90,138 +91,121 @@ static double getScale(Pix *source)
|
|||||||
return scale;
|
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 *);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Wrapper(const QString &libraryName)
|
explicit PixGuard(Pix *pix = nullptr)
|
||||||
: lib(libraryName)
|
: pix_(pix)
|
||||||
{
|
{
|
||||||
if (!lib.load()) {
|
}
|
||||||
LERROR() << "Failed to load tesseract library" << libraryName;
|
~PixGuard()
|
||||||
|
{
|
||||||
|
if (pix_)
|
||||||
|
pixDestroy(&pix_);
|
||||||
|
}
|
||||||
|
void operator=(Pix *pix)
|
||||||
|
{
|
||||||
|
if (!pix)
|
||||||
return;
|
return;
|
||||||
}
|
if (pix_)
|
||||||
|
pixDestroy(&pix_);
|
||||||
LTRACE() << "Loaded tesseract library" << lib.fileName();
|
pix_ = pix;
|
||||||
auto ok = true;
|
}
|
||||||
ok &= bool(createApi_ = (CreateApi)lib.resolve("TessBaseAPICreate"));
|
operator Pix *() { return pix_; }
|
||||||
ok &= bool(deleteApi_ = (DeleteApi)lib.resolve("TessBaseAPIDelete"));
|
Pix *operator->() { return pix_; }
|
||||||
ok &= bool(initApi_ = (InitApi)lib.resolve("TessBaseAPIInit2"));
|
Pix *&get() { return pix_; }
|
||||||
ok &= bool(setImage_ = (SetImage)lib.resolve("TessBaseAPISetImage2"));
|
Pix *take()
|
||||||
ok &= bool(getUtf8_ = (GetUtf8)lib.resolve("TessBaseAPIGetUTF8Text"));
|
{
|
||||||
ok &= bool(clearApi_ = (ClearApi)lib.resolve("TessBaseAPIClear"));
|
auto ret = pix_;
|
||||||
ok &= bool(deleteUtf8_ = (DeleteUtf8)lib.resolve("TessDeleteText"));
|
pix_ = nullptr;
|
||||||
if (!ok) {
|
return ret;
|
||||||
LERROR() << "Failed to resolve tesseract functions from" << libraryName;
|
}
|
||||||
|
void trace(const QString &name) const
|
||||||
|
{
|
||||||
|
LTRACE() << qPrintable(name) << pix_;
|
||||||
|
#if 0
|
||||||
|
if (!pix_)
|
||||||
return;
|
return;
|
||||||
}
|
auto fileName = name + ".png";
|
||||||
handle_ = createApi_();
|
fileName.replace(' ', "_");
|
||||||
}
|
convertImage(*pix_).save(fileName);
|
||||||
|
#endif
|
||||||
~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(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLibrary lib;
|
Pix *pix_;
|
||||||
CreateApi createApi_{nullptr};
|
|
||||||
DeleteApi deleteApi_{nullptr};
|
Q_DISABLE_COPY(PixGuard);
|
||||||
InitApi initApi_{nullptr};
|
|
||||||
SetImage setImage_{nullptr};
|
|
||||||
GetUtf8 getUtf8_{nullptr};
|
|
||||||
ClearApi clearApi_{nullptr};
|
|
||||||
DeleteUtf8 deleteUtf8_{nullptr};
|
|
||||||
TessBaseAPI *handle_{nullptr};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Tesseract::Tesseract(const LanguageId &language, const QString &tessdataPath,
|
static Pix *prepareImage(const QImage &image)
|
||||||
const QString &tesseractLibrary)
|
{
|
||||||
: tesseractLibrary_(tesseractLibrary)
|
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(!tessdataPath.isEmpty(), return );
|
||||||
SOFT_ASSERT(!language.isEmpty(), return );
|
SOFT_ASSERT(!language.isEmpty(), return );
|
||||||
@ -233,20 +217,22 @@ Tesseract::~Tesseract() = default;
|
|||||||
|
|
||||||
void Tesseract::init(const LanguageId &language, const QString &tessdataPath)
|
void Tesseract::init(const LanguageId &language, const QString &tessdataPath)
|
||||||
{
|
{
|
||||||
SOFT_ASSERT(!engine_, return );
|
SOFT_ASSERT(!api_, return );
|
||||||
|
|
||||||
engine_ = std::make_unique<Wrapper>(tesseractLibrary_);
|
api_ = std::make_unique<tesseract::TessBaseAPI>();
|
||||||
LTRACE() << "Created Tesseract api" << engine_.get();
|
LTRACE() << "Created Tesseract api" << api_.get();
|
||||||
|
|
||||||
const auto tesseractName = LanguageCodes::tesseract(language);
|
const auto tesseractName = LanguageCodes::tesseract(language);
|
||||||
auto result =
|
auto result = api_->Init(qPrintable(tessdataPath), qPrintable(tesseractName),
|
||||||
engine_->Init(qPrintable(tessdataPath), qPrintable(tesseractName));
|
tesseract::OcrEngineMode::OEM_DEFAULT);
|
||||||
LTRACE() << "Inited Tesseract api" << result;
|
LTRACE() << "Inited Tesseract api" << result;
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
api_->SetPageSegMode(tesseract::PageSegMode::PSM_AUTO);
|
||||||
|
|
||||||
error_ = QObject::tr("init failed");
|
error_ = QObject::tr("init failed");
|
||||||
engine_.reset();
|
api_.reset();
|
||||||
LTRACE() << "Cleared Tesseract api";
|
LTRACE() << "Cleared Tesseract api";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,19 +267,28 @@ QStringList Tesseract::availableLanguageNames(const QString &path)
|
|||||||
|
|
||||||
QString Tesseract::recognize(const QPixmap &source)
|
QString Tesseract::recognize(const QPixmap &source)
|
||||||
{
|
{
|
||||||
SOFT_ASSERT(engine_, return {});
|
SOFT_ASSERT(api_, return {});
|
||||||
SOFT_ASSERT(!source.isNull(), return {});
|
SOFT_ASSERT(!source.isNull(), return {});
|
||||||
|
|
||||||
error_.clear();
|
error_.clear();
|
||||||
|
|
||||||
Pix *image = prepareImage(source.toImage());
|
PixGuard image(prepareImage(source.toImage()));
|
||||||
SOFT_ASSERT(image, return {});
|
SOFT_ASSERT(image, return {});
|
||||||
LTRACE() << "Preprocessed Pix for OCR" << image;
|
LTRACE() << "Preprocessed Pix for OCR" << image;
|
||||||
|
|
||||||
auto result = engine_->GetText(image);
|
api_->SetImage(image);
|
||||||
|
LTRACE() << "Set Pix to engine";
|
||||||
|
|
||||||
cleanupImage(&image);
|
const auto outText = api_->GetUTF8Text();
|
||||||
LTRACE() << "Cleared preprocessed Pix";
|
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())
|
if (result.isEmpty())
|
||||||
error_ = QObject::tr("Failed to recognize text or no text selected");
|
error_ = QObject::tr("Failed to recognize text or no text selected");
|
||||||
@ -302,5 +297,5 @@ QString Tesseract::recognize(const QPixmap &source)
|
|||||||
|
|
||||||
bool Tesseract::isValid() const
|
bool Tesseract::isValid() const
|
||||||
{
|
{
|
||||||
return engine_.get();
|
return api_.get();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,15 @@
|
|||||||
|
|
||||||
class QPixmap;
|
class QPixmap;
|
||||||
class Task;
|
class Task;
|
||||||
|
namespace tesseract
|
||||||
|
{
|
||||||
|
class TessBaseAPI;
|
||||||
|
}
|
||||||
|
|
||||||
class Tesseract
|
class Tesseract
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Tesseract(const LanguageId& language, const QString& tessdataPath,
|
Tesseract(const LanguageId& language, const QString& tessdataPath);
|
||||||
const QString& tesseractLibrary);
|
|
||||||
~Tesseract();
|
~Tesseract();
|
||||||
|
|
||||||
QString recognize(const QPixmap& source);
|
QString recognize(const QPixmap& source);
|
||||||
@ -23,10 +26,8 @@ public:
|
|||||||
static QStringList availableLanguageNames(const QString& path);
|
static QStringList availableLanguageNames(const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Wrapper;
|
|
||||||
void init(const LanguageId& language, const QString& tessdataPath);
|
void init(const LanguageId& language, const QString& tessdataPath);
|
||||||
|
|
||||||
const QString tesseractLibrary_;
|
std::unique_ptr<tesseract::TessBaseAPI> api_;
|
||||||
std::unique_ptr<Wrapper> engine_;
|
|
||||||
QString error_;
|
QString error_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -135,6 +135,10 @@ bool Representer::eventFilter(QObject * /*watched*/, QEvent *event)
|
|||||||
const auto casted = static_cast<QMouseEvent *>(event);
|
const auto casted = static_cast<QMouseEvent *>(event);
|
||||||
if (casted->button() == Qt::LeftButton)
|
if (casted->button() == Qt::LeftButton)
|
||||||
hide();
|
hide();
|
||||||
|
} else if (event->type() == QEvent::KeyPress) {
|
||||||
|
const auto casted = static_cast<QKeyEvent *>(event);
|
||||||
|
if (casted->key() == Qt::Key_Escape)
|
||||||
|
hide();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,6 +104,7 @@ void ResultEditor::translate()
|
|||||||
task_->targetLanguage =
|
task_->targetLanguage =
|
||||||
LanguageCodes::idForName(targetLanguage_->currentText());
|
LanguageCodes::idForName(targetLanguage_->currentText());
|
||||||
task_->translators = settings_.translators;
|
task_->translators = settings_.translators;
|
||||||
|
task_->corrected = recognizedEdit_->toPlainText();
|
||||||
manager_.corrected(task_);
|
manager_.corrected(task_);
|
||||||
close();
|
close();
|
||||||
task_.reset();
|
task_.reset();
|
||||||
|
|||||||
@ -11,13 +11,15 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
ResultWidget::ResultWidget(Manager &manager, Representer &representer,
|
ResultWidget::ResultWidget(Manager &manager, Representer &representer,
|
||||||
const Settings &settings, QWidget *parent)
|
const Settings &settings, QWidget *parent)
|
||||||
: QFrame(parent)
|
: QFrame(parent)
|
||||||
, representer_(representer)
|
, representer_(representer)
|
||||||
, settings_(settings)
|
, settings_(settings)
|
||||||
, image_(new QLabel(this))
|
, imagePlaceholder_(new QWidget(this))
|
||||||
|
, image_(new QLabel(imagePlaceholder_))
|
||||||
, recognized_(new QLabel(this))
|
, recognized_(new QLabel(this))
|
||||||
, separator_(new QLabel(this))
|
, separator_(new QLabel(this))
|
||||||
, translated_(new QLabel(this))
|
, translated_(new QLabel(this))
|
||||||
@ -30,8 +32,6 @@ ResultWidget::ResultWidget(Manager &manager, Representer &representer,
|
|||||||
setFrameShape(QFrame::StyledPanel);
|
setFrameShape(QFrame::StyledPanel);
|
||||||
setFrameShadow(QFrame::Plain);
|
setFrameShadow(QFrame::Plain);
|
||||||
|
|
||||||
image_->setAlignment(Qt::AlignCenter);
|
|
||||||
|
|
||||||
recognized_->setAlignment(Qt::AlignCenter);
|
recognized_->setAlignment(Qt::AlignCenter);
|
||||||
recognized_->setWordWrap(true);
|
recognized_->setWordWrap(true);
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ ResultWidget::ResultWidget(Manager &manager, Representer &representer,
|
|||||||
installEventFilter(this);
|
installEventFilter(this);
|
||||||
|
|
||||||
auto layout = new QVBoxLayout(this);
|
auto layout = new QVBoxLayout(this);
|
||||||
layout->addWidget(image_);
|
layout->addWidget(imagePlaceholder_);
|
||||||
layout->addWidget(recognized_);
|
layout->addWidget(recognized_);
|
||||||
layout->addWidget(separator_);
|
layout->addWidget(separator_);
|
||||||
layout->addWidget(translated_);
|
layout->addWidget(translated_);
|
||||||
@ -86,6 +86,8 @@ void ResultWidget::show(const TaskPtr &task)
|
|||||||
task_ = task;
|
task_ = task;
|
||||||
|
|
||||||
image_->setPixmap(task->captured);
|
image_->setPixmap(task->captured);
|
||||||
|
image_->resize(task->captured.size());
|
||||||
|
imagePlaceholder_->setMinimumSize(image_->size());
|
||||||
|
|
||||||
recognized_->setText(task->corrected);
|
recognized_->setText(task->corrected);
|
||||||
const auto tooltip = task->recognized == task->corrected
|
const auto tooltip = task->recognized == task->corrected
|
||||||
@ -106,20 +108,37 @@ void ResultWidget::show(const TaskPtr &task)
|
|||||||
show();
|
show();
|
||||||
adjustSize();
|
adjustSize();
|
||||||
|
|
||||||
if (!image_->isVisible())
|
// window should not be smaller than selected image
|
||||||
|
if (!imagePlaceholder_->isVisible())
|
||||||
resize(std::max(width(), task->captured.width()),
|
resize(std::max(width(), task->captured.width()),
|
||||||
std::max(height(), task->captured.height()));
|
std::max(height(), task->captured.height()));
|
||||||
|
|
||||||
QDesktopWidget *desktop = QApplication::desktop();
|
// if window is wider than image then image should be at horizontal center
|
||||||
Q_CHECK_PTR(desktop);
|
const auto correctionToCenterImage =
|
||||||
const auto correction =
|
QPoint((width() - task->captured.width()) / 2, 2 * lineWidth());
|
||||||
QPoint((width() - task->captured.width()) / 2, lineWidth());
|
auto rect = QRect(task->capturePoint - correctionToCenterImage, size());
|
||||||
auto rect = QRect(task->capturePoint - correction, 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();
|
const auto shouldTextOnTop = rect.bottom() > screenRect.bottom();
|
||||||
if (shouldTextOnTop)
|
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());
|
auto layout = static_cast<QBoxLayout *>(this->layout());
|
||||||
SOFT_ASSERT(layout, return );
|
SOFT_ASSERT(layout, return );
|
||||||
@ -156,7 +175,7 @@ void ResultWidget::updateSettings()
|
|||||||
palette.setColor(QPalette::Window, separatorColor);
|
palette.setColor(QPalette::Window, separatorColor);
|
||||||
separator_->setPalette(palette);
|
separator_->setPalette(palette);
|
||||||
|
|
||||||
image_->setVisible(settings_.showCaptured);
|
imagePlaceholder_->setVisible(settings_.showCaptured);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResultWidget::mousePressEvent(QMouseEvent *event)
|
void ResultWidget::mousePressEvent(QMouseEvent *event)
|
||||||
|
|||||||
@ -31,6 +31,7 @@ private:
|
|||||||
Representer& representer_;
|
Representer& representer_;
|
||||||
const Settings& settings_;
|
const Settings& settings_;
|
||||||
TaskPtr task_;
|
TaskPtr task_;
|
||||||
|
QWidget* imagePlaceholder_;
|
||||||
QLabel* image_;
|
QLabel* image_;
|
||||||
QLabel* recognized_;
|
QLabel* recognized_;
|
||||||
QLabel* separator_;
|
QLabel* separator_;
|
||||||
|
|||||||
@ -153,6 +153,9 @@ quint32 GlobalAction::nativeKeycode(Qt::Key key)
|
|||||||
{
|
{
|
||||||
Display *display = QX11Info::display();
|
Display *display = QX11Info::display();
|
||||||
KeySym keySym = XStringToKeysym(qPrintable(QKeySequence(key).toString()));
|
KeySym keySym = XStringToKeysym(qPrintable(QKeySequence(key).toString()));
|
||||||
|
if (XKeysymToString(keySym) == nullptr) {
|
||||||
|
keySym = QChar(key).unicode();
|
||||||
|
}
|
||||||
return XKeysymToKeycode(display, keySym);
|
return XKeysymToKeycode(display, keySym);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +226,9 @@ quint32 GlobalAction::nativeKeycode(Qt::Key key)
|
|||||||
case Qt::Key_Down: return VK_DOWN;
|
case Qt::Key_Down: return VK_DOWN;
|
||||||
case Qt::Key_PageUp: return VK_PRIOR;
|
case Qt::Key_PageUp: return VK_PRIOR;
|
||||||
case Qt::Key_PageDown: return VK_NEXT;
|
case Qt::Key_PageDown: return VK_NEXT;
|
||||||
|
case Qt::Key_CapsLock: return VK_CAPITAL;
|
||||||
|
case Qt::Key_NumLock: return VK_NUMLOCK;
|
||||||
|
case Qt::Key_ScrollLock: return VK_SCROLL;
|
||||||
case Qt::Key_F1: return VK_F1;
|
case Qt::Key_F1: return VK_F1;
|
||||||
case Qt::Key_F2: return VK_F2;
|
case Qt::Key_F2: return VK_F2;
|
||||||
case Qt::Key_F3: return VK_F3;
|
case Qt::Key_F3: return VK_F3;
|
||||||
@ -248,11 +254,27 @@ quint32 GlobalAction::nativeKeycode(Qt::Key key)
|
|||||||
case Qt::Key_F23: return VK_F23;
|
case Qt::Key_F23: return VK_F23;
|
||||||
case Qt::Key_F24: return VK_F24;
|
case Qt::Key_F24: return VK_F24;
|
||||||
case Qt::Key_Space: return VK_SPACE;
|
case Qt::Key_Space: return VK_SPACE;
|
||||||
|
|
||||||
|
case Qt::Key_QuoteDbl: return VK_OEM_7;
|
||||||
|
case Qt::Key_Apostrophe: return VK_OEM_7;
|
||||||
|
case Qt::Key_Period: return VK_DECIMAL;
|
||||||
|
case Qt::Key_Colon: return VK_OEM_1;
|
||||||
|
case Qt::Key_Semicolon: return VK_OEM_1;
|
||||||
|
case Qt::Key_Less: return VK_OEM_COMMA;
|
||||||
|
case Qt::Key_Greater: return VK_OEM_PERIOD;
|
||||||
|
case Qt::Key_Question: return VK_OEM_2;
|
||||||
|
case Qt::Key_BracketLeft: return VK_OEM_4;
|
||||||
|
case Qt::Key_Backslash: return VK_OEM_5;
|
||||||
|
case Qt::Key_BracketRight: return VK_OEM_6;
|
||||||
|
case Qt::Key_QuoteLeft: return VK_OEM_3;
|
||||||
|
case Qt::Key_BraceLeft: return VK_OEM_4;
|
||||||
|
case Qt::Key_Bar: return VK_OEM_5;
|
||||||
|
case Qt::Key_BraceRight: return VK_OEM_6;
|
||||||
case Qt::Key_Asterisk: return VK_MULTIPLY;
|
case Qt::Key_Asterisk: return VK_MULTIPLY;
|
||||||
case Qt::Key_Plus: return VK_ADD;
|
case Qt::Key_Plus: return VK_OEM_PLUS;
|
||||||
case Qt::Key_Comma: return VK_SEPARATOR;
|
case Qt::Key_Comma: return VK_OEM_COMMA;
|
||||||
case Qt::Key_Minus: return VK_SUBTRACT;
|
case Qt::Key_Minus: return VK_OEM_MINUS;
|
||||||
case Qt::Key_Slash: return VK_DIVIDE;
|
case Qt::Key_Slash: return VK_OEM_2;
|
||||||
case Qt::Key_MediaNext: return VK_MEDIA_NEXT_TRACK;
|
case Qt::Key_MediaNext: return VK_MEDIA_NEXT_TRACK;
|
||||||
case Qt::Key_MediaPrevious: return VK_MEDIA_PREV_TRACK;
|
case Qt::Key_MediaPrevious: return VK_MEDIA_PREV_TRACK;
|
||||||
case Qt::Key_MediaPlay: return VK_MEDIA_PLAY_PAUSE;
|
case Qt::Key_MediaPlay: return VK_MEDIA_PLAY_PAUSE;
|
||||||
|
|||||||
71
src/service/keysequenceedit.cpp
Normal file
71
src/service/keysequenceedit.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#include "keysequenceedit.h"
|
||||||
|
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
namespace service
|
||||||
|
{
|
||||||
|
KeySequenceEdit::KeySequenceEdit(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
setPlaceholderText(tr("Press shortcut"));
|
||||||
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
|
setAttribute(Qt::WA_MacShowFocusRect, true);
|
||||||
|
setAttribute(Qt::WA_InputMethodEnabled, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QKeySequence &KeySequenceEdit::keySequence() const
|
||||||
|
{
|
||||||
|
return current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeySequenceEdit::setKeySequence(const QKeySequence &newKeySequence)
|
||||||
|
{
|
||||||
|
setKeySequence(newKeySequence, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeySequenceEdit::setKeySequence(const QKeySequence ¤t,
|
||||||
|
bool updateFallback)
|
||||||
|
{
|
||||||
|
current_ = current;
|
||||||
|
if (updateFallback)
|
||||||
|
fallback_ = current;
|
||||||
|
setText(current_.toString(QKeySequence::NativeText));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeySequenceEdit::event(QEvent *e)
|
||||||
|
{
|
||||||
|
switch (e->type()) {
|
||||||
|
case QEvent::Shortcut: return true;
|
||||||
|
case QEvent::ShortcutOverride: e->accept(); return true;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::event(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeySequenceEdit::keyPressEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
const auto key = event->key();
|
||||||
|
if (key == Qt::Key_Control || key == Qt::Key_Meta || key == Qt::Key_Alt ||
|
||||||
|
key == Qt::Key_Shift || key == Qt::Key_unknown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->key() == Qt::Key_Escape) {
|
||||||
|
setKeySequence(fallback_, false);
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->key() == Qt::Key_Backspace) {
|
||||||
|
setKeySequence({}, false);
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QKeySequence seq = event->modifiers() + event->key();
|
||||||
|
setKeySequence(seq, false);
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace service
|
||||||
25
src/service/keysequenceedit.h
Normal file
25
src/service/keysequenceedit.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
namespace service
|
||||||
|
{
|
||||||
|
class KeySequenceEdit : public QLineEdit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeySequenceEdit(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
const QKeySequence &keySequence() const;
|
||||||
|
void setKeySequence(const QKeySequence &newKeySequence);
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setKeySequence(const QKeySequence ¤t, bool updateFallback);
|
||||||
|
QKeySequence current_;
|
||||||
|
QKeySequence fallback_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace service
|
||||||
@ -37,12 +37,13 @@ void RunAtSystemStart::setEnabled(bool isOn)
|
|||||||
if (!f.open(QFile::WriteOnly))
|
if (!f.open(QFile::WriteOnly))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const auto appPath =
|
||||||
|
qEnvironmentVariable("APPIMAGE", QCoreApplication::applicationFilePath());
|
||||||
const auto contents = QString(R"([Desktop Entry]
|
const auto contents = QString(R"([Desktop Entry]
|
||||||
Name=%1
|
Name=%1
|
||||||
Exec=%2
|
Exec=%2
|
||||||
)")
|
)")
|
||||||
.arg(QCoreApplication::applicationName(),
|
.arg(QCoreApplication::applicationName(), appPath);
|
||||||
QCoreApplication::applicationFilePath());
|
|
||||||
f.write(contents.toUtf8());
|
f.write(contents.toUtf8());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -11,20 +11,19 @@ class QTreeView;
|
|||||||
namespace update
|
namespace update
|
||||||
{
|
{
|
||||||
enum class State { NotAvailable, NotInstalled, UpdateAvailable, Actual };
|
enum class State { NotAvailable, NotInstalled, UpdateAvailable, Actual };
|
||||||
enum class Action { NoAction, Remove, Install };
|
enum class Action { Install, Remove };
|
||||||
|
|
||||||
|
class Updater;
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
QVector<QUrl> urls;
|
QVector<QUrl> urls;
|
||||||
QString rawPath;
|
QString rawPath;
|
||||||
QString expandedPath;
|
QString expandedPath;
|
||||||
QString downloadPath;
|
|
||||||
QString md5;
|
QString md5;
|
||||||
QDateTime versionDate;
|
QDateTime versionDate;
|
||||||
int progress{0};
|
int progress{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
using UserActions = std::multimap<Action, File>;
|
|
||||||
|
|
||||||
class UpdateDelegate : public QStyledItemDelegate
|
class UpdateDelegate : public QStyledItemDelegate
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -38,30 +37,17 @@ class Model : public QAbstractItemModel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum class Column {
|
enum class Column { Name, State, Size, Version, Progress, Count };
|
||||||
Name,
|
|
||||||
State,
|
|
||||||
Action,
|
|
||||||
Size,
|
|
||||||
Version,
|
|
||||||
Progress,
|
|
||||||
Files,
|
|
||||||
Count
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit Model(QObject* parent = nullptr);
|
explicit Model(Updater& updater);
|
||||||
|
|
||||||
void initView(QTreeView* view);
|
|
||||||
|
|
||||||
QString parse(const QByteArray& data);
|
QString parse(const QByteArray& data);
|
||||||
void setExpansions(const std::map<QString, QString>& expansions);
|
void setExpansions(const QHash<QString, QString>& expansions);
|
||||||
UserActions userActions() const;
|
|
||||||
void updateStates();
|
void updateStates();
|
||||||
bool hasUpdates() const;
|
bool hasUpdates() const;
|
||||||
void updateProgress(const QUrl& url, int progress);
|
void updateProgress(const QUrl& url, int progress);
|
||||||
void resetProgress();
|
|
||||||
void selectAllUpdates();
|
void selectAllUpdates();
|
||||||
void resetActions();
|
void tryAction(Action action, const QModelIndex& index);
|
||||||
|
|
||||||
QModelIndex index(int row, int column,
|
QModelIndex index(int row, int column,
|
||||||
const QModelIndex& parent) const override;
|
const QModelIndex& parent) const override;
|
||||||
@ -72,16 +58,13 @@ public:
|
|||||||
int role) const override;
|
int role) const override;
|
||||||
QVariant data(const QModelIndex& index, int role) const override;
|
QVariant data(const QModelIndex& index, int role) const override;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value,
|
|
||||||
int role) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Component {
|
struct Component {
|
||||||
QString name;
|
QString name;
|
||||||
State state{State::NotAvailable};
|
State state{State::NotAvailable};
|
||||||
Action action{Action::NoAction};
|
|
||||||
QString version;
|
QString version;
|
||||||
std::vector<File> files;
|
QVector<File> files;
|
||||||
bool checkOnly{false};
|
bool checkOnly{false};
|
||||||
std::vector<std::unique_ptr<Component>> children;
|
std::vector<std::unique_ptr<Component>> children;
|
||||||
Component* parent{nullptr};
|
Component* parent{nullptr};
|
||||||
@ -91,33 +74,14 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Component> parse(const QJsonObject& json) const;
|
std::unique_ptr<Component> parse(const QJsonObject& json) const;
|
||||||
void updateProgress(Component& component, const QUrl& url, int progress);
|
|
||||||
void updateState(Component& component);
|
|
||||||
State currentState(const File& file) const;
|
State currentState(const File& file) const;
|
||||||
QString expanded(const QString& source) const;
|
QString expanded(const QString& source) const;
|
||||||
Component* toComponent(const QModelIndex& index) const;
|
Component* toComponent(const QModelIndex& index) const;
|
||||||
QModelIndex toIndex(const Component& component, int column) const;
|
QModelIndex toIndex(const Component& component, int column) const;
|
||||||
|
|
||||||
|
Updater& updater_;
|
||||||
std::unique_ptr<Component> root_;
|
std::unique_ptr<Component> root_;
|
||||||
std::map<QString, QString> expansions_;
|
QHash<QString, QString> expansions_;
|
||||||
};
|
|
||||||
|
|
||||||
class Installer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Installer(const UserActions& actions);
|
|
||||||
bool commit();
|
|
||||||
QString errorString() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void remove(const File& file);
|
|
||||||
void install(const File& file);
|
|
||||||
void checkRemove(const File& file);
|
|
||||||
void checkInstall(const File& file);
|
|
||||||
bool checkIsPossible();
|
|
||||||
|
|
||||||
UserActions actions_;
|
|
||||||
QStringList errors_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Loader : public QObject
|
class Loader : public QObject
|
||||||
@ -125,60 +89,85 @@ class Loader : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
using Urls = QVector<QUrl>;
|
using Urls = QVector<QUrl>;
|
||||||
explicit Loader(const Urls& updateUrls, QObject* parent = nullptr);
|
explicit Loader(Updater& updater);
|
||||||
|
|
||||||
void checkForUpdates();
|
void download(const Urls& urls);
|
||||||
void applyUserActions();
|
|
||||||
Model* model() const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void updatesAvailable();
|
|
||||||
void updated();
|
|
||||||
void error(const QString& error);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addError(const QString& text);
|
void start(const Urls& urls, const QUrl& previous, const QString& error);
|
||||||
void dumpErrors();
|
|
||||||
void handleReply(QNetworkReply* reply);
|
void handleReply(QNetworkReply* reply);
|
||||||
bool handleComponentReply(QNetworkReply* reply);
|
|
||||||
void handleUpdateReply(QNetworkReply* reply);
|
|
||||||
QString toError(QNetworkReply& reply) const;
|
|
||||||
void finishUpdate(const QString& error = {});
|
|
||||||
void commitUpdate();
|
|
||||||
void updateProgress(qint64 bytesSent, qint64 bytesTotal);
|
|
||||||
bool startDownload(File& file);
|
|
||||||
void startDownloadUpdates(const QUrl& previous);
|
|
||||||
|
|
||||||
|
Updater& updater_;
|
||||||
QNetworkAccessManager* network_;
|
QNetworkAccessManager* network_;
|
||||||
Model* model_;
|
QHash<QNetworkReply*, Urls> downloads_;
|
||||||
Urls updateUrls_;
|
};
|
||||||
QString downloadPath_;
|
|
||||||
std::map<QNetworkReply*, File*> downloads_;
|
class Installer
|
||||||
QStringList errors_;
|
{
|
||||||
UserActions currentActions_;
|
public:
|
||||||
|
void remove(const File& file);
|
||||||
|
void install(const File& file, const QByteArray& data);
|
||||||
|
void checkInstall(const File& file);
|
||||||
|
const QString& error() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString error_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AutoChecker : public QObject
|
class AutoChecker : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit AutoChecker(Loader& loader, QObject* parent = nullptr);
|
AutoChecker(Updater& updater, int intervalDays, const QDateTime& lastCheck);
|
||||||
~AutoChecker();
|
~AutoChecker();
|
||||||
|
|
||||||
bool isLastCheckDateChanged() const;
|
const QDateTime& lastCheckDate() const;
|
||||||
QDateTime lastCheckDate() const;
|
|
||||||
void setCheckIntervalDays(int days);
|
|
||||||
void setLastCheckDate(const QDateTime& dt);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleModelReset();
|
void updateLastCheckDate();
|
||||||
void scheduleNextCheck();
|
void scheduleNextCheck();
|
||||||
|
|
||||||
Loader& loader_;
|
Updater& updater_;
|
||||||
bool isLastCheckDateChanged_{false};
|
|
||||||
int checkIntervalDays_{0};
|
int checkIntervalDays_{0};
|
||||||
QDateTime lastCheckDate_;
|
QDateTime lastCheckDate_;
|
||||||
std::unique_ptr<QTimer> timer_;
|
std::unique_ptr<QTimer> timer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Updater : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Updater(const QVector<QUrl>& updateUrls);
|
||||||
|
|
||||||
|
void initView(QTreeView* view);
|
||||||
|
void setExpansions(const QHash<QString, QString>& expansions);
|
||||||
|
void checkForUpdates();
|
||||||
|
|
||||||
|
QDateTime lastUpdateCheck() const;
|
||||||
|
void setAutoUpdate(int intervalDays, const QDateTime& lastCheck);
|
||||||
|
|
||||||
|
void applyAction(Action action, const QVector<File>& files);
|
||||||
|
void downloaded(const QUrl& url, const QByteArray& data);
|
||||||
|
void updateProgress(const QUrl& url, qint64 bytesSent, qint64 bytesTotal);
|
||||||
|
void downloadFailed(const QUrl& url, const QString& error);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void checkedForUpdates();
|
||||||
|
void updatesAvailable();
|
||||||
|
void updated();
|
||||||
|
void error(const QString& error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleModelDoubleClick(const QModelIndex& index);
|
||||||
|
void showModelContextMenu();
|
||||||
|
int findDownload(const QUrl& url) const;
|
||||||
|
QModelIndex fromProxy(const QModelIndex& index) const;
|
||||||
|
|
||||||
|
std::unique_ptr<Model> model_;
|
||||||
|
std::unique_ptr<Loader> loader_;
|
||||||
|
std::unique_ptr<AutoChecker> autoChecker_;
|
||||||
|
QVector<QUrl> updateUrls_;
|
||||||
|
QVector<File> downloading_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace update
|
} // namespace update
|
||||||
|
|||||||
@ -7,9 +7,14 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const QString iniFileName = "settings.ini";
|
const QString iniFileName()
|
||||||
|
{
|
||||||
|
return QApplication::applicationDirPath() + "/" + "settings.ini";
|
||||||
|
}
|
||||||
|
|
||||||
const QString qs_guiGroup = "GUI";
|
const QString qs_guiGroup = "GUI";
|
||||||
const QString qs_captureHotkey = "captureHotkey";
|
const QString qs_captureHotkey = "captureHotkey";
|
||||||
@ -30,7 +35,6 @@ const QString qs_showMessageOnStart = "showMessageOnStart";
|
|||||||
|
|
||||||
const QString qs_recogntionGroup = "Recognition";
|
const QString qs_recogntionGroup = "Recognition";
|
||||||
const QString qs_ocrLanguage = "language";
|
const QString qs_ocrLanguage = "language";
|
||||||
const QString qs_tesseractVersion = "tesseractVersion";
|
|
||||||
|
|
||||||
const QString qs_correctionGroup = "Correction";
|
const QString qs_correctionGroup = "Correction";
|
||||||
const QString qs_userSubstitutions = "userSubstitutions";
|
const QString qs_userSubstitutions = "userSubstitutions";
|
||||||
@ -97,8 +101,7 @@ Substitutions loadLegacySubstitutions()
|
|||||||
|
|
||||||
const auto data = f.readAll();
|
const auto data = f.readAll();
|
||||||
|
|
||||||
const auto lines =
|
const auto lines = QString::fromUtf8(data).split('\n', Qt::SkipEmptyParts);
|
||||||
QString::fromUtf8(data).split('\n', QString::SkipEmptyParts);
|
|
||||||
for (const auto& line : lines) {
|
for (const auto& line : lines) {
|
||||||
const auto parts = line.mid(1, line.size() - 2).split("\",\""); // remove "
|
const auto parts = line.mid(1, line.size() - 2).split("\",\""); // remove "
|
||||||
if (parts.size() < 3)
|
if (parts.size() < 3)
|
||||||
@ -134,11 +137,12 @@ void cleanupOutdated(QSettings& settings)
|
|||||||
void Settings::save() const
|
void Settings::save() const
|
||||||
{
|
{
|
||||||
std::unique_ptr<QSettings> ptr;
|
std::unique_ptr<QSettings> ptr;
|
||||||
|
const auto iniName = iniFileName();
|
||||||
if (isPortable_) {
|
if (isPortable_) {
|
||||||
ptr = std::make_unique<QSettings>(iniFileName, QSettings::IniFormat);
|
ptr = std::make_unique<QSettings>(iniName, QSettings::IniFormat);
|
||||||
} else {
|
} else {
|
||||||
ptr = std::make_unique<QSettings>();
|
ptr = std::make_unique<QSettings>();
|
||||||
QFile::remove(iniFileName);
|
QFile::remove(iniName);
|
||||||
}
|
}
|
||||||
auto& settings = *ptr;
|
auto& settings = *ptr;
|
||||||
|
|
||||||
@ -172,7 +176,6 @@ void Settings::save() const
|
|||||||
|
|
||||||
settings.beginGroup(qs_recogntionGroup);
|
settings.beginGroup(qs_recogntionGroup);
|
||||||
settings.setValue(qs_ocrLanguage, sourceLanguage);
|
settings.setValue(qs_ocrLanguage, sourceLanguage);
|
||||||
settings.setValue(qs_tesseractVersion, int(tesseractVersion));
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.beginGroup(qs_correctionGroup);
|
settings.beginGroup(qs_correctionGroup);
|
||||||
@ -213,8 +216,9 @@ void Settings::save() const
|
|||||||
void Settings::load()
|
void Settings::load()
|
||||||
{
|
{
|
||||||
std::unique_ptr<QSettings> ptr;
|
std::unique_ptr<QSettings> ptr;
|
||||||
if (QFile::exists(iniFileName)) {
|
const auto iniName = iniFileName();
|
||||||
ptr = std::make_unique<QSettings>(iniFileName, QSettings::IniFormat);
|
if (QFile::exists(iniName)) {
|
||||||
|
ptr = std::make_unique<QSettings>(iniName, QSettings::IniFormat);
|
||||||
setPortable(true);
|
setPortable(true);
|
||||||
} else {
|
} else {
|
||||||
ptr = std::make_unique<QSettings>();
|
ptr = std::make_unique<QSettings>();
|
||||||
@ -259,9 +263,6 @@ void Settings::load()
|
|||||||
|
|
||||||
settings.beginGroup(qs_recogntionGroup);
|
settings.beginGroup(qs_recogntionGroup);
|
||||||
sourceLanguage = settings.value(qs_ocrLanguage, sourceLanguage).toString();
|
sourceLanguage = settings.value(qs_ocrLanguage, sourceLanguage).toString();
|
||||||
tesseractVersion = TesseractVersion(std::clamp(
|
|
||||||
settings.value(qs_tesseractVersion, int(tesseractVersion)).toInt(),
|
|
||||||
int(TesseractVersion::Optimized), int(TesseractVersion::Compatible)));
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.beginGroup(qs_correctionGroup);
|
settings.beginGroup(qs_correctionGroup);
|
||||||
@ -309,8 +310,9 @@ void Settings::load()
|
|||||||
void Settings::saveLastUpdateCheck()
|
void Settings::saveLastUpdateCheck()
|
||||||
{
|
{
|
||||||
std::unique_ptr<QSettings> ptr;
|
std::unique_ptr<QSettings> ptr;
|
||||||
if (QFile::exists(iniFileName)) {
|
const auto iniName = iniFileName();
|
||||||
ptr = std::make_unique<QSettings>(iniFileName, QSettings::IniFormat);
|
if (QFile::exists(iniName)) {
|
||||||
|
ptr = std::make_unique<QSettings>(iniName, QSettings::IniFormat);
|
||||||
} else {
|
} else {
|
||||||
ptr = std::make_unique<QSettings>();
|
ptr = std::make_unique<QSettings>();
|
||||||
}
|
}
|
||||||
@ -331,11 +333,11 @@ void Settings::setPortable(bool isPortable)
|
|||||||
isPortable_ = isPortable;
|
isPortable_ = isPortable;
|
||||||
|
|
||||||
const auto baseDataPath =
|
const auto baseDataPath =
|
||||||
(isPortable ? QDir().absolutePath()
|
(isPortable ? QApplication::applicationDirPath()
|
||||||
: QStandardPaths::writableLocation(
|
: QStandardPaths::writableLocation(
|
||||||
QStandardPaths::AppDataLocation)) +
|
QStandardPaths::AppDataLocation)) +
|
||||||
"/assets";
|
"/assets";
|
||||||
tessdataPath = baseDataPath + "/tessdata";
|
tessdataPath = baseDataPath + "/tessdata";
|
||||||
translatorsDir = baseDataPath + "/translators";
|
translatorsPath = baseDataPath + "/translators";
|
||||||
hunspellDir = baseDataPath + "/hunspell";
|
hunspellPath = baseDataPath + "/hunspell";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
enum class ResultMode { Widget, Tooltip };
|
enum class ResultMode { Widget, Tooltip };
|
||||||
|
|
||||||
@ -14,12 +15,10 @@ struct Substitution {
|
|||||||
QString source;
|
QString source;
|
||||||
QString target;
|
QString target;
|
||||||
};
|
};
|
||||||
using Substitutions = std::unordered_multimap<LanguageId, Substitution>;
|
using Substitutions = std::multimap<LanguageId, Substitution>;
|
||||||
|
|
||||||
enum class ProxyType { Disabled, System, Socks5, Http };
|
enum class ProxyType { Disabled, System, Socks5, Http };
|
||||||
|
|
||||||
enum class TesseractVersion { Optimized, Compatible };
|
|
||||||
|
|
||||||
class Settings
|
class Settings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -51,7 +50,7 @@ public:
|
|||||||
QDateTime lastUpdateCheck;
|
QDateTime lastUpdateCheck;
|
||||||
|
|
||||||
bool useHunspell{false};
|
bool useHunspell{false};
|
||||||
QString hunspellDir;
|
QString hunspellPath;
|
||||||
Substitutions userSubstitutions;
|
Substitutions userSubstitutions;
|
||||||
bool useUserSubstitutions{true};
|
bool useUserSubstitutions{true};
|
||||||
|
|
||||||
@ -59,13 +58,12 @@ public:
|
|||||||
|
|
||||||
QString tessdataPath;
|
QString tessdataPath;
|
||||||
QString sourceLanguage{"eng"};
|
QString sourceLanguage{"eng"};
|
||||||
TesseractVersion tesseractVersion{TesseractVersion::Optimized};
|
|
||||||
|
|
||||||
bool doTranslation{true};
|
bool doTranslation{true};
|
||||||
bool ignoreSslErrors{false};
|
bool ignoreSslErrors{false};
|
||||||
LanguageId targetLanguage{"rus"};
|
LanguageId targetLanguage{"rus"};
|
||||||
std::chrono::seconds translationTimeout{15};
|
std::chrono::seconds translationTimeout{15};
|
||||||
QString translatorsDir;
|
QString translatorsPath;
|
||||||
QStringList translators{"google.js"};
|
QStringList translators{"google.js"};
|
||||||
|
|
||||||
ResultMode resultShowType{ResultMode::Widget}; // dialog
|
ResultMode resultShowType{ResultMode::Widget}; // dialog
|
||||||
|
|||||||
@ -1,18 +1,36 @@
|
|||||||
#include "settingseditor.h"
|
#include "settingseditor.h"
|
||||||
|
#include "debug.h"
|
||||||
#include "languagecodes.h"
|
#include "languagecodes.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "runatsystemstart.h"
|
#include "runatsystemstart.h"
|
||||||
#include "translator.h"
|
#include "settingsvalidator.h"
|
||||||
#include "ui_settingseditor.h"
|
#include "ui_settingseditor.h"
|
||||||
#include "updates.h"
|
#include "updates.h"
|
||||||
#include "widgetstate.h"
|
#include "widgetstate.h"
|
||||||
|
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
SettingsEditor::SettingsEditor(Manager &manager, update::Loader &updater)
|
namespace
|
||||||
|
{
|
||||||
|
enum class Page { // order must match ui->pagesView
|
||||||
|
General,
|
||||||
|
Recognition,
|
||||||
|
Correction,
|
||||||
|
Translation,
|
||||||
|
Representation,
|
||||||
|
Update,
|
||||||
|
Help,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
enum class PageColumn { Name, Description, Error, Count };
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SettingsEditor::SettingsEditor(Manager &manager, update::Updater &updater)
|
||||||
: ui(new Ui::SettingsEditor)
|
: ui(new Ui::SettingsEditor)
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, updater_(updater)
|
, updater_(updater)
|
||||||
|
, pageModel_(new QStandardItemModel(this))
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
@ -20,19 +38,70 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Loader &updater)
|
|||||||
this, &SettingsEditor::handleButtonBoxClicked);
|
this, &SettingsEditor::handleButtonBoxClicked);
|
||||||
|
|
||||||
connect(ui->portable, &QCheckBox::toggled, //
|
connect(ui->portable, &QCheckBox::toggled, //
|
||||||
this, &SettingsEditor::handlePortableChanged);
|
this, &SettingsEditor::updateState);
|
||||||
|
|
||||||
ui->runAtSystemStart->setEnabled(service::RunAtSystemStart::isAvailable());
|
ui->runAtSystemStart->setEnabled(service::RunAtSystemStart::isAvailable());
|
||||||
|
|
||||||
{
|
{
|
||||||
auto model = new QStringListModel(this);
|
struct Info {
|
||||||
model->setStringList({tr("General"), tr("Recognition"), tr("Correction"),
|
QString title;
|
||||||
tr("Translation"), tr("Representation"), tr("Update"),
|
QString description;
|
||||||
tr("About")});
|
};
|
||||||
ui->pagesList->setModel(model);
|
|
||||||
|
QMap<Page, Info> names{
|
||||||
|
{Page::General,
|
||||||
|
{tr("General"), tr("This page contains general program settings")}},
|
||||||
|
|
||||||
|
{Page::Recognition,
|
||||||
|
{tr("Recognition"),
|
||||||
|
tr("This page contains text recognition settings. "
|
||||||
|
"It shows the available languages that program can convert from "
|
||||||
|
"image to text")}},
|
||||||
|
|
||||||
|
{Page::Correction,
|
||||||
|
{tr("Correction"),
|
||||||
|
tr("This page contains recognized text correction settings. "
|
||||||
|
"It allows to fix some errors after recognition.\n"
|
||||||
|
"Hunspell searches for words that are similar to recognized ones "
|
||||||
|
"in its dictionary.\n"
|
||||||
|
"User correction allows to manually fix some frequently "
|
||||||
|
"happening mistakes.\n"
|
||||||
|
"User correction occurs before hunspell correction if both "
|
||||||
|
"are enabled")}},
|
||||||
|
|
||||||
|
{Page::Translation,
|
||||||
|
{tr("Translation"),
|
||||||
|
tr("This page contains settings, related to translation of the "
|
||||||
|
"recognized text. "
|
||||||
|
"Translation is done via enabled (checked) translation services. "
|
||||||
|
"If one fails, then second one will be used and so on. "
|
||||||
|
"If translator hangs it will be treated as failed after "
|
||||||
|
"given timeout")}},
|
||||||
|
|
||||||
|
{Page::Representation,
|
||||||
|
{tr("Representation"),
|
||||||
|
tr("This page contains result representation settings")}},
|
||||||
|
|
||||||
|
{Page::Update,
|
||||||
|
{tr("Update"),
|
||||||
|
tr("This page allow to install/update/remove program resources")}},
|
||||||
|
|
||||||
|
{Page::Help, {tr("Help"), tr("")}},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &i : names) {
|
||||||
|
const auto error = QString();
|
||||||
|
pageModel_->appendRow({new QStandardItem(i.title),
|
||||||
|
new QStandardItem(i.description),
|
||||||
|
new QStandardItem(error)});
|
||||||
|
}
|
||||||
|
ui->pagesList->setModel(pageModel_);
|
||||||
|
ui->pagesList->setModelColumn(int(PageColumn::Name));
|
||||||
auto selection = ui->pagesList->selectionModel();
|
auto selection = ui->pagesList->selectionModel();
|
||||||
connect(selection, &QItemSelectionModel::currentRowChanged, //
|
connect(selection, &QItemSelectionModel::currentRowChanged, //
|
||||||
this, &SettingsEditor::updateCurrentPage);
|
this, &SettingsEditor::updateCurrentPage);
|
||||||
|
selection->select(pageModel_->index(0, 0),
|
||||||
|
QItemSelectionModel::SelectCurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -53,14 +122,6 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Loader &updater)
|
|||||||
|
|
||||||
// recognition
|
// recognition
|
||||||
ui->tesseractLangCombo->setModel(models_.sourceLanguageModel());
|
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
|
// correction
|
||||||
ui->userSubstitutionsTable->setEnabled(ui->useUserSubstitutions->isChecked());
|
ui->userSubstitutionsTable->setEnabled(ui->useUserSubstitutions->isChecked());
|
||||||
@ -80,6 +141,12 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Loader &updater)
|
|||||||
ui->fontColor->setAutoFillBackground(true);
|
ui->fontColor->setAutoFillBackground(true);
|
||||||
ui->backgroundColor->setAutoFillBackground(true);
|
ui->backgroundColor->setAutoFillBackground(true);
|
||||||
ui->backgroundColor->setText(tr("Sample text"));
|
ui->backgroundColor->setText(tr("Sample text"));
|
||||||
|
ui->fontColor->setFocusPolicy(Qt::FocusPolicy::NoFocus);
|
||||||
|
ui->backgroundColor->setFocusPolicy(Qt::FocusPolicy::NoFocus);
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
ui->fontColor->setFlat(true);
|
||||||
|
ui->backgroundColor->setFlat(true);
|
||||||
|
#endif
|
||||||
connect(ui->dialogRadio, &QRadioButton::toggled, //
|
connect(ui->dialogRadio, &QRadioButton::toggled, //
|
||||||
ui->resultWindow, &QTableWidget::setEnabled);
|
ui->resultWindow, &QTableWidget::setEnabled);
|
||||||
connect(ui->resultFont, &QFontComboBox::currentFontChanged, //
|
connect(ui->resultFont, &QFontComboBox::currentFontChanged, //
|
||||||
@ -87,21 +154,23 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Loader &updater)
|
|||||||
connect(ui->resultFontSize, qOverload<int>(&QSpinBox::valueChanged), //
|
connect(ui->resultFontSize, qOverload<int>(&QSpinBox::valueChanged), //
|
||||||
this, &SettingsEditor::updateResultFont);
|
this, &SettingsEditor::updateResultFont);
|
||||||
connect(ui->fontColor, &QPushButton::clicked, //
|
connect(ui->fontColor, &QPushButton::clicked, //
|
||||||
this, [this] { pickColor(ColorContext::Font); });
|
this, [this] {
|
||||||
|
pickColor(ui->fontColor);
|
||||||
|
updateResultFont();
|
||||||
|
});
|
||||||
connect(ui->backgroundColor, &QPushButton::clicked, //
|
connect(ui->backgroundColor, &QPushButton::clicked, //
|
||||||
this, [this] { pickColor(ColorContext::Bagkround); });
|
this, [this] {
|
||||||
|
pickColor(ui->backgroundColor);
|
||||||
|
updateResultFont();
|
||||||
|
});
|
||||||
|
|
||||||
// updates
|
// updates
|
||||||
updater.model()->initView(ui->updatesView);
|
ui->updatesView->header()->setObjectName("updatesHeader");
|
||||||
adjustUpdatesView();
|
updater_.initView(ui->updatesView);
|
||||||
connect(updater_.model(), &QAbstractItemModel::modelReset, //
|
connect(&updater_, &update::Updater::updated, //
|
||||||
this, &SettingsEditor::adjustUpdatesView);
|
this, &SettingsEditor::updateState);
|
||||||
connect(&updater_, &update::Loader::updated, //
|
|
||||||
this, &SettingsEditor::adjustUpdatesView);
|
|
||||||
connect(ui->checkUpdates, &QPushButton::clicked, //
|
connect(ui->checkUpdates, &QPushButton::clicked, //
|
||||||
&updater_, &update::Loader::checkForUpdates);
|
&updater_, &update::Updater::checkForUpdates);
|
||||||
connect(ui->applyUpdates, &QPushButton::clicked, //
|
|
||||||
&updater_, &update::Loader::applyUserActions);
|
|
||||||
|
|
||||||
// about
|
// about
|
||||||
{
|
{
|
||||||
@ -113,11 +182,16 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Loader &updater)
|
|||||||
baseUrl + "/blob/master/share/Changelog_" +
|
baseUrl + "/blob/master/share/Changelog_" +
|
||||||
(locale.language() == QLocale::Russian ? "ru" : "en") + ".md";
|
(locale.language() == QLocale::Russian ? "ru" : "en") + ".md";
|
||||||
const auto license = baseUrl + "/blob/master/LICENSE.md";
|
const auto license = baseUrl + "/blob/master/LICENSE.md";
|
||||||
|
const auto help = locale.language() == QLocale::Russian
|
||||||
|
? "https://translator.gres.biz/page/download/"
|
||||||
|
: baseUrl + "/blob/master/README.md";
|
||||||
const auto aboutLines = QStringList{
|
const auto aboutLines = QStringList{
|
||||||
QObject::tr(
|
QObject::tr(
|
||||||
R"(<p>Optical character recognition (OCR) and translation tool</p>)"),
|
R"(<p>Optical character recognition (OCR) and translation tool</p>)"),
|
||||||
QObject::tr(R"(<p>Version: %1</p>)")
|
QObject::tr(R"(<p>Version: %1</p>)")
|
||||||
.arg(QApplication::applicationVersion()),
|
.arg(QApplication::applicationVersion()),
|
||||||
|
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>)")
|
QObject::tr(R"(<p>Changelog: <a href="%1">%2</a></p>)")
|
||||||
.arg(changelog, QUrl(changelog).fileName()),
|
.arg(changelog, QUrl(changelog).fileName()),
|
||||||
QObject::tr(R"(<p>License: <a href="%3">MIT</a></p>)").arg(license),
|
QObject::tr(R"(<p>License: <a href="%3">MIT</a></p>)").arg(license),
|
||||||
@ -186,8 +260,6 @@ Settings SettingsEditor::settings() const
|
|||||||
|
|
||||||
settings.sourceLanguage =
|
settings.sourceLanguage =
|
||||||
LanguageCodes::idForName(ui->tesseractLangCombo->currentText());
|
LanguageCodes::idForName(ui->tesseractLangCombo->currentText());
|
||||||
settings.tesseractVersion =
|
|
||||||
TesseractVersion(ui->tesseractVersion->currentIndex());
|
|
||||||
|
|
||||||
settings.useHunspell = ui->useHunspell->isChecked();
|
settings.useHunspell = ui->useHunspell->isChecked();
|
||||||
settings.useUserSubstitutions = ui->useUserSubstitutions->isChecked();
|
settings.useUserSubstitutions = ui->useUserSubstitutions->isChecked();
|
||||||
@ -199,13 +271,7 @@ Settings SettingsEditor::settings() const
|
|||||||
std::chrono::seconds(ui->translateTimeoutSpin->value());
|
std::chrono::seconds(ui->translateTimeoutSpin->value());
|
||||||
settings.targetLanguage =
|
settings.targetLanguage =
|
||||||
LanguageCodes::idForName(ui->translateLangCombo->currentText());
|
LanguageCodes::idForName(ui->translateLangCombo->currentText());
|
||||||
|
settings.translators = enabledTranslators();
|
||||||
settings.translators.clear();
|
|
||||||
for (auto i = 0, end = ui->translatorList->count(); i < end; ++i) {
|
|
||||||
auto item = ui->translatorList->item(i);
|
|
||||||
if (item->checkState() == Qt::Checked)
|
|
||||||
settings.translators.append(item->text());
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.resultShowType =
|
settings.resultShowType =
|
||||||
ui->trayRadio->isChecked() ? ResultMode::Tooltip : ResultMode::Widget;
|
ui->trayRadio->isChecked() ? ResultMode::Tooltip : ResultMode::Widget;
|
||||||
@ -224,11 +290,10 @@ Settings SettingsEditor::settings() const
|
|||||||
|
|
||||||
void SettingsEditor::setSettings(const Settings &settings)
|
void SettingsEditor::setSettings(const Settings &settings)
|
||||||
{
|
{
|
||||||
if (settings.isPortable() == ui->portable->isChecked())
|
|
||||||
updateModels(settings.tessdataPath);
|
|
||||||
|
|
||||||
wasPortable_ = settings.isPortable();
|
wasPortable_ = settings.isPortable();
|
||||||
|
ui->portable->blockSignals(true);
|
||||||
ui->portable->setChecked(settings.isPortable());
|
ui->portable->setChecked(settings.isPortable());
|
||||||
|
ui->portable->blockSignals(false);
|
||||||
|
|
||||||
ui->runAtSystemStart->setChecked(settings.runAtSystemStart);
|
ui->runAtSystemStart->setChecked(settings.runAtSystemStart);
|
||||||
|
|
||||||
@ -249,23 +314,23 @@ void SettingsEditor::setSettings(const Settings &settings)
|
|||||||
ui->proxySaveCheck->setChecked(settings.proxySavePassword);
|
ui->proxySaveCheck->setChecked(settings.proxySavePassword);
|
||||||
|
|
||||||
ui->tessdataPath->setText(settings.tessdataPath);
|
ui->tessdataPath->setText(settings.tessdataPath);
|
||||||
|
ui->translatorsPath->setText(settings.translatorsPath);
|
||||||
|
updateModels();
|
||||||
|
|
||||||
ui->tesseractLangCombo->setCurrentText(
|
ui->tesseractLangCombo->setCurrentText(
|
||||||
LanguageCodes::name(settings.sourceLanguage));
|
LanguageCodes::name(settings.sourceLanguage));
|
||||||
ui->tesseractVersion->setCurrentIndex(int(settings.tesseractVersion));
|
|
||||||
|
|
||||||
ui->useHunspell->setChecked(settings.useHunspell);
|
ui->useHunspell->setChecked(settings.useHunspell);
|
||||||
ui->hunspellDir->setText(settings.hunspellDir);
|
ui->hunspellDir->setText(settings.hunspellPath);
|
||||||
ui->useUserSubstitutions->setChecked(settings.useUserSubstitutions);
|
ui->useUserSubstitutions->setChecked(settings.useUserSubstitutions);
|
||||||
ui->userSubstitutionsTable->setSubstitutions(settings.userSubstitutions);
|
ui->userSubstitutionsTable->setSubstitutions(settings.userSubstitutions);
|
||||||
|
|
||||||
ui->doTranslationCheck->setChecked(settings.doTranslation);
|
ui->doTranslationCheck->setChecked(settings.doTranslation);
|
||||||
ui->ignoreSslCheck->setChecked(settings.ignoreSslErrors);
|
ui->ignoreSslCheck->setChecked(settings.ignoreSslErrors);
|
||||||
ui->translateTimeoutSpin->setValue(settings.translationTimeout.count());
|
ui->translateTimeoutSpin->setValue(settings.translationTimeout.count());
|
||||||
ui->translatorsPath->setText(settings.translatorsDir);
|
|
||||||
enabledTranslators_ = settings.translators;
|
|
||||||
updateTranslators();
|
|
||||||
ui->translateLangCombo->setCurrentText(
|
ui->translateLangCombo->setCurrentText(
|
||||||
LanguageCodes::name(settings.targetLanguage));
|
LanguageCodes::name(settings.targetLanguage));
|
||||||
|
updateTranslators(settings.translators);
|
||||||
|
|
||||||
ui->trayRadio->setChecked(settings.resultShowType == ResultMode::Tooltip);
|
ui->trayRadio->setChecked(settings.resultShowType == ResultMode::Tooltip);
|
||||||
ui->dialogRadio->setChecked(settings.resultShowType == ResultMode::Widget);
|
ui->dialogRadio->setChecked(settings.resultShowType == ResultMode::Widget);
|
||||||
@ -283,49 +348,73 @@ void SettingsEditor::setSettings(const Settings &settings)
|
|||||||
ui->showCaptured->setChecked(settings.showCaptured);
|
ui->showCaptured->setChecked(settings.showCaptured);
|
||||||
|
|
||||||
ui->autoUpdateInterval->setValue(settings.autoUpdateIntervalDays);
|
ui->autoUpdateInterval->setValue(settings.autoUpdateIntervalDays);
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::updateState()
|
||||||
|
{
|
||||||
|
Settings settings;
|
||||||
|
settings.setPortable(ui->portable->isChecked());
|
||||||
|
ui->tessdataPath->setText(settings.tessdataPath);
|
||||||
|
ui->translatorsPath->setText(settings.translatorsPath);
|
||||||
|
ui->hunspellDir->setText(settings.hunspellPath);
|
||||||
|
|
||||||
|
updateModels();
|
||||||
|
updateTranslators(enabledTranslators());
|
||||||
|
validateSettings();
|
||||||
|
updateCurrentPage();
|
||||||
|
|
||||||
|
const auto portableChanged = wasPortable_ != settings.isPortable();
|
||||||
|
ui->pageUpdate->setEnabled(!portableChanged);
|
||||||
|
ui->pageUpdate->setToolTip(portableChanged
|
||||||
|
? tr("Portable changed. Apply settings first")
|
||||||
|
: QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsEditor::updateCurrentPage()
|
void SettingsEditor::updateCurrentPage()
|
||||||
{
|
{
|
||||||
ui->pagesView->setCurrentIndex(ui->pagesList->currentIndex().row());
|
const auto row = ui->pagesList->currentIndex().row();
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsEditor::updateTranslators()
|
const auto description = pageModel_->index(row, int(PageColumn::Description));
|
||||||
{
|
ui->pageInfoLabel->setText(description.data().toString());
|
||||||
ui->translatorList->clear();
|
ui->pageInfoLabel->setVisible(!ui->pageInfoLabel->text().isEmpty());
|
||||||
|
|
||||||
auto names = Translator::availableTranslators(ui->translatorsPath->text());
|
const auto error = pageModel_->index(row, int(PageColumn::Error));
|
||||||
if (names.isEmpty())
|
ui->pageErrorLabel->setText(error.data().toString());
|
||||||
|
ui->pageErrorLabel->setVisible(!ui->pageErrorLabel->text().isEmpty());
|
||||||
|
|
||||||
|
ui->pagesView->setCurrentIndex(row);
|
||||||
|
|
||||||
|
if (ui->pagesView->currentWidget() != ui->pageUpdate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::sort(names.begin(), names.end());
|
if (ui->updatesView->model()->rowCount() == 0)
|
||||||
|
updater_.checkForUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
if (!enabledTranslators_.isEmpty()) {
|
void SettingsEditor::updateTranslators(const QStringList &translators)
|
||||||
for (const auto &name : enabledTranslators_) names.removeOne(name);
|
{
|
||||||
names = enabledTranslators_ + names;
|
ui->translatorList->clear();
|
||||||
|
if (models_.translators().isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QStringList all;
|
||||||
|
for (const auto &i : translators) {
|
||||||
|
if (models_.translators().contains(i))
|
||||||
|
all.append(i);
|
||||||
}
|
}
|
||||||
|
all += models_.translators();
|
||||||
ui->translatorList->addItems(names);
|
all.removeDuplicates();
|
||||||
|
ui->translatorList->addItems(all);
|
||||||
|
|
||||||
for (auto i = 0, end = ui->translatorList->count(); i < end; ++i) {
|
for (auto i = 0, end = ui->translatorList->count(); i < end; ++i) {
|
||||||
auto item = ui->translatorList->item(i);
|
auto item = ui->translatorList->item(i);
|
||||||
item->setCheckState(enabledTranslators_.contains(item->text())
|
item->setCheckState(translators.contains(item->text()) ? Qt::Checked
|
||||||
? Qt::Checked
|
: Qt::Unchecked);
|
||||||
: Qt::Unchecked);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsEditor::adjustUpdatesView()
|
|
||||||
{
|
|
||||||
ui->updatesView->resizeColumnToContents(int(update::Model::Column::Name));
|
|
||||||
|
|
||||||
if (ui->tessdataPath->text().isEmpty()) // not inited yet
|
|
||||||
return;
|
|
||||||
|
|
||||||
updateModels(ui->tessdataPath->text());
|
|
||||||
updateTranslators();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsEditor::handleButtonBoxClicked(QAbstractButton *button)
|
void SettingsEditor::handleButtonBoxClicked(QAbstractButton *button)
|
||||||
{
|
{
|
||||||
if (!button)
|
if (!button)
|
||||||
@ -342,42 +431,39 @@ void SettingsEditor::handleButtonBoxClicked(QAbstractButton *button)
|
|||||||
if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
|
if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
|
||||||
const auto settings = this->settings();
|
const auto settings = this->settings();
|
||||||
manager_.applySettings(settings);
|
manager_.applySettings(settings);
|
||||||
if (settings.isPortable() != wasPortable_) {
|
wasPortable_ = ui->portable->isChecked();
|
||||||
wasPortable_ = settings.isPortable();
|
updateState();
|
||||||
handlePortableChanged();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsEditor::handlePortableChanged()
|
|
||||||
{
|
|
||||||
Settings settings;
|
|
||||||
settings.setPortable(ui->portable->isChecked());
|
|
||||||
ui->tessdataPath->setText(settings.tessdataPath);
|
|
||||||
ui->translatorsPath->setText(settings.translatorsDir);
|
|
||||||
ui->hunspellDir->setText(settings.hunspellDir);
|
|
||||||
updateModels(settings.tessdataPath);
|
|
||||||
updateTranslators();
|
|
||||||
|
|
||||||
const auto portableChanged = wasPortable_ != settings.isPortable();
|
|
||||||
ui->pageUpdate->setEnabled(!portableChanged);
|
|
||||||
ui->pageUpdate->setToolTip(portableChanged
|
|
||||||
? tr("Portable changed. Apply settings first")
|
|
||||||
: QString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsEditor::updateResultFont()
|
void SettingsEditor::updateResultFont()
|
||||||
{
|
{
|
||||||
auto font = ui->resultFont->currentFont();
|
auto font = ui->resultFont->currentFont();
|
||||||
font.setPointSize(ui->resultFontSize->value());
|
font.setPointSize(ui->resultFontSize->value());
|
||||||
ui->resultFont->setFont(font);
|
ui->backgroundColor->setFont(font);
|
||||||
|
|
||||||
|
auto fontColor = ui->fontColor->palette().color(QPalette::Button);
|
||||||
|
QPalette palette(ui->backgroundColor->palette());
|
||||||
|
palette.setColor(QPalette::ButtonText, fontColor);
|
||||||
|
ui->backgroundColor->setPalette(palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsEditor::updateModels(const QString &tessdataPath)
|
QStringList SettingsEditor::enabledTranslators() const
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
for (auto i = 0, end = ui->translatorList->count(); i < end; ++i) {
|
||||||
|
auto item = ui->translatorList->item(i);
|
||||||
|
if (item->checkState() == Qt::Checked)
|
||||||
|
result.append(item->text());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::updateModels()
|
||||||
{
|
{
|
||||||
const auto source = ui->tesseractLangCombo->currentText();
|
const auto source = ui->tesseractLangCombo->currentText();
|
||||||
models_.update(tessdataPath);
|
models_.update(ui->tessdataPath->text(), ui->translatorsPath->text());
|
||||||
if (!source.isEmpty()) {
|
if (!source.isEmpty()) {
|
||||||
ui->tesseractLangCombo->setCurrentText(source);
|
ui->tesseractLangCombo->setCurrentText(source);
|
||||||
} else if (ui->tesseractLangCombo->count() > 0) {
|
} else if (ui->tesseractLangCombo->count() > 0) {
|
||||||
@ -385,10 +471,8 @@ void SettingsEditor::updateModels(const QString &tessdataPath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsEditor::pickColor(ColorContext context)
|
void SettingsEditor::pickColor(QWidget *widget)
|
||||||
{
|
{
|
||||||
const auto widget =
|
|
||||||
context == ColorContext::Font ? ui->fontColor : ui->backgroundColor;
|
|
||||||
const auto original = widget->palette().color(QPalette::Button);
|
const auto original = widget->palette().color(QPalette::Button);
|
||||||
const auto color = QColorDialog::getColor(original, this);
|
const auto color = QColorDialog::getColor(original, this);
|
||||||
|
|
||||||
@ -398,12 +482,53 @@ void SettingsEditor::pickColor(ColorContext context)
|
|||||||
QPalette palette(widget->palette());
|
QPalette palette(widget->palette());
|
||||||
palette.setColor(QPalette::Button, color);
|
palette.setColor(QPalette::Button, color);
|
||||||
widget->setPalette(palette);
|
widget->setPalette(palette);
|
||||||
|
}
|
||||||
|
|
||||||
if (context == ColorContext::Bagkround)
|
void SettingsEditor::validateSettings()
|
||||||
|
{
|
||||||
|
SettingsValidator validator;
|
||||||
|
{
|
||||||
|
auto settings = this->settings();
|
||||||
|
if (validator.correct(settings, models_)) {
|
||||||
|
setSettings(settings);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0, end = pageModel_->rowCount(); i < end; ++i) {
|
||||||
|
const auto name = pageModel_->index(i, int(PageColumn::Name));
|
||||||
|
pageModel_->setData(name, QBrush(Qt::black), Qt::ForegroundRole);
|
||||||
|
|
||||||
|
const auto error = pageModel_->index(i, int(PageColumn::Error));
|
||||||
|
pageModel_->setData(error, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto errors = validator.check(settings(), models_);
|
||||||
|
if (errors.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
palette = ui->backgroundColor->palette();
|
using E = SettingsValidator::Error;
|
||||||
palette.setColor(QPalette::ButtonText, color);
|
QMap<E, Page> errorToPage{
|
||||||
ui->backgroundColor->setPalette(palette);
|
{E::NoSourceInstalled, Page::Update},
|
||||||
ui->backgroundColor->update();
|
{E::NoSourceSet, Page::Recognition},
|
||||||
|
{E::NoTranslatorInstalled, Page::Update},
|
||||||
|
{E::NoTranslatorSet, Page::Translation},
|
||||||
|
{E::NoTargetSet, Page::Translation},
|
||||||
|
};
|
||||||
|
|
||||||
|
QMap<Page, QStringList> summary;
|
||||||
|
for (const auto err : errors) {
|
||||||
|
SOFT_ASSERT(errorToPage.contains(err), continue);
|
||||||
|
auto page = errorToPage[err];
|
||||||
|
summary[page].push_back(validator.toString(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = summary.cbegin(), end = summary.cend(); it != end; ++it) {
|
||||||
|
const auto row = int(it.key());
|
||||||
|
const auto index = pageModel_->index(row, int(PageColumn::Name));
|
||||||
|
pageModel_->setData(index, QBrush(Qt::red), Qt::ForegroundRole);
|
||||||
|
|
||||||
|
const auto error = pageModel_->index(row, int(PageColumn::Error));
|
||||||
|
pageModel_->setData(error, it.value().join('\n'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,33 +10,35 @@ namespace Ui
|
|||||||
class SettingsEditor;
|
class SettingsEditor;
|
||||||
}
|
}
|
||||||
class QAbstractButton;
|
class QAbstractButton;
|
||||||
|
class QStandardItemModel;
|
||||||
|
|
||||||
class SettingsEditor : public QDialog
|
class SettingsEditor : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SettingsEditor(Manager &manager, update::Loader &updater);
|
SettingsEditor(Manager &manager, update::Updater &updater);
|
||||||
~SettingsEditor();
|
~SettingsEditor();
|
||||||
|
|
||||||
Settings settings() const;
|
Settings settings() const;
|
||||||
void setSettings(const Settings &settings);
|
void setSettings(const Settings &settings);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum ColorContext { Font, Bagkround };
|
|
||||||
void updateCurrentPage();
|
|
||||||
void updateTranslators();
|
|
||||||
void adjustUpdatesView();
|
|
||||||
void handleButtonBoxClicked(QAbstractButton *button);
|
void handleButtonBoxClicked(QAbstractButton *button);
|
||||||
void handlePortableChanged();
|
void pickColor(QWidget *widget);
|
||||||
void updateResultFont();
|
void updateResultFont();
|
||||||
void updateModels(const QString &tessdataPath);
|
QStringList enabledTranslators() const;
|
||||||
void pickColor(ColorContext context);
|
|
||||||
|
void updateState();
|
||||||
|
void updateCurrentPage();
|
||||||
|
void updateTranslators(const QStringList &translators);
|
||||||
|
void updateModels();
|
||||||
|
void validateSettings();
|
||||||
|
|
||||||
Ui::SettingsEditor *ui;
|
Ui::SettingsEditor *ui;
|
||||||
Manager &manager_;
|
Manager &manager_;
|
||||||
update::Loader &updater_;
|
update::Updater &updater_;
|
||||||
CommonModels models_;
|
CommonModels models_;
|
||||||
QStringList enabledTranslators_;
|
|
||||||
bool wasPortable_{false};
|
bool wasPortable_{false};
|
||||||
|
QStandardItemModel *pageModel_;
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
58
src/settingsvalidator.cpp
Normal file
58
src/settingsvalidator.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include "settingsvalidator.h"
|
||||||
|
|
||||||
|
#include <commonmodels.h>
|
||||||
|
#include <settings.h>
|
||||||
|
|
||||||
|
QVector<SettingsValidator::Error> SettingsValidator::check(
|
||||||
|
const Settings &settings, const CommonModels &models) const
|
||||||
|
{
|
||||||
|
QVector<SettingsValidator::Error> result;
|
||||||
|
|
||||||
|
if (models.sourceLanguageModel()->rowCount() == 0)
|
||||||
|
result.append(Error::NoSourceInstalled);
|
||||||
|
|
||||||
|
if (settings.sourceLanguage.isEmpty())
|
||||||
|
result.append(Error::NoSourceSet);
|
||||||
|
|
||||||
|
if (settings.doTranslation && models.translators().isEmpty())
|
||||||
|
result.append(Error::NoTranslatorInstalled);
|
||||||
|
|
||||||
|
if (settings.doTranslation && settings.translators.isEmpty())
|
||||||
|
result.append(Error::NoTranslatorSet);
|
||||||
|
|
||||||
|
if (settings.doTranslation && settings.targetLanguage.isEmpty())
|
||||||
|
result.append(Error::NoTargetSet);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsValidator::correct(Settings &settings, const CommonModels &models)
|
||||||
|
{
|
||||||
|
auto changed = false;
|
||||||
|
|
||||||
|
if (settings.doTranslation && settings.translators.isEmpty() &&
|
||||||
|
!models.translators().isEmpty()) {
|
||||||
|
settings.translators = models.translators();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SettingsValidator::toString(Error error) const
|
||||||
|
{
|
||||||
|
switch (error) {
|
||||||
|
case Error::NoSourceInstalled:
|
||||||
|
return QObject::tr("No recognizers installed");
|
||||||
|
|
||||||
|
case Error::NoSourceSet: return QObject::tr("Recognition language not set");
|
||||||
|
|
||||||
|
case Error::NoTranslatorInstalled:
|
||||||
|
return QObject::tr("No translators installed");
|
||||||
|
|
||||||
|
case Error::NoTranslatorSet:
|
||||||
|
return QObject::tr("No translators enabled (selected)");
|
||||||
|
|
||||||
|
case Error::NoTargetSet: return QObject::tr("Translation language not set");
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
22
src/settingsvalidator.h
Normal file
22
src/settingsvalidator.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stfwd.h>
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class SettingsValidator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Error {
|
||||||
|
NoSourceInstalled,
|
||||||
|
NoSourceSet,
|
||||||
|
NoTranslatorSet,
|
||||||
|
NoTranslatorInstalled,
|
||||||
|
NoTargetSet
|
||||||
|
};
|
||||||
|
|
||||||
|
QVector<Error> check(const Settings& settings,
|
||||||
|
const CommonModels& models) const;
|
||||||
|
bool correct(Settings& settings, const CommonModels& models);
|
||||||
|
QString toString(Error error) const;
|
||||||
|
};
|
||||||
@ -22,8 +22,7 @@ class CommonModels;
|
|||||||
|
|
||||||
namespace update
|
namespace update
|
||||||
{
|
{
|
||||||
class Loader;
|
class Updater;
|
||||||
class AutoChecker;
|
|
||||||
} // namespace update
|
} // namespace update
|
||||||
|
|
||||||
using TaskPtr = std::shared_ptr<Task>;
|
using TaskPtr = std::shared_ptr<Task>;
|
||||||
|
|||||||
@ -132,11 +132,11 @@ void Translator::updateSettings()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const auto loaded =
|
const auto loaded =
|
||||||
loadScripts(settings_.translatorsDir, settings_.translators);
|
loadScripts(settings_.translatorsPath, settings_.translators);
|
||||||
if (loaded.empty()) {
|
if (loaded.empty()) {
|
||||||
manager_.fatalError(
|
manager_.fatalError(
|
||||||
tr("No translators loaded from\n%1\n(%2)")
|
tr("No translators loaded from\n%1\n(%2)")
|
||||||
.arg(settings_.translatorsDir, settings_.translators.join(", ")));
|
.arg(settings_.translatorsPath, settings_.translators.join(", ")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,16 @@ void TrayIcon::blockActions(bool block)
|
|||||||
{
|
{
|
||||||
isActionsBlocked_ = block;
|
isActionsBlocked_ = block;
|
||||||
updateActions();
|
updateActions();
|
||||||
|
const auto actions =
|
||||||
|
QVector<QAction *>{captureAction_, repeatCaptureAction_, showLastAction_,
|
||||||
|
clipboardAction_, captureLockedAction_};
|
||||||
|
for (const auto i : actions) {
|
||||||
|
if (block) {
|
||||||
|
GlobalAction::removeGlobal(i);
|
||||||
|
} else {
|
||||||
|
GlobalAction::makeGlobal(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrayIcon::setTaskActionsEnabled(bool isEnabled)
|
void TrayIcon::setTaskActionsEnabled(bool isEnabled)
|
||||||
|
|||||||
@ -14,10 +14,9 @@ const auto f1 = "from1.txt";
|
|||||||
const auto t1 = "test/to1.txt";
|
const auto t1 = "test/to1.txt";
|
||||||
const auto data = "sample";
|
const auto data = "sample";
|
||||||
|
|
||||||
File toFile(const QString& from, const QString& to)
|
File toFile(const QString& to)
|
||||||
{
|
{
|
||||||
File result;
|
File result;
|
||||||
result.downloadPath = from;
|
|
||||||
result.expandedPath = to;
|
result.expandedPath = to;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -47,53 +46,38 @@ bool removeFile(const QString& name)
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(UpdateInstaller, Empty)
|
|
||||||
{
|
|
||||||
UserActions actions;
|
|
||||||
Installer testee(actions);
|
|
||||||
ASSERT_TRUE(testee.commit());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UpdateInstaller, SuccessInstall)
|
TEST(UpdateInstaller, SuccessInstall)
|
||||||
{
|
{
|
||||||
ASSERT_TRUE(removeFile(t1));
|
ASSERT_TRUE(removeFile(t1));
|
||||||
ASSERT_TRUE(writeFile(f1, data));
|
|
||||||
|
|
||||||
UserActions actions{{Action::Install, toFile(f1, t1)}};
|
Installer testee;
|
||||||
Installer testee(actions);
|
testee.install(toFile(t1), data);
|
||||||
ASSERT_TRUE(testee.commit());
|
|
||||||
ASSERT_EQ(data, readFile(t1));
|
ASSERT_EQ(data, readFile(t1));
|
||||||
|
ASSERT_TRUE(testee.error().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UpdateInstaller, SuccessRemove)
|
TEST(UpdateInstaller, SuccessRemove)
|
||||||
{
|
{
|
||||||
ASSERT_TRUE(writeFile(f1, data));
|
ASSERT_TRUE(writeFile(f1, data));
|
||||||
|
|
||||||
UserActions actions{{Action::Remove, toFile(f1, f1)}};
|
Installer testee;
|
||||||
Installer testee(actions);
|
testee.remove(toFile(f1));
|
||||||
ASSERT_TRUE(testee.commit());
|
|
||||||
ASSERT_FALSE(QFile::exists(f1));
|
ASSERT_FALSE(QFile::exists(f1));
|
||||||
|
ASSERT_TRUE(testee.error().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UpdateInstaller, FailInstallNoSource)
|
#ifdef Q_OS_LINUX
|
||||||
{
|
|
||||||
ASSERT_TRUE(removeFile(f1));
|
|
||||||
|
|
||||||
UserActions actions{{Action::Install, toFile(f1, t1)}};
|
|
||||||
Installer testee(actions);
|
|
||||||
ASSERT_FALSE(testee.commit());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UpdateInstaller, FailInstallNoWritable)
|
TEST(UpdateInstaller, FailInstallNoWritable)
|
||||||
{
|
{
|
||||||
const auto t1 = "/foo.txt";
|
const auto t1 = "/foo.txt";
|
||||||
QFile f(t1);
|
QFile f(t1);
|
||||||
ASSERT_FALSE(f.isWritable());
|
ASSERT_FALSE(f.isWritable());
|
||||||
|
|
||||||
UserActions actions{{Action::Install, toFile(f1, t1)}};
|
Installer testee;
|
||||||
Installer testee(actions);
|
testee.install(toFile(t1), data);
|
||||||
ASSERT_FALSE(testee.commit());
|
ASSERT_FALSE(testee.error().isEmpty());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(UpdateInstaller, FailRemove)
|
TEST(UpdateInstaller, FailRemove)
|
||||||
{
|
{
|
||||||
@ -103,9 +87,9 @@ TEST(UpdateInstaller, FailRemove)
|
|||||||
return;
|
return;
|
||||||
ASSERT_FALSE(QFile::copy(f1, f1 + "1")); // non writable
|
ASSERT_FALSE(QFile::copy(f1, f1 + "1")); // non writable
|
||||||
|
|
||||||
UserActions actions{{Action::Remove, toFile(f1, f1)}};
|
Installer testee;
|
||||||
Installer testee(actions);
|
testee.remove(toFile(f1));
|
||||||
ASSERT_FALSE(testee.commit());
|
ASSERT_FALSE(testee.error().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UpdateModel, ParseFail)
|
TEST(UpdateModel, ParseFail)
|
||||||
@ -113,7 +97,8 @@ TEST(UpdateModel, ParseFail)
|
|||||||
const auto updates = R"({
|
const auto updates = R"({
|
||||||
})";
|
})";
|
||||||
|
|
||||||
Model testee;
|
Updater updater({});
|
||||||
|
Model testee(updater);
|
||||||
const auto error = testee.parse(updates);
|
const auto error = testee.parse(updates);
|
||||||
|
|
||||||
ASSERT_FALSE(error.isEmpty());
|
ASSERT_FALSE(error.isEmpty());
|
||||||
@ -134,7 +119,8 @@ TEST(UpdateModel, Parse)
|
|||||||
}
|
}
|
||||||
})";
|
})";
|
||||||
|
|
||||||
Model testee;
|
Updater updater({});
|
||||||
|
Model testee(updater);
|
||||||
const auto error = testee.parse(updates);
|
const auto error = testee.parse(updates);
|
||||||
|
|
||||||
ASSERT_TRUE(error.isEmpty());
|
ASSERT_TRUE(error.isEmpty());
|
||||||
@ -146,40 +132,3 @@ TEST(UpdateModel, Parse)
|
|||||||
comp1.sibling(comp1.row(), int(Model::Column::Name)).data().toString();
|
comp1.sibling(comp1.row(), int(Model::Column::Name)).data().toString();
|
||||||
ASSERT_EQ("comp1", comp1Name);
|
ASSERT_EQ("comp1", comp1Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UpdateLoader, InstallFile)
|
|
||||||
{
|
|
||||||
const auto uf = "updates.json";
|
|
||||||
const auto url = QUrl::fromLocalFile(QFileInfo(f1).absoluteFilePath());
|
|
||||||
const auto updates = QString(R"({
|
|
||||||
"version":1,
|
|
||||||
"comp1":{"files":[{"url":"%1", "path":"./%2", "md5":"1"}]}
|
|
||||||
})")
|
|
||||||
.arg(url.toString(), t1);
|
|
||||||
ASSERT_TRUE(writeFile(uf, updates.toUtf8()));
|
|
||||||
ASSERT_TRUE(writeFile(f1, updates.toUtf8()));
|
|
||||||
ASSERT_TRUE(removeFile(t1));
|
|
||||||
|
|
||||||
Loader testee({QUrl::fromLocalFile(QFileInfo(uf).absoluteFilePath())});
|
|
||||||
testee.checkForUpdates();
|
|
||||||
QCoreApplication::processEvents();
|
|
||||||
|
|
||||||
auto model = testee.model();
|
|
||||||
ASSERT_NE(nullptr, model);
|
|
||||||
ASSERT_EQ(1, model->rowCount({}));
|
|
||||||
|
|
||||||
const auto comp1 = model->index(0, int(Model::Column::Action), {});
|
|
||||||
model->setData(comp1, int(Action::Install), Qt::EditRole);
|
|
||||||
|
|
||||||
QSignalSpy okSpy(&testee, &Loader::updated);
|
|
||||||
QSignalSpy errorSpy(&testee, &Loader::error);
|
|
||||||
|
|
||||||
testee.applyUserActions();
|
|
||||||
|
|
||||||
QCoreApplication::processEvents();
|
|
||||||
ASSERT_EQ(1, okSpy.count());
|
|
||||||
ASSERT_EQ(0, errorSpy.count());
|
|
||||||
|
|
||||||
ASSERT_TRUE(QFile::exists(f1));
|
|
||||||
ASSERT_EQ(updates, readFile(t1));
|
|
||||||
}
|
|
||||||
|
|||||||
@ -20,14 +20,25 @@ function checkFinished() {
|
|||||||
|
|
||||||
function translate(text, from, to) {
|
function translate(text, from, to) {
|
||||||
console.log('start translate', text, from, to)
|
console.log('start translate', text, from, to)
|
||||||
|
|
||||||
|
if (text.trim().length == 0) {
|
||||||
|
proxy.setTranslated('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
active = true;
|
active = true;
|
||||||
|
|
||||||
let langs = from + '/' + to;
|
let langs = from + '/' + to;
|
||||||
if (window.location.href.indexOf('//fanyi.baidu.com/') !== -1
|
if (window.location.href.indexOf('//fanyi.baidu.com/') !== -1
|
||||||
&& window.location.href.indexOf(langs) !== -1) {
|
&& window.location.href.indexOf(langs) !== -1) {
|
||||||
document.querySelector('textarea#baidu_translate_input').value = text;
|
var input = document.querySelector('textarea#baidu_translate_input');
|
||||||
document.querySelector('textarea#baidu_translate_input').dispatchEvent(
|
if (input.value == text) {
|
||||||
new Event("input", { bubbles: true, cancelable: true }));
|
console.log('using cached result');
|
||||||
|
lastText = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input.value = text;
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,13 +20,24 @@ function checkFinished() {
|
|||||||
|
|
||||||
function translate(text, from, to) {
|
function translate(text, from, to) {
|
||||||
console.log('start translate', text, from, to)
|
console.log('start translate', text, from, to)
|
||||||
|
|
||||||
|
if (text.trim().length == 0) {
|
||||||
|
proxy.setTranslated('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
active = true;
|
active = true;
|
||||||
|
|
||||||
if (window.location.href.indexOf('bing.com/translator') !== -1
|
if (window.location.href.indexOf('bing.com/translator') !== -1
|
||||||
&& window.location.href.indexOf('&to=' + to + '&') !== -1) {
|
&& window.location.href.indexOf('&to=' + to + '&') !== -1) {
|
||||||
document.querySelector('textarea#tta_input_ta').value = text;
|
var input = document.querySelector('textarea#tta_input_ta');
|
||||||
document.querySelector('textarea#tta_input_ta').dispatchEvent(
|
if (input.value == text) {
|
||||||
new Event("input", { bubbles: true, cancelable: true }));
|
console.log('using cached result');
|
||||||
|
lastText = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input.value = text;
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,16 @@ var active = window.location.href !== "about:blank";
|
|||||||
function checkFinished() {
|
function checkFinished() {
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
|
|
||||||
let area = document.querySelector('textarea[dl-test=translator-target-input]');
|
let area = document.querySelector('div#target-dummydiv');
|
||||||
let text = area ? area.value : '';
|
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 === '')
|
if (text === lastText || text === '')
|
||||||
return;
|
return;
|
||||||
@ -18,10 +26,18 @@ function checkFinished() {
|
|||||||
|
|
||||||
function translate(text, from, to) {
|
function translate(text, from, to) {
|
||||||
console.log('start translate', text, from, to)
|
console.log('start translate', text, from, to)
|
||||||
|
|
||||||
|
if (text.trim().length == 0) {
|
||||||
|
proxy.setTranslated('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
from = from == 'zh-CN' ? 'zh' : from;
|
from = from == 'zh-CN' ? 'zh' : from;
|
||||||
to = to == 'zh-CN' ? 'zh' : to;
|
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) {
|
if (supported.indexOf(from) == -1) {
|
||||||
proxy.setFailed('Source language not supported');
|
proxy.setFailed('Source language not supported');
|
||||||
return;
|
return;
|
||||||
@ -33,16 +49,32 @@ function translate(text, from, to) {
|
|||||||
|
|
||||||
active = true;
|
active = true;
|
||||||
|
|
||||||
|
var singleLineText = text.replace(/(?:\r\n|\r|\n)/g, ' ');
|
||||||
|
|
||||||
let langs = from + '/' + to + '/';
|
let langs = from + '/' + to + '/';
|
||||||
if (window.location.href.indexOf('www.deepl.com/translator') !== -1
|
if (window.location.href.indexOf('www.deepl.com/translator') !== -1
|
||||||
&& window.location.href.indexOf(langs) !== -1) {
|
&& window.location.href.indexOf(langs) !== -1) {
|
||||||
document.querySelector('textarea[dl-test=translator-source-input]').value = text;
|
|
||||||
document.querySelector('textarea[dl-test=translator-source-input]').dispatchEvent(
|
var input = document.querySelector('d-textarea[dl-test=translator-source-input] p');
|
||||||
new Event("input", { bubbles: true, cancelable: true }));
|
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.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;
|
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);
|
console.log("setting url", url);
|
||||||
window.location = url;
|
window.location = url;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ var active = window.location.href !== "about:blank";
|
|||||||
function checkFinished() {
|
function checkFinished() {
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
|
|
||||||
let spans = [].slice.call(document.querySelectorAll('span.translation > span, #result_box > span'));
|
let spans = [].slice.call(document.querySelectorAll('span.HwtZe > span > span'));
|
||||||
let text = spans.reduce(function (res, i) {
|
let text = spans.reduce(function (res, i) {
|
||||||
return res + ' ' + i.innerText;
|
return res + ' ' + i.innerText;
|
||||||
}, '');
|
}, '');
|
||||||
@ -21,14 +21,26 @@ function checkFinished() {
|
|||||||
|
|
||||||
function translate(text, from, to) {
|
function translate(text, from, to) {
|
||||||
console.log('start translate', text, from, to)
|
console.log('start translate', text, from, to)
|
||||||
|
|
||||||
|
if (text.trim().length == 0) {
|
||||||
|
proxy.setTranslated('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
active = true;
|
active = true;
|
||||||
|
|
||||||
if (window.location.href.indexOf('//translate.google') !== -1
|
if (window.location.href.indexOf('//translate.google') !== -1
|
||||||
&& window.location.href.indexOf('&tl=' + to + '&') !== -1) {
|
&& window.location.href.indexOf('&tl=' + to + '&') !== -1) {
|
||||||
document.querySelector('textarea#source').value = text;
|
var input = document.querySelector('textarea.er8xn');
|
||||||
|
if (input.value == text) {
|
||||||
|
console.log('using cached result');
|
||||||
|
lastText = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input.value = text;
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// let url = 'https://translate.google.com/#auto/' + to + '/' + text;
|
|
||||||
let url = 'https://translate.google.com/#view=home&op=translate&sl=auto&tl=' + to + '&text=' + encodeURIComponent(text);
|
let url = 'https://translate.google.com/#view=home&op=translate&sl=auto&tl=' + to + '&text=' + encodeURIComponent(text);
|
||||||
console.log("setting url", url);
|
console.log("setting url", url);
|
||||||
window.location = url;
|
window.location = url;
|
||||||
|
|||||||
@ -31,6 +31,12 @@ function checkFinished() {
|
|||||||
|
|
||||||
function translate(text, from, to) {
|
function translate(text, from, to) {
|
||||||
console.log('start translate', text, from, to)
|
console.log('start translate', text, from, to)
|
||||||
|
|
||||||
|
if (text.trim().length == 0) {
|
||||||
|
proxy.setTranslated('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let supported = ['ko', 'ru', 'en', 'fr', 'pt', 'th', 'ja',
|
let supported = ['ko', 'ru', 'en', 'fr', 'pt', 'th', 'ja',
|
||||||
'zh-CN', 'zh-TW', 'de', 'it', 'id', 'es', 'vi', 'hi'];
|
'zh-CN', 'zh-TW', 'de', 'it', 'id', 'es', 'vi', 'hi'];
|
||||||
|
|
||||||
@ -48,9 +54,14 @@ function translate(text, from, to) {
|
|||||||
let langs = '?sk=auto&tk=' + to + '&';
|
let langs = '?sk=auto&tk=' + to + '&';
|
||||||
if (window.location.href.indexOf('//papago.naver.com/') !== -1
|
if (window.location.href.indexOf('//papago.naver.com/') !== -1
|
||||||
&& window.location.href.indexOf(langs) !== -1) {
|
&& window.location.href.indexOf(langs) !== -1) {
|
||||||
document.querySelector('textarea#txtSource').value = text
|
var input = document.querySelector('textarea#txtSource');
|
||||||
document.querySelector('textarea#txtSource').dispatchEvent(
|
if (input.value == text) {
|
||||||
new Event("input", { bubbles: true, cancelable: true }));
|
console.log('using cached result');
|
||||||
|
lastText = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input.value = text;
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,9 @@ var active = window.location.href !== "about:blank";
|
|||||||
function checkFinished() {
|
function checkFinished() {
|
||||||
if (!active) return;
|
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) {
|
let text = spans.reduce(function (res, i) {
|
||||||
return res + ' ' + i.innerText;
|
return res + i.innerText;
|
||||||
}, '').trim();
|
}, '').trim();
|
||||||
|
|
||||||
if (text === lastText || text === '')
|
if (text === lastText || text === '')
|
||||||
@ -20,10 +20,21 @@ function checkFinished() {
|
|||||||
|
|
||||||
function translate(text, from, to) {
|
function translate(text, from, to) {
|
||||||
console.log('start translate', text, from, to)
|
console.log('start translate', text, from, to)
|
||||||
|
|
||||||
|
if (text.trim().length == 0) {
|
||||||
|
proxy.setTranslated('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
active = true;
|
active = true;
|
||||||
|
|
||||||
let langs = 'lang=' + from + '-' + to;
|
let langs = 'lang=' + from + '-' + to;
|
||||||
let url = 'https://translate.yandex.ru/?' + langs + '&text=' + encodeURIComponent(text);
|
let url = 'https://translate.yandex.ru/?' + langs + '&text=' + encodeURIComponent(text);
|
||||||
|
if (window.location.href == url) {
|
||||||
|
console.log('using cached result');
|
||||||
|
lastText = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.log("setting url", url);
|
console.log("setting url", url);
|
||||||
window.location = url;
|
window.location = url;
|
||||||
}
|
}
|
||||||
|
|||||||
473
updates.json
473
updates.json
@ -2,384 +2,384 @@
|
|||||||
"version":1
|
"version":1
|
||||||
|
|
||||||
,"app":{
|
,"app":{
|
||||||
"win32":{"version":"3.0.1", "host":"win32", "files":[{"path":"$appdir$/screen-translator.exe", "date":"2020-05-09T06:00:00+03:00"}]}
|
"win32":{"version":"3.3.0", "host":"win32", "files":[{"path":"$appdir$/screen-translator.exe", "md5":"414c74c4594e0b90aff3cd86a73f96dd"}]}
|
||||||
,"win64":{"version":"3.0.1", "host":"win64", "files":[{"path":"$appdir$/screen-translator.exe", "date":"2020-05-09T06:00:00+03:00"}]}
|
,"win64":{"version":"3.3.0", "host":"win64", "files":[{"path":"$appdir$/screen-translator.exe", "md5":"3f2c3c27364f25c239ea63243a8910a3"}]}
|
||||||
,"linux":{"version":"3.0.1", "host":"linux", "files":[{"path":"$appdir$/screen-translator", "date":"2020-05-09T06:00:00+03:00"}]}
|
,"linux":{"version":"3.3.0", "host":"linux", "files":[{"path":"$appdir$/screen-translator", "md5":"a091be0443fd128a02b01b313e0270bc"}]}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
,"recognizers": {
|
,"recognizers": {
|
||||||
"Afrikaans":{"files":[
|
"Afrikaans":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/afr.traineddata","https://translator.gres.biz/resources/tessdata_best/afr.traineddata.zip"], "path":"$tessdata$/afr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12800552}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/afr.traineddata","https://translator.gres.biz/resources/tessdata_best/afr.traineddata.zip"], "path":"$tessdata$/afr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12800552}
|
||||||
]}
|
]}
|
||||||
, "Amharic":{"files":[
|
, "Amharic":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/amh.traineddata","https://translator.gres.biz/resources/tessdata_best/amh.traineddata.zip"], "path":"$tessdata$/amh.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8389639}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/amh.traineddata","https://translator.gres.biz/resources/tessdata_best/amh.traineddata.zip"], "path":"$tessdata$/amh.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8389639}
|
||||||
]}
|
]}
|
||||||
, "Arabic":{"files":[
|
, "Arabic":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ara.traineddata","https://translator.gres.biz/resources/tessdata_best/ara.traineddata.zip"], "path":"$tessdata$/ara.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12603724}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ara.traineddata","https://translator.gres.biz/resources/tessdata_best/ara.traineddata.zip"], "path":"$tessdata$/ara.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12603724}
|
||||||
]}
|
]}
|
||||||
, "Assamese":{"files":[
|
, "Assamese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/asm.traineddata","https://translator.gres.biz/resources/tessdata_best/asm.traineddata.zip"], "path":"$tessdata$/asm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11315350}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/asm.traineddata","https://translator.gres.biz/resources/tessdata_best/asm.traineddata.zip"], "path":"$tessdata$/asm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11315350}
|
||||||
]}
|
]}
|
||||||
, "Azerbaijani":{"files":[
|
, "Azerbaijani":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/aze.traineddata","https://translator.gres.biz/resources/tessdata_best/aze.traineddata.zip"], "path":"$tessdata$/aze.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6281404}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/aze.traineddata","https://translator.gres.biz/resources/tessdata_best/aze.traineddata.zip"], "path":"$tessdata$/aze.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6281404}
|
||||||
]}
|
]}
|
||||||
, "aze_cyrl":{"files":[
|
, "aze_cyrl":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/aze_cyrl.traineddata","https://translator.gres.biz/resources/tessdata_best/aze_cyrl.traineddata.zip"], "path":"$tessdata$/aze_cyrl.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4700277}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/aze_cyrl.traineddata","https://translator.gres.biz/resources/tessdata_best/aze_cyrl.traineddata.zip"], "path":"$tessdata$/aze_cyrl.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4700277}
|
||||||
]}
|
]}
|
||||||
, "Belarusian":{"files":[
|
, "Belarusian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/bel.traineddata","https://translator.gres.biz/resources/tessdata_best/bel.traineddata.zip"], "path":"$tessdata$/bel.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10870278}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/bel.traineddata","https://translator.gres.biz/resources/tessdata_best/bel.traineddata.zip"], "path":"$tessdata$/bel.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10870278}
|
||||||
]}
|
]}
|
||||||
, "Bengali":{"files":[
|
, "Bengali":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ben.traineddata","https://translator.gres.biz/resources/tessdata_best/ben.traineddata.zip"], "path":"$tessdata$/ben.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11045427}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ben.traineddata","https://translator.gres.biz/resources/tessdata_best/ben.traineddata.zip"], "path":"$tessdata$/ben.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11045427}
|
||||||
]}
|
]}
|
||||||
, "Tibetan":{"files":[
|
, "Tibetan":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/bod.traineddata","https://translator.gres.biz/resources/tessdata_best/bod.traineddata.zip"], "path":"$tessdata$/bod.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8623846}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/bod.traineddata","https://translator.gres.biz/resources/tessdata_best/bod.traineddata.zip"], "path":"$tessdata$/bod.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8623846}
|
||||||
]}
|
]}
|
||||||
, "Bosnian":{"files":[
|
, "Bosnian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/bos.traineddata","https://translator.gres.biz/resources/tessdata_best/bos.traineddata.zip"], "path":"$tessdata$/bos.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5264248}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/bos.traineddata","https://translator.gres.biz/resources/tessdata_best/bos.traineddata.zip"], "path":"$tessdata$/bos.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5264248}
|
||||||
]}
|
]}
|
||||||
, "Breton":{"files":[
|
, "Breton":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/bre.traineddata","https://translator.gres.biz/resources/tessdata_best/bre.traineddata.zip"], "path":"$tessdata$/bre.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15640760}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/bre.traineddata","https://translator.gres.biz/resources/tessdata_best/bre.traineddata.zip"], "path":"$tessdata$/bre.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15640760}
|
||||||
]}
|
]}
|
||||||
, "Bulgarian":{"files":[
|
, "Bulgarian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/bul.traineddata","https://translator.gres.biz/resources/tessdata_best/bul.traineddata.zip"], "path":"$tessdata$/bul.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8844613}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/bul.traineddata","https://translator.gres.biz/resources/tessdata_best/bul.traineddata.zip"], "path":"$tessdata$/bul.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8844613}
|
||||||
]}
|
]}
|
||||||
, "Catalan":{"files":[
|
, "Catalan":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/cat.traineddata","https://translator.gres.biz/resources/tessdata_best/cat.traineddata.zip"], "path":"$tessdata$/cat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3802329}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/cat.traineddata","https://translator.gres.biz/resources/tessdata_best/cat.traineddata.zip"], "path":"$tessdata$/cat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3802329}
|
||||||
]}
|
]}
|
||||||
, "Cebuano":{"files":[
|
, "Cebuano":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ceb.traineddata","https://translator.gres.biz/resources/tessdata_best/ceb.traineddata.zip"], "path":"$tessdata$/ceb.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3452674}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ceb.traineddata","https://translator.gres.biz/resources/tessdata_best/ceb.traineddata.zip"], "path":"$tessdata$/ceb.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3452674}
|
||||||
]}
|
]}
|
||||||
, "Czech":{"files":[
|
, "Czech":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ces.traineddata","https://translator.gres.biz/resources/tessdata_best/ces.traineddata.zip"], "path":"$tessdata$/ces.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10918912}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ces.traineddata","https://translator.gres.biz/resources/tessdata_best/ces.traineddata.zip"], "path":"$tessdata$/ces.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10918912}
|
||||||
]}
|
]}
|
||||||
, "Chinese (Simplified)":{"files":[
|
, "Chinese (Simplified)":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/chi_sim.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_sim.traineddata.zip"], "path":"$tessdata$/chi_sim.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13077423}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/chi_sim.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_sim.traineddata.zip"], "path":"$tessdata$/chi_sim.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13077423}
|
||||||
]}
|
]}
|
||||||
, "chi_sim_vert":{"files":[
|
, "Chinese (Simplified) vertical":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/chi_sim_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_sim_vert.traineddata.zip"], "path":"$tessdata$/chi_sim_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13077507}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/chi_sim_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_sim_vert.traineddata.zip"], "path":"$tessdata$/chi_sim_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13077507}
|
||||||
]}
|
]}
|
||||||
, "Chinese (Traditional)":{"files":[
|
, "Chinese (Traditional)":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/chi_tra.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_tra.traineddata.zip"], "path":"$tessdata$/chi_tra.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12985735}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/chi_tra.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_tra.traineddata.zip"], "path":"$tessdata$/chi_tra.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12985735}
|
||||||
]}
|
]}
|
||||||
, "chi_tra_vert":{"files":[
|
, "Chinese (Traditional) vertical":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/chi_tra_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_tra_vert.traineddata.zip"], "path":"$tessdata$/chi_tra_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12985521}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/chi_tra_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/chi_tra_vert.traineddata.zip"], "path":"$tessdata$/chi_tra_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12985521}
|
||||||
]}
|
]}
|
||||||
, "Cherokee":{"files":[
|
, "Cherokee":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/chr.traineddata","https://translator.gres.biz/resources/tessdata_best/chr.traineddata.zip"], "path":"$tessdata$/chr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":2258703}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/chr.traineddata","https://translator.gres.biz/resources/tessdata_best/chr.traineddata.zip"], "path":"$tessdata$/chr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":2258703}
|
||||||
]}
|
]}
|
||||||
, "Corsican":{"files":[
|
, "Corsican":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/cos.traineddata","https://translator.gres.biz/resources/tessdata_best/cos.traineddata.zip"], "path":"$tessdata$/cos.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8830216}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/cos.traineddata","https://translator.gres.biz/resources/tessdata_best/cos.traineddata.zip"], "path":"$tessdata$/cos.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8830216}
|
||||||
]}
|
]}
|
||||||
, "Welsh":{"files":[
|
, "Welsh":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/cym.traineddata","https://translator.gres.biz/resources/tessdata_best/cym.traineddata.zip"], "path":"$tessdata$/cym.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8750784}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/cym.traineddata","https://translator.gres.biz/resources/tessdata_best/cym.traineddata.zip"], "path":"$tessdata$/cym.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8750784}
|
||||||
]}
|
]}
|
||||||
, "Danish":{"files":[
|
, "Danish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/dan.traineddata","https://translator.gres.biz/resources/tessdata_best/dan.traineddata.zip"], "path":"$tessdata$/dan.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9758142}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/dan.traineddata","https://translator.gres.biz/resources/tessdata_best/dan.traineddata.zip"], "path":"$tessdata$/dan.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9758142}
|
||||||
]}
|
]}
|
||||||
, "German":{"files":[
|
, "German":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/deu.traineddata","https://translator.gres.biz/resources/tessdata_best/deu.traineddata.zip"], "path":"$tessdata$/deu.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8628461}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/deu.traineddata","https://translator.gres.biz/resources/tessdata_best/deu.traineddata.zip"], "path":"$tessdata$/deu.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8628461}
|
||||||
]}
|
]}
|
||||||
, "Divehi, Dhivehi, Maldivian":{"files":[
|
, "Divehi, Dhivehi, Maldivian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/div.traineddata","https://translator.gres.biz/resources/tessdata_best/div.traineddata.zip"], "path":"$tessdata$/div.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4574116}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/div.traineddata","https://translator.gres.biz/resources/tessdata_best/div.traineddata.zip"], "path":"$tessdata$/div.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4574116}
|
||||||
]}
|
]}
|
||||||
, "Dzongkha":{"files":[
|
, "Dzongkha":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/dzo.traineddata","https://translator.gres.biz/resources/tessdata_best/dzo.traineddata.zip"], "path":"$tessdata$/dzo.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3243805}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/dzo.traineddata","https://translator.gres.biz/resources/tessdata_best/dzo.traineddata.zip"], "path":"$tessdata$/dzo.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3243805}
|
||||||
]}
|
]}
|
||||||
, "Greek":{"files":[
|
, "Greek":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ell.traineddata","https://translator.gres.biz/resources/tessdata_best/ell.traineddata.zip"], "path":"$tessdata$/ell.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8945021}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ell.traineddata","https://translator.gres.biz/resources/tessdata_best/ell.traineddata.zip"], "path":"$tessdata$/ell.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8945021}
|
||||||
]}
|
]}
|
||||||
, "English":{"files":[
|
, "English":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/eng.traineddata","https://translator.gres.biz/resources/tessdata_best/eng.traineddata.zip"], "path":"$tessdata$/eng.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15400601}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/eng.traineddata","https://translator.gres.biz/resources/tessdata_best/eng.traineddata.zip"], "path":"$tessdata$/eng.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15400601}
|
||||||
]}
|
]}
|
||||||
, "English, Middle (1100-1500)":{"files":[
|
, "English, Middle (1100-1500)":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/enm.traineddata","https://translator.gres.biz/resources/tessdata_best/enm.traineddata.zip"], "path":"$tessdata$/enm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13281564}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/enm.traineddata","https://translator.gres.biz/resources/tessdata_best/enm.traineddata.zip"], "path":"$tessdata$/enm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13281564}
|
||||||
]}
|
]}
|
||||||
, "Esperanto":{"files":[
|
, "Esperanto":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/epo.traineddata","https://translator.gres.biz/resources/tessdata_best/epo.traineddata.zip"], "path":"$tessdata$/epo.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7402169}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/epo.traineddata","https://translator.gres.biz/resources/tessdata_best/epo.traineddata.zip"], "path":"$tessdata$/epo.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7402169}
|
||||||
]}
|
]}
|
||||||
, "Estonian":{"files":[
|
, "Estonian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/est.traineddata","https://translator.gres.biz/resources/tessdata_best/est.traineddata.zip"], "path":"$tessdata$/est.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15833749}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/est.traineddata","https://translator.gres.biz/resources/tessdata_best/est.traineddata.zip"], "path":"$tessdata$/est.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15833749}
|
||||||
]}
|
]}
|
||||||
, "Basque":{"files":[
|
, "Basque":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/eus.traineddata","https://translator.gres.biz/resources/tessdata_best/eus.traineddata.zip"], "path":"$tessdata$/eus.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7933869}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/eus.traineddata","https://translator.gres.biz/resources/tessdata_best/eus.traineddata.zip"], "path":"$tessdata$/eus.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7933869}
|
||||||
]}
|
]}
|
||||||
, "Faroese":{"files":[
|
, "Faroese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/fao.traineddata","https://translator.gres.biz/resources/tessdata_best/fao.traineddata.zip"], "path":"$tessdata$/fao.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10030003}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/fao.traineddata","https://translator.gres.biz/resources/tessdata_best/fao.traineddata.zip"], "path":"$tessdata$/fao.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10030003}
|
||||||
]}
|
]}
|
||||||
, "Persian":{"files":[
|
, "Persian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/fas.traineddata","https://translator.gres.biz/resources/tessdata_best/fas.traineddata.zip"], "path":"$tessdata$/fas.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3325955}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/fas.traineddata","https://translator.gres.biz/resources/tessdata_best/fas.traineddata.zip"], "path":"$tessdata$/fas.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3325955}
|
||||||
]}
|
]}
|
||||||
, "Filipino":{"files":[
|
, "Filipino":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/fil.traineddata","https://translator.gres.biz/resources/tessdata_best/fil.traineddata.zip"], "path":"$tessdata$/fil.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8978743}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/fil.traineddata","https://translator.gres.biz/resources/tessdata_best/fil.traineddata.zip"], "path":"$tessdata$/fil.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8978743}
|
||||||
]}
|
]}
|
||||||
, "Finnish":{"files":[
|
, "Finnish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/fin.traineddata","https://translator.gres.biz/resources/tessdata_best/fin.traineddata.zip"], "path":"$tessdata$/fin.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14369979}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/fin.traineddata","https://translator.gres.biz/resources/tessdata_best/fin.traineddata.zip"], "path":"$tessdata$/fin.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14369979}
|
||||||
]}
|
]}
|
||||||
, "French":{"files":[
|
, "French":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/fra.traineddata","https://translator.gres.biz/resources/tessdata_best/fra.traineddata.zip"], "path":"$tessdata$/fra.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3972885}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/fra.traineddata","https://translator.gres.biz/resources/tessdata_best/fra.traineddata.zip"], "path":"$tessdata$/fra.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3972885}
|
||||||
]}
|
]}
|
||||||
, "frk":{"files":[
|
, "frk":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/frk.traineddata","https://translator.gres.biz/resources/tessdata_best/frk.traineddata.zip"], "path":"$tessdata$/frk.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12938047}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/frk.traineddata","https://translator.gres.biz/resources/tessdata_best/frk.traineddata.zip"], "path":"$tessdata$/frk.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12938047}
|
||||||
]}
|
]}
|
||||||
, "French, Middle (ca.1400-1600)":{"files":[
|
, "French, Middle (ca.1400-1600)":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/frm.traineddata","https://translator.gres.biz/resources/tessdata_best/frm.traineddata.zip"], "path":"$tessdata$/frm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4043005}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/frm.traineddata","https://translator.gres.biz/resources/tessdata_best/frm.traineddata.zip"], "path":"$tessdata$/frm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4043005}
|
||||||
]}
|
]}
|
||||||
, "Western Frisian":{"files":[
|
, "Western Frisian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/fry.traineddata","https://translator.gres.biz/resources/tessdata_best/fry.traineddata.zip"], "path":"$tessdata$/fry.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8442509}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/fry.traineddata","https://translator.gres.biz/resources/tessdata_best/fry.traineddata.zip"], "path":"$tessdata$/fry.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8442509}
|
||||||
]}
|
]}
|
||||||
, "Gaelic":{"files":[
|
, "Gaelic":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/gla.traineddata","https://translator.gres.biz/resources/tessdata_best/gla.traineddata.zip"], "path":"$tessdata$/gla.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9599424}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/gla.traineddata","https://translator.gres.biz/resources/tessdata_best/gla.traineddata.zip"], "path":"$tessdata$/gla.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9599424}
|
||||||
]}
|
]}
|
||||||
, "Irish":{"files":[
|
, "Irish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/gle.traineddata","https://translator.gres.biz/resources/tessdata_best/gle.traineddata.zip"], "path":"$tessdata$/gle.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3942458}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/gle.traineddata","https://translator.gres.biz/resources/tessdata_best/gle.traineddata.zip"], "path":"$tessdata$/gle.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3942458}
|
||||||
]}
|
]}
|
||||||
, "Galician":{"files":[
|
, "Galician":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/glg.traineddata","https://translator.gres.biz/resources/tessdata_best/glg.traineddata.zip"], "path":"$tessdata$/glg.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12709487}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/glg.traineddata","https://translator.gres.biz/resources/tessdata_best/glg.traineddata.zip"], "path":"$tessdata$/glg.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12709487}
|
||||||
]}
|
]}
|
||||||
, "Greek, Ancient (to 1453)":{"files":[
|
, "Greek, Ancient (to 1453)":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/grc.traineddata","https://translator.gres.biz/resources/tessdata_best/grc.traineddata.zip"], "path":"$tessdata$/grc.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5168122}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/grc.traineddata","https://translator.gres.biz/resources/tessdata_best/grc.traineddata.zip"], "path":"$tessdata$/grc.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5168122}
|
||||||
]}
|
]}
|
||||||
, "Gujarati":{"files":[
|
, "Gujarati":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/guj.traineddata","https://translator.gres.biz/resources/tessdata_best/guj.traineddata.zip"], "path":"$tessdata$/guj.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8515761}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/guj.traineddata","https://translator.gres.biz/resources/tessdata_best/guj.traineddata.zip"], "path":"$tessdata$/guj.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8515761}
|
||||||
]}
|
]}
|
||||||
, "Haitian":{"files":[
|
, "Haitian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/hat.traineddata","https://translator.gres.biz/resources/tessdata_best/hat.traineddata.zip"], "path":"$tessdata$/hat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12128251}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/hat.traineddata","https://translator.gres.biz/resources/tessdata_best/hat.traineddata.zip"], "path":"$tessdata$/hat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12128251}
|
||||||
]}
|
]}
|
||||||
, "Hebrew":{"files":[
|
, "Hebrew":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/heb.traineddata","https://translator.gres.biz/resources/tessdata_best/heb.traineddata.zip"], "path":"$tessdata$/heb.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3704077}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/heb.traineddata","https://translator.gres.biz/resources/tessdata_best/heb.traineddata.zip"], "path":"$tessdata$/heb.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3704077}
|
||||||
]}
|
]}
|
||||||
, "Hindi":{"files":[
|
, "Hindi":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/hin.traineddata","https://translator.gres.biz/resources/tessdata_best/hin.traineddata.zip"], "path":"$tessdata$/hin.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11895564}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/hin.traineddata","https://translator.gres.biz/resources/tessdata_best/hin.traineddata.zip"], "path":"$tessdata$/hin.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11895564}
|
||||||
]}
|
]}
|
||||||
, "Croatian":{"files":[
|
, "Croatian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/hrv.traineddata","https://translator.gres.biz/resources/tessdata_best/hrv.traineddata.zip"], "path":"$tessdata$/hrv.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11195424}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/hrv.traineddata","https://translator.gres.biz/resources/tessdata_best/hrv.traineddata.zip"], "path":"$tessdata$/hrv.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11195424}
|
||||||
]}
|
]}
|
||||||
, "Hungarian":{"files":[
|
, "Hungarian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/hun.traineddata","https://translator.gres.biz/resources/tessdata_best/hun.traineddata.zip"], "path":"$tessdata$/hun.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12350405}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/hun.traineddata","https://translator.gres.biz/resources/tessdata_best/hun.traineddata.zip"], "path":"$tessdata$/hun.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12350405}
|
||||||
]}
|
]}
|
||||||
, "Armenian":{"files":[
|
, "Armenian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/hye.traineddata","https://translator.gres.biz/resources/tessdata_best/hye.traineddata.zip"], "path":"$tessdata$/hye.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6372242}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/hye.traineddata","https://translator.gres.biz/resources/tessdata_best/hye.traineddata.zip"], "path":"$tessdata$/hye.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6372242}
|
||||||
]}
|
]}
|
||||||
, "Inuktitut":{"files":[
|
, "Inuktitut":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/iku.traineddata","https://translator.gres.biz/resources/tessdata_best/iku.traineddata.zip"], "path":"$tessdata$/iku.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6139484}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/iku.traineddata","https://translator.gres.biz/resources/tessdata_best/iku.traineddata.zip"], "path":"$tessdata$/iku.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6139484}
|
||||||
]}
|
]}
|
||||||
, "Indonesian":{"files":[
|
, "Indonesian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ind.traineddata","https://translator.gres.biz/resources/tessdata_best/ind.traineddata.zip"], "path":"$tessdata$/ind.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8253606}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ind.traineddata","https://translator.gres.biz/resources/tessdata_best/ind.traineddata.zip"], "path":"$tessdata$/ind.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8253606}
|
||||||
]}
|
]}
|
||||||
, "Icelandic":{"files":[
|
, "Icelandic":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/isl.traineddata","https://translator.gres.biz/resources/tessdata_best/isl.traineddata.zip"], "path":"$tessdata$/isl.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9486436}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/isl.traineddata","https://translator.gres.biz/resources/tessdata_best/isl.traineddata.zip"], "path":"$tessdata$/isl.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9486436}
|
||||||
]}
|
]}
|
||||||
, "Italian":{"files":[
|
, "Italian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ita.traineddata","https://translator.gres.biz/resources/tessdata_best/ita.traineddata.zip"], "path":"$tessdata$/ita.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8863667}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ita.traineddata","https://translator.gres.biz/resources/tessdata_best/ita.traineddata.zip"], "path":"$tessdata$/ita.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8863667}
|
||||||
]}
|
]}
|
||||||
, "ita_old":{"files":[
|
, "ita_old":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ita_old.traineddata","https://translator.gres.biz/resources/tessdata_best/ita_old.traineddata.zip"], "path":"$tessdata$/ita_old.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9852171}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ita_old.traineddata","https://translator.gres.biz/resources/tessdata_best/ita_old.traineddata.zip"], "path":"$tessdata$/ita_old.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9852171}
|
||||||
]}
|
]}
|
||||||
, "Javanese":{"files":[
|
, "Javanese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/jav.traineddata","https://translator.gres.biz/resources/tessdata_best/jav.traineddata.zip"], "path":"$tessdata$/jav.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8650382}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/jav.traineddata","https://translator.gres.biz/resources/tessdata_best/jav.traineddata.zip"], "path":"$tessdata$/jav.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8650382}
|
||||||
]}
|
]}
|
||||||
, "Japanese":{"files":[
|
, "Japanese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/jpn.traineddata","https://translator.gres.biz/resources/tessdata_best/jpn.traineddata.zip"], "path":"$tessdata$/jpn.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14330109}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/jpn.traineddata","https://translator.gres.biz/resources/tessdata_best/jpn.traineddata.zip"], "path":"$tessdata$/jpn.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14330109}
|
||||||
]}
|
]}
|
||||||
, "jpn_vert":{"files":[
|
, "Japanese vertical":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/jpn_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/jpn_vert.traineddata.zip"], "path":"$tessdata$/jpn_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14330809}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/jpn_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/jpn_vert.traineddata.zip"], "path":"$tessdata$/jpn_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14330809}
|
||||||
]}
|
]}
|
||||||
, "Kannada":{"files":[
|
, "Kannada":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kan.traineddata","https://translator.gres.biz/resources/tessdata_best/kan.traineddata.zip"], "path":"$tessdata$/kan.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10233763}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kan.traineddata","https://translator.gres.biz/resources/tessdata_best/kan.traineddata.zip"], "path":"$tessdata$/kan.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10233763}
|
||||||
]}
|
]}
|
||||||
, "Georgian":{"files":[
|
, "Georgian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kat.traineddata","https://translator.gres.biz/resources/tessdata_best/kat.traineddata.zip"], "path":"$tessdata$/kat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4487336}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kat.traineddata","https://translator.gres.biz/resources/tessdata_best/kat.traineddata.zip"], "path":"$tessdata$/kat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4487336}
|
||||||
]}
|
]}
|
||||||
, "kat_old":{"files":[
|
, "kat_old":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kat_old.traineddata","https://translator.gres.biz/resources/tessdata_best/kat_old.traineddata.zip"], "path":"$tessdata$/kat_old.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3174400}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kat_old.traineddata","https://translator.gres.biz/resources/tessdata_best/kat_old.traineddata.zip"], "path":"$tessdata$/kat_old.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3174400}
|
||||||
]}
|
]}
|
||||||
, "Kazakh":{"files":[
|
, "Kazakh":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kaz.traineddata","https://translator.gres.biz/resources/tessdata_best/kaz.traineddata.zip"], "path":"$tessdata$/kaz.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7528853}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kaz.traineddata","https://translator.gres.biz/resources/tessdata_best/kaz.traineddata.zip"], "path":"$tessdata$/kaz.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7528853}
|
||||||
]}
|
]}
|
||||||
, "Central Khmer":{"files":[
|
, "Central Khmer":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/khm.traineddata","https://translator.gres.biz/resources/tessdata_best/khm.traineddata.zip"], "path":"$tessdata$/khm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8104332}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/khm.traineddata","https://translator.gres.biz/resources/tessdata_best/khm.traineddata.zip"], "path":"$tessdata$/khm.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8104332}
|
||||||
]}
|
]}
|
||||||
, "Kyrgyz":{"files":[
|
, "Kyrgyz":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kir.traineddata","https://translator.gres.biz/resources/tessdata_best/kir.traineddata.zip"], "path":"$tessdata$/kir.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11948344}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kir.traineddata","https://translator.gres.biz/resources/tessdata_best/kir.traineddata.zip"], "path":"$tessdata$/kir.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11948344}
|
||||||
]}
|
]}
|
||||||
, "kmr":{"files":[
|
, "kmr":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kmr.traineddata","https://translator.gres.biz/resources/tessdata_best/kmr.traineddata.zip"], "path":"$tessdata$/kmr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10196464}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kmr.traineddata","https://translator.gres.biz/resources/tessdata_best/kmr.traineddata.zip"], "path":"$tessdata$/kmr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10196464}
|
||||||
]}
|
]}
|
||||||
, "Korean":{"files":[
|
, "Korean":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kor.traineddata","https://translator.gres.biz/resources/tessdata_best/kor.traineddata.zip"], "path":"$tessdata$/kor.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12528128}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kor.traineddata","https://translator.gres.biz/resources/tessdata_best/kor.traineddata.zip"], "path":"$tessdata$/kor.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12528128}
|
||||||
]}
|
]}
|
||||||
, "kor_vert":{"files":[
|
, "Korean vertical":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/kor_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/kor_vert.traineddata.zip"], "path":"$tessdata$/kor_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3964469}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/kor_vert.traineddata","https://translator.gres.biz/resources/tessdata_best/kor_vert.traineddata.zip"], "path":"$tessdata$/kor_vert.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3964469}
|
||||||
]}
|
]}
|
||||||
, "Lao":{"files":[
|
, "Lao":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/lao.traineddata","https://translator.gres.biz/resources/tessdata_best/lao.traineddata.zip"], "path":"$tessdata$/lao.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13532551}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/lao.traineddata","https://translator.gres.biz/resources/tessdata_best/lao.traineddata.zip"], "path":"$tessdata$/lao.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13532551}
|
||||||
]}
|
]}
|
||||||
, "Latin":{"files":[
|
, "Latin":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/lat.traineddata","https://translator.gres.biz/resources/tessdata_best/lat.traineddata.zip"], "path":"$tessdata$/lat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9705145}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/lat.traineddata","https://translator.gres.biz/resources/tessdata_best/lat.traineddata.zip"], "path":"$tessdata$/lat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9705145}
|
||||||
]}
|
]}
|
||||||
, "Latvian":{"files":[
|
, "Latvian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/lav.traineddata","https://translator.gres.biz/resources/tessdata_best/lav.traineddata.zip"], "path":"$tessdata$/lav.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5623473}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/lav.traineddata","https://translator.gres.biz/resources/tessdata_best/lav.traineddata.zip"], "path":"$tessdata$/lav.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5623473}
|
||||||
]}
|
]}
|
||||||
, "Lithuanian":{"files":[
|
, "Lithuanian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/lit.traineddata","https://translator.gres.biz/resources/tessdata_best/lit.traineddata.zip"], "path":"$tessdata$/lit.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10252680}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/lit.traineddata","https://translator.gres.biz/resources/tessdata_best/lit.traineddata.zip"], "path":"$tessdata$/lit.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10252680}
|
||||||
]}
|
]}
|
||||||
, "Luxembourgish":{"files":[
|
, "Luxembourgish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ltz.traineddata","https://translator.gres.biz/resources/tessdata_best/ltz.traineddata.zip"], "path":"$tessdata$/ltz.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12721945}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ltz.traineddata","https://translator.gres.biz/resources/tessdata_best/ltz.traineddata.zip"], "path":"$tessdata$/ltz.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12721945}
|
||||||
]}
|
]}
|
||||||
, "Malayalam":{"files":[
|
, "Malayalam":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/mal.traineddata","https://translator.gres.biz/resources/tessdata_best/mal.traineddata.zip"], "path":"$tessdata$/mal.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12524967}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/mal.traineddata","https://translator.gres.biz/resources/tessdata_best/mal.traineddata.zip"], "path":"$tessdata$/mal.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12524967}
|
||||||
]}
|
]}
|
||||||
, "Marathi":{"files":[
|
, "Marathi":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/mar.traineddata","https://translator.gres.biz/resources/tessdata_best/mar.traineddata.zip"], "path":"$tessdata$/mar.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13437670}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/mar.traineddata","https://translator.gres.biz/resources/tessdata_best/mar.traineddata.zip"], "path":"$tessdata$/mar.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13437670}
|
||||||
]}
|
]}
|
||||||
, "Macedonian":{"files":[
|
, "Macedonian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/mkd.traineddata","https://translator.gres.biz/resources/tessdata_best/mkd.traineddata.zip"], "path":"$tessdata$/mkd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3453054}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/mkd.traineddata","https://translator.gres.biz/resources/tessdata_best/mkd.traineddata.zip"], "path":"$tessdata$/mkd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3453054}
|
||||||
]}
|
]}
|
||||||
, "Maltese":{"files":[
|
, "Maltese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/mlt.traineddata","https://translator.gres.biz/resources/tessdata_best/mlt.traineddata.zip"], "path":"$tessdata$/mlt.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5060029}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/mlt.traineddata","https://translator.gres.biz/resources/tessdata_best/mlt.traineddata.zip"], "path":"$tessdata$/mlt.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5060029}
|
||||||
]}
|
]}
|
||||||
, "Mongolian":{"files":[
|
, "Mongolian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/mon.traineddata","https://translator.gres.biz/resources/tessdata_best/mon.traineddata.zip"], "path":"$tessdata$/mon.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8646663}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/mon.traineddata","https://translator.gres.biz/resources/tessdata_best/mon.traineddata.zip"], "path":"$tessdata$/mon.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8646663}
|
||||||
]}
|
]}
|
||||||
, "Maori":{"files":[
|
, "Maori":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/mri.traineddata","https://translator.gres.biz/resources/tessdata_best/mri.traineddata.zip"], "path":"$tessdata$/mri.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3610177}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/mri.traineddata","https://translator.gres.biz/resources/tessdata_best/mri.traineddata.zip"], "path":"$tessdata$/mri.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3610177}
|
||||||
]}
|
]}
|
||||||
, "Malay":{"files":[
|
, "Malay":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/msa.traineddata","https://translator.gres.biz/resources/tessdata_best/msa.traineddata.zip"], "path":"$tessdata$/msa.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8230552}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/msa.traineddata","https://translator.gres.biz/resources/tessdata_best/msa.traineddata.zip"], "path":"$tessdata$/msa.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8230552}
|
||||||
]}
|
]}
|
||||||
, "Burmese":{"files":[
|
, "Burmese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/mya.traineddata","https://translator.gres.biz/resources/tessdata_best/mya.traineddata.zip"], "path":"$tessdata$/mya.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14971060}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/mya.traineddata","https://translator.gres.biz/resources/tessdata_best/mya.traineddata.zip"], "path":"$tessdata$/mya.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14971060}
|
||||||
]}
|
]}
|
||||||
, "Nepali":{"files":[
|
, "Nepali":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/nep.traineddata","https://translator.gres.biz/resources/tessdata_best/nep.traineddata.zip"], "path":"$tessdata$/nep.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12387399}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/nep.traineddata","https://translator.gres.biz/resources/tessdata_best/nep.traineddata.zip"], "path":"$tessdata$/nep.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12387399}
|
||||||
]}
|
]}
|
||||||
, "Dutch":{"files":[
|
, "Dutch":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/nld.traineddata","https://translator.gres.biz/resources/tessdata_best/nld.traineddata.zip"], "path":"$tessdata$/nld.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8903736}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/nld.traineddata","https://translator.gres.biz/resources/tessdata_best/nld.traineddata.zip"], "path":"$tessdata$/nld.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8903736}
|
||||||
]}
|
]}
|
||||||
, "Norwegian":{"files":[
|
, "Norwegian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/nor.traineddata","https://translator.gres.biz/resources/tessdata_best/nor.traineddata.zip"], "path":"$tessdata$/nor.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14312333}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/nor.traineddata","https://translator.gres.biz/resources/tessdata_best/nor.traineddata.zip"], "path":"$tessdata$/nor.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14312333}
|
||||||
]}
|
]}
|
||||||
, "Occitan":{"files":[
|
, "Occitan":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/oci.traineddata","https://translator.gres.biz/resources/tessdata_best/oci.traineddata.zip"], "path":"$tessdata$/oci.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12917692}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/oci.traineddata","https://translator.gres.biz/resources/tessdata_best/oci.traineddata.zip"], "path":"$tessdata$/oci.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12917692}
|
||||||
]}
|
]}
|
||||||
, "Oriya":{"files":[
|
, "Oriya":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ori.traineddata","https://translator.gres.biz/resources/tessdata_best/ori.traineddata.zip"], "path":"$tessdata$/ori.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8110602}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ori.traineddata","https://translator.gres.biz/resources/tessdata_best/ori.traineddata.zip"], "path":"$tessdata$/ori.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8110602}
|
||||||
]}
|
]}
|
||||||
, "osd":{"files":[
|
, "osd":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/osd.traineddata","https://translator.gres.biz/resources/tessdata_best/osd.traineddata.zip"], "path":"$tessdata$/osd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10562727}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/osd.traineddata","https://translator.gres.biz/resources/tessdata_best/osd.traineddata.zip"], "path":"$tessdata$/osd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10562727}
|
||||||
]}
|
]}
|
||||||
, "Punjabi":{"files":[
|
, "Punjabi":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/pan.traineddata","https://translator.gres.biz/resources/tessdata_best/pan.traineddata.zip"], "path":"$tessdata$/pan.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11893154}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/pan.traineddata","https://translator.gres.biz/resources/tessdata_best/pan.traineddata.zip"], "path":"$tessdata$/pan.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11893154}
|
||||||
]}
|
]}
|
||||||
, "Polish":{"files":[
|
, "Polish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/pol.traineddata","https://translator.gres.biz/resources/tessdata_best/pol.traineddata.zip"], "path":"$tessdata$/pol.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11978867}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/pol.traineddata","https://translator.gres.biz/resources/tessdata_best/pol.traineddata.zip"], "path":"$tessdata$/pol.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11978867}
|
||||||
]}
|
]}
|
||||||
, "Portuguese":{"files":[
|
, "Portuguese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/por.traineddata","https://translator.gres.biz/resources/tessdata_best/por.traineddata.zip"], "path":"$tessdata$/por.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8159939}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/por.traineddata","https://translator.gres.biz/resources/tessdata_best/por.traineddata.zip"], "path":"$tessdata$/por.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8159939}
|
||||||
]}
|
]}
|
||||||
, "Pashto":{"files":[
|
, "Pashto":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/pus.traineddata","https://translator.gres.biz/resources/tessdata_best/pus.traineddata.zip"], "path":"$tessdata$/pus.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11987930}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/pus.traineddata","https://translator.gres.biz/resources/tessdata_best/pus.traineddata.zip"], "path":"$tessdata$/pus.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11987930}
|
||||||
]}
|
]}
|
||||||
, "Quechua":{"files":[
|
, "Quechua":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/que.traineddata","https://translator.gres.biz/resources/tessdata_best/que.traineddata.zip"], "path":"$tessdata$/que.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10774587}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/que.traineddata","https://translator.gres.biz/resources/tessdata_best/que.traineddata.zip"], "path":"$tessdata$/que.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10774587}
|
||||||
]}
|
]}
|
||||||
, "Romanian":{"files":[
|
, "Romanian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ron.traineddata","https://translator.gres.biz/resources/tessdata_best/ron.traineddata.zip"], "path":"$tessdata$/ron.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9595755}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ron.traineddata","https://translator.gres.biz/resources/tessdata_best/ron.traineddata.zip"], "path":"$tessdata$/ron.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9595755}
|
||||||
]}
|
]}
|
||||||
, "Russian":{"files":[
|
, "Russian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/rus.traineddata","https://translator.gres.biz/resources/tessdata_best/rus.traineddata.zip"], "path":"$tessdata$/rus.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15301764}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/rus.traineddata","https://translator.gres.biz/resources/tessdata_best/rus.traineddata.zip"], "path":"$tessdata$/rus.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15301764}
|
||||||
]}
|
]}
|
||||||
, "Sanskrit":{"files":[
|
, "Sanskrit":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/san.traineddata","https://translator.gres.biz/resources/tessdata_best/san.traineddata.zip"], "path":"$tessdata$/san.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15136202}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/san.traineddata","https://translator.gres.biz/resources/tessdata_best/san.traineddata.zip"], "path":"$tessdata$/san.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":15136202}
|
||||||
]}
|
]}
|
||||||
, "Sinhala, Sinhalese":{"files":[
|
, "Sinhala, Sinhalese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/sin.traineddata","https://translator.gres.biz/resources/tessdata_best/sin.traineddata.zip"], "path":"$tessdata$/sin.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8282713}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/sin.traineddata","https://translator.gres.biz/resources/tessdata_best/sin.traineddata.zip"], "path":"$tessdata$/sin.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":8282713}
|
||||||
]}
|
]}
|
||||||
, "Slovak":{"files":[
|
, "Slovak":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/slk.traineddata","https://translator.gres.biz/resources/tessdata_best/slk.traineddata.zip"], "path":"$tessdata$/slk.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11542252}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/slk.traineddata","https://translator.gres.biz/resources/tessdata_best/slk.traineddata.zip"], "path":"$tessdata$/slk.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11542252}
|
||||||
]}
|
]}
|
||||||
, "Slovenian":{"files":[
|
, "Slovenian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/slv.traineddata","https://translator.gres.biz/resources/tessdata_best/slv.traineddata.zip"], "path":"$tessdata$/slv.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5879151}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/slv.traineddata","https://translator.gres.biz/resources/tessdata_best/slv.traineddata.zip"], "path":"$tessdata$/slv.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":5879151}
|
||||||
]}
|
]}
|
||||||
, "Sindhi":{"files":[
|
, "Sindhi":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/snd.traineddata","https://translator.gres.biz/resources/tessdata_best/snd.traineddata.zip"], "path":"$tessdata$/snd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11981538}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/snd.traineddata","https://translator.gres.biz/resources/tessdata_best/snd.traineddata.zip"], "path":"$tessdata$/snd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":11981538}
|
||||||
]}
|
]}
|
||||||
, "Spanish":{"files":[
|
, "Spanish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/spa.traineddata","https://translator.gres.biz/resources/tessdata_best/spa.traineddata.zip"], "path":"$tessdata$/spa.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13570187}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/spa.traineddata","https://translator.gres.biz/resources/tessdata_best/spa.traineddata.zip"], "path":"$tessdata$/spa.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13570187}
|
||||||
]}
|
]}
|
||||||
, "spa_old":{"files":[
|
, "spa_old":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/spa_old.traineddata","https://translator.gres.biz/resources/tessdata_best/spa_old.traineddata.zip"], "path":"$tessdata$/spa_old.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9476925}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/spa_old.traineddata","https://translator.gres.biz/resources/tessdata_best/spa_old.traineddata.zip"], "path":"$tessdata$/spa_old.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9476925}
|
||||||
]}
|
]}
|
||||||
, "Albanian":{"files":[
|
, "Albanian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/sqi.traineddata","https://translator.gres.biz/resources/tessdata_best/sqi.traineddata.zip"], "path":"$tessdata$/sqi.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4631498}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/sqi.traineddata","https://translator.gres.biz/resources/tessdata_best/sqi.traineddata.zip"], "path":"$tessdata$/sqi.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4631498}
|
||||||
]}
|
]}
|
||||||
, "Serbian":{"files":[
|
, "Serbian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/srp.traineddata","https://translator.gres.biz/resources/tessdata_best/srp.traineddata.zip"], "path":"$tessdata$/srp.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9345851}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/srp.traineddata","https://translator.gres.biz/resources/tessdata_best/srp.traineddata.zip"], "path":"$tessdata$/srp.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9345851}
|
||||||
]}
|
]}
|
||||||
, "srp_latn":{"files":[
|
, "srp_latn":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/srp_latn.traineddata","https://translator.gres.biz/resources/tessdata_best/srp_latn.traineddata.zip"], "path":"$tessdata$/srp_latn.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9831713}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/srp_latn.traineddata","https://translator.gres.biz/resources/tessdata_best/srp_latn.traineddata.zip"], "path":"$tessdata$/srp_latn.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9831713}
|
||||||
]}
|
]}
|
||||||
, "Sundanese":{"files":[
|
, "Sundanese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/sun.traineddata","https://translator.gres.biz/resources/tessdata_best/sun.traineddata.zip"], "path":"$tessdata$/sun.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4132820}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/sun.traineddata","https://translator.gres.biz/resources/tessdata_best/sun.traineddata.zip"], "path":"$tessdata$/sun.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4132820}
|
||||||
]}
|
]}
|
||||||
, "Swahili":{"files":[
|
, "Swahili":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/swa.traineddata","https://translator.gres.biz/resources/tessdata_best/swa.traineddata.zip"], "path":"$tessdata$/swa.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4914855}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/swa.traineddata","https://translator.gres.biz/resources/tessdata_best/swa.traineddata.zip"], "path":"$tessdata$/swa.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4914855}
|
||||||
]}
|
]}
|
||||||
, "Swedish":{"files":[
|
, "Swedish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/swe.traineddata","https://translator.gres.biz/resources/tessdata_best/swe.traineddata.zip"], "path":"$tessdata$/swe.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14325549}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/swe.traineddata","https://translator.gres.biz/resources/tessdata_best/swe.traineddata.zip"], "path":"$tessdata$/swe.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":14325549}
|
||||||
]}
|
]}
|
||||||
, "Syriac":{"files":[
|
, "Syriac":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/syr.traineddata","https://translator.gres.biz/resources/tessdata_best/syr.traineddata.zip"], "path":"$tessdata$/syr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12498294}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/syr.traineddata","https://translator.gres.biz/resources/tessdata_best/syr.traineddata.zip"], "path":"$tessdata$/syr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12498294}
|
||||||
]}
|
]}
|
||||||
, "Tamil":{"files":[
|
, "Tamil":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/tam.traineddata","https://translator.gres.biz/resources/tessdata_best/tam.traineddata.zip"], "path":"$tessdata$/tam.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6023201}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/tam.traineddata","https://translator.gres.biz/resources/tessdata_best/tam.traineddata.zip"], "path":"$tessdata$/tam.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":6023201}
|
||||||
]}
|
]}
|
||||||
, "Tatar":{"files":[
|
, "Tatar":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/tat.traineddata","https://translator.gres.biz/resources/tessdata_best/tat.traineddata.zip"], "path":"$tessdata$/tat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7585204}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/tat.traineddata","https://translator.gres.biz/resources/tessdata_best/tat.traineddata.zip"], "path":"$tessdata$/tat.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7585204}
|
||||||
]}
|
]}
|
||||||
, "Telugu":{"files":[
|
, "Telugu":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/tel.traineddata","https://translator.gres.biz/resources/tessdata_best/tel.traineddata.zip"], "path":"$tessdata$/tel.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9098795}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/tel.traineddata","https://translator.gres.biz/resources/tessdata_best/tel.traineddata.zip"], "path":"$tessdata$/tel.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":9098795}
|
||||||
]}
|
]}
|
||||||
, "Tajik":{"files":[
|
, "Tajik":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/tgk.traineddata","https://translator.gres.biz/resources/tessdata_best/tgk.traineddata.zip"], "path":"$tessdata$/tgk.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4602842}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/tgk.traineddata","https://translator.gres.biz/resources/tessdata_best/tgk.traineddata.zip"], "path":"$tessdata$/tgk.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4602842}
|
||||||
]}
|
]}
|
||||||
, "Thai":{"files":[
|
, "Thai":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/tha.traineddata","https://translator.gres.biz/resources/tessdata_best/tha.traineddata.zip"], "path":"$tessdata$/tha.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7614571}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/tha.traineddata","https://translator.gres.biz/resources/tessdata_best/tha.traineddata.zip"], "path":"$tessdata$/tha.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7614571}
|
||||||
]}
|
]}
|
||||||
, "Tigrinya":{"files":[
|
, "Tigrinya":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/tir.traineddata","https://translator.gres.biz/resources/tessdata_best/tir.traineddata.zip"], "path":"$tessdata$/tir.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":2410256}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/tir.traineddata","https://translator.gres.biz/resources/tessdata_best/tir.traineddata.zip"], "path":"$tessdata$/tir.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":2410256}
|
||||||
]}
|
]}
|
||||||
, "Tonga (Tonga Islands)":{"files":[
|
, "Tonga (Tonga Islands)":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ton.traineddata","https://translator.gres.biz/resources/tessdata_best/ton.traineddata.zip"], "path":"$tessdata$/ton.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3729371}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ton.traineddata","https://translator.gres.biz/resources/tessdata_best/ton.traineddata.zip"], "path":"$tessdata$/ton.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3729371}
|
||||||
]}
|
]}
|
||||||
, "Turkish":{"files":[
|
, "Turkish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/tur.traineddata","https://translator.gres.biz/resources/tessdata_best/tur.traineddata.zip"], "path":"$tessdata$/tur.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7456265}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/tur.traineddata","https://translator.gres.biz/resources/tessdata_best/tur.traineddata.zip"], "path":"$tessdata$/tur.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7456265}
|
||||||
]}
|
]}
|
||||||
, "Uighur, Uyghur":{"files":[
|
, "Uighur, Uyghur":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/uig.traineddata","https://translator.gres.biz/resources/tessdata_best/uig.traineddata.zip"], "path":"$tessdata$/uig.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13074609}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/uig.traineddata","https://translator.gres.biz/resources/tessdata_best/uig.traineddata.zip"], "path":"$tessdata$/uig.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":13074609}
|
||||||
]}
|
]}
|
||||||
, "Ukrainian":{"files":[
|
, "Ukrainian":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/ukr.traineddata","https://translator.gres.biz/resources/tessdata_best/ukr.traineddata.zip"], "path":"$tessdata$/ukr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10859081}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/ukr.traineddata","https://translator.gres.biz/resources/tessdata_best/ukr.traineddata.zip"], "path":"$tessdata$/ukr.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":10859081}
|
||||||
]}
|
]}
|
||||||
, "Urdu":{"files":[
|
, "Urdu":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/urd.traineddata","https://translator.gres.biz/resources/tessdata_best/urd.traineddata.zip"], "path":"$tessdata$/urd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7994323}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/urd.traineddata","https://translator.gres.biz/resources/tessdata_best/urd.traineddata.zip"], "path":"$tessdata$/urd.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":7994323}
|
||||||
]}
|
]}
|
||||||
, "Uzbek":{"files":[
|
, "Uzbek":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/uzb.traineddata","https://translator.gres.biz/resources/tessdata_best/uzb.traineddata.zip"], "path":"$tessdata$/uzb.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12953454}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/uzb.traineddata","https://translator.gres.biz/resources/tessdata_best/uzb.traineddata.zip"], "path":"$tessdata$/uzb.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12953454}
|
||||||
]}
|
]}
|
||||||
, "uzb_cyrl":{"files":[
|
, "uzb_cyrl":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/uzb_cyrl.traineddata","https://translator.gres.biz/resources/tessdata_best/uzb_cyrl.traineddata.zip"], "path":"$tessdata$/uzb_cyrl.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4325478}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/uzb_cyrl.traineddata","https://translator.gres.biz/resources/tessdata_best/uzb_cyrl.traineddata.zip"], "path":"$tessdata$/uzb_cyrl.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":4325478}
|
||||||
]}
|
]}
|
||||||
, "Vietnamese":{"files":[
|
, "Vietnamese":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/vie.traineddata","https://translator.gres.biz/resources/tessdata_best/vie.traineddata.zip"], "path":"$tessdata$/vie.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12435550}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/vie.traineddata","https://translator.gres.biz/resources/tessdata_best/vie.traineddata.zip"], "path":"$tessdata$/vie.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":12435550}
|
||||||
]}
|
]}
|
||||||
, "Yiddish":{"files":[
|
, "Yiddish":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/yid.traineddata","https://translator.gres.biz/resources/tessdata_best/yid.traineddata.zip"], "path":"$tessdata$/yid.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3278995}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/yid.traineddata","https://translator.gres.biz/resources/tessdata_best/yid.traineddata.zip"], "path":"$tessdata$/yid.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3278995}
|
||||||
]}
|
]}
|
||||||
, "Yoruba":{"files":[
|
, "Yoruba":{"files":[
|
||||||
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/master/yor.traineddata","https://translator.gres.biz/resources/tessdata_best/yor.traineddata.zip"], "path":"$tessdata$/yor.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3736121}
|
{"url":["https://github.com/tesseract-ocr/tessdata_best/raw/main/yor.traineddata","https://translator.gres.biz/resources/tessdata_best/yor.traineddata.zip"], "path":"$tessdata$/yor.traineddata", "date":"2020-03-09T08:28:45+01:00", "size":3736121}
|
||||||
]}
|
]}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,188 +387,200 @@
|
|||||||
|
|
||||||
,"correction": {
|
,"correction": {
|
||||||
"Afrikaans":{"files":[
|
"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.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-03-17T12:21:16+01:00", "size":1262203}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":7217161}
|
,{"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":[
|
, "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-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_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.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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":1566331}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2596038}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":4637}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":339863}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2209232}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2915525}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":4356858}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":10125390}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":551260}
|
,{"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":[
|
, "Esperanto":{"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/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/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}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":4383841}
|
,{"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":[
|
, "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.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-03-17T12:21:16+01:00", "size":1100397}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":4806682}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":8636406}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":3792870}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":7796259}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":303963}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":731819}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":1653155}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":315384}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2454138}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":1290681}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":203209}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":1085291}
|
,{"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":[
|
, "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.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-03-17T12:21:16+01:00", "size":2226834}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":874372}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":1881063}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":5274030}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":4539105}
|
,{"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":[
|
, "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_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_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.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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2196348}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":1969349}
|
,{"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":[
|
, "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.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-03-17T12:21:16+01:00", "size":3289769}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2967766}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2605147}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":5878745}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2317112}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":630844}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":3402272}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":1251425}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":9061155}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":2584267}
|
,{"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":[
|
, "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.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":"2020-03-17T12:21:16+01:00", "size":39852}
|
,{"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}
|
||||||
]}
|
]}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,26 +588,27 @@
|
|||||||
|
|
||||||
,"translators":{
|
,"translators":{
|
||||||
"baidu": {"files":[
|
"baidu": {"files":[
|
||||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/baidu.js", "path":"$translators$/baidu.js", "md5":"93f7bca9b792877350f54a1d47767583", "size":1306}
|
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/baidu.js", "path":"$translators$/baidu.js", "md5":"be3eb6d11fa5faebb046c887c9a8f3bd", "size":1501}
|
||||||
]}
|
]}
|
||||||
,"bing": {"files":[
|
,"bing": {"files":[
|
||||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/bing.js", "path":"$translators$/bing.js", "md5":"5c20fe78c25a4f9e97160fdc3bc4572c", "size":1277}
|
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/bing.js", "path":"$translators$/bing.js", "md5":"a982e9aa6cac598f4c9bf4a56386d13e", "size":1481}
|
||||||
]}
|
]}
|
||||||
,"deepl": {"files":[
|
,"deepl": {"files":[
|
||||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/deepl.js", "path":"$translators$/deepl.js", "md5":"a6dfae3f63ca3fa9c7edbfaff87600e4", "size":1596}
|
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/deepl.js", "path":"$translators$/deepl.js", "md5":"76856af9b80c3d0e852ca73f8f1ebbdb", "size":2611}
|
||||||
]}
|
]}
|
||||||
,"google": {"files":[
|
,"google": {"files":[
|
||||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google.js", "path":"$translators$/google.js", "md5":"16ffead93035e08e8db13279cc8b65a7", "size":1260}
|
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google.js", "path":"$translators$/google.js", "md5":"793d6628ac9e26a1f3cc00fa9c863495", "size":1508}
|
||||||
]}
|
]}
|
||||||
,"google_api": {"files":[
|
,"google_api": {"files":[
|
||||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google_api.js", "path":"$translators$/google_api.js", "md5":"90b9b1a5c8dc52fd4a3f28be93442a56", "size":1030}
|
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google_api.js", "path":"$translators$/google_api.js", "md5":"90b9b1a5c8dc52fd4a3f28be93442a56", "size":1030}
|
||||||
]}
|
]}
|
||||||
,"papago": {"files":[
|
,"papago": {"files":[
|
||||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/papago.js", "path":"$translators$/papago.js", "md5":"538bb7280192b18d15d24b07adae2638", "size":1956}
|
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/papago.js", "path":"$translators$/papago.js", "md5":"603a56fc23990453942064ec53d1eaa3", "size":2164}
|
||||||
]}
|
]}
|
||||||
,"yandex": {"files":[
|
,"yandex": {"files":[
|
||||||
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/yandex.js", "path":"$translators$/yandex.js", "md5":"f8c625e8f6ced4a9f5be6791a6ee3f87", "size":957}
|
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/yandex.js", "path":"$translators$/yandex.js", "md5":"82c10bddde30f3a1dc6675f7eea71986", "size":1170}
|
||||||
]}
|
]}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
15
version.json
15
version.json
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"url": "https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/version.json",
|
|
||||||
"Application": {
|
|
||||||
"version": 3,
|
|
||||||
"compatibleVersion": 3,
|
|
||||||
"built_in": true,
|
|
||||||
"versionString": "3.0.0",
|
|
||||||
"permissions": "0x7755",
|
|
||||||
"url_win": "disabled",
|
|
||||||
"path_win": "ScreenTranslator.exe",
|
|
||||||
"url_linux": "disabled",
|
|
||||||
"path_linux": "ScreenTranslator"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user