适用目标:给
Codex.app做本地中文化(非官方多语言开关),并保证应用可启动。
这条路径是可行的:
- 只改 用户目录副本(
~/Applications/Codex.app),不要直接改系统安装版。 - 对
app.asar做 等字节长度 文案替换(中文不足用空格补齐)。 - 同步更新
asar内部完整性(文件 hash + blocks)。 - 同步更新
Info.plist里的ElectronAsarIntegrity哈希(哈希对象是asar header)。 - 重新
codesign --deep。 - 清缓存并从指定路径启动。
否则会出现:启动崩溃、ASAR Integrity Violation、界面不生效、总是看到旧英文。
- macOS
- 已安装 Codex
- 有权限写
~/Applications - 终端可用
python3、codesign
仓库已提供可直接执行的补丁脚本:
scripts/apply_codex_cn_patch.shscripts/apply_codex_cn_patch.pypatches/codex_5_3_cn_patch.json
执行方式:
./scripts/apply_codex_cn_patch.sh ~/Applications/Codex.app只检测不写入:
python3 ./scripts/apply_codex_cn_patch.py --app ~/Applications/Codex.app --dry-run- 补丁目标(推荐):
/Users/<你用户名>/Applications/Codex.app - 官方安装(只读参考):
/Applications/Codex.app
先复制一份官方应用到用户目录:
mkdir -p ~/Applications
rsync -a /Applications/Codex.app/ ~/Applications/Codex.app/常见原因:
- 实际运行的是另一个
Codex.app(系统版和用户版混用)。 app.asar改了,但没更新 Electron 完整性哈希。- 改了字符串长度,导致 asar 结构偏移或校验失败。
- 缓存没清(Code Cache / Cache / GPUCache)。
- 钥匙串弹窗未处理,页面操作被拦截。
- 原文:
"Open"(4 字节) - 替换:
"开 "(中文 3 字节 + 空格 1 字节)
规则:
- 新字符串字节数 <= 原字符串字节数
- 不足部分用空格补齐
- 不要改变 JS 文件总长度
除非你非常确定工具输出兼容。推荐只做“原地字节替换 + header 校验更新”。
下面脚本是通用模板:
- 读取
app.asarheader - 定位目标 js 文件
- 做等长替换
- 更新该文件
integrity.hash与integrity.blocks - 回写 header
- 更新
Info.plist中ElectronAsarIntegrity - 重签名
注意:
replacements里仅填“你确认存在”的文本对。
# -*- coding: utf-8 -*-
import json, struct, hashlib, plistlib, subprocess
from pathlib import Path
APP = Path('/Users/<你用户名>/Applications/Codex.app')
ASAR = APP / 'Contents/Resources/app.asar'
PLIST = APP / 'Contents/Info.plist'
TARGET = 'webview/assets/index-xxxx.js' # 先定位真实文件名
# old -> new(new 必须 <= old 字节长度)
replacements = [
('defaultMessage:"New thread"', 'defaultMessage:"新对话 "'),
('defaultMessage:"Open"', 'defaultMessage:"开 "'),
]
pairs = []
for old, new in replacements:
ob = old.encode('utf-8')
nb = new.encode('utf-8')
if len(nb) > len(ob):
raise ValueError(f'new too long: {old} -> {new}')
nb = nb + b' ' * (len(ob) - len(nb))
pairs.append((ob, nb, old))
with ASAR.open('r+b') as f:
head = f.read(16)
hlen = struct.unpack('<I', head[12:16])[0]
hbytes = f.read(hlen)
header = json.loads(hbytes.decode('utf-8'))
base = 16 + hlen
node = header
for p in TARGET.split('/'):
node = node['files'][p]
off = base + int(node['offset'])
size = int(node['size'])
f.seek(off)
data = f.read(size)
old_data = data
for ob, nb, label in pairs:
count = data.count(ob)
print(label, count)
if count:
data = data.replace(ob, nb)
if data != old_data:
f.seek(off)
f.write(data)
# 更新目标文件完整性
block_size = 4 * 1024 * 1024
node['integrity'] = {
'algorithm': 'SHA256',
'hash': hashlib.sha256(data).hexdigest(),
'blockSize': block_size,
'blocks': [
hashlib.sha256(data[i:i+block_size]).hexdigest()
for i in range(0, len(data), block_size)
],
}
new_hbytes = json.dumps(header, separators=(',', ':'), ensure_ascii=False).encode('utf-8')
if len(new_hbytes) != hlen:
raise RuntimeError(f'header size changed: {hlen} -> {len(new_hbytes)}')
f.seek(16)
f.write(new_hbytes)
# 更新 Info.plist 的 ElectronAsarIntegrity
with ASAR.open('rb') as f:
f.read(12)
hlen = struct.unpack('<I', f.read(4))[0]
hbytes = f.read(hlen)
header_sha = hashlib.sha256(hbytes).hexdigest()
with PLIST.open('rb') as pf:
pl = plistlib.load(pf)
pl['ElectronAsarIntegrity']['Resources/app.asar']['hash'] = header_sha
with PLIST.open('wb') as pf:
plistlib.dump(pl, pf)
# 重签名
subprocess.run(['codesign', '--force', '--deep', '--sign', '-', str(APP)], check=True)
print('done')先在 asar 里搜文案关键词,确定是哪个 webview/assets/*.js:
rg -a -n "Let’s build|New thread|settings.nav.general-settings" ~/Applications/Codex.app/Contents/Resources/app.asar再按 asar header 解析对应路径内容(可写个小脚本)确认该字符串确实在该文件中。
每次补丁后都做:
pkill -f '/Users/<你用户名>/Applications/Codex.app/Contents/MacOS/Codex' || true
rm -rf "$HOME/Library/Application Support/Codex/Code Cache" \
"$HOME/Library/Application Support/Codex/Cache" \
"$HOME/Library/Application Support/Codex/GPUCache"
open -a /Users/<你用户名>/Applications/Codex.app并确认运行进程路径:
ps -ax -o pid=,command= | rg 'Codex.app/Contents/MacOS/Codex'必须看到你希望的路径(用户版或系统版)。
首次读取 Codex Safe Storage 会弹钥匙串授权。
- 选
始终允许 - 如果不处理,该弹窗会挡住设置页和操作,造成“看起来没生效”
- 看当前运行的是哪个 app 路径。
- 查
app.asar里目标字符串是否已是中文。 - 若是中文但界面仍英文:清缓存后重启。
- 若启动崩溃:检查
ElectronAsarIntegrity与codesign。 - 若仍有英文:继续按页面截图做增量替换(分批次)。
- 每次官方更新可能覆盖补丁,需要重做。
- 非官方方式,存在兼容性风险。
- 建议保留一份未修改的
Codex.app备份。