Browse Source

Merge pull request #51 from open-falcon/i18n

i18n support
laiwei 7 years ago
parent
commit
fbecab2986

+ 6 - 0
README.md

@@ -14,6 +14,12 @@ Open-Falcon homepage:[http://open-falcon.org](http://open-falcon.org)
 ## Demo site
 - TODO
 
+## i18n
+Open-Falcon natively support zh_CN and currently partially support English. Any translation will be appreciated.
+
+[How to participate in the translation](./i18n.md)
+
+
 ## Run in docker
 ```
 # make the image,run commands under dir of dashboard:

+ 3 - 0
babel.cfg

@@ -0,0 +1,3 @@
+[python: **.py]
+[jinja2: **/templates/**.html]
+extensions=jinja2.ext.autoescape,jinja2.ext.with_

+ 24 - 0
i18n.md

@@ -0,0 +1,24 @@
+Open-Falcon support zh_CN by default. Any translation will be appreciated.
+
+If you want to translate open-falcon to a new language or just refine the exist translation, you could follow the instructions below.
+
+## refine the exist translation
+
+1. find what you want to translate in the source code and replace it to `{{_('xxx')}}`.
+2. run `python ./scripts/tr_update.py`, it will update `rrd/translations/foo/LC_MESSAGES/messages.po`.
+3. run `python ./scripts/tr_compile.py`, it will generate a complied file in `rrd/translations/foo/LC_MESSAGES/messages.mo`
+
+
+## create a new language(such as Spanish support)
+1. update `rrd/config.py`, add a new key to `LANGUAGES`, such as:
+    
+        LANGUAGES   = {
+            'en':  'English',
+            'zh_CN':  'Chinese-Simplified',
+            'es': Spanish,
+        }
+    
+2. run `python ./scripts/tr_init.py`, it will update `rrd/translations/es/LC_MESSAGES/messages.po`.
+3. find what you want to translate in the source code and replace it to `{{_('xxx')}}`.
+4. run `python ./scripts/tr_update.py`, it will update `rrd/translations/es/LC_MESSAGES/messages.po`.
+5. run `python ./scripts/tr_compile.py`, it will generate a complied file in `rrd/translations/es/LC_MESSAGES/messages.mo`

+ 1 - 0
pip_requirements.txt

@@ -1,4 +1,5 @@
 Flask==0.10.1
+Flask-Babel==0.9
 Jinja2==2.7.2
 Werkzeug==0.9.4
 gunicorn==18.0

+ 15 - 3
rrd/__init__.py

@@ -1,17 +1,29 @@
 #-*- coding:utf-8 -*-
 import os
 import traceback
-from flask import Flask
+from flask import Flask, request
+from flask.ext.babel import Babel, gettext
+from rrd import config
 
 #-- create app --
 app = Flask(__name__)
 app.config.from_object("rrd.config")
+babel = Babel(app)
 
 @app.errorhandler(Exception)
 def all_exception_handler(error):
     tb = traceback.format_exc()
-    err_msg = '<pre>暂时无法访问,请联系管理员.\n\nerror: %s\n\ntraceback日志如下:\n%s</pre>' %(error, tb)
-    return err_msg, 500
+    err_tip = gettext('Temporary error, please contact your administrator.')
+    err_msg = err_tip + '\n\nError: %s\n\nTraceback:\n%s' %(error, tb)
+    return '<pre>' + err_msg + '</pre>', 500
+
+@babel.localeselector
+def get_locale():
+    return request.accept_languages.best_match(config.LANGUAGES.keys())
+
+@babel.timezoneselector
+def get_timezone():
+    return app.config.get("BABEL_DEFAULT_TIMEZONE")
 
 from view import index
 from view.auth import auth

+ 9 - 0
rrd/config.py

@@ -40,6 +40,15 @@ LDAP_TLS_KEYFILE = ""
 LDAP_TLS_REQUIRE_CERT = True
 LDAP_TLS_CIPHER_SUITE = ""
 
+# i18n
+BABEL_DEFAULT_LOCALE   = 'zh_CN'
+BABEL_DEFAULT_TIMEZONE = 'Asia/Shanghai'
+# aviliable translations
+LANGUAGES   = {
+    'en':  'English',
+    'zh_CN':  'Chinese-Simplified',
+}
+
 # portal site config
 MAINTAINERS = ['root']
 CONTACT = 'root@open-falcon.org'

+ 1 - 1
rrd/templates/auth/register.html

@@ -18,7 +18,7 @@
 									type="text" id="name" class="form-control" required autofocus />
 							</div>
 							<div class="form-group">
-								<label for="cnname">中文名:</label> <input
+                                <label for="cnname">{{ _('UserName') }}:</label> <input
 									type="text" id="cnname" class="form-control" required autofocus />
 							</div>
 							<div class="form-group">

+ 3 - 3
rrd/templates/base.html

@@ -111,7 +111,7 @@
             <div class="modal-content">
                 <div class="modal-header">
                     <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
-                    <h4 class="modal-title">批量编辑 Graph</h4>
+                    <h4 class="modal-title">{{ _('batch edit') }} Graph</h4>
                 </div>
                 <div class="modal-body">
                     <div class="edit-area">
@@ -119,8 +119,8 @@
                     </div>
                 </div>
                 <div class="modal-footer">
-                    <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
-                    <button type="button" class="btn btn-primary editModalYes">确定</button>
+                    <button type="button" class="btn btn-default" data-dismiss="modal"> {{ _('Close') }}</button>
+                    <button type="button" class="btn btn-primary editModalYes">{{ _('OK') }}</button>
                 </div>
             </div>
         </div>

+ 14 - 14
rrd/templates/chart/big_ng.html

@@ -53,7 +53,7 @@
                         <span class="caret"></span></a>
                     <ul class="dropdown-menu" style="min-width:400px; margin:0 0 0 0px;">
                         <table class="table table-striped">
-                            <tbody><tr><td width="100px;"><span>时间跨度</span>
+                            <tbody><tr><td width="100px;"><span>{{_('date range')}}</span>
                                     </td><td>
                                         <a ng-class="{enable: vm.param.start == -3600}" ng-click="vm.param.start=-3600" class="chart-opt">1h</a>
                                         <a ng-class="{enable: vm.param.start == -21600}" ng-click="vm.param.start=-21600" class="chart-opt">6h</a>
@@ -64,19 +64,19 @@
                                         <a ng-class="{enable: vm.param.start == -2592000}" ng-click="vm.param.start=-2592000" class="chart-opt">1m</a>
                                 </td></tr>
 
-                                <tr><td> <span>采样方法</span>
+                                <tr><td> <span>{{_('sampling method')}}</span>
                                     </td><td>
-                                        <a ng-class="{enable: vm.param.cf=='MAX'}" ng-click="vm.param.cf='MAX'" class="chart-opt">最大值</a>
-                                        <a ng-class="{enable: vm.param.cf=='MIN'}" ng-click="vm.param.cf='MIN'" class="chart-opt">最小值</a>
-                                        <a ng-class="{enable: vm.param.cf=='AVERAGE'}" ng-click="vm.param.cf='AVERAGE'" class="chart-opt">平均值</a>
+                                        <a ng-class="{enable: vm.param.cf=='MAX'}" ng-click="vm.param.cf='MAX'" class="chart-opt">{{_('max')}}</a>
+                                        <a ng-class="{enable: vm.param.cf=='MIN'}" ng-click="vm.param.cf='MIN'" class="chart-opt">{{_('min')}}</a>
+                                        <a ng-class="{enable: vm.param.cf=='AVERAGE'}" ng-click="vm.param.cf='AVERAGE'" class="chart-opt">{{_('average')}}</a>
                                 </td></tr>
 
-                                <tr><td><span>其他</span>
+                                <tr><td><span>{{_('other')}}</span>
                                     </td><td>
-                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='off'" data-k="sum|sumonly" data-v="1| " class="chart-opt">求和</a>
-                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='on'" data-k="sumonly" data-v="1" class="chart-opt">只显示sum</a>
-                                        <a ng-click="vm.param.sum='off';vm.param.sumonly='off'" data-k="sum|sumonly" data-v=" | " class="chart-opt">取消求和</a>
-                                        <a ng-click="vm.reset()" data-k="start|end|sum|cf|tongbi|sumonly" data-v=" | | | | | | " class="chart-opt"><b>重置</b></a>
+                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='off'" data-k="sum|sumonly" data-v="1| " class="chart-opt">{{_('sum')}}</a>
+                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='on'" data-k="sumonly" data-v="1" class="chart-opt">{{_('sum only')}}m</a>
+                                        <a ng-click="vm.param.sum='off';vm.param.sumonly='off'" data-k="sum|sumonly" data-v=" | " class="chart-opt">{{_('cancel sum')}}</a>
+                                        <a ng-click="vm.reset()" data-k="start|end|sum|cf|tongbi|sumonly" data-v=" | | | | | | " class="chart-opt"><b>{{_('reset')}}</b></a>
                                 </td></tr>
                         </tbody></table>
                     </ul>
@@ -103,20 +103,20 @@
                                     <div class="form-group">
                                         <div class="checkbox">
                                             <label>
-                                                <input type="checkbox" name="all" id="" ng-model="vm.all" ng-change="vm.checkAll()"/> 全选
+                                                <input type="checkbox" name="all" id="" ng-model="vm.all" ng-change="vm.checkAll()"/>{{_('select all')}} 
                                             </label>
                                         </div>
                                         <div class="checkbox">
                                             <label>
-                                                <input type="checkbox" name="reverse" id="" ng-model="vm.reverse" ng-change="vm.checkReverse()" />反选
+                                                <input type="checkbox" name="reverse" id="" ng-model="vm.reverse" ng-change="vm.checkReverse()" />{{_('reverse select')}}反选
                                             </label>
                                         </div>
-                                        <a class="form-control input-sm btn btn-default check" ng-click="vm.checkSearch()"><small>刷新</small></a>
+                                        <a class="form-control input-sm btn btn-default check" ng-click="vm.checkSearch()"><small>{{_('refresh')}}</small></a>
                                     </div>
                                     <div class="form-group search-area">
                                         <input class="form-control input-sm" ng-model="vm.query" id="searchinput" style="width:250px;"
                                             ng-enter="vm.checkFilter(); vm.checkSearch();"
-                                            type="search" placeholder="输入字符过滤, 回车刷新..">
+                                            type="search" placeholder="{{_('type to fiter, enter to refresh...')}}">
                                     </div>
                                 </div>
                                 <ul multi-check-group class="list-unstyled" style="font-family: 'verdana', 'Microsoft YaHei', 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono';">

+ 10 - 10
rrd/templates/chart/embed.html

@@ -52,7 +52,7 @@
                         <span class="caret"></span></a>
                     <ul class="dropdown-menu" style="min-width:400px; margin:0 0 0 0px;">
                         <table class="table table-striped">
-                            <tbody><tr><td width="100px;"><span>时间跨度</span>
+                            <tbody><tr><td width="100px;"><span>{{_('date range')}}</span>
                                     </td><td>
                                         <a ng-class="{enable: vm.param.start == -3600}" ng-click="vm.param.start=-3600" class="chart-opt">1h</a>
                                         <a ng-class="{enable: vm.param.start == -21600}" ng-click="vm.param.start=-21600" class="chart-opt">6h</a>
@@ -63,19 +63,19 @@
                                         <a ng-class="{enable: vm.param.start == -2592000}" ng-click="vm.param.start=-2592000" class="chart-opt">1m</a>
                                 </td></tr>
 
-                                <tr><td> <span>采样方法</span>
+                                <tr><td> <span>{{_('sampling method')}}</span>
                                     </td><td>
-                                        <a ng-class="{enable: vm.param.cf=='MAX'}" ng-click="vm.param.cf='MAX'" class="chart-opt">最大值</a>
-                                        <a ng-class="{enable: vm.param.cf=='MIN'}" ng-click="vm.param.cf='MIN'" class="chart-opt">最小值</a>
-                                        <a ng-class="{enable: vm.param.cf=='AVERAGE'}" ng-click="vm.param.cf='AVERAGE'" class="chart-opt">平均值</a>
+                                        <a ng-class="{enable: vm.param.cf=='MAX'}" ng-click="vm.param.cf='MAX'" class="chart-opt">{{_('max')}}</a>
+                                        <a ng-class="{enable: vm.param.cf=='MIN'}" ng-click="vm.param.cf='MIN'" class="chart-opt">{{_('min')}}</a>
+                                        <a ng-class="{enable: vm.param.cf=='AVERAGE'}" ng-click="vm.param.cf='AVERAGE'" class="chart-opt">{{_('average')}}</a>
                                 </td></tr>
 
-                                <tr><td><span>其他</span>
+                                <tr><td><span>{{_('other')}}</span>
                                     </td><td>
-                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='off'" data-k="sum|sumonly" data-v="1| " class="chart-opt">求和</a>
-                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='on'" data-k="sumonly" data-v="1" class="chart-opt">只显示sum</a>
-                                        <a ng-click="vm.param.sum='off';vm.param.sumonly='off'" data-k="sum|sumonly" data-v=" | " class="chart-opt">取消求和</a>
-                                        <a ng-click="vm.reset()" data-k="start|end|sum|cf|tongbi|sumonly" data-v=" | | | | | | " class="chart-opt"><b>重置</b></a>
+                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='off'" data-k="sum|sumonly" data-v="1| " class="chart-opt">{{_('sum')}}</a>
+                                        <a ng-click="vm.param.sum='on';vm.param.sumonly='on'" data-k="sumonly" data-v="1" class="chart-opt">{{_('sum only')}}</a>
+                                        <a ng-click="vm.param.sum='off';vm.param.sumonly='off'" data-k="sum|sumonly" data-v=" | " class="chart-opt">{{_('cancel sum')}}</a>
+                                        <a ng-click="vm.reset()" data-k="start|end|sum|cf|tongbi|sumonly" data-v=" | | | | | | " class="chart-opt"><b>{{_('reset')}}</b></a>
                                 </td></tr>
                         </tbody></table>
                     </ul>

+ 10 - 10
rrd/templates/chart/multi_ng.html

@@ -43,7 +43,7 @@
                     <div class="col-md-12">
                         <form class="form-inline" role="form">
                             <div class="form-group">
-                                <label class="control-label">起始</label>
+                                <label class="control-label">{{_('begin')}}</label>
                                 <a class="dropdown-toggle form-control input-sm date-area" id="dropdown1" role="button" data-toggle="dropdown" href="javascript:;">
                                     <div class="input-group">
                                         <span class="form-control-static">[[ vm.globalParam.start | date: 'yyyy-MM-dd HH:mm' ]]</span>
@@ -56,7 +56,7 @@
                             </div>
 
                             <div class="form-group">
-                                <label class="control-label">结束</label>
+                                <label class="control-label">{{_('end')}}</label>
                                 <a class="dropdown-toggle form-control input-sm date-area" id="dropdown2" role="button" data-toggle="dropdown" href="javascript:;">
                                     <div class="input-group">
                                         <span class="form-control-static">[[ vm.globalParam.end | date: 'yyyy-MM-dd HH:mm' ]]</span>
@@ -69,7 +69,7 @@
                             </div>
 
                             <div class="form-group">
-                                <label class="control-label">采样方法</label>
+                                <label class="control-label">{{_('sampling method')}}</label>
                                 <select class="form-control input-sm" name="cf" ng-model="vm.globalParam.cf">
                                     <option value="AVERAGE" selected="">AVERAGE</option>
                                     <option value="MAX">MAX</option>
@@ -77,26 +77,26 @@
                                 </select>
                             </div>
                             <div class="form-group">
-                                <label class="control-label">视角</label>
+                                <label class="control-label">{{_('view')}}</label>
                                 <select class="form-control input-sm" name="graph_type"  ng-model="vm.globalParam.graph_type">
-                                    <option value="h" selected="">Endpoint视角</option>
-                                    <option value="k">Counter视角</option>
-                                    <option value="a">组合视角</option>
+                                    <option value="h" selected="">{{_('endpoint view')}}</option>
+                                    <option value="k">{{_('counter view')}}</option>
+                                    <option value="a">{{_('multi view')}}</option>
                                 </select>
                             </div>
 
                             <div class="form-group">
                                 <div class="checkbox">
                                     <label>
-                                        <input type="checkbox" id="sum" name="sum" value="on"  ng-model="vm.globalParam.sum" ng-true-value="on" ng-false-value="off"> 求和
+                                        <input type="checkbox" id="sum" name="sum" value="on"  ng-model="vm.globalParam.sum" ng-true-value="on" ng-false-value="off">{{_('sum')}}
                                     </label>
                                 </div>
                             </div>
                             <div class="form-group">
-                                <button id="go" class="btn btn-default btn-sm" ng-click="vm.show()">看图</button>
+                                <button id="go" class="btn btn-default btn-sm" ng-click="vm.show()">{{_('show')}}</button>
                             </div>
                             <div class="form-group">
-                                <button id="reset" class="btn btn-default btn-sm" ng-click="vm.reset()">重置条件</button>
+                                <button id="reset" class="btn btn-default btn-sm" ng-click="vm.reset()">{{_('reset')}}</button>
                             </div>
 
                         </form>

+ 9 - 9
rrd/templates/chart/multi_pic.html

@@ -8,17 +8,17 @@
             <form class="form-inline" role="form">
 
                 <div class="form-group">
-                    <label class="control-label">起始</label>
+                    <label class="control-label">{{_('begin')}}</label>
                     <input type='text' class="form-control" name="start_s" data-date-format="YYYY-MM-DD HH:mm" />
                 </div>
 
                 <div class="form-group">
-                    <label class="control-label">结束</label>
+                    <label class="control-label">{{_('end')}}</label>
                     <input class="form-control" name="end_s" data-date-format="YYYY-MM-DD HH:mm" type="text"></input>
                 </div>
 
                 <div class="form-group">
-                    <label class="control-label">采样方法</label>
+                    <label class="control-label">{{_('sampling method')}}</label>
                     <select class="form-control" name="cf">
                         {%for x in ["AVERAGE", "MAX", "MIN",]%}
                         <option value="{{x}}" {%if g.cf==x%}selected{%endif%}>{{x}}</option>
@@ -26,25 +26,25 @@
                     </select>
                 </div>
                 <div class="form-group">
-                    <label class="control-label">视角</label>
+                    <label class="control-label">{{_('view')}}</label>
                     <select class="form-control" name="graph_type">
-                        <option value="h" {%if g.graph_type=="h" or g.graph_type==""%}selected{%endif%}>Endpoint视角</option>
-                        <option value="k" {%if g.graph_type=="k"%}selected{%endif%}>Counter视角</option>
+                        <option value="h" {%if g.graph_type=="h" or g.graph_type==""%}selected{%endif%}>{{_('endpoint view')}}</option>
+                        <option value="k" {%if g.graph_type=="k"%}selected{%endif%}>{{_('counter view')}}</option>
                     </select>
                 </div>
 
                 <div class="form-group">
                     <div class="checkbox">
                         <label>
-                            <input type="checkbox" id="sum" name="sum" value="on" {%if g.sum == 'on'%}checked{%endif%}> 求和
+                            <input type="checkbox" id="sum" name="sum" value="on" {%if g.sum == 'on'%}checked{%endif%}>{{_('sum')}}
                         </label>
                     </div>
                 </div>
                 <div class="form-group">
-                    <button id="go" class="btn btn-default" class="btn btn-default">看图</button>
+                    <button id="go" class="btn btn-default" class="btn btn-default">{{_('show')}}</button>
                 </div>
                 <div class="form-group">
-                    <button id="reset" class="btn btn-default" class="btn btn-default">重置条件</button>
+                    <button id="reset" class="btn btn-default" class="btn btn-default">{{_('reset')}}</button>
                 </div>
 
             </form>

+ 19 - 19
rrd/templates/index.html

@@ -65,7 +65,7 @@
                     <div class="panel panel-info">
                         <div class="panel-heading">
                             <img class="loading pull-right" src="/static/img/loading.gif" style="display:none;"></img>
-                            搜索Endpoints
+                            {{_('search endpoints')}}
                         </div>
 
                         <div class="panel-body">
@@ -73,16 +73,16 @@
                                 <div class="form-group">
                                     <label>Endpoint</label>
                                     <input id="endpoint-search" name="endpoint_search" type="text" class="form-control input-sm">
-                                    <p class="help-block"><span class="text-warning">可以用空格分割多个搜索关键字</span></p>
+                                    <p class="help-block"><span class="text-warning">{{_('using SPACE to separate keywords')}}</span></p>
                                 </div>
                                 <div class="form-group">
-                                    <label>标签( eg: job=appstore-web)</label>
+                                    <label>{{_('labels')}}( eg: job=appstore-web)</label>
                                     <input id="tag-search" name="tag_search" type="text" class="form-control input-sm">
                                 </div>
                             </form>
                             <form class="form-inline" role="form">
                                 <div class="form-group">
-                                    <button type="button" id="btn-search-endpoints" class="btn btn-default btn-sm btn-success" onclick="fn_list_endpoints();return false;">全局搜索</button>
+                                    <button type="button" id="btn-search-endpoints" class="btn btn-default btn-sm btn-success" onclick="fn_list_endpoints();return false;">{{_('global search')}}</button>
                                     <select class="form-control input-sm" id="endpoint-limit" onchange="fn_list_endpoints();return false;">
                                         <option value="50"> Limit 50</option>
                                         <option value="100">Limit 100</option>
@@ -94,14 +94,14 @@
                                         {%endfor%}
                                     </select>
                                     <span class="cut-line">¦</span>
-                                    <button class="btn btn-default btn-sm" onclick="fn_delete_endpoints();return false;">删除endpoints</button>
+                                    <button class="btn btn-default btn-sm" onclick="fn_delete_endpoints();return false;">{{_('delete endpoints')}}</button>
                                 </div>
                             </form>
                             <hr/>
                             <form class="form-inline" role="form">
                                 <div class="form-group">
                                     <input id="endpoint-filter" type="text" class="form-control input-sm">
-                                    <button class="btn btn-default btn-sm btn-info" onclick="filter_endpoint();return false;">快速过滤</button>
+                                    <button class="btn btn-default btn-sm btn-info" onclick="filter_endpoint();return false;">{{_('quick filter')}}</button>
                                 </div>
                             </form>
 
@@ -112,10 +112,10 @@
                                 <th>
                                 <span style="display: inline-block;vertical-align:middle;">
                                     <input type="checkbox" id="check_all_endpoints" style="height:13px;">
-                                    <span class="text-warning">支持shift分段选择</span>
+                                    <span class="text-warning">{{_('using SHIFT to continue select')}}</span>
                                 </span>
                                 <span class="pull-right">
-                                    <button class="btn btn-default btn-xs" onclick="fn_list_counters();return false;">刷新counter列表</button>
+                                    <button class="btn btn-default btn-xs" onclick="fn_list_counters();return false;">{{_('list counters')}}</button>
                                 </span>
                                 </th>
                             </tr></thead>
@@ -150,7 +150,7 @@
                     <div class="panel panel-info">
                         <div class="panel-heading">
                             <img class="loading pull-right" src="/static/img/loading.gif" style="display:none;"></img>
-                            搜索Counters
+                            {{_('search counters')}}
                         </div>
 
                         <div class="panel-body">
@@ -158,12 +158,12 @@
                                 <div class="form-group">
                                     <label>Counter</label>
                                     <input id="counter-search" type="text" class="form-control input-sm">
-                                    <p class="help-block"><span class="text-warning">可以用空格分割多个搜索关键字</span></p>
+                                    <p class="help-block"><span class="text-warning">{{_('using SPACE to separate keywords')}}</span></p>
                                 </div>
                             </form>
 
                             <form class="form-inline" role="form">
-                                <button class="btn btn-default btn-sm btn-success" onclick="fn_list_counters();return false;">搜索</button>
+                                <button class="btn btn-default btn-sm btn-success" onclick="fn_list_counters();return false;">{{_('search')}}</button>
                                 <div class="form-group">
                                     <select class="form-control input-sm" id="counter-limit" onchange="fn_list_counters();return false;">
                                         <option value="50"> Limit 50</option>
@@ -178,16 +178,16 @@
                                 </div>
                                 <div class="form-group">
                                     <input id="counter-filter" type="text" class="form-control input-sm">
-                                    <button class="btn btn-default btn-sm btn-info" onclick="filter_counter();return false;">快速过滤</button>
-                                    <button class="btn btn-default btn-sm" onclick="fn_delete_counters();return false;">删除counters</button>
+                                    <button class="btn btn-default btn-sm btn-info" onclick="filter_counter();return false;">{{_('quick filter')}}</button>
+                                    <button class="btn btn-default btn-sm" onclick="fn_delete_counters();return false;">{{_('delete counters')}}</button>
                                 </div>
 
                                 <div class="dropdown form-group pull-right">
-                                    <a class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" href="#"> 看图 <span class="caret"></span></a>
+                                    <a class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" href="#"> {{_('show')}}<span class="caret"></span></a>
                                     <ul class="dropdown-menu" role="menu">
-                                      <li><a href="#" class="btn btn-link btn-xs" onclick="fn_show_all('h');return false;">Endpoint视角</a></li>
-                                      <li><a href="#" class="btn btn-link btn-xs" onclick="fn_show_all('k');return false;">Counter视角</a></li>
-                                      <li><a href="#" class="btn btn-link btn-xs" onclick="fn_show_all('a');return false;">组合视角</a></li>
+                                      <li><a href="#" class="btn btn-link btn-xs" onclick="fn_show_all('h');return false;">{{_('endpoint view')}}</a></li>
+                                      <li><a href="#" class="btn btn-link btn-xs" onclick="fn_show_all('k');return false;">{{_('counter view')}}</a></li>
+                                      <li><a href="#" class="btn btn-link btn-xs" onclick="fn_show_all('a');return false;">{{_('multi view')}}</a></li>
                                     </ul>
                                 </div>
 
@@ -199,8 +199,8 @@
                             <thead> <tr>
                                 <th width="30px"><input type="checkbox" id="check_all_counters"></th>
                                 <th >Counters</th>
-                                <th width="60px">类型</th>
-                                <th width="60px">频率</th>
+                                <th width="60px">{{_('type')}}</th>
+                                <th width="60px">{{_('step')}}</th>
                             </tr></thead>
 
                             <tbody id="tbody-counters"> </tbody>

+ 6 - 6
rrd/templates/portal/alarm/case.html

@@ -3,8 +3,8 @@
 
     <div class="panel panel-danger">
         <div class="panel-heading">
-            <h4 class="panel-title">报警case列表
-                <a class="pull-right small" href="/portal/alarm-dash/case?status=PROBLEM">查看未恢复报警列表</a>
+            <h4 class="panel-title">{{_('alerting cases')}}
+                <a class="pull-right small" href="/portal/alarm-dash/case?status=PROBLEM">{{_('not recovered cases')}}</a>
             </h4>
         </div>
         <div class="panel-body">
@@ -31,7 +31,7 @@
                     <span class="cut-line">¦</span>
                     <a href="javascript:alarm_case_rm('{{case.id}}');">delete</a>
                     <span class="cut-line">¦</span>
-                    <a href="/portal/alarm-dash/case/event?case_id={{case.id}}">展开事件列表</a>
+                    <a href="/portal/alarm-dash/case/event?case_id={{case.id}}">{{_('event list')}}</a>
                     <span class="gray">]</span>
                     </br>
 
@@ -47,9 +47,9 @@
                 </div>
                 <hr>
                 {%endfor%}
-                <a href="javascript:alarm_case_all_select();">全选</a>/
-                <a href="javascript:alarm_case_reverse_select();">反选</a>
-                <button class="btn btn-warning btn-sm" onclick="alarm_case_batch_rm();">批量删除</button>
+                <a href="javascript:alarm_case_all_select();">{{_('select all')}}</a>/
+                <a href="javascript:alarm_case_reverse_select();">{{_('reverse select')}}</a>
+                <button class="btn btn-warning btn-sm" onclick="alarm_case_batch_rm();">{{_('batch delete')}}</button>
             </div>
         </div>
 

+ 5 - 5
rrd/templates/portal/alarm/case_events.html

@@ -3,8 +3,8 @@
 
     <div class="panel panel-danger">
         <div class="panel-heading">
-            <h4 class="panel-title">case:{{case.id}} 所关联的事件列表
-                <a href="/portal/alarm-dash/case" class="pull-right small">返回</a></h4>
+            <h4 class="panel-title">case:{{case.id}} {{_('event list')}}
+                <a href="/portal/alarm-dash/case" class="pull-right small">{{_('back')}}</a></h4>
         </div>
         <div class="panel-body">
             <div class="alarms">
@@ -31,9 +31,9 @@
                 </div>
                 <hr>
                 {%endfor%}
-                <a href="javascript:alarm_case_event_all_select();">全选</a>/
-                <a href="javascript:alarm_case_event_reverse_select();">反选</a>
-                <button class="btn btn-warning btn-sm" onclick="alarm_case_event_batch_rm();">批量删除</button>
+                <a href="javascript:alarm_case_event_all_select();">{{_('select all')}}</a>/
+                <a href="javascript:alarm_case_event_reverse_select();">{{_('reverse select')}}</a>
+                <button class="btn btn-warning btn-sm" onclick="alarm_case_event_batch_rm();">{{_('batch delete')}}</button>
             </div>
         </div>
 

+ 2 - 2
rrd/templates/portal/cluster/creator.html

@@ -5,8 +5,8 @@
 
         <div class="panel panel-default">
             <div class="panel-heading">
-                创建集群监控项 
-                <a target="_blank" href="https://book.open-falcon.org/zh/usage/aggregator.html" title="aggregator配置说明">
+                {{_('create cluster monitor metric')}}
+                <a target="_blank" href="https://book.open-falcon.org/zh/usage/aggregator.html" title="aggregator{{_('configure help')}}">
                     <span class="glyphicon glyphicon-question-sign"></span>
                 </a>
             </div>

BIN
rrd/translations/en/LC_MESSAGES/messages.mo


+ 230 - 0
rrd/translations/en/LC_MESSAGES/messages.po

@@ -0,0 +1,230 @@
+# English translations for PROJECT.
+# Copyright (C) 2017 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2017-06-07 23:01+0800\n"
+"PO-Revision-Date: 2017-06-07 19:28+0800\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language: en\n"
+"Language-Team: en <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.4.0\n"
+
+#: rrd/__init__.py:16
+msgid "Temporary error, please contact your administrator."
+msgstr ""
+
+#: rrd/templates/base.html:114
+msgid "batch edit"
+msgstr ""
+
+#: rrd/templates/base.html:122
+msgid "Close"
+msgstr ""
+
+#: rrd/templates/base.html:123
+msgid "OK"
+msgstr ""
+
+#: rrd/templates/index.html:68
+msgid "search endpoints"
+msgstr ""
+
+#: rrd/templates/index.html:76 rrd/templates/index.html:161
+msgid "using SPACE to separate keywords"
+msgstr ""
+
+#: rrd/templates/index.html:79
+msgid "labels"
+msgstr ""
+
+#: rrd/templates/index.html:85
+msgid "global search"
+msgstr ""
+
+#: rrd/templates/index.html:97
+msgid "delete endpoints"
+msgstr ""
+
+#: rrd/templates/index.html:104 rrd/templates/index.html:181
+msgid "quick filter"
+msgstr ""
+
+#: rrd/templates/index.html:115
+msgid "using SHIFT to continue select"
+msgstr ""
+
+#: rrd/templates/index.html:118
+msgid "list counters"
+msgstr ""
+
+#: rrd/templates/index.html:153
+msgid "search counters"
+msgstr ""
+
+#: rrd/templates/index.html:166
+msgid "search"
+msgstr ""
+
+#: rrd/templates/index.html:182
+msgid "delete counters"
+msgstr ""
+
+#: rrd/templates/chart/multi_ng.html:96 rrd/templates/chart/multi_pic.html:44
+#: rrd/templates/index.html:186
+msgid "show"
+msgstr ""
+
+#: rrd/templates/chart/multi_ng.html:82 rrd/templates/chart/multi_pic.html:31
+#: rrd/templates/index.html:188
+msgid "endpoint view"
+msgstr ""
+
+#: rrd/templates/chart/multi_ng.html:83 rrd/templates/chart/multi_pic.html:32
+#: rrd/templates/index.html:189
+msgid "counter view"
+msgstr ""
+
+#: rrd/templates/chart/multi_ng.html:84 rrd/templates/index.html:190
+msgid "multi view"
+msgstr ""
+
+#: rrd/templates/index.html:202
+msgid "type"
+msgstr ""
+
+#: rrd/templates/index.html:203
+msgid "step"
+msgstr ""
+
+#: rrd/templates/auth/register.html:21
+msgid "UserName"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:56 rrd/templates/chart/embed.html:55
+msgid "date range"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:67 rrd/templates/chart/embed.html:66
+#: rrd/templates/chart/multi_ng.html:72 rrd/templates/chart/multi_pic.html:21
+msgid "sampling method"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:69 rrd/templates/chart/embed.html:68
+msgid "max"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:70 rrd/templates/chart/embed.html:69
+msgid "min"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:71 rrd/templates/chart/embed.html:70
+msgid "average"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:74 rrd/templates/chart/embed.html:73
+msgid "other"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:76 rrd/templates/chart/embed.html:75
+#: rrd/templates/chart/multi_ng.html:91 rrd/templates/chart/multi_pic.html:39
+msgid "sum"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:77 rrd/templates/chart/embed.html:76
+msgid "sum only"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:78 rrd/templates/chart/embed.html:77
+msgid "cancel sum"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:79 rrd/templates/chart/embed.html:78
+#: rrd/templates/chart/multi_ng.html:99 rrd/templates/chart/multi_pic.html:47
+msgid "reset"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:106 rrd/templates/portal/alarm/case.html:50
+#: rrd/templates/portal/alarm/case_events.html:34
+msgid "select all"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:111 rrd/templates/portal/alarm/case.html:51
+#: rrd/templates/portal/alarm/case_events.html:35
+msgid "reverse select"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:114
+msgid "refresh"
+msgstr ""
+
+#: rrd/templates/chart/big_ng.html:119
+msgid "type to fiter, enter to refresh..."
+msgstr ""
+
+#: rrd/templates/chart/multi_ng.html:46 rrd/templates/chart/multi_pic.html:11
+msgid "begin"
+msgstr ""
+
+#: rrd/templates/chart/multi_ng.html:59 rrd/templates/chart/multi_pic.html:16
+msgid "end"
+msgstr ""
+
+#: rrd/templates/chart/multi_ng.html:80 rrd/templates/chart/multi_pic.html:29
+msgid "view"
+msgstr ""
+
+#: rrd/templates/portal/alarm/case.html:6
+msgid "alerting cases"
+msgstr ""
+
+#: rrd/templates/portal/alarm/case.html:7
+msgid "not recovered cases"
+msgstr ""
+
+#: rrd/templates/portal/alarm/case.html:34
+#: rrd/templates/portal/alarm/case_events.html:6
+msgid "event list"
+msgstr ""
+
+#: rrd/templates/portal/alarm/case.html:52
+#: rrd/templates/portal/alarm/case_events.html:36
+msgid "batch delete"
+msgstr ""
+
+#: rrd/templates/portal/alarm/case_events.html:7
+msgid "back"
+msgstr ""
+
+#: rrd/templates/portal/cluster/creator.html:8
+msgid "create cluster monitor metric"
+msgstr ""
+
+#: rrd/templates/portal/cluster/creator.html:9
+msgid "configure help"
+msgstr ""
+
+#: rrd/view/dashboard/screen.py:58
+msgid "screen create fail"
+msgstr ""
+
+#: rrd/view/dashboard/screen.py:224
+msgid "edit successful"
+msgstr ""
+
+#: rrd/view/portal/cluster.py:85
+msgid "edit"
+msgstr ""
+
+#: rrd/view/portal/cluster.py:95
+msgid "clone"
+msgstr ""
+

BIN
rrd/translations/zh/LC_MESSAGES/messages.mo


+ 230 - 0
rrd/translations/zh/LC_MESSAGES/messages.po

@@ -0,0 +1,230 @@
+# Chinese translations for PROJECT.
+# Copyright (C) 2017 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2017-06-07 23:01+0800\n"
+"PO-Revision-Date: 2017-06-07 17:26+0800\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language: zh\n"
+"Language-Team: zh <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.4.0\n"
+
+#: rrd/__init__.py:16
+msgid "Temporary error, please contact your administrator."
+msgstr "暂时无法访问,请联系管理员。"
+
+#: rrd/templates/base.html:114
+msgid "batch edit"
+msgstr "批量修改"
+
+#: rrd/templates/base.html:122
+msgid "Close"
+msgstr "关闭"
+
+#: rrd/templates/base.html:123
+msgid "OK"
+msgstr "确认"
+
+#: rrd/templates/index.html:68
+msgid "search endpoints"
+msgstr "搜索Endpoints"
+
+#: rrd/templates/index.html:76 rrd/templates/index.html:161
+msgid "using SPACE to separate keywords"
+msgstr "空格分割多个关键字"
+
+#: rrd/templates/index.html:79
+msgid "labels"
+msgstr "标签"
+
+#: rrd/templates/index.html:85
+msgid "global search"
+msgstr "全局搜索"
+
+#: rrd/templates/index.html:97
+msgid "delete endpoints"
+msgstr "删除endpoints"
+
+#: rrd/templates/index.html:104 rrd/templates/index.html:181
+msgid "quick filter"
+msgstr "快速过滤"
+
+#: rrd/templates/index.html:115
+msgid "using SHIFT to continue select"
+msgstr "支持shift范围选择"
+
+#: rrd/templates/index.html:118
+msgid "list counters"
+msgstr "刷新counter列表"
+
+#: rrd/templates/index.html:153
+msgid "search counters"
+msgstr "搜索Counters"
+
+#: rrd/templates/index.html:166
+msgid "search"
+msgstr "搜索"
+
+#: rrd/templates/index.html:182
+msgid "delete counters"
+msgstr "删除counters"
+
+#: rrd/templates/chart/multi_ng.html:96 rrd/templates/chart/multi_pic.html:44
+#: rrd/templates/index.html:186
+msgid "show"
+msgstr "看图"
+
+#: rrd/templates/chart/multi_ng.html:82 rrd/templates/chart/multi_pic.html:31
+#: rrd/templates/index.html:188
+msgid "endpoint view"
+msgstr "Endpoint视角"
+
+#: rrd/templates/chart/multi_ng.html:83 rrd/templates/chart/multi_pic.html:32
+#: rrd/templates/index.html:189
+msgid "counter view"
+msgstr "Counter视角"
+
+#: rrd/templates/chart/multi_ng.html:84 rrd/templates/index.html:190
+msgid "multi view"
+msgstr "组合视角"
+
+#: rrd/templates/index.html:202
+msgid "type"
+msgstr "类型"
+
+#: rrd/templates/index.html:203
+msgid "step"
+msgstr "频率"
+
+#: rrd/templates/auth/register.html:21
+msgid "UserName"
+msgstr "中文名"
+
+#: rrd/templates/chart/big_ng.html:56 rrd/templates/chart/embed.html:55
+msgid "date range"
+msgstr "时间范围"
+
+#: rrd/templates/chart/big_ng.html:67 rrd/templates/chart/embed.html:66
+#: rrd/templates/chart/multi_ng.html:72 rrd/templates/chart/multi_pic.html:21
+msgid "sampling method"
+msgstr "采样方法"
+
+#: rrd/templates/chart/big_ng.html:69 rrd/templates/chart/embed.html:68
+msgid "max"
+msgstr "最大值"
+
+#: rrd/templates/chart/big_ng.html:70 rrd/templates/chart/embed.html:69
+msgid "min"
+msgstr "最小值"
+
+#: rrd/templates/chart/big_ng.html:71 rrd/templates/chart/embed.html:70
+msgid "average"
+msgstr "平均值"
+
+#: rrd/templates/chart/big_ng.html:74 rrd/templates/chart/embed.html:73
+msgid "other"
+msgstr "其他"
+
+#: rrd/templates/chart/big_ng.html:76 rrd/templates/chart/embed.html:75
+#: rrd/templates/chart/multi_ng.html:91 rrd/templates/chart/multi_pic.html:39
+msgid "sum"
+msgstr "求和"
+
+#: rrd/templates/chart/big_ng.html:77 rrd/templates/chart/embed.html:76
+msgid "sum only"
+msgstr "只求和"
+
+#: rrd/templates/chart/big_ng.html:78 rrd/templates/chart/embed.html:77
+msgid "cancel sum"
+msgstr "取消求和"
+
+#: rrd/templates/chart/big_ng.html:79 rrd/templates/chart/embed.html:78
+#: rrd/templates/chart/multi_ng.html:99 rrd/templates/chart/multi_pic.html:47
+msgid "reset"
+msgstr "重置"
+
+#: rrd/templates/chart/big_ng.html:106 rrd/templates/portal/alarm/case.html:50
+#: rrd/templates/portal/alarm/case_events.html:34
+msgid "select all"
+msgstr "全选"
+
+#: rrd/templates/chart/big_ng.html:111 rrd/templates/portal/alarm/case.html:51
+#: rrd/templates/portal/alarm/case_events.html:35
+msgid "reverse select"
+msgstr "反选"
+
+#: rrd/templates/chart/big_ng.html:114
+msgid "refresh"
+msgstr "刷新"
+
+#: rrd/templates/chart/big_ng.html:119
+msgid "type to fiter, enter to refresh..."
+msgstr "输入字符过滤, 回车刷新..."
+
+#: rrd/templates/chart/multi_ng.html:46 rrd/templates/chart/multi_pic.html:11
+msgid "begin"
+msgstr "开始"
+
+#: rrd/templates/chart/multi_ng.html:59 rrd/templates/chart/multi_pic.html:16
+msgid "end"
+msgstr "结束"
+
+#: rrd/templates/chart/multi_ng.html:80 rrd/templates/chart/multi_pic.html:29
+msgid "view"
+msgstr "视角"
+
+#: rrd/templates/portal/alarm/case.html:6
+msgid "alerting cases"
+msgstr "告警case列表"
+
+#: rrd/templates/portal/alarm/case.html:7
+msgid "not recovered cases"
+msgstr "未恢复告警列表"
+
+#: rrd/templates/portal/alarm/case.html:34
+#: rrd/templates/portal/alarm/case_events.html:6
+msgid "event list"
+msgstr "告警事件列表"
+
+#: rrd/templates/portal/alarm/case.html:52
+#: rrd/templates/portal/alarm/case_events.html:36
+msgid "batch delete"
+msgstr "批量删除"
+
+#: rrd/templates/portal/alarm/case_events.html:7
+msgid "back"
+msgstr "返回"
+
+#: rrd/templates/portal/cluster/creator.html:8
+msgid "create cluster monitor metric"
+msgstr "创建集群监控项"
+
+#: rrd/templates/portal/cluster/creator.html:9
+msgid "configure help"
+msgstr "配置说明"
+
+#: rrd/view/dashboard/screen.py:58
+msgid "screen create fail"
+msgstr "创建screen失败"
+
+#: rrd/view/dashboard/screen.py:224
+msgid "edit successful"
+msgstr "更新成功"
+
+#: rrd/view/portal/cluster.py:85
+msgid "edit"
+msgstr "编辑"
+
+#: rrd/view/portal/cluster.py:95
+msgid "clone"
+msgstr "克隆"
+

+ 2 - 1
rrd/view/__init__.py

@@ -3,7 +3,7 @@ import datetime
 import time
 from flask import g, session, request, redirect
 
-from rrd import app
+from rrd import app, config
 from rrd.view.utils import get_usertoken_from_session, get_current_user_profile
 
 @app.template_filter('fmt_time')
@@ -41,6 +41,7 @@ def app_teardown(exception):
 def app_before():
     g.user_token = get_usertoken_from_session(session)
     g.user = get_current_user_profile(g.user_token)
+    g.locale = request.accept_languages.best_match(config.LANGUAGES.keys())
 
     path = request.path
     if not g.user and not path.startswith("/auth/login") and \

+ 1 - 0
rrd/view/auth/auth.py

@@ -1,5 +1,6 @@
 #-*- coding:utf-8 -*-
 from flask import request, g, abort, render_template, redirect
+from flask.ext.babel import refresh
 import requests
 import json
 from rrd import app

+ 3 - 2
rrd/view/dashboard/screen.py

@@ -3,6 +3,7 @@ import json
 import copy
 import json
 from flask import render_template, abort, request, url_for, redirect, g
+from flask.ext.babel import gettext
 import time
 import datetime
 
@@ -54,7 +55,7 @@ def dash_screen_clone(sid):
 
         new_s = DashboardScreen.add(screen.pid, screen_name)
         if not new_s:
-            abort(404, "创建screen失败了")
+            abort(404, gettext("screen create fail"))
 
         if with_graph:
             old_graphs = DashboardGraph.gets_by_screen_id(sid)
@@ -220,7 +221,7 @@ def dash_graph_edit(gid):
         graph = graph.update(title, hosts, counters, screen_id,
                 timespan, graph_type, method, position)
 
-        error = u"修改成功了"
+        error = gettext("edit successful")
         if not ajax:
             return render_template("screen/graph_edit.html", config=config, **locals())
         else:

+ 3 - 2
rrd/view/portal/cluster.py

@@ -1,6 +1,7 @@
 #-*- coding:utf-8 -*-
 __author__ = 'Ulric Qin'
 from flask import jsonify, render_template, request, g
+from flask.ext.babel import gettext
 from rrd import app
 from rrd.model.portal.host_group import HostGroup
 from rrd.model.portal.cluster import Cluster
@@ -81,7 +82,7 @@ def cluster_node_post(group_id):
 def cluster_edit_get(cluster_id):
     cluster_id = int(cluster_id)
     cluster = Cluster.get(cluster_id)
-    op = u'修改'
+    op = gettext('edit')
     return render_template('portal/cluster/edit.html', **locals())
 
 
@@ -91,7 +92,7 @@ def cluster_clone_get(cluster_id):
     cluster = Cluster.get(cluster_id)
     # for clone
     cluster_id = 0
-    op = u'克隆'
+    op = gettext('clone')
     return render_template('portal/cluster/edit.html', **locals())
 
 

+ 8 - 0
scripts/tr_compile.py

@@ -0,0 +1,8 @@
+#!env/bin/python
+import os
+import sys
+if sys.platform == 'win32':
+    pybabel = 'env\\Scripts\\pybabel'
+else:
+    pybabel = 'env/bin/pybabel'
+os.system(pybabel + ' compile -d rrd/translations')

+ 15 - 0
scripts/tr_init.py

@@ -0,0 +1,15 @@
+#!env/bin/python
+import os
+import sys
+if sys.platform == 'win32':
+    pybabel = 'env\\Scripts\\pybabel'
+else:
+    pybabel = 'env/bin/pybabel'
+if len(sys.argv) != 2:
+    print "usage: tr_init <language-code>"
+    sys.exit(1)
+os.system(pybabel +
+          ' extract -F babel.cfg -k lazy_gettext -o messages.pot rrd')
+os.system(pybabel +
+          ' init -i messages.pot -d rrd/translations -l ' + sys.argv[1])
+os.unlink('messages.pot')

+ 10 - 0
scripts/tr_update.py

@@ -0,0 +1,10 @@
+#!env/bin/python
+import os
+import sys
+if sys.platform == 'win32':
+    pybabel = 'env\\Scripts\\pybabel'
+else:
+    pybabel = 'env/bin/pybabel'
+os.system(pybabel + ' extract -F babel.cfg -k lazy_gettext -o messages.pot rrd')
+os.system(pybabel + ' update -i messages.pot -d rrd/translations')
+os.unlink('messages.pot')