diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..600f0a0 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,118 @@ +name: Build + +on: [push] + +jobs: + release: + name: Create release + if: contains(github.ref, '/tags/') + runs-on: ubuntu-16.04 + steps: + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: false + prerelease: false + + - name: Store release url + run: echo "${{ steps.create_release.outputs.upload_url }}" > ./release_upload_url + + - name: Upload release url + uses: actions/upload-artifact@v1 + with: + path: ./release_upload_url + name: release_upload_url + + build: + name: Build ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + env: + OS: ${{ matrix.config.name }} + MSVC_VERSION: 2019/Enterprise + strategy: + matrix: + config: + - { name: "win64", os: windows-latest } + - { name: "win32", os: windows-latest } + - { name: "linux", os: ubuntu-16.04 } + - { name: "macos", os: macos-latest } + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 10 + + - name: Setup python + uses: actions/setup-python@v1 + with: + python-version: "3.x" + + - name: Install system libs + if: runner.os == 'Linux' + run: sudo apt install libgl1-mesa-dev libxkbcommon-x11-0 + + - name: Get Qt + run: python ./share/ci/get_qt.py + + - name: Get dependencies + run: | + python ./share/ci/get_leptonica.py + python ./share/ci/get_tesseract.py + + - name: Build + run: python ./share/ci/build.py + + - name: Create AppImage + if: runner.os == 'Linux' + shell: bash + run: | + python ./share/ci/appimage.py + echo ::set-env name=artifact::`python ./share/ci/appimage.py artifact_name` + + - 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 + if: env.artifact != '' + uses: actions/upload-artifact@v1 + with: + name: ${{ env.artifact }} + path: ./${{ env.artifact }} + + - name: Download release url + if: contains(github.ref, '/tags/') + uses: actions/download-artifact@v1 + with: + name: release_upload_url + path: ./ + + - name: Set release env + if: contains(github.ref, '/tags/') + shell: bash + run: echo ::set-env name=upload_url::`cat ./release_upload_url` + + - name: Upload release artifacts + if: contains(github.ref, '/tags/') + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ env.upload_url }} + asset_path: ./${{ env.artifact }} + asset_name: ${{ env.artifact }} + asset_content_type: application/zip diff --git a/screen-translator.pro b/screen-translator.pro index 59cc9e1..96485b7 100644 --- a/screen-translator.pro +++ b/screen-translator.pro @@ -87,6 +87,35 @@ OTHER_FILES += \ translators/*.js \ version.json -#TRANSLATIONS += \ -# translations/translation_en.ts \ -# translations/translation_ru.ts +TRANSLATIONS += \ + share/translations/screentranslator_ru.ts + +translations.files = $$PWD/share/translations/screentranslator_ru.qm + +translators.files = $$PWD/translators/*.js + +linux { + PREFIX = /usr + + target.path = $$PREFIX/bin + + shortcuts.files = $$PWD/share/screentranslator.desktop + shortcuts.path = $$PREFIX/share/applications/ + pixmaps.files += $$PWD/share/images/screentranslator.png + pixmaps.path = $$PREFIX/share/icons/hicolor/128x128/apps/ + translations.path = $$PREFIX/translations + + INSTALLS += target shortcuts pixmaps translations +} +win32 { + RC_ICONS = $$PWD/share/images/icon.ico + translations.path = /translations + target.path = / + translators.path = /translators + INSTALLS += target translations translators +} +mac { + translations.path = Contents/Translations + QMAKE_BUNDLE_DATA += translations + ICON = $$PWD/share/images/icon.icns +} diff --git a/share/ci/appimage.py b/share/ci/appimage.py new file mode 100644 index 0000000..0373e9d --- /dev/null +++ b/share/ci/appimage.py @@ -0,0 +1,54 @@ +import common as c +from config import * +import os +import sys +import subprocess as sub + +if len(sys.argv) > 1 and sys.argv[1] == 'glibc_version': # subcommand + sub.run('ldd --version | head -n 1 | grep -Po "\\d\\.\\d\\d"', shell=True) + exit(0) + +artifact_name = '{}-{}.AppImage'.format(app_name, app_version) +if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name': # subcommand + c.print(artifact_name) + exit(0) +artifact_path = os.path.abspath(artifact_name) + +c.print('>> Making appimage') + +base_url = 'https://github.com/probonopd/linuxdeployqt/releases/download' +continuous_url = base_url + '/continuous/linuxdeployqt-continuous-x86_64.AppImage' +tagged_url = base_url + '/6/linuxdeployqt-6-x86_64.AppImage' +linuxdeployqt_url = tagged_url +linuxdeployqt_original = os.path.basename(linuxdeployqt_url) + +c.download(linuxdeployqt_url, linuxdeployqt_original) +c.run('chmod a+x {}'.format(linuxdeployqt_original)) + +linuxdeployqt_bin = os.path.abspath('linuxdeployqt') +c.symlink(linuxdeployqt_original, linuxdeployqt_bin) + +os.chdir(build_dir) + +install_dir = os.path.abspath('appdir') +c.recreate_dir(install_dir) +c.run('make INSTALL_ROOT={0} DESTDIR={0} install'.format(install_dir)) + +if c.is_inside_docker(): + c.run('{} --appimage-extract'.format(linuxdeployqt_bin)) + linuxdeployqt_bin = os.path.abspath('squashfs-root/AppRun') + +os.environ['LD_LIBRARY_PATH'] = dependencies_dir + '/lib' +os.environ['VERSION'] = app_version +# debug flags: -unsupported-bundle-everything -unsupported-allow-new-glibc +flags = '' if os.getenv("DEBUG") is None else '-unsupported-allow-new-glibc' + +# remove serial post dependency +os.rename(qt_dir + '/plugins/position', qt_dir + '/plugins/position1') + +c.run('{} {}/usr/share/applications/*.desktop {} -appimage -qmake={}/bin/qmake'.format( + linuxdeployqt_bin, install_dir, flags, qt_dir)) + +os.rename(qt_dir + '/plugins/position1', qt_dir + '/plugins/position') + +c.run('mv {}-{}*.AppImage "{}"'.format(app_name, app_version, artifact_path)) diff --git a/share/ci/build.py b/share/ci/build.py new file mode 100644 index 0000000..3c9939f --- /dev/null +++ b/share/ci/build.py @@ -0,0 +1,24 @@ +import common as c +from config import * +import os +import platform + +c.print('>> Building {} on {}'.format(app_name, os_name)) + +c.add_to_path(os.path.abspath(qt_dir + '/bin')) +os.environ['ST_DEPS_DIR'] = dependencies_dir + +if platform.system() == "Windows": + env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version) + c.apply_cmd_env(env_cmd) + +c.recreate_dir(build_dir) +os.chdir(build_dir) + +c.run('lupdate "{}"'.format(pro_file)) +c.run('lrelease "{}"'.format(pro_file)) + +c.set_make_threaded() +c.run('qmake "{}"'.format(pro_file)) +make_cmd = c.get_make_cmd() +c.run(make_cmd) diff --git a/share/ci/common.py b/share/ci/common.py new file mode 100644 index 0000000..47b76c2 --- /dev/null +++ b/share/ci/common.py @@ -0,0 +1,213 @@ +import os +import subprocess as sub +import urllib.request +from shutil import which +import zipfile +import tarfile +import functools +import shutil +import multiprocessing +import platform +import re + + +print = functools.partial(print, flush=True) + + +def run(cmd, capture_output=False, silent=False): + print('>> Running', cmd) + if capture_output: + result = sub.run(cmd, check=True, shell=True, universal_newlines=True, + stdout=sub.PIPE, stderr=sub.STDOUT) + if not silent: + print(result.stdout) + else: + if not silent: + result = sub.run(cmd, check=True, shell=True) + else: + result = sub.run(cmd, check=True, shell=True, + stdout=sub.DEVNULL, stderr=sub.DEVNULL) + return result + + +def download(url, out, force=False): + print('>> Downloading', url, 'as', out) + if not force and os.path.exists(out): + print('>>', out, 'already exists') + return + out_path = os.path.dirname(out) + if len(out_path) > 0: + os.makedirs(out_path, exist_ok=True) + urllib.request.urlretrieve(url, out) + + +def extract(src, dest): + abs_path = os.path.abspath(src) + print('>> Extracting', abs_path, 'to', dest) + if len(dest) > 0: + os.makedirs(dest, exist_ok=True) + + if which('cmake'): + out = run('cmake -E tar t "{}"'.format(abs_path), + capture_output=True, silent=True) + files = out.stdout.split('\n') + already_exist = True + for file in files: + if not os.path.exists(os.path.join(dest, file)): + already_exist = False + break + if already_exist: + print('>> All files already exist') + return + sub.run('cmake -E tar xvf "{}"'.format(abs_path), + check=True, shell=True, cwd=dest) + return + + is_tar_smth = src.endswith('.tar', 0, src.rfind('.')) + if which('7z'): + sub.run('7z x "{}" -o"{}"'.format(abs_path, dest), + check=True, shell=True, input=b'S\n') + + if is_tar_smth: + inner_name = abs_path[:abs_path.rfind('.')] + sub.run('7z x "{}" -o"{}"'.format(inner_name, dest), + check=True, shell=True, input=b'S\n') + return + + if src.endswith('.tar') or is_tar_smth: + path = abs_path if platform.system() != "Windows" else os.path.relpath(abs_path) + if which('tar'): + sub.run('tar xf "{}" --keep-newer-files -C "{}"'.format(path, dest), + check=True, shell=True) + return + + raise RuntimeError('No archiver to extract {} file'.format(src)) + + +def get_folder_files(path): + result = [] + for root, _, files in os.walk(path): + for file in files: + result.append(os.path.join(root, file)) + return result + + +def get_archive_top_dir(path): + """Return first top level folder name in given archive or raises RuntimeError""" + with tarfile.open(path) as tar: + first = tar.next() + if not first is None: + result = os.path.dirname(first.path) + if len(result) == 0: + result = first.path + return result + raise RuntimeError('Failed to open file or empty archive ' + path) + + +def archive(files, out): + print('>> Archiving', files, 'into', out) + if out.endswith('.zip'): + arc = zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) + for f in files: + arc.write(f) + arc.close() + return + + if out.endswith('.tar.gz'): + arc = tarfile.open(out, 'w|gz') + for f in files: + arc.add(f) + arc.close() + return + + raise RuntimeError('No archiver to create {} file'.format(out)) + + +def symlink(src, dest): + print('>> Creating symlink', src, '=>', dest) + norm_src = os.path.normcase(src) + norm_dest = os.path.normcase(dest) + if os.path.lexists(norm_dest): + os.remove(norm_dest) + os.symlink(norm_src, norm_dest, + target_is_directory=os.path.isdir(norm_src)) + + +def recreate_dir(path): + shutil.rmtree(path, ignore_errors=True) + os.mkdir(path) + + +def add_to_path(entry, prepend=True): + path_separator = ';' if platform.system() == "Windows" else ':' + os.environ['PATH'] = entry + path_separator + os.environ['PATH'] + + +def get_msvc_env_cmd(bitness='64', msvc_version=''): + """Return environment setup command for running msvc compiler for current platform""" + if platform.system() != "Windows": + return None + + msvc_path = 'C:/Program Files (x86)/Microsoft Visual Studio' + if len(msvc_version) == 0: + with os.scandir(msvc_path) as ver_it: + version = next(ver_it, '') + with os.scandir(msvc_path + '/' + version) as ed_it: + msvc_version = version + '/' + next(ed_it, '') + + env_script = msvc_path + '/{}/VC/Auxiliary/Build/vcvars{}.bat'.format( + msvc_version, bitness) + return '"' + env_script + '"' + + +def get_cmake_arch_args(bitness='64'): + if platform.system() != "Windows": + return '' + return '-A {}'.format('Win32' if bitness == '32' else 'x64') + + +def get_make_cmd(): + """Return `make` command for current platform""" + return 'nmake' if platform.system() == "Windows" else 'make' + + +def set_make_threaded(): + """Adjust environment to run threaded make command""" + if platform.system() == "Windows": + os.environ['CL'] = '/MP' + else: + os.environ['MAKEFLAGS'] = '-j{}'.format(multiprocessing.cpu_count()) + + +def is_inside_docker(): + """ Return True if running in a Docker container """ + with open('/proc/1/cgroup', 'rt') as f: + return 'docker' in f.read() + + +def ensure_got_path(path): + os.makedirs(path, exist_ok=True) + + +def apply_cmd_env(cmd): + """Run cmd and apply its modified environment""" + print('>> Applying env after', cmd) + env_cmd = 'env' if which('env') else 'set' + env = sub.run('{} && {}'.format(cmd, env_cmd), shell=True, universal_newlines=True, + stdout=sub.PIPE) + + is_mingw = 'MINGW_CHOST' in os.environ + + lines = env.stdout.split('\n') + for line in lines: + match = re.match(r"^([a-zA-Z0-9_-]+)=(.*)$", line) + if not match: + continue + key, value = match.groups() + if key in os.environ and os.environ[key] == value: + continue + if is_mingw and key.find('PATH') != -1 and value.find('/') != -1: + value = value.replace(':', ';') + value = re.sub(r'/(\w)/', r'\1:\\', value) + value = value.replace('/', '\\') + os.environ[key] = value diff --git a/share/ci/config.py b/share/ci/config.py new file mode 100644 index 0000000..d24a1fc --- /dev/null +++ b/share/ci/config.py @@ -0,0 +1,23 @@ +from os import getenv, path + +app_name = 'ScreenTranslator' +app_version = '3.0.0' + +target_name = app_name +qt_version = '5.14.0' +qt_modules = ['qtbase', 'qttools', 'icu', + 'qttranslations', 'qtx11extras', 'qtwebengine', 'qtwebchannel', + 'qtdeclarative', 'qtlocation', 'opengl32sw', 'd3dcompiler_47'] +qt_dir = path.abspath('qt') + +build_dir = path.abspath('build') +dependencies_dir = path.abspath('deps') +pro_file = path.abspath(path.dirname(__file__) + + '/../../screen-translator.pro') +ts_files_dir = path.abspath(path.dirname(__file__) + '/../../translations') + +os_name = getenv('OS', 'linux') +app_version += {'linux': '', 'macos': '-experimental', + 'win32': '', 'win64': ''}[os_name] +bitness = '32' if os_name == 'win32' else '64' +msvc_version = getenv('MSVC_VERSION', '2017/Community') diff --git a/share/ci/get_leptonica.py b/share/ci/get_leptonica.py new file mode 100644 index 0000000..e31fe82 --- /dev/null +++ b/share/ci/get_leptonica.py @@ -0,0 +1,71 @@ +import common as c +from config import bitness, msvc_version, build_dir, dependencies_dir +import os +import platform + +c.print('>> Installing leptonica') + +install_dir = dependencies_dir +url = 'http://www.leptonica.org/source/leptonica-1.78.0.tar.gz' +required_version = '1.78.0' + + +def check_existing(): + if platform.system() == "Windows": + dll = install_dir + '/bin/leptonica-1.78.0.dll' + lib = install_dir + '/lib/leptonica-1.78.0.lib' + if not os.path.exists(dll) or not os.path.exists(lib): + return False + c.symlink(dll, install_dir + '/bin/leptonica.dll') + c.symlink(lib, install_dir + '/lib/leptonica.lib') + elif platform.system() == "Darwin": + lib = install_dir + '/lib/libleptonica.1.78.0.dylib' + if not os.path.exists(lib): + return False + c.symlink(lib, install_dir + '/lib/libleptonica.dylib') + else: + if not os.path.exists(install_dir + '/lib/libleptonica.so'): + return False + + includes_path = install_dir + '/include/leptonica' + if len(c.get_folder_files(includes_path)) == 0: + return False + + version_file = install_dir + '/cmake/LeptonicaConfig-version.cmake' + if not os.path.exists(version_file): + return False + + with open(version_file, 'rt') as f: + existing_version = f.readline()[22:28] # set(Leptonica_VERSION 1.78.0) + if existing_version != required_version: + return False + return True + + +if check_existing(): + c.print('>> Using cached') + exit(0) + +archive = os.path.basename(url) +c.download(url, archive) + +src_dir = os.path.abspath('leptonica_src') +c.extract(archive, '.') +c.symlink(c.get_archive_top_dir(archive), src_dir) + +c.ensure_got_path(install_dir) + +c.recreate_dir(build_dir) +os.chdir(build_dir) + +cmake_args = '"{}" -DCMAKE_INSTALL_PREFIX="{}"'.format(src_dir, install_dir) + +if platform.system() == "Windows": + env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version) + c.apply_cmd_env(env_cmd) + cmake_args += ' ' + c.get_cmake_arch_args(bitness=bitness) + +c.set_make_threaded() +c.run('cmake {}'.format(cmake_args)) +c.run('cmake --build . --config Release') +c.run('cmake --build . --target install --config Release') diff --git a/share/ci/get_qt.py b/share/ci/get_qt.py new file mode 100644 index 0000000..430ce2f --- /dev/null +++ b/share/ci/get_qt.py @@ -0,0 +1,82 @@ +import common as c +from config import qt_modules, qt_version, qt_dir, os_name +import sys +import xml.etree.ElementTree as ET + +c.print('>> Downloading Qt {} ({}) for {}'.format( + qt_version, qt_modules, os_name)) + +if os_name == 'linux': + os_url = 'linux_x64' + kit_arch = 'gcc_64' + qt_dir_prefix = '{}/gcc_64'.format(qt_version) +elif os_name == 'win32': + os_url = 'windows_x86' + kit_arch = 'win32_msvc2017' + qt_dir_prefix = '{}/msvc2017'.format(qt_version) +elif os_name == 'win64': + os_url = 'windows_x86' + kit_arch = 'win64_msvc2017_64' + qt_dir_prefix = '{}/msvc2017_64'.format(qt_version) +elif os_name == 'macos': + os_url = 'mac_x64' + kit_arch = 'clang_64' + qt_dir_prefix = '{}/clang_64'.format(qt_version) + +qt_version_dotless = qt_version.replace('.', '') +base_url = 'https://download.qt.io/online/qtsdkrepository/{}/desktop/qt5_{}' \ + .format(os_url, qt_version_dotless) +updates_file = 'Updates-{}-{}.xml'.format(qt_version, os_name) +c.download(base_url + '/Updates.xml', updates_file) + +updates = ET.parse(updates_file) +updates_root = updates.getroot() +all_modules = {} +for i in updates_root.iter('PackageUpdate'): + name = i.find('Name').text + if 'debug' in name or not kit_arch in name: + continue + + archives = i.find('DownloadableArchives') + if archives.text is None: + continue + + archives_parts = archives.text.split(',') + version = i.find('Version').text + for archive in archives_parts: + archive = archive.strip() + parts = archive.split('-') + module_name = parts[0] + all_modules[module_name] = {'package': name, 'file': version + archive} + +if len(sys.argv) > 1: # handle subcommand + if sys.argv[1] == 'list': + c.print('Available modules:') + for k in iter(sorted(all_modules.keys())): + c.print(k, '---', all_modules[k]['file']) + exit(0) + +for module in qt_modules: + if module not in all_modules: + c.print('>> Required module {} not available'.format(module)) + continue + file_name = all_modules[module]['file'] + package = all_modules[module]['package'] + c.download(base_url + '/' + package + '/' + file_name, file_name) + c.extract(file_name, '.') + +c.symlink(qt_dir_prefix, qt_dir) + +c.print('>> Updating license') +config_name = qt_dir + '/mkspecs/qconfig.pri' +config = '' +with open(config_name, 'r') as f: + config = f.read() + +config = config.replace('Enterprise', 'OpenSource') +config = config.replace('licheck.exe', '') +config = config.replace('licheck64', '') +config = config.replace('licheck_mac', '') + +with open(config_name, 'w') as f: + f.write(config) diff --git a/share/ci/get_tesseract.py b/share/ci/get_tesseract.py new file mode 100644 index 0000000..0c89e5b --- /dev/null +++ b/share/ci/get_tesseract.py @@ -0,0 +1,74 @@ +import common as c +from config import bitness, msvc_version, build_dir, dependencies_dir +import os +import platform + +c.print('>> Installing tesseract') + +install_dir = dependencies_dir +url = 'https://github.com/tesseract-ocr/tesseract/archive/4.1.1.tar.gz' +required_version = '4.1.1' + + +def check_existing(): + if platform.system() == "Windows": + dll = install_dir + '/bin/tesseract41.dll' + lib = install_dir + '/lib/tesseract41.lib' + if not os.path.exists(dll) or not os.path.exists(lib): + return False + c.symlink(dll, install_dir + '/bin/tesseract.dll') + c.symlink(lib, install_dir + '/lib/tesseract.lib') + elif platform.system() == "Darwin": + lib = install_dir + '/lib/libtesseract.4.1.1.dylib' + if not os.path.exists(lib): + return False + c.symlink(lib, install_dir + '/lib/libtesseract.dylib') + else: + if not os.path.exists(install_dir + '/lib/libtesseract.so'): + return False + + includes_path = install_dir + '/include/tesseract' + if len(c.get_folder_files(includes_path)) == 0: + return False + + version_file = install_dir + '/cmake/TesseractConfig-version.cmake' + if not os.path.exists(version_file): + return False + + with open(version_file, 'rt') as f: + existing_version = f.readline()[22:27] # set(Tesseract_VERSION 1.78.0) + if existing_version != required_version: + return False + return True + + +if check_existing(): + c.print('>> Using cached') + exit(0) + +archive = 'tesseract-' + os.path.basename(url) +c.download(url, archive) + +src_dir = os.path.abspath('tesseract_src') +c.extract(archive, '.') +c.symlink(c.get_archive_top_dir(archive), src_dir) + +c.ensure_got_path(install_dir) + +c.recreate_dir(build_dir) +os.chdir(build_dir) + +os.environ['PKG_CONFIG_PATH'] = install_dir + '/lib/pkgconfig' + +cmake_args = '"{}" -DCMAKE_INSTALL_PREFIX="{}" -DBUILD_TRAINING_TOOLS=OFF \ + -DBUILD_TESTS=OFF'.format(src_dir, install_dir) + +if platform.system() == "Windows": + env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version) + c.apply_cmd_env(env_cmd) + cmake_args += ' ' + c.get_cmake_arch_args(bitness=bitness) + +c.set_make_threaded() +c.run('cmake {}'.format(cmake_args)) +c.run('cmake --build . --config Release') +c.run('cmake --build . --target install --config Release') diff --git a/share/ci/macdeploy.py b/share/ci/macdeploy.py new file mode 100644 index 0000000..9650f85 --- /dev/null +++ b/share/ci/macdeploy.py @@ -0,0 +1,18 @@ +import common as c +from config import * +import os +import sys + +artifact_name = '{}-{}.dmg'.format(app_name, app_version) +if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name': # subcommand + c.print(artifact_name) + exit(0) +artifact_path = os.path.abspath(artifact_name) + +c.print('>> Making mac deploy') + +os.chdir(build_dir) +build_target = build_dir + '/' + target_name + '.app' +built_dmg = build_dir + '/' + target_name + '.dmg' +c.run('{}/bin/macdeployqt "{}" -dmg'.format(qt_dir, build_target)) +os.rename(built_dmg, artifact_path) diff --git a/share/ci/windeploy.py b/share/ci/windeploy.py new file mode 100644 index 0000000..8e9def4 --- /dev/null +++ b/share/ci/windeploy.py @@ -0,0 +1,35 @@ +import common as c +from config import * +import os +import sys +import shutil + +artifact_name = '{}-{}-{}.zip'.format(app_name, app_version, os_name) +if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name': # subcommand + c.print(artifact_name) + exit(0) +artifact_path = os.path.abspath(artifact_name) + +c.print('>> Making win deploy') + +if os_name.startswith('win'): + env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version) + c.apply_cmd_env(env_cmd) + +pwd = os.getcwd() +os.chdir(build_dir) + +install_dir = os.path.abspath(app_name) +c.recreate_dir(install_dir) + +c.run('nmake INSTALL_ROOT="{0}" DESTDIR="{0}" install'.format(install_dir)) +c.run('{}/bin/windeployqt.exe "{}"'.format(qt_dir, install_dir)) + +libs_dir = os.path.join(dependencies_dir, 'bin') +for file in os.scandir(libs_dir): + if file.is_file(follow_symlinks=False) and file.name.endswith('.dll'): + full_name = os.path.join(libs_dir, file.name) + c.print('>> Copying {} to {}'.format(full_name, install_dir)) + shutil.copy(full_name, install_dir) + +c.archive(c.get_folder_files(os.path.relpath(install_dir)), artifact_path) diff --git a/share/images/icon.icns b/share/images/icon.icns new file mode 100644 index 0000000..314215b Binary files /dev/null and b/share/images/icon.icns differ diff --git a/share/images/screentranslator.png b/share/images/screentranslator.png new file mode 100644 index 0000000..7ca2c3c Binary files /dev/null and b/share/images/screentranslator.png differ diff --git a/share/screentranslator.desktop b/share/screentranslator.desktop new file mode 100644 index 0000000..5d0730f --- /dev/null +++ b/share/screentranslator.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Comment=OCR and translation tool +Exec=screen-translator +GenericName=Screen Translator +Name=ScreenTranslator +Icon=screentranslator +Terminal=false +Type=Application +Categories=Utility;Core;Qt; diff --git a/share/translations/screentranslator_ru.ts b/share/translations/screentranslator_ru.ts new file mode 100644 index 0000000..6d7c496 --- /dev/null +++ b/share/translations/screentranslator_ru.ts @@ -0,0 +1,399 @@ + + + + + QObject + + + OCR and translation tool + + + + + The last result was copied to the clipboard. + + + + + Optical character recognition (OCR) and translation tool +Author: Gres (translator@gres.biz) +Version: %1 + + + + + About + + + + + unknown recognition language: %1 + + + + + troubles with tessdata + + + + + Failed to recognize text + + + + + Another instance is running. Lock file is busy. + + + + + unknown translation language: %1 + + + + + RecognizeWorker + + + Failed to init OCR engine: %1 + + + + + Recognizer + + + Tessdata path is empty + + + + + SettingsEditor + + + Настройки + + + + + Shortcuts + + + + + <html><head/><body><p>Сочетание клавиш для перехода в режим захвата.</p></body></html> + + + + + Capture + + + + + <html><head/><body><p>Сочетание клавиш для перехода в режим захвата, но с использованием последнего использованного, а не текущего, изображения.</p></body></html> + + + + + Repeat capture + + + + + <html><head/><body><p>Сочетание клавиш для повторного отображения последнего результата.</p></body></html> + + + + + Show last result + + + + + <html><head/><body><p>Сочетание клавиш для копирования последнего результата в буфер обмена.</p></body></html> + + + + + Copy result to clipboard + + + + + Proxy + + + + + Type: + + + + + User: + + + + + Address: + + + + + Password: + + + + + Port: + + + + + save password (unsafe) + + + + + + <html><head/><body><p>Заполняется на основании содержания tessdata</p></body></html> + + + + + Language + + + + + ... + + + + + + Path to tessdata + + + + + Enter path + + + + + + Language: + + + + + <html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html> + + + + + Corrections + + + + + <html><head/><body><p>Отображает окно переводчика. Следует использовать только для разработки переводчиков.</p></body></html> + + + + + Debug mode + + + + + secs + + + + + Ignore SSL errors + + + + + Translators + + + + + <html><head/><body><p>Язык, на который осуществляется перевод.</p></body></html> + + + + + <html><head/><body><p>Отображены в порядке убывания приоритета.</p></body></html> + + + + + <html><head/><body><p>Необходимо ли переводить (вкл) распознанный текст.</p></body></html> + + + + + Translate text + + + + + <html><head/><body><p>Максимальное время, которое может быть затрачено на перевод, чтобы он не считался &quot;зависшим&quot;.</p></body></html> + + + + + Single translator timeout: + + + + + Result type + + + + + Tray + + + + + Window + + + + + Check for updates: + + + + + Check now + + + + + General + + + + + Recognition + + + + + Correction + + + + + Translation + + + + + Representation + + + + + Update + + + + + Never + + + + + Daily + + + + + Weekly + + + + + Monthly + + + + + Translator + + + No translators loaded from %1 (named %2) + + + + + All translators failed + + + + + TrayIcon + + + Failed to register global shortcuts: +%1 + + + + + + Error + + + + + Capture + + + + + Repeat capture + + + + + Result + + + + + Show + + + + + To clipboard + + + + + Settings + + + + + About + + + + + Quit + + + +