diff --git a/src/cli.py b/src/cli.py index 715bb17..19d0472 100755 --- a/src/cli.py +++ b/src/cli.py @@ -8,6 +8,8 @@ import getpass import argparse import builtins +from i18n import _ + # Try to get the original username (not "root") from shell try: user = os.getlogin() @@ -20,48 +22,48 @@ if user == "root" or user is None: # If even that fails, error out if env_user == "": - print("Could not determine user, please use the --user flag") + print(_("Could not determine user, please use the --user flag")) sys.exit(1) else: user = env_user # Basic command setup parser = argparse.ArgumentParser( - description="Command line interface for Howdy face authentication.", + description=_("Command line interface for Howdy face authentication."), formatter_class=argparse.RawDescriptionHelpFormatter, add_help=False, prog="howdy", - epilog="For support please visit\nhttps://github.com/boltgolt/howdy") + epilog=_("For support please visit\nhttps://github.com/boltgolt/howdy")) # Add an argument for the command parser.add_argument( - "command", - help="The command option to execute, can be one of the following: add, clear, config, disable, list, remove, snapshot, set, test or version.", + _("command"), + help=_("The command option to execute, can be one of the following: add, clear, config, disable, list, remove, snapshot, set, test or version."), metavar="command", choices=["add", "clear", "config", "disable", "list", "remove", "set", "snapshot", "test", "version"]) # Add an argument for the extra arguments of diable and remove parser.add_argument( - "arguments", - help="Optional arguments for the add, disable, remove and set commands.", + _("arguments"), + help=_("Optional arguments for the add, disable, remove and set commands."), nargs="*") # Add the user flag parser.add_argument( "-U", "--user", default=user, - help="Set the user account to use.") + help=_("Set the user account to use.")) # Add the -y flag parser.add_argument( "-y", - help="Skip all questions.", + help=_("Skip all questions."), action="store_true") # Add the --plain flag parser.add_argument( "--plain", - help="Print machine-friendly output.", + help=_("Print machine-friendly output."), action="store_true") # Overwrite the default help message so we can use a uppercase S @@ -69,11 +71,11 @@ parser.add_argument( "-h", "--help", action="help", default=argparse.SUPPRESS, - help="Show this help message and exit.") + help=_("Show this help message and exit.")) # If we only have 1 argument we print the help text if len(sys.argv) < 2: - print("current active user: " + user + "\n") + print(_("current active user: ") + user + "\n") parser.print_help() sys.exit(0) @@ -87,13 +89,13 @@ builtins.howdy_user = args.user # Check if we have rootish rights # This is this far down the file so running the command for help is always possible if os.geteuid() != 0: - print("Please run this command as root:\n") + print(_("Please run this command as root:\n")) print("\tsudo howdy " + " ".join(sys.argv[1:])) sys.exit(1) # Beond this point the user can't change anymore, if we still have root as user we need to abort if args.user == "root": - print("Can't run howdy commands as root, please run this command with the --user flag") + print(_("Can't run howdy commands as root, please run this command with the --user flag")) sys.exit(1) # Execute the right command diff --git a/src/cli/add.py b/src/cli/add.py index 7aa7e01..0851b0e 100644 --- a/src/cli/add.py +++ b/src/cli/add.py @@ -8,7 +8,9 @@ import json import configparser import builtins import numpy as np + from recorders.video_capture import VideoCapture +from i18n import _ # Try to import dlib and give a nice error if we can't # Add should be the first point where import issues show up @@ -17,7 +19,7 @@ try: except ImportError as err: print(err) - print("\nCan't import the dlib module, check the output of") + print(_("\nCan't import the dlib module, check the output of")) print("pip3 show dlib") sys.exit(1) @@ -29,7 +31,7 @@ path = os.path.abspath(__file__ + "/..") # Test if at lest 1 of the data files is there and abort if it's not if not os.path.isfile(path + "/../dlib-data/shape_predictor_5_face_landmarks.dat"): - print("Data files have not been downloaded, please run the following commands:") + print(_("Data files have not been downloaded, please run the following commands:")) print("\n\tcd " + os.path.realpath(path + "/../dlib-data")) print("\tsudo ./install.sh\n") sys.exit(1) @@ -55,7 +57,7 @@ encodings = [] # Make the ./models folder if it doesn't already exist if not os.path.exists(path + "/../models"): - print("No face model folder found, creating one") + print(_("No face model folder found, creating one")) os.makedirs(path + "/../models") # To try read a premade encodings file if it exists @@ -66,30 +68,30 @@ except FileNotFoundError: # Print a warning if too many encodings are being added if len(encodings) > 3: - print("NOTICE: Each additional model slows down the face recognition engine slightly") - print("Press Ctrl+C to cancel\n") + print(_("NOTICE: Each additional model slows down the face recognition engine slightly")) + print(_("Press Ctrl+C to cancel\n")) # Make clear what we are doing if not human if not builtins.howdy_args.plain: - print("Adding face model for the user " + user) + print(_("Adding face model for the user " + user)) # Set the default label label = "Initial model" # Get the label from the cli arguments if provided if builtins.howdy_args.arguments: - label = builtins.howdy_args.arguments[0] + label = builtins.howdy_args.arguments[0] # If models already exist, set that default label elif encodings: - label = "Model #" + str(len(encodings) + 1) + label = _("Model #") + str(len(encodings) + 1) # Keep de default name if we can't ask questions if builtins.howdy_args.y: - print('Using default label "%s" because of -y flag' % (label, )) + print(_('Using default label "%s" because of -y flag') % (label, )) else: # Ask the user for a custom label - label_in = input("Enter a label for this new model [" + label + "]: ") + label_in = input(_("Enter a label for this new model [{}]: ").format(label)) # Set the custom label (if any) and limit it to 24 characters if label_in != "": @@ -97,7 +99,7 @@ else: # Remove illegal characters if "," in label: - print("NOTICE: Removing illegal character \",\" from model name") + print(_("NOTICE: Removing illegal character \",\" from model name")) label = label.replace(",", "") # Prepare the metadata for insertion @@ -111,7 +113,7 @@ insert_model = { # Set up video_capture video_capture = VideoCapture(config) -print("\nPlease look straight into the camera") +print(_("\nPlease look straight into the camera")) # Give the user time to read time.sleep(2) @@ -176,17 +178,17 @@ video_capture.release() # If we've found no faces, try to determine why if face_locations is None or not face_locations: if valid_frames == 0: - print("Camera saw only black frames - is IR emitter working?") + print(_("Camera saw only black frames - is IR emitter working?")) elif valid_frames == dark_tries: - print("All frames were too dark, please check dark_threshold in config") - print("Average darkness: " + str(dark_running_total / valid_frames) + ", Threshold: " + str(dark_threshold)) + print(_("All frames were too dark, please check dark_threshold in config")) + print(_("Average darkness: {avg}, Threshold: {threshold}").format(avg=str(dark_running_total / valid_frames), threshold=str(dark_threshold))) else: - print("No face detected, aborting") + print(_("No face detected, aborting")) sys.exit(1) # If more than 1 faces are detected we can't know wich one belongs to the user elif len(face_locations) > 1: - print("Multiple faces detected, aborting") + print(_("Multiple faces detected, aborting")) sys.exit(1) face_location = face_locations[0] @@ -207,6 +209,5 @@ with open(enc_file, "w") as datafile: json.dump(encodings, datafile) # Give let the user know how it went -print("""Scan complete - -Added a new model to """ + user) +print(_("""\nScan complete +Added a new model to """) + user) diff --git a/src/cli/clear.py b/src/cli/clear.py index 817de48..22b6579 100644 --- a/src/cli/clear.py +++ b/src/cli/clear.py @@ -5,6 +5,8 @@ import os import sys import builtins +from i18n import _ + # Get the full path to this file path = os.path.dirname(os.path.abspath(__file__)) # Get the passed user @@ -12,25 +14,25 @@ user = builtins.howdy_user # Check if the models folder is there if not os.path.exists(path + "/../models"): - print("No models created yet, can't clear them if they don't exist") + print(_("No models created yet, can't clear them if they don't exist")) sys.exit(1) # Check if the user has a models file to delete if not os.path.isfile(path + "/../models/" + user + ".dat"): - print(user + " has no models or they have been cleared already") + print(_("{} has no models or they have been cleared already").format(user)) sys.exit(1) # Only ask the user if there's no -y flag if not builtins.howdy_args.y: # Double check with the user - print("This will clear all models for " + user) - ans = input("Do you want to continue [y/N]: ") + print(_("This will clear all models for ") + user) + ans = input(_("Do you want to continue [y/N]: ")) # Abort if they don't answer y or Y if (ans.lower() != "y"): - print('\nInerpeting as a "NO"') + print(_('\nInerpeting as a "NO", aborting')) sys.exit(1) # Delete otherwise os.remove(path + "/../models/" + user + ".dat") -print("\nModels cleared") +print(_("\nModels cleared")) diff --git a/src/cli/config.py b/src/cli/config.py index 764833d..e2b0b0f 100644 --- a/src/cli/config.py +++ b/src/cli/config.py @@ -4,8 +4,10 @@ import os import subprocess +from i18n import _ + # Let the user know what we're doing -print("Opening config.ini in the default editor") +print(_("Opening config.ini in the default editor")) # Default to the nano editor editor = "/bin/nano" diff --git a/src/cli/disable.py b/src/cli/disable.py index 7fb9d8a..485fe1a 100644 --- a/src/cli/disable.py +++ b/src/cli/disable.py @@ -7,6 +7,8 @@ import builtins import fileinput import configparser +from i18n import _ + # Get the absolute filepath config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini" @@ -16,7 +18,7 @@ config.read(config_path) # Check if enough arguments have been passed if not builtins.howdy_args.arguments: - print("Please add a 0 (enable) or a 1 (disable) as an argument") + print(_("Please add a 0 (enable) or a 1 (disable) as an argument")) sys.exit(1) # Get the cli argument @@ -29,12 +31,12 @@ elif argument == "0" or argument.lower() == "false": out_value = "false" else: # Of it's not a 0 or a 1, it's invalid - print("Please only use 0 (enable) or 1 (disable) as an argument") + print(_("Please only use 0 (enable) or 1 (disable) as an argument")) sys.exit(1) # Don't do anything when the state is already the requested one if out_value == config.get("core", "disabled"): - print("The disable option has already been set to " + out_value) + print(_("The disable option has already been set to ") + out_value) sys.exit(1) # Loop though the config file and only replace the line containing the disable config @@ -43,6 +45,6 @@ for line in fileinput.input([config_path], inplace=1): # Print what we just did if out_value == "true": - print("Howdy has been disabled") + print(_("Howdy has been disabled")) else: - print("Howdy has been enabled") + print(_("Howdy has been enabled")) diff --git a/src/cli/list.py b/src/cli/list.py index aad7eb3..a098498 100644 --- a/src/cli/list.py +++ b/src/cli/list.py @@ -7,13 +7,15 @@ import json import time import builtins +from i18n import _ + # Get the absolute path and the username path = os.path.dirname(os.path.realpath(__file__)) + "/.." user = builtins.howdy_user # Check if the models file has been created yet if not os.path.exists(path + "/models"): - print("Face models have not been initialized yet, please run:") + print(_("Face models have not been initialized yet, please run:")) print("\n\tsudo howdy -U " + user + " add\n") sys.exit(1) @@ -24,14 +26,14 @@ enc_file = path + "/models/" + user + ".dat" try: encodings = json.load(open(enc_file)) except FileNotFoundError: - print("No face model known for the user " + user + ", please run:") + print(_("No face model known for the user {}, please run:").format(user)) print("\n\tsudo howdy -U " + user + " add\n") sys.exit(1) # Print a header if we're not in plain mode if not builtins.howdy_args.plain: - print("Known face models for " + user + ":") - print("\n\033[1;29mID Date Label\033[0m") + print(_("Known face models for {}:").format(user)) + print("\n\033[1;29m" + _("ID Date Label\033[0m")) # Loop through all encodings and print info about them for enc in encodings: diff --git a/src/cli/remove.py b/src/cli/remove.py index e4f4736..b594fec 100644 --- a/src/cli/remove.py +++ b/src/cli/remove.py @@ -6,22 +6,24 @@ import os import json import builtins +from i18n import _ + # Get the absolute path and the username path = os.path.dirname(os.path.realpath(__file__)) + "/.." user = builtins.howdy_user # Check if enough arguments have been passed if not builtins.howdy_args.arguments: - print("Please add the ID of the model you want to remove as an argument") - print("For example:") + print(_("Please add the ID of the model you want to remove as an argument")) + print(_("For example:")) print("\n\thowdy remove 0\n") - print("You can find the IDs by running:") + print(_("You can find the IDs by running:")) print("\n\thowdy list\n") sys.exit(1) # Check if the models file has been created yet if not os.path.exists(path + "/models"): - print("Face models have not been initialized yet, please run:") + print(_("Face models have not been initialized yet, please run:")) print("\n\thowdy add\n") sys.exit(1) @@ -32,7 +34,7 @@ enc_file = path + "/models/" + user + ".dat" try: encodings = json.load(open(enc_file)) except FileNotFoundError: - print("No face model known for the user " + user + ", please run:") + print(_("No face model known for the user " + user + ", please run:")) print("\n\thowdy add\n") sys.exit(1) @@ -48,12 +50,12 @@ for enc in encodings: # Only ask the user if there's no -y flag if not builtins.howdy_args.y: # Double check with the user - print('This will remove the model called "' + enc["label"] + '" for ' + user) - ans = input("Do you want to continue [y/N]: ") + print(_('This will remove the model called "{label}" for {user}').format(label=enc["label"], user=user)) + ans = input(_("Do you want to continue [y/N]: ")) # Abort if the answer isn't yes if (ans.lower() != "y"): - print('\nInterpreting as a "NO", aborting') + print(_('\nInterpreting as a "NO", aborting')) sys.exit(1) # Add a padding empty line @@ -65,13 +67,13 @@ for enc in encodings: # Abort if no matching id was found if not found: - print("No model with ID " + id + " exists for " + user) + print(_("No model with ID {id} exists for {user}").format(id=id, user=user)) sys.exit(1) # Remove the entire file if this encoding is the only one if len(encodings) == 1: os.remove(path + "/models/" + user + ".dat") - print("Removed last model, howdy disabled for user") + print(_("Removed last model, howdy disabled for user")) else: # A place holder to contain the encodings that will remain new_encodings = [] @@ -85,4 +87,4 @@ else: with open(enc_file, "w") as datafile: json.dump(new_encodings, datafile) - print("Removed model " + id) + print(_("Removed model {}").format(id)) diff --git a/src/cli/set.py b/src/cli/set.py index 6235133..72c5c8e 100644 --- a/src/cli/set.py +++ b/src/cli/set.py @@ -6,13 +6,15 @@ import os import builtins import fileinput +from i18n import _ + # Get the absolute filepath config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini" # Check if enough arguments have been passed if len(builtins.howdy_args.arguments) < 2: - print("Please add a setting you would like to change and the value to set it to") - print("For example:") + print(_("Please add a setting you would like to change and the value to set it to")) + print(_("For example:")) print("\n\thowdy set certainty 3\n") sys.exit(1) @@ -31,11 +33,11 @@ for line in fileinput.input([config_path]): # If we don't have the line it is not in the config file if not found_line: - print('Could not find a "' + set_name + '" config option to set') + print(_('Could not find a "{}" config option to set').format(set_name)) sys.exit(1) # Go through the file again and update the correct line for line in fileinput.input([config_path], inplace=1): print(line.replace(found_line, set_name + " = " + set_value + "\n"), end="") -print("Config option updated") +print(_("Config option updated")) diff --git a/src/cli/snap.py b/src/cli/snap.py index 7dce8f5..8dbc49a 100644 --- a/src/cli/snap.py +++ b/src/cli/snap.py @@ -7,6 +7,8 @@ import datetime import snapshot from recorders.video_capture import VideoCapture +from i18n import _ + # Get the absolute path to the current directory path = os.path.abspath(__file__ + "/..") @@ -40,12 +42,12 @@ while True: # Generate a snapshot image from the frames file = snapshot.generate(frames, [ - "GENERATED SNAPSHOT", - "Date: " + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"), - "Dark threshold config: " + str(config.getfloat("video", "dark_threshold", fallback=50.0)), - "Certainty config: " + str(config.getfloat("video", "certainty", fallback=3.5)) + _("GENERATED SNAPSHOT"), + _("Date: ") + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"), + _("Dark threshold config: ") + str(config.getfloat("video", "dark_threshold", fallback=50.0)), + _("Certainty config: ") + str(config.getfloat("video", "certainty", fallback=3.5)) ]) # Show the file location in console -print("Generated snapshot saved as") +print(_("Generated snapshot saved as")) print(file) diff --git a/src/cli/test.py b/src/cli/test.py index c62fcef..ae66a90 100644 --- a/src/cli/test.py +++ b/src/cli/test.py @@ -9,6 +9,8 @@ import dlib import cv2 from recorders.video_capture import VideoCapture +from i18n import _ + # Get the absolute path to the current file path = os.path.dirname(os.path.abspath(__file__)) @@ -17,8 +19,7 @@ config = configparser.ConfigParser() config.read(path + "/../config.ini") if config.get("video", "recording_plugin") != "opencv": - print("Howdy has been configured to use a recorder which doesn't support the test command yet") - print("Aborting") + print(_("Howdy has been configured to use a recorder which doesn't support the test command yet, aborting")) sys.exit(12) video_capture = VideoCapture(config) @@ -28,12 +29,12 @@ exposure = config.getint("video", "exposure", fallback=-1) dark_threshold = config.getfloat("video", "dark_threshold") # Let the user know what's up -print(""" +print(_(""" Opening a window with a test feed Press ctrl+C in this terminal to quit Click on the image to enable or disable slow mode -""") +""")) def mouse(event, x, y, flags, param): @@ -97,7 +98,7 @@ try: sec_frames = 0 # Grab a single frame of video - _, frame = video_capture.read_frame() + ret, frame = video_capture.read_frame() frame = clahe.apply(frame) # Make a frame to put overlays in @@ -127,22 +128,22 @@ try: cv2.rectangle(overlay, p1, p2, (0, 200, 0), thickness=cv2.FILLED) # Print the statis in the bottom left - print_text(0, "RESOLUTION: %dx%d" % (height, width)) - print_text(1, "FPS: %d" % (fps, )) - print_text(2, "FRAMES: %d" % (total_frames, )) - print_text(3, "RECOGNITION: %dms" % (round(rec_tm * 1000), )) + print_text(0, _("RESOLUTION: %dx%d") % (height, width)) + print_text(1, _("FPS: %d") % (fps, )) + print_text(2, _("FRAMES: %d") % (total_frames, )) + print_text(3, _("RECOGNITION: %dms") % (round(rec_tm * 1000), )) # Show that slow mode is on, if it's on if slow_mode: - cv2.putText(overlay, "SLOW MODE", (width - 66, height - 10), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA) + cv2.putText(overlay, _("SLOW MODE"), (width - 66, height - 10), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA) # Ignore dark frames if hist_perc[0] > dark_threshold: # Show that this is an ignored frame in the top right - cv2.putText(overlay, "DARK FRAME", (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA) + cv2.putText(overlay, _("DARK FRAME"), (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA) else: # SHow that this is an active frame - cv2.putText(overlay, "SCAN FRAME", (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 255, 0), 0, cv2.LINE_AA) + cv2.putText(overlay, _("SCAN FRAME"), (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 255, 0), 0, cv2.LINE_AA) rec_tm = time.time() # Get the locations of all faces and their locations @@ -197,7 +198,7 @@ try: # On ctrl+C except KeyboardInterrupt: # Let the user know we're stopping - print("\nClosing window") + print(_("\nClosing window")) # Release handle to the webcam cv2.destroyAllWindows() diff --git a/src/compare.py b/src/compare.py index 51d9279..902eaaf 100644 --- a/src/compare.py +++ b/src/compare.py @@ -20,7 +20,9 @@ import datetime import snapshot import numpy as np import _thread as thread + from recorders.video_capture import VideoCapture +from i18n import _ def init_detector(lock): @@ -29,7 +31,7 @@ def init_detector(lock): # Test if at lest 1 of the data files is there and abort if it's not if not os.path.isfile(PATH + "/dlib-data/shape_predictor_5_face_landmarks.dat"): - print("Data files have not been downloaded, please run the following commands:") + print(_("Data files have not been downloaded, please run the following commands:")) print("\n\tcd " + PATH + "/dlib-data") print("\tsudo ./install.sh\n") lock.release() @@ -53,12 +55,12 @@ def init_detector(lock): def make_snapshot(type): """Generate snapshot after detection""" snapshot.generate(snapframes, [ - type + " LOGIN", - "Date: " + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"), - "Scan time: " + str(round(time.time() - timings["fr"], 2)) + "s", - "Frames: " + str(frames) + " (" + str(round(frames / (time.time() - timings["fr"]), 2)) + "FPS)", - "Hostname: " + os.uname().nodename, - "Best certainty value: " + str(round(lowest_certainty * 10, 1)) + type + _(" LOGIN"), + _("Date: ") + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"), + _("Scan time: ") + str(round(time.time() - timings["fr"], 2)) + "s", + _("Frames: ") + str(frames) + " (" + str(round(frames / (time.time() - timings["fr"]), 2)) + "FPS)", + _("Hostname: ") + os.uname().nodename, + _("Best certainty value: ") + str(round(lowest_certainty * 10, 1)) ]) @@ -172,11 +174,11 @@ while True: if time.time() - timings["fr"] > timeout: # Create a timeout snapshot if enabled if capture_failed: - make_snapshot("FAILED") + make_snapshot(_("FAILED")) if dark_tries == valid_frames: - print("All frames were too dark, please check dark_threshold in config") - print("Average darkness: " + str(dark_running_total / max(1, valid_frames)) + ", Threshold: " + str(dark_threshold)) + print(_("All frames were too dark, please check dark_threshold in config")) + print(_("Average darkness: {avg}, Threshold: {threshold}").format(avg=str(dark_running_total / max(1, valid_frames)), threshold=str(dark_threshold))) sys.exit(13) else: sys.exit(11) @@ -255,32 +257,32 @@ while True: print(" %s: %dms" % (label, round(timings[k] * 1000))) # Print a nice timing report - print("Time spent") - print_timing("Starting up", "in") - print(" Open cam + load libs: %dms" % (round(max(timings["ll"], timings["ic"]) * 1000, ))) - print_timing(" Opening the camera", "ic") - print_timing(" Importing recognition libs", "ll") - print_timing("Searching for known face", "fl") - print_timing("Total time", "tt") + print(_("Time spent")) + print_timing(_("Starting up"), "in") + print(_(" Open cam + load libs: %dms") % (round(max(timings["ll"], timings["ic"]) * 1000, ))) + print_timing(_(" Opening the camera"), "ic") + print_timing(_(" Importing recognition libs"), "ll") + print_timing(_("Searching for known face"), "fl") + print_timing(_("Total time"), "tt") - print("\nResolution") + print(_("\nResolution")) width = video_capture.fw or 1 - print(" Native: %dx%d" % (height, width)) + print(_(" Native: %dx%d") % (height, width)) # Save the new size for diagnostics scale_height, scale_width = frame.shape[:2] - print(" Used: %dx%d" % (scale_height, scale_width)) + print(_(" Used: %dx%d") % (scale_height, scale_width)) # Show the total number of frames and calculate the FPS by deviding it by the total scan time - print("\nFrames searched: %d (%.2f fps)" % (frames, frames / timings["fl"])) - print("Black frames ignored: %d " % (black_tries, )) - print("Dark frames ignored: %d " % (dark_tries, )) - print("Certainty of winning frame: %.3f" % (match * 10, )) + print(_("\nFrames searched: %d (%.2f fps)") % (frames, frames / timings["fl"])) + print(_("Black frames ignored: %d ") % (black_tries, )) + print(_("Dark frames ignored: %d ") % (dark_tries, )) + print(_("Certainty of winning frame: %.3f") % (match * 10, )) - print("Winning model: %d (\"%s\")" % (match_index, models[match_index]["label"])) + print(_("Winning model: %d (\"%s\")") % (match_index, models[match_index]["label"])) # Make snapshot if enabled if capture_successful: - make_snapshot("SUCCESSFUL") + make_snapshot(_("SUCCESSFUL")) # Run rubberstamps if enabled if config.getboolean("rubberstamps", "enabled", fallback=False): diff --git a/src/i18n.py b/src/i18n.py new file mode 100644 index 0000000..cec8fef --- /dev/null +++ b/src/i18n.py @@ -0,0 +1,12 @@ +# Support file for translations + +# Import modules +import gettext +import os + +# Get the right translation based on locale, falling back to base if none found +translation = gettext.translation("core", localedir=os.path.join(os.path.dirname(__file__), 'locales'), fallback=True) +translation.install() + +# Export translation function as _ +_ = translation.gettext diff --git a/src/recorders/ffmpeg_reader.py b/src/recorders/ffmpeg_reader.py index e9856f6..6355045 100644 --- a/src/recorders/ffmpeg_reader.py +++ b/src/recorders/ffmpeg_reader.py @@ -4,14 +4,16 @@ import numpy import sys import re + from subprocess import Popen, PIPE from cv2 import CAP_PROP_FRAME_WIDTH from cv2 import CAP_PROP_FRAME_HEIGHT +from i18n import _ try: import ffmpeg except ImportError: - print("Missing ffmpeg module, please run:") + print(_("Missing ffmpeg module, please run:")) print(" pip3 install ffmpeg-python\n") sys.exit(12) diff --git a/src/recorders/pyv4l2_reader.py b/src/recorders/pyv4l2_reader.py index d0efcf5..a315f30 100644 --- a/src/recorders/pyv4l2_reader.py +++ b/src/recorders/pyv4l2_reader.py @@ -1,16 +1,18 @@ # Class that simulates the functionality of opencv so howdy can use v4l2 devices seamlessly # Import required modules. lib4l-dev package is also required. -from recorders import v4l2 import fcntl import numpy import sys + +from recorders import v4l2 from cv2 import cvtColor, COLOR_GRAY2BGR, CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT +from i18n import _ try: from pyv4l2.frame import Frame except ImportError: - print("Missing pyv4l2 module, please run:") + print(_("Missing pyv4l2 module, please run:")) print(" pip3 install pyv4l2\n") sys.exit(13) diff --git a/src/recorders/video_capture.py b/src/recorders/video_capture.py index 9d23d27..be38873 100644 --- a/src/recorders/video_capture.py +++ b/src/recorders/video_capture.py @@ -7,6 +7,7 @@ import cv2 import os import sys +from i18n import _ # Class to provide boilerplate code to build a video recorder with the # correct settings from the config file. @@ -33,8 +34,8 @@ class VideoCapture: # Check device path if not os.path.exists(self.config.get("video", "device_path")): if self.config.getboolean("video", "warn_no_device"): - print("Howdy could not find a camera device at the path specified in the config file.") - print("It is very likely that the path is not configured correctly, please edit the 'device_path' config value by running:") + print(_("Howdy could not find a camera device at the path specified in the config file.")) + print(_("It is very likely that the path is not configured correctly, please edit the 'device_path' config value by running:")) print("\n\tsudo howdy config\n") sys.exit(1) @@ -81,7 +82,7 @@ class VideoCapture: # Don't remove ret, it doesn't work without it ret, frame = self.internal.read() if not ret: - print("Failed to read camera specified in your 'device_path', aborting") + print(_("Failed to read camera specified in the 'device_path' config option, aborting")) sys.exit(1) try: