瀏覽代碼

demo: i18n

wangweimin 4 年之前
父節點
當前提交
d912a493c6
共有 8 個文件被更改,包括 348 次插入129 次删除
  1. 47 3
      demos/__main__.py
  2. 35 12
      demos/bmi.py
  3. 19 9
      demos/bokeh_app.py
  4. 22 18
      demos/chat_room.py
  5. 6 1
      demos/doc_demo.py
  6. 86 31
      demos/input_usage.py
  7. 118 47
      demos/output_usage.py
  8. 15 8
      demos/set_env_demo.py

+ 47 - 3
demos/__main__.py

@@ -12,9 +12,50 @@ from demos.set_env_demo import main as set_env_demo
 from pywebio import STATIC_PATH
 from pywebio.output import put_markdown, put_row, put_html, style
 from pywebio.platform.tornado import webio_handler
+from pywebio.session import get_info
 from tornado.options import define, options
 
-index_md = r"""### 基本demo
+index_md = r"""### Basic demo
+
+ - [BMI calculation](./bmi): Calculating Body Mass Index based on height and weight [source](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py)
+ - [Online chat room](./chat_room): Chat with everyone currently online [source](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)
+ - [Input demo](./input_usage): Demonstrate various input usage supported by PyWebIO [source](https://github.com/wang0618/PyWebIO/blob/dev/demos/input_usage.py)
+ - [Output demo](./output_usage): Demonstrate various output usage supported by PyWebIO [source](https://github.com/wang0618/PyWebIO/blob/dev/demos/output_usage.py)
+
+### Data visualization demo
+PyWebIO supports for data visualization with the third-party libraries.
+
+ - Use `bokeh` for data visualization [**demos**]({charts_demo_host}/?app=bokeh)
+ - Use `plotly` for data visualization [**demos**]({charts_demo_host}/?app=plotly)
+ - Use `pyecharts` for data visualization [**demos**]({charts_demo_host}/?app=pyecharts)
+ - Use `cutecharts.py` to create hand drawing style charts [**demos**]({charts_demo_host}/?app=cutecharts)
+
+**Screenshots**
+
+<a href="{charts_demo_host}/?app=bokeh">
+    <img src="https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/bokeh.png" alt="bokeh demo">
+</a>
+
+<a href="{charts_demo_host}/?app=plotly">
+    <img src="https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/plotly.png" alt="plotly demo">
+</a>
+
+<a href="{charts_demo_host}/?app=pyecharts">
+    <img src="https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/pyecharts.gif" alt="pyecharts demo">
+</a>
+
+<a href="{charts_demo_host}/?app=cutecharts">
+    <img src="https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/cutecharts.png" alt="cutecharts demo">
+</a>
+
+### Links
+* PyWebIO Github [github.com/wang0618/PyWebIO](https://github.com/wang0618/PyWebIO)
+* Document [pywebio.readthedocs.io](https://pywebio.readthedocs.io)
+
+""".format(charts_demo_host=charts_demo_host)
+
+
+index_md_zh = r"""### 基本demo
 
  - [BMI计算](./bmi): 根据身高体重计算BMI指数 [源码](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py)
  - [聊天室](./chat_room): 和当前所有在线的人聊天 [源码](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)
@@ -54,10 +95,10 @@ PyWebIO还支持使用第三方库进行数据可视化
 
 """.format(charts_demo_host=charts_demo_host)
 
-
 def index():
     """PyWebIO demos
 
+    Basic demo and data visualization demo of PyWebIO.
     PyWebIO的基本demo和数据可视化demo
     """
     style(put_row([
@@ -66,7 +107,10 @@ def index():
     ], size='1fr auto'), 'align-items:center')
     put_html('<script async defer src="https://buttons.github.io/buttons.js"></script>')
 
-    put_markdown(index_md)
+    if 'zh' in get_info().user_language:
+        put_markdown(index_md_zh)
+    else:
+        put_markdown(index_md)
 
 
 if __name__ == "__main__":

+ 35 - 12
demos/bmi.py

@@ -9,24 +9,47 @@ Simple application for calculating `Body Mass Index <https://en.wikipedia.org/wi
 from pywebio import start_server
 from pywebio.input import *
 from pywebio.output import *
-from pywebio.session import set_env
+from pywebio.session import get_info
+
+
+def t(eng, chinese):
+    """return English or Chinese text according to the user's browser language"""
+    return chinese if 'zh' in get_info().user_language else eng
 
 
 def main():
     """BMI Calculation
 
+    Simple application for calculating Body Mass Index.
     计算BMI指数的简单应用
     """
 
-    put_markdown("""# BMI指数
+    put_markdown(t("""# Body Mass Index
+    
+    [Body mass index](https://en.wikipedia.org/wiki/Body_mass_index) (BMI) is a measure of body fat based on height and weight that applies to adult men and women. 
+    
+    BMI Categories:
+    
+    | Category             | BMI           |
+    | -------------------- | ------------- |
+    | Severely underweight | BMI<14.9      |
+    | Underweight          | 14.9≤BMI<18.4 |
+    | Normal               | 18.4≤BMI<22.9 |
+    | Overweight           | 22.9≤BMI<27.5 |
+    | Moderately obese     | 27.5≤BMI<40   |
+    | Severely obese       | BMI≥40        |
+    
+    ## BMI calculation
+    The source code of this application is [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py)
+    """, """# BMI指数
 
     [`BMI指数`](https://baike.baidu.com/item/%E4%BD%93%E8%B4%A8%E6%8C%87%E6%95%B0/1455733)(Body Mass Index,BMI),是用体重千克数除以身高米数的平方得出的数字,是国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。
     
     成年人的BMI值处于以下阶段
     
     | 体形分类 | BMI值范围 |
-    | -------- | --------- |
-    | 极瘦   | BMI<14.9    |
+    | ------ | -------- |
+    | 极瘦    | BMI<14.9    |
     | 偏瘦    | 14.9≤BMI<18.4     |
     | 正常    | 18.4≤BMI<22.9     |
     | 过重    |  22.9≤BMI<27.5  |
@@ -36,22 +59,22 @@ def main():
     ## BMI指数计算器
     本程序的源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py)
     
-    """, strip_indent=4)
+    """), strip_indent=4)
 
-    info = input_group('计算BMI:', [
-        input("请输入你的身高(cm)", name="height", type=FLOAT),
-        input("请输入你的体重(kg)", name="weight", type=FLOAT),
+    info = input_group(t('BMI calculation', '计算BMI:'), [
+        input(t("Your Height(cm)", "请输入你的身高(cm)"), name="height", type=FLOAT),
+        input(t("Your Weight(kg)", "请输入你的体重(kg)"), name="weight", type=FLOAT),
     ])
 
     BMI = info['weight'] / (info['height'] / 100) ** 2
 
-    top_status = [(14.9, '极瘦'), (18.4, '偏瘦'),
-                  (22.9, '正常'), (27.5, '过重'),
-                  (40.0, '肥胖'), (float('inf'), '非常肥胖')]
+    top_status = [(14.9, t('Severely underweight', '极瘦')), (18.4, t('Underweight', '偏瘦')),
+                  (22.9, t('Normal', '正常')), (27.5, t('Overweight', '过重')),
+                  (40.0, t('Moderately obese', '肥胖')), (float('inf'), t('Severely obese', '非常肥胖'))]
 
     for top, status in top_status:
         if BMI <= top:
-            put_markdown('你的 BMI 值: `%.1f`,身体状态:`%s`' % (BMI, status))
+            put_markdown(t('Your BMI: `%.1f`, Category: `%s`', '你的 BMI 值: `%.1f`,身体状态: `%s`') % (BMI, status))
             break
 
 

+ 19 - 9
demos/bokeh_app.py

@@ -7,6 +7,7 @@ from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature
 
 from pywebio import start_server
 from pywebio.output import *
+from pywebio.session import get_info
 
 
 def bkapp(doc):
@@ -34,15 +35,24 @@ def bkapp(doc):
 def main():
     output_notebook(verbose=False, notebook_type='pywebio')
 
-    put_markdown("""# Bokeh Applications in PyWebIO
-    
-    [Bokeh Applications](https://docs.bokeh.org/en/latest/docs/user_guide/server.html) 支持向图表的添加按钮、输入框等交互组件,并向组件添加Python回调,从而创建可以与Python代码交互的可视化图表。
-    
-    在PyWebIO中,你也可以使用 `bokeh.io.show()` 来显示一个Bokeh App,和输出普通图表一样,只需要在会话开始时调用 `bokeh.io.output_notebook(notebook_type='pywebio')` 来设置PyWebIO输出环境。
-    
-    以下为一个 Bokeh App demo:
-    
-    """, lstrip=True)
+    if 'zh' in get_info().user_language:
+        put_markdown("""# Bokeh Applications in PyWebIO
+        [Bokeh Applications](https://docs.bokeh.org/en/latest/docs/user_guide/server.html) 支持向图表的添加按钮、输入框等交互组件,并向组件添加Python回调,从而创建可以与Python代码交互的可视化图表。
+
+        在PyWebIO中,你也可以使用 `bokeh.io.show()` 来显示一个Bokeh App,和输出普通图表一样,只需要在会话开始时调用 `bokeh.io.output_notebook(notebook_type='pywebio')` 来设置PyWebIO输出环境。
+
+        以下为一个 Bokeh App demo:
+        """, lstrip=True)
+    else:
+        put_markdown("""# Bokeh Applications in PyWebIO
+        [Bokeh Applications](https://docs.bokeh.org/en/latest/docs/user_guide/server.html) can be built by starting the Bokeh server. The purpose of the Bokeh server is to make it easy for Python users to create interactive web applications that can connect front-end UI events to real, running Python code.
+
+        In PyWebIO, you can also use bokeh.io.show() to display a Bokeh App.
+
+        You can use `bokeh.io.output_notebook(notebook_type='pywebio')` in the PyWebIO session to setup Bokeh environment. Then you can use `bokeh.io.show()` to output a boken application.
+
+        This is a demo of Bokeh App: 
+        """, lstrip=True)
 
     show(bkapp)
 

+ 22 - 18
demos/chat_room.py

@@ -13,7 +13,11 @@ import asyncio
 from pywebio import start_server, run_async
 from pywebio.input import *
 from pywebio.output import *
-from pywebio.session import defer_call, set_env, run_js
+from pywebio.session import defer_call, get_info
+
+def t(eng, chinese):
+    """return English or Chinese text according to the user's browser language"""
+    return chinese if 'zh' in get_info().user_language else eng
 
 # 最大消息记录保存
 MAX_MESSAGES_CNT = 10 ** 4
@@ -23,16 +27,16 @@ online_users = set()  # 在线用户
 
 
 async def refresh_msg(my_name, msg_box):
-    """刷新聊天消息"""
+    """send new message to current session"""
     global chat_msgs
     last_idx = len(chat_msgs)
     while True:
         await asyncio.sleep(0.5)
         for m in chat_msgs[last_idx:]:
-            if m[0] != my_name:  # 仅刷新其他人的新信息
+            if m[0] != my_name:  # only refresh message that not sent by current user
                 msg_box.append(put_markdown('`%s`: %s' % m))
 
-        # 清理聊天记录
+        # remove expired message
         if len(chat_msgs) > MAX_MESSAGES_CNT:
             chat_msgs = chat_msgs[len(chat_msgs) // 2:]
 
@@ -40,44 +44,44 @@ async def refresh_msg(my_name, msg_box):
 
 
 async def main():
-    """PyWebIO聊天室
+    """PyWebIO chat room
 
+    You can chat with everyone currently online.
     和当前所有在线的人聊天
     """
     global chat_msgs
 
-    put_markdown("## PyWebIO聊天室\n欢迎来到聊天室,你可以和当前所有在线的人聊天。你可以在浏览器的多个标签页中打开本页面来测试聊天效果。"
-    "本应用使用不到80行代码实现,源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)", lstrip=True)
+    put_markdown(t("## PyWebIO chat room\nWelcome to the chat room, you can chat with all the people currently online. You can open this page in multiple tabs of your browser to simulate a multi-user environment. This application uses less than 90 lines of code, the source code is [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)", "## PyWebIO聊天室\n欢迎来到聊天室,你可以和当前所有在线的人聊天。你可以在浏览器的多个标签页中打开本页面来测试聊天效果。本应用使用不到90行代码实现,源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)"), lstrip=True)
 
     msg_box = output()
     put_scrollable(msg_box, height=300, keep_bottom=True)
-    nickname = await input("请输入你的昵称", required=True, validate=lambda n: '昵称已被使用' if n in online_users or n == '📢' else None)
+    nickname = await input(t("Your nickname", "请输入你的昵称"), required=True, validate=lambda n: t('This name is already been used', '昵称已被使用') if n in online_users or n == '📢' else None)
 
     online_users.add(nickname)
-    chat_msgs.append(('📢', '`%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
-    msg_box.append(put_markdown('`📢`: `%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
+    chat_msgs.append(('📢', '`%s` joins the room. %s users currently online' % (nickname, len(online_users))))
+    msg_box.append(put_markdown('`📢`: `%s` join the room. %s users currently online' % (nickname, len(online_users))))
 
     @defer_call
     def on_close():
         online_users.remove(nickname)
-        chat_msgs.append(('📢', '`%s`退出聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
+        chat_msgs.append(('📢', '`%s` leaves the room. %s users currently online' % (nickname, len(online_users))))
 
     refresh_task = run_async(refresh_msg(nickname, msg_box))
 
     while True:
-        data = await input_group('发送消息', [
-            input(name='msg', help_text='消息内容支持行内Markdown语法'),
-            actions(name='cmd', buttons=['发送', '多行输入', {'label': '退出', 'type': 'cancel'}])
-        ], validate=lambda d: ('msg', '消息不为空') if d['cmd'] == '发送' and not d['msg'] else None)
+        data = await input_group(t('Send message', '发送消息'), [
+            input(name='msg', help_text=t('Message content supports inline Markdown syntax', '消息内容支持行内Markdown语法')),
+            actions(name='cmd', buttons=[t('Send', '发送'), t('Multiline Input', '多行输入'), {'label': t('Exit', '退出'), 'type': 'cancel'}])
+        ], validate=lambda d: ('msg', 'Message content cannot be empty') if d['cmd'] == t('Send', '发送') and not d['msg'] else None)
         if data is None:
             break
-        if data['cmd'] == '多行输入':
-            data['msg'] = '\n' + await textarea('消息内容', help_text='消息内容支持Markdown语法')
+        if data['cmd'] == t('Multiline Input', '多行输入'):
+            data['msg'] = '\n' + await textarea('Message content', help_text=t('Message content supports Markdown syntax', '消息内容支持Markdown语法'))
         msg_box.append(put_markdown('`%s`: %s' % (nickname, data['msg']), sanitize=True))
         chat_msgs.append((nickname, data['msg']))
 
     refresh_task.close()
-    toast("你已经退出聊天室")
+    toast("You have left the chat room")
 
 
 if __name__ == '__main__':

+ 6 - 1
demos/doc_demo.py

@@ -10,6 +10,11 @@ from os import path, listdir
 from functools import partial
 from pywebio.platform import seo
 
+
+def t(eng, chinese):
+    """return English or Chinese text according to the user's browser language"""
+    return chinese if 'zh' in get_info().user_language else eng
+
 here_dir = path.dirname(path.abspath(__file__))
 
 
@@ -70,7 +75,7 @@ def handle_code(code, title):
         with use_scope() as scope:
             put_code(p, 'python')
 
-            put_buttons(['运行', '复制代码'], onclick=[
+            put_buttons([t('Run', '运行'), t("Copy to clipboard", '复制代码')], onclick=[
                 partial(run_code, code=p, scope=scope, locals=locals),
                 partial(copytoclipboard, code=p)
             ])

+ 86 - 31
demos/input_usage.py

@@ -1,24 +1,46 @@
 """
-Demo of input
+Input demo
 ^^^^^^^^^^^^^^^
-To demonstrate various input forms supported by PyWebIO
+Demonstrate various input usage supported by PyWebIO
 
 :demo_host:`Demo </?pywebio_api=input_usage>`  `Source code <https://github.com/wang0618/PyWebIO/blob/dev/demos/input_usage.py>`_
 """
 from pywebio import start_server
 from pywebio.input import *
 from pywebio.output import *
-from pywebio.session import set_env
+from pywebio.session import set_env, get_info
+
+
+def t(eng, chinese):
+    """return English or Chinese text according to the user's browser language"""
+    return chinese if 'zh' in get_info().user_language else eng
 
 
 def main():
-    """PyWebIO输入演示
+    """PyWebIO input demo
 
-    演示PyWebIO支持的各种输入形式
+    Demonstrate various input usage supported by PyWebIO.
+    演示PyWebIO输入模块的使用
     """
     set_env(auto_scroll_bottom=True)
 
-    put_markdown("""# PyWebIO 输入演示
+    put_markdown(t("""# PyWebIO Input Example
+    
+    You can get the source code of this demo in [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/input_usage.py)
+    
+    This demo only introduces part of the functions of the PyWebIO input module. For the complete features, please refer to the [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html).
+    
+    The input functions are all defined in the `pywebio.input` module and can be imported using `from pywebio.input import *`.
+    
+    ### Basic input
+    Here are some basic types of input.
+    
+    #### Text input
+    ```python
+    name = input("What's your name?")
+    ```
+    """,
+    """# PyWebIO 输入演示
     
     在[这里](https://github.com/wang0618/PyWebIO/blob/dev/demos/input_usage.py)可以获取本Demo的源码。
     
@@ -33,13 +55,34 @@ def main():
     ```python
     name = input("What's your name?")
     ```
-    """, lstrip=True)
-    put_text("这样一行代码的效果如下:",)
+    """), lstrip=True)
+    put_text(t("The results of the above example are as follows:", "这样一行代码的效果如下:"))
     name = input("What's your name?")
     put_markdown("`name = %r`" % name)
 
     # 其他类型的输入
-    put_markdown("""PyWebIO的输入函数是同步的,在表单被提交之前,输入函数不会返回。
+    put_markdown(t("""
+    PyWebIO’s input functions is blocking and will not return until the form is successfully submitted.
+    #### Other types of input
+    Here are some other types of input functions:
+    ```python
+    # Password input
+    password = input("Input password", type=PASSWORD)
+    
+    # Drop-down selection
+    gift = select('Which gift you want?', ['keyboard', 'ipad'])
+    
+    # CheckBox
+    agree = checkbox("User Term", options=['I agree to terms and conditions'])
+    
+    # Text Area
+    text = textarea('Text Area', rows=3, placeholder='Some text')
+    
+    # File Upload
+    img = file_upload("Select a image:", accept="image/*")
+    ```
+    """, """
+    PyWebIO的输入函数是同步的,在表单被提交之前,输入函数不会返回。
     #### 其他类型的输入:
     ```python
     # 密码输入
@@ -57,21 +100,25 @@ def main():
     # 文件上传
     img = file_upload("Select a image:", accept="image/*")
     ```
-    """, lstrip=True)
+    """), lstrip=True)
     password = input("Input password", type=PASSWORD)
     put_markdown("`password = %r`" % password)
     gift = select('Which gift you want?', ['keyboard', 'ipad'])
     put_markdown("`gift = %r`" % gift)
-    agree = checkbox("用户协议", options=['I agree to terms and conditions'])
+    agree = checkbox(t("User Term", "用户协议"), options=['I agree to terms and conditions'])
     put_markdown("`agree = %r`" % agree)
     text = textarea('Text Area', rows=3, placeholder='Some text')
     put_markdown("`text = %r`" % text)
-    img = file_upload("Select a image:", accept="image/*", help_text='可以直接选择"提交"')
+    img = file_upload("Select a image:", accept="image/*", help_text=t('You can just click "Submit" button', '可以直接选择"提交"'))
     put_markdown("`img = %r`" % img)
 
     # 输入选项
-    put_markdown("""#### 输入选项
+    put_markdown(t("""#### Parameter of input functions
+    There are many parameters that can be passed to the input function:
+    ""","""#### 输入选项
     输入函数可指定的参数非常丰富:
+    """), strip_indent=4)
+    put_markdown("""
     ```python
     input('This is label', type=TEXT, placeholder='This is placeholder', 
           help_text='This is help text', required=True, 
@@ -83,9 +130,10 @@ def main():
           datalist=['candidate1', 'candidate2', 'candidate2'])
 
     # 校验函数
-    put_markdown("""我们可以为输入指定校验函数,校验函数校验通过时返回None,否则返回错误消息:
+    put_markdown(t("""You can specify a validation function for the input by using validate parameter. The validation function should return None when the check passes, otherwise an error message will be returned:""", """我们可以为输入指定校验函数,校验函数校验通过时返回None,否则返回错误消息:"""), strip_indent=4)
+    put_markdown("""
     ```python
-    def check_age(p):  # 检验函数校验通过时返回None,否则返回错误消息
+    def check_age(p):  # return None when the check passes, otherwise return the error message
         if p < 10:
             return 'Too young!!'
         if p > 60:
@@ -101,18 +149,19 @@ def main():
         if p > 60:
             return 'Too old!!'
 
-    age = input("How old are you?", type=NUMBER, validate=check_age, help_text='尝试输入一些非法值,比如"8"、"65"')
+    age = input("How old are you?", type=NUMBER, validate=check_age, help_text=t('Try to input some illegal values, such as "8", "65"','尝试输入一些非法值,比如"8"、"65"'))
     put_markdown('`age = %r`' % age)
 
     # Codemirror
-    put_markdown(r"""PyWebIO 的 `textarea()` 输入函数还支持使用 [Codemirror](https://codemirror.net/) 实现代码风格的编辑区,只需使用 `code` 参数传入Codemirror支持的选项即可(最简单的情况是直接传入` code={}` 或 `code=True`):
+    put_markdown(t("""You can use `code` parameter in `pywebio.input.textarea()` to make a code editing textarea:""", """PyWebIO 的 `textarea()` 输入函数还支持使用 [Codemirror](https://codemirror.net/) 实现代码风格的编辑区,只需使用 `code` 参数传入Codemirror支持的选项即可(最简单的情况是直接传入` code={}` 或 `code=True`):"""), strip_indent=4)
+    put_markdown(r"""
     ```python
     code = textarea('Code Edit', code={
-        'mode': "python",  # 编辑区代码语言
-        'theme': 'darcula',  # 编辑区darcula主题
+        'mode': "python",  # code language
+        'theme': 'darcula',  #  Codemirror theme
     }, value='import something\n# Write your python code')
     ```
-        """, strip_indent=4)
+    """, strip_indent=4)
 
     code = textarea('Code Edit', code={
         'mode': "python",  # 编辑区代码语言
@@ -122,15 +171,19 @@ def main():
     put_markdown("Your code:\n```python\n%s\n```" % code)
 
     # 输入组
-    put_markdown(r"""### 输入组
-    `input_group()` 接受单项输入组成的列表作为参数,输入组中需要在每一项输入函数中提供 `name` 参数来用于在结果中标识不同输入项。输入组中同样支持设置校验函数,其接受整个表单数据作为参数。
-
+    put_markdown(t("""### Input Group
+    `input_group()` accepts a list of single input function call as parameter, and returns a dictionary with the name from the single input function as the key and the input data as the value.
+    The input group also supports using `validate` parameter to set the validation function, which accepts the entire form data as parameter:""",
+    """### 输入组
+    `input_group()` 接受单项输入组成的列表作为参数,输入组中需要在每一项输入函数中提供 `name` 参数来用于在结果中标识不同输入项。输入组中同样支持设置校验函数,其接受整个表单数据作为参数。检验函数校验通过时返回None,否则返回 `(input name,错误消息)`
+    """), strip_indent=4)
+    put_markdown(r"""
     ```python
-    def check_form(data):  # 检验函数校验通过时返回None,否则返回 (input name,错误消息)
+    def check_form(data):  # input group validation: return (input name, error msg) when validation fail
         if len(data['name']) > 6:
-            return ('name', '名字太长!')
+            return ('name', 'Name too long!')
         if data['age'] <= 0:
-            return ('age', '年龄不能为负数!')
+            return ('age', 'Age can not be negative!')
 
     data = input_group("Basic info", [
         input('Input your name', name='name'),
@@ -139,11 +192,11 @@ def main():
     ```
     """, strip_indent=4)
 
-    def check_form(data):  # 检验函数校验通过时返回None,否则返回 (input name,错误消息)
+    def check_form(data):  # input group validation: return (input name, error msg) when validation fail
         if len(data['name']) > 6:
-            return ('name', '名字太长!')
+            return ('name', 'Name too long!')
         if data['age'] <= 0:
-            return ('age', '年龄不能为负数!')
+            return ('age', 'Age can not be negative!')
 
     data = input_group("Basic info", [
         input('Input your name', name='name'),
@@ -152,9 +205,11 @@ def main():
 
     put_markdown("`data = %r`" % data)
 
-    put_markdown("""----
+    put_markdown(t("""----
+    For more information about input of PyWebIO, please visit PyWebIO [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html) and [input module documentation](https://pywebio.readthedocs.io/zh_CN/latest/input.html).
+    """, """----
     PyWebIO的输入演示到这里就结束了,更多内容请访问PyWebIO[用户指南](https://pywebio.readthedocs.io/zh_CN/latest/guide.html)和[input模块文档](https://pywebio.readthedocs.io/zh_CN/latest/input.html)。
-    """, lstrip=True)
+    """), lstrip=True)
 
 
 if __name__ == '__main__':

+ 118 - 47
demos/output_usage.py

@@ -1,16 +1,21 @@
 """
-Demo of output
+Output demo
 ^^^^^^^^^^^^^^
-To demonstrate various output forms supported by PyWebIO
+Demonstrate various output usage supported by PyWebIO
 
 :demo_host:`Demo </?pywebio_api=output_usage>`  `Source code <https://github.com/wang0618/PyWebIO/blob/dev/demos/output_usage.py>`_
 """
 from pywebio import start_server
 from pywebio.output import *
-from pywebio.session import hold, set_env
+from pywebio.session import hold, get_info
 from functools import partial
 
 
+def t(eng, chinese):
+    """return English or Chinese text according to the user's browser language"""
+    return chinese if 'zh' in get_info().user_language else eng
+
+
 def code_block(code, strip_indent=4):
     if strip_indent:
         lines = (
@@ -26,16 +31,25 @@ def code_block(code, strip_indent=4):
 
     with use_scope() as scope:
         put_code(code, 'python')
-        put_buttons([{'label': '运行', 'value': '', 'color': 'success'}],
+        put_buttons([{'label': t('Run', '运行'), 'value': '', 'color': 'success'}],
                     onclick=[partial(run_code, code=code, scope=scope)], small=True)
 
 
 async def main():
-    """PyWebIO输出演示
+    """PyWebIO Output demo
 
-    演示PyWebIO支持的各种输出形式
+    Demonstrate various output usage supported by PyWebIO.
+    演示PyWebIO输出模块的使用
     """
-    put_markdown("""# PyWebIO 输出演示
+    put_markdown(t("""# PyWebIO Output demo
+    
+    You can get the source code of this demo in [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/output_usage.py)
+    
+    This demo only introduces part of the functions of the PyWebIO output module. For the complete features, please refer to the [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html).
+    
+    The output functions are all defined in the `pywebio.output` module and can be imported using `from pywebio.output import *`.
+    
+    """, """# PyWebIO 输出演示
     
     在[这里](https://github.com/wang0618/PyWebIO/blob/dev/demos/output_usage.py)可以获取本Demo的源码。
     
@@ -45,9 +59,25 @@ async def main():
 
     ### 基本输出
     PyWebIO提供了一些便捷函数来输出表格、链接等格式:
-    """, strip_indent=4)
+    """), strip_indent=4)
 
-    code_block(r"""
+    code_block(t(r"""
+    # Text Output
+    put_text("Hello world!")
+
+    # Table Output
+    put_table([
+        ['Commodity', 'Price'],
+        ['Apple', '5.5'],
+        ['Banana', '7'],
+    ])
+    
+    # Markdown Output
+    put_markdown('~~Strikethrough~~')
+    
+    # File Output
+    put_file('hello_word.txt', b'hello word!')
+    """, r"""
     # 文本输出
     put_text("Hello world!")
 
@@ -63,22 +93,28 @@ async def main():
 
     # 文件输出
     put_file('hello_word.txt', b'hello word!')
-    """)
+    """))
 
-    put_markdown(r"""PyWebIO提供的全部输出函数请参考PyWebIO文档
+    put_markdown(t(r"""For all output functions provided by PyWebIO, please refer to the document.
+    
+    ### Combined Output
+    The output functions whose name starts with put_ can be combined with some output functions as part of the final output:
+
+    You can pass `put_xxx()` calls to `put_table()` as cell content:
+    """, r"""PyWebIO提供的全部输出函数请参考PyWebIO文档
     
     ### 组合输出
     
     函数名以 `put_` 开始的输出函数,可以与一些输出函数组合使用,作为最终输出的一部分。
 
     比如`put_table()`支持以`put_xxx()`调用作为单元格内容:
-    """, strip_indent=4)
+    """), strip_indent=4)
 
     code_block(r"""
     put_table([
         ['Type', 'Content'],
         ['html', put_html('X<sup>2</sup>')],
-        ['text', '<hr/>'],  # 等价于 ['text', put_text('<hr/>')]
+        ['text', '<hr/>'],  # equal to ['text', put_text('<hr/>')]
         ['buttons', put_buttons(['A', 'B'], onclick=toast)],  
         ['markdown', put_markdown('`Awesome PyWebIO!`')],
         ['file', put_file('hello.text', b'hello world')],
@@ -86,25 +122,32 @@ async def main():
     ])
     """)
 
-    put_markdown(r"""类似地,`popup()`也可以将`put_xxx()`调用作为弹窗内容:
-    
-    """, strip_indent=4)
+    put_markdown(t(r"Similarly, you can pass `put_xxx()` calls to `popup()` as the popup content:",
+                   r"类似地,`popup()`也可以将`put_xxx()`调用作为弹窗内容:"), strip_indent=4)
 
     code_block(r"""
     popup('Popup title', [
         put_html('<h3>Popup Content</h3>'),
-        'plain html: <br/>',  # 等价于 put_text('plain html: <br/>')
+        'plain html: <br/>',  # equal to put_text('plain html: <br/>')
         put_table([['A', 'B'], ['C', 'D']]),
         put_buttons(['close_popup()'], onclick=lambda _: close_popup())
     ])
     """)
 
-    put_markdown(r"更多接受`put_xxx()`作为参数的输出函数请参考函数文档。")
+    put_markdown(t(r"For more output functions that accept `put_xxx()` calls as parameters, please refer to corresponding function documentation.",
+                   r"更多接受`put_xxx()`作为参数的输出函数请参考函数文档。"))
 
-    put_markdown(r"""### 事件回调
+    put_markdown(t(r"""### Callback
+    PyWebIO allows you to output some buttons, and the provided callback function will be executed when the button is clicked.
+    
+    This is an example:%s
+    The call to `put_table()` will not block. When user clicks a button, the corresponding callback function will be invoked:
+    """, r"""### 事件回调
     PyWebIO允许你输出一些控件,当控件被点击时执行提供的回调函数,就像编写GUI程序一样。
     
-    下面是一个例子:
+    下面是一个例子:%s
+    `put_table()`的调用不会阻塞。当用户点击了某行中的按钮时,PyWebIO会自动调用相应的回调函数:
+    """) % """
     ```python
     from functools import partial
 
@@ -118,8 +161,6 @@ async def main():
         [3, put_buttons(['edit', 'delete'], onclick=partial(edit_row, row=3))],
     ])
     ```
-    `put_table()`的调用不会阻塞。当用户点击了某行中的按钮时,PyWebIO会自动调用相应的回调函数:
-    
     """, strip_indent=4)
 
     from functools import partial
@@ -136,7 +177,7 @@ async def main():
     ])
     set_scope('table-callback')
 
-    put_markdown(r"""当然,PyWebIO还支持单独的按钮控件:
+    put_markdown(t("Of course, PyWebIO also supports outputting individual button:", "当然,PyWebIO还支持单独的按钮控件:")+r"""
     ```python
     def btn_click(btn_val):
         put_markdown("> You click `%s` button" % btn_val)
@@ -152,7 +193,17 @@ async def main():
     put_buttons(['A', 'B', 'C'], onclick=btn_click)
     set_scope('button-callback')
 
-    put_markdown(r"""### 输出域Scope
+    put_markdown(t(r"""### Output Scope
+    
+    PyWebIO uses the scope model to give more control to the location of content output. The output area of PyWebIO can be divided into different output domains. The output domain is called Scope in PyWebIO.
+
+    The output domain is a container of output content, and each output domain is arranged vertically, and the output domains can also be nested.
+
+    Each output function (function name like `put_xxx()`) will output its content to a scope, the default is "current scope". "current scope" is determined by the runtime context. The output function can also manually specify the scope to output. The scope name is unique within the session.
+    
+    You can use `use_scope()` to open and enter a new output scope, or enter an existing output scope: %s
+    The above code will generate the following Scope layout:
+    """, r"""### 输出域Scope
 
     PyWebIO使用Scope模型来对内容输出的位置进行灵活地控制,PyWebIO的内容输出区可以划分出不同的输出域,PyWebIO将输出域称作`Scope`。
     
@@ -160,8 +211,9 @@ async def main():
     
     每个输出函数(函数名形如 `put_xxx()` )都会将内容输出到一个Scope,默认为”当前Scope”,”当前Scope”由运行时上下文确定,输出函数也可以手动指定输出到的Scope。Scope名在会话内唯一。
     
-    可以使用 `use_scope()` 开启并进入一个新的输出域,或进入一个已经存在的输出域:
-
+    可以使用 `use_scope()` 开启并进入一个新的输出域,或进入一个已经存在的输出域: %s
+    以上代码将会产生如下Scope布局:
+    """) % """
     ```python
     with use_scope('A'):
         put_text('Text in scope A')
@@ -172,7 +224,6 @@ async def main():
     with use_scope('C'):
         put_text('Text in scope C')
     ```
-    以上代码将会产生如下Scope布局:
     """, strip_indent=4)
     with use_scope('A'):
         put_text('Text in scope A')
@@ -189,19 +240,22 @@ async def main():
     #pywebio-scope-C {border: 1px solid green;margin-top:2px}    
     </style><br/>""")
 
-    put_markdown(r"""
+    put_markdown(t(r"""The output function (function name like `put_xxx()`) will output the content to the "current scope" by default, and the "current scope" of the runtime context can be set by `use_scope()`.
+    
+    In addition, you can use the `scope` parameter of the output function to specify the destination scope to output:
+    """, r"""
     输出函数(函数名形如 `put_xxx()` )在默认情况下,会将内容输出到”当前Scope”,可以通过 `use_scope()` 设置运行时上下文的”当前Scope”。
     
     此外,也可以通过输出函数的 scope 参数指定输出的目的Scope:
-    """, strip_indent=4)
+    """), strip_indent=4)
 
     put_grid([
-        [put_code("put_text('A', scope='A')", 'python'), None, put_buttons(['运行'], [lambda: put_text('A', scope='A')])],
-        [put_code("put_text('B', scope='B')", 'python'), None, put_buttons(['运行'], [lambda: put_text('B', scope='B')])],
-        [put_code("put_text('C', scope='C')", 'python'), None, put_buttons(['运行'], [lambda: put_text('C', scope='C')])],
+        [put_code("put_text('A', scope='A')", 'python'), None, put_buttons([t('Run', '运行')], [lambda: put_text('A', scope='A')])],
+        [put_code("put_text('B', scope='B')", 'python'), None, put_buttons([t('Run', '运行')], [lambda: put_text('B', scope='B')])],
+        [put_code("put_text('C', scope='C')", 'python'), None, put_buttons([t('Run', '运行')], [lambda: put_text('C', scope='C')])],
     ], cell_widths='1fr 10px auto')
 
-    put_markdown(r"""输出函数可以使用position参数指定内容在Scope中输出的位置
+    put_markdown(t("The output content can be inserted into any positions of the target scope by using the `position` parameter of the output function.", "输出函数可以使用`position`参数指定内容在Scope中输出的位置") + """
     ```python
     put_text(now(), scope='A', position=...)
     ```
@@ -211,15 +265,27 @@ async def main():
     put_buttons([('position=%s' % i, i) for i in [1, 2, 3, -1, -2, -3]],
                 lambda i: put_text(datetime.datetime.now(), position=i, scope='A'), small=True)
 
-    put_markdown(r"除了 `use_scope()` , PyWebIO同样提供了以下scope控制函数: ")
+    put_markdown(t(r"In addition to `use_scope()`, PyWebIO also provides the following scope control functions:",
+                   r"除了 `use_scope()` , PyWebIO同样提供了以下scope控制函数: "))
 
     put_grid([
-        [put_code("clear('B')  # 清除Scope B中的内容", 'python'), None, put_buttons(['运行'], [lambda: clear('B')])],
-        [put_code("remove('C')  # 移除Scope C", 'python'), None, put_buttons(['运行'], [lambda: remove('C')])],
-        [put_code("scroll_to('A')  # 将页面滚动到Scope A处", 'python'), None, put_buttons(['运行'], [lambda: scroll_to('A')])],
+        [put_code("clear('B')  # Clear content of Scope B", 'python'), None, put_buttons(['运行'], [lambda: clear('B')])],
+        [put_code("remove('C')  # Remove Scope C", 'python'), None, put_buttons(['运行'], [lambda: remove('C')])],
+        [put_code("scroll_to('A')  # Scroll the page to position of Scope A", 'python'), None, put_buttons(['运行'], [lambda: scroll_to('A')])],
     ], cell_widths='1fr 10px auto')
 
-    put_markdown(r"""### 布局
+    put_markdown(t(r"""### Layout
+    
+    In general, using the various output functions introduced above is enough to output what you want, but these outputs are arranged vertically. If you want to make a more complex layout (such as displaying a code block on the left side of the page and an image on the right), you need to use layout functions.
+    
+    The `pywebio.output` module provides 3 layout functions, and you can create complex layouts by combining them:
+    
+     - `put_row()` : Use row layout to output content. The content is arranged horizontally
+     - `put_column()` : Use column layout to output content. The content is arranged vertically
+     - `put_grid()` : Output content using grid layout
+    
+    Here is an example by combining `put_row()` and `put_column()`:
+    """, r"""### 布局
     一般情况下,使用上文介绍的各种输出函数足以完成各种内容的展示,但直接调用输出函数产生的输出之间都是竖直排列的,如果想实现更复杂的布局(比如在页 面左侧显示一个代码块,在右侧显示一个图像),就需要借助布局函数。
 
     `pywebio.output` 模块提供了3个布局函数,通过对他们进行组合可以完成各种复杂的布局:
@@ -229,14 +295,14 @@ async def main():
      - `put_grid()` : 使用网格布局输出内容
 
     比如,通过通过组合 `put_row()` 和 `put_column()` 实现的布局:
-    """, strip_indent=4)
+    """), strip_indent=4)
 
     code_block(r"""
     put_row([
         put_column([
             put_code('A'),
             put_row([
-                put_code('B1'), None,  # None 表示输出之间的空白
+                put_code('B1'), None,  # %s
                 put_code('B2'), None,
                 put_code('B3'),
             ]),
@@ -245,15 +311,18 @@ async def main():
         put_code('D'), None,
         put_code('E')
     ])
-    """)
+    """ % t('None represents the space between the output', 'None 表示输出之间的空白'))
 
-    put_markdown(r"""
-    ### 样式
+    put_markdown(t(r"""### Style
+    If you are familiar with CSS styles, you can use the `style()` function to set a custom style for the output.
+
+    You can set the CSS style for a single `put_xxx()` output:
+    """, r"""### 样式
     
     如果你熟悉 CSS样式 ,你还可以使用 `style()` 函数给输出设定自定义样式。
 
     可以给单个的 `put_xxx()` 输出设定CSS样式,也可以配合组合输出使用:
-    """, strip_indent=4)
+    """), strip_indent=4)
 
     code_block(r"""
     style(put_text('Red'), 'color: red')
@@ -264,7 +333,7 @@ async def main():
     ])
     """, strip_indent=4)
 
-    put_markdown(r"`style()` 也接受列表作为输入:")
+    put_markdown(t(r"`style()` also accepts a list of output calls:", r"`style()` 也接受列表作为输入:"))
 
     code_block(r"""
     style([
@@ -279,9 +348,11 @@ async def main():
 
     """, strip_indent=4)
 
-    put_markdown("""----
+    put_markdown(t("""----
+    For more information about output of PyWebIO, please visit PyWebIO [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html) and [output module documentation](https://pywebio.readthedocs.io/zh_CN/latest/output.html).
+    ""","""----
     PyWebIO的输出演示到这里就结束了,更多内容请访问PyWebIO[用户指南](https://pywebio.readthedocs.io/zh_CN/latest/guide.html)和[output模块文档](https://pywebio.readthedocs.io/zh_CN/latest/output.html)。
-    """, lstrip=True)
+    """), lstrip=True)
 
     await hold()
 

+ 15 - 8
demos/set_env_demo.py

@@ -10,24 +10,31 @@ import datetime
 import asyncio
 
 
+def t(eng, chinese):
+    """return English or Chinese text according to the user's browser language"""
+    return chinese if 'zh' in get_info().user_language else eng
+
+
 async def main():
     """`pywebio.session.set_env()` demo"""
 
     set_scope('time')
-    put_markdown('> 可用于观察 `output_animation` 项的动画效果')
+    put_markdown(t('> Can be used to observe the animation effect of the `output_animation` setting',
+                   '> 可用于观察 `output_animation` 项的动画效果'))
     put_markdown('---')
 
     async def bg_task():
         while 1:
             with use_scope('time', clear=True):
-                put_text('当前时间:', datetime.datetime.now())
+                put_text('Current time:', datetime.datetime.now())
 
             await asyncio.sleep(1)
 
     run_async(bg_task())
 
-    put_buttons(['输出文本'], [lambda: put_text(datetime.datetime.now())])
-    put_markdown('> 可用于观察 `auto_scroll_bottom` 项的自动滚动效果')
+    put_buttons([t('Output some texts', '输出文本')], [lambda: put_text(datetime.datetime.now())])
+    put_markdown(t('> Can be used to observe the automatic scrolling effect of the `auto_scroll_bottom` setting',
+                   '> 可用于观察 `auto_scroll_bottom` 项的自动滚动效果'))
     put_markdown('---')
     put_text('Some text.\n' * 10)
 
@@ -40,15 +47,15 @@ async def main():
 
     while 1:
         curr_state_info = ', '.join('%s=%r' % (k, v) for k, v in state.items())
-        key = await actions('选择要更改的会话环境设置项', list(state.keys()), help_text='当前状态:' + curr_state_info)
+        key = await actions(t('Select the setting item to be changed', '选择要更改的会话环境设置项'), list(state.keys()), help_text='Current state: ' + curr_state_info)
         if key == 'title':
-            state['title'] = await input('请输入标题', value=state['title'])
+            state['title'] = await input('Title', value=state['title'])
             set_env(title=state['title'])
-            toast('已将标题设置为%r' % state['title'])
+            toast('Title is set to %r' % state['title'])
         elif key in state:
             state[key] = not (state[key])
             set_env(**{key: state[key]})
-            toast('已将`%s`设置为%r' % (key, state[key]))
+            toast('`%s` is set to %r' % (key, state[key]))
 
 
 if __name__ == '__main__':