…) must keep its own # markup untouched - its text is rebuilt by those nested blocks containers = set() for b in blocks: n = b["node"] if n is None: continue if any(id(d) in collect_ids for d in n.find_all(True)): containers.add(id(n)) print("container blocks kept as-is:", len(containers)) fixed = 0 for b in blocks: uid = b["uid"] zh = trans.get(uid, b["tpl"]) if uid not in trans or not markers_ok(b, zh): continue if id(b["node"]) in containers: continue headerlinks = [h.extract() for h in b["node"].find_all("a", class_="headerlink")] html_str = build_block_html(zh, b["markers"]) set_inner(b["node"], html_str) for h in headerlinks: b["node"].append(h) fixed += 1 if title_node is not None and 1451 in trans: title_node.string = trans[1451] # ---------------------------------------------------------------- polish pass ABBR_TITLE = { "No return value.": "无返回值。", "This method has no side effects. It doesn't modify any of the instance's member variables.": "该方法没有副作用,不会修改实例的任何成员变量。", "This method should typically be overridden by the user to have any effect.": "该方法通常需要由用户重写才能生效。", "This method doesn't need an instance to be called, so it can be called directly using the class name.": "该方法无需实例即可调用,因此可以直接用类名调用。", } for el in soup.find_all(attrs={"title": True}): t = el.get("title") if t in ABBR_TITLE: el["title"] = ABBR_TITLE[t] # breadcrumb for li in soup.select("li.breadcrumb-item"): if li.get_text(strip=True) == "LimboAI 1.0 documentation": li.string = "LimboAI 1.0 文档" # footer foot = soup.find("footer") if foot: info = foot.find("div", {"role": "contentinfo"}) if info and info.find("p"): info.find("p").string = "© 2023 年至今 Serhii Snitsaruk 及 LimboAI 贡献者。" for child in list(foot.children): if isinstance(child, NavigableString): s = str(child) s = s.replace("Built with", "使用 ") s = s.replace("using a", " 构建,") s = s.replace("provided by", " 由 ") child.replace_with(s) elif getattr(child, "name", None) == "a" and child.get_text(strip=True) == "theme": child.string = "主题(theme)" last = foot.contents[-1] if foot.contents else None if isinstance(last, NavigableString): last.replace_with(" 提供。\n") # code comments inside
CODE_COMMENTS = {
"# Set common target shared between agents in a group:": "# 设置组内各智能体共享的公共目标:",
"# to prevent errors when the instance is freed.": "# 以避免实例被释放时出错。",
'# - Important: Avoid specifying a type for "obj" in GDScript': '# - 重要:在 GDScript 中不要为 "obj" 指定类型',
"# Access common target shared between agents in a group:": "# 访问组内各智能体共享的公共目标:",
'# Check if the "speed" variable exists:': '# 检查 "speed" 变量是否存在:',
'# Get object instance stored in the "object" variable.': '# 获取存储在 "object" 变量中的对象实例。',
'# Get the value of the "speed" variable, with a default value of 100.0 if not found:':
'# 获取 "speed" 变量的值,如果不存在则默认为 100.0:',
'# Handle the "movement_started" event here.': '# 在这里处理 "movement_started" 事件。',
"# It's quite handy when you need to pass some data to the event handler.":
"# 当你需要向事件处理器传递一些数据时,这非常方便。",
'# Set the behavior tree, or use a load("res://ai/trees/...") call':
'# 设置行为树,或使用 load("res://ai/trees/...") 调用',
'# Set the value of the "speed" variable:': '# 设置 "speed" 变量的值:',
"# Use chained methods and delegation to set up states:": "# 使用链式方法与委托来配置状态:",
"# `cargo` can be passed with the event when calling `dispatch()`.":
"# 调用 `dispatch()` 时可以随事件一起传入 `cargo`。",
"# Assume `hsm` is a LimboHSM instance and `bt_state` is a BTState instance":
"# 假设 `hsm` 是一个 LimboHSM 实例,`bt_state` 是一个 BTState 实例",
"# Called each time this task is ticked (aka executed).": "# 每次该任务被 tick(即被执行)时调用。",
"# Called to generate a display name for the task (requires @tool).":
"# 调用它来为任务生成显示名称(需要 @tool)。",
"# Called to generate a display name for the task.": "# 调用它来为任务生成显示名称。",
"# Called to initialize the task.": "# 调用它来初始化任务。",
"# Called when the task is entered.": "# 当任务被进入时调用。",
"# Called when the task is executed.": "# 当任务被执行时调用。",
"# Called when the task is exited.": "# 当任务被退出时调用。",
"# Example: programmatically link a BT blackboard variable to an HSM blackboard variable":
"# 示例:用代码把 BT 黑板变量链接到 HSM 黑板变量",
"# NOTE: variable names do not need to match": "# 注意:变量名不必相同",
"# Strings returned from this method are displayed as warnings in the editor.":
"# 此方法返回的字符串会作为警告显示在编辑器中。",
"# Task parameters.": "# 任务参数。",
"## AgentGroup node: Manages the shared Blackboard for agents in a group.":
"## AgentGroup 节点:管理组内各智能体共享的 Blackboard。",
'## At minimum, you only need to define the "_tick" method.': '## 至少只需要定义 "_tick" 方法。',
"## Called each frame when this state is active.": "## 当该状态处于活动状态时每帧调用。",
"## Called once, when state is initialized.": "## 状态初始化时调用一次。",
"## Called when state is entered.": "## 当状态被进入时调用。",
"## Called when state is exited.": "## 当状态被退出时调用。",
"## Children of this node are assumed to be agents that belong to a common group.":
"## 该节点的子节点被假定为属于同一组的智能体。",
"## InRange condition checks if the agent is within a range of target,":
"## InRange 条件检查智能体是否处于目标的某个范围内,",
"## Note: Each method declaration is optional.": "## 注意:每个方法声明都是可选的。",
"## PlayAnimation: Play an animation on AnimationPlayer, and wait for it to finish.":
"## PlayAnimation:在 AnimationPlayer 上播放动画,并等待其播放完毕。",
"## Returns FAILURE if the node is not found.": "## 如果找不到该节点,返回 FAILURE。",
"## Returns SUCCESS if the agent is within the defined range;":
"## 如果智能体处于定义的范围内,返回 SUCCESS;",
"## Shows or hides a node and returns SUCCESS.": "## 显示或隐藏一个节点并返回 SUCCESS。",
'## This implementation assumes that each agent has a "BTPlayer" node for AI.':
'## 该实现假定每个智能体都有一个用于 AI 的 "BTPlayer" 节点。',
"## defined by distance_min and distance_max.": "## 该范围由 distance_min 和 distance_max 定义。",
"## otherwise, returns FAILURE.": "## 否则返回 FAILURE。",
}
hits = 0
for pre in soup.find_all("pre"):
for txt in pre.find_all(string=True):
s = str(txt)
orig = s
for en, zh in CODE_COMMENTS.items():
if en in s:
s = s.replace(en, zh)
if s != orig:
txt.replace_with(s)
hits += 1
print("code comment spans translated:", hits)
# translation badge in the sidebar
logo_link = soup.select_one(".wy-side-nav-search > a")
if logo_link is not None:
badge = BeautifulSoup(
''
'中文汉化版 · 基于官方英文文档翻译,欢迎对照原文阅读
', "html.parser")
for k in list(badge.children):
logo_link.insert_after(k.extract())
out = str(soup)
out = out.replace('LimboAI 1.0 documentation",
"LimboAI 1.0 中文文档 ")
open(OUT, "w", encoding="utf-8").write(out)
print("rebuilt blocks:", fixed, "->", OUT)