Merge pull request #70 from mattrose/13-gettext

This commit is contained in:
Markus Frosch 2020-05-18 00:19:46 +02:00 committed by GitHub
commit ec2d50cdfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 803 additions and 778 deletions

5
.gitignore vendored
View File

@ -19,8 +19,3 @@ remotinatorc
terminatorlib/meliae terminatorlib/meliae
/dist /dist
/MANIFEST /MANIFEST
## language / intltool related files
.intltool*
data/terminator.appdata.xml
data/terminator.desktop

27
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,27 @@
Development Notes
=================
Here we connect notes and howtos for development around Terminator. Feel free to extend or submit suggestions.
## Translation i18n
Tooling is based on [Babel](https://babel.pocoo.org), the configuration is stored in `babel.cfg`, `setup.cfg` and
some code in `setup.py`.
The POT file [po/terminator.pot](po/terminator.pot) contains the template for all translations and should be updated
regularly, especially when messages changed inside the source code.
```
$ python setup.py extract_messages
```
Usually catalogs are updated with external translation tools, e.g. when new translations are merged. But we can update
the catalogs here, so translators will have it more easy to pick up their work.
This is a custom extension in `setup.py`.
```
$ python setup.py update_catalogs
```
Compilation of catalogs into the binary form, from `*.po` to `*.mo` is done during `setup.py build`, and the files are
installed during `setup.py install`.

7
babel.cfg Normal file
View File

@ -0,0 +1,7 @@
[ignore: build/**]
[python: **.py]
[python: terminator]
[python: remotinator]
[glade: **.glade]
[glade: data/terminator.metainfo.xml]
[desktop: **.desktop]

View File

@ -4,45 +4,45 @@
<id>terminator.desktop</id> <id>terminator.desktop</id>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0 only</project_license> <project_license>GPL-2.0 only</project_license>
<_name>Terminator</_name> <name translatable="yes">Terminator</name>
<_summary>Multiple terminals in one window</_summary> <summary translatable="yes">Multiple terminals in one window</summary>
<description> <description>
<_p> <p translatable="yes">
The robot future of terminals The robot future of terminals
</_p> </p>
<_p> <p translatable="yes">
A power-user tool for arranging terminals. It is inspired by programs such as A power-user tool for arranging terminals. It is inspired by programs such as
gnome-multi-term, quadkonsole, etc. in that the main focus is arranging terminals gnome-multi-term, quadkonsole, etc. in that the main focus is arranging terminals
in grids (tabs is the most common default method, which Terminator also supports). in grids (tabs is the most common default method, which Terminator also supports).
</_p> </p>
<_p> <p translatable="yes">
Much of the behavior of Terminator is based on GNOME Terminal, and we are adding Much of the behavior of Terminator is based on GNOME Terminal, and we are adding
more features from that as time goes by, but we also want to extend out in different more features from that as time goes by, but we also want to extend out in different
directions with useful features for sysadmins and other users. directions with useful features for sysadmins and other users.
</_p> </p>
<_p>Some highlights:</_p> <p translatable="yes">Some highlights:</p>
<ul> <ul>
<_li>Arrange terminals in a grid</_li> <li translatable="yes">Arrange terminals in a grid</li>
<_li>Tabs</_li> <li translatable="yes">Tabs</li>
<_li>Drag and drop re-ordering of terminals</_li> <li translatable="yes">Drag and drop re-ordering of terminals</li>
<_li>Lots of keyboard shortcuts</_li> <li translatable="yes">Lots of keyboard shortcuts</li>
<_li>Save multiple layouts and profiles via GUI preferences editor</_li> <li translatable="yes">Save multiple layouts and profiles via GUI preferences editor</li>
<_li>Simultaneous typing to arbitrary groups of terminals</_li> <li translatable="yes">Simultaneous typing to arbitrary groups of terminals</li>
</ul> </ul>
<_p>And lots more...</_p> <p translatable="yes">And lots more...</p>
</description> </description>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<image>http://4.bp.blogspot.com/-xt4Tja1TMQ0/Vdemmf8wYSI/AAAAAAAAA9A/uROTre0PMls/s1600/terminator_main_basic.png</image> <image>http://4.bp.blogspot.com/-xt4Tja1TMQ0/Vdemmf8wYSI/AAAAAAAAA9A/uROTre0PMls/s1600/terminator_main_basic.png</image>
<_caption>The main window showing the application in action</_caption> <caption translatable="yes">The main window showing the application in action</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>http://4.bp.blogspot.com/-rRxALSpEEZw/Vdeu58JgpnI/AAAAAAAAA9o/XewWKJ5HNo4/s1600/terminator_main_complex.png</image> <image>http://4.bp.blogspot.com/-rRxALSpEEZw/Vdeu58JgpnI/AAAAAAAAA9o/XewWKJ5HNo4/s1600/terminator_main_complex.png</image>
<_caption>Getting a little crazy with the terminals</_caption> <caption translatable="yes">Getting a little crazy with the terminals</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>http://2.bp.blogspot.com/-t_8oRyMXUls/VdemmRVnZnI/AAAAAAAAA88/rHIr8L1X7Ho/s1600/terminator_prefs_global.png</image> <image>http://2.bp.blogspot.com/-t_8oRyMXUls/VdemmRVnZnI/AAAAAAAAA88/rHIr8L1X7Ho/s1600/terminatorprefs_global.png</image>
<_caption>The preferences window where you can change the defaults</_caption> <caption translatable="yes">The preferences window where you can change the defaults</caption>
</screenshot> </screenshot>
</screenshots> </screenshots>
<url type="homepage">https://github.com/gnome-terminator/terminator</url> <url type="homepage">https://github.com/gnome-terminator/terminator</url>

View File

@ -1,11 +0,0 @@
#!/bin/sh
# Stupid workaround for intltools not handling extensionless files
ln -s terminator ../terminator.py
ln -s remotinator ../remotinator.py
# Make translation files
intltool-update -g terminator -o terminator.pot -p
# Cleanup after stupid workaround
rm ../terminator.py
rm ../remotinator.py

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
#!/bin/sh
# Update translation files
for po_file in `ls *.po`; do
msgmerge -N -U ${po_file} terminator.pot
done

View File

@ -1,2 +1,7 @@
[aliases] [aliases]
test=pytest test=pytest
[extract_messages]
mapping_file = babel.cfg
output_file = po/terminator.pot
input_dirs = .

109
setup.py
View File

@ -16,76 +16,61 @@ import platform
from terminatorlib.version import APP_NAME, APP_VERSION from terminatorlib.version import APP_NAME, APP_VERSION
PO_DIR = 'po' GETTEXT_SOURCE = 'po'
MO_DIR = os.path.join('build', 'mo') GETTEXT_DOMAIN = 'terminator'
GETTEXT_TARGET = os.path.join('share', 'locale')
CSS_DIR = os.path.join('terminatorlib', 'themes') CSS_DIR = os.path.join('terminatorlib', 'themes')
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
PYTEST_VERSION = '<5' PYTEST_VERSION = '<5'
BABELGLADE_VERSION = '< 0.7'
else: else:
PYTEST_VERSION = '>0' PYTEST_VERSION = '>0'
BABELGLADE_VERSION = '> 0'
class TerminatorDist(Distribution): class TerminatorDist(Distribution):
global_options = Distribution.global_options + [ global_options = Distribution.global_options + [
("build-documentation", None, "Build the documentation"), ("build-documentation", None, "Build the documentation"),
("install-documentation", None, "Install the documentation"), ("install-documentation", None, "Install the documentation"),
("without-gettext", None, "Don't build/install gettext .mo files"),
("without-icon-cache", None, "Don't attempt to run gtk-update-icon-cache")] ("without-icon-cache", None, "Don't attempt to run gtk-update-icon-cache")]
def __init__ (self, *args): def __init__ (self, *args):
self.without_gettext = False
self.without_icon_cache = False self.without_icon_cache = False
Distribution.__init__(self, *args) Distribution.__init__(self, *args)
class BuildData(build): class CustomBuild(build):
def run (self): """
build.run (self) Custom build extensions to build
"""
if not self.distribution.without_gettext: def run(self):
# Build the translations build.run(self)
for po in glob.glob (os.path.join (PO_DIR, '*.po')): self.build_i18n()
def build_i18n(self):
"""
Compiling files for gettext from *.po to *.mo with the proper target path
"""
info('compiling i18n files')
from babel.messages.frontend import compile_catalog
compiler = compile_catalog(self.distribution)
compiler.domain = [GETTEXT_DOMAIN]
for po in glob.glob(os.path.join(GETTEXT_SOURCE, '*.po')):
lang = os.path.basename(po[:-3]) lang = os.path.basename(po[:-3])
mo = os.path.join(MO_DIR, lang, 'terminator.mo') mo = os.path.join(self.build_base, GETTEXT_TARGET, lang, 'LC_MESSAGES', 'terminator.mo')
directory = os.path.dirname(mo) directory = os.path.dirname(mo)
if not os.path.exists(directory): if not os.path.exists(directory):
info('creating %s' % directory)
os.makedirs(directory) os.makedirs(directory)
if newer(po, mo): if newer(po, mo):
info('compiling %s -> %s' % (po, mo)) compiler.input_file = po
try: compiler.output_file = mo
rc = subprocess.call(['msgfmt', '-o', mo, po]) compiler.run()
if rc != 0:
raise Warning("msgfmt returned %d" % rc)
except Exception as e:
error("Building gettext files failed. Ensure you have gettext installed. Alternatively, try setup.py --without-gettext [build|install]")
error("Error: %s" % str(e))
sys.exit(1)
TOP_BUILDDIR='.'
INTLTOOL_MERGE='intltool-merge'
desktop_in='data/terminator.desktop.in'
desktop_data='data/terminator.desktop'
rc = os.system ("C_ALL=C " + INTLTOOL_MERGE + " -d -u -c " + TOP_BUILDDIR +
"/po/.intltool-merge-cache " + TOP_BUILDDIR + "/po " +
desktop_in + " " + desktop_data)
if rc != 0:
# run the desktop_in through a command to strip the "_" characters
with open(desktop_in) as file_in, open(desktop_data, 'w') as file_data:
[file_data.write(line.lstrip('_')) for line in file_in]
appdata_in='data/terminator.appdata.xml.in'
appdata_data='data/terminator.metainfo.xml'
rc = os.system ("C_ALL=C " + INTLTOOL_MERGE + " -x -u -c " + TOP_BUILDDIR +
"/po/.intltool-merge-cache " + TOP_BUILDDIR + "/po " +
appdata_in + " " + appdata_data)
if rc != 0:
# run the appdata_in through a command to strip the "_" characters
with open(appdata_in) as file_in, open(appdata_data, 'w') as file_data:
[file_data.write(line.replace('<_','<').replace('</_','</')) for line in file_in]
class Uninstall(Command): class Uninstall(Command):
description = "Attempt an uninstall from an install --record file" description = "Attempt an uninstall from an install --record file"
@ -148,7 +133,7 @@ class Uninstall(Command):
class InstallData(install_data): class InstallData(install_data):
def run (self): def run (self):
self.data_files.extend (self._find_css_files ()) self.data_files.extend (self._find_css_files ())
self.data_files.extend (self._find_mo_files ()) self.data_files.extend(self._find_mo_files())
install_data.run (self) install_data.run (self)
if not self.distribution.without_icon_cache: if not self.distribution.without_icon_cache:
self._update_icon_cache () self._update_icon_cache ()
@ -161,13 +146,15 @@ class InstallData(install_data):
except Exception as e: except Exception as e:
warn("updating the GTK icon cache failed: %s" % str(e)) warn("updating the GTK icon cache failed: %s" % str(e))
def _find_mo_files (self): def _find_mo_files(self):
"""
search for gettext files built during build step
"""
data_files = [] data_files = []
if not self.distribution.without_gettext: build_base = self.distribution.command_obj['build'].build_base
for mo in glob.glob (os.path.join (MO_DIR, '*', 'terminator.mo')): for mo in glob.glob(os.path.join(build_base, GETTEXT_TARGET, '*', 'LC_MESSAGES', '*.mo')):
lang = os.path.basename(os.path.dirname(mo)) dest = mo.lstrip(build_base + os.sep)
dest = os.path.join('share', 'locale', lang, 'LC_MESSAGES')
data_files.append((dest, [mo])) data_files.append((dest, [mo]))
return data_files return data_files
@ -183,6 +170,27 @@ class InstallData(install_data):
return data_files return data_files
class UpdateCatalogs(Command):
"""Update all gettext catalogs """
description = __doc__
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
from babel.messages.frontend import update_catalog
updater = update_catalog(self.distribution)
updater.input_file = os.path.join(GETTEXT_SOURCE, 'terminator.pot')
for po in glob.glob(os.path.join(GETTEXT_SOURCE, '*.po')):
updater.output_file = po
updater.run()
if platform.system() in ['FreeBSD', 'OpenBSD']: if platform.system() in ['FreeBSD', 'OpenBSD']:
man_dir = 'man' man_dir = 'man'
else: else:
@ -229,6 +237,8 @@ setup(name=APP_NAME,
], ],
setup_requires=[ setup_requires=[
'pytest-runner', 'pytest-runner',
'babel',
'BabelGladeExtractor ' + BABELGLADE_VERSION,
], ],
install_requires=[ install_requires=[
'pycairo', 'pycairo',
@ -240,6 +250,7 @@ setup(name=APP_NAME,
tests_require=test_deps, tests_require=test_deps,
extras_require={'test': test_deps}, extras_require={'test': test_deps},
package_data={'terminatorlib': ['preferences.glade', 'layoutlauncher.glade']}, package_data={'terminatorlib': ['preferences.glade', 'layoutlauncher.glade']},
cmdclass={'build': BuildData, 'install_data': InstallData, 'uninstall': Uninstall}, cmdclass={'build': CustomBuild, 'install_data': InstallData, 'uninstall': Uninstall,
'update_catalogs': UpdateCatalogs},
distclass=TerminatorDist) distclass=TerminatorDist)