diff --git a/.gitignore b/.gitignore
index aaa1491f..8c567d9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ __pycache__/
/docs/captures/
/share/logitech_icons/
+/share/locale/
diff --git a/MANIFEST.in b/MANIFEST.in
index 73994f1c..1b699cdc 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,3 @@
include COPYRIGHT COPYING README.md ChangeLog
recursive-include rules.d *
+recursive-include share/locale *
diff --git a/lib/hidapi/hidconsole.py b/lib/hidapi/hidconsole.py
index e159411a..d76431e5 100644
--- a/lib/hidapi/hidconsole.py
+++ b/lib/hidapi/hidconsole.py
@@ -81,7 +81,7 @@ def _print(marker, data, scroll=False):
def _error(text, scroll=False):
- _print("!!", text, scroll)
+ _print('!!', text, scroll)
def _continuous_read(handle, timeout=2000):
@@ -93,7 +93,7 @@ def _continuous_read(handle, timeout=2000):
break
assert reply is not None
if reply:
- _print(">>", reply, True)
+ _print('>>', reply, True)
def _validate_input(line, hidpp=False):
@@ -170,10 +170,10 @@ def _open(args):
def _parse_arguments():
import argparse
arg_parser = argparse.ArgumentParser()
- arg_parser.add_argument('--history', help='history file (default ~/.hidconsole-history)')
- arg_parser.add_argument('--hidpp', action='store_true', help='ensure input data is a valid HID++ request')
- arg_parser.add_argument('device', nargs='?', help='linux device to connect to (/dev/hidrawX); '
- 'may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver')
+ arg_parser.add_argument('--history', help="history file (default ~/.hidconsole-history)")
+ arg_parser.add_argument('--hidpp', action='store_true', help="ensure input data is a valid HID++ request")
+ arg_parser.add_argument('device', nargs='?', help="linux device to connect to (/dev/hidrawX); "
+ "may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver")
return arg_parser.parse_args()
@@ -187,7 +187,7 @@ def main():
import readline
if args.history is None:
import os.path
- args.history = os.path.join(os.path.expanduser("~"), ".hidconsole-history")
+ args.history = os.path.join(os.path.expanduser('~'), '.hidconsole-history')
try:
readline.read_history_file(args.history)
except:
@@ -215,7 +215,7 @@ def main():
if data is None:
continue
- _print("<<", data)
+ _print('<<', data)
_hid.write(handle, data)
# wait for some kind of reply
if args.hidpp and not interactive:
diff --git a/lib/logitech_receiver/descriptors.py b/lib/logitech_receiver/descriptors.py
index e5af818a..06d5ae88 100644
--- a/lib/logitech_receiver/descriptors.py
+++ b/lib/logitech_receiver/descriptors.py
@@ -178,7 +178,7 @@ _D('Wireless Mouse M510', protocol=1.0, wpid='1025',
],
)
_D('Couch Mouse M515', protocol=2.0)
-_D('Wireless Mouse M525', protocol=2.0)
+_D('Wireless Mouse M525', protocol=2.0, wpid='4013')
_D('Touch Mouse M600', protocol=2.0, wpid='401A')
_D('Marathon Mouse M705', protocol=1.0, wpid='101B',
registers=(_R.battery_charge, ),
diff --git a/lib/solaar/gtk.py b/lib/solaar/gtk.py
index 8adf706d..a1e426df 100644
--- a/lib/solaar/gtk.py
+++ b/lib/solaar/gtk.py
@@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from solaar import __version__, NAME
+import solaar.i18n as _i18n
#
#
@@ -23,7 +24,7 @@ def _parse_arguments():
import argparse
arg_parser = argparse.ArgumentParser(prog=NAME.lower())
arg_parser.add_argument('-d', '--debug', action='count', default=0,
- help='print logging messages, for debugging purposes (may be repeated for extra verbosity)')
+ help="print logging messages, for debugging purposes (may be repeated for extra verbosity)")
arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__)
args = arg_parser.parse_args()
@@ -36,6 +37,9 @@ def _parse_arguments():
logging.root.addHandler(logging.NullHandler())
logging.root.setLevel(logging.ERROR)
+ if logging.root.isEnabledFor(logging.INFO):
+ logging.info("language %s (%s), translations path %s", _i18n.language, _i18n.encoding, _i18n.path)
+
return args
@@ -63,7 +67,7 @@ def main():
listener.stop_all()
except Exception as e:
import sys
- sys.exit("%s: error: %s" % (NAME.lower(), e))
+ sys.exit('%s: error: %s' % (NAME.lower(), e))
if __name__ == '__main__':
diff --git a/lib/solaar/i18n.py b/lib/solaar/i18n.py
new file mode 100644
index 00000000..ba558676
--- /dev/null
+++ b/lib/solaar/i18n.py
@@ -0,0 +1,50 @@
+#
+#
+#
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from solaar import NAME as _NAME
+
+#
+#
+#
+
+def _find_locale_path(lc_domain):
+ import os.path as _path
+
+ import sys as _sys
+ prefix_share = _path.normpath(_path.join(_path.realpath(_sys.path[0]), '..'))
+ src_share = _path.normpath(_path.join(_path.realpath(_sys.path[0]), '..', 'share'))
+ del _sys
+
+ from glob import glob as _glob
+
+ for location in prefix_share, src_share:
+ mo_files = _glob(_path.join(location, 'locale', '*', 'LC_MESSAGES', lc_domain + '.mo'))
+ if mo_files:
+ return _path.join(location, 'locale')
+
+ # del _path
+
+
+import locale
+locale.setlocale(locale.LC_ALL, '')
+language, encoding = locale.getlocale()
+del locale
+
+_LOCALE_DOMAIN = _NAME.lower()
+path = _find_locale_path(_LOCALE_DOMAIN)
+
+import gettext
+
+gettext.bindtextdomain(_LOCALE_DOMAIN, path)
+gettext.textdomain(_LOCALE_DOMAIN)
+gettext.install(_LOCALE_DOMAIN)
+
+try:
+ unicode
+ def _(x):
+ return gettext.gettext(x).decode('UTF-8')
+except:
+ _ = gettext.gettext
diff --git a/lib/solaar/listener.py b/lib/solaar/listener.py
index ac82fdcf..8417d869 100644
--- a/lib/solaar/listener.py
+++ b/lib/solaar/listener.py
@@ -8,6 +8,8 @@ from logging import getLogger, INFO as _INFO
_log = getLogger(__name__)
del getLogger
+
+from solaar.i18n import _
from . import configuration
from logitech_receiver import (
Receiver,
@@ -75,7 +77,7 @@ class ReceiverListener(_listener.EventsListener):
# make sure to clean up in _all_listeners
_all_listeners.pop(r.path, None)
- r.status = 'The receiver was unplugged.'
+ r.status = _("The receiver was unplugged.")
if r:
try:
r.close()
@@ -262,8 +264,7 @@ _error_callback = None
def setup_scanner(status_changed_callback, error_callback):
global _status_callback, _error_callback
- if _status_callback:
- raise Exception("scanner was already set-up")
+ assert _status_callback is None, 'scanner was already set-up'
_status_callback = status_changed_callback
_error_callback = error_callback
diff --git a/lib/solaar/ui/__init__.py b/lib/solaar/ui/__init__.py
index 7fc4656c..b99ab445 100644
--- a/lib/solaar/ui/__init__.py
+++ b/lib/solaar/ui/__init__.py
@@ -10,12 +10,16 @@ _log = getLogger(__name__)
del getLogger
from gi.repository import GLib, Gtk
-GLib.threads_init()
+
+
+from solaar.i18n import _
#
#
#
+GLib.threads_init()
+
def _init_application():
app = Gtk.Application.new('io.github.pwr.solaar', 0)
# not sure this is necessary...
@@ -43,17 +47,15 @@ def _error_dialog(reason, object):
_log.error("error: %s %s", reason, object)
if reason == 'permissions':
- title = 'Permissions error'
- text = ('Found a Logitech Receiver (%s), but did not have permission to open it.\n'
- '\n'
- 'If you\'ve just installed Solaar, try removing the receiver\n'
- 'and plugging it back in.' % object)
+ title = _("Permissions error")
+ text = _("Found a Logitech Receiver (%s), but did not have permission to open it.") % object + \
+ '\n\n' + \
+ _("If you've just installed Solaar, try removing the receiver and plugging it back in.")
elif reason == 'unpair':
- title = 'Unpairing failed'
- text = ('Failed to unpair %s from %s.\n'
- '\n'
- 'The receiver returned an error, with no further details.'
- % (object.name, object.receiver.name))
+ title = _("Unpairing failed")
+ text = _("Failed to unpair %s from %s.") % (object.name, object.receiver.name) + \
+ '\n\n' + \
+ _("The receiver returned an error, with no further details.")
else:
raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object)
diff --git a/lib/solaar/ui/about.py b/lib/solaar/ui/about.py
index 3f216058..42f8512e 100644
--- a/lib/solaar/ui/about.py
+++ b/lib/solaar/ui/about.py
@@ -1,4 +1,4 @@
-#
+# -*- coding: UTF-8 -*-
#
#
from __future__ import absolute_import, division, print_function, unicode_literals
@@ -6,7 +6,11 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from gi.repository import Gtk
from solaar import __version__, NAME
+from solaar.i18n import _
+#
+#
+#
_dialog = None
@@ -16,22 +20,22 @@ def _create():
about.set_program_name(NAME)
about.set_version(__version__)
- about.set_comments('Shows status of devices connected\nto a Logitech Unifying Receiver.')
+ about.set_comments(_("Shows status of devices connected\nthrough wireless Logitech receivers."))
about.set_logo_icon_name(NAME.lower())
- about.set_copyright(b'\xC2\xA9'.decode('utf-8') + ' 2012-2013 Daniel Pavel')
+ about.set_copyright('© 2012-2013 Daniel Pavel')
about.set_license_type(Gtk.License.GPL_2_0)
about.set_authors(('Daniel Pavel http://github.com/pwr',))
try:
- about.add_credit_section('GUI design', ('Julien Gascard',))
- about.add_credit_section('Testing', (
+ about.add_credit_section(_("GUI design"), ('Julien Gascard', 'Daniel Pavel'))
+ about.add_credit_section(_("Testing"), (
'Douglas Wagner',
'Julien Gascard',
'Peter Wu http://www.lekensteyn.nl/logitech-unifying.html',
))
- about.add_credit_section('Logitech documentation', (
+ about.add_credit_section(_("Logitech documentation"), (
'Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower',
'Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28',
))
@@ -44,6 +48,10 @@ def _create():
import logging
logging.exception("failed to fully create the about dialog")
+ about.set_translator_credits('\n'.join((
+ 'Daniel Pavel (română)',
+ )))
+
about.set_website('http://pwr.github.io/Solaar/')
about.set_website_label(NAME)
diff --git a/lib/solaar/ui/action.py b/lib/solaar/ui/action.py
index 30228c60..ac1b5492 100644
--- a/lib/solaar/ui/action.py
+++ b/lib/solaar/ui/action.py
@@ -10,21 +10,28 @@ from gi.repository import Gtk, Gdk
# _log = getLogger(__name__)
# del getLogger
+
+from solaar.i18n import _
+
#
#
#
-def make(name, label, function, *args):
+def make(name, label, function, stock_id=None, *args):
action = Gtk.Action(name, label, label, None)
action.set_icon_name(name)
+ if stock_id is not None:
+ action.set_stock_id(stock_id)
if function:
action.connect('activate', function, *args)
return action
-def make_toggle(name, label, function, *args):
+def make_toggle(name, label, function, stock_id=None, *args):
action = Gtk.ToggleAction(name, label, label, None)
action.set_icon_name(name)
+ if stock_id is not None:
+ action.set_stock_id(stock_id)
action.connect('activate', function, *args)
return action
@@ -43,7 +50,7 @@ def make_toggle(name, label, function, *args):
from .about import show_window as _show_about_window
from solaar import NAME
-about = make('help-about', 'About ' + NAME, _show_about_window)
+about = make('help-about', _("About") + ' ' + NAME, _show_about_window, stock_id=Gtk.STOCK_ABOUT)
#
#
@@ -70,10 +77,10 @@ def unpair(window, device):
qdialog = Gtk.MessageDialog(window, 0,
Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
- "Unpair device\n%s ?" % device.name)
+ _("Unpair") + ' ' + device.name + ' ?')
qdialog.set_icon_name('remove')
qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
- qdialog.add_button('Unpair', Gtk.ResponseType.ACCEPT)
+ qdialog.add_button(_("Unpair"), Gtk.ResponseType.ACCEPT)
choice = qdialog.run()
qdialog.destroy()
if choice == Gtk.ResponseType.ACCEPT:
diff --git a/lib/solaar/ui/config_panel.py b/lib/solaar/ui/config_panel.py
index 4c047efd..e5eeec5a 100644
--- a/lib/solaar/ui/config_panel.py
+++ b/lib/solaar/ui/config_panel.py
@@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from gi.repository import Gtk, GLib
+from solaar.i18n import _
from solaar.ui import async as _ui_async
from logitech_receiver.settings import KIND as _SETTING_KIND
@@ -75,13 +76,14 @@ def _create_choice_control(setting):
def _create_sbox(s):
sbox = Gtk.HBox(homogeneous=False, spacing=6)
- sbox.pack_start(Gtk.Label(s.label), False, False, 0)
+ label_text = _(s.label)
+ sbox.pack_start(Gtk.Label(label_text), False, False, 0)
spinner = Gtk.Spinner()
- spinner.set_tooltip_text('Working...')
+ spinner.set_tooltip_text(_("Working") + '...')
failed = Gtk.Image.new_from_icon_name('dialog-warning', Gtk.IconSize.SMALL_TOOLBAR)
- failed.set_tooltip_text('Failed to read value from the device.')
+ failed.set_tooltip_text(_("Read/write operation failed."))
if s.kind == _SETTING_KIND.toggle:
control = _create_toggle_control(s)
@@ -98,7 +100,8 @@ def _create_sbox(s):
sbox.pack_end(failed, False, False, 0)
if s.description:
- sbox.set_tooltip_text(s.description)
+ description_text = _(s.description)
+ sbox.set_tooltip_text(description_text)
sbox.show_all()
spinner.start() # the first read will stop it
diff --git a/lib/solaar/ui/notify.py b/lib/solaar/ui/notify.py
index 2801caf2..2035406f 100644
--- a/lib/solaar/ui/notify.py
+++ b/lib/solaar/ui/notify.py
@@ -5,6 +5,12 @@
from __future__ import absolute_import, division, print_function, unicode_literals
+from solaar.i18n import _
+
+#
+#
+#
+
try:
# this import is allowed to fail, in which case the entire feature is unavailable
from gi.repository import Notify
@@ -89,8 +95,8 @@ try:
if n is None:
n = _notifications[summary] = Notify.Notification()
- message = reason or ('unpaired' if dev.status is None else
- (str(dev.status) or ('connected' if dev.status else 'offline')))
+ message = reason or (_("unpaired") if dev.status is None else
+ (str(dev.status) or (_("connected") if dev.status else _("offline"))))
# we need to use the filename here because the notifications daemon
# is an external application that does not know about our icon sets
diff --git a/lib/solaar/ui/pair_window.py b/lib/solaar/ui/pair_window.py
index 92600677..0c349a53 100644
--- a/lib/solaar/ui/pair_window.py
+++ b/lib/solaar/ui/pair_window.py
@@ -10,6 +10,8 @@ from logging import getLogger, DEBUG as _DEBUG
_log = getLogger(__name__)
del getLogger
+
+from solaar.i18n import _
from . import icons as _icons
from logitech_receiver.status import KEYS as _K
@@ -113,19 +115,15 @@ def _pairing_failed(assistant, receiver, error):
assistant.commit()
- header = 'Pairing failed: %s.' % error
+ header = _("Pairing failed") + ': ' + _(error) + '.'
if 'timeout' in str(error):
- text = ('Make sure your device is within range,\n'
- 'and it has a decent battery charge.')
+ text = _("Make sure your device is within range, and it has a decent battery charge.")
elif str(error) == 'device not supported':
- text = ('A new device was detected, but\n'
- 'it is not compatible with this receiver.')
+ text = _("A new device was detected, but it is not compatible with this receiver.")
elif 'many' in str(error):
- text = ('The receiver only supports\n'
- '%d paired device(s).')
+ text = _("The receiver only supports %d paired device(s).")
else:
- text = ('No further details are available\n'
- 'about the error.')
+ text = _("No further details are available about the error.")
_create_page(assistant, Gtk.AssistantPageType.SUMMARY, header, 'dialog-error', text)
assistant.next_page()
@@ -139,7 +137,7 @@ def _pairing_succeeded(assistant, receiver, device):
page = _create_page(assistant, Gtk.AssistantPageType.SUMMARY)
- header = Gtk.Label('Found a new device:')
+ header = Gtk.Label(_("Found a new device") + ':')
header.set_alignment(0.5, 0)
page.pack_start(header, False, False, 0)
@@ -164,7 +162,7 @@ def _pairing_succeeded(assistant, receiver, device):
if assistant.is_drawable():
if device.status.get(_K.LINK_ENCRYPTED) == False:
hbox.pack_start(Gtk.Image.new_from_icon_name('security-low', Gtk.IconSize.MENU), False, False, 0)
- hbox.pack_start(Gtk.Label('The wireless link is not encrypted!'), False, False, 0)
+ hbox.pack_start(Gtk.Label(_("The wireless link is not encrypted") + '!'), False, False, 0)
hbox.show_all()
else:
return True
@@ -181,7 +179,7 @@ def create(receiver):
assert receiver.kind is None
assistant = Gtk.Assistant()
- assistant.set_title(receiver.name + ': pair new device')
+ assistant.set_title(receiver.name + ': ' + _("pair new device"))
assistant.set_icon_name('list-add')
assistant.set_size_request(400, 240)
@@ -189,8 +187,8 @@ def create(receiver):
assistant.set_role('pair-device')
page_intro = _create_page(assistant, Gtk.AssistantPageType.PROGRESS,
- 'Turn on the device you want to pair.', 'preferences-desktop-peripherals',
- 'If the device is already turned on,\nturn if off and on again.')
+ _("Turn on the device you want to pair."), 'preferences-desktop-peripherals',
+ _("If the device is already turned on,\nturn if off and on again."))
spinner = Gtk.Spinner()
spinner.set_visible(True)
page_intro.pack_end(spinner, True, True, 24)
diff --git a/lib/solaar/ui/tray.py b/lib/solaar/ui/tray.py
index 7e28d412..33709639 100644
--- a/lib/solaar/ui/tray.py
+++ b/lib/solaar/ui/tray.py
@@ -13,7 +13,9 @@ from time import time as _timestamp
from gi.repository import Gtk, GLib
from gi.repository.Gdk import ScrollDirection
+
from solaar import NAME
+from solaar.i18n import _
from logitech_receiver.status import KEYS as _K
from . import icons as _icons
from .window import popup as _window_popup, toggle as _window_toggle
@@ -35,14 +37,14 @@ def _create_menu(quit_handler):
# per-device menu entries will be generated as-needed
- no_receiver = Gtk.MenuItem.new_with_label('No receiver found')
+ no_receiver = Gtk.MenuItem.new_with_label(_("No Logitech receiver found"))
no_receiver.set_sensitive(False)
menu.append(no_receiver)
menu.append(Gtk.SeparatorMenuItem.new())
from .action import about, make
menu.append(about.create_menu_item())
- menu.append(make('application-exit', 'Quit', quit_handler).create_menu_item())
+ menu.append(make('application-exit', _("Quit"), quit_handler, stock_id=Gtk.STOCK_QUIT).create_menu_item())
del about, make
menu.show_all()
@@ -249,7 +251,7 @@ except ImportError:
def _generate_tooltip_lines():
if not _devices_info:
- yield '%s: no receivers' % NAME
+ yield '%s: ' % NAME + _("no receiver")
return
yield '%s' % NAME
@@ -265,12 +267,12 @@ def _generate_tooltip_lines():
if status:
yield '\t%s' % p
else:
- yield '\t%s (offline)' % p
+ yield '\t%s (' % p + _("offline") + ')'
else:
if status:
- yield '%s no status' % name
+ yield '%s (' % name + _("no status") + ')'
else:
- yield '%s (offline)' % name
+ yield '%s (' % name + _("offline") + ')'
yield ''
diff --git a/lib/solaar/ui/window.py b/lib/solaar/ui/window.py
index 6b4c0899..fc91af52 100644
--- a/lib/solaar/ui/window.py
+++ b/lib/solaar/ui/window.py
@@ -1,4 +1,4 @@
-#
+# -*- coding: UTF-8 -*-
#
#
@@ -11,7 +11,9 @@ del getLogger
from gi.repository import Gtk, Gdk, GLib
from gi.repository.GObject import TYPE_PYOBJECT
+
from solaar import NAME
+from solaar.i18n import _
# from solaar import __version__ as VERSION
from solaar.ui import async as _ui_async
from logitech_receiver import hidpp10 as _hidpp10
@@ -38,21 +40,21 @@ _TREE_SEPATATOR = (None, 0, False, None, None, None, None, None)
assert len(_TREE_SEPATATOR) == len(_COLUMN_TYPES)
assert len(_COLUMN_TYPES) == len(_COLUMN)
-_TOOLTIP_LINK_SECURE = 'The wireless link between this device and its receiver is encrypted.'
-_TOOLTIP_LINK_INSECURE = ('The wireless link between this device and its receiver is not encrypted.\n'
- '\n'
- 'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n'
- '\n'
- 'It is, however, a major security issue for text-input devices (keyboards, numpads),\n'
- 'because typed text can be sniffed inconspicuously by 3rd parties within range.')
+_TOOLTIP_LINK_SECURE = _("The wireless link between this device and its receiver is encrypted.")
+_TOOLTIP_LINK_INSECURE = _("The wireless link between this device and its receiver is not encrypted.\n"
+ "\n"
+ "For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n"
+ "\n"
+ "It is, however, a major security issue for text-input devices (keyboards, numpads),\n"
+ "because typed text can be sniffed inconspicuously by 3rd parties within range.")
_UNIFYING_RECEIVER_TEXT = (
- 'No paired devices.\n\nUp to %d devices can be paired to this receiver.',
- '%d paired device(s).\n\nUp to %d devices can be paired to this receiver.',
+ _("No device paired") + '.\n\n' + _("Up to %d devices can be paired to this receiver") + '.',
+ '%d ' + _("paired devices") + '\n\n' + _("Up to %d devices can be paired to this receiver") + '.',
)
_NANO_RECEIVER_TEXT = (
- 'No paired device.\n\n ',
- ' \n\nOnly one device can be paired to this receiver.',
+ _("No device paired") + '.\n\n ',
+ ' \n\n' + _("Only one device can be paired to this receiver") + '.',
)
#
@@ -93,7 +95,7 @@ def _create_receiver_panel():
p._count.set_alignment(0, 0.5)
p.pack_start(p._count, True, True, 0)
- p._scanning = Gtk.Label('Scanning...')
+ p._scanning = Gtk.Label(_("Scanning") + '...')
p._spinner = Gtk.Spinner()
bp = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8)
@@ -126,14 +128,14 @@ def _create_device_panel():
return b
- p._battery = _status_line('Battery')
+ p._battery = _status_line(_("Battery"))
p.pack_start(p._battery, False, False, 0)
- p._secure = _status_line('Wireless Link')
+ p._secure = _status_line(_("Wireless Link"))
p._secure._icon.set_from_icon_name('dialog-warning', _INFO_ICON_SIZE)
p.pack_start(p._secure, False, False, 0)
- p._lux = _status_line('Lighting')
+ p._lux = _status_line(_("Lighting"))
p.pack_start(p._lux, False, False, 0)
p._config = _config_panel.create()
@@ -162,7 +164,7 @@ def _create_buttons_box():
bb.set_layout(Gtk.ButtonBoxStyle.END)
bb._details = _new_button(None, 'dialog-information', _SMALL_BUTTON_ICON_SIZE,
- tooltip='Show Technical Details', toggle=True, clicked=_update_details)
+ tooltip=_("Show Technical Details"), toggle=True, clicked=_update_details)
bb.add(bb._details)
bb.set_child_secondary(bb._details, True)
bb.set_child_non_homogeneous(bb._details, True)
@@ -175,7 +177,7 @@ def _create_buttons_box():
assert receiver.kind is None
_action.pair(_window, receiver)
- bb._pair = _new_button('Pair new device', 'list-add', clicked=_pair_new_device)
+ bb._pair = _new_button(_("Pair new device"), 'list-add', clicked=_pair_new_device)
bb.add(bb._pair)
def _unpair_current_device(trigger):
@@ -186,7 +188,7 @@ def _create_buttons_box():
assert device.kind is not None
_action.unpair(_window, device)
- bb._unpair = _new_button('Unpair', 'edit-delete', clicked=_unpair_current_device)
+ bb._unpair = _new_button(_("Unpair"), 'edit-delete', clicked=_unpair_current_device)
bb.add(bb._unpair)
return bb
@@ -194,7 +196,7 @@ def _create_buttons_box():
def _create_empty_panel():
p = Gtk.Label()
- p.set_markup('Select a device')
+ p.set_markup('' + _("Select a device") + '')
p.set_sensitive(False)
return p
@@ -299,7 +301,7 @@ def _create_window_layout():
panel.pack_start(_info, True, True, 0)
panel.pack_start(_empty, True, True, 0)
- about_button = _new_button('About ' + NAME, 'help-about',
+ about_button = _new_button(_("About") + ' ' + NAME, 'help-about',
icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window)
bottom_buttons_box = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL)
@@ -491,39 +493,39 @@ def _update_details(button):
# cached, and involves no HID++ calls.
if device.kind is None:
- yield ('Path', device.path)
+ yield (_("Path"), device.path)
# 046d is the Logitech vendor id
- yield ('USB id', '046d:' + device.product_id)
+ yield (_("USB id"), '046d:' + device.product_id)
if read_all:
- yield ('Serial', device.serial)
+ yield (_("Serial"), device.serial)
else:
- yield ('Serial', '...')
+ yield (_("Serial"), '...')
else:
# yield ('Codename', device.codename)
- yield ('Index', device.number)
- yield ('Wireless PID', device.wpid)
+ yield (_("Index"), device.number)
+ yield (_("Wireless PID"), device.wpid)
hid_version = device.protocol
- yield ('Protocol', 'HID++ %1.1f' % hid_version if hid_version else 'unknown')
+ yield (_("Protocol"), 'HID++ %1.1f' % hid_version if hid_version else 'unknown')
if read_all and device.polling_rate:
- yield ('Polling rate', '%d ms (%dHz)' % (device.polling_rate, 1000 // device.polling_rate))
+ yield (_("Polling rate"), '%d ms (%dHz)' % (device.polling_rate, 1000 // device.polling_rate))
if read_all or not device.online:
- yield ('Serial', device.serial)
+ yield (_("Serial"), device.serial)
else:
- yield ('Serial', '...')
+ yield (_("Serial"), '...')
if read_all:
for fw in list(device.firmware):
yield (' ' + str(fw.kind), (fw.name + ' ' + fw.version).strip())
elif device.kind is None or device.online:
- yield (' Firmware', '...')
+ yield (' %s' % _("Firmware"), '...')
flag_bits = device.status.get(_K.NOTIFICATION_FLAGS)
if flag_bits is not None:
- flag_names = ('(none)',) if flag_bits == 0 else _hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits)
- yield ('Notifications', ('\n%15s' % ' ').join(flag_names))
+ flag_names = ('(%s)' % _("none"),) if flag_bits == 0 else _hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits)
+ yield (_("Notifications"), ('\n%15s' % ' ').join(flag_names))
def _set_details(text):
_details._text.set_markup(text)
@@ -605,7 +607,7 @@ def _update_device_panel(device, panel, buttons, full=False):
panel._battery._icon.set_sensitive(False)
panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE)
panel._battery._text.set_sensitive(True)
- panel._battery._text.set_markup('unknown')
+ panel._battery._text.set_markup('%s' % _("unknown"))
else:
charging = device.status.get(_K.BATTERY_CHARGING)
icon_name = _icons.battery(battery_level, charging)
@@ -618,25 +620,25 @@ def _update_device_panel(device, panel, buttons, full=False):
text = '%d%%' % battery_level
if is_online:
if charging:
- text += ' (charging)'
+ text += ' (%s)' % _("charging")
else:
- text += ' (last known)'
+ text += ' (%s)' % _("last known")
panel._battery._text.set_sensitive(is_online)
panel._battery._text.set_markup(text)
if is_online:
not_secure = device.status.get(_K.LINK_ENCRYPTED) == False
if not_secure:
- panel._secure._text.set_text('not encrypted')
+ panel._secure._text.set_text(_("not encrypted"))
panel._secure._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE)
panel._secure.set_tooltip_text(_TOOLTIP_LINK_INSECURE)
else:
- panel._secure._text.set_text('encrypted')
+ panel._secure._text.set_text(_("encrypted"))
panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE)
panel._secure.set_tooltip_text(_TOOLTIP_LINK_SECURE)
panel._secure._icon.set_visible(True)
else:
- panel._secure._text.set_markup('offline')
+ panel._secure._text.set_markup('%s' % _("offline"))
panel._secure._icon.set_visible(False)
panel._secure.set_tooltip_text('')
@@ -646,7 +648,7 @@ def _update_device_panel(device, panel, buttons, full=False):
panel._lux.set_visible(False)
else:
panel._lux._icon.set_from_icon_name(_icons.lux(light_level), _INFO_ICON_SIZE)
- panel._lux._text.set_text('%d lux' % light_level)
+ panel._lux._text.set_text('%d %s' % (light_level, _("lux")))
panel._lux.set_visible(True)
else:
panel._lux.set_visible(False)
diff --git a/po/README b/po/README
new file mode 100644
index 00000000..e69de29b
diff --git a/po/ro.po b/po/ro.po
new file mode 100644
index 00000000..34d297a6
--- /dev/null
+++ b/po/ro.po
@@ -0,0 +1,292 @@
+# Romanian translations for solaar package.
+# Copyright (C) 2013 THE solaar'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the solaar package.
+# Automatically generated, 2013.
+#
+msgid ""
+msgstr "Project-Id-Version: solaar 0.8.99.12\n"
+ "Report-Msgid-Bugs-To: daniel.pavel@gmail.com\n"
+ "POT-Creation-Date: 2013-07-15 17:11+0200\n"
+ "PO-Revision-Date: 2013-07-13 16:27+0200\n"
+ "Last-Translator: Daniel Pavel\n"
+ "Language-Team: none\n"
+ "Language: ro\n"
+ "MIME-Version: 1.0\n"
+ "Content-Type: text/plain; charset=UTF-8\n"
+ "Content-Transfer-Encoding: 8bit\n"
+ "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n"
+ "%100 < 20)) ? 1 : 2;\n"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/listener.py:80
+msgid "The receiver was unplugged."
+msgstr "Receptor deconectat."
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:50
+msgid "Permissions error"
+msgstr "Eroare de permisiuni"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:51
+#, python-format
+msgid "Found a Logitech Receiver (%s), but did not have permission to open "
+ "it."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:53
+msgid "If you've just installed Solaar, try removing the receiver and "
+ "plugging it back in."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:55
+msgid "Unpairing failed"
+msgstr "Deconectare eșuată"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:56
+#, python-format
+msgid "Failed to unpair %s from %s."
+msgstr "Deconectarea %s de la %s a eșuat."
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:58
+msgid "The receiver returned an error, with no further details."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:23
+msgid "Shows status of devices connected\n"
+ "through wireless Logitech receivers."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:32
+msgid "GUI design"
+msgstr "Interfață grafica"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:33
+msgid "Testing"
+msgstr "Testare"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:38
+msgid "Logitech documentation"
+msgstr "Documentație Logitech"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/action.py:53
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:304
+msgid "About"
+msgstr "Despre"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/action.py:80
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/action.py:83
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:191
+msgid "Unpair"
+msgstr "Deconectează"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/config_panel.py:83
+msgid "Working"
+msgstr "Prelucrez"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/config_panel.py:86
+msgid "Read/write operation failed."
+msgstr "Operațiunea a eșuat."
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/notify.py:98
+msgid "unpaired"
+msgstr "deconectat(ă)"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/notify.py:99
+msgid "connected"
+msgstr "conectat(ă)"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/notify.py:99
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:270
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:275
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:641
+msgid "offline"
+msgstr "inactiv"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:118
+msgid "Pairing failed"
+msgstr "Conectare eșuată"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:120
+msgid "Make sure your device is within range, and it has a decent battery "
+ "charge."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:122
+msgid "A new device was detected, but it is not compatible with this "
+ "receiver."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:124
+#, python-format
+msgid "The receiver only supports %d paired device(s)."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:126
+msgid "No further details are available about the error."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:140
+msgid "Found a new device"
+msgstr "Dispozitiv detectat"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:165
+msgid "The wireless link is not encrypted"
+msgstr "Legătura fără fir nu este criptată"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:182
+msgid "pair new device"
+msgstr "conectează dispozitiv nou"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:190
+msgid "Turn on the device you want to pair."
+msgstr "Opriți dispozitivul pe care doriți să-l conectați."
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:191
+msgid "If the device is already turned on,\n"
+ "turn if off and on again."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:40
+msgid "No Logitech receiver found"
+msgstr "Nu am găsit nici un receptor Logitech"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:47
+msgid "Quit"
+msgstr "Ieșire"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:254
+msgid "no receiver"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:273
+msgid "no status"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:43
+msgid "The wireless link between this device and its receiver is encrypted."
+msgstr "Legătura fără fir este criptată."
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:44
+msgid "The wireless link between this device and its receiver is not "
+ "encrypted.\n"
+ "\n"
+ "For pointing devices (mice, trackballs, trackpads), this is a minor "
+ "security issue.\n"
+ "\n"
+ "It is, however, a major security issue for text-input devices "
+ "(keyboards, numpads),\n"
+ "because typed text can be sniffed inconspicuously by 3rd parties "
+ "within range."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:52
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:56
+msgid "No device paired"
+msgstr "Nici un dispozitiv conectat"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:52
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:53
+#, python-format
+msgid "Up to %d devices can be paired to this receiver"
+msgstr "Acest receptor suportă maxim %d dispozitive conectate"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:53
+msgid "paired devices"
+msgstr "dispozitive conectate"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:57
+msgid "Only one device can be paired to this receiver"
+msgstr "Acest receptor suportă un singur dispozitiv conectat"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:98
+msgid "Scanning"
+msgstr "Caut"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:131
+msgid "Battery"
+msgstr "Baterie"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:134
+msgid "Wireless Link"
+msgstr "Legatură fără fir"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:138
+msgid "Lighting"
+msgstr "Lumină"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:167
+msgid "Show Technical Details"
+msgstr "Detalii tehnice"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:180
+msgid "Pair new device"
+msgstr "Conectează dispozitiv nou"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:199
+msgid "Select a device"
+msgstr "Selectați un dispozitiv"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:496
+msgid "Path"
+msgstr "Cale"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:498
+msgid "USB id"
+msgstr "ID USB"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:501
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:503
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:515
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:517
+msgid "Serial"
+msgstr "Serial"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:507
+msgid "Index"
+msgstr "Index"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:508
+msgid "Wireless PID"
+msgstr "ID Produs"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:510
+msgid "Protocol"
+msgstr "Protocol"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:512
+msgid "Polling rate"
+msgstr "Rată _?_"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:523
+msgid "Firmware"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:527
+msgid "none"
+msgstr "nici una"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:528
+msgid "Notifications"
+msgstr "Notificări"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:610
+msgid "unknown"
+msgstr "necunoscut"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:623
+msgid "charging"
+msgstr "se încarcă"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:625
+msgid "last known"
+msgstr "ultima valoare"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:632
+msgid "not encrypted"
+msgstr "ne-criptată"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:636
+msgid "encrypted"
+msgstr "criptată"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:651
+msgid "lux"
+msgstr "lucși"
diff --git a/po/solaar.pot b/po/solaar.pot
new file mode 100644
index 00000000..7dcf259e
--- /dev/null
+++ b/po/solaar.pot
@@ -0,0 +1,291 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr "Project-Id-Version: solaar 0.9.1\n"
+ "Report-Msgid-Bugs-To: \n"
+ "POT-Creation-Date: 2013-07-15 17:11+0200\n"
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+ "Last-Translator: FULL NAME \n"
+ "Language-Team: LANGUAGE \n"
+ "Language: \n"
+ "MIME-Version: 1.0\n"
+ "Content-Type: text/plain; charset=UTF-8\n"
+ "Content-Transfer-Encoding: 8bit\n"
+
+#: /home/pwr/projects/solaar/master/lib/solaar/listener.py:80
+msgid "The receiver was unplugged."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:50
+msgid "Permissions error"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:51
+#, python-format
+msgid "Found a Logitech Receiver (%s), but did not have permission to open "
+ "it."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:53
+msgid "If you've just installed Solaar, try removing the receiver and "
+ "plugging it back in."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:55
+msgid "Unpairing failed"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:56
+#, python-format
+msgid "Failed to unpair %s from %s."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/__init__.py:58
+msgid "The receiver returned an error, with no further details."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:23
+msgid "Shows status of devices connected\n"
+ "through wireless Logitech receivers."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:32
+msgid "GUI design"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:33
+msgid "Testing"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/about.py:38
+msgid "Logitech documentation"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/action.py:53
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:304
+msgid "About"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/action.py:80
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/action.py:83
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:191
+msgid "Unpair"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/config_panel.py:83
+msgid "Working"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/config_panel.py:86
+msgid "Read/write operation failed."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/notify.py:98
+msgid "unpaired"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/notify.py:99
+msgid "connected"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/notify.py:99
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:270
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:275
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:641
+msgid "offline"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:118
+msgid "Pairing failed"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:120
+msgid "Make sure your device is within range, and it has a decent battery "
+ "charge."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:122
+msgid "A new device was detected, but it is not compatible with this "
+ "receiver."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:124
+#, python-format
+msgid "The receiver only supports %d paired device(s)."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:126
+msgid "No further details are available about the error."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:140
+msgid "Found a new device"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:165
+msgid "The wireless link is not encrypted"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:182
+msgid "pair new device"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:190
+msgid "Turn on the device you want to pair."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/pair_window.py:191
+msgid "If the device is already turned on,\n"
+ "turn if off and on again."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:40
+msgid "No Logitech receiver found"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:47
+msgid "Quit"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:254
+msgid "no receiver"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/tray.py:273
+msgid "no status"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:43
+msgid "The wireless link between this device and its receiver is encrypted."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:44
+msgid "The wireless link between this device and its receiver is not "
+ "encrypted.\n"
+ "\n"
+ "For pointing devices (mice, trackballs, trackpads), this is a minor "
+ "security issue.\n"
+ "\n"
+ "It is, however, a major security issue for text-input devices "
+ "(keyboards, numpads),\n"
+ "because typed text can be sniffed inconspicuously by 3rd parties "
+ "within range."
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:52
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:56
+msgid "No device paired"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:52
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:53
+#, python-format
+msgid "Up to %d devices can be paired to this receiver"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:53
+msgid "paired devices"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:57
+msgid "Only one device can be paired to this receiver"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:98
+msgid "Scanning"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:131
+msgid "Battery"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:134
+msgid "Wireless Link"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:138
+msgid "Lighting"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:167
+msgid "Show Technical Details"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:180
+msgid "Pair new device"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:199
+msgid "Select a device"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:496
+msgid "Path"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:498
+msgid "USB id"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:501
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:503
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:515
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:517
+msgid "Serial"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:507
+msgid "Index"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:508
+msgid "Wireless PID"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:510
+msgid "Protocol"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:512
+msgid "Polling rate"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:523
+msgid "Firmware"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:527
+msgid "none"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:528
+msgid "Notifications"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:610
+msgid "unknown"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:623
+msgid "charging"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:625
+msgid "last known"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:632
+msgid "not encrypted"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:636
+msgid "encrypted"
+msgstr ""
+
+#: /home/pwr/projects/solaar/master/lib/solaar/ui/window.py:651
+msgid "lux"
+msgstr ""
diff --git a/tools/po-compile.sh b/tools/po-compile.sh
new file mode 100755
index 00000000..bef6d6c2
--- /dev/null
+++ b/tools/po-compile.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+set -e
+
+cd "$(readlink -f "$(dirname "$0")/..")"
+
+find "$PWD/po" -type f -name '*.po' | \
+while read po_file; do
+ language="$(basename "$po_file")"
+ language="${language%.po}"
+ target="$PWD/share/locale/$language/LC_MESSAGES/solaar.mo"
+ /bin/mkdir --parents "$(dirname "$target")"
+ /usr/bin/msgfmt \
+ --check \
+ --output-file="$target" \
+ "$po_file"
+done
diff --git a/tools/po-update.sh b/tools/po-update.sh
new file mode 100755
index 00000000..50602c1c
--- /dev/null
+++ b/tools/po-update.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+if test -z "$1"; then
+ echo "Use: $0 "
+ exit 2
+fi
+LL_CC="$1"
+shift
+
+set -e
+
+cd "$(readlink -f "$(dirname "$0")/..")"
+
+VERSION=$(python setup.py --version)
+DOMAIN=$(python setup.py --name)
+
+SOURCE_FILES=$(/bin/mktemp --tmpdir $DOMAIN-po-update-XXXXXX)
+find "$PWD/lib" -name '*.py' >"$SOURCE_FILES"
+
+POT_DIR="$PWD/po"
+test -d "$POT_DIR"
+
+POT_FILE="$POT_DIR/$DOMAIN.pot"
+
+/usr/bin/xgettext \
+ --package-name "$DOMAIN" \
+ --package-version "$VERSION" \
+ --default-domain="$L_NAME" \
+ --language=Python --from-code=UTF-8 --files-from="$SOURCE_FILES" \
+ --no-escape --indent --add-location --sort-by-file \
+ --add-comments=I18N \
+ --output="$POT_FILE"
+
+/bin/sed --in-place --expression="s/charset=CHARSET/charset=UTF-8/" "$POT_FILE"
+
+PO_FILE="$POT_DIR/$LL_CC.po"
+
+test -r "$PO_FILE" || /usr/bin/msginit \
+ --no-translator --locale="$LL_CC" \
+ --input="$POT_FILE" \
+ --output-file="$PO_FILE"
+
+unfmt() {
+ local SOURCE="/usr/share/locale/$LL_CC/LC_MESSAGES/$1.mo"
+ local TARGET="$(mktemp --tmpdir $1-$LL_CC-XXXXXX.po)"
+ /usr/bin/msgunfmt \
+ --no-escape --indent \
+ --output-file="$TARGET" \
+ "$SOURCE"
+ echo "$TARGET"
+}
+
+/usr/bin/msgmerge \
+ --update --no-fuzzy-matching \
+ --no-escape --indent --add-location --sort-by-file \
+ --lang="$LL_CC" \
+ --compendium="$(unfmt gtk30)" \
+ --compendium="$(unfmt gtk30-properties)" \
+ "$PO_FILE" "$POT_FILE"
+
+# /bin/sed --in-place --expression="s/Language: \\\\n/Language: $L_NAME\\\\n/" "$PO_FILE"
+
+echo "Language file is $PO_FILE"