Commit 0e1de290 authored by BaiJiangJie's avatar BaiJiangJie Committed by 老广

[Update] 显示授权节点树,修复无授权资产时显示资产的bug (#119)

* [Update] 修改变量名小细节

* [Update] 显示授权节点树

* [Bugfix] 修复无授权资产时,显示资产卡死的bug

* [Update] 修改翻译
parent 026713de
...@@ -7,6 +7,7 @@ import threading ...@@ -7,6 +7,7 @@ import threading
import os import os
import math import math
import time import time
from treelib import Tree
from . import char from . import char
from .config import config from .config import config
...@@ -40,6 +41,7 @@ class InteractiveServer: ...@@ -40,6 +41,7 @@ class InteractiveServer:
self.page = 1 self.page = 1
self.total_assets = 0 self.total_assets = 0
self.total_count = 0 # 分页展示中用来存放数目总条数 self.total_count = 0 # 分页展示中用来存放数目总条数
self.nodes_tree = None # 授权节点树
self.get_user_assets_paging_async() self.get_user_assets_paging_async()
self.get_user_nodes_async() self.get_user_nodes_async()
...@@ -81,7 +83,7 @@ class InteractiveServer: ...@@ -81,7 +83,7 @@ class InteractiveServer:
_("{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} search, such as: /ip.{R}"), _("{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} search, such as: /ip.{R}"),
_("{T}3) Enter {green}p{end} to display the host you have permission.{R}"), _("{T}3) Enter {green}p{end} to display the host you have permission.{R}"),
_("{T}4) Enter {green}g{end} to display the node that you have permission.{R}"), _("{T}4) Enter {green}g{end} to display the node that you have permission.{R}"),
_("{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under the node, such as g1.{R}"), _("{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the node, such as g1.{R}"),
_("{T}6) Enter {green}s{end} Chinese-english switch.{R}"), _("{T}6) Enter {green}s{end} Chinese-english switch.{R}"),
_("{T}7) Enter {green}h{end} help.{R}"), _("{T}7) Enter {green}h{end} help.{R}"),
_("{T}0) Enter {green}q{end} exit.{R}") _("{T}0) Enter {green}q{end} exit.{R}")
...@@ -104,7 +106,7 @@ class InteractiveServer: ...@@ -104,7 +106,7 @@ class InteractiveServer:
elif opt in ['p', 'P', '']: elif opt in ['p', 'P', '']:
self.display_assets() self.display_assets()
elif opt in ['g', 'G']: elif opt in ['g', 'G']:
self.display_nodes() self.display_nodes_tree()
elif opt.startswith("g") and opt.lstrip("g").isdigit(): elif opt.startswith("g") and opt.lstrip("g").isdigit():
self.display_node_assets(int(opt.lstrip("g"))) self.display_node_assets(int(opt.lstrip("g")))
elif opt in ['q', 'Q', 'exit', 'quit']: elif opt in ['q', 'Q', 'exit', 'quit']:
...@@ -169,13 +171,27 @@ class InteractiveServer: ...@@ -169,13 +171,27 @@ class InteractiveServer:
self.client.send(wr(format_with_zh(size_list, *data))) self.client.send(wr(format_with_zh(size_list, *data)))
self.client.send(wr(_("Total: {}").format(len(self.nodes)), before=1)) self.client.send(wr(_("Total: {}").format(len(self.nodes)), before=1))
def display_nodes_tree(self):
if self.nodes is None:
self.get_user_nodes()
if not self.nodes:
self.client.send(wr(_('No Nodes'), before=1))
return
self.nodes_tree.show(key=lambda node: node.identifier)
self.client.send(wr(title(_("Node: [ ID.Name(Asset amount) ]")), before=1))
self.client.send(wr(self.nodes_tree._reader.replace('\n', '\r\n'), before=1))
prompt = _("Tips: Enter g+NodeID to display the host under the node, such as g1")
self.client.send(wr(title(prompt), before=1))
def display_node_assets(self, _id): def display_node_assets(self, _id):
if self.nodes is None: if self.nodes is None:
self.get_user_nodes() self.get_user_nodes()
if _id > len(self.nodes) or _id <= 0: if _id > len(self.nodes) or _id <= 0:
msg = wr(warning(_("There is no matched node, please re-enter"))) msg = wr(warning(_("There is no matched node, please re-enter")))
self.client.send(msg) self.client.send(msg)
self.display_nodes() self.display_nodes_tree()
return return
assets = self.nodes[_id - 1].assets_granted assets = self.nodes[_id - 1].assets_granted
...@@ -218,6 +234,19 @@ class InteractiveServer: ...@@ -218,6 +234,19 @@ class InteractiveServer:
def get_user_nodes(self): def get_user_nodes(self):
self.nodes = app_service.get_user_asset_groups(self.client.user) self.nodes = app_service.get_user_asset_groups(self.client.user)
self.sort_nodes()
self.construct_nodes_tree()
def sort_nodes(self):
self.nodes = sorted(self.nodes, key=lambda node: node.key)
def construct_nodes_tree(self):
self.nodes_tree = Tree()
for index, node in enumerate(self.nodes):
tag = "{}.{}({})".format(index+1, node.name, node.assets_amount)
key = node.key
parent_key = key[:node.key.rfind(':')] or None
self.nodes_tree.create_node(tag=tag, identifier=key, data=node, parent=parent_key)
def get_user_nodes_async(self): def get_user_nodes_async(self):
thread = threading.Thread(target=self.get_user_nodes) thread = threading.Thread(target=self.get_user_nodes)
...@@ -306,11 +335,16 @@ class InteractiveServer: ...@@ -306,11 +335,16 @@ class InteractiveServer:
while True: while True:
try: try:
page, result = gen_result.send(action) page, result = gen_result.send(action)
except TypeError as e: except TypeError:
try:
page, result = next(gen_result) page, result = next(gen_result)
logger.info(e)
except StopIteration: except StopIteration:
logger.info('StopIteration') logger.info('No Assets')
self.display_banner()
self.client.send(wr(_("No Assets"), before=1))
return None
except StopIteration:
logger.info('Back display result paging.')
self.display_banner() self.display_banner()
return None return None
self.display_result_of_page(page, result) self.display_result_of_page(page, result)
...@@ -324,13 +358,12 @@ class InteractiveServer: ...@@ -324,13 +358,12 @@ class InteractiveServer:
right = left + self.page_size right = left + self.page_size
result = result_list[left:right] result = result_list[left:right]
if not result and (result_list is self.assets_list) and self.finish: if not result and (result_list is self.assets_list) and self.finish and self.total_assets == 0:
# 上一页已经是最后一页, 还是展示最后一页(展示上一页) # 无授权资产
left -= page_up_size return None, None
page -= 1
continue elif not result and (result_list is self.assets_list) and self.finish:
elif not result and (result_list is not self.assets_list): # 上一页是最后一页
# 上一页已经是最后一页, 还是展示最后一页(展示上一页)
left -= page_up_size left -= page_up_size
page -= 1 page -= 1
continue continue
...@@ -338,6 +371,11 @@ class InteractiveServer: ...@@ -338,6 +371,11 @@ class InteractiveServer:
# 还有下一页(暂时没有加载完),需要等待 # 还有下一页(暂时没有加载完),需要等待
time.sleep(1) time.sleep(1)
continue continue
elif not result and (result_list is not self.assets_list):
# 上一页是最后一页
left -= page_up_size
page -= 1
continue
else: else:
# 其他4中情况,返回assets # 其他4中情况,返回assets
action = yield (page, result) action = yield (page, result)
...@@ -369,8 +407,8 @@ class InteractiveServer: ...@@ -369,8 +407,8 @@ class InteractiveServer:
self.client.send(wr(_('Tips: Enter the asset ID and log directly into the asset.'), before=1)) self.client.send(wr(_('Tips: Enter the asset ID and log directly into the asset.'), before=1))
prompt_page_up = _("Page up: P/p") prompt_page_up = _("Page up: P/p")
prompt_page_down = _("Page down: Enter|N/n") prompt_page_down = _("Page down: Enter|N/n")
prompt_exit = _("BACK: B/b") prompt_back = _("BACK: B/b")
prompts = [prompt_page_up, prompt_page_down, prompt_exit] prompts = [prompt_page_up, prompt_page_down, prompt_back]
prompt = '\t'.join(prompts) prompt = '\t'.join(prompts)
self.client.send(wr(prompt, before=1)) self.client.send(wr(prompt, before=1))
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-30 10:37+0800\n" "POT-Creation-Date: 2018-10-31 11:49+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n" "PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n" "Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/en/LC\n" "Language-Team: Language locale/en/LC\n"
...@@ -20,7 +20,7 @@ msgstr "" ...@@ -20,7 +20,7 @@ msgstr ""
msgid "Connect idle more than {} minutes, disconnect" msgid "Connect idle more than {} minutes, disconnect"
msgstr "" msgstr ""
#: coco/interactive.py:78 #: coco/interactive.py:80
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"\n" "\n"
...@@ -28,123 +28,131 @@ msgid "" ...@@ -28,123 +28,131 @@ msgid ""
"{end}{R}{R}" "{end}{R}{R}"
msgstr "" msgstr ""
#: coco/interactive.py:80 #: coco/interactive.py:82
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, " "{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
"Comment{end} to search login(if unique).{R}" "Comment{end} to search login(if unique).{R}"
msgstr "" msgstr ""
#: coco/interactive.py:81 #: coco/interactive.py:83
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} " "{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
"search, such as: /ip.{R}" "search, such as: /ip.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:82 #: coco/interactive.py:84
#, python-brace-format #, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}" msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:83 #: coco/interactive.py:85
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}" "{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:84 #: coco/interactive.py:86
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under " "{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the "
"the node, such as g1.{R}" "node, such as g1.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:85 #: coco/interactive.py:87
#, python-brace-format #, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}" msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:86 #: coco/interactive.py:88
#, python-brace-format #, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}" msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:87 #: coco/interactive.py:89
#, python-brace-format #, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}" msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:157 #: coco/interactive.py:159
msgid "No" msgid "No"
msgstr "" msgstr ""
#: coco/interactive.py:164 #: coco/interactive.py:166
msgid "Name" msgid "Name"
msgstr "" msgstr ""
#: coco/interactive.py:164 #: coco/interactive.py:166
msgid "Assets" msgid "Assets"
msgstr "" msgstr ""
#: coco/interactive.py:170 #: coco/interactive.py:172
msgid "Total: {}" msgid "Total: {}"
msgstr "" msgstr ""
#: coco/interactive.py:176 #: coco/interactive.py:177
msgid "Node [ ID.Name(Asset) ]"
msgstr ""
#: coco/interactive.py:179
msgid "Enter g+NodeID to display the host under the node, such as g1."
msgstr ""
#: coco/interactive.py:186
msgid "There is no matched node, please re-enter" msgid "There is no matched node, please re-enter"
msgstr "" msgstr ""
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "Hostname" msgid "Hostname"
msgstr "" msgstr ""
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "IP" msgid "IP"
msgstr "" msgstr ""
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "LoginAs" msgid "LoginAs"
msgstr "" msgstr ""
#: coco/interactive.py:201 #: coco/interactive.py:211
msgid "Comment" msgid "Comment"
msgstr "" msgstr ""
#: coco/interactive.py:211 #: coco/interactive.py:221
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}" msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "" msgstr ""
#: coco/interactive.py:264 #: coco/interactive.py:296
msgid "Select a login:: " msgid "Select a login:: "
msgstr "" msgstr ""
#: coco/interactive.py:287 #: coco/interactive.py:319
msgid "" msgid ""
"Terminal does not support login Windows, please use web terminal to access" "Terminal does not support login Windows, please use web terminal to access"
msgstr "" msgstr ""
#: coco/interactive.py:369 #: coco/interactive.py:401
msgid "Tips: Enter the asset ID and log directly into the asset." msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr "" msgstr ""
#: coco/interactive.py:370 #: coco/interactive.py:402
msgid "Page up: P/p" msgid "Page up: P/p"
msgstr "" msgstr ""
#: coco/interactive.py:371 #: coco/interactive.py:403
msgid "Page down: Enter|N/n" msgid "Page down: Enter|N/n"
msgstr "" msgstr ""
#: coco/interactive.py:372 #: coco/interactive.py:404
msgid "BACK: B/b" msgid "BACK: B/b"
msgstr "" msgstr ""
#: coco/interactive.py:393 #: coco/interactive.py:425
msgid "No system user" msgid "No system user"
msgstr "" msgstr ""
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-30 10:37+0800\n" "POT-Creation-Date: 2018-10-31 11:49+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n" "PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n" "Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/zh\n" "Language-Team: Language locale/zh\n"
...@@ -20,7 +20,7 @@ msgstr "" ...@@ -20,7 +20,7 @@ msgstr ""
msgid "Connect idle more than {} minutes, disconnect" msgid "Connect idle more than {} minutes, disconnect"
msgstr "空闲时间超过 {} 分钟,断开连接" msgstr "空闲时间超过 {} 分钟,断开连接"
#: coco/interactive.py:78 #: coco/interactive.py:80
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"\n" "\n"
...@@ -30,7 +30,7 @@ msgstr "" ...@@ -30,7 +30,7 @@ msgstr ""
"\n" "\n"
"{T}{T}{title} {user}, 欢迎使用Jumpserver开源跳板机系统 {end}{R}{R}" "{T}{T}{title} {user}, 欢迎使用Jumpserver开源跳板机系统 {end}{R}{R}"
#: coco/interactive.py:80 #: coco/interactive.py:82
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, " "{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
...@@ -39,7 +39,7 @@ msgstr "" ...@@ -39,7 +39,7 @@ msgstr ""
"{T}1) 输入 {green}ID{end} 直接登录 或 输入{green}部分 IP,主机名,备注{end} 进" "{T}1) 输入 {green}ID{end} 直接登录 或 输入{green}部分 IP,主机名,备注{end} 进"
"行搜索登录(如果唯一).{R}" "行搜索登录(如果唯一).{R}"
#: coco/interactive.py:81 #: coco/interactive.py:83
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} " "{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
...@@ -48,109 +48,116 @@ msgstr "" ...@@ -48,109 +48,116 @@ msgstr ""
"{T}2) 输入 {green}/{end} + {green}IP, 主机名{end} or {green}备注 {end}搜索. " "{T}2) 输入 {green}/{end} + {green}IP, 主机名{end} or {green}备注 {end}搜索. "
"如: /ip{R}" "如: /ip{R}"
#: coco/interactive.py:82 #: coco/interactive.py:84
#, python-brace-format #, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}" msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr "{T}3) 输入 {green}p{end} 显示您有权限的主机.{R}" msgstr "{T}3) 输入 {green}p{end} 显示您有权限的主机.{R}"
#: coco/interactive.py:83 #: coco/interactive.py:85
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}" "{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr "{T}4) 输入 {green}g{end} 显示您有权限的节点.{R}" msgstr "{T}4) 输入 {green}g{end} 显示您有权限的节点.{R}"
#: coco/interactive.py:84 #: coco/interactive.py:86
#, python-brace-format
msgid "" msgid ""
"{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under " "{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the "
"the node, such as g1.{R}" "node, such as g1.{R}"
msgstr "{T}5) 输入 {green}g{end} + {green}ID{end} 显示节点下主机. 如: g1{R}" msgstr "{T}5) 输入 {green}g{end} + {green}节点ID{end} 显示节点下主机. 如: g1{R}"
#: coco/interactive.py:85 #: coco/interactive.py:87
#, python-brace-format #, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}" msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr "{T}6) 输入 {green}s{end} 中/英文切换.{R}" msgstr "{T}6) 输入 {green}s{end} 中/英文切换.{R}"
#: coco/interactive.py:86 #: coco/interactive.py:88
#, python-brace-format #, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}" msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr "{T}7) 输入 {green}h{end} 帮助.{R}" msgstr "{T}7) 输入 {green}h{end} 帮助.{R}"
#: coco/interactive.py:87 #: coco/interactive.py:89
#, python-brace-format #, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}" msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr "{T}0) 输入 {green}q{end} 退出.{R}" msgstr "{T}0) 输入 {green}q{end} 退出.{R}"
#: coco/interactive.py:157 #: coco/interactive.py:159
msgid "No" msgid "No"
msgstr "无" msgstr "无"
#: coco/interactive.py:164 #: coco/interactive.py:166
msgid "Name" msgid "Name"
msgstr "名称" msgstr "名称"
#: coco/interactive.py:164 #: coco/interactive.py:166
msgid "Assets" msgid "Assets"
msgstr "资产" msgstr "资产"
#: coco/interactive.py:170 #: coco/interactive.py:172
msgid "Total: {}" msgid "Total: {}"
msgstr "总共: {}" msgstr "总共: {}"
#: coco/interactive.py:176 #: coco/interactive.py:177
msgid "Node: [ ID.Name(Asset amount) ]"
msgstr "节点: [ ID.名称(资产数量) ]"
#: coco/interactive.py:179
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr "提示: 输入 g+节点ID 显示节点下主机. 如: g1"
#: coco/interactive.py:186
msgid "There is no matched node, please re-enter" msgid "There is no matched node, please re-enter"
msgstr "没有匹配分组,请重新输入" msgstr "没有匹配分组,请重新输入"
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "Hostname" msgid "Hostname"
msgstr "主机名" msgstr "主机名"
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "IP" msgid "IP"
msgstr "" msgstr ""
#: coco/interactive.py:187 #: coco/interactive.py:197
msgid "LoginAs" msgid "LoginAs"
msgstr "登录用户" msgstr "登录用户"
#: coco/interactive.py:201 #: coco/interactive.py:211
msgid "Comment" msgid "Comment"
msgstr "备注" msgstr "备注"
#: coco/interactive.py:211 #: coco/interactive.py:221
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}" msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}" msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}"
#: coco/interactive.py:264 #: coco/interactive.py:296
msgid "Select a login:: " msgid "Select a login:: "
msgstr "选择一个登录:" msgstr "选择一个登录:"
#: coco/interactive.py:287 #: coco/interactive.py:319
msgid "" msgid ""
"Terminal does not support login Windows, please use web terminal to access" "Terminal does not support login Windows, please use web terminal to access"
msgstr "终端不支持登录windows, 请使用web terminal访问" msgstr "终端不支持登录windows, 请使用web terminal访问"
#: coco/interactive.py:369 #: coco/interactive.py:401
msgid "Tips: Enter the asset ID and log directly into the asset." msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr "提示: 输入资产ID,直接登录资产." msgstr "提示: 输入资产ID,直接登录资产."
#: coco/interactive.py:370 #: coco/interactive.py:402
msgid "Page up: P/p" msgid "Page up: P/p"
msgstr "上一页: P/p" msgstr "上一页: P/p"
#: coco/interactive.py:371 #: coco/interactive.py:403
msgid "Page down: Enter|N/n" msgid "Page down: Enter|N/n"
msgstr "下一页: Enter|N/n" msgstr "下一页: Enter|N/n"
#: coco/interactive.py:372 #: coco/interactive.py:404
msgid "BACK: B/b" msgid "BACK: B/b"
msgstr "返回: B/b" msgstr "返回: B/b"
#: coco/interactive.py:393 #: coco/interactive.py:425
msgid "No system user" msgid "No system user"
msgstr "没有系统用户" msgstr "没有系统用户"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment