Add build and ci scripts
This commit is contained in:
parent
81f370f5c7
commit
f2b7ac1821
118
.github/workflows/build.yml
vendored
Normal file
118
.github/workflows/build.yml
vendored
Normal file
@ -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
|
@ -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
|
||||
}
|
||||
|
54
share/ci/appimage.py
Normal file
54
share/ci/appimage.py
Normal file
@ -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))
|
24
share/ci/build.py
Normal file
24
share/ci/build.py
Normal file
@ -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)
|
213
share/ci/common.py
Normal file
213
share/ci/common.py
Normal file
@ -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
|
23
share/ci/config.py
Normal file
23
share/ci/config.py
Normal file
@ -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')
|
71
share/ci/get_leptonica.py
Normal file
71
share/ci/get_leptonica.py
Normal file
@ -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')
|
82
share/ci/get_qt.py
Normal file
82
share/ci/get_qt.py
Normal file
@ -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)
|
74
share/ci/get_tesseract.py
Normal file
74
share/ci/get_tesseract.py
Normal file
@ -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')
|
18
share/ci/macdeploy.py
Normal file
18
share/ci/macdeploy.py
Normal file
@ -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)
|
35
share/ci/windeploy.py
Normal file
35
share/ci/windeploy.py
Normal file
@ -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)
|
BIN
share/images/icon.icns
Normal file
BIN
share/images/icon.icns
Normal file
Binary file not shown.
BIN
share/images/screentranslator.png
Normal file
BIN
share/images/screentranslator.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
9
share/screentranslator.desktop
Normal file
9
share/screentranslator.desktop
Normal file
@ -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;
|
399
share/translations/screentranslator_ru.ts
Normal file
399
share/translations/screentranslator_ru.ts
Normal file
@ -0,0 +1,399 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="ru_RU">
|
||||
<context>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../../src/main.cpp" line="31"/>
|
||||
<source>OCR and translation tool</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="189"/>
|
||||
<source>The last result was copied to the clipboard.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="195"/>
|
||||
<source>Optical character recognition (OCR) and translation tool
|
||||
Author: Gres (translator@gres.biz)
|
||||
Version: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/manager.cpp" line="200"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/ocr/tesseract.cpp" line="173"/>
|
||||
<source>unknown recognition language: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/ocr/tesseract.cpp" line="185"/>
|
||||
<source>troubles with tessdata</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/ocr/tesseract.cpp" line="213"/>
|
||||
<source>Failed to recognize text</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/service/singleapplication.cpp" line="23"/>
|
||||
<source>Another instance is running. Lock file is busy.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/translate/webpage.cpp" line="96"/>
|
||||
<source>unknown translation language: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>RecognizeWorker</name>
|
||||
<message>
|
||||
<location filename="../../src/ocr/recognizerworker.cpp" line="21"/>
|
||||
<source>Failed to init OCR engine: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Recognizer</name>
|
||||
<message>
|
||||
<location filename="../../src/ocr/recognizer.cpp" line="43"/>
|
||||
<source>Tessdata path is empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsEditor</name>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="14"/>
|
||||
<source>Настройки</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="53"/>
|
||||
<source>Shortcuts</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="59"/>
|
||||
<source><html><head/><body><p>Сочетание клавиш для перехода в режим захвата.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="62"/>
|
||||
<source>Capture</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="72"/>
|
||||
<source><html><head/><body><p>Сочетание клавиш для перехода в режим захвата, но с использованием последнего использованного, а не текущего, изображения.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="75"/>
|
||||
<source>Repeat capture</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="85"/>
|
||||
<source><html><head/><body><p>Сочетание клавиш для повторного отображения последнего результата.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="88"/>
|
||||
<source>Show last result</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="98"/>
|
||||
<source><html><head/><body><p>Сочетание клавиш для копирования последнего результата в буфер обмена.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="101"/>
|
||||
<source>Copy result to clipboard</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="114"/>
|
||||
<source>Proxy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="120"/>
|
||||
<source>Type:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="130"/>
|
||||
<source>User:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="140"/>
|
||||
<source>Address:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="150"/>
|
||||
<source>Password:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="160"/>
|
||||
<source>Port:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="177"/>
|
||||
<source>save password (unsafe)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="204"/>
|
||||
<location filename="../../src/settingseditor.ui" line="261"/>
|
||||
<source><html><head/><body><p>Заполняется на основании содержания tessdata</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="207"/>
|
||||
<source>Language</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="217"/>
|
||||
<source>...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="237"/>
|
||||
<location filename="../../src/settingseditor.cpp" line="133"/>
|
||||
<source>Path to tessdata</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="247"/>
|
||||
<source>Enter path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="264"/>
|
||||
<location filename="../../src/settingseditor.ui" line="341"/>
|
||||
<source>Language:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="277"/>
|
||||
<source><html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="280"/>
|
||||
<source>Corrections</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="304"/>
|
||||
<source><html><head/><body><p>Отображает окно переводчика. Следует использовать только для разработки переводчиков.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="307"/>
|
||||
<source>Debug mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="314"/>
|
||||
<source> secs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="321"/>
|
||||
<source>Ignore SSL errors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="328"/>
|
||||
<source>Translators</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="338"/>
|
||||
<source><html><head/><body><p>Язык, на который осуществляется перевод.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="351"/>
|
||||
<source><html><head/><body><p>Отображены в порядке убывания приоритета.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="367"/>
|
||||
<source><html><head/><body><p>Необходимо ли переводить (вкл) распознанный текст.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="370"/>
|
||||
<source>Translate text</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="380"/>
|
||||
<source><html><head/><body><p>Максимальное время, которое может быть затрачено на перевод, чтобы он не считался &quot;зависшим&quot;.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="383"/>
|
||||
<source>Single translator timeout:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="400"/>
|
||||
<source>Result type</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="412"/>
|
||||
<source>Tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="425"/>
|
||||
<source>Window</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="471"/>
|
||||
<source>Check for updates:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.ui" line="487"/>
|
||||
<source>Check now</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="21"/>
|
||||
<source>General</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="21"/>
|
||||
<source>Recognition</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="21"/>
|
||||
<source>Correction</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="22"/>
|
||||
<source>Translation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="22"/>
|
||||
<source>Representation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="23"/>
|
||||
<source>Update</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="65"/>
|
||||
<source>Never</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="65"/>
|
||||
<source>Daily</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="65"/>
|
||||
<source>Weekly</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/settingseditor.cpp" line="65"/>
|
||||
<source>Monthly</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Translator</name>
|
||||
<message>
|
||||
<location filename="../../src/translate/translator.cpp" line="101"/>
|
||||
<source>No translators loaded from %1 (named %2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/translate/translator.cpp" line="168"/>
|
||||
<source>All translators failed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TrayIcon</name>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="45"/>
|
||||
<source>Failed to register global shortcuts:
|
||||
%1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="140"/>
|
||||
<location filename="../../src/trayicon.cpp" line="147"/>
|
||||
<source>Error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="178"/>
|
||||
<source>Capture</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="183"/>
|
||||
<source>Repeat capture</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="189"/>
|
||||
<source>Result</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="191"/>
|
||||
<source>Show</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="196"/>
|
||||
<source>To clipboard</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="203"/>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="209"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/trayicon.cpp" line="215"/>
|
||||
<source>Quit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
Loading…
Reference in New Issue
Block a user