diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index e729e2d..84b4a87 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -188,13 +188,22 @@ static const TerminalRegexPattern url_regex_patterns[] = {
   { "(?:news:|man:|info:)[-[:alnum:]\\Q^_{|}~!\"#$%&'()*+,./;:=?`\\E]+", FLAVOR_AS_IS, TRUE },
 };
 
+static const TerminalRegexPattern extra_regex_patterns[] = {
+  { "0[Xx][\\dA-Fa-f]+", FLAVOR_NUMBER, FALSE },
+  { "\\d+", FLAVOR_NUMBER, FALSE },
+};
+
 #ifdef WITH_PCRE2
 static VteRegex **url_regexes;
+static VteRegex **extra_regexes;
 #else
 static GRegex **url_regexes;
+static GRegex **extra_regexes;
 #endif
 static TerminalURLFlavour *url_regex_flavors;
+static TerminalURLFlavour *extra_regex_flavors;
 static guint n_url_regexes;
+static guint n_extra_regexes;
 
 /* See bug #697024 */
 #ifndef __linux__
@@ -221,6 +230,53 @@ free_tag_data (TagData *tagdata)
 }
 
 static void
+precompile_regexes (const TerminalRegexPattern *regex_patterns,
+                    guint n_regexes,
+#ifdef WITH_PCRE2
+                    VteRegex ***regexes,
+#else
+                    GRegex ***regexes,
+#endif
+                    TerminalURLFlavour **regex_flavors)
+{
+  guint i;
+
+#ifdef WITH_PCRE2
+  *regexes = g_new0 (VteRegex*, n_regexes);
+#else
+  *regexes = g_new0 (GRegex*, n_regexes);
+#endif
+  *regex_flavors = g_new0 (TerminalURLFlavour, n_regexes);
+
+  for (i = 0; i < n_regexes; ++i)
+    {
+      GError *error = NULL;
+
+#ifdef WITH_PCRE2
+      (*regexes)[i] = vte_regex_new (regex_patterns[i].pattern, -1,
+                                     PCRE2_UTF | PCRE2_NO_UTF_CHECK |
+                                     (regex_patterns[i].caseless ? PCRE2_CASELESS : 0),
+                                     &error);
+      g_assert_no_error (error);
+
+      if (!vte_regex_jit ((*regexes)[i], PCRE2_JIT_COMPLETE, &error) ||
+          !vte_regex_jit ((*regexes)[i], PCRE2_JIT_PARTIAL_SOFT, &error)) {
+        g_printerr ("Failed to JIT regex '%s': %s\n", regex_patterns[i].pattern, error->message);
+        g_clear_error (&error);
+      }
+#else
+      (*regexes)[i] = g_regex_new (regex_patterns[i].pattern,
+                                   G_REGEX_OPTIMIZE |
+                                   (regex_patterns[i].caseless ? G_REGEX_CASELESS : 0),
+                                   0, &error);
+      g_assert_no_error (error);
+#endif
+
+      (*regex_flavors)[i] = regex_patterns[i].flavor;
+    }
+}
+
+static void
 terminal_screen_class_enable_menu_bar_accel_notify_cb (GSettings *settings,
                                                        const char *key,
                                                        TerminalScreenClass *klass)
@@ -452,7 +508,6 @@ terminal_screen_class_init (TerminalScreenClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
   VteTerminalClass *terminal_class = VTE_TERMINAL_CLASS (klass);
   GSettings *settings;
-  guint i;
 
   object_class->constructed = terminal_screen_constructed;
   object_class->dispose = terminal_screen_dispose;
@@ -546,41 +601,10 @@ terminal_screen_class_init (TerminalScreenClass *klass)
 
   g_type_class_add_private (object_class, sizeof (TerminalScreenPrivate));
 
-  /* Precompile the regexes */
   n_url_regexes = G_N_ELEMENTS (url_regex_patterns);
-#ifdef WITH_PCRE2
-  url_regexes = g_new0 (VteRegex*, n_url_regexes);
-#else
-  url_regexes = g_new0 (GRegex*, n_url_regexes);
-#endif
-  url_regex_flavors = g_new0 (TerminalURLFlavour, n_url_regexes);
-
-  for (i = 0; i < n_url_regexes; ++i)
-    {
-      GError *error = NULL;
-
-#ifdef WITH_PCRE2
-      url_regexes[i] = vte_regex_new (url_regex_patterns[i].pattern, -1,
-                                      PCRE2_UTF | PCRE2_NO_UTF_CHECK |
-                                      (url_regex_patterns[i].caseless ? PCRE2_CASELESS : 0),
-                                      &error);
-      g_assert_no_error (error);
-
-      if (!vte_regex_jit (url_regexes[i], PCRE2_JIT_COMPLETE, &error) ||
-          !vte_regex_jit (url_regexes[i], PCRE2_JIT_PARTIAL_SOFT, &error)) {
-        g_printerr ("Failed to JIT regex '%s': %s\n", url_regex_patterns[i].pattern, error->message);
-        g_clear_error (&error);
-      }
-#else
-      url_regexes[i] = g_regex_new (url_regex_patterns[i].pattern,
-                                    G_REGEX_OPTIMIZE |
-                                    (url_regex_patterns[i].caseless ? G_REGEX_CASELESS : 0),
-                                    0, &error);
-      g_assert_no_error (error);
-#endif
-
-      url_regex_flavors[i] = url_regex_patterns[i].flavor;
-    }
+  precompile_regexes (url_regex_patterns, n_url_regexes, &url_regexes, &url_regex_flavors);
+  n_extra_regexes = G_N_ELEMENTS (extra_regex_patterns);
+  precompile_regexes (extra_regex_patterns, n_extra_regexes, &extra_regexes, &extra_regex_flavors);
 
   /* This fixes bug #329827 */
   settings = terminal_app_get_global_settings (terminal_app_get ());
@@ -1892,7 +1916,9 @@ terminal_screen_check_match (TerminalScreen *screen,
   TerminalScreenPrivate *priv = screen->priv;
   GSList *tags;
   int tag;
-  char *match;
+  guint i, j;
+  char *match = NULL;
+  gs_free char **matches = NULL;
 
   match = vte_terminal_match_check_event (VTE_TERMINAL (screen), event, &tag);
   for (tags = priv->match_tags; tags != NULL; tags = tags->next)
@@ -1905,8 +1931,36 @@ terminal_screen_check_match (TerminalScreen *screen,
 	  return match;
 	}
     }
-
   g_free (match);
+
+  matches = g_malloc (n_extra_regexes * sizeof (char *));
+  if (
+#ifdef WITH_PCRE2
+      vte_terminal_event_check_regex_simple(
+#else
+      vte_terminal_event_check_gregex_simple(
+#endif
+                                             VTE_TERMINAL (screen),
+                                             event,
+                                             extra_regexes,
+                                             n_extra_regexes,
+                                             0,
+                                             matches))
+    {
+      for (i = 0; i < n_extra_regexes; i++)
+        {
+          if (matches[i] != NULL)
+            {
+              /* Free the others but not this one */
+              for (j = i + 1; j < n_extra_regexes; j++)
+                g_free (matches[j]);
+              if (flavor)
+                *flavor = extra_regex_flavors[i];
+              return matches[i];
+            }
+        }
+    }
+
   return NULL;
 }
 
diff --git a/src/terminal-screen.h b/src/terminal-screen.h
index a168e57..c2a4bb5 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -30,7 +30,8 @@ typedef enum {
   FLAVOR_AS_IS,
   FLAVOR_DEFAULT_TO_HTTP,
   FLAVOR_VOIP_CALL,
-  FLAVOR_EMAIL
+  FLAVOR_EMAIL,
+  FLAVOR_NUMBER,
 } TerminalURLFlavour;
 
 /* Forward decls */
diff --git a/src/terminal-util.c b/src/terminal-util.c
index 2c4e0ff..057a2d4 100644
--- a/src/terminal-util.c
+++ b/src/terminal-util.c
@@ -26,6 +26,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <langinfo.h>
 #include <errno.h>
 
 #include <glib.h>
@@ -44,6 +45,8 @@
 #include "terminal-window.h"
 #include "terminal-libgsystem.h"
 
+static const char* iec_prefixes[] = { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
+
 /**
  * terminal_util_show_error_dialog:
  * @transient_parent: parent of the future dialog window;
@@ -275,7 +278,7 @@ terminal_util_set_atk_name_description (GtkWidget  *widget,
     atk_object_set_name (obj, name);
 }
 
-void
+gboolean
 terminal_util_open_url (GtkWidget *parent,
                         const char *orig_url,
                         TerminalURLFlavour flavor,
@@ -284,7 +287,7 @@ terminal_util_open_url (GtkWidget *parent,
   gs_free_error GError *error = NULL;
   gs_free char *uri = NULL;
 
-  g_return_if_fail (orig_url != NULL);
+  g_return_val_if_fail (orig_url != NULL, FALSE);
 
   switch (flavor)
     {
@@ -301,6 +304,8 @@ terminal_util_open_url (GtkWidget *parent,
     case FLAVOR_AS_IS:
       uri = g_strdup (orig_url);
       break;
+    case FLAVOR_NUMBER:
+      return FALSE;
     default:
       uri = NULL;
       g_assert_not_reached ();
@@ -312,6 +317,7 @@ terminal_util_open_url (GtkWidget *parent,
                                        _("Could not open the address “%s”"),
                                        uri);
     }
+  return TRUE;
 }
 
 /**
@@ -951,3 +957,111 @@ terminal_util_bind_mnemonic_label_sensitivity (GtkWidget *widget)
                            (GtkCallback) terminal_util_bind_mnemonic_label_sensitivity,
                            NULL);
 }
+
+/*
+ * "1234567", "'", 3 -> "1'234'567"
+ */
+static char *
+add_separators (const char *in, const char *sep, int groupby)
+{
+  int inlen, outlen, seplen, firstgrouplen;
+  char *out, *ret;
+
+  if (in[0] == '\0')
+    return g_strdup("");
+
+  inlen = strlen(in);
+  seplen = strlen(sep);
+  outlen = inlen + (inlen - 1) / groupby * seplen;
+  ret = out = g_malloc(outlen + 1);
+
+  firstgrouplen = (inlen - 1) % groupby + 1;
+  strncpy(out, in, firstgrouplen);
+  in += firstgrouplen;
+  out += firstgrouplen;
+
+  while (*in != '\0') {
+    strncpy(out, sep, seplen);
+    out += seplen;
+    strncpy(out, in, groupby);
+    in += groupby;
+    out += groupby;
+  }
+
+  g_assert(out - ret == outlen);
+  *out = '\0';
+  return ret;
+}
+
+/**
+ * terminal_util_number_info:
+ * @str: a dec or hex number as string
+ *
+ * Returns: Useful info about @str, or %NULL if it's too large
+ */
+char *
+terminal_util_number_info (const char *str)
+{
+  gs_free char *decstr = NULL;
+  gs_free char *hextmp = NULL;
+  gs_free char *hexstr = NULL;
+  gs_free char *magnitudestr = NULL;
+  unsigned long long num;
+  gboolean exact = TRUE;
+  gboolean hex = FALSE;
+  const char *thousep;
+
+  errno = 0;
+  if (str[1] == 'x' || str[1] == 'X') {
+    num = strtoull(str + 2, NULL, 16);
+    hex = TRUE;
+  } else {
+    num = strtoull(str, NULL, 10);
+  }
+  if (errno) {
+    return NULL;
+  }
+
+  /* No use in dec-hex conversion for so small numbers */
+  if (num < 10) {
+    return NULL;
+  }
+
+  /* Group the decimal digits */
+  thousep = nl_langinfo(THOUSEP);
+  if (thousep[0] != '\0') {
+    /* If thousep is nonempty, use printf's magic which can handle
+       more complex separating logics, e.g. 2+2+2+3 for some locales */
+    decstr = g_strdup_printf("%'llu", num);
+  } else {
+    /* If, however, thousep is empty, override it with a space so that we
+       do always group the digits (that's the whole point of this feature)
+       (and btw no chance of conflicting with the decimal separator) */
+    gs_free char *tmp = g_strdup_printf("%llu", num);
+    thousep = " ";
+    decstr = add_separators(tmp, thousep, 3);
+  }
+
+  /* Group the hex digits by 4 using the same nonempty separator */
+  hextmp = g_strdup_printf("%llx", num);
+  hexstr = add_separators(hextmp, thousep, 4);
+
+  /* Find out the human-readable magnitude, e.g. 15.99 Mi */
+  if (num >= 1024) {
+    int pow = 0;
+    while (num >= 1024 * 1024) {
+      pow++;
+      if (num % 1024 != 0) exact = FALSE;
+      num /= 1024;
+    }
+    /* Show 2 fraction digits, always rounding downwards. Printf rounds floats to the nearest representable value,
+       so do the calculation with integers until we get 100-fold the desired value, and then switch to float. */
+    if (100 * num % 1024 != 0) exact = FALSE;
+    num = 100 * num / 1024;
+    magnitudestr = g_strdup_printf(" %s %.2f %s", exact ? "=" : "≈", (double) num / 100, iec_prefixes[pow]);
+  } else {
+    magnitudestr = g_strdup("");
+  }
+
+  return g_strdup_printf(hex ? "0x%2$s = %1$s%3$s" : "%s = 0x%s%s", decstr, hexstr, magnitudestr);
+}
diff --git a/src/terminal-util.h b/src/terminal-util.h
index d4b5ae3..7a59bce 100644
--- a/src/terminal-util.h
+++ b/src/terminal-util.h
@@ -41,10 +41,10 @@ void terminal_util_set_atk_name_description (GtkWidget  *widget,
                                              const char *name,
                                              const char *desc);
 
-void terminal_util_open_url (GtkWidget *parent,
-                             const char *orig_url,
-                             TerminalURLFlavour flavor,
-                             guint32 user_time);
+gboolean terminal_util_open_url (GtkWidget *parent,
+                                 const char *orig_url,
+                                 TerminalURLFlavour flavor,
+                                 guint32 user_time);
 
 void terminal_util_transform_uris_to_quoted_fuse_paths (char **uris);
 
@@ -89,6 +89,8 @@ void terminal_g_settings_set_rgba_palette (GSettings      *settings,
 
 void terminal_util_bind_mnemonic_label_sensitivity (GtkWidget *widget);
 
+char *terminal_util_number_info (const char *str);
+
 G_END_DECLS
 
 #endif /* TERMINAL_UTIL_H */
diff --git a/src/terminal-window.c b/src/terminal-window.c
index 94ec80f..1340d06 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -2094,7 +2094,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   TerminalScreen *screen = info->screen;
   GtkWidget *popup_menu;
   GtkAction *action;
-  gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link;
+  gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link, show_number;
 
   window = terminal_screen_popup_info_ref_window (info);
   if (window == NULL ||
@@ -2115,6 +2115,7 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   show_link = info->string != NULL && (info->flavour == FLAVOR_AS_IS || info->flavour == FLAVOR_DEFAULT_TO_HTTP);
   show_email_link = info->string != NULL && info->flavour == FLAVOR_EMAIL;
   show_call_link = info->string != NULL && info->flavour == FLAVOR_VOIP_CALL;
+  show_number = info->string != NULL && info->flavour == FLAVOR_NUMBER;
 
   action = gtk_action_group_get_action (priv->action_group, "PopupSendEmail");
   gtk_action_set_visible (action, show_email_link);
@@ -2128,6 +2129,17 @@ popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
   gtk_action_set_visible (action, show_link);
   action = gtk_action_group_get_action (priv->action_group, "PopupCopyLinkAddress");
   gtk_action_set_visible (action, show_link);
+  action = gtk_action_group_get_action (priv->action_group, "PopupNumber");
+  if (show_number) {
+    gs_free char *numfmt = terminal_util_number_info (info->string);
+    if (numfmt == NULL) {
+      show_number = FALSE;
+    } else {
+      gtk_action_set_label (action, numfmt);
+    }
+  }
+  gtk_action_set_sensitive (action, FALSE);
+  gtk_action_set_visible (action, show_number);
 
   action = gtk_action_group_get_action (priv->action_group, "PopupCopy");
   gtk_action_set_sensitive (action, vte_terminal_get_has_selection (VTE_TERMINAL (screen)));
@@ -2185,10 +2197,8 @@ screen_match_clicked_cb (TerminalScreen *screen,
     return FALSE;
 
   gtk_widget_grab_focus (GTK_WIDGET (screen));
-  terminal_util_open_url (GTK_WIDGET (window), match, flavour,
-                          gtk_get_current_event_time ());
-
-  return TRUE;
+  return terminal_util_open_url (GTK_WIDGET (window), match, flavour,
+                                 gtk_get_current_event_time ());
 }
 
 static void
@@ -2661,6 +2671,9 @@ terminal_window_init (TerminalWindow *window)
       { "PopupCopyLinkAddress", NULL, N_("_Copy Link Address"), NULL,
         NULL,
         G_CALLBACK (popup_copy_url_callback) },
+      { "PopupNumber", NULL, "", NULL,
+        NULL,
+        NULL },
       { "PopupTerminalProfiles", NULL, N_("P_rofiles") },
       { "PopupCopy", "edit-copy", N_("Copy"), "",
         NULL,
diff --git a/src/terminal.xml b/src/terminal.xml
index d2065b3..3c30755 100644
--- a/src/terminal.xml
+++ b/src/terminal.xml
@@ -82,6 +82,7 @@
     <menuitem action="PopupCopyCallAddress" />
     <menuitem action="PopupOpenLink" />
     <menuitem action="PopupCopyLinkAddress" />
+    <menuitem action="PopupNumber" />
     <separator />
     <menuitem action="PopupNewTerminal" />
     <separator />
