diff --git a/code/game/objects/items/devices/translator.dm b/code/game/objects/items/devices/translator.dm
new file mode 100644
index 0000000000..a66d9710c2
--- /dev/null
+++ b/code/game/objects/items/devices/translator.dm
@@ -0,0 +1,54 @@
+//Universal translator
+/obj/item/device/universal_translator
+ name = "handheld translator"
+ desc = "This handy device appears to translate the languages it hears into onscreen text for a user."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "translator"
+ w_class = ITEMSIZE_SMALL
+ origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3)
+ var/listening = 0
+ var/datum/language/langset
+
+/obj/item/device/universal_translator/attack_self(mob/user)
+ if(!listening) //Turning ON
+ langset = input(user,"Translate to which of your languages?","Language Selection") as null|anything in user.languages
+ if(langset)
+ listening = 1
+ listening_objects |= src
+ icon_state = "[initial(icon_state)]1"
+ to_chat(user, "You enable \the [src], translating into [langset.name].")
+ else //Turning OFF
+ listening = 0
+ listening_objects -= src
+ langset = null
+ icon_state = "[initial(icon_state)]"
+ to_chat(user, "You disable \the [src].")
+
+/obj/item/device/universal_translator/hear_talk(var/mob/speaker, var/message, var/vrb, var/datum/language/language)
+ if(!listening || !istype(speaker))
+ return
+
+ //Show the "I heard something" animation.
+ flick("[initial(icon_state)]2",src)
+
+ //Handheld or pocket only.
+ if(!isliving(loc))
+ return
+
+ var/mob/living/L = loc
+
+ if (language && (language.flags & NONVERBAL))
+ return //Not gonna translate sign language
+
+ //Only translate if they can't understand, otherwise pointlessly spammy
+ //I'll just assume they don't look at the screen in that case
+
+ //They don't understand the spoken language we're translating FROM
+ if(!L.say_understands(speaker,language))
+ //They understand the PRINTED language
+ if(L.say_understands(null,langset))
+ to_chat(L, "[src] displays, \"[message]\"")
+
+ //They don't understand the PRINTED language
+ else
+ to_chat(L, "[src] displays, \"[langset.scramble(message)]\"")
diff --git a/code/modules/client/preference_setup/loadout/loadout_utility.dm b/code/modules/client/preference_setup/loadout/loadout_utility.dm
index 4c2a378e7d..f9ffc0eb0b 100644
--- a/code/modules/client/preference_setup/loadout/loadout_utility.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_utility.dm
@@ -61,3 +61,8 @@
cost = 2
slot = "implant"
var/implant_type = "EAL"
+
+/datum/gear/utility/translator
+ display_name = "universal translator"
+ path = /obj/item/device/universal_translator
+ cost = 5
diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm
index ee1bbcaf2f..49678c966c 100644
--- a/code/modules/research/designs.dm
+++ b/code/modules/research/designs.dm
@@ -1586,6 +1586,14 @@ CIRCUITS BELOW
build_path = /obj/item/weapon/implant/integrated_circuit
sort_string = "VCAAE"
+/datum/design/item/translator
+ name = "handheld translator"
+ id = "translator"
+ req_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3)
+ materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 2000)
+ build_path = /obj/item/device/universal_translator
+ sort_string = "HABBA"
+
/* Uncomment if someone makes these buildable
/datum/design/circuit/general_alert
name = "general alert console"
diff --git a/html/changelogs/Anewbe - Translator.yml b/html/changelogs/Anewbe - Translator.yml
new file mode 100644
index 0000000000..2219cc592d
--- /dev/null
+++ b/html/changelogs/Anewbe - Translator.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Anewbe
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Adds a universal translator device, courtesy of a downstream."
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 3c5b0ce667..4e366be140 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/polaris.dme b/polaris.dme
index f7bfd240ce..53da36a18f 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -779,6 +779,7 @@
#include "code\game\objects\items\devices\taperecorder.dm"
#include "code\game\objects\items\devices\traitordevices.dm"
#include "code\game\objects\items\devices\transfer_valve.dm"
+#include "code\game\objects\items\devices\translator.dm"
#include "code\game\objects\items\devices\tvcamera.dm"
#include "code\game\objects\items\devices\uplink.dm"
#include "code\game\objects\items\devices\uplink_random_lists.dm"