Repository: CMDR-WDX/EDMC-Massacres
Pull request #27: feat: add internalization, add chinese translations, add missions column, add partial missions
Author: login: ckx000
Created: 2025-04-12T05:17:53Z | Merged: 2025-04-23T22:46:20Z

Added multilingual support (currently Simplified Chinese added)
Fixed a setting error
Modified and improved the handling of mission kill target completion
Added more information display (better tracking of completion progress)
Optimized layout display


Diff:
diff --git a/L10n/zh-Hans.strings b/L10n/zh-Hans.strings
new file mode 100644
index 0000000..f77c494
--- /dev/null
+++ b/L10n/zh-Hans.strings
@@ -0,0 +1,58 @@
+/* Language name */
+"!Language" = "简体中文";
+
+"Multiple Target Factions" = "多目标派系";
+
+"Multiple Target Types" = "多目标类型";
+
+"Multiple Target Systems" = "多目标星系";
+
+"Missing Active Mission Data" = "未获取到任务数据";
+
+"If you are in game, go to main menu and come back" = "如果你正在游戏,请小退到主菜单再进入";
+
+"Faction" = "任务派系";
+
+"R/T" = "R/T";
+
+"KRM/REQ" = "余/总需";
+
+"Reward (Wing)" = "奖励(可共享)";
+
+"Δmax" = "Δ差值";
+
+"CompletedSum" = "已完成合计";
+
+"AcceptedSum" = "已接任务合计";
+
+"Ratio" = "效率";
+
+"Reward" = "奖励";
+
+"M CR/Kill." = "百万 CR/每击杀. 总";
+
+"Kills" = "个任务目标数";
+
+"Mission Count" = "已接任务数量";
+
+"Massacre Plugin is Outdated" = "屠杀插件已有更新";
+
+"Go to Download" = "去下载";
+
+"Dismiss" = "拒绝";
+
+"Massacre Plugin is ready." = "清缴插件已就绪";
+
+"UI Settings" = "UI面板设定";
+
+"Display Delta-Column" = "显示最高值差值";
+
+"Display Sum-Row" = "显示合计行";
+
+"Display Summary-Row" = "显示效率信息行";
+
+"Display Mission Count" = "显示任务数量";
+
+"Other" = "其他";
+
+"Check for Updates on Start" = "启动时检查更新";
\ No newline at end of file
diff --git a/README.md b/README.md
index e213aeb..dc04e59 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,15 @@
-# ED Market Connector - Massacre Plugin
+中文说明[请点这里](./README_zh.md)
+# EDMC - Massacre Plugin
 This is a plugin for [Elite Dangerous Market Connector](https://github.com/EDCD/EDMarketConnector).
 
 Its purpose is to help you keep track of accepted Massacre-type missions in the game "Elite Dangerous".
 
-The plugin displays a table showing how many kills are required per Faction.  
+The plugin displays a table showing how many kills are required per Faction.
 If you are new to Massacre Stacking, you could take a look at [this article](https://sites.google.com/view/ed-pve-combat/making-money).
 
 <p align="center">
     <img src="./readme-src/example_screenshot.png" alt="An example screenshot"/>
     <br>
-    Screenshot of EDMC with the Plugin running.
 </p>
 
 ## Usage
@@ -17,8 +17,9 @@ If you are new to Massacre Stacking, you could take a look at [this article](htt
 Simply start EDMC and the Game. If you start EDMC after the game the plugin will ask you to go
 to main menu and back.
 
-From then on, start stacking missions :)  
-For each added or abandoned / completed missions the Table will update.
+From then on, start stacking missions :)<br>
+For each added or abandoned / completed missions the Table will update.<br>
+The status will also update each time the mission kill count is completed.
 
 ### How to read
 
@@ -30,13 +31,15 @@ Below you can see the main table's explanation:
 </p>
 
 1. These are the mission givers
-2. This is the sum of kills from all massacre missions handed out by that mission giver
-3. This is how much reward you will get upon completion, in Millions. The Value in brackets indicates how much of that is shareable with a wing.
-4. This is the Delta-Column. It displays the difference to the highest stack. The highest stack shows the difference to the second-highest stack and can be identified by the `-`
-5. The Sum-Row shows show many Kills you need to do in total, and how big the total reward is.
-6. More details showing the stack ratio (see below), rewards normalized per required kill, and the sum of all Mission-Kills.
-
-**Stack-Ratio**: This figure tells you how effective you stack is. It is calculated as follows:  
+2. These are the faction's mission counts,  display Missions with `Remaining with uncompleted target kills. / Total number of missions for this faction`
+3. This is distributed by the mission issuer:  `REMaining kills required / total kills REQuired for this faction's all missions`
+4. This is how much reward you will get upon completion, in Millions. The Value in brackets indicates how much of that is shareable with a wing.
+5. This is the Delta-Column. It displays the difference to the highest stack. The highest stack shows the difference to the second-highest stack and can be identified by the `-`
+6. The CompletedSum-row displays your current progress: `Number of missions with kill targets completed / Total kills achieved / Reward for completed portions`
+7. The AcceptedSum-Row shows show many Kills you need to do in total, and how big the total reward is.
+8. More details showing the stack ratio (see below), rewards normalized per required kill, and the sum of all Mission-Kills.
+
+**Stack-Ratio**: This figure tells you how effective you stack is. It is calculated as follows:<br>
 `stack_ratio = all_mission_kills / required_kills`. It is a value >= 1. The higher, the better.
 A Stack-ratio of 1 for example would be just taking missions from one faction. In the example above the stack ratio is `1.83 = sum([45, 54]) / max([45, 54])`.
 
@@ -52,11 +55,16 @@ and collect all Mission-Events.
 
 Also, when doing an Update-Check the `version`-File is read.
 
-
 ## Integrations
-This plugin features integrations. You can think of them as Plugins for this Plugin.
+This plugin features integrations. You can think of them as Plugins for this Plugin.<br>
 Pull Requests are welcome for new integrations. Create an Issue if you have any questions :)
 
 ### edmcoverlay (Linux)
 This integration adds the option to send data to the Linux Implementation of edmcoverlay. When you pick up new missions you will get the current stack as an overlay.
-Thank you [@pan-mroku](https://github.com/pan-mroku) for the Pull Request.
+Thank you [@pan-mroku](https://github.com/pan-mroku) for the Pull Request.<br>
+
+
+## Acknowledgments
+[ckx000](https://github.com/ckx000/EDMC-Massacres)<br>
+[AlphaConqueror](https://github.com/AlphaConqueror/EDMC-Massacres)<br>
+[CMDR-WDX](https://github.com/CMDR-WDX/EDMC-Massacres)
\ No newline at end of file
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 0000000..5ff8644
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,68 @@
+# EDMC - 清缴任务插件
+这是 [EDMC](https://github.com/EDCD/EDMarketConnector) 的一个插件
+
+其目的是帮助你在游戏《Elite Dangerous》中跟踪已接受的 Massacre 类型任务。
+
+该插件显示一个表格，展示每个派系需要多少击杀。<br>
+如果您是初次接触清缴堆叠，可以阅读[这篇文章](https://tieba.baidu.com/p/9327050589).
+
+<p align="center">
+    <img src="./readme-src/example_screenshot_cn.png" alt="An example screenshot"/>
+    <br>
+</p>
+
+## 用法
+
+只需启动 EDMC 和游戏。如果在游戏启动后才启动 EDMC，插件会要求您返回主菜单再重新进入。
+
+从那时起，开始堆叠任务 :)<br>
+每添加或放弃/完成任务时，表格都会更新。<br>
+每个任务的击杀数量完成时也会更新状态
+
+### 如何查看
+
+下面你可以看到关于表格的解释：
+
+<p align="center">
+    <img src="./readme-src/main_table_explanations_cn.png" alt="Main Table Explained"/>
+    <br>
+</p>
+
+1. 这些是任务发布者,显示为各个派系<br>
+2. 这些是该派系的任务数量,用于显示 `还剩几个未完成目标击杀数的 / 该派系任务总数`
+3. 这是由该任务发布者分发的:  `剩余需要完成击杀目标数的合计 / 所有清缴任务中击杀数的总和`<br>
+    (因无法准确的统计个数,所以使用单个任务的数量来大致计算)
+4. 这是完成后你将获得的奖励，以百万为单位。括号中的数值表示其中可分享给小队成员的金额。
+5. 这是 Delta 列。它显示与最高堆叠的差异。最高堆叠显示与第二高堆叠的差异，并可通过 `-` 标识。<br>
+6. 已完成合计行显示你目前的进度:  `已完成击杀目标的任务个数`  `已完成的击杀数量`  `完成部分的奖励规模`
+7. 已接任务合计行显示了你合计任务数量，总共需要完成的击杀次数，以及总奖励。
+8. 更多细节展示了堆叠效率（见下文）、每次所需击杀的奖励标准化值，以及所有任务击杀的总和。
+
+**堆叠效率**: 这个数值告诉你堆叠的效果如何。其计算方式如下：<br>
+`stack_ratio = all_mission_kills / required_kills`.<br>
+它是一个 >=1 的值。数值越高，效果越好。例如，堆叠效率为 1 意味着只从一个派系接取任务。<br>
+在上面的例子中，堆叠效率为 `1.83 = sum([45, 54]) / max([45, 54])`.<br>
+因为当你完成一个击杀目标时,不同派系的任务均会使击杀完成数+1
+
+### 更新
+该插件在启动时会向 GitHub 发送请求以检查是否有新版本可用。如果有新版本，插件会在 UI 中通知你。你可以在设置中关闭此行为。
+
+更新检查是本插件会唯一进行网络通信的部分。它所做的只是对[版本文件](./version)执行 GET 请求。
+
+### 文件访问
+由于 EDMC 不会跟踪任务，插件将在启动时读取过去 2 周的日志并收集所有任务事件。
+
+此外，在进行更新检查时，会读取 `version` 文件
+
+## 集成功能
+此插件具有集成功能。你可以将它们视为该插件的插件。欢迎提交拉取请求以添加新的集成。如有任何问题，请创建一个Issue :)
+
+### edmcoverlay (Linux)
+此集成增加了将数据发送到 Linux 版 edmcoverlay 的选项。当你接取新任务时，当前堆栈将作为覆盖层显示。感谢 [@pan-mroku](https://github.com/pan-mroku) 的 Pull Request。
+
+另外也支持windows版的edmcoverlay
+
+## Acknowledgments
+[ckx000](https://github.com/ckx000/EDMC-Massacres)<br>
+[AlphaConqueror](https://github.com/AlphaConqueror/EDMC-Massacres)<br>
+[CMDR-WDX](https://github.com/CMDR-WDX/EDMC-Massacres)
\ No newline at end of file
diff --git a/load.py b/load.py
index c2cdf88..50d3e05 100644
--- a/load.py
+++ b/load.py
@@ -64,7 +64,14 @@ def journal_entry(cmdr: str, _is_beta: bool, _system: str,
         if mission_repository is not None:
             mission_repository.notify_about_new_mission_accepted(entry, cmdr)
 
-    elif entry["event"] in ["MissionAbandoned", "MissionCompleted"]:  # TODO: What about MissionRedirected?
+    elif entry["event"] == "MissionRedirected":
+        # Add handling for mission objective completion; this section processes "events."
+        mission_uuid = entry["MissionID"]
+        from massacre.mission_repository import mission_repository
+        if mission_repository is not None:
+            mission_repository.notify_complete_mission_gone(mission_uuid)
+
+    elif entry["event"] in ["MissionAbandoned", "MissionCompleted"]:
         # Mission has been completed or failed -> It is no longer active
         mission_uuid = entry["MissionID"]
         from massacre.mission_repository import mission_repository
@@ -74,10 +81,9 @@ def journal_entry(cmdr: str, _is_beta: bool, _system: str,
     # Pass through the Event to any Integration that needs it
     for integration in integrations.get_all_active():
         try:
-           integration.notify_new_event(entry)
+            integration.notify_new_event(entry)
         except Exception as e:
             logger.exception(e)
-    
 
 
 def plugin_prefs(parent: Any, _cmdr: str, _is_beta: bool):
diff --git a/massacre/massacre_mission_state.py b/massacre/massacre_mission_state.py
index 5cea23e..a4797fc 100644
--- a/massacre/massacre_mission_state.py
+++ b/massacre/massacre_mission_state.py
@@ -20,6 +20,7 @@ class MassacreMission:
     target_type: str
     source_faction: str
     is_wing: bool
+    is_completed:bool # Add completion marker
     id: int
 
     def as_dict(self):
@@ -31,7 +32,8 @@ def as_dict(self):
             "reward": self.reward,
             "target_system": self.target_system,
             "source_faction": self.source_faction,
-            "is_wing": self.is_wing
+            "is_wing": self.is_wing,
+            "is_completed": self.is_completed
         }
         return as_dict
 
@@ -48,6 +50,7 @@ def __build_from_event(event: dict) -> MassacreMission:
     source_faction: str = event["Faction"]
     mission_id: int = event["MissionID"]
     wing: bool = event["Wing"]
+    completed: bool = event.get("is_completed", False)  # Retrieve possible existing completion markers
     return MassacreMission(
             target_faction, 
             count, 
@@ -56,6 +59,7 @@ def __build_from_event(event: dict) -> MassacreMission:
             target_type, 
             source_faction, 
             wing, 
+            completed,
             mission_id
         )
 
diff --git a/massacre/massacre_settings.py b/massacre/massacre_settings.py
index a690ba5..81c327a 100644
--- a/massacre/massacre_settings.py
+++ b/massacre/massacre_settings.py
@@ -2,6 +2,8 @@
 A wrapper around EDMCs Configuration
 """
 import os
+import l10n
+import functools
 from massacre.logger_factory import logger
 from massacre.version_check import download_url
 from typing import Callable, Optional
@@ -11,6 +13,7 @@
 # noinspection PyPep8Naming
 import myNotebook as nb
 
+_ = functools.partial(l10n.Translations.translate, context=__file__)
 plugin_name = os.path.basename(os.path.dirname(__file__))
 
 class Configuration:
@@ -140,22 +143,22 @@ def build_settings_ui(root: nb.Notebook) -> tk.Frame:
         tk.IntVar(value=configuration.display_mission_count)
 
 
-    nb.Label(frame, text="UI Settings", pady=10).grid(sticky=tk.W, padx=title_offset)
+    nb.Label(frame, text=_("UI Settings"), pady=10).grid(sticky=tk.W, padx=title_offset)
     ui_settings_checkboxes = [
-        nb.Checkbutton(frame, text="Display Delta-Column",
+        nb.Checkbutton(frame, text=_("Display Delta-Column"),
                        variable=__setting_changes["display_delta_column"]),
-        nb.Checkbutton(frame, text="Display Sum-Row",
+        nb.Checkbutton(frame, text=_("Display Sum-Row"),
                        variable=__setting_changes["display_sum_row"]),
-        nb.Checkbutton(frame, text="Display Summary-Row",
+        nb.Checkbutton(frame, text=_("Display Summary-Row"),
                        variable=__setting_changes["display_ratio_and_cr_per_kill_row"]),
-        nb.Checkbutton(frame, text="Display Mission Count",
+        nb.Checkbutton(frame, text=_("Display Mission Count"),
                         variable=__setting_changes["display_mission_count"])
     ]
     for entry in ui_settings_checkboxes:
         entry.grid(columnspan=2, padx=checkbox_offset, sticky=tk.W)
  
-    nb.Label(frame, text="Other", pady=10, padx=title_offset).grid(sticky=tk.W)
-    nb.Checkbutton(frame, text="Check for Updates on Start", variable=__setting_changes["check_updates"])\
+    nb.Label(frame, text=_("Other"), pady=10, padx=title_offset).grid(sticky=tk.W)
+    nb.Checkbutton(frame, text=_("Check for Updates on Start"), variable=__setting_changes["check_updates"])\
         .grid(columnspan=2, sticky=tk.W, padx=checkbox_offset)
     nb.Label(frame, text="", pady=10).grid()
     
diff --git a/massacre/mission_aggregation_helper.py b/massacre/mission_aggregation_helper.py
index acb7e2c..6336904 100644
--- a/massacre/mission_aggregation_helper.py
+++ b/massacre/mission_aggregation_helper.py
@@ -11,11 +11,17 @@
     file_location = config.get_str("journaldir")
 else:
     # noinspection SpellCheckingInspection
-    file_location = config.get("journaldir") #type: ignore
+    file_location = config.get("journaldir")  #type: ignore
 if file_location is None or file_location == "":
     file_location = config.default_journal_dir
 
 
+class MissionEventsLog:
+
+    def __init__(self) -> None:
+        self._cmdr = cmdr
+
+
 def __get_logs_after_timestamp(timestamp: dt.date) -> list[Path]:
     logs_after_timestamp = []
 
@@ -28,30 +34,6 @@ def __get_logs_after_timestamp(timestamp: dt.date) -> list[Path]:
     return logs_after_timestamp
 
 
-def __extract_mission_accepted_events_from_log(file_path: Path) -> tuple[str, list[dict]]:
-    """
-    Return all Mission-Accepted events in this file, as well as the CMDR for this log.
-    If the log does not contain a CMDR, "" is returned
-    """
-    cmdr = ""
-    return_list = []
-
-    with open(file_path, "r", encoding="utf8") as current_log_file:
-        line = current_log_file.readline()
-        while line != "":
-            try:
-                line_as_json = json.loads(line)
-                if line_as_json["event"] == "Commander":
-                    cmdr = str(line_as_json["Name"])
-                if line_as_json["event"] == "MissionAccepted":
-                    return_list.append(line_as_json)
-            except Exception:
-                logger.warning(f"Failed to open File {file_path}. Skipping...")
-            finally:
-                line = current_log_file.readline()
-        return cmdr, return_list
-
-
 # noinspection SpellCheckingInspection
 def get_missions_for_all_cmdrs(timestamp: dt.date) -> dict[str, dict[int, dict]]:
     """
@@ -64,26 +46,48 @@ def get_missions_for_all_cmdrs(timestamp: dt.date) -> dict[str, dict[int, dict]]
     :return: Dictionary [CMDR Name, Dictionary[Mission ID, Mission Object]]
     """
 
-    # This contains MissionAccepted-events from all CMDRs
-    all_mission_logs_after_timestamp_for_all_cmdrs = \
-        map(__extract_mission_accepted_events_from_log, __get_logs_after_timestamp(timestamp))
-
-    return_list: dict[str, list[dict]] = {}
-
-    # This contains the events in a normal list
-    for cmdr_from_event, events in all_mission_logs_after_timestamp_for_all_cmdrs:
-        if cmdr_from_event not in return_list.keys():
-            return_list[cmdr_from_event] = []
-
-        return_list[cmdr_from_event].extend(events)
-
-    # Now create a UUID -> Mission Lookup for each CMDR
-    return_array: dict[str, dict[int, dict]] = {}
-
-    for cmdr in return_list.keys():
-        return_array[cmdr] = {}
-        for event in return_list[cmdr]:
-            return_array[cmdr][event["MissionID"]] = event
-
-
-    return return_array
+    current_name: str = ""
+    current_dict: dict[int, dict] = {}
+    return_map: dict[str, dict[int, dict]] = {}
+
+    for path in __get_logs_after_timestamp(timestamp):
+        logger.debug(f"Opening file {path} ...")
+
+        with open(path, "r", encoding="utf8") as current_log_file:
+            line = current_log_file.readline()
+
+            while line != "":
+                try:
+                    line_as_json = json.loads(line)
+
+                    if line_as_json["event"] == "Commander":
+                        if current_name != "" and current_dict is not {}:
+                            return_map[current_name] = current_dict
+
+                        name = str(line_as_json["Name"])
+
+                        if current_name != name:
+                            current_name = name
+                            current_dict = {}
+
+                    if current_name != "":
+                        if line_as_json["event"] == "MissionAccepted":
+                            current_dict[line_as_json["MissionID"]] = line_as_json
+                        elif line_as_json["event"] == "MissionRedirected":
+                            mission_id = line_as_json["MissionID"]
+                            """
+                            This section processes the MissionRedirected event in log files.
+                            For massacre missions, the format is consistent. Simply check if the MissionID exists in the original mission list to confirm completion.
+                            """
+                            if mission_id in current_dict:
+                                # missions completed
+                                current_dict[mission_id]["is_completed"] = True
+                except Exception:
+                    logger.warning("An error occurred, skipping line.")
+                finally:
+                    line = current_log_file.readline()
+
+    if current_name != "" and current_dict is not {}:
+        return_map[current_name] = current_dict
+
+    return return_map
diff --git a/massacre/mission_repository.py b/massacre/mission_repository.py
index 9f73b27..69f7f2a 100644
--- a/massacre/mission_repository.py
+++ b/massacre/mission_repository.py
@@ -78,7 +78,7 @@ def notify_about_active_mission_uuids(self, uuids: list[int], cmdr: str):
         if self._state & MissionRepoState.HAS_MISSIONS_EVENT == 0:
             self._state |= MissionRepoState.HAS_MISSIONS_EVENT
         else:
-            logger.warning("Mission UUIDs were passed even though the State is already initialized")
+            logger.warning("Mission UUIDs were passed even though the State is already initialized. CMDR: "+cmdr+"  UUIDs: %s", repr(uuids))
             pass
 
         self._active_missions = {}
@@ -88,7 +88,7 @@ def notify_about_active_mission_uuids(self, uuids: list[int], cmdr: str):
             if uuid in all_known_uuids:
                 self._active_missions[uuid] = self._mission_store[cmdr][uuid]
             else:
-                logger.warning("A Mission could not be found in the Store even though the UUID is present")
+                logger.warning("A Mission could not be found in the Store even though the UUID is present. UUID: %s  all_known_uuids: %s", uuid, all_known_uuids)
                 pass
 
         #  Emit an Event notifying that the pool of active missions has changed
@@ -110,6 +110,13 @@ def notify_about_mission_gone(self, mission_uuid: int):
         for listener in active_missions_changed_event_listeners:
             listener(self._active_missions)
 
+    def notify_complete_mission_gone(self, mission_uuid: int):
+        # Add a completed mission objective and mark it
+        logger.info(f"Mission with ID {mission_uuid} has been Complete")
+        # Mark as completed
+        self._active_missions[mission_uuid]["is_completed"] = True
+        self.update_all_listeners()
+
     def update_all_listeners(self):
         global active_missions_changed_event_listeners, all_missions_changed_event_listeners
         for listener in active_missions_changed_event_listeners:
diff --git a/massacre/ui.py b/massacre/ui.py
index 1cb661b..e783036 100644
--- a/massacre/ui.py
+++ b/massacre/ui.py
@@ -1,4 +1,6 @@
 import json
+import l10n
+import functools
 import tkinter as tk
 from typing import Optional
 from dataclasses import dataclass
@@ -10,6 +12,7 @@
 from massacre.version_check import open_download_page
 from theme import theme
 
+_ = functools.partial(l10n.Translations.translate, context=__file__)
 
 class MassacreMissionData:
     """
@@ -19,7 +22,11 @@ class MassacreMissionData:
 
     @dataclass
     class FactionState:
-        killcount: int
+        
+        all_mission_count: int # Add the number of missions for this faction.
+        completed_mission_count: int # Number of completed missions for this faction
+        completed_kill_count: int # Number of completed kills for this faction
+        kill_count: int
         reward: int 
         shareable_reward: int
 
@@ -66,6 +73,7 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
         This is used for the delta-Column of the highest Stack to show the negative
         delta towards the second-highest stack.
         """
+
         self.target_sum = 0
         """
         The amount of total mission kills (not total required kills (see stack_height))
@@ -82,6 +90,10 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
         """
         How many (massacre) missions does the user currently have.
         """
+        self.completed_mission_count = 0 # Add the total number of completed missions.
+        self.completed_reward = 0 # Add the total rewards from completed missions.
+        self.completed_shareable_reward = 0 # Add the total wing mission rewards from completed missions.
+        self.completed_kill_count = 0 # Add the total completed kills.
 
         for mission in massacre_state.values():
             mission_giver = mission.source_faction
@@ -89,15 +101,24 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
 
             if mission_giver not in self.faction_to_count_lookup.keys():
                 """If no Mission from that Faction is known yet, it will first be initialized"""
-                self.faction_to_count_lookup[mission_giver] = MassacreMissionData.FactionState(0, 0, 0)
+                self.faction_to_count_lookup[mission_giver] = MassacreMissionData.FactionState(0, 0, 0, 0, 0, 0)
 
             faction_state = self.faction_to_count_lookup[mission_giver]
             """
             Get the currently summed kill count and rewards from this faction. This might contain data
             from previous Missions from that faction, or 0,0,0 if this is the first mission.
             """
+            
+            faction_state.all_mission_count += 1 # Add a count for the number of missions from this faction
+            if mission.is_completed: 
+                faction_state.completed_mission_count += 1 # Accumulate the count of completed missions.
+                faction_state.completed_kill_count += mission.count # Accumulate the count of completed kills.
+                self.completed_reward += mission.reward # Add rewards from completed missions to the total.
+                if mission.is_wing: # Add rewards from completed wing missions to the total.
+                    self.completed_shareable_reward += mission.reward
+
             faction_state.killcount += mission.count
-            self.target_sum += mission.count
+            self.target_sum += mission.count 
             faction_state.reward += mission.reward
             # Only wing missions are considered for shareable rewards
             if mission.is_wing:
@@ -122,14 +143,18 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
         for faction_state in self.faction_to_count_lookup.values():
             self.reward += faction_state.reward
             self.shareable_reward += faction_state.shareable_reward
+            self.completed_mission_count += faction_state.completed_mission_count # Total count of all completed missions (aggregated per faction).
+            # Loop through each faction to find the largest total kills count and use it as the aggregated kill count for all missions
+            if faction_state.completed_kill_count > self.completed_kill_count:
+                self.completed_kill_count = faction_state.completed_kill_count
 
         # Check for Warnings
         if len(target_factions) > 1:
-            self.warnings.append(f"Multiple Target Factions: {', '.join(target_factions)}!")
+            self.warnings.append(f"{_('Multiple Target Factions')}: {', '.join(target_factions)}!")
         if len(target_types) > 1:
-            self.warnings.append(f"Multiple Target Types: {', '.join(target_types)}!")
+            self.warnings.append(f"{_('Multiple Target Types')}: {', '.join(target_types)}!")
         if len(target_systems) > 1:
-            self.warnings.append(f"Multiple Target Systems: {', '.join(target_systems)}!")
+            self.warnings.append(f"{_('Multiple Target Systems')}: {', '.join(target_systems)}!")
 
         # Calculate before_stack_height
         for faction_state in self.faction_to_count_lookup.values():
@@ -144,7 +169,7 @@ class GridUiSettings:
     Subset of the entire Configuration that focuses on which information is displayed
     """
     def __init__(self, config: Configuration):
-        self.sum = config.display_delta_column
+        self.sum = config.display_sum_row # fix error
         self.delta = config.display_delta_column
         self.summary = config.display_ratio_and_cr_per_kill_row
         self.mission_count = config.display_mission_count
@@ -156,8 +181,8 @@ def __get_row_width(settings: GridUiSettings) -> int:
     This depends on if the delta-Column should be displayed
     """
     if settings.delta:
-        return 4
-    return 3
+        return 5
+    return 4
 
 
 def _display_no_data_info(frame: tk.Frame):
@@ -167,8 +192,7 @@ def _display_no_data_info(frame: tk.Frame):
     Return Row-Pointer for next row
     """
 
-    warning_label = tk.Label(frame, text="Missing Active Mission Data.\n"
-                                         "If you are in game, go to main menu and come back")
+    warning_label = tk.Label(frame, text=_("Missing Active Mission Data")+"\n"+_("If you are in game, go to main menu and come back"))
     warning_label.config(foreground="yellow")
     warning_label.grid(column=0, row=0)
 
@@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
+    faction_label = tk.Label(frame, text=_("Faction"))
+    mission_num_label = tk.Label(frame, text=_("R/T")) # Add the number of missions.
+    kills_label = tk.Label(frame, text=_("KRM/REQ"))
+    payout_label = tk.Label(frame, text=_("Reward (Wing)"))
+
+    ui_elements = [faction_label, mission_num_label, kills_label, payout_label]
     if settings.delta:
         # noinspection SpellCheckingInspection
-        delta_label = tk.Label(frame, text="Δmax")
+        delta_label = tk.Label(frame, text=_("Δmax"))
         ui_elements.append(delta_label)
 
     for i, item in enumerate(ui_elements):
@@ -204,35 +231,55 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
 
     faction_label = tk.Label(frame, text=faction)
-    kills_label = tk.Label(frame, text=data.killcount)
+    completed_mission_count_sum = int(data.all_mission_count) - int(data.completed_mission_count)
+    mission_num_label = tk.Label(frame, text=f"{completed_mission_count_sum}/{data.all_mission_count}") # Add the number of missions.
+    completed_kill_sum =int(data.killcount) - int(data.completed_kill_count)
+    kills_label = tk.Label(frame, text=f"{completed_kill_sum}/{data.killcount}") # Modify the kill count display
     payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
 
-    ui_elements = [faction_label, kills_label, payout_label]
-
-    if settings.delta:
+    ui_elements = [faction_label, mission_num_label, kills_label, payout_label]
+    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
+    if settings.delta: 
         # Calculate difference
         delta = max_count - data.killcount
         text = delta if delta > 0 else second_largest_count - max_count
         delta_label = tk.Label(frame, text=str(text))
         ui_elements.append(delta_label)
-        delta_label.grid(row=row, column=3)
+        #delta_label.grid(row=row, column=4) # Next line of code processed.
 
     for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=tk.W)
+        element.grid(row=row, column=i, sticky=sticky_settings[i])
 
+def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    """
+    Add a row to display the total number of currently completed missions.
+    """
+    label = tk.Label(frame, text=_("CompletedSum"))
+    
+    completed_num = tk.Label(frame, text=data.completed_mission_count) # Number of completed missions.
+    kill_sum = tk.Label(frame, text=data.completed_kill_count) # Number of kills completed in missions.
+    reward_sum_normal = "{:.1f}".format(float(data.completed_reward) / 1_000_000)
+    reward_sum_wing = "{:.1f}".format(float(data.completed_shareable_reward) / 1_000_000)
+    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
+    sticky_settings = [tk.W, tk.E, tk.E, tk.E]
+    for i, entry in enumerate([label, completed_num, kill_sum, reward_sum]):
+        entry.config(fg="YellowGreen")
+        entry.grid(row=row, column=i, sticky=sticky_settings[i])
 
 def __display_sum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
     """
     Display the Sum-Row containing the Reward-Sum and the amount of Kills required.
     """
-    label = tk.Label(frame, text="Sum")
+    label = tk.Label(frame, text=_("AcceptedSum"))
+    all_missions_num = tk.Label(frame, text=data.mission_count)
     kill_sum = tk.Label(frame, text=data.stack_height)
     reward_sum_normal = "{:.1f}".format(float(data.reward) / 1_000_000)
     reward_sum_wing = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
     reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
-    for i, entry in enumerate([label, kill_sum, reward_sum]):
+    sticky_settings = [tk.W, tk.E, tk.E, tk.E]
+    for i, entry in enumerate([label, all_missions_num, kill_sum, reward_sum]):
         entry.config(fg="green")
-        entry.grid(row=row, column=i, sticky=tk.W)
+        entry.grid(row=row, column=i, sticky=sticky_settings[i])
 
 
 def __display_summary(frame: tk.Frame, data: MassacreMissionData, settings: GridUiSettings, row: int):
@@ -241,7 +288,7 @@ def __display_summary(frame: tk.Frame, data: MassacreMissionData, settings: Grid
     wing_reward_in_millions = float(data.shareable_reward) / 1_000_000
     reward_text = "{:.2f}".format(reward_in_millions/data.stack_height)
     wing_reward_text = "{:.2f}".format(wing_reward_in_millions/data.stack_height)
-    label_text = f"Ratio: {ratio_text}, Reward: {reward_text} ({wing_reward_text}) M CR/Kill. {data.target_sum} Kills."
+    label_text = f"{_('Ratio')}: {ratio_text}, {_('Reward')}: {reward_text} ({wing_reward_text}) {_('M CR/Kill.')} {data.target_sum} {_('Kills')}."
 
     label = tk.Label(frame, text=label_text, fg="green")
     label.grid(row=row, column=0, columnspan=__get_row_width(settings), sticky=tk.W)
@@ -260,8 +307,11 @@ def _display_data(frame: tk.Frame, data: MassacreMissionData, settings: GridUiSe
         __display_row(frame, faction, data.faction_to_count_lookup[faction], data.stack_height, settings, row_pointer,
                       data.before_stack_height)
         row_pointer += 1
+        # todo In the future, add a toggle button here to display detailed mission information under each faction.
 
     if settings.sum:
+        __display_cmpsum(frame, data, settings, row_pointer)
+        row_pointer += 1
         __display_sum(frame, data, settings, row_pointer)
         row_pointer += 1
 
@@ -269,6 +319,7 @@ def _display_data(frame: tk.Frame, data: MassacreMissionData, settings: GridUiSe
         __display_summary(frame, data, settings, row_pointer)
         row_pointer += 1
     full_width = __get_row_width(settings)
+
     if settings.mission_count:
         __display_mission_count(frame, data, full_width, row_pointer)
         row_pointer += 1
@@ -281,17 +332,17 @@ def _display_data(frame: tk.Frame, data: MassacreMissionData, settings: GridUiSe
 
 
 def __display_mission_count(frame: tk.Frame, data: MassacreMissionData, width: int, row: int):
-    label = tk.Label(frame, text=f"Mission Count: {data.mission_count}/20")
-    label.config(fg="white")
+    label = tk.Label(frame, text=f"{_('Mission Count')}: {data.mission_count}/20")
+    #label.config(fg="white") # White is hard to see against the default white appearance—switch to blue, light blue, or use EDMC's theme color.
     label.grid(column=0, columnspan=width, row=row, sticky=tk.W)
 
 def _display_outdated_version(frame: tk.Frame, settings: GridUiSettings, row: int) -> int:
     sub_frame = tk.Frame(frame)
     sub_frame.grid(row=row, column=0, columnspan=__get_row_width(settings))
     sub_frame.config(pady=10)
-    tk.Label(sub_frame, text="Massacre Plugin is Outdated").grid(row=0, column=0, columnspan=2)
-    btn_github = tk.Button(sub_frame, text="Go to Download", command=open_download_page)
-    btn_dismiss = tk.Button(sub_frame, text="Dismiss", command=ui.notify_version_outdated_dismissed)
+    tk.Label(sub_frame, text=_("Massacre Plugin is Outdated")).grid(row=0, column=0, columnspan=2)
+    btn_github = tk.Button(sub_frame, text=_("Go to Download"), command=open_download_page)
+    btn_dismiss = tk.Button(sub_frame, text=_("Dismiss"), command=ui.notify_version_outdated_dismissed)
 
     for i, item in enumerate([btn_github, btn_dismiss]):
         item.grid(row=1, column=i)
@@ -300,7 +351,7 @@ def _display_outdated_version(frame: tk.Frame, settings: GridUiSettings, row: in
 
 
 def _display_waiting_for_missions(frame: tk.Frame):
-    tk.Label(frame, text="Massacre Plugin is ready.").grid()
+    tk.Label(frame, text=_("Massacre Plugin is ready.")).grid()
     return 1
 
 
@@ -325,6 +376,7 @@ def set_frame(self, frame: tk.Frame):
         if cspan < 1:
             cspan = 2
         self.__frame = tk.Frame(frame)
+        #self.__frame.config(bg="red") # debug Test layout display.
         self.__frame.grid(column=0, columnspan=cspan, sticky=tk.W)
         self.__frame.bind("<<Refresh>>", lambda _: self.update_ui())
         self.update_ui()
diff --git a/readme-src/example_screenshot.png b/readme-src/example_screenshot.png
index 21231b2..8c18b0a 100644
Binary files a/readme-src/example_screenshot.png and b/readme-src/example_screenshot.png differ
diff --git a/readme-src/example_screenshot_cn.png b/readme-src/example_screenshot_cn.png
new file mode 100644
index 0000000..24e8203
Binary files /dev/null and b/readme-src/example_screenshot_cn.png differ
diff --git a/readme-src/main_table_explanations.png b/readme-src/main_table_explanations.png
index a179097..87f0407 100644
Binary files a/readme-src/main_table_explanations.png and b/readme-src/main_table_explanations.png differ
diff --git a/readme-src/main_table_explanations_cn.png b/readme-src/main_table_explanations_cn.png
new file mode 100644
index 0000000..f7efbdb
Binary files /dev/null and b/readme-src/main_table_explanations_cn.png differ
diff --git a/version b/version
index e010258..7ec1d6d 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2.0.5
+2.1.0


Reviews:
user: login: CMDR-WDX
body: General changes look good. I left some minor comments here and there. 

I would ask you not to make a new version just yet. After the merge I would also add German translations and extend the Settings a bit.

I would afterwards add another option to Hide the Info about partially finished missions because I think some people could be overwhelmed with the new information.
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-04-16T23:22:24Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T01:08:07Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T01:10:55Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T01:26:09Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T01:28:11Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T01:55:49Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T02:02:16Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T02:08:53Z
user: login: ckx000
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T02:11:51Z
user: login: CMDR-WDX
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-17T14:27:30Z
user: login: CMDR-WDX
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-17T14:31:43Z
user: login: CMDR-WDX
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-17T14:32:48Z
user: login: CMDR-WDX
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-17T14:34:19Z
user: login: CMDR-WDX
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-17T14:35:12Z

Review comments:
diff_hunk: @@ -1 +1 @@
-2.0.5
+2.1.0
path: version
user: login: CMDR-WDX
body: can you please undo the version change. I want to add German translations after this is merged :)
created_at: 2025-04-16T21:54:44Z
author_association: OWNER
diff_hunk: @@ -325,6 +375,7 @@ def set_frame(self, frame: tk.Frame):
         if cspan < 1:
             cspan = 2
         self.__frame = tk.Frame(frame)
+        #self.__frame.config(bg="red") # debug
path: massacre/ui.py
user: login: CMDR-WDX
body: ```suggestion
```
created_at: 2025-04-16T22:04:46Z
author_association: OWNER
diff_hunk: @@ -204,35 +231,54 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
 
     faction_label = tk.Label(frame, text=faction)
-    kills_label = tk.Label(frame, text=data.killcount)
+    cmpnum_sum = int(data.allnum) - int(data.cmpnum)
+    missionnum_label = tk.Label(frame, text=f"{cmpnum_sum}/{data.allnum}") # Add the number of missions.
+    killscmp_sum =int(data.killcount) - int(data.killcmpcount)
+    kills_label = tk.Label(frame, text=f"{killscmp_sum}/{data.killcount}") # Modify the kill count display
     payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
 
-    ui_elements = [faction_label, kills_label, payout_label]
-
-    if settings.delta:
+    ui_elements = [faction_label, missionnum_label, kills_label, payout_label]
+    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
+    if settings.delta: 
         # Calculate difference
         delta = max_count - data.killcount
         text = delta if delta > 0 else second_largest_count - max_count
         delta_label = tk.Label(frame, text=str(text))
         ui_elements.append(delta_label)
-        delta_label.grid(row=row, column=3)
+        #delta_label.grid(row=row, column=4) # Next line of code processed.
 
     for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=tk.W)
+        element.grid(row=row, column=i, sticky=sticky_settings[i])
 
+def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    """
+    Add a row to display the total number of currently completed missions.
+    """
+    label = tk.Label(frame, text=_("CompletedSum"))
+    
+    cmp_num = tk.Label(frame, text=data.cmpmission_count) # Number of completed missions.
+    kill_sum = tk.Label(frame, text=data.killcmpcount) # Number of kills completed in missions.
+    reward_sum_normal = "{:.1f}".format(float(data.cmpreward) / 1_000_000)
+    reward_sum_wing = "{:.1f}".format(float(data.cmpshareable_reward) / 1_000_000)
+    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
+    for i, entry in enumerate([label, cmp_num, kill_sum, reward_sum]):
+        entry.config(fg="YellowGreen")
+        entry.grid(row=row, column=i, sticky=tk.W)
path: massacre/ui.py
user: login: CMDR-WDX
body: please also use the same column ordering like the Sum column. It might even make sense to have the sum be part of the previous element and just use a newline.

Otherwise the ordering here is a bit confusing
![image](https://github.com/user-attachments/assets/b429b0d7-fd65-4748-9d69-7fea2310f6cf)

created_at: 2025-04-16T22:35:06Z
author_association: OWNER
diff_hunk: @@ -204,35 +231,54 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
 
     faction_label = tk.Label(frame, text=faction)
-    kills_label = tk.Label(frame, text=data.killcount)
+    cmpnum_sum = int(data.allnum) - int(data.cmpnum)
+    missionnum_label = tk.Label(frame, text=f"{cmpnum_sum}/{data.allnum}") # Add the number of missions.
+    killscmp_sum =int(data.killcount) - int(data.killcmpcount)
+    kills_label = tk.Label(frame, text=f"{killscmp_sum}/{data.killcount}") # Modify the kill count display
     payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
 
-    ui_elements = [faction_label, kills_label, payout_label]
-
-    if settings.delta:
+    ui_elements = [faction_label, missionnum_label, kills_label, payout_label]
+    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
+    if settings.delta: 
         # Calculate difference
         delta = max_count - data.killcount
         text = delta if delta > 0 else second_largest_count - max_count
         delta_label = tk.Label(frame, text=str(text))
         ui_elements.append(delta_label)
-        delta_label.grid(row=row, column=3)
+        #delta_label.grid(row=row, column=4) # Next line of code processed.
 
     for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=tk.W)
+        element.grid(row=row, column=i, sticky=sticky_settings[i])
 
+def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    """
+    Add a row to display the total number of currently completed missions.
+    """
+    label = tk.Label(frame, text=_("CompletedSum"))
+    
+    cmp_num = tk.Label(frame, text=data.cmpmission_count) # Number of completed missions.
+    kill_sum = tk.Label(frame, text=data.killcmpcount) # Number of kills completed in missions.
+    reward_sum_normal = "{:.1f}".format(float(data.cmpreward) / 1_000_000)
+    reward_sum_wing = "{:.1f}".format(float(data.cmpshareable_reward) / 1_000_000)
+    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
+    for i, entry in enumerate([label, cmp_num, kill_sum, reward_sum]):
+        entry.config(fg="YellowGreen")
+        entry.grid(row=row, column=i, sticky=tk.W)
path: massacre/ui.py
user: login: CMDR-WDX
body: actually… what about merging those?

Instead of 
```
CompletedSum   X   X   X.X (X.X)
AcceptedSum      X   X   X.X (X.X)
```

``` 
Finished/All ∑ X/X X/X X.X (X.X)/
                       X.X (X.X) 
```


created_at: 2025-04-16T22:39:23Z
author_association: OWNER
diff_hunk: @@ -204,35 +231,54 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
 
     faction_label = tk.Label(frame, text=faction)
-    kills_label = tk.Label(frame, text=data.killcount)
+    cmpnum_sum = int(data.allnum) - int(data.cmpnum)
+    missionnum_label = tk.Label(frame, text=f"{cmpnum_sum}/{data.allnum}") # Add the number of missions.
+    killscmp_sum =int(data.killcount) - int(data.killcmpcount)
+    kills_label = tk.Label(frame, text=f"{killscmp_sum}/{data.killcount}") # Modify the kill count display
     payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
 
-    ui_elements = [faction_label, kills_label, payout_label]
-
-    if settings.delta:
+    ui_elements = [faction_label, missionnum_label, kills_label, payout_label]
+    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
+    if settings.delta: 
         # Calculate difference
         delta = max_count - data.killcount
         text = delta if delta > 0 else second_largest_count - max_count
         delta_label = tk.Label(frame, text=str(text))
         ui_elements.append(delta_label)
-        delta_label.grid(row=row, column=3)
+        #delta_label.grid(row=row, column=4) # Next line of code processed.
 
     for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=tk.W)
+        element.grid(row=row, column=i, sticky=sticky_settings[i])
 
+def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    """
+    Add a row to display the total number of currently completed missions.
+    """
+    label = tk.Label(frame, text=_("CompletedSum"))
+    
+    cmp_num = tk.Label(frame, text=data.cmpmission_count) # Number of completed missions.
+    kill_sum = tk.Label(frame, text=data.killcmpcount) # Number of kills completed in missions.
+    reward_sum_normal = "{:.1f}".format(float(data.cmpreward) / 1_000_000)
+    reward_sum_wing = "{:.1f}".format(float(data.cmpshareable_reward) / 1_000_000)
+    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
+    for i, entry in enumerate([label, cmp_num, kill_sum, reward_sum]):
+        entry.config(fg="YellowGreen")
+        entry.grid(row=row, column=i, sticky=tk.W)
 
 def __display_sum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
     """
     Display the Sum-Row containing the Reward-Sum and the amount of Kills required.
     """
-    label = tk.Label(frame, text="Sum")
+    label = tk.Label(frame, text=_("AcceptedSum"))
+    allmissionnum = tk.Label(frame, text=data.mission_count)
path: massacre/ui.py
user: login: CMDR-WDX
body: ```suggestion
    all_missions_num = tk.Label(frame, text=data.mission_count)
```
created_at: 2025-04-16T22:40:32Z
author_association: OWNER
diff_hunk: @@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
path: massacre/ui.py
user: login: CMDR-WDX
body: ```suggestion
```
I wouldn't add a toggle. There is little gain to see the individual missions
created_at: 2025-04-16T22:43:08Z
author_association: OWNER
diff_hunk: @@ -82,22 +90,35 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
         """
         How many (massacre) missions does the user currently have.
         """
+        self.cmpmission_count = 0 # Add the total number of completed missions.
+        self.cmpreward = 0 # Add the total rewards from completed missions.
+        self.cmpshareable_reward = 0 # Add the total wing mission rewards from completed missions.
+        self.killcmpcount = 0 # Add the total completed kills.
path: massacre/ui.py
user: login: CMDR-WDX
body: please use snake for these. Also write out `completed` because `cmp` usually means "compare". e.g. `completed_mission_count`, `completed_reward`, etc

Also thanks for translating the comments back to english :)
created_at: 2025-04-16T22:47:14Z
author_association: OWNER
diff_hunk: @@ -19,6 +22,10 @@ class MassacreMissionData:
 
     @dataclass
     class FactionState:
+        
+        allnum: int # Add the number of missions for this faction.
path: massacre/ui.py
user: login: CMDR-WDX
body: ```suggestion
        all_mission_count: int # Add the number of missions for this faction.
```
created_at: 2025-04-16T22:58:01Z
author_association: OWNER
diff_hunk: @@ -19,6 +22,10 @@ class MassacreMissionData:
 
     @dataclass
     class FactionState:
+        
+        allnum: int # Add the number of missions for this faction.
+        cmpnum: int # Number of completed missions for this faction
path: massacre/ui.py
user: login: CMDR-WDX
body: ```suggestion
        completed_mission_count: int # Number of completed missions for this faction
```
created_at: 2025-04-16T22:58:18Z
author_association: OWNER
diff_hunk: @@ -19,6 +22,10 @@ class MassacreMissionData:
 
     @dataclass
     class FactionState:
+        
+        allnum: int # Add the number of missions for this faction.
+        cmpnum: int # Number of completed missions for this faction
+        killcmpcount: int # Number of completed kills for this faction
         killcount: int
path: massacre/ui.py
user: login: CMDR-WDX
body: ```suggestion
        completed_kill_count: int # Number of completed kills for this faction
        kill_count: int
```
created_at: 2025-04-16T23:02:52Z
author_association: OWNER
diff_hunk: @@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
+    faction_label = tk.Label(frame, text=_("Faction"))
+    missionnum_label = tk.Label(frame, text=_("R/T")) # Add the number of missions.
path: massacre/ui.py
user: login: CMDR-WDX
body: I wonder which  value seeing how many missions one has for one faction, and how many of them are already done, brings.

I can see the value for Kills, but not for missions. Could we make this an optional column that can be added in the settings?
created_at: 2025-04-16T23:17:45Z
author_association: OWNER
diff_hunk: @@ -1 +1 @@
-2.0.5
+2.1.0
path: version
user: login: ckx000
body: Of course, we can delay merging this change until you add German support. As for GitHub operations, I'm not entirely clear, but it seems possible to modify directly within this pull request?
created_at: 2025-04-17T01:08:07Z
author_association: NONE
diff_hunk: @@ -325,6 +375,7 @@ def set_frame(self, frame: tk.Frame):
         if cspan < 1:
             cspan = 2
         self.__frame = tk.Frame(frame)
+        #self.__frame.config(bg="red") # debug
path: massacre/ui.py
user: login: ckx000
body: This commented-out code was used to display the debug UI layout. Are you suggesting that such debug code should not be retained in the codebase either?
created_at: 2025-04-17T01:10:55Z
author_association: NONE
diff_hunk: @@ -204,35 +231,54 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
 
     faction_label = tk.Label(frame, text=faction)
-    kills_label = tk.Label(frame, text=data.killcount)
+    cmpnum_sum = int(data.allnum) - int(data.cmpnum)
+    missionnum_label = tk.Label(frame, text=f"{cmpnum_sum}/{data.allnum}") # Add the number of missions.
+    killscmp_sum =int(data.killcount) - int(data.killcmpcount)
+    kills_label = tk.Label(frame, text=f"{killscmp_sum}/{data.killcount}") # Modify the kill count display
     payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
 
-    ui_elements = [faction_label, kills_label, payout_label]
-
-    if settings.delta:
+    ui_elements = [faction_label, missionnum_label, kills_label, payout_label]
+    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
+    if settings.delta: 
         # Calculate difference
         delta = max_count - data.killcount
         text = delta if delta > 0 else second_largest_count - max_count
         delta_label = tk.Label(frame, text=str(text))
         ui_elements.append(delta_label)
-        delta_label.grid(row=row, column=3)
+        #delta_label.grid(row=row, column=4) # Next line of code processed.
 
     for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=tk.W)
+        element.grid(row=row, column=i, sticky=sticky_settings[i])
 
+def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    """
+    Add a row to display the total number of currently completed missions.
+    """
+    label = tk.Label(frame, text=_("CompletedSum"))
+    
+    cmp_num = tk.Label(frame, text=data.cmpmission_count) # Number of completed missions.
+    kill_sum = tk.Label(frame, text=data.killcmpcount) # Number of kills completed in missions.
+    reward_sum_normal = "{:.1f}".format(float(data.cmpreward) / 1_000_000)
+    reward_sum_wing = "{:.1f}".format(float(data.cmpshareable_reward) / 1_000_000)
+    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
+    for i, entry in enumerate([label, cmp_num, kill_sum, reward_sum]):
+        entry.config(fg="YellowGreen")
+        entry.grid(row=row, column=i, sticky=tk.W)
path: massacre/ui.py
user: login: ckx000
body: I previously tried debugging this display method, but there's an issue: if forced to display in a single row, it stretches the grid layout excessively wide, causing awkward spacing in the data above.
And putting "Total Rewards" on a separate line feels like something is missing.

The intentional left/right alignment of these two rows was designed to mimic an "x/x" display style without the stretching caused by forcing single-row layout. (Apologies if the English doesn’t fully convey my intent.) Alternatively, should both rows be right-aligned?

created_at: 2025-04-17T01:26:09Z
author_association: NONE
diff_hunk: @@ -204,35 +231,54 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
 
     faction_label = tk.Label(frame, text=faction)
-    kills_label = tk.Label(frame, text=data.killcount)
+    cmpnum_sum = int(data.allnum) - int(data.cmpnum)
+    missionnum_label = tk.Label(frame, text=f"{cmpnum_sum}/{data.allnum}") # Add the number of missions.
+    killscmp_sum =int(data.killcount) - int(data.killcmpcount)
+    kills_label = tk.Label(frame, text=f"{killscmp_sum}/{data.killcount}") # Modify the kill count display
     payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
 
-    ui_elements = [faction_label, kills_label, payout_label]
-
-    if settings.delta:
+    ui_elements = [faction_label, missionnum_label, kills_label, payout_label]
+    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
+    if settings.delta: 
         # Calculate difference
         delta = max_count - data.killcount
         text = delta if delta > 0 else second_largest_count - max_count
         delta_label = tk.Label(frame, text=str(text))
         ui_elements.append(delta_label)
-        delta_label.grid(row=row, column=3)
+        #delta_label.grid(row=row, column=4) # Next line of code processed.
 
     for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=tk.W)
+        element.grid(row=row, column=i, sticky=sticky_settings[i])
 
+def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    """
+    Add a row to display the total number of currently completed missions.
+    """
+    label = tk.Label(frame, text=_("CompletedSum"))
+    
+    cmp_num = tk.Label(frame, text=data.cmpmission_count) # Number of completed missions.
+    kill_sum = tk.Label(frame, text=data.killcmpcount) # Number of kills completed in missions.
+    reward_sum_normal = "{:.1f}".format(float(data.cmpreward) / 1_000_000)
+    reward_sum_wing = "{:.1f}".format(float(data.cmpshareable_reward) / 1_000_000)
+    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
+    for i, entry in enumerate([label, cmp_num, kill_sum, reward_sum]):
+        entry.config(fg="YellowGreen")
+        entry.grid(row=row, column=i, sticky=tk.W)
 
 def __display_sum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
     """
     Display the Sum-Row containing the Reward-Sum and the amount of Kills required.
     """
-    label = tk.Label(frame, text="Sum")
+    label = tk.Label(frame, text=_("AcceptedSum"))
+    allmissionnum = tk.Label(frame, text=data.mission_count)
path: massacre/ui.py
user: login: ckx000
body: ok
created_at: 2025-04-17T01:28:11Z
author_association: NONE
diff_hunk: @@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
path: massacre/ui.py
user: login: ckx000
body: This is a feature I plan to add later:
It will list details of each mission under its faction row (e.g., rewards, target counts) to help identify the lowest-value ones for optimization.

Current Pain Point:
In practice, I have to manually check the in-game mission panel to see which tasks I’ve accepted under each faction—then decide whether to abandon the lowest-reward ones for higher alternatives. This is cumbersome.

Solution:
A toggle button to:
1.Display all accepted missions per faction.
2.Highlight the lowest/highest rewards.
3.Streamline mission management.

This button's displayed information is typically used to optimize stacked missions (to maximize rewards, e.g., total rewards exceeding 500M—my friends all chase peak efficiency).
Our general workflow:
1.Accept 20/20 missions first.(Set a minimum target: e.g., only accept missions worth 20M+.)
2.Evaluate optimization potential (e.g., wait for the next 10-minute refresh to check if higher 30M+ missions are available to replace low-reward tasks).
3.The minor reputation loss from abandoning a few missions is acceptable
created_at: 2025-04-17T01:55:48Z
author_association: NONE
diff_hunk: @@ -82,22 +90,35 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
         """
         How many (massacre) missions does the user currently have.
         """
+        self.cmpmission_count = 0 # Add the total number of completed missions.
+        self.cmpreward = 0 # Add the total rewards from completed missions.
+        self.cmpshareable_reward = 0 # Add the total wing mission rewards from completed missions.
+        self.killcmpcount = 0 # Add the total completed kills.
path: massacre/ui.py
user: login: ckx000
body: It seems I’ll need to edit the files separately—I thought changes could be made directly within this pull request.
created_at: 2025-04-17T02:02:16Z
author_association: NONE
diff_hunk: @@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
+    faction_label = tk.Label(frame, text=_("Faction"))
+    missionnum_label = tk.Label(frame, text=_("R/T")) # Add the number of missions.
path: massacre/ui.py
user: login: ckx000
body: There are some special cases:
When the scheduled time with friends is approaching but your missions aren’t yet complete, you’ll need to evaluate:
Whether to ask friends to wait while you finish the remaining kills.
Or discuss:"I’ve currently completed X missions, totaling only XX rewards—can we share these now?"
created_at: 2025-04-17T02:08:52Z
author_association: NONE
diff_hunk: @@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
+    faction_label = tk.Label(frame, text=_("Faction"))
+    missionnum_label = tk.Label(frame, text=_("R/T")) # Add the number of missions.
path: massacre/ui.py
user: login: ckx000
body: From my months of playing with friends, I’ve noticed they often run into this situation.
Since they don’t use plugins (or use outdated ones), they’re rarely aware of their current mission progress.
created_at: 2025-04-17T02:11:51Z
author_association: NONE
diff_hunk: @@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
+    faction_label = tk.Label(frame, text=_("Faction"))
+    missionnum_label = tk.Label(frame, text=_("R/T")) # Add the number of missions.
path: massacre/ui.py
user: login: CMDR-WDX
body: yes. I understand that. What I am trying to say is that looking at `Missions: 3/5` and `Kills: 40/60` gives you the same information, if not even more because only completed missions will show up there. 
created_at: 2025-04-17T14:27:30Z
author_association: OWNER
diff_hunk: @@ -179,16 +203,19 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-
-    faction_label = tk.Label(frame, text="Faction")
-    kills_label = tk.Label(frame, text="Kills")
-    payout_label = tk.Label(frame, text="Reward (Wing)")
-
-    ui_elements = [faction_label, kills_label, payout_label]
-
+    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
+        frame.grid_columnconfigure(col,weight=0)
+    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    # todo Check if a toggle button can be inserted here after the faction name.
path: massacre/ui.py
user: login: CMDR-WDX
body: Hmm. Thanks for mentioning the use-case. Can you write an issue for it?
 I will then try to draw some mockups. The current UI logic is a pain point for me and I wanted to improve it for some time :D 
created_at: 2025-04-17T14:31:43Z
author_association: OWNER
diff_hunk: @@ -204,35 +231,54 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
 
     faction_label = tk.Label(frame, text=faction)
-    kills_label = tk.Label(frame, text=data.killcount)
+    cmpnum_sum = int(data.allnum) - int(data.cmpnum)
+    missionnum_label = tk.Label(frame, text=f"{cmpnum_sum}/{data.allnum}") # Add the number of missions.
+    killscmp_sum =int(data.killcount) - int(data.killcmpcount)
+    kills_label = tk.Label(frame, text=f"{killscmp_sum}/{data.killcount}") # Modify the kill count display
     payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
 
-    ui_elements = [faction_label, kills_label, payout_label]
-
-    if settings.delta:
+    ui_elements = [faction_label, missionnum_label, kills_label, payout_label]
+    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
+    if settings.delta: 
         # Calculate difference
         delta = max_count - data.killcount
         text = delta if delta > 0 else second_largest_count - max_count
         delta_label = tk.Label(frame, text=str(text))
         ui_elements.append(delta_label)
-        delta_label.grid(row=row, column=3)
+        #delta_label.grid(row=row, column=4) # Next line of code processed.
 
     for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=tk.W)
+        element.grid(row=row, column=i, sticky=sticky_settings[i])
 
+def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    """
+    Add a row to display the total number of currently completed missions.
+    """
+    label = tk.Label(frame, text=_("CompletedSum"))
+    
+    cmp_num = tk.Label(frame, text=data.cmpmission_count) # Number of completed missions.
+    kill_sum = tk.Label(frame, text=data.killcmpcount) # Number of kills completed in missions.
+    reward_sum_normal = "{:.1f}".format(float(data.cmpreward) / 1_000_000)
+    reward_sum_wing = "{:.1f}".format(float(data.cmpshareable_reward) / 1_000_000)
+    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
+    for i, entry in enumerate([label, cmp_num, kill_sum, reward_sum]):
+        entry.config(fg="YellowGreen")
+        entry.grid(row=row, column=i, sticky=tk.W)
path: massacre/ui.py
user: login: CMDR-WDX
body: Yeah. I think right-aligned would be the best to make it consistent with the rest

PS: no need to apologize :) You're doing great!
created_at: 2025-04-17T14:32:48Z
author_association: OWNER
diff_hunk: @@ -325,6 +375,7 @@ def set_frame(self, frame: tk.Frame):
         if cspan < 1:
             cspan = 2
         self.__frame = tk.Frame(frame)
+        #self.__frame.config(bg="red") # debug
path: massacre/ui.py
user: login: CMDR-WDX
body: yes. Commented out code is code smell. It's fine during development, but should not end up in "production code"
created_at: 2025-04-17T14:34:19Z
author_association: OWNER
diff_hunk: @@ -1 +1 @@
-2.0.5
+2.1.0
path: version
user: login: CMDR-WDX
body: hmm… yeah. That's a good point. I think I can directly push onto this branch. Alternatively, I can create a `develop` branch and we merge it into there :D 
created_at: 2025-04-17T14:35:12Z
author_association: OWNER

PR comments:
user: login: CMDR-WDX
created_at: 2025-04-15T17:29:16Z
body: I ill try to review this tonight :)
author_association: OWNER
user: login: CMDR-WDX
created_at: 2025-04-23T18:08:55Z
body: @ckx000  I am currently adding some changes in the settings and in the UI. Could you please translate the following keys for me to Chinese? :) 

`Display partially completed missions/rewards`  - this is used in the settings
`Display Mission Count Column` - this is used in the settings

I also replaced the the  column names from "R/T" to "Missions" and "KRM/REQ" to "Kills" again as the shorthands are confusing. 

Could you then please also translate `Missions` and  `Kills`? 


author_association: OWNER
user: login: CMDR-WDX
created_at: 2025-04-23T22:35:58Z
body: Github doesnt let me push onto the branch, even though `Maintainers are allowed to edit this pull request. `  is set. 

Can you apply this patch for me? 

`0001-feat-add-german-i18n-refactor-UI-code-add-new-featur.patch`
```patch
From a7facfe9e450805ab8c9cb85b2366320074a1fc3 Mon Sep 17 00:00:00 2001
From: CMDR-WDX <83889860+CMDR-WDX@users.noreply.github.com>
Date: Thu, 24 Apr 2025 00:30:08 +0200
Subject: [PATCH] feat: add german i18n, refactor UI code, add new features
 behind settings flags

---
 L10n/de.strings                          |  66 ++++++
 massacre/integrations/overlay/overlay.py |   8 +-
 massacre/massacre_settings.py            |  45 +++-
 massacre/ui.py                           | 285 ++++++++++++++++-------
 4 files changed, 301 insertions(+), 103 deletions(-)
 create mode 100644 L10n/de.strings

diff --git a/L10n/de.strings b/L10n/de.strings
new file mode 100644
index 0000000..fb89190
--- /dev/null
+++ b/L10n/de.strings
@@ -0,0 +1,66 @@
+/* Language name */
+"!Language" = "Deutsch";
+
+"Multiple Target Factions" = "Mehrere Zielfraktionen";
+
+"Multiple Target Types" = "Mehrere Zieltypen";
+
+"Multiple Target Systems" = "Mehrere Zielsysteme";
+
+"Missing Active Mission Data" = "Es Fehlen Informationen zu aktiven Missionen";
+
+"If you are in game, go to main menu and come back" = "Falls du schon im Spiel bist, gehe ins Hauptmenü und komme danach wieder zurück";
+
+"Faction" = "Fraktion";
+
+"Reward (Wing)" = "Belohnung (Wing)";
+
+"Δmax" = "Δmax";
+
+"Ratio" = "Verhältnis";
+
+"Reward" = "Belohnung";
+
+"M CR/Kill." = "M CR/Abschuss";
+
+"Kills" = "Abschüsse";
+
+"Mission Count" = "Missionenanzahl";
+
+"Massacre Plugin is Outdated" = "Massacre-Plugin ist veraltet";
+
+"Go to Download" = "Zum Download gehen";
+
+"Dismiss" = "Ausblenden";
+
+"Massacre Plugin is ready." = "Massacre Plugin ist bereit.";
+
+"UI Settings" = "UI Einstellungen";
+
+"Display Delta-Column" = "Delta-Spalte anzeigen";
+
+"Display Sum-Row" = "Summenzeile anzeigen";
+
+"Display Summary-Row" = "Zusammenfassungszeile anzeigen";
+
+"Display Mission Count" = "Missionenanzahl anzeigen";
+
+"Other" = "Sonstiges";
+
+"Check for Updates on Start" = "Beim Start nach Aktualisierungen suchen";
+
+"Made by" = "Entwickelt von";
+
+"and contributors" = "und Mitwirkenden";
+
+"Open Releases on Github" = "Versionen auf Github öffnen";
+
+"Display kills/rewards for already completed completed missions" = "Zeige Abschüsse / Belohnungen für fertige Missionen an";
+
+"Display Mission Count Summary" = "Zeige Missionenanzahl-Zeile an";
+
+"Completed Missions" = "Fertige Missionen";
+
+"All Missions" = "Alle Missionen";
+
+"Missions" = "Missionen";
\ No newline at end of file
diff --git a/massacre/integrations/overlay/overlay.py b/massacre/integrations/overlay/overlay.py
index b738549..4d59dfa 100644
--- a/massacre/integrations/overlay/overlay.py
+++ b/massacre/integrations/overlay/overlay.py
@@ -19,10 +19,10 @@ def __display_row(faction: str, data: MassacreMissionData.FactionState):
     """
     Draw one Data-Row for the Table
     """
-    reward_str = "{:.1f}".format(float(data.reward) / 1_000_000)
-    shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
+    reward_str = "{:.1f}".format(float(data.reward.total) / 1_000_000)
+    shareable_reward_str = "{:.1f}".format(float(data.shareable_reward.total) / 1_000_000)
     whole_reward_str = f'{reward_str} ({shareable_reward_str})'
-    overlay_elements = [f'{data.killcount:5}', f'{whole_reward_str:{len("Reward (Wing)")}}', f'{faction:15}']
+    overlay_elements = [f'{data.kills.total:5}', f'{whole_reward_str:{len("Reward (Wing)")}}', f'{faction:15}']
 
     return overlay_elements
 
@@ -71,7 +71,7 @@ class Overlay:
         self._create_overlay()
                 
     def __bool__(self):
-        return self.__overlay != None
+        return self.__overlay is not None
 
     def rebuild_settings(self):
         self._create_overlay()
diff --git a/massacre/massacre_settings.py b/massacre/massacre_settings.py
index 81c327a..73fe071 100644
--- a/massacre/massacre_settings.py
+++ b/massacre/massacre_settings.py
@@ -76,6 +76,21 @@ class Configuration:
         config.set(f"{self.plugin_name}.display_mission_count", value)
 
     #######################################
+    @property
+    def display_missions_column(self):
+        return config.get_bool(f"{self.plugin_name}.display_missions_column", default=False)
+
+    @display_missions_column.setter
+    def display_missions_column(self, value: bool):
+        config.set(f"{self.plugin_name}.display_missions_column", value)
+    #######################################
+    @property
+    def display_partial_stacks(self):
+        return config.get_bool(f"{self.plugin_name}.display_partial_stacks", default=False)
+    @display_partial_stacks.setter
+    def display_partial_stacks(self, value: bool):
+        config.set(f"{self.plugin_name}.display_partial_stacks", value)
+    #######################################
     def __init__(self, plugin_name: str):
        self.plugin_name = plugin_name
        self.config_changed_listeners: list[Callable[[Configuration], None]] = []
@@ -97,6 +112,10 @@ class Configuration:
             self.overlay_ttl = data['overlay_ttl'].get()
         if "display_mission_count" in keys:
             self.display_mission_count = data['display_mission_count'].get()
+        if "display_missions_column" in keys:
+            self.display_missions_column = data["display_missions_column"].get()
+        if "display_partial_stacks" in keys:
+            self.display_partial_stacks = data["display_partial_stacks"].get()
 
         for listener in self.config_changed_listeners:
             listener(self)
@@ -132,27 +151,33 @@ def build_settings_ui(root: nb.Notebook) -> tk.Frame:
     frame.columnconfigure(1, weight=1)
     __setting_changes.clear()
     __setting_changes["check_updates"] = \
-        tk.IntVar(value=configuration.check_updates)
+        tk.BooleanVar(value=configuration.check_updates)
     __setting_changes["display_delta_column"] = \
-        tk.IntVar(value=configuration.display_delta_column)
+        tk.BooleanVar(value=configuration.display_delta_column)
     __setting_changes["display_sum_row"] = \
-        tk.IntVar(value=configuration.display_sum_row)
+        tk.BooleanVar(value=configuration.display_sum_row)
     __setting_changes["display_ratio_and_cr_per_kill_row"] = \
-        tk.IntVar(value=configuration.display_ratio_and_cr_per_kill_row)
+        tk.BooleanVar(value=configuration.display_ratio_and_cr_per_kill_row)
     __setting_changes["display_mission_count"] = \
-        tk.IntVar(value=configuration.display_mission_count)
-
+        tk.BooleanVar(value=configuration.display_mission_count)
+    __setting_changes["display_partial_stacks"] = \
+        tk.BooleanVar(value=configuration.display_partial_stacks)
+    __setting_changes["display_missions_column"] = \
+        tk.BooleanVar(value=configuration.display_missions_column)
 
     nb.Label(frame, text=_("UI Settings"), pady=10).grid(sticky=tk.W, padx=title_offset)
     ui_settings_checkboxes = [
         nb.Checkbutton(frame, text=_("Display Delta-Column"),
                        variable=__setting_changes["display_delta_column"]),
+        nb.Checkbutton(frame, text=_("Display Mission Count Column"), variable=__setting_changes["display_missions_column"]),
         nb.Checkbutton(frame, text=_("Display Sum-Row"),
                        variable=__setting_changes["display_sum_row"]),
         nb.Checkbutton(frame, text=_("Display Summary-Row"),
                        variable=__setting_changes["display_ratio_and_cr_per_kill_row"]),
-        nb.Checkbutton(frame, text=_("Display Mission Count"),
-                        variable=__setting_changes["display_mission_count"])
+        nb.Checkbutton(frame, text=_("Display Mission Count Summary"),
+                        variable=__setting_changes["display_mission_count"]),
+        nb.Checkbutton(frame, text=_("Display kills/rewards for already completed completed missions"),
+                       variable=__setting_changes["display_partial_stacks"])
     ]
     for entry in ui_settings_checkboxes:
         entry.grid(columnspan=2, padx=checkbox_offset, sticky=tk.W)
@@ -167,8 +192,8 @@ def build_settings_ui(root: nb.Notebook) -> tk.Frame:
     massacre.integrations.main.notify_about_settings(frame)
     
     
-    nb.Label(frame, text="Made by CMDR WDX").grid(sticky=tk.W, padx=checkbox_offset)
-    HyperlinkLabel(frame, text="Github", background=nb.Label().cget("background"), url=download_url, underline=True)\
+    nb.Label(frame, text=f"{_("Made by")} CMDR WDX {_("and contributors")}").grid(sticky=tk.W, padx=checkbox_offset)
+    HyperlinkLabel(frame, text=_("Open Releases on Github"), background=nb.Label().cget("background"), url=download_url, underline=True)\
         .grid(columnspan=2, sticky=tk.W, padx=checkbox_offset)
 
     
diff --git a/massacre/ui.py b/massacre/ui.py
index e783036..4ea9943 100644
--- a/massacre/ui.py
+++ b/massacre/ui.py
@@ -4,6 +4,7 @@ import functools
 import tkinter as tk
 from typing import Optional
 from dataclasses import dataclass
+from enum import Enum
 
 import massacre.massacre_settings
 from massacre.massacre_mission_state import massacre_mission_listeners, MassacreMission
@@ -22,13 +23,63 @@ class MassacreMissionData:
 
     @dataclass
     class FactionState:
-        
-        all_mission_count: int # Add the number of missions for this faction.
-        completed_mission_count: int # Number of completed missions for this faction
-        completed_kill_count: int # Number of completed kills for this faction
-        kill_count: int
-        reward: int 
-        shareable_reward: int
+        @dataclass
+        class Entry:
+            total: int
+            completed: int
+            @property
+            def remaining(self):
+                return self.total - self.completed
+            @staticmethod
+            def default():
+                return MassacreMissionData.FactionState.Entry(0, 0)
+
+        missions: Entry
+        kills: Entry
+        reward: Entry
+        shareable_reward: Entry
+
+        @staticmethod
+        def default():
+            return MassacreMissionData.FactionState(
+                missions = MassacreMissionData.FactionState.Entry.default(),
+                kills = MassacreMissionData.FactionState.Entry.default(),
+                reward = MassacreMissionData.FactionState.Entry.default(),
+                shareable_reward = MassacreMissionData.FactionState.Entry.default(),
+            )
+
+    class ColumnType(Enum):
+        FACTION = 1
+        MISSIONS_OPT = 2
+        KILLS = 3
+        REWARD = 4
+        DELTA_OPT = 5
+
+        def get_alignment_head(self):
+            return self.get_alignment_body() # we might change this later. for now "inherit" this from the body
+
+        def get_alignment_body(self):
+            if self == MassacreMissionData.ColumnType.FACTION:
+                return tk.W
+            if self in [
+                MassacreMissionData.ColumnType.MISSIONS_OPT,
+                MassacreMissionData.ColumnType.KILLS,
+                MassacreMissionData.ColumnType.REWARD,
+                MassacreMissionData.ColumnType.DELTA_OPT,
+            ]:
+                return tk.E
+            logger.warning("unhandled Column: "+str(self))
+            return tk.W
+
+        def get_name(self):
+            lookup: dict[MassacreMissionData.ColumnType, str] = {
+                MassacreMissionData.ColumnType.FACTION: "Faction",
+                MassacreMissionData.ColumnType.MISSIONS_OPT: "Missions",
+                MassacreMissionData.ColumnType.KILLS: "Kills",
+                MassacreMissionData.ColumnType.REWARD: "Reward (Wing)",
+                MassacreMissionData.ColumnType.DELTA_OPT: "Δmax",
+            }
+            return lookup.get(self, "#MISSING COL DEF")
 
 
     def __init__(self, massacre_state: dict[int, MassacreMission]):
@@ -101,7 +152,7 @@ class MassacreMissionData:
 
             if mission_giver not in self.faction_to_count_lookup.keys():
                 """If no Mission from that Faction is known yet, it will first be initialized"""
-                self.faction_to_count_lookup[mission_giver] = MassacreMissionData.FactionState(0, 0, 0, 0, 0, 0)
+                self.faction_to_count_lookup[mission_giver] = MassacreMissionData.FactionState.default()
 
             faction_state = self.faction_to_count_lookup[mission_giver]
             """
@@ -109,20 +160,20 @@ class MassacreMissionData:
             from previous Missions from that faction, or 0,0,0 if this is the first mission.
             """
             
-            faction_state.all_mission_count += 1 # Add a count for the number of missions from this faction
+            faction_state.missions.total += 1 # Add a count for the number of missions from this faction
             if mission.is_completed: 
-                faction_state.completed_mission_count += 1 # Accumulate the count of completed missions.
-                faction_state.completed_kill_count += mission.count # Accumulate the count of completed kills.
+                faction_state.missions.completed += 1 # Accumulate the count of completed missions.
+                faction_state.kills.completed += mission.count # Accumulate the count of completed kills.
                 self.completed_reward += mission.reward # Add rewards from completed missions to the total.
                 if mission.is_wing: # Add rewards from completed wing missions to the total.
                     self.completed_shareable_reward += mission.reward
 
-            faction_state.killcount += mission.count
-            self.target_sum += mission.count 
-            faction_state.reward += mission.reward
+            faction_state.kills.total += mission.count
+            self.target_sum += mission.count
+            faction_state.reward.total += mission.reward
             # Only wing missions are considered for shareable rewards
             if mission.is_wing:
-                faction_state.shareable_reward += mission.reward
+                faction_state.shareable_reward.total += mission.reward
 
             ### Add Faction, Target Type and Target System to the list if they are not 
             ### yet present. This will be later used to generate a warning if more than 
@@ -136,17 +187,18 @@ class MassacreMissionData:
             if mission.target_system not in target_systems:
                 target_systems.append(mission.target_system)
 
-            if faction_state.killcount > self.stack_height:
-                self.stack_height = faction_state.killcount
+            if faction_state.kills.total > self.stack_height:
+                self.stack_height = faction_state.kills.total
 
         # After all Missions have been handled, iterate through the faction_to_count_lookup to calculate the Total Rewards   
         for faction_state in self.faction_to_count_lookup.values():
-            self.reward += faction_state.reward
-            self.shareable_reward += faction_state.shareable_reward
-            self.completed_mission_count += faction_state.completed_mission_count # Total count of all completed missions (aggregated per faction).
+            self.reward += faction_state.reward.total
+            self.shareable_reward += faction_state.shareable_reward.total
+            self.completed_mission_count += faction_state.missions.completed # Total count of all completed missions (aggregated per faction).
+            self.completed_shareable_reward += faction_state.shareable_reward.completed
             # Loop through each faction to find the largest total kills count and use it as the aggregated kill count for all missions
-            if faction_state.completed_kill_count > self.completed_kill_count:
-                self.completed_kill_count = faction_state.completed_kill_count
+            if faction_state.kills.completed > self.completed_kill_count:
+                self.completed_kill_count = faction_state.kills.completed
 
         # Check for Warnings
         if len(target_factions) > 1:
@@ -158,8 +210,8 @@ class MassacreMissionData:
 
         # Calculate before_stack_height
         for faction_state in self.faction_to_count_lookup.values():
-            if faction_state.killcount > self.before_stack_height and faction_state.killcount != self.stack_height:
-                self.before_stack_height = faction_state.killcount
+            if faction_state.kills.total > self.before_stack_height and faction_state.kills.total != self.stack_height:
+                self.before_stack_height = faction_state.kills.total
         if self.before_stack_height == 0:  # No other elements. All at max value.
             self.before_stack_height = self.stack_height
 
@@ -169,10 +221,22 @@ class GridUiSettings:
     Subset of the entire Configuration that focuses on which information is displayed
     """
     def __init__(self, config: Configuration):
-        self.sum = config.display_sum_row # fix error
-        self.delta = config.display_delta_column
-        self.summary = config.display_ratio_and_cr_per_kill_row
-        self.mission_count = config.display_mission_count
+        self.has_sum = config.display_sum_row # fix error
+        self.has_delta_col = config.display_delta_column
+        self.has_summary_row = config.display_ratio_and_cr_per_kill_row
+        self.has_mission_count_row = config.display_mission_count
+        self.has_missions_col = config.display_missions_column
+        self.display_partial_stacks = config.display_partial_stacks
+
+    def get_ordered_columns(self) -> list[MassacreMissionData.ColumnType]:
+        return [f for f in [
+            MassacreMissionData.ColumnType.FACTION,
+            MassacreMissionData.ColumnType.MISSIONS_OPT if self.has_missions_col else None,
+            MassacreMissionData.ColumnType.KILLS,
+            MassacreMissionData.ColumnType.REWARD,
+            MassacreMissionData.ColumnType.DELTA_OPT if self.has_delta_col else None,
+        ] if f is not None]
+
 
 
 def __get_row_width(settings: GridUiSettings) -> int:
@@ -180,10 +244,12 @@ def __get_row_width(settings: GridUiSettings) -> int:
     Return how many columns wide the Table is.
     This depends on if the delta-Column should be displayed
     """
-    if settings.delta:
-        return 5
-    return 4
-
+    col_count = 4
+    if settings.has_delta_col:
+        col_count += 1
+    if settings.has_missions_col:
+        col_count += 1
+    return col_count
 
 def _display_no_data_info(frame: tk.Frame):
     """
@@ -203,23 +269,16 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
     """
     Display the Labels of the Table
     """
-    for col in range(3+ int(settings.delta)):  # Adjust the number of columns based on whether the delta column is include
-        frame.grid_columnconfigure(col,weight=0)
-    frame.grid_columnconfigure(0,minsize=120, weight=1) 
+    cols = settings.get_ordered_columns()
+    for idx, col in enumerate(cols):
+        weight = 0
+        if col == MassacreMissionData.ColumnType.FACTION:
+            weight = 1
+        frame.grid_columnconfigure(idx, weight=weight)
+        # TODO: Needed?
+        # frame.grid_columnconfigure(0, minsize=120, weight=1)
+        tk.Label(frame, text=_(col.get_name())).grid(row=row, column=idx, sticky=col.get_alignment_head())
     # todo Check if a toggle button can be inserted here after the faction name.
-    faction_label = tk.Label(frame, text=_("Faction"))
-    mission_num_label = tk.Label(frame, text=_("R/T")) # Add the number of missions.
-    kills_label = tk.Label(frame, text=_("KRM/REQ"))
-    payout_label = tk.Label(frame, text=_("Reward (Wing)"))
-
-    ui_elements = [faction_label, mission_num_label, kills_label, payout_label]
-    if settings.delta:
-        # noinspection SpellCheckingInspection
-        delta_label = tk.Label(frame, text=_("Δmax"))
-        ui_elements.append(delta_label)
-
-    for i, item in enumerate(ui_elements):
-        item.grid(row=row, column=i, sticky=tk.W)
 
 
 def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.FactionState, max_count: int,
@@ -227,34 +286,58 @@ def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.Facti
     """
     Draw one Data-Row for the Table
     """
-    reward_str = "{:.1f}".format(float(data.reward) / 1_000_000)
-    shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
-
-    faction_label = tk.Label(frame, text=faction)
-    completed_mission_count_sum = int(data.all_mission_count) - int(data.completed_mission_count)
-    mission_num_label = tk.Label(frame, text=f"{completed_mission_count_sum}/{data.all_mission_count}") # Add the number of missions.
-    completed_kill_sum =int(data.killcount) - int(data.completed_kill_count)
-    kills_label = tk.Label(frame, text=f"{completed_kill_sum}/{data.killcount}") # Modify the kill count display
-    payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")
-
-    ui_elements = [faction_label, mission_num_label, kills_label, payout_label]
-    sticky_settings = [tk.W, tk.W, tk.W+tk.E , tk.W, tk.E] # Considering delta, define one more.
-    if settings.delta: 
-        # Calculate difference
-        delta = max_count - data.killcount
-        text = delta if delta > 0 else second_largest_count - max_count
-        delta_label = tk.Label(frame, text=str(text))
-        ui_elements.append(delta_label)
-        #delta_label.grid(row=row, column=4) # Next line of code processed.
-
-    for i, element in enumerate(ui_elements):
-        element.grid(row=row, column=i, sticky=sticky_settings[i])
-
-def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+    for i, col_type in enumerate(settings.get_ordered_columns()):
+        align = col_type.get_alignment_body()
+
+        match col_type:
+            case MassacreMissionData.ColumnType.FACTION:
+                text = faction
+
+            case MassacreMissionData.ColumnType.MISSIONS_OPT:
+                if settings.display_partial_stacks:
+                    text = f"{data.missions.completed}/{data.missions.total}"
+                else:
+                    text = str(data.missions.total)
+
+            case MassacreMissionData.ColumnType.REWARD:
+                # helper to format millions
+                def fmt(x):
+                    return f"{float(x) / 1_000_000:.1f}"
+                total_r = fmt(data.reward.total)
+                total_shareable = fmt(data.shareable_reward.total)
+                completed_r = fmt(data.reward.completed)
+                completed_share = fmt(data.shareable_reward.completed)
+
+                parts = []
+                if settings.display_partial_stacks:
+                    parts.append(f"{completed_r} ({completed_share})")
+                parts.append(f"{total_r} ({total_shareable})")
+                text = " / ".join(parts)
+
+            case MassacreMissionData.ColumnType.KILLS:
+                parts = []
+                if settings.display_partial_stacks:
+                    parts.append(str(data.kills.completed))
+                parts.append(str(data.kills.total))
+                text = "/".join(parts)
+
+            case MassacreMissionData.ColumnType.DELTA_OPT:
+                delta = max_count - data.kills.total
+                if delta > 0:
+                    text = str(delta)
+                else:
+                    text = str(second_largest_count - max_count)
+
+            case _:
+                text = "#MISSING COL DEF"
+        tk.Label(frame, text=text).grid(row=row, column=i, sticky=align)
+
+
+def __display_completed_sum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
     """
     Add a row to display the total number of currently completed missions.
     """
-    label = tk.Label(frame, text=_("CompletedSum"))
+    label = tk.Label(frame, text=_("Completed Missions"))
     
     completed_num = tk.Label(frame, text=data.completed_mission_count) # Number of completed missions.
     kill_sum = tk.Label(frame, text=data.completed_kill_count) # Number of kills completed in missions.
@@ -266,21 +349,47 @@ def __display_cmpsum(frame: tk.Frame, data: MassacreMissionData, _settings: Grid
         entry.config(fg="YellowGreen")
         entry.grid(row=row, column=i, sticky=sticky_settings[i])
 
-def __display_sum(frame: tk.Frame, data: MassacreMissionData, _settings: GridUiSettings, row: int):
+def __display_sum(frame: tk.Frame, data: MassacreMissionData, settings: GridUiSettings, row: int):
     """
     Display the Sum-Row containing the Reward-Sum and the amount of Kills required.
     """
-    label = tk.Label(frame, text=_("AcceptedSum"))
-    all_missions_num = tk.Label(frame, text=data.mission_count)
-    kill_sum = tk.Label(frame, text=data.stack_height)
-    reward_sum_normal = "{:.1f}".format(float(data.reward) / 1_000_000)
-    reward_sum_wing = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)
-    reward_sum = tk.Label(frame, text=f"{reward_sum_normal} ({reward_sum_wing})")
-    sticky_settings = [tk.W, tk.E, tk.E, tk.E]
-    for i, entry in enumerate([label, all_missions_num, kill_sum, reward_sum]):
-        entry.config(fg="green")
-        entry.grid(row=row, column=i, sticky=sticky_settings[i])
 
+    for i, col_type in enumerate(settings.get_ordered_columns()):
+        align = col_type.get_alignment_body()
+
+        match col_type:
+            case MassacreMissionData.ColumnType.FACTION:
+                text = _("Summary")
+
+            case MassacreMissionData.ColumnType.MISSIONS_OPT:
+                if settings.display_partial_stacks:
+                    text = f"{data.completed_mission_count}/{data.mission_count}"
+                else:
+                    text = str(data.mission_count)
+
+            case MassacreMissionData.ColumnType.REWARD:
+                # helper to format millions
+                def fmt(x):
+                    return f"{float(x) / 1_000_000:.1f}"
+                parts = []
+                if settings.display_partial_stacks:
+                    parts.append(f"{fmt(data.completed_reward)} ({fmt(data.completed_shareable_reward)})")
+                parts.append(f"{fmt(data.reward)} ({fmt(data.shareable_reward)})")
+                text = " / ".join(parts)
+
+            case MassacreMissionData.ColumnType.KILLS:
+                parts = []
+                if settings.display_partial_stacks:
+                    parts.append(str(data.completed_kill_count))
+                parts.append(str(data.stack_height))
+                text = "\\".join(parts)
+
+            case MassacreMissionData.ColumnType.DELTA_OPT:
+                text = ""
+
+            case _:
+                text = "#MISSING COL DEF"
+        tk.Label(frame, text=text, fg="green").grid(row=row, column=i, sticky=align)
 
 def __display_summary(frame: tk.Frame, data: MassacreMissionData, settings: GridUiSettings, row: int):
     ratio_text = "{:.2f}".format(float(data.target_sum)/float(data.stack_height))
@@ -309,18 +418,16 @@ def _display_data(frame: tk.Frame, data: MassacreMissionData, settings: GridUiSe
         row_pointer += 1
         # todo In the future, add a toggle button here to display detailed mission information under each faction.
 
-    if settings.sum:
-        __display_cmpsum(frame, data, settings, row_pointer)
-        row_pointer += 1
+    if settings.has_sum:
         __display_sum(frame, data, settings, row_pointer)
         row_pointer += 1
 
-    if settings.summary:
+    if settings.has_summary_row:
         __display_summary(frame, data, settings, row_pointer)
         row_pointer += 1
     full_width = __get_row_width(settings)
 
-    if settings.mission_count:
+    if settings.has_mission_count_row:
         __display_mission_count(frame, data, full_width, row_pointer)
         row_pointer += 1
 
-- 
2.49.0

```

You can apply it using `git am pathToPatch` 

author_association: OWNER
user: login: CMDR-WDX
created_at: 2025-04-23T22:44:03Z
body: actually… i ill just merge this into a dev branch and work from there :)
author_association: OWNER