You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by mi...@apache.org on 2018/12/30 16:22:35 UTC

[incubator-dubbo-ops] 01/01: use vue-i18n to support front end i18n

This is an automated email from the ASF dual-hosted git repository.

min pushed a commit to branch i18n
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo-ops.git

commit cb8c39788077a50cdf6c2f292d5a867c581ed10a
Author: nzomkxia <z8...@gmail.com>
AuthorDate: Mon Dec 31 00:22:44 2018 +0800

    use vue-i18n to support front end i18n
---
 dubbo-admin-frontend/package.json                  |   1 +
 dubbo-admin-frontend/src/api/menu.js               |  22 ++--
 .../src/components/ServiceDetail.vue               | 120 +++++++++++---------
 .../src/components/ServiceSearch.vue               |  73 ++++++------
 .../src/components/governance/AccessControl.vue    |  90 ++++++++-------
 .../src/components/governance/LoadBalance.vue      | 123 +++++++++++---------
 .../src/components/governance/Overrides.vue        |  98 +++++++++-------
 .../src/components/governance/RoutingRule.vue      | 126 ++++++++++++---------
 .../src/components/governance/TagRule.vue          |  65 ++++++-----
 .../src/components/governance/WeightAdjust.vue     | 118 ++++++++++---------
 .../src/components/public/Drawer.vue               |   6 +-
 .../src/components/public/Toolbar.vue              |  43 +++++++
 .../src/components/test/ServiceMock.vue            |   2 +-
 dubbo-admin-frontend/src/lang/en.js                |  67 +++++++++++
 dubbo-admin-frontend/src/{store => lang}/index.js  |  25 ++--
 dubbo-admin-frontend/src/lang/zh.js                |  67 +++++++++++
 dubbo-admin-frontend/src/main.js                   |   3 +
 dubbo-admin-frontend/src/store/index.js            |  15 ++-
 .../src/{store/index.js => util/i18n.js}           |  22 ++--
 19 files changed, 685 insertions(+), 401 deletions(-)

diff --git a/dubbo-admin-frontend/package.json b/dubbo-admin-frontend/package.json
index e4a3919..3625632 100644
--- a/dubbo-admin-frontend/package.json
+++ b/dubbo-admin-frontend/package.json
@@ -16,6 +16,7 @@
     "http-status": "^1.2.0",
     "js-yaml": "^3.12.0",
     "vue": "^2.5.2",
+    "vue-i18n": "^8.6.0",
     "vue-router": "^3.0.1",
     "vuetify": "^1.2.2",
     "vuex": "^3.0.1"
diff --git a/dubbo-admin-frontend/src/api/menu.js b/dubbo-admin-frontend/src/api/menu.js
index 05f1660..9ccbe06 100644
--- a/dubbo-admin-frontend/src/api/menu.js
+++ b/dubbo-admin-frontend/src/api/menu.js
@@ -16,23 +16,23 @@
  */
 
 const Menu = [
-  { title: 'Service Search', path: '/service', icon: 'search' },
+  { title: 'serviceSearch', path: '/service', icon: 'search' },
   {
-    title: 'Service Governance',
+    title: 'serviceGovernance',
     icon: 'edit',
     group: 'governance',
     items: [
-      { title: 'Routing Rule', path: '/governance/routingRule' },
-      {title: 'Tag Rule', path: '/governance/tagRule', badge: 'new'},
-      { title: 'Dynamic Config', path: '/governance/config' },
-      { title: 'Access Control', path: '/governance/access' },
-      { title: 'Weight Adjust', path: '/governance/weight' },
-      { title: 'Load Balance', path: '/governance/loadbalance' }
+      { title: 'routingRule', path: '/governance/routingRule' },
+      {title: 'tagRule', path: '/governance/tagRule', badge: 'new'},
+      { title: 'dynamicConfig', path: '/governance/config' },
+      { title: 'accessControl', path: '/governance/access' },
+      { title: 'weightAdjust', path: '/governance/weight' },
+      { title: 'loadBalance', path: '/governance/loadbalance' }
     ]
   },
-  { title: 'Service Test', path: '/test', icon: 'code', badge: 'feature' },
-  { title: 'Service Mock', path: '/mock', icon: 'build', badge: 'feature' },
-  { title: 'Metrics', path: '/metrics', icon: 'show_chart', badge: 'feature' }
+  { title: 'serviceTest', path: '/test', icon: 'code', badge: 'feature' },
+  { title: 'serviceMock', path: '/mock', icon: 'build', badge: 'feature' },
+  { title: 'metrics', path: '/metrics', icon: 'show_chart', badge: 'feature' }
 
 ]
 
diff --git a/dubbo-admin-frontend/src/components/ServiceDetail.vue b/dubbo-admin-frontend/src/components/ServiceDetail.vue
index 74130ec..ebae455 100644
--- a/dubbo-admin-frontend/src/components/ServiceDetail.vue
+++ b/dubbo-admin-frontend/src/components/ServiceDetail.vue
@@ -19,7 +19,7 @@
   <v-container grid-list-xl fluid >
     <v-layout row wrap >
       <v-flex sm12>
-        <h3>Basic Info</h3>
+        <h3>{{$t('basicInfo')}}</h3>
       </v-flex>
       <v-flex lg12>
         <v-data-table
@@ -85,7 +85,7 @@
         </v-tabs>
       </v-flex>
       <v-flex sm12>
-        <h3>Metadata</h3>
+        <h3>{{$t('metaData')}}</h3>
       </v-flex>
       <v-flex lg12>
         <v-data-table
@@ -109,68 +109,74 @@
 <script>
   export default {
     data: () => ({
-      metaHeaders: [
-        {
-          text: 'Method Name',
-          value: 'method',
-          sortable: false
-        },
-        {
-          text: 'Parameter List',
-          value: 'parameter',
-          sortable: false
-        },
-        {
-          text: 'Return Type',
-          value: 'returnType',
-          sortable: false
-        }
-      ],
-      detailHeaders: {
-        providers: [
-          {
-            text: 'IP',
-            value: 'ip'
-          },
-          {
-            text: 'Port',
-            value: 'port'
-          },
-          {
-            text: 'Timeout(ms)',
-            value: 'timeout'
-          },
-          {
-            text: 'Serialization',
-            value: 'serial'
-          },
-          {
-            text: 'Operation',
-            value: 'operate'
-          }
-
-        ],
-        consumers: [
+      metaHeaders: [],
+      detailHeaders: {},
+      providerDetails: [],
+      consumerDetails: [],
+      methodMetaData: [],
+      basic: []
+    }),
+    methods: {
+      setmetaHeaders: function () {
+        this.metaHeaders = [
           {
-            text: 'IP',
-            value: 'ip'
+            text: this.$t('methodName'),
+            value: 'method',
+            sortable: false
           },
           {
-            text: 'Port',
-            value: 'port'
+            text: this.$t('parameterList'),
+            value: 'parameter',
+            sortable: false
           },
           {
-            text: 'Application Name',
-            value: 'appName'
+            text: this.$t('returnType'),
+            value: 'returnType',
+            sortable: false
           }
         ]
       },
-      providerDetails: [],
-      consumerDetails: [],
-      methodMetaData: [],
-      basic: []
-    }),
-    methods: {
+      setdetailHeaders: function () {
+        this.detailHeaders = {
+          providers: [
+            {
+              text: this.$t('ip'),
+              value: 'ip'
+            },
+            {
+              text: this.$t('port'),
+              value: 'port'
+            },
+            {
+              text: this.$t('timeout(ms)'),
+              value: 'timeout'
+            },
+            {
+              text: this.$t('serialization'),
+              value: 'serial'
+            },
+            {
+              text: this.$t('operation'),
+              value: 'operate'
+            }
+
+          ],
+          consumers: [
+            {
+              text: this.$t('ip'),
+              value: 'ip'
+            },
+            {
+              text: this.$t('port'),
+              value: 'port'
+            },
+            {
+              text: this.$t('appName'),
+              value: 'appName'
+            }
+          ]
+        }
+      },
       detail: function (service) {
         this.$axios.get('/service/' + service)
             .then(response => {
@@ -196,6 +202,8 @@
       }
     },
     mounted: function () {
+      this.setmetaHeaders()
+      this.setdetailHeaders()
       let query = this.$route.query
       let meta = {
         'service': '',
diff --git a/dubbo-admin-frontend/src/components/ServiceSearch.vue b/dubbo-admin-frontend/src/components/ServiceSearch.vue
index 3725115..c80f772 100644
--- a/dubbo-admin-frontend/src/components/ServiceSearch.vue
+++ b/dubbo-admin-frontend/src/components/ServiceSearch.vue
@@ -34,7 +34,7 @@
                   hide-no-data
                   :suffix="queryBy"
                   :hint="hint"
-                  label="Search Dubbo Services"
+                  :label="$t('searchDubboService')"
                   @keyup.enter="submit"
                 ></v-combobox>
                 <v-menu class="hidden-xs-only">
@@ -62,7 +62,7 @@
     <v-flex lg12>
       <v-card>
         <v-toolbar flat color="transparent" class="elevation-0">
-          <v-toolbar-title><span class="headline">Search Result</span></v-toolbar-title>
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
           <v-spacer></v-spacer>
         </v-toolbar>
 
@@ -155,34 +155,7 @@
       typeAhead: [],
       services: [],
       filter: '',
-      headers: [
-        {
-          text: 'Service Name',
-          value: 'service',
-          align: 'left'
-        },
-        {
-          text: 'Group',
-          value: 'group',
-          align: 'left'
-        },
-        {
-          text: 'Version',
-          value: 'version',
-          align: 'left'
-        },
-        {
-          text: 'Application',
-          value: 'application',
-          align: 'left'
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '110px'
-        }
-      ]
+      headers: []
     }),
     computed: {
       queryBy () {
@@ -190,20 +163,53 @@
       },
       hint () {
         if (this.selected === 0) {
-          return 'Service ID, org.apache.dubbo.demo.api.DemoService, * for all services'
+          return this.$t('serviceSearchHint')
         } else if (this.selected === 1) {
-          return 'Find all services provided by the target server on the specified IP address'
+          return this.$t('ipSearchHint')
         } else if (this.selected === 2) {
-          return 'Input an application name to find all services provided by one particular application, * for all'
+          return this.$t('appSearchHint')
         }
       }
     },
     watch: {
       input (val) {
         this.querySelections(val)
+      },
+      area () {
+        this.setHeaders()
       }
     },
     methods: {
+      setHeaders: function () {
+        this.headers = [
+          {
+            text: this.$t('serviceName'),
+            value: 'service',
+            align: 'left'
+          },
+          {
+            text: this.$t('group'),
+            value: 'group',
+            align: 'left'
+          },
+          {
+            text: this.$t('version'),
+            value: 'version',
+            align: 'left'
+          },
+          {
+            text: this.$t('app'),
+            value: 'application',
+            align: 'left'
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '110px'
+          }
+        ]
+      },
       querySelections (v) {
         if (this.timerID) {
           clearTimeout(this.timerID)
@@ -276,6 +282,7 @@
       }
     },
     mounted: function () {
+      this.setHeaders()
       let query = this.$route.query
       let filter = null
       let pattern = null
diff --git a/dubbo-admin-frontend/src/components/governance/AccessControl.vue b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
index bfbb274..b201497 100644
--- a/dubbo-admin-frontend/src/components/governance/AccessControl.vue
+++ b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
@@ -32,7 +32,7 @@
                   append-icon=""
                   hide-no-data
                   :suffix="queryBy"
-                  label="Search Routing Rule"
+                  :label="$t('searchRoutingRule')"
                 ></v-combobox>
                 <v-menu class="hidden-xs-only">
                   <v-btn slot="activator" large icon>
@@ -44,11 +44,11 @@
                       v-for="(item, i) in items"
                       :key="i"
                       @click="selected = i">
-                      <v-list-tile-title>{{ item.title }}</v-list-tile-title>
+                      <v-list-tile-title>{{ $t(item.title) }}</v-list-tile-title>
                     </v-list-tile>
                   </v-list>
                 </v-menu>
-                <v-btn @click="search" color="primary" large>Search</v-btn>
+                <v-btn @click="search" color="primary" large>{{$t('search')}}</v-btn>
 
               </v-layout>
             </v-form>
@@ -63,7 +63,7 @@
                    color="transparent"
                    class="elevation-0">
           <v-toolbar-title>
-            <span class="headline">Search Result</span>
+            <span class="headline">{{$t('searchResult')}}</span>
           </v-toolbar-title>
           <v-spacer></v-spacer>
           <v-btn outline
@@ -145,12 +145,12 @@
         <v-card-text>
           <v-form ref="modalForm">
             <v-text-field label="Service Unique ID"
-                          hint="A service ID in form of group/service:version, group and version are optional"
+                          :hint="$t('dataIdHint')"
                           :readonly="modal.id != null"
                           v-model="modal.service" />
             <v-text-field
-              label="Application Name"
-              hint="Application name the service belongs to"
+              :label="$t('appName')"
+              :hint="$t('appNameHint')"
               :readonly="modal.id != null"
               v-model="modal.application"
             ></v-text-field>
@@ -162,7 +162,7 @@
           <v-spacer></v-spacer>
           <v-btn color="darken-1"
                  flat
-                 @click="closeModal()">Close</v-btn>
+                 @click="closeModal()">{{$t('close')}}</v-btn>
           <v-btn color="primary"
                  depressed
                  @click="modal.click">{{ modal.saveBtn }}</v-btn>
@@ -180,10 +180,10 @@
           <v-spacer></v-spacer>
           <v-btn color="darken-1"
                  flat
-                 @click="confirm.enable = false">Disagree</v-btn>
+                 @click="confirm.enable = false">{{$t('disagree')}}</v-btn>
           <v-btn color="primary"
                  depressed
-                 @click="deleteItem(confirm.id)">Agree</v-btn>
+                 @click="deleteItem(confirm.id)">{{$t('agree')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
@@ -199,38 +199,14 @@ export default {
   name: 'AccessControl',
   data: () => ({
     items: [
-      {id: 0, title: 'service name', value: 'service'},
-      {id: 1, title: 'application', value: 'application'}
+      {id: 0, title: 'serviceName', value: 'service'},
+      {id: 1, title: 'app', value: 'application'}
     ],
     selected: 0,
     filter: null,
     loading: false,
-    serviceHeaders: [
-      {
-        text: 'Service Name',
-        value: 'service',
-        align: 'left'
-      },
-      {
-        text: 'Operation',
-        value: 'operation',
-        sortable: false,
-        width: '115px'
-      }
-    ],
-    appHeaders: [
-      {
-        text: 'Application Name',
-        value: 'application',
-        align: 'left'
-      },
-      {
-        text: 'Operation',
-        value: 'operation',
-        sortable: false,
-        width: '115px'
-      }
-    ],
+    serviceHeaders: [],
+    appHeaders: [],
     accesses: [],
     modal: {
       enable: false,
@@ -262,6 +238,36 @@ export default {
     }
   }),
   methods: {
+    setAppHeaders () {
+      this.appHeaders = [
+        {
+          text: this.$t('appName'),
+          value: 'application',
+          align: 'left'
+        },
+        {
+          text: this.$t('operation'),
+          value: 'operation',
+          sortable: false,
+          width: '115px'
+        }
+      ]
+    },
+    setServiceHeaders () {
+      this.serviceHeaders = [
+        {
+          text: this.$t('serviceName'),
+          value: 'service',
+          align: 'left'
+        },
+        {
+          text: this.$t('operation'),
+          value: 'operation',
+          sortable: false,
+          width: '115px'
+        }
+      ]
+    },
     search () {
       this.filter = document.querySelector('#serviceSearch').value.trim()
       if (!this.filter) {
@@ -408,7 +414,15 @@ export default {
       return 'by ' + this.items[this.selected].title
     }
   },
+  watch: {
+    area () {
+      this.setAppHeaders()
+      this.setServiceHeaders()
+    }
+  },
   mounted () {
+    this.setAppHeaders()
+    this.setServiceHeaders()
     let query = this.$route.query
     if ('service' in query) {
       this.filter = query['service']
diff --git a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
index 650cc8f..1f7ba77 100644
--- a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
+++ b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
@@ -42,11 +42,11 @@
                       v-for="(item, i) in items"
                       :key="i"
                       @click="selected = i">
-                      <v-list-tile-title>{{ item.title }}</v-list-tile-title>
+                      <v-list-tile-title>{{ $t(item.title) }}</v-list-tile-title>
                     </v-list-tile>
                   </v-list>
                 </v-menu>
-                <v-btn @click="submit" color="primary" large>Search</v-btn>
+                <v-btn @click="submit" color="primary" large>{{$t('search')}}</v-btn>
 
               </v-layout>
             </v-form>
@@ -58,9 +58,9 @@
     <v-flex lg12>
       <v-card>
         <v-toolbar flat color="transparent" class="elevation-0">
-          <v-toolbar-title><span class="headline">Search Result</span></v-toolbar-title>
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
           <v-spacer></v-spacer>
-          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">CREATE</v-btn>
+          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">{{$t('create')}}</v-btn>
         </v-toolbar>
 
         <v-card-text class="pa-0" v-if="selected == 0">
@@ -78,7 +78,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon, props.item)">
                     {{op.icon}}
                   </v-icon>
-                  <span>{{op.tooltip}}</span>
+                  <span>{{$t(op.tooltip)}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -100,7 +100,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon, props.item)">
                     {{op.icon}}
                   </v-icon>
-                  <span>{{op.tooltip}}</span>
+                  <span>{{$t(op.tooltip)}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -117,12 +117,12 @@
         <v-card-text >
           <v-text-field
             label="Service Unique ID"
-            hint="A service ID in form of group/service:version, group and version are optional"
+            :hint="$t('dataIdHint')"
             v-model="service"
           ></v-text-field>
           <v-text-field
-            label="Application Name"
-            hint="Application name the service belongs to"
+            :label="$t('appName')"
+            :hint="$t('appNameHint')"
             v-model="application"
           ></v-text-field>
           <v-subheader class="pa-0 mt-3">RULE CONTENT</v-subheader>
@@ -139,7 +139,7 @@
 
     <v-dialog v-model="warn" persistent max-width="500px">
       <v-card>
-        <v-card-title class="headline">{{this.warnTitle}}</v-card-title>
+        <v-card-title class="headline">{{$t(this.warnTitle)}}</v-card-title>
         <v-card-text >{{this.warnText}}</v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
@@ -163,8 +163,8 @@
     },
     data: () => ({
       items: [
-        {id: 0, title: 'service name', value: 'service'},
-        {id: 1, title: 'application', value: 'application'}
+        {id: 0, title: 'serviceName', value: 'service'},
+        {id: 1, title: 'app', value: 'application'}
       ],
       selected: 0,
       dropdown_font: [ 'Service', 'App', 'IP' ],
@@ -181,57 +181,62 @@
       warnStatus: {},
       height: 0,
       operations: [
-        {id: 0, icon: 'visibility', tooltip: 'View'},
-        {id: 1, icon: 'edit', tooltip: 'Edit'},
-        {id: 3, icon: 'delete', tooltip: 'Delete'}
-      ],
-      loadBalances: [
+        {id: 0, icon: 'visibility', tooltip: 'view'},
+        {id: 1, icon: 'edit', tooltip: 'edit'},
+        {id: 3, icon: 'delete', tooltip: 'delete'}
       ],
+      loadBalances: [],
       template:
         'methodName: *  # * for all methods\n' +
         'strategy:  # leastactive, random, roundrobin',
       ruleText: '',
       readonly: false,
-      serviceHeaders: [
-        {
-          text: 'Service Name',
-          value: 'service',
-          align: 'left'
-        },
-        {
-          text: 'Method',
-          value: 'method',
-          align: 'left'
-
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ],
-      appHeaders: [
-        {
-          text: 'Application Name',
-          value: 'application',
-          align: 'left'
-        },
-        {
-          text: 'Method',
-          value: 'method',
-          align: 'left'
-
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ]
+      serviceHeaders: [],
+      appHeaders: []
     }),
     methods: {
+      setServiceHeaders: function () {
+        this.serviceHeaders = [
+          {
+            text: this.$t('serviceName'),
+            value: 'service',
+            align: 'left'
+          },
+          {
+            text: this.$t('method'),
+            value: 'method',
+            align: 'left'
+
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
+      setAppHeaders: function () {
+        this.appHeaders = [
+          {
+            text: this.$t('appName'),
+            value: 'application',
+            align: 'left'
+          },
+          {
+            text: this.$t('method'),
+            value: 'method',
+            align: 'left'
+
+          },
+          {
+            text: this.$('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -397,7 +402,15 @@
         return 'by ' + this.items[this.selected].title
       }
     },
+    watch: {
+      area () {
+        this.setServiceHeaders()
+        this.setAppHeaders()
+      }
+    },
     mounted: function () {
+      this.setServiceHeaders()
+      this.setAppHeaders()
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/governance/Overrides.vue b/dubbo-admin-frontend/src/components/governance/Overrides.vue
index 89c9aa4..abacb5e 100644
--- a/dubbo-admin-frontend/src/components/governance/Overrides.vue
+++ b/dubbo-admin-frontend/src/components/governance/Overrides.vue
@@ -30,7 +30,7 @@
                   append-icon=""
                   hide-no-data
                   :suffix="queryBy"
-                  label="Search Dynamic Config"
+                  :label="$t('searchDynamicConfig')"
                 ></v-combobox>
                 <v-menu class="hidden-xs-only">
                   <v-btn slot="activator" large icon>
@@ -42,11 +42,11 @@
                       v-for="(item, i) in items"
                       :key="i"
                       @click="selected = i">
-                      <v-list-tile-title>{{ item.title }}</v-list-tile-title>
+                      <v-list-tile-title>{{ $t(item.title) }}</v-list-tile-title>
                     </v-list-tile>
                   </v-list>
                 </v-menu>
-                <v-btn @click="submit" color="primary" large>Search</v-btn>
+                <v-btn @click="submit" color="primary" large>{{$t('search')}}</v-btn>
 
               </v-layout>
             </v-form>
@@ -58,9 +58,9 @@
     <v-flex lg12>
       <v-card>
         <v-toolbar flat color="transparent" class="elevation-0">
-          <v-toolbar-title><span class="headline">Search Result</span></v-toolbar-title>
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
           <v-spacer></v-spacer>
-          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">CREATE</v-btn>
+          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">{{$t('create')}}</v-btn>
         </v-toolbar>
 
         <v-card-text class="pa-0" v-if="selected == 0">
@@ -77,7 +77,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon(props.item), props.item)">
                     {{op.icon(props.item)}}
                   </v-icon>
-                  <span>{{op.tooltip(props.item)}}</span>
+                  <span>{{$t(op.tooltip(props.item))}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -98,7 +98,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon(props.item), props.item)">
                     {{op.icon(props.item)}}
                   </v-icon>
-                  <span>{{op.tooltip(props.item)}}</span>
+                  <span>{{$t(op.tooltip(props.item))}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -110,7 +110,7 @@
     <v-dialog   v-model="dialog" width="800px" persistent >
       <v-card>
         <v-card-title class="justify-center">
-          <span class="headline">Create New Dynamic Config Rule</span>
+          <span class="headline">{{$t('createNewDynamicConfigRule')}}</span>
         </v-card-title>
         <v-card-text >
           <v-text-field
@@ -124,26 +124,26 @@
             v-model="application"
           ></v-text-field>
 
-          <v-subheader class="pa-0 mt-3">RULE CONTENT</v-subheader>
+          <v-subheader class="pa-0 mt-3">{{$t('ruleContent')}}</v-subheader>
           <ace-editor v-model="ruleText" :readonly="readonly"/>
 
         </v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
-          <v-btn color="darken-1" flat @click.native="closeDialog">Close</v-btn>
-          <v-btn color="primary darken-1" depressed @click.native="saveItem">Save</v-btn>
+          <v-btn color="darken-1" flat @click.native="closeDialog">{{$t('close')}}</v-btn>
+          <v-btn color="primary darken-1" depressed @click.native="saveItem">{{$t('save')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
 
     <v-dialog v-model="warn" persistent max-width="500px">
       <v-card>
-        <v-card-title class="headline">{{this.warnTitle}}</v-card-title>
+        <v-card-title class="headline">{{$t(this.warnTitle)}}</v-card-title>
         <v-card-text >{{this.warnText}}</v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
-          <v-btn color="darken-1" flat @click.native="closeWarn">CANCLE</v-btn>
-          <v-btn color="primary darken-1" depressed @click.native="deleteItem(warnStatus)">CONFIRM</v-btn>
+          <v-btn color="darken-1" flat @click.native="closeWarn">{{$t('cancel')}}</v-btn>
+          <v-btn color="primary darken-1" depressed @click.native="deleteItem(warnStatus)">{{$t('confirm')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
@@ -163,8 +163,8 @@
     },
     data: () => ({
       items: [
-        {id: 0, title: 'service name', value: 'service'},
-        {id: 1, title: 'application', value: 'application'}
+        {id: 0, title: 'serviceName', value: 'service'},
+        {id: 1, title: 'app', value: 'application'}
       ],
       selected: 0,
       dropdown_font: [ 'Service', 'App', 'IP' ],
@@ -195,34 +195,40 @@
         '      timeout: 6000       # dynamic config parameter\n',
       ruleText: '',
       readonly: false,
-      serviceHeaders: [
-        {
-          text: 'Service Name',
-          value: 'service',
-          align: 'left'
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ],
-      appHeaders: [
-        {
-          text: 'Application Name',
-          value: 'application',
-          align: 'left'
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ]
+      serviceHeaders: [],
+      appHeaders: []
     }),
     methods: {
+      setAppHeaders: function () {
+        this.appHeaders = [
+          {
+            text: this.$t('appName'),
+            value: 'application',
+            align: 'left'
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
+      setServiceHeaders: function () {
+        this.serviceHeaders = [
+          {
+            text: this.$t('serviceName'),
+            value: 'service',
+            align: 'left'
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -425,7 +431,15 @@
         return 'by ' + this.items[this.selected].title
       }
     },
+    watch: {
+      area () {
+        this.setAppHeaders()
+        this.setServiceHeaders()
+      }
+    },
     mounted: function () {
+      this.setAppHeaders()
+      this.setServiceHeaders()
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
index d87ae45..4e5e124 100644
--- a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
@@ -30,7 +30,7 @@
                   append-icon=""
                   hide-no-data
                   :suffix="queryBy"
-                  label="Search Routing Rule"
+                  :label="$t('searchRoutingRule')"
                 ></v-combobox>
                 <v-menu class="hidden-xs-only">
                   <v-btn slot="activator" large icon>
@@ -46,7 +46,7 @@
                     </v-list-tile>
                   </v-list>
                 </v-menu>
-                <v-btn @click="submit" color="primary" large>Search</v-btn>
+                <v-btn @click="submit" color="primary" large>{{$t('search')}}</v-btn>
 
               </v-layout>
             </v-form>
@@ -57,9 +57,9 @@
     <v-flex lg12>
       <v-card>
         <v-toolbar flat color="transparent" class="elevation-0">
-          <v-toolbar-title><span class="headline">Search Result</span></v-toolbar-title>
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
           <v-spacer></v-spacer>
-          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">CREATE</v-btn>
+          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">{{$t('create')}}</v-btn>
         </v-toolbar>
 
         <v-card-text class="pa-0" v-if="selected == 0">
@@ -78,7 +78,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon(props.item), props.item)">
                     {{op.icon(props.item)}}
                   </v-icon>
-                  <span>{{op.tooltip(props.item)}}</span>
+                  <span>{{$t(op.tooltip(props.item))}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -99,7 +99,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon(props.item), props.item)">
                     {{op.icon(props.item)}}
                   </v-icon>
-                  <span>{{op.tooltip(props.item)}}</span>
+                  <span>{{$t(op.tooltip(props.item))}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -111,12 +111,12 @@
     <v-dialog   v-model="dialog" width="800px" persistent >
       <v-card>
         <v-card-title class="justify-center">
-          <span class="headline">Create New Routing Rule</span>
+          <span class="headline">{{$t('createNewRoutingRule')}}</span>
         </v-card-title>
         <v-card-text >
           <v-text-field
             label="Service Unique ID"
-            hint="A service ID in form of group/service:version, group and version are optional"
+            :hint="$t('dataIdHint')"
             v-model="service"
           ></v-text-field>
           <v-text-field
@@ -125,26 +125,26 @@
             v-model="application"
           ></v-text-field>
 
-          <v-subheader class="pa-0 mt-3">RULE CONTENT</v-subheader>
+          <v-subheader class="pa-0 mt-3">{{$t('ruleContent')}}</v-subheader>
           <ace-editor v-model="ruleText" :readonly="readonly"></ace-editor>
 
         </v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
-          <v-btn flat @click.native="closeDialog">Close</v-btn>
-          <v-btn depressed color="primary" @click.native="saveItem">Save</v-btn>
+          <v-btn flat @click.native="closeDialog">{{$t('close')}}</v-btn>
+          <v-btn depressed color="primary" @click.native="saveItem">{{$t('save')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
 
     <v-dialog v-model="warn" persistent max-width="500px">
       <v-card>
-        <v-card-title class="headline">{{this.warnTitle}}</v-card-title>
+        <v-card-title class="headline">{{$t(this.warnTitle)}}</v-card-title>
         <v-card-text >{{this.warnText}}</v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
-          <v-btn flat @click.native="closeWarn">CANCLE</v-btn>
-          <v-btn depressed color="primary" @click.native="deleteItem(warnStatus)">CONFIRM</v-btn>
+          <v-btn flat @click.native="closeWarn">{{$t('cancel')}}</v-btn>
+          <v-btn depressed color="primary" @click.native="deleteItem(warnStatus)">{{$t('confirm')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
@@ -192,50 +192,56 @@
         ' - \'=> host != 172.22.3.91\'\n',
       ruleText: '',
       readonly: false,
-      appHeaders: [
-        {
-          text: 'Application Name',
-          value: 'application',
-          align: 'left'
-        },
-        {
-          text: 'Enabled',
-          value: 'enabled',
-          sortable: false
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ],
-      serviceHeaders: [
-        {
-          text: 'Service Name',
-          value: 'service',
-          align: 'left'
-        },
-        {
-          text: 'Group',
-          value: 'group',
-          align: 'left'
-
-        },
-        {
-          text: 'Enabled',
-          value: 'enabled',
-          sortable: false
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ]
+      appHeaders: [],
+      serviceHeaders: []
     }),
     methods: {
+      setAppHeaders: function () {
+        this.appHeaders = [
+          {
+            text: this.$t('appName'),
+            value: 'application',
+            align: 'left'
+          },
+          {
+            text: this.$t('enabled'),
+            value: 'enabled',
+            sortable: false
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
+      setServiceHeaders: function () {
+        this.serviceHeaders = [
+          {
+            text: 'Service Name',
+            value: 'service',
+            align: 'left'
+          },
+          {
+            text: 'Group',
+            value: 'group',
+            align: 'left'
+
+          },
+          {
+            text: 'Enabled',
+            value: 'enabled',
+            sortable: false
+          },
+          {
+            text: 'Operation',
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -433,7 +439,15 @@
         return 'by ' + this.items[this.selected].title
       }
     },
+    watch: {
+      area () {
+        this.setAppHeaders()
+        this.setServiceHeaders()
+      }
+    },
     mounted: function () {
+      this.setAppHeaders()
+      this.setServiceHeaders()
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/governance/TagRule.vue b/dubbo-admin-frontend/src/components/governance/TagRule.vue
index 17b3bb2..6b2f688 100644
--- a/dubbo-admin-frontend/src/components/governance/TagRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/TagRule.vue
@@ -25,9 +25,9 @@
     <v-flex lg12>
       <v-card>
         <v-toolbar flat color="transparent" class="elevation-0">
-          <v-toolbar-title><span class="headline">Search Result</span></v-toolbar-title>
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
           <v-spacer></v-spacer>
-          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">CREATE</v-btn>
+          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">{{$t('create')}}</v-btn>
         </v-toolbar>
 
         <v-card-text class="pa-0" >
@@ -45,7 +45,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon(props.item), props.item)">
                     {{op.icon(props.item)}}
                   </v-icon>
-                  <span>{{op.tooltip(props.item)}}</span>
+                  <span>{{$t(op.tooltip(props.item))}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -57,30 +57,30 @@
     <v-dialog   v-model="dialog" width="800px" persistent >
       <v-card>
         <v-card-title class="justify-center">
-          <span class="headline">Create New Routing Rule</span>
+          <span class="headline">{{$t('createNewTagRule')}}</span>
         </v-card-title>
         <v-card-text >
           <v-text-field
-            label="Application Name"
-            hint="Application name the service belongs to"
+            :label="$t('appName')"
+            :hint="$t('appNameHint')"
             v-model="application"
           ></v-text-field>
 
-          <v-subheader class="pa-0 mt-3">RULE CONTENT</v-subheader>
+          <v-subheader class="pa-0 mt-3">{{$t('ruleContent')}}</v-subheader>
           <ace-editor v-model="ruleText" :readonly="readonly"></ace-editor>
 
         </v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
-          <v-btn flat @click.native="closeDialog">Close</v-btn>
-          <v-btn depressed color="primary" @click.native="saveItem">Save</v-btn>
+          <v-btn flat @click.native="closeDialog">{{$t('close')}}</v-btn>
+          <v-btn depressed color="primary" @click.native="saveItem">{{$t('save')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
 
     <v-dialog v-model="warn" persistent max-width="500px">
       <v-card>
-        <v-card-title class="headline">{{this.warnTitle}}</v-card-title>
+        <v-card-title class="headline">{{$t(this.warnTitle)}}</v-card-title>
         <v-card-text >{{this.warnText}}</v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
@@ -131,26 +131,29 @@
         '   addresses: [192.168.0.2:20882]\n',
       ruleText: '',
       readonly: false,
-      headers: [
-        {
-          text: 'Application Name',
-          value: 'application',
-          align: 'left'
-        },
-        {
-          text: 'Enabled',
-          value: 'enabled',
-          sortable: false
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ]
+      headers: []
     }),
     methods: {
+      setHeaders: function () {
+        this.headers = [
+          {
+            text: this.$t('appName'),
+            value: 'application',
+            align: 'left'
+          },
+          {
+            text: this.$t('enabled'),
+            value: 'enabled',
+            sortable: false
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -310,7 +313,13 @@
     created () {
       this.setHeight()
     },
+    watch: {
+      area () {
+        this.setHeaders()
+      }
+    },
     mounted: function () {
+      this.setHeaders()
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
index 433ba02..53acb79 100644
--- a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
+++ b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
@@ -30,7 +30,7 @@
                   append-icon=""
                   hide-no-data
                   :suffix="queryBy"
-                  label="Search Routing Rule"
+                  :label="$t('searchWeightRule')"
                 ></v-combobox>
                 <v-menu class="hidden-xs-only">
                   <v-btn slot="activator" large icon>
@@ -58,9 +58,9 @@
     <v-flex lg12>
       <v-card>
         <v-toolbar flat color="transparent" class="elevation-0">
-          <v-toolbar-title><span class="headline">Search Result</span></v-toolbar-title>
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
           <v-spacer></v-spacer>
-          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">CREATE</v-btn>
+          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">{{$t('create')}}</v-btn>
         </v-toolbar>
 
         <v-card-text class="pa-0" v-if="selected == 0">
@@ -78,7 +78,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon, props.item)">
                     {{op.icon}}
                   </v-icon>
-                  <span>{{op.tooltip}}</span>
+                  <span>{{$t(op.tooltip)}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -99,7 +99,7 @@
                   <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon, props.item)">
                     {{op.icon}}
                   </v-icon>
-                  <span>{{op.tooltip}}</span>
+                  <span>{{$t(op.tooltip)}}</span>
                 </v-tooltip>
               </td>
             </template>
@@ -111,28 +111,28 @@
     <v-dialog   v-model="dialog" width="800px" persistent >
       <v-card>
         <v-card-title class="justify-center">
-          <span class="headline">Create New Weight Rule</span>
+          <span class="headline">{{$t('createNewWeightRule')}}</span>
         </v-card-title>
         <v-card-text >
           <v-text-field
             label="Service Unique ID"
-            hint="A service ID in form of group/service:version, group and version are optional"
+            :hint="$t('serviceIdHint')"
             v-model="service"
           ></v-text-field>
           <v-text-field
-            label="Application Name"
-            hint="Application name the service belongs to"
+            :label="$t('appName')"
+            :hint="$t('appNameHint')"
             v-model="application"
           ></v-text-field>
-          <v-subheader class="pa-0 mt-3">RULE CONTENT</v-subheader>
+          <v-subheader class="pa-0 mt-3">{{$t('ruleContent')}}</v-subheader>
 
           <ace-editor v-model="ruleText" :readonly="readonly"></ace-editor>
 
         </v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
-          <v-btn color="darken-1" flat @click.native="closeDialog">Close</v-btn>
-          <v-btn color="primary darken-1" depressed @click.native="saveItem">Save</v-btn>
+          <v-btn color="darken-1" flat @click.native="closeDialog">{{$t('close')}}</v-btn>
+          <v-btn color="primary darken-1" depressed @click.native="saveItem">{{$t('save')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
@@ -143,8 +143,8 @@
         <v-card-text >{{this.warnText}}</v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
-          <v-btn color="darken-1" flat @click.native="closeWarn">CANCLE</v-btn>
-          <v-btn color="primary darken-1" depressed @click.native="deleteItem(warnStatus)">CONFIRM</v-btn>
+          <v-btn color="darken-1" flat @click.native="closeWarn">{{$t('cancel')}}</v-btn>
+          <v-btn color="primary darken-1" depressed @click.native="deleteItem(warnStatus)">{{$t('confirm')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
@@ -194,46 +194,52 @@
         '  - 192.168.0.2',
       ruleText: '',
       readonly: false,
-      serviceHeaders: [
-        {
-          text: 'Service Name',
-          value: 'service',
-          align: 'left'
-        },
-        {
-          text: 'Weight',
-          value: 'weight',
-          align: 'left'
-
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ],
-      appHeaders: [
-        {
-          text: 'Application Name',
-          value: 'application',
-          align: 'left'
-        },
-        {
-          text: 'Weight',
-          value: 'weight',
-          align: 'left'
-
-        },
-        {
-          text: 'Operation',
-          value: 'operation',
-          sortable: false,
-          width: '115px'
-        }
-      ]
+      serviceHeaders: [],
+      appHeaders: []
     }),
     methods: {
+      setServiceHeaders: function () {
+        this.serviceHeaders = [
+          {
+            text: this.$t('serviceName'),
+            value: 'service',
+            align: 'left'
+          },
+          {
+            text: this.$t('weight'),
+            value: 'weight',
+            align: 'left'
+
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
+      setAppHeaders: function () {
+        this.appHeaders = [
+          {
+            text: this.$t('appName'),
+            value: 'application',
+            align: 'left'
+          },
+          {
+            text: this.$t('weight'),
+            value: 'weight',
+            align: 'left'
+
+          },
+          {
+            text: this.$t('operation'),
+            value: 'operation',
+            sortable: false,
+            width: '115px'
+          }
+        ]
+      },
       submit: function () {
         this.filter = document.querySelector('#serviceSearch').value.trim()
         this.search(this.filter, true)
@@ -386,7 +392,15 @@
         return 'by ' + this.items[this.selected].title
       }
     },
+    watch: {
+      area () {
+        this.setAppHeaders()
+        this.setServiceHeaders()
+      }
+    },
     mounted: function () {
+      this.setAppHeaders()
+      this.setServiceHeaders()
       this.ruleText = this.template
       let query = this.$route.query
       let filter = null
diff --git a/dubbo-admin-frontend/src/components/public/Drawer.vue b/dubbo-admin-frontend/src/components/public/Drawer.vue
index 9f9cb8e..7f92ed8 100644
--- a/dubbo-admin-frontend/src/components/public/Drawer.vue
+++ b/dubbo-admin-frontend/src/components/public/Drawer.vue
@@ -36,14 +36,14 @@
         <v-list-group v-if="item.items" :group="item.group" :prepend-icon="item.icon" no-action>
           <v-list-tile slot="activator" ripple>
             <v-list-tile-content>
-              <v-list-tile-title>{{ item.title }}</v-list-tile-title>
+              <v-list-tile-title>{{ $t(item.title) }}</v-list-tile-title>
             </v-list-tile-content>
           </v-list-tile>
 
           <template v-for="(subItem, i) in item.items">
             <v-list-tile :to="subItem.path" ripple>
               <v-list-tile-content>
-                <v-list-tile-title>{{ subItem.title }}</v-list-tile-title>
+                <v-list-tile-title>{{ $t(subItem.title) }}</v-list-tile-title>
               </v-list-tile-content>
               <v-chip v-if="subItem.badge" color="primary" class="v-chip--x-small" disabled text-color="white">
                 {{subItem.badge}}
@@ -56,7 +56,7 @@
           <v-list-tile-action>
             <v-icon>{{ item.icon }}</v-icon>
           </v-list-tile-action>
-          <v-list-tile-content>{{ item.title }}</v-list-tile-content>
+          <v-list-tile-content>{{ $t(item.title) }}</v-list-tile-content>
           <v-chip v-if="item.badge" color="primary" class="v-chip--x-small" disabled text-color="white">
             {{item.badge}}
           </v-chip>
diff --git a/dubbo-admin-frontend/src/components/public/Toolbar.vue b/dubbo-admin-frontend/src/components/public/Toolbar.vue
index b32c37a..48ff026 100644
--- a/dubbo-admin-frontend/src/components/public/Toolbar.vue
+++ b/dubbo-admin-frontend/src/components/public/Toolbar.vue
@@ -34,12 +34,32 @@
     </v-text-field>
 
     <v-spacer></v-spacer>
+
+    <!--settings button-->
     <v-btn icon v-if="false">
       <v-icon>settings</v-icon>
     </v-btn>
+
+    <!--full screen button-->
     <v-btn icon @click="handleFullScreen()">
       <v-icon>fullscreen</v-icon>
     </v-btn>
+
+    <!--language select button-->
+    <v-menu  attach bottom left offset-y max-height="500">
+      <v-btn flat slot="activator" style="mini-width: 48px">
+        {{selectedLang}}
+      </v-btn>
+      <v-list class="pa-0">
+        <v-list-tile v-for="(item, index) in lang" @click="change(index)" :key="index">
+          <v-list-tile-content>
+            <v-list-tile-title>{{item}}</v-list-tile-title>
+          </v-list-tile-content>
+        </v-list-tile>
+      </v-list>
+    </v-menu>
+
+    <!--notifictation button-->
     <v-menu offset-y origin="center center" class="elelvation-1" :nudge-bottom="14" transition="scale-transition" v-if="false">
       <v-btn icon flat slot="activator">
         <v-badge color="red" overlap>
@@ -49,6 +69,8 @@
       </v-btn>
       <!--<notification-list></notification-list>-->
     </v-menu>
+
+    <!--login user info-->
     <v-menu offset-y origin="center center" :nudge-bottom="10" transition="scale-transition" v-if="false">
       <v-btn icon large flat slot="activator">
         <v-avatar size="30px">
@@ -73,6 +95,11 @@
   export default {
     name: 'toolbar',
     data: () => ({
+      selectedLang: '',
+      lang: [
+        '简体中文',
+        'English'
+      ],
       items: [
         {
           icon: 'account_circle',
@@ -104,12 +131,28 @@
       handleDrawerToggle () {
         window.getApp.$emit('DRAWER_TOGGLED')
       },
+      change (index) {
+        this.selectedLang = this.lang[index]
+        if (index === 0) {
+          this.$i18n.locale = 'zh'
+        } else {
+          this.$i18n.locale = 'en'
+        }
+        this.$store.dispatch('changeArea', { area: this.$i18n.locale })
+      },
       handleTheme () {
         window.getApp.$emit('CHANGE_THEME')
       },
       handleFullScreen () {
         Util.toggleFullScreen()
       }
+    },
+    mounted: function () {
+      if (this.$i18n.locale === 'zh') {
+        this.selectedLang = '简体中文'
+      } else {
+        this.selectedLang = 'English'
+      }
     }
   }
 </script>
diff --git a/dubbo-admin-frontend/src/components/test/ServiceMock.vue b/dubbo-admin-frontend/src/components/test/ServiceMock.vue
index 7fd9a76..42e7e81 100644
--- a/dubbo-admin-frontend/src/components/test/ServiceMock.vue
+++ b/dubbo-admin-frontend/src/components/test/ServiceMock.vue
@@ -21,7 +21,7 @@
       <v-flex lg12>
         <v-card>
           <v-card-title>
-            <h1>Service Mock will release in the future</h1>
+            <h1>Service Test will release in the future</h1>
           </v-card-title>
         </v-card>
       </v-flex>
diff --git a/dubbo-admin-frontend/src/lang/en.js b/dubbo-admin-frontend/src/lang/en.js
new file mode 100644
index 0000000..d747050
--- /dev/null
+++ b/dubbo-admin-frontend/src/lang/en.js
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export default {
+  serviceSearch: 'Service Search',
+  serviceGovernance: 'Service Governance',
+  routingRule: 'Routing Rule',
+  tagRule: 'Tag Rule',
+  dynamicConfig: 'Dynamic Config',
+  accessControl: 'Access Control',
+  weightAdjust: 'Weight Adjust',
+  loadBalance: 'Load Balance',
+  serviceTest: 'Service Test',
+  serviceMock: 'Service Mock',
+  metrics: 'Metrics',
+  group: 'Group',
+  version: 'Version',
+  app: 'Application',
+  appName: 'Application Name',
+  serviceName: 'Service Name',
+  operation: 'Operation',
+  searchResult: 'Search Result',
+  search: 'Search',
+  methodName: 'Method Name',
+  enabled: 'Enabled',
+  disabled: 'Disabled',
+  method: 'Method',
+  weight: 'Weight',
+  create: 'CREATE',
+  save: 'Save',
+  cancel: 'CANCEL',
+  close: 'Close',
+  confirm: 'CONFIRM',
+  ruleContent: 'RULE CONTENT',
+  createNewRoutingRule: 'Create New Routing Rule',
+  createNewTagRule: 'Create New Tag Rule',
+  createNewDynamicConfigRule: 'Create New Dynamic Config Rule',
+  view: 'View',
+  edit: 'Edit',
+  delete: 'Delete',
+  searchRoutingRule: 'Search Routing Rule',
+  searchWeightRUle: 'Search Weight Adjust Rule',
+  dataIdHint: 'A service ID in form of group/service:version, group and version are optional',
+  agree: 'Agree',
+  disagree: 'Disagree',
+  searchDynamicConfig: 'Search Dynamic Config',
+  appNameHint: 'Application name the service belongs to',
+  basicInfo: 'BasicInfo',
+  metaData: 'MetaData',
+  searchDubboService: 'Search Dubbo Services',
+  serviceSearchHint: 'Service ID, org.apache.dubbo.demo.api.DemoService, * for all services',
+  ipSearchHint: 'Find all services provided by the target server on the specified IP address',
+  appSearchHint: 'Input an application name to find all services provided by one particular application, * for all'
+}
diff --git a/dubbo-admin-frontend/src/store/index.js b/dubbo-admin-frontend/src/lang/index.js
similarity index 75%
copy from dubbo-admin-frontend/src/store/index.js
copy to dubbo-admin-frontend/src/lang/index.js
index ffbd5fd..b52011a 100644
--- a/dubbo-admin-frontend/src/store/index.js
+++ b/dubbo-admin-frontend/src/lang/index.js
@@ -14,17 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import Vue from 'vue'
-import Vuex from 'vuex'
+import VueI18n from 'vue-i18n'
+import enLocale from './en'
+import zhLocale from './zh'
 
-Vue.use(Vuex)
+Vue.use(VueI18n)
 
-export const store = new Vuex.Store({
-  state: {
-    appTitle: 'Dubbo OPS'
+const messages = {
+  en: {
+    ...enLocale
   },
-  mutations: {},
-  actions: {},
-  getters: {}
+  zh: {
+    ...zhLocale
+  }
+}
+
+export default new VueI18n({
+  locale: 'zh',
+  selectedLang: '简体中文',
+  messages
 })
diff --git a/dubbo-admin-frontend/src/lang/zh.js b/dubbo-admin-frontend/src/lang/zh.js
new file mode 100644
index 0000000..b0d8f3b
--- /dev/null
+++ b/dubbo-admin-frontend/src/lang/zh.js
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export default {
+  serviceSearch: '服务查询',
+  serviceGovernance: '服务治理',
+  routingRule: 'Routing Rule',
+  tagRule: 'Tag Rule',
+  dynamicConfig: 'Dynamic Config',
+  accessControl: 'Access Control',
+  weightAdjust: 'Weight Adjust',
+  loadBalance: 'Load Balance',
+  serviceTest: 'Service Test',
+  serviceMock: 'Service Mock',
+  metrics: 'Metrics',
+  group: 'Group',
+  version: 'Version',
+  app: 'Application',
+  appName: 'Application Name',
+  serviceName: 'Service Name',
+  operation: 'Operation',
+  searchResult: 'Search Result',
+  search: 'Search',
+  methodName: 'Method Name',
+  enabled: 'Enabled',
+  disabled: 'Disabled',
+  method: 'Method',
+  weight: 'Weight',
+  create: 'CREATE',
+  save: 'Save',
+  cancel: 'CANCEL',
+  close: 'Close',
+  confirm: 'CONFIRM',
+  ruleContent: 'RULE CONTENT',
+  createNewRoutingRule: 'Create New Routing Rule',
+  createNewTagRule: 'Create New Tag Rule',
+  createNewDynamicConfigRule: 'Create New Dynamic Config Rule',
+  view: 'View',
+  edit: 'Edit',
+  delete: 'Delete',
+  searchRoutingRule: 'Search Routing Rule',
+  searchWeightRUle: 'Search Weight Adjust Rule',
+  dataIdHint: 'A service ID in form of group/service:version, group and version are optional',
+  agree: 'Agree',
+  disagree: 'Disagree',
+  searchDynamicConfig: 'Search Dynamic Config',
+  appNameHint: 'Application name the service belongs to',
+  basicInfo: 'BasicInfo',
+  metaData: 'MetaData',
+  searchDubboService: 'Search Dubbo Services',
+  serviceSearchHint: 'Service ID, org.apache.dubbo.demo.api.DemoService, * for all services',
+  ipSearchHint: 'Find all services provided by the target server on the specified IP address',
+  appSearchHint: 'Input an application name to find all services provided by one particular application, * for all'
+}
diff --git a/dubbo-admin-frontend/src/main.js b/dubbo-admin-frontend/src/main.js
index 4eeb96d..f323034 100644
--- a/dubbo-admin-frontend/src/main.js
+++ b/dubbo-admin-frontend/src/main.js
@@ -25,9 +25,11 @@ import 'vuetify/dist/vuetify.min.css'
 import { store } from './store'
 import Notify from './components/public/notify'
 import { AXIOS } from './components/http-common'
+import i18n from './lang'
 
 Vue.use(Vuetify)
 Vue.use(Notify)
+
 Vue.prototype.$axios = AXIOS
 
 Vue.config.productionTip = false
@@ -37,6 +39,7 @@ new Vue({
   el: '#app',
   router,
   store,
+  i18n,
   components: { App },
   template: '<App/>'
 })
diff --git a/dubbo-admin-frontend/src/store/index.js b/dubbo-admin-frontend/src/store/index.js
index ffbd5fd..a631f4d 100644
--- a/dubbo-admin-frontend/src/store/index.js
+++ b/dubbo-admin-frontend/src/store/index.js
@@ -22,9 +22,18 @@ Vue.use(Vuex)
 
 export const store = new Vuex.Store({
   state: {
-    appTitle: 'Dubbo OPS'
+    appTitle: 'Dubbo OPS',
+    area: null
+  },
+  mutations: {
+    setArea (state, area) {
+      state.area = area
+    }
+  },
+  actions: {
+    changeArea ({commit}, area) {
+      commit('setArea', area)
+    }
   },
-  mutations: {},
-  actions: {},
   getters: {}
 })
diff --git a/dubbo-admin-frontend/src/store/index.js b/dubbo-admin-frontend/src/util/i18n.js
similarity index 80%
copy from dubbo-admin-frontend/src/store/index.js
copy to dubbo-admin-frontend/src/util/i18n.js
index ffbd5fd..3654cb8 100644
--- a/dubbo-admin-frontend/src/store/index.js
+++ b/dubbo-admin-frontend/src/util/i18n.js
@@ -14,17 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import Vue from 'vue'
-import Vuex from 'vuex'
-
-Vue.use(Vuex)
-
-export const store = new Vuex.Store({
-  state: {
-    appTitle: 'Dubbo OPS'
-  },
-  mutations: {},
-  actions: {},
-  getters: {}
-})
+export function translateTitle (title) {
+  let key = this.$te('drawer.' + title)
+  if (key) {
+    let newTitle = this.$t('drawer.' + title)
+    return newTitle
+  }
+  return title
+}