赛博朋克2077全球文本档案 - 19种语言完全整理(V4)

创作者: Kontrust87
已更新:2026-07-26 23:05:18
3.4MB

关于此 MOD

一个庞大、完整提取并整理好的文本档案,包含全部19种官方游戏语言碎片。按母语翻译整理好的文件夹结构。无论是剧情考据党、翻译者还是模组制作者都适用!为你节省12小时以上的
================================================================================
⚠️ 重要更新(请阅读) ⚠️
很遗憾,04 ❌ 和 05 ❌ 文件夹中的文件目前已损坏。
这是因为一个我刚发现的脚本错误。我本来很高兴
成功集齐了所有碎片及其称号,而我却失误了
以为开发者对其余部分也会采用同样的逻辑。
文件夹 03_Journal_SPLITTER ✅ 和文件夹 06_Computer_MAILS ✅ 完全正确。
================================================================================


描述
这个模组提供了《赛博朋克2077》及《往日之影》资料片中全面提取并精心整理的碎片数据库。它涵盖了游戏全部19种官方语言。

此档案专为模组作者、背景考据者和翻译者设计,作为节省时间的开发资源。它省去了通过WolvenKit手动提取数小时的繁琐工作。

主要特色
在19个语言文件夹中,每个都包含四个主要类别,其结构设计旨在实现最大实用性:

✅ * 03_Journal_SPLITTER:所有碎片和日志文本的完整数据库。文件夹名称已完全翻译为各自母语,方便浏览。
❌ * 04_SMS_NPC [当前已损坏]:NPC 接收和发送的所有短信。角色文件夹名称保留英文/拉丁文字,以确保与开发工具在全球范围内的兼容性。
❌ * 05_Codex_LEXIKON [当前不可用]:所有数据库条目、阵营和背景设定的综合数据库。子文件夹已进行本地化翻译。
✅ * 06_Computer_MAILS: 笔记本电脑文件、终端日志和电子邮件。严格按技术开发者ID整理,以防止游戏数据库资产冲突。

安装说明
这是一个原始文本资源包。无需将其放入游戏文件夹。

1. 下载存档。
2. 将ZIP文件解压到电脑上的任意位置。
3. 打开你首选语言的文件夹,以阅读或使用原始 .txt 资源。

需求
* 无。这是一个独立的 .txt 文件资源归档。

来自Kontrust87的私人留言
我为这个项目倾尽了全力。既然我已经深入到自己项目的大规模提取工作中,我决定额外多做一些,顺便为整个社区完成全部19种语言。

我真心希望你能享受使用这份档案,也希望它能在你自己的旅程和模组制作中为你节省大量时间!在夜之城一路平安,兄弟们!🦾

短信之所以棘手,是因为它使用嵌套ID而非纯文本。虽然“碎片”通常以连续块的形式存储,但.json文件中的短信往往包含大量交叉引用(本地化键值或十六进制ID)。在游戏中,一条文本通常由发件人ID、收件人ID、时间戳以及实际文本片段动态组合而成;如果你的脚本只是简单地读取语言文件,就会缺少这些必要的关联信息。


以下是剧透中的源代码。我怀疑我卡在短信和数据库部分了。

剧透:  
显示
导入 os
导入 json

PROJECT_DIR = r"C:\Users\Admin\Desktop\赛博朋克工作\所有赛博朋克文本\01_游戏原始数据"
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
主工作区 = os.path.dirname(当前目录)

OUTPUT_SHARDS = os.path.join(MAIN_WORKSPACE, "03_已完成日志文本_拆分器")
OUTPUT_SMS = os.path.join(MAIN_WORKSPACE, "04_FERTIGE_TEXTE_FUER_SMS_NPC")
OUTPUT_CODEX = os.path.join(MAIN_WORKSPACE, "05_完成文本_用于法典_词典")
OUTPUT_MAILS = os.path.join(MAIN_WORKSPACE, "06_用于电脑邮件的完成文本")

加载JSON文件(文件路径)
    如果os.path.exists(filepath):
        尝试:
            以只读模式打开文件路径并以UTF-8编码读取,返回解析后的JSON数据。
        except Exception as e: print(f"[警告] 加载时出错: {e}")
    无

在根文件夹中查找包含指定内容且扩展名匹配的精确文件
    遍历根目录下的所有目录、子目录和文件:
        如果 "archiv" 在 root 的小写形式中:继续
        对于文件中的文件:
            如果必须包含的小写文本在文件小写内容中且文件以小写扩展名结尾:
                如果“journal”在must_contain.lower()中且“onscreen”在file.lower()中:继续
                返回 os.path.join(root, file)
    无

定义解析文本条目函数(数据):
    条目 = {}
    def scan_dict(node):
        如果节点是字典类型:
            如果“femaleVariant”在节点中:
                pk_val = node.get("primaryKey")
                如果 pk_val 是字典且包含 "value" 键:pk_val = pk_val["value"]
                txt_val = 节点["女性变体"]
                如果 isinstance(txt_val, dict) 且 "value" 在 txt_val 中:txt_val = txt_val["value"]
                如果主键值且有文本值:
                    entries[str(pk_val).strip()] = str(txt_val).strip()
            对于node.values()中的v:scan_dict(v)
        否则如果isinstance(node, list):
            遍历 node 中的每一项:scan_dict(item)
    扫描字典(数据)
    返回条目

def 清理锁匙值(val):
    如果 not val: 返回 ""
    val_str = str(val).strip()
    如果“lockey#”在val_str.lower()中:
        返回 val_str.lower().replace("lockey#", "").strip()
    返回 val_str

def scan_journal_all(node, current_cat="其他", current_contact="未知", codex_cat="通用", mail_owner="Computer_Dateien"):
    并行扫描整个日志中的所有4种内容类型
    碎片,短信列表,法典,邮件 = [], [], [], []
   
    如果节点是字典类型:
        node_type = str(node.get("$type", ""))
        node_id = str(node.get("id", "")).lower()
       
        # 碎片与词典的分类映射
        如果 node_id != “”:
            s_map = {"world": "世界", "literature": "文学", "religion": "宗教与哲学", "art": "艺术", "prospectus": "宣传册", "articles": "文章", "tech": "科技", "note": "笔记", "poetry": "诗歌", "lyric": "诗歌", "character": "夜之城人物", "cyberpsycho": "赛博精神病"}
            对于 s_map.items() 中的 eng、de:
                如果 node_id 中包含 eng:current_cat = de;跳出
               
            c_map = {"vehicle": "载具", "car": "载具", "weapon": "武器与制造商", "gang": "帮派与阵营", "corpo": "企业", "district": "区域与地点", "glossary": "术语表", "biography": "角色传记", "history": "历史与背景设定"}
            对于 c_map.items() 中的 eng, de:
                如果 node_id 中包含 eng:codex_cat = de; break

        如果“files”在node_id中或者“email”在node_id中或者“group”在node_id中:
            如果去除了首尾空白的node_id还有内容:将node_id中的“_files”和“_emails”替换为空字符串,再去除首尾空白,然后转换为首字母大写形式,赋值给mail_owner

        如果节点类型中包含“gameJournalContact”或节点类型的小写形式中包含“contact”:
            如果“id”在节点中且节点中的“id”是字符串类型:current_contact = str(node["id"]).strip().title()

        1. 匹配:CODEX / LEXIKON(支持同一节点或子节点中的描述)
        如果“gameJournalCodex”在节点类型中或“codex”在节点类型的小写形式中:
            c_title = clean_lockey(node["title"].get("value")) if "title" in node and isinstance(node["title"], dict) else ""
           
            在当前节点或嵌套节点中查找真实文本ID
            c_body = ""
            对于 ["description", "text", "content"] 中的 k:
                如果k在node中且isinstance(node[k], dict):
                    c_body = clean_lockey(node[k].get("value"))
                    如果 c_body:中断
           
            如果 c_body 或 c_title:
                codex.append({"title_id": c_title, "body_id": c_body, "category": codex_cat})

        # 2. 比赛:笔记本电脑文件与邮件
        否则,如果节点类型中包含“gameJournalFile”或“gameJournalEmail”,或者节点类型的小写形式中包含“file”或“email”:
            t_id = clean_lockey(node["title"].get("value")) if "title" in node and isinstance(node["title"], dict) else ""
            b_id = ""
            对于["content", "description", "text"]中的k:
                如果 k 在 node 中且 isinstance(node[k], dict):
                    b_id = clean_lockey(node[k].get("value"))
                    如果b_id:中断
            如果 b_id 或 t_id:mails.append({"title_id": t_id, "body_id": b_id, "owner": mail_owner})

        # 3. 比赛:普通分裂者
        elif "gameJournalOnscreen" in node_type or "onscreen" in node_type.lower():
            t_id = clean_lockey(node["title"].get("value")) if "title" in node and isinstance(node["title"], dict) else ""
            b_id = clean_lockey(node["description"].get("value")) if "description" in node and isinstance(node["description"], dict) else ""
            s_id = clean_lockey(node["text"].get("value")) if "text" in node and isinstance(node["text"], dict) else ""
            如果 t_id 或 b_id:shards.append({"title_id": t_id, "body_id": b_id, "sign_id": s_id, "category": current_cat})

        # 4. 匹配:NPC-SMS
        否则如果“gameJournalPhoneMessage”出现在节点类型中,或“phonemessage”出现在节点类型的小写形式中:
            is_player = node.get("isPlayer", 0)
            如果字符串形式的is_player在["0", "False", "false"]中:
                msg_id = clean_lockey(node["text"].get("value")) if "text" in node and isinstance(node["text"], dict) else ""
                如果 msg_id:sms_list.append({"msg_id": msg_id, "contact": current_contact})

        对于node.values()中的v
            如果v是字典或列表类型:
                s_res, m_res, c_res, e_res = scan_journal_all(v, current_cat, current_contact, codex_cat, mail_owner)
                shards.extend(s_res); sms_list.extend(m_res); codex.extend(c_res); mails.extend(e_res)
           
    elif isinstance(node, list):
        对于 node 中的 item:
            s_res, m_res, c_res, e_res = scan_journal_all(item, current_cat, current_contact, codex_cat, mail_owner)
            碎片列表扩展(s_res);短信列表扩展(m_res);图鉴扩展(c_res);邮件扩展(e_res)
           
    返回碎片、短信列表、法典、邮件
提取分流器
    print("启动赛博朋克全能工作室(V4 - 词典更新)...")
    main_dir, dlc_dir = os.path.join(PROJECT_DIR, "主游戏"), os.path.join(PROJECT_DIR, "DLC")
   
    main_text_path = find_exact_file(main_dir, "onscreens", ".json")
    main_journal_path = find_exact_file(main_dir, "journal", ".json")
    dlc_text_path = find_exact_file(dlc_dir, "ep1_onscreens", ".json")
    dlc_journal_path = find_exact_file(dlc_dir, "ep1_cooked", ".json") or find_exact_file(dlc_dir, "cooked_journal", ".json")

    print("\n[INFO] 正在读取语言文件...")
    text_map = {}
    对于[main_text_path, dlc_text_path]中的路径:
        如果路径不存在或路径无效则继续
        数据 = 加载JSON(路径)
        如果数据存在:
            found_map = parse_text_entries(data)
            text_map.update(found_map)
            print(f"-> 已从 {os.path.basename(path)} 学习到 {len(found_map)} 行文本。")

    打印("\n[信息] 正在按碎片、短信、邮件和词典分析开发者数据……")
    j_shards, j_sms, j_codex, j_mails = [], [], [], []
    对于 j_path 在 [main_journal_path, dlc_journal_path] 中:
        如果不满足 j_path 或 os.path.exists(j_path):继续
        j_data = load_json(j_path)
        如果 j_data:
            s_f, m_f, c_f, e_f = scan_journal_all(j_data)
            j_shards.extend(s_f); j_sms.extend(m_f); j_codex.extend(c_f); j_mails.extend(e_f)
            print(f"-> 已成功处理 {os.path.basename(j_path)} 中的关联项。")

    # === 处理 1:碎片 ===
    print(f"\n[信息] 正在合成 {len(j_shards)} 个碎片...")
    碎片计数, 已用碎片 = 0, set()
    对于 j_shards 中的 pkg:
        body_id = pkg["body_id"]
        title_id = pkg["title_id"]
        sign_id = pkg["sign_id"]
        category = pkg["category"]
       
        title_text = text_map.get(title_id, "").replace("\\n", "\n").strip()
        body_text = text_map.get(body_id, "").replace("\n", "\n").strip()
        sign_text = text_map.get(sign_id, "").replace("\\n", "\n").strip()
       
        如果没有正文文本而有标题文本:
            body_text, title_text, body_id = title_text, "", title_id
        如果没有正文文本或正文标识已在已使用碎片中,则继续。
        used_shards.add(body_id)

        {身体标识}\n\n
        如果 title_text:final_content += f“{title_text.upper()}\n\n”
        final_content += body_text
        如果 sign_text:final_content += f"\n\n{sign_text}"

        cat_dir = os.path.join(OUTPUT_SHARDS, category)
        创建目录,如果目录已存在则不报错
        name_source = title_text if title_text else body_text.split("\n")
        clean_name = "".join([c for c in name_source if c.isalpha() or c.isspace() or c.isdigit()]).strip()
        file_name = f"{body_id}_{clean_name[:20]}.txt" if clean_name else f"Splitter_{body_id}.txt"
        用 open(os.path.join(cat_dir, file_name), "w", encoding="utf-8") 作为 out_f:out_f.write(final_content)
        碎片数量 += 1

    # === 处理 2: NPC短信 ===
    print(f"[INFO] 正在按角色整理收到的短信...")
    短信计数,已用短信 = 0, set()
    对于 j_sms 中的 msg:
        msg_id = msg["msg_id"]
        contact = msg["contact"]
       
        sms_text = text_map.get(msg_id, "").replace("\\n", "\n").strip()
        如果短信文本为空或消息ID已使用过,则跳过本次循环。
        已使用短信.添加(消息编号)

        final_sms_content = f"{msg_id}\n\n{sms_text}"
       
        char_dir = os.path.join(OUTPUT_SMS, contact)
        创建目录(如果不存在则自动创建)
       
        clean_sms_name = "".join([c for c in sms_text.split("\n") if c.isalpha() or c.isspace() or c.isdigit()]).strip()
        sms_file_name = f"{msg_id}_{clean_sms_name[:20]}.txt" if clean_sms_name else f"SMS_{msg_id}.txt"
        with open(os.path.join(char_dir, sms_file_name), "w", encoding="utf-8") as out_f: out_f.write(final_sms_content)
        短信计数 += 1

    # === 处理 3:图鉴 / 词典 ===
    print(f"[信息] 按类别整理Codex词条...")
    图鉴数量,已使用图鉴 = 0, set()
    对于j_codex中的c_entry:
        body_id = c_entry["body_id"]
        title_id = c_entry["title_id"]
        类别 = c_entry["category"]
       
        title_text = text_map.get(title_id, "").replace("\n", "\n").strip()
        body_text = text_map.get(body_id, "").replace("\n", "\n").strip()
       
        如果没有正文文本而有标题文本:
            body_text, title_text, body_id = title_text, "", title_id
        如果正文为空,或正文编号已在已用图鉴中,则继续
        已使用.codex.add(body_id)
       
        {body_id}\n\n
        如果 title_text:final_c += f“{title_text.upper()}\n\n”
        final_c += body_text
       
        os.makedirs(os.path.join(OUTPUT_CODEX, category), exist_ok=True)
        lines = body_text.split("\n")
        name_source = title_text if title_text else (lines if lines else "Codex")
        c_name = "".join([c for c in name_source if c.isalpha() or c.isspace() or c.isdigit()]).strip()
        用 open(os.path.join(OUTPUT_CODEX, category, f"{body_id}_{c_name[:20]}.txt"), "w", encoding="utf-8") as f: f.write(final_c)
        codex_count += 1

    # === 处理 4:电脑邮件 ===
    打印(f"[INFO] 按所有者对笔记本电脑邮件和系统文件进行排序...")
    邮件数量,已用邮件 = 0,集合()
    对于 j_mails 中的邮件:
        body_id = mail["body_id"]
        title_id = mail["title_id"]
        owner = mail["owner"]
       
        title_text = text_map.get(title_id, "").replace("\\n", "\n").strip()
        body_text = text_map.get(body_id, "").replace("\\n", "\n").strip()
       
        如果没有正文文本而有标题文本:
            body_text, title_text, body_id = title_text, "", title_id
        如果没有正文文本或正文ID已在已使用邮件中:继续
        已用邮件.添加(正文编号)
       
        {body_id}\n\n
        如果 title_text: final_m += f"{title_text.upper()}\n\n"
        final_m += body_text
       
        使用exist_ok=True创建输出邮件目录中所有者对应的文件夹,路径通过os.path.join拼接。
        lines = body_text.split("\n")
        name_source = title_text if title_text else (lines if lines else "邮件")
        c_name = "".join([c for c in name_source if c.isalpha() or c.isspace() or c.isdigit()]).strip()
        用open(os.path.join(OUTPUT_MAILS, owner, f"{body_id}_{c_name[:20]}.txt"), "w", encoding="utf-8") as f: f.write(final_m)
        邮件数量+=1

    print(f"\n===========================================================")
    print(f"[成功] {shard_count} 个碎片已原样集齐!")
    print(f"[成功] 已提取 {sms_count} 条传入的 NPC 消息!")
    print(f"[成功] {codex_count} 条法典词条已整理!")
    print(f"[成功] 已将{mail_count}封电脑邮件按所有者分类!")
    打印(f"\n-> 存档已准备于: {MAIN_WORKSPACE}")
    print(f"===========================================================")
    输入(“\n按回车键关闭……”)

如果 __name__ == "__main__":
    提取分流器()


💥 💥 💥 这正是我费了这么大劲的原因。也许有人能从赛博朋克源代码中提取出短信和索引数据库。💪

https://www.nexusmods.com/cyberpunk2077/mods/30884?tab=description <-- 我为德语语音制作的项目

赛博朋克 2077 的热门 MOD

探索适用于 赛博朋克 2077 的最佳MOD,带来新功能、升级画面,以及令人兴奋的方式来改变您的游戏体验。

使用 Xmod 解锁 赛博朋克 2077 的全部潜力 — 今天就探索这些顶级 MOD!