diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 29462c982d375f15a8208a8abaf8b62ad6f71f1a..98f09325808abc7077a9279fed50d7c0b52455cc 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -89,7 +89,8 @@ void Config::ReadValues() {
 
     // System
     Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
-    Settings::values.region_value = sdl2_config->GetInteger("System", "region_value", 1);
+    Settings::values.region_value =
+        sdl2_config->GetInteger("System", "region_value", Settings::REGION_VALUE_AUTO_SELECT);
 
     // Miscellaneous
     Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 001b18ac2e0aaf0fa29f5a66be98dd2c04e24253..50c4a28129c4d7508d2d9a77e8b0ee36bf7d5138 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -101,7 +101,7 @@ use_virtual_sd =
 is_new_3ds =
 
 # The system region that Citra will use during emulation
-# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
+# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
 region_value =
 
 [Miscellaneous]
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 06a4e9d25c9f5304d84344227935dab1c48b35df..c904c4b0006e1c99fd67bfbf2adc36d7e5eb9048 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -72,7 +72,8 @@ void Config::ReadValues() {
 
     qt_config->beginGroup("System");
     Settings::values.is_new_3ds = qt_config->value("is_new_3ds", false).toBool();
-    Settings::values.region_value = qt_config->value("region_value", 1).toInt();
+    Settings::values.region_value =
+        qt_config->value("region_value", Settings::REGION_VALUE_AUTO_SELECT).toInt();
     qt_config->endGroup();
 
     qt_config->beginGroup("Miscellaneous");
diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configure_general.cpp
index 03cd8835bac0e62524f007c2f079d3ddb2db0ddd..ac90a6df4726d21c4acd468ede15cdfdb4e0ad4e 100644
--- a/src/citra_qt/configure_general.cpp
+++ b/src/citra_qt/configure_general.cpp
@@ -23,13 +23,15 @@ void ConfigureGeneral::setConfiguration() {
     ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
     ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
     ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
-    ui->region_combobox->setCurrentIndex(Settings::values.region_value);
+
+    // The first item is "auto-select" with actual value -1, so plus one here will do the trick
+    ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
 }
 
 void ConfigureGeneral::applyConfiguration() {
     UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
     UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
-    Settings::values.region_value = ui->region_combobox->currentIndex();
+    Settings::values.region_value = ui->region_combobox->currentIndex() - 1;
     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
     Settings::Apply();
 }
diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configure_general.ui
index 81688113f27e7e9d6e160cb7d92830271330198f..342954e4159132dc7ffa38879c65de86349cec2f 100644
--- a/src/citra_qt/configure_general.ui
+++ b/src/citra_qt/configure_general.ui
@@ -82,6 +82,11 @@
             </item>
             <item>
              <widget class="QComboBox" name="region_combobox">
+              <item>
+               <property name="text">
+                <string>Auto-select</string>
+               </property>
+              </item>
               <item>
                <property name="text">
                 <string notr="true">JPN</string>
diff --git a/src/citra_qt/configure_system.ui b/src/citra_qt/configure_system.ui
index 6a906b61bd1ad093a48ecaeb5b32d9e39c43bfd5..cc54fa37f192b6a7c6f5c3fd1c3c50df66e26366 100644
--- a/src/citra_qt/configure_system.ui
+++ b/src/citra_qt/configure_system.ui
@@ -129,6 +129,9 @@
         </item>
         <item row="2" column="1">
          <widget class="QComboBox" name="combo_language">
+          <property name="toolTip">
+           <string>Note: this can be overridden when region setting is auto-select</string>
+          </property>
           <item>
            <property name="text">
             <string>Japanese (日本語)</string>
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 65655f45dc93d2131db5a641b0ab545dc885a10d..0bf59eb76cd20bd3e4e14176afb807889fadcf4d 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -115,6 +115,8 @@ static const std::vector<u8> cfg_system_savedata_id = {
     0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
 };
 
+static u32 preferred_region_code = 0;
+
 void GetCountryCodeString(Service::Interface* self) {
     u32* cmd_buff = Kernel::GetCommandBuffer();
     u32 country_code_id = cmd_buff[1];
@@ -160,11 +162,18 @@ void GetCountryCodeID(Service::Interface* self) {
     cmd_buff[2] = country_code_id;
 }
 
+static u32 GetRegionValue() {
+    if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
+        return preferred_region_code;
+
+    return Settings::values.region_value;
+}
+
 void SecureInfoGetRegion(Service::Interface* self) {
     u32* cmd_buff = Kernel::GetCommandBuffer();
 
     cmd_buff[1] = RESULT_SUCCESS.raw;
-    cmd_buff[2] = Settings::values.region_value;
+    cmd_buff[2] = GetRegionValue();
 }
 
 void GenHashConsoleUnique(Service::Interface* self) {
@@ -184,7 +193,7 @@ void GetRegionCanadaUSA(Service::Interface* self) {
     cmd_buff[1] = RESULT_SUCCESS.raw;
 
     u8 canada_or_usa = 1;
-    if (canada_or_usa == Settings::values.region_value) {
+    if (canada_or_usa == GetRegionValue()) {
         cmd_buff[2] = 1;
     } else {
         cmd_buff[2] = 0;
@@ -314,10 +323,47 @@ static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 fl
     return MakeResult<void*>(pointer);
 }
 
+/// Checks if the language is available in the chosen region, and returns a proper one
+static u8 AdjustLanguageInfoBlock(u32 region, u8 language) {
+    static const std::array<std::vector<u8>, 7> region_languages{{
+        // JPN
+        {LANGUAGE_JP},
+        // USA
+        {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_ES, LANGUAGE_PT},
+        // EUR
+        {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
+         LANGUAGE_RU},
+        // AUS
+        {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
+         LANGUAGE_RU},
+        // CHN
+        {LANGUAGE_ZH},
+        // KOR
+        {LANGUAGE_KO},
+        // TWN
+        {LANGUAGE_TW},
+    }};
+    const auto& available = region_languages[region];
+    if (std::find(available.begin(), available.end(), language) == available.end()) {
+        return available[0];
+    }
+    return language;
+}
+
 ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
     void* pointer;
     CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
     memcpy(output, pointer, size);
+
+    // override the language setting if the region setting is auto
+    if (block_id == LanguageBlockID &&
+        Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) {
+        u8 language;
+        memcpy(&language, output, sizeof(u8));
+        language = AdjustLanguageInfoBlock(preferred_region_code, language);
+        memcpy(output, &language, sizeof(u8));
+    }
+
     return RESULT_SUCCESS;
 }
 
@@ -535,10 +581,17 @@ void Init() {
     AddService(new CFG_U);
 
     LoadConfigNANDSaveFile();
+
+    preferred_region_code = 0;
 }
 
 void Shutdown() {}
 
+void SetPreferredRegionCode(u32 region_code) {
+    preferred_region_code = region_code;
+    LOG_INFO(Service_CFG, "Preferred region code set to %u", preferred_region_code);
+}
+
 void SetUsername(const std::u16string& name) {
     ASSERT(name.size() <= 10);
     UsernameBlock block{};
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index fb47c2aa558da4dd90e59d42a0e3f82891d05568..618c9647e66d8eb06ce711ab44d3bba74786d449 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -282,6 +282,13 @@ void Init();
 /// Shutdown the config service
 void Shutdown();
 
+/**
+ * Set the region code preferred by the game so that CFG will adjust to it when the region setting
+ * is auto.
+ * @param region_code the preferred region code to set
+ */
+void SetPreferredRegionCode(u32 region_code);
+
 // Utilities for frontend to set config data.
 // Note: before calling these functions, LoadConfigNANDSaveFile should be called,
 // and UpdateConfigNANDSavegame should be called after making changes to config data.
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 6f216442868314931cd0a2248e16c654c48c1b1d..a204dc33628786c2bcea526eeefa1f280b3146cf 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -11,8 +11,10 @@
 #include "core/file_sys/archive_romfs.h"
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/resource_limit.h"
+#include "core/hle/service/cfg/cfg.h"
 #include "core/hle/service/fs/archive.h"
 #include "core/loader/ncch.h"
+#include "core/loader/smdh.h"
 #include "core/memory.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -309,6 +311,23 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
     return ResultStatus::Success;
 }
 
+void AppLoader_NCCH::ParseRegionLockoutInfo() {
+    std::vector<u8> smdh_buffer;
+    if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) {
+        SMDH smdh;
+        memcpy(&smdh, smdh_buffer.data(), sizeof(SMDH));
+        u32 region_lockout = smdh.region_lockout;
+        constexpr u32 REGION_COUNT = 7;
+        for (u32 region = 0; region < REGION_COUNT; ++region) {
+            if (region_lockout & 1) {
+                Service::CFG::SetPreferredRegionCode(region);
+                break;
+            }
+            region_lockout >>= 1;
+        }
+    }
+}
+
 ResultStatus AppLoader_NCCH::Load() {
     if (is_loaded)
         return ResultStatus::ErrorAlreadyLoaded;
@@ -325,6 +344,9 @@ ResultStatus AppLoader_NCCH::Load() {
 
     Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this),
                                      Service::FS::ArchiveIdCode::RomFS);
+
+    ParseRegionLockoutInfo();
+
     return ResultStatus::Success;
 }
 
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 6afc171a50f2e6ddc648c1ce04d3cff2c528aceb..fe08f5b45af1ff4ab97f5ece18679e16e15ca208 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -229,6 +229,9 @@ private:
      */
     ResultStatus LoadExeFS();
 
+    /// Reads the region lockout info in the SMDH and send it to CFG service
+    void ParseRegionLockoutInfo();
+
     bool is_exefs_loaded = false;
     bool is_compressed = false;
 
diff --git a/src/core/settings.h b/src/core/settings.h
index db4c8fadaa28a94aa58d20ad6b615061d7947085..4e7a4b1be27f7db59dec0397d6ad8de1333bd94c 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -110,5 +110,9 @@ struct Values {
     u16 gdbstub_port;
 } extern values;
 
+// a special value for Values::region_value indicating that citra will automatically select a region
+// value to fit the region lockout info of the game
+static constexpr int REGION_VALUE_AUTO_SELECT = -1;
+
 void Apply();
 }