about: update translators for v3.1
[git-cola.git] / cola / widgets / about.py
blob1ade2e990c29eaf1df5f99283c2d49cad297d883
1 # encoding: utf-8
2 from __future__ import division, absolute_import, unicode_literals
3 import platform
4 import webbrowser
5 import sys
7 import qtpy
8 from qtpy.QtCore import Qt
9 from qtpy import QtGui
10 from qtpy import QtWidgets
12 from ..i18n import N_
13 from .. import core
14 from .. import resources
15 from .. import hotkeys
16 from .. import icons
17 from .. import qtutils
18 from .. import version
19 from . import defs
22 def about_dialog():
23 """Launches the Help -> About dialog"""
24 view = AboutView(qtutils.active_window())
25 view.show()
26 return view
29 class ExpandingTabBar(QtWidgets.QTabBar):
30 """A TabBar with tabs that expand to fill the empty space
32 The setExpanding(True) method does not work in practice because
33 it respects the OS style. We override the style by implementing
34 tabSizeHint() so that we can specify the size explicitly.
36 """
38 def tabSizeHint(self, tab_index):
39 width = self.parent().width() / max(1, self.count()) - 1
40 size = super(ExpandingTabBar, self).tabSizeHint(tab_index)
41 size.setWidth(width)
42 return size
45 class ExpandingTabWidget(QtWidgets.QTabWidget):
47 def __init__(self, parent=None):
48 super(ExpandingTabWidget, self).__init__(parent)
49 self.setTabBar(ExpandingTabBar(self))
51 def resizeEvent(self, event):
52 """Forward resize events to the ExpandingTabBar"""
53 # Qt does not resize the tab bar when the dialog is resized
54 # so manually forward resize events to the tab bar.
55 width = event.size().width()
56 height = self.tabBar().height()
57 self.tabBar().resize(width, height)
58 return super(ExpandingTabWidget, self).resizeEvent(event)
61 class AboutView(QtWidgets.QDialog):
62 """Provides the git-cola 'About' dialog"""
64 def __init__(self, parent=None):
65 QtWidgets.QDialog.__init__(self, parent)
67 self.setWindowTitle(N_('About git-cola'))
68 self.setWindowModality(Qt.WindowModal)
70 # Top-most large icon
71 logo_pixmap = icons.cola().pixmap(defs.huge_icon, defs.large_icon)
73 self.logo_label = QtWidgets.QLabel()
74 self.logo_label.setPixmap(logo_pixmap)
75 self.logo_label.setAlignment(Qt.AlignCenter)
77 self.logo_text_label = QtWidgets.QLabel()
78 self.logo_text_label.setText('Git Cola')
79 self.logo_text_label.setAlignment(Qt.AlignLeft | Qt.AlignCenter)
81 font = self.logo_text_label.font()
82 font.setPointSize(defs.logo_text)
83 self.logo_text_label.setFont(font)
85 self.text = qtutils.textbrowser(text=copyright_text())
86 self.version = qtutils.textbrowser(text=version_text())
87 self.authors = qtutils.textbrowser(text=authors_text())
88 self.translators = qtutils.textbrowser(text=translators_text())
90 self.tabs = ExpandingTabWidget()
91 self.tabs.addTab(self.text, N_('About'))
92 self.tabs.addTab(self.version, N_('Version'))
93 self.tabs.addTab(self.authors, N_('Authors'))
94 self.tabs.addTab(self.translators, N_('Translators'))
96 self.close_button = qtutils.close_button()
97 self.close_button.setDefault(True)
99 self.logo_layout = qtutils.hbox(defs.no_margin, defs.button_spacing,
100 self.logo_label, self.logo_text_label,
101 qtutils.STRETCH)
103 self.button_layout = qtutils.hbox(defs.spacing, defs.margin,
104 qtutils.STRETCH, self.close_button)
106 self.main_layout = qtutils.vbox(defs.no_margin, defs.spacing,
107 self.logo_layout,
108 self.tabs,
109 self.button_layout)
110 self.setLayout(self.main_layout)
112 qtutils.connect_button(self.close_button, self.accept)
114 self.resize(defs.scale(600), defs.scale(720))
117 def copyright_text():
118 return """
119 Git Cola: The highly caffeinated Git GUI
121 Copyright (C) 2007-2018 David Aguilar and contributors
123 This program is free software: you can redistribute it and/or
124 modify it under the terms of the GNU General Public License
125 version 2 as published by the Free Software Foundation.
127 This program is distributed in the hope that it will
128 be useful, but WITHOUT ANY WARRANTY; without even the
129 implied warranty of MERCHANTABILITY or
130 FITNESS FOR A PARTICULAR PURPOSE.
132 See the GNU General Public License for more details.
134 You should have received a copy of the
135 GNU General Public License along with this program.
136 If not, see http://www.gnu.org/licenses/.
141 def version_text():
142 git_version = version.git_version()
143 cola_version = version.version()
144 build_version = version.build_version()
145 python_path = sys.executable
146 python_version = sys.version
147 qt_version = qtpy.QT_VERSION
148 qtpy_version = qtpy.__version__
149 pyqt_api_name = qtpy.API_NAME
150 if qtpy.PYQT5 or qtpy.PYQT4:
151 pyqt_api_version = qtpy.PYQT_VERSION
152 elif qtpy.PYSIDE:
153 pyqt_api_version = qtpy.PYSIDE_VERSION
154 else:
155 pyqt_api_version = 'unknown'
157 platform_version = platform.platform()
159 # Only show the build version if _build_version.py exists
160 if build_version:
161 build_version = '(%s)' % build_version
162 else:
163 build_version = ''
165 return N_("""
166 <br>
167 Git Cola version %(cola_version)s %(build_version)s
168 <ul>
169 <li> %(platform_version)s
170 <li> Python (%(python_path)s) %(python_version)s
171 <li> Git %(git_version)s
172 <li> Qt %(qt_version)s
173 <li> QtPy %(qtpy_version)s
174 <li> %(pyqt_api_name)s %(pyqt_api_version)s
175 </ul>
176 """) % locals()
179 def link(url, text, palette=None):
180 if palette is None:
181 palette = QtGui.QPalette()
183 color = palette.color(QtGui.QPalette.Foreground)
184 rgb = 'rgb(%s, %s, %s)' % (color.red(), color.green(), color.blue())
186 return ("""
187 <a style="font-style: italic; text-decoration: none; color: %(rgb)s;"
188 href="%(url)s">
189 %(text)s
190 </a>
191 """ % locals())
193 def mailto(email, text, palette):
194 return link('mailto:%s' % email, text, palette) + '<br>'
197 def render_authors(authors):
198 """Render a list of author details into richtext html"""
199 for x in authors:
200 x.setdefault('email', '')
202 entries = [("""
204 <strong>%(name)s</strong><br>
205 <em>%(title)s</em><br>
206 %(email)s
207 </p>
208 """ % author) for author in authors]
210 return ''.join(entries)
213 def contributors_text(authors, prelude='', epilogue=''):
214 author_text = render_authors(authors)
216 return '''
217 %(prelude)s
218 %(author_text)s
219 %(epilogue)s
220 ''' % locals()
223 def authors_text():
224 palette = QtGui.QPalette()
225 contact = N_('Email contributor')
226 authors = (
227 # The names listed here are listed in the same order as
228 # `git shortlog --summary --numbered --no-merges`
229 # Please submit a pull request if you would like to include your
230 # email address in the about screen.
231 # See the `generate-about` script in the "todo" branch.
232 # vim :read! ./Meta/generate-about
233 dict(name='David Aguilar',
234 title=N_('Maintainer (since 2007) and developer'),
235 email=mailto('davvid@gmail.com', contact, palette)),
236 dict(name='Daniel Harding', title=N_('Developer')),
237 dict(name='V字龍(Vdragon)', title=N_('Developer'),
238 email=mailto('Vdragon.Taiwan@gmail.com', contact, palette)),
239 dict(name='Efimov Vasily', title=N_('Developer')),
240 dict(name='Guillaume de Bure', title=N_('Developer')),
241 dict(name='Alex Chernetz', title=N_('Developer')),
242 dict(name='Uri Okrent', title=N_('Developer')),
243 dict(name='Andreas Sommer', title=N_('Developer')),
244 dict(name='Thomas Kluyver', title=N_('Developer')),
245 dict(name='Minarto Margoliono', title=N_('Developer')),
246 dict(name='Szymon Judasz', title=N_('Developer')),
247 dict(name='Javier Rodriguez Cuevas', title=N_('Developer')),
248 dict(name='Igor Galarraga', title=N_('Developer')),
249 dict(name='Stanislaw Halik', title=N_('Developer')),
250 dict(name='Virgil Dupras', title=N_('Developer')),
251 dict(name='Barry Roberts', title=N_('Developer')),
252 dict(name='Stefan Naewe', title=N_('Developer')),
253 dict(name='Ville Skyttä', title=N_('Developer')),
254 dict(name='Benedict Lee', title=N_('Developer')),
255 dict(name='Filip Danilović', title=N_('Developer')),
256 dict(name='Steffen Prohaska', title=N_('Developer')),
257 dict(name='xhl', title=N_('Developer')),
258 dict(name='Michael Geddes', title=N_('Developer')),
259 dict(name='Rustam Safin', title=N_('Developer')),
260 dict(name='David Martínez Martí', title=N_('Developer')),
261 dict(name='Justin Lecher', title=N_('Developer')),
262 dict(name='Karl Bielefeldt', title=N_('Developer')),
263 dict(name='Marco Costalba', title=N_('Developer')),
264 dict(name='Michael Homer', title=N_('Developer')),
265 dict(name='Sebastian Schuberth', title=N_('Developer')),
266 dict(name='Sven Claussner', title=N_('Developer')),
267 dict(name='v.paritskiy', title=N_('Developer')),
268 dict(name='AJ Bagwell', title=N_('Developer')),
269 dict(name='Adrien be', title=N_('Developer')),
270 dict(name='Audrius Karabanovas', title=N_('Developer')),
271 dict(name='Barrett Lowe', title=N_('Developer')),
272 dict(name='Ben Boeckel', title=N_('Developer')),
273 dict(name='Boris W', title=N_('Developer')),
274 dict(name='Charles', title=N_('Developer')),
275 dict(name='Clément Pit--Claudel', title=N_('Developer')),
276 dict(name='Daniel Haskin', title=N_('Developer')),
277 dict(name='Daniel King', title=N_('Developer')),
278 dict(name='Daniel Pavel', title=N_('Developer')),
279 dict(name='David Zumbrunnen', title=N_('Developer')),
280 dict(name='George Vasilakos', title=N_('Developer')),
281 dict(name='Ilya Tumaykin', title=N_('Developer')),
282 dict(name='Iulian Udrea', title=N_('Developer')),
283 dict(name='Jake Biesinger', title=N_('Developer')),
284 dict(name='Jamie Pate', title=N_('Developer')),
285 dict(name='Jean-Francois Dagenais', title=N_('Developer')),
286 dict(name='Karthik Manamcheri', title=N_('Developer')),
287 dict(name='Kelvie Wong', title=N_('Developer')),
288 dict(name='Kyle', title=N_('Developer')),
289 dict(name='Maciej Filipiak', title=N_('Developer')),
290 dict(name='Maicon D. Filippsen', title=N_('Developer')),
291 dict(name='Markus Heidelberg', title=N_('Developer')),
292 dict(name='Matthew E. Levine', title=N_('Developer')),
293 dict(name='Matthias Mailänder', title=N_('Developer')),
294 dict(name='Md. Mahbub Alam', title=N_('Developer')),
295 dict(name='Mikhail Terekhov', title=N_('Developer')),
296 dict(name='Paul Hildebrandt', title=N_('Developer')),
297 dict(name='Paul Weingardt', title=N_('Developer')),
298 dict(name='Paulo Fidalgo', title=N_('Developer')),
299 dict(name='Philip Stark', title=N_('Developer')),
300 dict(name='Rainer Müller', title=N_('Developer')),
301 dict(name='Rolando Espinoza', title=N_('Developer')),
302 dict(name="Samsul Ma'arif", title=N_('Developer')),
303 dict(name='Sebastian Brass', title=N_('Developer')),
304 dict(name='Vaibhav Sagar', title=N_('Developer')),
305 dict(name='Ved Vyas', title=N_('Developer')),
306 dict(name='Voicu Hodrea', title=N_('Developer')),
307 dict(name='Wesley Wong', title=N_('Developer')),
308 dict(name='Wolfgang Ocker', title=N_('Developer')),
309 dict(name='Zhang Han', title=N_('Developer')),
310 dict(name='beauxq', title=N_('Developer')),
312 bug_url = 'https://github.com/git-cola/git-cola/issues'
313 bug_link = link(bug_url, bug_url)
314 prelude = N_('''
315 <br>
316 Please use %(bug_link)s to report issues.
317 <br>
318 ''') % locals()
320 return contributors_text(authors, prelude=prelude)
323 def translators_text():
324 palette = QtGui.QPalette()
325 contact = N_('Email contributor')
326 email = lambda addr: mailto(addr, contact, palette)
328 translators = (
329 # See the `generate-about` script in the "todo" branch.
330 # vim :read! ./Meta/generate-about --translators
331 dict(name='V字龍(Vdragon)',
332 title=N_('Traditional Chinese (Taiwan) translation'),
333 email=mailto('Vdragon.Taiwan@gmail.com', contact, palette)),
334 dict(name='Pavel Rehak',
335 title=N_('Czech translation')),
336 dict(name='Vitor Lobo',
337 title=N_('Brazilian translation')),
338 dict(name='Zhang Han',
339 title=N_('Simplified Chinese translation')),
340 dict(name='Igor Kopach',
341 title=N_('Ukranian translation')),
342 dict(name='Barış ÇELİK',
343 title=N_('Turkish translation')),
344 dict(name='Minarto Margoliono',
345 title=N_('Indonesian translation')),
346 dict(name='Sven Claussner',
347 title=N_('German translation')),
348 dict(name='Vaiz',
349 title=N_('Russian translation')),
350 dict(name='Guo Yunhe',
351 title=N_('Simplified Chinese translation')),
352 dict(name='Kai Krakow',
353 title=N_('German translation')),
354 dict(name='Louis Rousseau',
355 title=N_('French translation')),
356 dict(name='Mickael Albertus',
357 title=N_('French translation')),
358 dict(name='Peter Dave Hello',
359 title=N_('Traditional Chinese (Taiwan) translation')),
360 dict(name='Pilar Molina Lopez',
361 title=N_('Spanish translation')),
362 dict(name="Samsul Ma'arif",
363 title=N_('Indonesian translation')),
364 dict(name='Zeioth',
365 title=N_('Spanish translation')),
366 dict(name='balping',
367 title=N_('Hungarian translation')),
368 dict(name='Łukasz Wojniłowicz',
369 title=N_('Polish translation')),
372 bug_url = 'https://github.com/git-cola/git-cola/issues'
373 bug_link = link(bug_url, bug_url)
375 prelude = N_('''
376 <br>
377 Git Cola has been translated into different languages thanks
378 to the help of the individuals listed below.
380 <br>
382 Translation is approximate. If you find a mistake,
383 please let us know by opening an issue on Github:
384 </p>
387 %(bug_link)s
388 </p>
390 <br>
392 We invite you to participate in translation by adding or updating
393 a translation and opening a pull request.
394 </p>
396 <br>
398 ''') % locals()
399 return contributors_text(translators, prelude=prelude)
402 def show_shortcuts():
403 hotkeys_html = resources.doc(N_('hotkeys.html'))
404 try:
405 from qtpy import QtWebEngineWidgets
406 except (ImportError, qtpy.PythonQtError):
407 # redhat disabled QtWebKit in their qt build but don't punish the users
408 webbrowser.open_new_tab('file://' + hotkeys_html)
409 return
411 html = core.read(hotkeys_html)
413 parent = qtutils.active_window()
414 widget = QtWidgets.QDialog()
415 widget.setWindowModality(Qt.WindowModal)
416 widget.setWindowTitle(N_('Shortcuts'))
418 web = QtWebEngineWidgets.QWebEngineView(parent)
419 web.setHtml(html)
421 layout = qtutils.hbox(defs.no_margin, defs.spacing, web)
422 widget.setLayout(layout)
423 widget.resize(800, min(parent.height(), 600))
424 qtutils.add_action(widget, N_('Close'), widget.accept,
425 hotkeys.QUESTION, *hotkeys.ACCEPT)
426 widget.show()
427 widget.exec_()