mirror of
https://github.com/sezanzeb/input-remapper
synced 2024-11-18 03:25:52 +00:00
Improve i18n (#288)
* Add Slovak translation for polkit message. * More strings can be translated now.
This commit is contained in:
parent
ee6b48ac32
commit
79e432e031
@ -24,11 +24,8 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import atexit
|
import atexit
|
||||||
import gettext
|
|
||||||
import locale
|
|
||||||
from inputremapper.configs.data import get_data_path
|
|
||||||
import os.path
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
from inputremapper.gui.gettext import _, LOCALE_DIR
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
@ -36,15 +33,6 @@ gi.require_version('GLib', '2.0')
|
|||||||
gi.require_version('GtkSource', '4')
|
gi.require_version('GtkSource', '4')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
|
|
||||||
APP_NAME = 'input-remapper'
|
|
||||||
LOCALE_DIR = os.path.join(get_data_path(), 'lang')
|
|
||||||
|
|
||||||
locale.bindtextdomain(APP_NAME, LOCALE_DIR)
|
|
||||||
locale.textdomain(APP_NAME)
|
|
||||||
|
|
||||||
translate = gettext.translation(APP_NAME, LOCALE_DIR, fallback=True)
|
|
||||||
_ = translate.gettext
|
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/Nuitka/Nuitka/issues/607#issuecomment-650217096
|
# https://github.com/Nuitka/Nuitka/issues/607#issuecomment-650217096
|
||||||
Gtk.init()
|
Gtk.init()
|
||||||
|
@ -23,6 +23,11 @@
|
|||||||
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import locale
|
||||||
|
import gettext
|
||||||
|
import os
|
||||||
|
from inputremapper.configs.data import get_data_path
|
||||||
|
from inputremapper.gui.gettext import _
|
||||||
|
|
||||||
from gi.repository import Gtk, GLib, Gdk
|
from gi.repository import Gtk, GLib, Gdk
|
||||||
|
|
||||||
@ -73,7 +78,7 @@ class SelectionLabel(Gtk.ListBoxRow):
|
|||||||
if combination:
|
if combination:
|
||||||
self.label.set_label(combination.beautify())
|
self.label.set_label(combination.beautify())
|
||||||
else:
|
else:
|
||||||
self.label.set_label("new entry")
|
self.label.set_label(_("new entry"))
|
||||||
|
|
||||||
def get_combination(self) -> EventCombination:
|
def get_combination(self) -> EventCombination:
|
||||||
return self.combination
|
return self.combination
|
||||||
@ -103,7 +108,7 @@ def ensure_everything_saved(func):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
SET_KEY_FIRST = "Set the key first"
|
SET_KEY_FIRST = _("Set the key first")
|
||||||
|
|
||||||
|
|
||||||
class Editor:
|
class Editor:
|
||||||
@ -374,7 +379,7 @@ class Editor:
|
|||||||
"""Add one empty row for a single mapped key."""
|
"""Add one empty row for a single mapped key."""
|
||||||
selection_label_listbox = self.get("selection_label_listbox")
|
selection_label_listbox = self.get("selection_label_listbox")
|
||||||
mapping_selection = SelectionLabel()
|
mapping_selection = SelectionLabel()
|
||||||
mapping_selection.set_label("new entry")
|
mapping_selection.set_label(_("new entry"))
|
||||||
mapping_selection.show_all()
|
mapping_selection.show_all()
|
||||||
selection_label_listbox.insert(mapping_selection, -1)
|
selection_label_listbox.insert(mapping_selection, -1)
|
||||||
|
|
||||||
@ -463,10 +468,11 @@ class Editor:
|
|||||||
"""Get a widget from the window"""
|
"""Get a widget from the window"""
|
||||||
return self.user_interface.builder.get_object(name)
|
return self.user_interface.builder.get_object(name)
|
||||||
|
|
||||||
def _on_recording_toggle_toggle(self, *_):
|
def _on_recording_toggle_toggle(self, *args):
|
||||||
"""Refresh useful usage information."""
|
"""Refresh useful usage information."""
|
||||||
if not self.get_recording_toggle().get_active():
|
if not self.get_recording_toggle().get_active():
|
||||||
return
|
return
|
||||||
|
|
||||||
self._reset_keycode_consumption()
|
self._reset_keycode_consumption()
|
||||||
reader.clear()
|
reader.clear()
|
||||||
if not self.user_interface.can_modify_preset():
|
if not self.user_interface.can_modify_preset():
|
||||||
@ -474,7 +480,7 @@ class Editor:
|
|||||||
# therefore the original keycode inaccessible
|
# therefore the original keycode inaccessible
|
||||||
logger.info("Cannot change keycodes while injecting")
|
logger.info("Cannot change keycodes while injecting")
|
||||||
self.user_interface.show_status(
|
self.user_interface.show_status(
|
||||||
CTX_ERROR, 'Use "Stop Injection" to stop before editing'
|
CTX_ERROR, _('Use "Stop Injection" to stop before editing')
|
||||||
)
|
)
|
||||||
self.get_recording_toggle().set_active(False)
|
self.get_recording_toggle().set_active(False)
|
||||||
|
|
||||||
@ -500,7 +506,7 @@ class Editor:
|
|||||||
"""Blocks until the user decided about an action."""
|
"""Blocks until the user decided about an action."""
|
||||||
confirm_delete = self.get("confirm-delete")
|
confirm_delete = self.get("confirm-delete")
|
||||||
|
|
||||||
text = f"Are you sure to delete this mapping?"
|
text = _("Are you sure to delete this mapping?")
|
||||||
self.get("confirm-delete-label").set_text(text)
|
self.get("confirm-delete-label").set_text(text)
|
||||||
|
|
||||||
confirm_delete.show()
|
confirm_delete.show()
|
||||||
@ -557,7 +563,10 @@ class Editor:
|
|||||||
if existing is not None:
|
if existing is not None:
|
||||||
existing = list(existing)
|
existing = list(existing)
|
||||||
existing[0] = re.sub(r"\s", "", existing[0])
|
existing[0] = re.sub(r"\s", "", existing[0])
|
||||||
msg = f'"{combination.beautify()}" already mapped to "{tuple(existing)}"'
|
msg = _('"%s" already mapped to "%s"') % (
|
||||||
|
combination.beautify(),
|
||||||
|
tuple(existing),
|
||||||
|
)
|
||||||
logger.info("%s %s", combination, msg)
|
logger.info("%s %s", combination, msg)
|
||||||
self.user_interface.show_status(CTX_KEYCODE, msg)
|
self.user_interface.show_status(CTX_KEYCODE, msg)
|
||||||
return True
|
return True
|
||||||
@ -565,10 +574,10 @@ class Editor:
|
|||||||
if combination.is_problematic():
|
if combination.is_problematic():
|
||||||
self.user_interface.show_status(
|
self.user_interface.show_status(
|
||||||
CTX_WARNING,
|
CTX_WARNING,
|
||||||
"ctrl, alt and shift may not combine properly",
|
_("ctrl, alt and shift may not combine properly"),
|
||||||
"Your system might reinterpret combinations "
|
_("Your system might reinterpret combinations ")
|
||||||
+ "with those after they are injected, and by doing so "
|
+ _("with those after they are injected, and by doing so ")
|
||||||
+ "break them.",
|
+ _("break them."),
|
||||||
)
|
)
|
||||||
|
|
||||||
# the newest_keycode is populated since the ui regularly polls it
|
# the newest_keycode is populated since the ui regularly polls it
|
||||||
|
34
inputremapper/gui/gettext.py
Normal file
34
inputremapper/gui/gettext.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# input-remapper - GUI for device specific keyboard mappings
|
||||||
|
# Copyright (C) 2022 sezanzeb <proxima@sezanzeb.de>
|
||||||
|
#
|
||||||
|
# This file is part of input-remapper.
|
||||||
|
#
|
||||||
|
# input-remapper is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# input-remapper is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import gettext
|
||||||
|
import locale
|
||||||
|
from inputremapper.configs.data import get_data_path
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
APP_NAME = "input-remapper"
|
||||||
|
LOCALE_DIR = os.path.join(get_data_path(), "lang")
|
||||||
|
|
||||||
|
locale.bindtextdomain(APP_NAME, LOCALE_DIR)
|
||||||
|
locale.textdomain(APP_NAME)
|
||||||
|
|
||||||
|
translate = gettext.translation(APP_NAME, LOCALE_DIR, fallback=True)
|
||||||
|
_ = translate.gettext
|
@ -26,6 +26,10 @@ import math
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import locale
|
||||||
|
import gettext
|
||||||
|
from inputremapper.configs.data import get_data_path
|
||||||
|
from inputremapper.gui.gettext import _
|
||||||
|
|
||||||
from evdev._ecodes import EV_KEY
|
from evdev._ecodes import EV_KEY
|
||||||
from gi.repository import Gtk, GtkSource, Gdk, GLib, GObject
|
from gi.repository import Gtk, GtkSource, Gdk, GLib, GObject
|
||||||
@ -118,7 +122,7 @@ def if_preset_selected(func):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
def on_close_about(about, _):
|
def on_close_about(about, arg):
|
||||||
"""Hide the about dialog without destroying it."""
|
"""Hide the about dialog without destroying it."""
|
||||||
about.hide()
|
about.hide()
|
||||||
return True
|
return True
|
||||||
@ -222,7 +226,7 @@ class UserInterface:
|
|||||||
self.button_left_warn = False
|
self.button_left_warn = False
|
||||||
|
|
||||||
if not is_helper_running():
|
if not is_helper_running():
|
||||||
self.show_status(CTX_ERROR, "The helper did not start")
|
self.show_status(CTX_ERROR, _("The helper did not start"))
|
||||||
|
|
||||||
def setup_timeouts(self):
|
def setup_timeouts(self):
|
||||||
"""Setup all GLib timeouts."""
|
"""Setup all GLib timeouts."""
|
||||||
@ -247,7 +251,7 @@ class UserInterface:
|
|||||||
|
|
||||||
def show_confirm_delete(self):
|
def show_confirm_delete(self):
|
||||||
"""Blocks until the user decided about an action."""
|
"""Blocks until the user decided about an action."""
|
||||||
text = f'Are you sure to delete preset "{self.preset_name}"?'
|
text = _("Are you sure to delete preset %s?") % self.preset_name
|
||||||
self.get("confirm-delete-label").set_text(text)
|
self.get("confirm-delete-label").set_text(text)
|
||||||
|
|
||||||
self.confirm_delete.show()
|
self.confirm_delete.show()
|
||||||
@ -255,7 +259,7 @@ class UserInterface:
|
|||||||
self.confirm_delete.hide()
|
self.confirm_delete.hide()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def on_key_press(self, _, event):
|
def on_key_press(self, arg, event):
|
||||||
"""To execute shortcuts.
|
"""To execute shortcuts.
|
||||||
|
|
||||||
This has nothing to do with the keycode reader.
|
This has nothing to do with the keycode reader.
|
||||||
@ -280,7 +284,7 @@ class UserInterface:
|
|||||||
if gdk_keycode == Gdk.KEY_Delete:
|
if gdk_keycode == Gdk.KEY_Delete:
|
||||||
self.on_restore_defaults_clicked()
|
self.on_restore_defaults_clicked()
|
||||||
|
|
||||||
def on_key_release(self, _, event):
|
def on_key_release(self, arg, event):
|
||||||
"""To execute shortcuts.
|
"""To execute shortcuts.
|
||||||
|
|
||||||
This has nothing to do with the keycode reader.
|
This has nothing to do with the keycode reader.
|
||||||
@ -322,7 +326,7 @@ class UserInterface:
|
|||||||
return self.builder.get_object(name)
|
return self.builder.get_object(name)
|
||||||
|
|
||||||
@ensure_everything_saved
|
@ensure_everything_saved
|
||||||
def on_close(self, *_):
|
def on_close(self, *args):
|
||||||
"""Safely close the application."""
|
"""Safely close the application."""
|
||||||
logger.debug("Closing window")
|
logger.debug("Closing window")
|
||||||
self.window.hide()
|
self.window.hide()
|
||||||
@ -390,7 +394,7 @@ class UserInterface:
|
|||||||
# and select the newest one (on the top). triggers on_select_preset
|
# and select the newest one (on the top). triggers on_select_preset
|
||||||
preset_selection.set_active(0)
|
preset_selection.set_active(0)
|
||||||
|
|
||||||
def can_modify_preset(self, *_) -> bool:
|
def can_modify_preset(self, *args) -> bool:
|
||||||
"""if changing the preset is possible."""
|
"""if changing the preset is possible."""
|
||||||
return self.dbus.get_state(self.group.key) != RUNNING
|
return self.dbus.get_state(self.group.key) != RUNNING
|
||||||
|
|
||||||
@ -413,10 +417,10 @@ class UserInterface:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@if_group_selected
|
@if_group_selected
|
||||||
def on_restore_defaults_clicked(self, *_):
|
def on_restore_defaults_clicked(self, *args):
|
||||||
"""Stop injecting the preset."""
|
"""Stop injecting the preset."""
|
||||||
self.dbus.stop_injecting(self.group.key)
|
self.dbus.stop_injecting(self.group.key)
|
||||||
self.show_status(CTX_APPLY, "Applied the system default")
|
self.show_status(CTX_APPLY, _("Applied the system default"))
|
||||||
GLib.timeout_add(100, self.show_device_mapping_status)
|
GLib.timeout_add(100, self.show_device_mapping_status)
|
||||||
|
|
||||||
def show_status(self, context_id, message, tooltip=None):
|
def show_status(self, context_id, message, tooltip=None):
|
||||||
@ -470,11 +474,11 @@ class UserInterface:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
position = key.beautify()
|
position = key.beautify()
|
||||||
msg = f"Syntax error at {position}, hover for info"
|
msg = _("Syntax error at %s, hover for info") % position
|
||||||
self.show_status(CTX_MAPPING, msg, error)
|
self.show_status(CTX_MAPPING, msg, error)
|
||||||
|
|
||||||
@ensure_everything_saved
|
@ensure_everything_saved
|
||||||
def on_rename_button_clicked(self, _):
|
def on_rename_button_clicked(self, arg):
|
||||||
"""Rename the preset based on the contents of the name input."""
|
"""Rename the preset based on the contents of the name input."""
|
||||||
new_name = self.get("preset_name_input").get_text()
|
new_name = self.get("preset_name_input").get_text()
|
||||||
|
|
||||||
@ -493,7 +497,7 @@ class UserInterface:
|
|||||||
self.populate_presets()
|
self.populate_presets()
|
||||||
|
|
||||||
@if_preset_selected
|
@if_preset_selected
|
||||||
def on_delete_preset_clicked(self, *_):
|
def on_delete_preset_clicked(self, *args):
|
||||||
"""Delete a preset from the file system."""
|
"""Delete a preset from the file system."""
|
||||||
accept = Gtk.ResponseType.ACCEPT
|
accept = Gtk.ResponseType.ACCEPT
|
||||||
if len(active_preset) > 0 and self.show_confirm_delete() != accept:
|
if len(active_preset) > 0 and self.show_confirm_delete() != accept:
|
||||||
@ -508,14 +512,14 @@ class UserInterface:
|
|||||||
self.populate_presets()
|
self.populate_presets()
|
||||||
|
|
||||||
@if_preset_selected
|
@if_preset_selected
|
||||||
def on_apply_preset_clicked(self, _):
|
def on_apply_preset_clicked(self, arg):
|
||||||
"""Apply a preset without saving changes."""
|
"""Apply a preset without saving changes."""
|
||||||
self.save_preset()
|
self.save_preset()
|
||||||
|
|
||||||
if active_preset.num_saved_keys == 0:
|
if active_preset.num_saved_keys == 0:
|
||||||
logger.error("Cannot apply empty preset file")
|
logger.error(_("Cannot apply empty preset file"))
|
||||||
# also helpful for first time use
|
# also helpful for first time use
|
||||||
self.show_status(CTX_ERROR, "You need to add keys and save first")
|
self.show_status(CTX_ERROR, _("You need to add keys and save first"))
|
||||||
return
|
return
|
||||||
|
|
||||||
preset = self.preset_name
|
preset = self.preset_name
|
||||||
@ -556,11 +560,11 @@ class UserInterface:
|
|||||||
self.dbus.set_config_dir(get_config_path())
|
self.dbus.set_config_dir(get_config_path())
|
||||||
self.dbus.start_injecting(self.group.key, preset)
|
self.dbus.start_injecting(self.group.key, preset)
|
||||||
|
|
||||||
self.show_status(CTX_APPLY, "Starting injection...")
|
self.show_status(CTX_APPLY, _("Starting injection..."))
|
||||||
|
|
||||||
GLib.timeout_add(100, self.show_injection_result)
|
GLib.timeout_add(100, self.show_injection_result)
|
||||||
|
|
||||||
def on_autoload_switch(self, _, active):
|
def on_autoload_switch(self, arg, active):
|
||||||
"""Load the preset automatically next time the user logs in."""
|
"""Load the preset automatically next time the user logs in."""
|
||||||
key = self.group.key
|
key = self.group.key
|
||||||
preset = self.preset_name
|
preset = self.preset_name
|
||||||
@ -595,10 +599,10 @@ class UserInterface:
|
|||||||
state = self.dbus.get_state(self.group.key)
|
state = self.dbus.get_state(self.group.key)
|
||||||
|
|
||||||
if state == RUNNING:
|
if state == RUNNING:
|
||||||
msg = f'Applied preset "{self.preset_name}"'
|
msg = _("Applied preset %s") % self.preset_name
|
||||||
|
|
||||||
if active_preset.get_mapping(EventCombination(InputEvent.btn_left())):
|
if active_preset.get_mapping(EventCombination(InputEvent.btn_left())):
|
||||||
msg += ", CTRL + DEL to stop"
|
msg += _(", CTRL + DEL to stop")
|
||||||
|
|
||||||
self.show_status(CTX_APPLY, msg)
|
self.show_status(CTX_APPLY, msg)
|
||||||
|
|
||||||
@ -606,7 +610,9 @@ class UserInterface:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if state == FAILED:
|
if state == FAILED:
|
||||||
self.show_status(CTX_ERROR, f'Failed to apply preset "{self.preset_name}"')
|
self.show_status(
|
||||||
|
CTX_ERROR, _("Failed to apply preset %s") % self.preset_name
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if state == NO_GRAB:
|
if state == NO_GRAB:
|
||||||
@ -633,12 +639,12 @@ class UserInterface:
|
|||||||
self.get("apply_system_layout").set_opacity(0.4)
|
self.get("apply_system_layout").set_opacity(0.4)
|
||||||
|
|
||||||
@if_preset_selected
|
@if_preset_selected
|
||||||
def on_copy_preset_clicked(self, *_):
|
def on_copy_preset_clicked(self, *args):
|
||||||
"""Copy the current preset and select it."""
|
"""Copy the current preset and select it."""
|
||||||
self.create_preset(copy=True)
|
self.create_preset(copy=True)
|
||||||
|
|
||||||
@if_group_selected
|
@if_group_selected
|
||||||
def on_create_preset_clicked(self, *_):
|
def on_create_preset_clicked(self, *args):
|
||||||
"""Create a new empty preset and select it."""
|
"""Create a new empty preset and select it."""
|
||||||
self.create_preset()
|
self.create_preset()
|
||||||
|
|
||||||
@ -667,7 +673,7 @@ class UserInterface:
|
|||||||
self.get("preset_selection").set_active_id(new_preset)
|
self.get("preset_selection").set_active_id(new_preset)
|
||||||
except PermissionError as error:
|
except PermissionError as error:
|
||||||
error = str(error)
|
error = str(error)
|
||||||
self.show_status(CTX_ERROR, "Permission denied!", error)
|
self.show_status(CTX_ERROR, _("Permission denied!"), error)
|
||||||
logger.error(error)
|
logger.error(error)
|
||||||
|
|
||||||
@ensure_everything_saved
|
@ensure_everything_saved
|
||||||
@ -721,7 +727,7 @@ class UserInterface:
|
|||||||
speed = 2 ** gtk_range.get_value()
|
speed = 2 ** gtk_range.get_value()
|
||||||
active_preset.set("gamepad.joystick.pointer_speed", speed)
|
active_preset.set("gamepad.joystick.pointer_speed", speed)
|
||||||
|
|
||||||
def save_preset(self, *_):
|
def save_preset(self, *args):
|
||||||
"""Write changes in the active_preset to disk."""
|
"""Write changes in the active_preset to disk."""
|
||||||
if not active_preset.has_unsaved_changes():
|
if not active_preset.has_unsaved_changes():
|
||||||
# optimization, and also avoids tons of redundant logs
|
# optimization, and also avoids tons of redundant logs
|
||||||
@ -739,10 +745,10 @@ class UserInterface:
|
|||||||
self.populate_presets()
|
self.populate_presets()
|
||||||
except PermissionError as error:
|
except PermissionError as error:
|
||||||
error = str(error)
|
error = str(error)
|
||||||
self.show_status(CTX_ERROR, "Permission denied!", error)
|
self.show_status(CTX_ERROR, _("Permission denied!"), error)
|
||||||
logger.error(error)
|
logger.error(error)
|
||||||
|
|
||||||
for _, mapping in active_preset:
|
for _x, mapping in active_preset:
|
||||||
if not mapping:
|
if not mapping:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -757,7 +763,7 @@ class UserInterface:
|
|||||||
or code not in global_uinputs.get_uinput(target).capabilities()[EV_KEY]
|
or code not in global_uinputs.get_uinput(target).capabilities()[EV_KEY]
|
||||||
):
|
):
|
||||||
trimmed = re.sub(r"\s+", " ", symbol).strip()
|
trimmed = re.sub(r"\s+", " ", symbol).strip()
|
||||||
self.show_status(CTX_MAPPING, f'Unknown mapping "{trimmed}"')
|
self.show_status(CTX_MAPPING, _("Unknown mapping %s") % trimmed)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# no broken mappings found
|
# no broken mappings found
|
||||||
@ -767,11 +773,11 @@ class UserInterface:
|
|||||||
# the regular mappings are allright
|
# the regular mappings are allright
|
||||||
self.check_macro_syntax()
|
self.check_macro_syntax()
|
||||||
|
|
||||||
def on_about_clicked(self, _):
|
def on_about_clicked(self, arg):
|
||||||
"""Show the about/help dialog."""
|
"""Show the about/help dialog."""
|
||||||
self.about.show()
|
self.about.show()
|
||||||
|
|
||||||
def on_about_key_press(self, _, event):
|
def on_about_key_press(self, arg, event):
|
||||||
"""Hide the about/help dialog."""
|
"""Hide the about/help dialog."""
|
||||||
gdk_keycode = event.get_keyval()[1]
|
gdk_keycode = event.get_keyval()[1]
|
||||||
if gdk_keycode == Gdk.KEY_Escape:
|
if gdk_keycode == Gdk.KEY_Escape:
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-07-03 22:39+0200\n"
|
"POT-Creation-Date: 2022-02-08 08:52+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -17,323 +17,290 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
#: data/input-remapper.glade:1128
|
#: inputremapper/gui/editor/editor.py:565
|
||||||
msgid "."
|
#, python-format
|
||||||
|
msgid "\"%s\" already mapped to \"%s\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1348
|
#: inputremapper/gui/user_interface.py:605
|
||||||
msgid "1, 2"
|
msgid ", CTRL + DEL to stop"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:926
|
#: data/input-remapper.glade:1070
|
||||||
msgid ""
|
|
||||||
"A \"key + key + ... + key\" syntax can be used to trigger key combinations. "
|
|
||||||
"For example \"control_l + a\".\n"
|
|
||||||
"\n"
|
|
||||||
"\"disable\" disables a key."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:898
|
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:209
|
#: inputremapper/gui/user_interface.py:602
|
||||||
|
#, python-format
|
||||||
|
msgid "Applied preset %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: inputremapper/gui/user_interface.py:423
|
||||||
|
msgid "Applied the system default"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:307
|
||||||
msgid "Apply"
|
msgid "Apply"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:403
|
#: inputremapper/gui/user_interface.py:254
|
||||||
|
#, python-format
|
||||||
|
msgid "Are you sure to delete preset %s?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: inputremapper/gui/editor/editor.py:508
|
||||||
|
msgid "Are you sure to delete this mapping?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:504
|
||||||
msgid "Autoload"
|
msgid "Autoload"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1420
|
#: data/input-remapper.glade:583 data/input-remapper.glade:627
|
||||||
msgid ""
|
|
||||||
"Between calls to k, key down and key up events, macros will sleep for 10ms "
|
|
||||||
"by default, which can be configured in ~/.config/input-remapper/config"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:482 data/input-remapper.glade:526
|
|
||||||
msgid "Buttons"
|
msgid "Buttons"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1286
|
#: data/input-remapper.glade:65
|
||||||
msgid "CTRL + a, CTRL + x"
|
msgid "Cancel"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:706
|
#: inputremapper/gui/user_interface.py:520
|
||||||
msgid ""
|
msgid "Cannot apply empty preset file"
|
||||||
"Click on a cell below and hit a key on your device. Click the \"Restore "
|
|
||||||
"Defaults\" button beforehand."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:228
|
#: data/input-remapper.glade:860
|
||||||
|
msgid "Change Key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:326
|
||||||
msgid "Copy"
|
msgid "Copy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:265
|
#: data/input-remapper.glade:350
|
||||||
|
msgid "Create a new preset"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:365 data/input-remapper.glade:889
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:96
|
#: data/input-remapper.glade:894
|
||||||
|
msgid "Delete this entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:370
|
||||||
|
msgid "Delete this preset"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:193
|
||||||
msgid "Device"
|
msgid "Device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: bin/input-remapper-gtk:58
|
#: data/input-remapper.glade:331
|
||||||
msgid "Displays additional debug information"
|
msgid "Duplicate this preset"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:214
|
#: inputremapper/gui/user_interface.py:614
|
||||||
msgid "Don't hold down any keys while the injection starts."
|
#, python-format
|
||||||
|
msgid "Failed to apply preset %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1213
|
#: data/input-remapper.glade:239
|
||||||
msgid "Examples"
|
msgid "Help"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1604
|
#: data/input-remapper.glade:149
|
||||||
msgid "Go Back"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:483 data/input-remapper.glade:527
|
|
||||||
msgid "Joystick"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:709
|
|
||||||
msgid "Key"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:54 data/input-remapper.glade:1583
|
|
||||||
#: data/input-remapper.glade:1695
|
|
||||||
msgid "Input Remapper"
|
msgid "Input Remapper"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:465
|
#: data/input-remapper.glade:584 data/input-remapper.glade:628
|
||||||
|
msgid "Joystick"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:566
|
||||||
msgid "Left joystick"
|
msgid "Left joystick"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:948
|
#: data/input-remapper.glade:581 data/input-remapper.glade:625
|
||||||
msgid "Macros"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:964
|
|
||||||
msgid "Macros allow multiple characters to be written with a single key-press."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:723
|
|
||||||
msgid "Mapping"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:480 data/input-remapper.glade:524
|
|
||||||
msgid "Mouse"
|
msgid "Mouse"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:553
|
#: data/input-remapper.glade:654
|
||||||
msgid "Mouse speed"
|
msgid "Mouse speed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:246
|
#: data/input-remapper.glade:345
|
||||||
msgid "New"
|
msgid "New"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:298
|
#: inputremapper/gui/user_interface.py:676
|
||||||
|
#: inputremapper/gui/user_interface.py:748
|
||||||
|
msgid "Permission denied!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:399
|
||||||
msgid "Preset"
|
msgid "Preset"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:337
|
#: data/input-remapper.glade:864
|
||||||
|
msgid "Record a button of your device that should be remapped"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:438
|
||||||
msgid "Rename"
|
msgid "Rename"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:120
|
#: data/input-remapper.glade:610
|
||||||
msgid "Stop Injection"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:509
|
|
||||||
msgid "Right joystick"
|
msgid "Right joystick"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:368
|
#: data/input-remapper.glade:469
|
||||||
msgid "Save the entered name"
|
msgid "Save the entered name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:124
|
#: data/input-remapper.glade:1098
|
||||||
msgid ""
|
msgid ""
|
||||||
"Shortcut: ctrl + del\n"
|
"See <a href=\"https://github.com/sezanzeb/input-remapper/blob/HEAD/readme/"
|
||||||
"To give your keys back their original mapping."
|
"usage.md\">usage.md</a> online on github for comprehensive information.\n"
|
||||||
|
"\n"
|
||||||
|
"A \"key + key + ... + key\" syntax can be used to trigger key combinations. "
|
||||||
|
"For example \"Control_L + a\".\n"
|
||||||
|
"\n"
|
||||||
|
"Writing \"disable\" as a mapping disables a key.\n"
|
||||||
|
"\n"
|
||||||
|
"Macros allow multiple characters to be written with a single key-press. "
|
||||||
|
"Information about programming them is available online on github. See <a "
|
||||||
|
"href=\"https://github.com/sezanzeb/input-remapper/blob/HEAD/readme/macros.md"
|
||||||
|
"\">macros.md</a> and <a href=\"https://github.com/sezanzeb/input-remapper/"
|
||||||
|
"blob/HEAD/readme/examples.md\">examples.md</a>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1556
|
#: inputremapper/gui/editor/editor.py:111
|
||||||
|
msgid "Set the key first"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:221
|
||||||
|
msgid ""
|
||||||
|
"Shortcut: ctrl + del\n"
|
||||||
|
"Gives your keys back their original function"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:1239
|
||||||
msgid "Shortcuts"
|
msgid "Shortcuts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1458
|
#: data/input-remapper.glade:1141
|
||||||
msgid ""
|
msgid ""
|
||||||
"Shortcuts only work while keys are not being recorded and the gui is in "
|
"Shortcuts only work while keys are not being recorded and the gui is in "
|
||||||
"focus."
|
"focus."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:402
|
#: data/input-remapper.glade:312
|
||||||
|
msgid "Start injecting. Don't hold down any keys while the injection starts"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: inputremapper/gui/user_interface.py:563
|
||||||
|
msgid "Starting injection..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:217
|
||||||
|
msgid "Stop Injection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: inputremapper/gui/user_interface.py:477
|
||||||
|
#, python-format
|
||||||
|
msgid "Syntax error at %s, hover for info"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: inputremapper/gui/user_interface.py:229
|
||||||
|
msgid "The helper did not start"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:879
|
||||||
|
msgid "The type of device this mapping is emulating."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:499
|
||||||
msgid "To automatically apply the preset after your login or when it connects."
|
msgid "To automatically apply the preset after your login or when it connects."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1439
|
#: inputremapper/gui/user_interface.py:766
|
||||||
|
#, python-format
|
||||||
|
msgid "Unknown mapping %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:1122
|
||||||
msgid "Usage"
|
msgid "Usage"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:843
|
#: inputremapper/gui/editor/editor.py:482
|
||||||
|
msgid "Use \"Stop Injection\" to stop before editing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:1015
|
||||||
msgid "Version unknown"
|
msgid "Version unknown"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:481 data/input-remapper.glade:525
|
#: data/input-remapper.glade:582 data/input-remapper.glade:626
|
||||||
msgid "Wheel"
|
msgid "Wheel"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:860
|
#: data/input-remapper.glade:1032
|
||||||
msgid ""
|
msgid ""
|
||||||
"You can find more information and report bugs at\n"
|
"You can find more information and report bugs at\n"
|
||||||
"<a href=\"https://github.com/sezanzeb/input-remapper\">https://github.com/"
|
"<a href=\"https://github.com/sezanzeb/input-remapper\">https://github.com/"
|
||||||
"sezanzeb/input-remapper</a>"
|
"sezanzeb/input-remapper</a>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1248
|
#: inputremapper/gui/user_interface.py:522
|
||||||
msgid "a, a, a with 500ms pause"
|
msgid "You need to add keys and save first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1490
|
#: inputremapper/gui/editor/editor.py:574
|
||||||
|
msgid "Your system might reinterpret combinations "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: inputremapper/gui/editor/editor.py:576
|
||||||
|
msgid "break them."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: data/input-remapper.glade:1173
|
||||||
msgid "closes the application"
|
msgid "closes the application"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1478
|
#: data/input-remapper.glade:1161
|
||||||
msgid "ctrl + del"
|
msgid "ctrl + del"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1502
|
#: data/input-remapper.glade:1185
|
||||||
msgid "ctrl + q"
|
msgid "ctrl + q"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1514
|
#: data/input-remapper.glade:1197
|
||||||
msgid "ctrl + r"
|
msgid "ctrl + r"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1044
|
#: inputremapper/gui/editor/editor.py:573
|
||||||
msgid "e"
|
msgid "ctrl, alt and shift may not combine properly"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1335
|
#: inputremapper/gui/editor/editor.py:81 inputremapper/gui/editor/editor.py:382
|
||||||
msgid "e(EV_REL, REL_X, 10)"
|
msgid "new entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1104
|
#: data/input-remapper.glade:1209
|
||||||
msgid "executes the parameter as long as the key is pressed down"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1140
|
|
||||||
msgid "executes two actions behind each other"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1116
|
|
||||||
msgid "h"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1056
|
|
||||||
msgid "holds a modifier while executing the second parameter"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1020
|
|
||||||
msgid "k"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1298
|
|
||||||
msgid "k(1).h(k(2)).k(3)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1235
|
|
||||||
msgid "k(1).k(2)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1398
|
|
||||||
msgid "keeps scrolling down while held"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1080
|
|
||||||
msgid "m"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1273
|
|
||||||
msgid "m(Control_L, k(a).k(x))"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1152
|
|
||||||
msgid "mouse"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1372
|
|
||||||
msgid "mouse(right, 4)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1311
|
|
||||||
msgid "moves the mouse cursor 10px to the right"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:984
|
|
||||||
msgid "r"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1260
|
|
||||||
msgid "r(3, k(a).w(500))"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1526
|
|
||||||
msgid "refreshes the device list"
|
msgid "refreshes the device list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1092
|
#: data/input-remapper.glade:1221
|
||||||
msgid "repeats the execution of the second parameter"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1188
|
|
||||||
msgid "same as mouse"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1538
|
|
||||||
msgid "stops the injection"
|
msgid "stops the injection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1176
|
#: inputremapper/gui/editor/editor.py:575
|
||||||
msgid "takes direction (up, left, ...) and speed as parameters"
|
msgid "with those after they are injected, and by doing so "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/input-remapper.glade:1008
|
#: data/input-remapper.glade:1052
|
||||||
msgid "w"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:996
|
|
||||||
msgid "waits in milliseconds"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1164
|
|
||||||
msgid "wheel"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1385
|
|
||||||
msgid "wheel(down, 1)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1360
|
|
||||||
msgid "which keeps moving the mouse while pressed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1323
|
|
||||||
msgid "writes 1 2 2 ... 2 2 3 while the key is pressed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1032
|
|
||||||
msgid "writes a single keystroke"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:1068
|
|
||||||
msgid "writes an event"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: data/input-remapper.glade:880
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"© 2021 Sezanzeb proxima@sezanzeb.de\n"
|
"© 2021 Sezanzeb proxima@sezanzeb.de\n"
|
||||||
"This program comes with absolutely no warranty.\n"
|
"This program comes with absolutely no warranty.\n"
|
||||||
|
Loading…
Reference in New Issue
Block a user