Browse Source

chore: add international components

richard1015 3 years ago
parent
commit
1b8d75955f
47 changed files with 464 additions and 229 deletions
  1. 4 4
      src/packages/__VUE/calendar/index.taro.vue
  2. 4 4
      src/packages/__VUE/calendar/index.vue
  3. 18 15
      src/packages/__VUE/calendaritem/index.taro.vue
  4. 18 15
      src/packages/__VUE/calendaritem/index.vue
  5. 2 2
      src/packages/__VUE/cascader/cascader-item.vue
  6. 7 4
      src/packages/__VUE/countdown/index.taro.vue
  7. 7 4
      src/packages/__VUE/countdown/index.vue
  8. 2 2
      src/packages/__VUE/datepicker/index.taro.vue
  9. 2 2
      src/packages/__VUE/datepicker/index.vue
  10. 2 2
      src/packages/__VUE/dialog/__tests__/dialog.ts
  11. 7 6
      src/packages/__VUE/dialog/index.taro.vue
  12. 2 2
      src/packages/__VUE/dialog/index.ts
  13. 7 6
      src/packages/__VUE/dialog/index.vue
  14. 5 4
      src/packages/__VUE/empty/index.taro.vue
  15. 5 4
      src/packages/__VUE/empty/index.vue
  16. 8 12
      src/packages/__VUE/fixednav/index.taro.vue
  17. 8 12
      src/packages/__VUE/fixednav/index.vue
  18. 1 5
      src/packages/__VUE/form/common.ts
  19. 6 5
      src/packages/__VUE/input/index.taro.vue
  20. 6 5
      src/packages/__VUE/input/index.vue
  21. 8 5
      src/packages/__VUE/numberkeyboard/index.taro.vue
  22. 8 5
      src/packages/__VUE/numberkeyboard/index.vue
  23. 7 6
      src/packages/__VUE/pagination/index.taro.vue
  24. 7 6
      src/packages/__VUE/pagination/index.vue
  25. 9 6
      src/packages/__VUE/picker/index.taro.vue
  26. 9 6
      src/packages/__VUE/picker/index.vue
  27. 5 4
      src/packages/__VUE/searchbar/index.taro.vue
  28. 5 4
      src/packages/__VUE/searchbar/index.vue
  29. 12 11
      src/packages/__VUE/shortpassword/index.taro.vue
  30. 12 11
      src/packages/__VUE/shortpassword/index.vue
  31. 3 2
      src/packages/__VUE/table/common.ts
  32. 3 3
      src/packages/__VUE/table/index.taro.vue
  33. 3 3
      src/packages/__VUE/table/index.vue
  34. 5 5
      src/packages/__VUE/textarea/demo.vue
  35. 5 4
      src/packages/__VUE/textarea/index.taro.vue
  36. 5 4
      src/packages/__VUE/textarea/index.vue
  37. 10 10
      src/packages/__VUE/toast/demo.vue
  38. 8 8
      src/packages/__VUE/uploader/index.taro.vue
  39. 7 7
      src/packages/__VUE/uploader/index.vue
  40. 5 4
      src/packages/__VUE/video/index.vue
  41. 24 0
      src/packages/locale/README.md
  42. 20 0
      src/packages/locale/index.ts
  43. 48 0
      src/packages/locale/lang/baseLang.ts
  44. 50 0
      src/packages/locale/lang/en-US.ts
  45. 50 0
      src/packages/locale/lang/zh-CN.ts
  46. 7 0
      src/packages/utils/create/component.ts
  47. 8 0
      src/packages/utils/util.ts

+ 4 - 4
src/packages/__VUE/calendar/index.taro.vue

@@ -119,19 +119,19 @@ export default create({
     },
     title: {
       type: String,
-      default: '日历选择'
+      default: ''
     },
     confirmText: {
       type: String,
-      default: '确认'
+      default: ''
     },
     startText: {
       type: String,
-      default: '开始'
+      default: ''
     },
     endText: {
       type: String,
-      default: '结束'
+      default: ''
     },
     defaultValue: {
       type: [String, Array]

+ 4 - 4
src/packages/__VUE/calendar/index.vue

@@ -122,19 +122,19 @@ export default create({
     },
     title: {
       type: String,
-      default: '日历选择'
+      default: ''
     },
     confirmText: {
       type: String,
-      default: '确认'
+      default: ''
     },
     startText: {
       type: String,
-      default: '开始'
+      default: ''
     },
     endText: {
       type: String,
-      default: '结束'
+      default: ''
     },
     defaultValue: {
       type: [String, Array]

+ 18 - 15
src/packages/__VUE/calendaritem/index.taro.vue

@@ -8,7 +8,7 @@
   >
     <!-- header -->
     <view class="nut-calendar-header" :class="{ 'nut-calendar-header-tile': !poppable }">
-      <view class="calendar-title" v-if="showTitle">{{ title }}</view>
+      <view class="calendar-title" v-if="showTitle">{{ title || translate('title') }}</view>
       <view class="calendar-top-slot" v-if="showTopBtn">
         <slot name="btn"> </slot>
       </view>
@@ -45,11 +45,13 @@
                     <view class="calendar-curr-tips calendar-curr-tips-bottom" v-if="bottomInfo">
                       <slot name="bottomInfo" :date="day.type == 'curr' ? day : ''"> </slot>
                     </view>
-                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(day)"> 今天 </view>
+                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(day)">
+                      {{ translate('today') }}
+                    </view>
                     <view :class="{ 'calendar-curr-tips-top': rangeTip(day, month), 'calendar-day-tip': true }">
-                      {{ isStartTip(day, month) ? startText : '' }}
+                      {{ isStartTip(day, month) ? startText || translate('start') : '' }}
                     </view>
-                    <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText }}</view>
+                    <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText || translate('end') }}</view>
                   </view>
                 </template>
               </view>
@@ -60,14 +62,14 @@
     </scroll-view>
     <!-- footer-->
     <view class="nut-calendar-footer" v-if="poppable && !isAutoBackFill">
-      <view class="calendar-confirm-btn" @click="confirm">{{ confirmText }}</view>
+      <view class="calendar-confirm-btn" @click="confirm">{{ confirmText || translate('confirm') }}</view>
     </view>
   </view>
 </template>
 <script lang="ts">
 import { PropType, reactive, ref, watch, toRefs, computed, onMounted, nextTick } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('calendar-item');
+const { create, translate } = createComponent('calendar-item');
 import Taro from '@tarojs/taro';
 import Utils from '../../utils/date';
 import requestAniFrame from '../../utils/raf';
@@ -141,19 +143,19 @@ export default create({
     },
     title: {
       type: String,
-      default: '日历选择'
+      default: ''
     },
     confirmText: {
       type: String,
-      default: '确认'
+      default: ''
     },
     startText: {
       type: String,
-      default: '开始'
+      default: ''
     },
     endText: {
       type: String,
-      default: '结束'
+      default: ''
     },
     defaultValue: {
       type: [String, Array],
@@ -171,7 +173,7 @@ export default create({
   emits: ['choose', 'update', 'close', 'select'],
 
   setup(props, { emit, slots }) {
-    const weeks = ref(['日', '一', '二', '三', '四', '五', '六']);
+    const weeks = ref(translate('weekdays'));
     // element refs
     const scalePx = ref(2);
     const viewHeight = ref(0);
@@ -394,7 +396,7 @@ export default create({
       };
       const monthInfo: MonthInfo = {
         curData: curData,
-        title: `${title.year}年${title.month}月`,
+        title: translate('monthTitle', title.year, title.month),
         monthData: [
           ...(getPreDaysStatus(preMonthDays, 'prev', { month: preMonth, year: preYear }, preCurrMonthDays) as Day[]),
           ...(getDaysStatus(currMonthDays, 'curr', title) as Day[])
@@ -504,11 +506,11 @@ export default create({
       let current = 0;
       let lastCurrent = 0;
       state.monthsData.forEach((item, index) => {
-        if (item.title == `${state.defaultData[0]}年${state.defaultData[1]}月`) {
+        if (item.title == translate('monthTitle', state.defaultData[0], state.defaultData[1])) {
           current = index;
         }
         if (state.isRange) {
-          if (item.title == `${state.defaultData[3]}年${state.defaultData[4]}月`) {
+          if (item.title == translate('monthTitle', state.defaultData[3], state.defaultData[4])) {
             lastCurrent = index;
           }
         }
@@ -686,7 +688,8 @@ export default create({
       confirm,
       months,
       ...toRefs(state),
-      ...toRefs(props)
+      ...toRefs(props),
+      translate
     };
   }
 });

+ 18 - 15
src/packages/__VUE/calendaritem/index.vue

@@ -8,7 +8,7 @@
   >
     <!-- header -->
     <view class="nut-calendar-header" :class="{ 'nut-calendar-header-tile': !poppable }">
-      <view class="calendar-title" v-if="showTitle">{{ title }}</view>
+      <view class="calendar-title" v-if="showTitle">{{ title || translate('title') }}</view>
       <view class="calendar-top-slot" v-if="showTopBtn">
         <slot name="btn"> </slot>
       </view>
@@ -39,15 +39,17 @@
                     <view class="calendar-curr-tips calendar-curr-tips-bottom" v-if="bottomInfo">
                       <slot name="bottomInfo" :date="day.type == 'curr' ? day : ''"> </slot>
                     </view>
-                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(day)"> 今天 </view>
+                    <view class="calendar-curr-tip-curr" v-if="!bottomInfo && showToday && isCurrDay(day)">
+                      {{ translate('today') }}</view
+                    >
                     <view
                       class="calendar-day-tip"
                       :class="{ 'calendar-curr-tips-top': rangeTip(day, month) }"
                       v-if="isStartTip(day, month)"
                     >
-                      {{ startText }}
+                      {{ startText || translate('start') }}
                     </view>
-                    <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText }}</view>
+                    <view class="calendar-day-tip" v-if="isEndTip(day, month)">{{ endText || translate('end') }}</view>
                   </view>
                 </template>
               </view>
@@ -58,14 +60,14 @@
     </view>
     <!-- footer-->
     <view class="nut-calendar-footer" v-if="poppable && !isAutoBackFill">
-      <view class="calendar-confirm-btn" @click="confirm">{{ confirmText }}</view>
+      <view class="calendar-confirm-btn" @click="confirm">{{ confirmText || translate('confirm') }}</view>
     </view>
   </view>
 </template>
 <script lang="ts">
 import { PropType, reactive, ref, watch, toRefs, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('calendar-item');
+const { create, translate } = createComponent('calendar-item');
 import Utils from '../../utils/date';
 import requestAniFrame from '../../utils/raf';
 type InputDate = string | string[];
@@ -133,19 +135,19 @@ export default create({
     },
     title: {
       type: String,
-      default: '日历选择'
+      default: ''
     },
     confirmText: {
       type: String,
-      default: '确认'
+      default: ''
     },
     startText: {
       type: String,
-      default: '开始'
+      default: ''
     },
     endText: {
       type: String,
-      default: '结束'
+      default: ''
     },
     defaultValue: {
       type: [String, Array],
@@ -163,7 +165,7 @@ export default create({
   emits: ['choose', 'update', 'close', 'select'],
 
   setup(props, { emit, slots }) {
-    const weeks = ref(['日', '一', '二', '三', '四', '五', '六']);
+    const weeks = ref(translate('weekdays'));
     // element refs
     const months = ref<null | HTMLElement>(null);
     const monthsPanel = ref<null | HTMLElement>(null);
@@ -382,7 +384,7 @@ export default create({
       };
       const monthInfo: MonthInfo = {
         curData: curData,
-        title: `${title.year}年${title.month}月`,
+        title: translate('monthTitle', title.year, title.month),
         monthData: [
           ...(getPreDaysStatus(preMonthDays, 'prev', { month: preMonth, year: preYear }, preCurrMonthDays) as Day[]),
           ...(getDaysStatus(currMonthDays, 'curr', title) as Day[])
@@ -486,11 +488,11 @@ export default create({
       let current = 0;
       let lastCurrent = 0;
       state.monthsData.forEach((item, index) => {
-        if (item.title == `${state.defaultData[0]}年${state.defaultData[1]}月`) {
+        if (item.title == translate('monthTitle', state.defaultData[0], state.defaultData[1])) {
           current = index;
         }
         if (state.isRange) {
-          if (item.title == `${state.defaultData[3]}年${state.defaultData[4]}月`) {
+          if (item.title == translate('monthTitle', state.defaultData[3], state.defaultData[4])) {
             lastCurrent = index;
           }
         }
@@ -644,7 +646,8 @@ export default create({
       weeksPanel,
       viewArea,
       ...toRefs(state),
-      ...toRefs(props)
+      ...toRefs(props),
+      translate
     };
   }
 });

+ 2 - 2
src/packages/__VUE/cascader/cascader-item.vue

@@ -31,7 +31,7 @@
 <script lang="ts">
 import { watch, ref, Ref, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('cascader-item');
+const { create, translate } = createComponent('cascader-item');
 import { convertListToOptions } from './helper';
 import { CascaderPane, CascaderOption, CascaderValue, convertConfig } from './types';
 import Tree from './tree';
@@ -273,7 +273,7 @@ export default create({
         tabsCursor.value = tab.paneKey as number;
       },
       formatTabTitle(pane: CascaderPane) {
-        return pane.selectedNode ? pane.selectedNode.text : '请选择';
+        return pane.selectedNode ? pane.selectedNode.text : translate('select');
       },
       isSelected(pane: CascaderPane, node: CascaderOption) {
         return pane.selectedNode && pane.selectedNode.value === node.value;

+ 7 - 4
src/packages/__VUE/countdown/index.taro.vue

@@ -9,7 +9,7 @@
     <template v-else>
       <template v-if="resttime.d >= 0 && showDays">
         <view class="nut-cd-block">{{ resttime.d }}</view>
-        <view class="nut-cd-dot"></view>
+        <view class="nut-cd-dot">{{ translate('day') }}</view>
       </template>
       <view class="nut-cd-block">{{ resttime.h }}</view
       ><view class="nut-cd-dot">:</view><view class="nut-cd-block">{{ resttime.m }}</view
@@ -32,7 +32,7 @@ import {
   vModelText
 } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('countdown');
+const { componentName, create, translate } = createComponent('countdown');
 
 export default create({
   props: {
@@ -96,7 +96,9 @@ export default create({
     const plainText = computed(() => {
       const { d, h, m, s } = resttime.value;
 
-      return `${d > 0 && props.showDays ? d + '天' + h : h}小时${m}分${s}秒`;
+      return `${d > 0 && props.showDays ? d + translate('day') + h : h}${translate('hour')}${m}${translate(
+        'minute'
+      )}${s}${translate('second')}`;
     });
 
     watch(
@@ -229,7 +231,8 @@ export default create({
       getTimeStamp,
       initTimer,
       resttime,
-      plainText
+      plainText,
+      translate
     };
   }
 });

+ 7 - 4
src/packages/__VUE/countdown/index.vue

@@ -9,7 +9,7 @@
     <template v-else>
       <template v-if="resttime.d >= 0 && showDays">
         <view class="nut-cd-block">{{ resttime.d }}</view>
-        <view class="nut-cd-dot"></view>
+        <view class="nut-cd-dot">{{ translate('day') }}</view>
       </template>
       <view class="nut-cd-block">{{ resttime.h }}</view
       ><view class="nut-cd-dot">:</view><view class="nut-cd-block">{{ resttime.m }}</view
@@ -32,7 +32,7 @@ import {
   vModelText
 } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('countdown');
+const { componentName, create, translate } = createComponent('countdown');
 
 export default create({
   props: {
@@ -94,7 +94,9 @@ export default create({
     const plainText = computed(() => {
       const { d, h, m, s } = resttime.value;
 
-      return `${d > 0 && props.showDays ? d + '天' + h : h}小时${m}分${s}秒`;
+      return `${d > 0 && props.showDays ? d + translate('day') + h : h}${translate('hour')}${m}${translate(
+        'minute'
+      )}${s}${translate('second')}`;
     });
 
     watch(
@@ -228,7 +230,8 @@ export default create({
       getTimeStamp,
       initTimer,
       resttime,
-      plainText
+      plainText,
+      translate
     };
   }
 });

+ 2 - 2
src/packages/__VUE/datepicker/index.taro.vue

@@ -51,11 +51,11 @@ export default create({
     },
     okText: {
       type: String,
-      default: '确定'
+      default: ''
     },
     cancelText: {
       type: String,
-      default: '取消'
+      default: ''
     },
     type: {
       type: String,

+ 2 - 2
src/packages/__VUE/datepicker/index.vue

@@ -51,11 +51,11 @@ export default create({
     },
     okText: {
       type: String,
-      default: '确定'
+      default: ''
     },
     cancelText: {
       type: String,
-      default: '取消'
+      default: ''
     },
     type: {
       type: String,

+ 2 - 2
src/packages/__VUE/dialog/__tests__/dialog.ts

@@ -3,8 +3,8 @@ import { render, createVNode, h } from 'vue';
 export class DialogOptions {
   title?: string = '';
   content?: string = '';
-  cancelText?: string = '取消';
-  okText?: string = '确定';
+  cancelText?: string = '';
+  okText?: string = '';
   textAlign?: string = 'center';
   teleport?: String | HTMLElement = 'body';
   id?: string | number = new Date().getTime();

+ 7 - 6
src/packages/__VUE/dialog/index.taro.vue

@@ -30,7 +30,7 @@
             v-if="!noCancelBtn"
             @click="onCancel"
           >
-            {{ cancelText }}
+            {{ cancelText || translate('cancel') }}
           </nut-button>
           <nut-button
             v-if="!noOkBtn"
@@ -41,7 +41,7 @@
             :disabled="okBtnDisabled"
             @click="onOk"
           >
-            {{ okText }}
+            {{ okText || translate('confirm') }}
           </nut-button>
         </template>
       </view>
@@ -51,7 +51,7 @@
 <script lang="ts">
 import { onMounted, computed, watch, ref } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('dialog');
+const { componentName, create, translate } = createComponent('dialog');
 import Popup, { popupProps } from '../popup/index.taro.vue';
 import Button from '../button/index.taro.vue';
 export default create({
@@ -88,11 +88,11 @@ export default create({
     },
     cancelText: {
       type: String,
-      default: '取消'
+      default: ''
     },
     okText: {
       type: String,
-      default: '确定'
+      default: ''
     },
     okBtnDisabled: {
       type: Boolean,
@@ -166,7 +166,8 @@ export default create({
       classes,
       onCancel,
       onOk,
-      showPopup
+      showPopup,
+      translate
     };
   }
 });

+ 2 - 2
src/packages/__VUE/dialog/index.ts

@@ -3,8 +3,8 @@ import { render, createVNode, h } from 'vue';
 export class DialogOptions {
   title?: string = '';
   content?: string = '';
-  cancelText?: string = '取消';
-  okText?: string = '确定';
+  cancelText?: string = '';
+  okText?: string = '';
   textAlign?: string = 'center';
   teleport?: String | HTMLElement = 'body';
   id?: string | number = new Date().getTime();

+ 7 - 6
src/packages/__VUE/dialog/index.vue

@@ -30,7 +30,7 @@
             v-if="!noCancelBtn"
             @click="onCancel"
           >
-            {{ cancelText }}
+            {{ cancelText || translate('cancel') }}
           </nut-button>
           <nut-button
             v-if="!noOkBtn"
@@ -41,7 +41,7 @@
             :disabled="okBtnDisabled"
             @click="onOk"
           >
-            {{ okText }}
+            {{ okText || translate('confirm') }}
           </nut-button>
         </template>
       </view>
@@ -51,7 +51,7 @@
 <script lang="ts">
 import { onMounted, computed, watch, ref } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('dialog');
+const { componentName, create, translate } = createComponent('dialog');
 import Popup, { popupProps } from '../popup/index.vue';
 import Button from '../button/index.vue';
 export default create({
@@ -88,11 +88,11 @@ export default create({
     },
     cancelText: {
       type: String,
-      default: '取消'
+      default: ''
     },
     okText: {
       type: String,
-      default: '确定'
+      default: ''
     },
     okBtnDisabled: {
       type: Boolean,
@@ -166,7 +166,8 @@ export default create({
       classes,
       onCancel,
       onOk,
-      showPopup
+      showPopup,
+      translate
     };
   }
 });

+ 5 - 4
src/packages/__VUE/empty/index.taro.vue

@@ -15,7 +15,7 @@
       <slot name="description"></slot>
     </template>
     <template v-else>
-      <view class="nut-empty-description">{{ description }}</view>
+      <view class="nut-empty-description">{{ description || translate('noData') }}</view>
     </template>
 
     <!-- 自定义slot -->
@@ -27,7 +27,7 @@
 <script lang="ts">
 import { toRefs, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('empty');
+const { componentName, create, translate } = createComponent('empty');
 
 type statusOptions = {
   [key: string]: string;
@@ -54,7 +54,7 @@ export default create({
     },
     description: {
       type: String, // 文字区
-      default: '无内容'
+      default: ''
     }
   },
 
@@ -81,7 +81,8 @@ export default create({
 
     return {
       imageUrl,
-      imgStyle
+      imgStyle,
+      translate
     };
   }
 });

+ 5 - 4
src/packages/__VUE/empty/index.vue

@@ -15,7 +15,7 @@
       <slot name="description"></slot>
     </template>
     <template v-else>
-      <view class="nut-empty-description">{{ description }}</view>
+      <view class="nut-empty-description">{{ description || translate('noData') }}</view>
     </template>
 
     <!-- 自定义slot -->
@@ -27,7 +27,7 @@
 <script lang="ts">
 import { toRefs, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('empty');
+const { componentName, create, translate } = createComponent('empty');
 
 type statusOptions = {
   [key: string]: string;
@@ -54,7 +54,7 @@ export default create({
     },
     description: {
       type: String, // 文字区
-      default: '无内容'
+      default: ''
     }
   },
 
@@ -80,7 +80,8 @@ export default create({
 
     return {
       imageUrl,
-      imgStyle
+      imgStyle,
+      translate
     };
   }
 });

+ 8 - 12
src/packages/__VUE/fixednav/index.taro.vue

@@ -1,11 +1,6 @@
 <template>
   <view :class="classes" :style="position">
-    <nut-overlay
-      v-if="overlay"
-      :visible="visible"
-      :z-index="200"
-      @click="updateValue(false)"
-    />
+    <nut-overlay v-if="overlay" :visible="visible" :z-index="200" @click="updateValue(false)" />
     <slot name="list">
       <view class="nut-fixednav__list">
         <view
@@ -23,7 +18,9 @@
     <div class="nut-fixednav__btn" @click="updateValue()">
       <slot name="btn">
         <nut-icon name="left" color="#fff" />
-        <view class="text">{{ visible ? activeText : unActiveText }}</view>
+        <view class="text"
+          >{{ visible ? activeText || translate('activeText') : unActiveText || translate('unActiveText') }}
+        </view>
       </slot>
     </div>
   </view>
@@ -31,7 +28,7 @@
 <script lang="ts">
 import { computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('fixednav');
+const { componentName, create, translate } = createComponent('fixednav');
 import overlay from '../overlay/index.taro.vue';
 export default create({
   components: {
@@ -51,11 +48,11 @@ export default create({
       type: Array
     },
     activeText: {
-      default: '收起导航',
+      default: '',
       type: String
     },
     unActiveText: {
-      default: '快速导航',
+      default: '',
       type: String
     },
     position: {
@@ -72,7 +69,6 @@ export default create({
       type: String
     }
   },
-  components: {},
   emits: ['update:visible', 'selected'],
 
   setup(props, { emit }) {
@@ -95,7 +91,7 @@ export default create({
       });
     };
 
-    return { classes, updateValue, selected };
+    return { classes, updateValue, selected, translate };
   }
 });
 </script>

+ 8 - 12
src/packages/__VUE/fixednav/index.vue

@@ -1,11 +1,6 @@
 <template>
   <view :class="classes" :style="position">
-    <nut-overlay
-      v-if="overlay"
-      :visible="visible"
-      :z-index="200"
-      @click="updateValue(false)"
-    />
+    <nut-overlay v-if="overlay" :visible="visible" :z-index="200" @click="updateValue(false)" />
     <slot name="list">
       <view class="nut-fixednav__list">
         <view
@@ -23,7 +18,9 @@
     <div class="nut-fixednav__btn" @click="updateValue()">
       <slot name="btn">
         <nut-icon name="left" color="#fff" />
-        <view class="text">{{ visible ? activeText : unActiveText }}</view>
+        <view class="text"
+          >{{ visible ? activeText || translate('activeText') : unActiveText || translate('unActiveText') }}
+        </view>
       </slot>
     </div>
   </view>
@@ -32,7 +29,7 @@
 import { computed } from 'vue';
 import { createComponent } from '../../utils/create';
 import overlay from '../overlay/index.vue';
-const { componentName, create } = createComponent('fixednav');
+const { componentName, create, translate } = createComponent('fixednav');
 
 export default create({
   components: {
@@ -52,11 +49,11 @@ export default create({
       type: Array
     },
     activeText: {
-      default: '收起导航',
+      default: '',
       type: String
     },
     unActiveText: {
-      default: '快速导航',
+      default: '',
       type: String
     },
     position: {
@@ -73,7 +70,6 @@ export default create({
       type: String
     }
   },
-  components: {},
   emits: ['update:visible', 'selected'],
 
   setup(props, { emit }) {
@@ -96,7 +92,7 @@ export default create({
       });
     };
 
-    return { classes, updateValue, selected };
+    return { classes, updateValue, selected, translate };
   }
 });
 </script>

+ 1 - 5
src/packages/__VUE/form/common.ts

@@ -1,4 +1,4 @@
-import { isPromise } from '../../utils/util';
+import { getPropByPath, isPromise } from '../../utils/util';
 import { computed, provide, reactive, VNode, watch } from 'vue';
 import { FormItemRule } from '../formitem/types';
 import { ErrorMessage, FormRule } from './types';
@@ -68,10 +68,6 @@ export const component = {
         });
       };
 
-      const getPropByPath = (obj: any, keyPath: string) => {
-        return keyPath.split('.').reduce((prev, curr) => prev[curr], obj);
-      };
-
       if (!prop) {
         console.warn('[NutUI] <FormItem> 使用 rules 校验规则时 , 必须设置 prop 参数');
       }

+ 6 - 5
src/packages/__VUE/input/index.taro.vue

@@ -25,7 +25,7 @@
           ref="inputRef"
           :style="stylesTextarea"
           :maxlength="maxLength"
-          :placeholder="placeholder"
+          :placeholder="placeholder || translate('placeholder')"
           :disabled="disabled"
           :readonly="readonly"
           :value="modelValue"
@@ -42,7 +42,7 @@
           :style="styles"
           :type="inputType(type)"
           :maxNum="maxNum"
-          :placeholder="placeholder"
+          :placeholder="placeholder || translate('placeholder')"
           :disabled="disabled"
           :readonly="readonly"
           :value="modelValue"
@@ -87,7 +87,7 @@ import { PropType, ref, reactive, computed, onMounted, watch, nextTick, inject }
 import { createComponent } from '../../utils/create';
 import { formatNumber } from './util';
 
-const { componentName, create } = createComponent('input');
+const { componentName, create, translate } = createComponent('input');
 interface Events {
   eventName: 'focus' | 'blur' | 'clear' | 'change' | 'update:modelValue';
   params: (string | number | Event)[];
@@ -142,7 +142,7 @@ export default create({
     },
     placeholder: {
       type: String,
-      default: '请输入信息'
+      default: ''
     },
     label: {
       type: String,
@@ -443,7 +443,8 @@ export default create({
       clear,
       onClickInput,
       onClickLeftIcon,
-      onClickRightIcon
+      onClickRightIcon,
+      translate
     };
   }
 });

+ 6 - 5
src/packages/__VUE/input/index.vue

@@ -25,7 +25,7 @@
           ref="inputRef"
           :style="stylesTextarea"
           :maxlength="maxLength"
-          :placeholder="placeholder"
+          :placeholder="placeholder || translate('placeholder')"
           :disabled="disabled"
           :readonly="readonly"
           :value="modelValue"
@@ -42,7 +42,7 @@
           :style="styles"
           :type="inputType(type)"
           :maxNum="maxNum"
-          :placeholder="placeholder"
+          :placeholder="placeholder || translate('placeholder')"
           :disabled="disabled"
           :readonly="readonly"
           :value="modelValue"
@@ -87,7 +87,7 @@ import { PropType, ref, reactive, computed, onMounted, watch, nextTick, inject }
 import { createComponent } from '../../utils/create';
 import { formatNumber } from './util';
 
-const { componentName, create } = createComponent('input');
+const { componentName, create, translate } = createComponent('input');
 interface Events {
   eventName: 'focus' | 'blur' | 'clear' | 'change' | 'update:modelValue';
   params: (string | number | Event)[];
@@ -142,7 +142,7 @@ export default create({
     },
     placeholder: {
       type: String,
-      default: '请输入信息'
+      default: ''
     },
     label: {
       type: String,
@@ -443,7 +443,8 @@ export default create({
       clear,
       onClickInput,
       onClickLeftIcon,
-      onClickRightIcon
+      onClickRightIcon,
+      translate
     };
   }
 });

+ 8 - 5
src/packages/__VUE/numberkeyboard/index.taro.vue

@@ -9,7 +9,7 @@
     <div class="nut-numberkeyboard" ref="root">
       <div class="number-board-header" v-if="title">
         <h3 class="tit">{{ title }}</h3>
-        <span class="keyboard-close" @click="closeBoard()">完成</span>
+        <span class="keyboard-close" @click="closeBoard()">{{ translate('done') }}</span>
       </div>
       <div class="number-board-body">
         <div class="number-board">
@@ -61,7 +61,9 @@
             </div>
           </div>
           <div class="key-board-wrapper" @click="closeBoard()" v-if="title == ''">
-            <div :class="['key', 'finish', { activeFinsh: clickKeyIndex == 'finish' }]"> {{ confirmText }} </div>
+            <div :class="['key', 'finish', { activeFinsh: clickKeyIndex == 'finish' }]">
+              {{ confirmText || translate('done') }}
+            </div>
           </div>
         </div>
       </div>
@@ -72,12 +74,12 @@
 <script lang="ts">
 import { computed, onMounted, provide, reactive, nextTick, ref, watch, Ref } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('numberkeyboard');
+const { create, translate } = createComponent('numberkeyboard');
 export default create({
   props: {
     confirmText: {
       type: String,
-      default: '完成'
+      default: ''
     },
     title: {
       type: String,
@@ -218,7 +220,8 @@ export default create({
       genCustomKeys,
       getBasicKeys,
       root,
-      show
+      show,
+      translate
     };
   }
 });

+ 8 - 5
src/packages/__VUE/numberkeyboard/index.vue

@@ -10,7 +10,7 @@
     <div class="nut-numberkeyboard" ref="root">
       <div class="number-board-header" v-if="title">
         <h3 class="tit">{{ title }}</h3>
-        <span class="keyboard-close" @click="closeBoard()">完成</span>
+        <span class="keyboard-close" @click="closeBoard()">{{ translate('done') }}</span>
       </div>
       <div class="number-board-body">
         <div class="number-board">
@@ -62,7 +62,9 @@
             </div>
           </div>
           <div class="key-board-wrapper key-board-finish" @click="closeBoard()" v-if="title == ''">
-            <div :class="['key', 'finish', { activeFinsh: clickKeyIndex == 'finish' }]"> {{ confirmText }} </div>
+            <div :class="['key', 'finish', { activeFinsh: clickKeyIndex == 'finish' }]">
+              {{ confirmText || translate('done') }}
+            </div>
           </div>
         </div>
       </div>
@@ -73,12 +75,12 @@
 <script lang="ts">
 import { computed, onMounted, provide, reactive, nextTick, ref, watch, Ref } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('numberkeyboard');
+const { create, translate } = createComponent('numberkeyboard');
 export default create({
   props: {
     confirmText: {
       type: String,
-      default: '完成'
+      default: ''
     },
     title: {
       type: String,
@@ -226,7 +228,8 @@ export default create({
       genCustomKeys,
       getBasicKeys,
       root,
-      show
+      show,
+      translate
     };
   }
 });

+ 7 - 6
src/packages/__VUE/pagination/index.taro.vue

@@ -5,7 +5,7 @@
       @click="select(modelValue - 1, true)"
     >
       <slot name="prev-text">
-        {{ prevText }}
+        {{ prevText || translate('prev') }}
       </slot>
     </view>
     <view class="nut-pagination-contain" v-if="mode == 'multi'">
@@ -28,7 +28,7 @@
       @click="select(modelValue + 1, true)"
     >
       <slot name="next-text">
-        {{ nextText }}
+        {{ nextText || translate('next') }}
       </slot>
     </view>
   </view>
@@ -36,7 +36,7 @@
 <script lang="ts">
 import { toRefs, onMounted, watchEffect, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('pagination');
+const { componentName, create, translate } = createComponent('pagination');
 
 export default create({
   props: {
@@ -50,11 +50,11 @@ export default create({
     },
     prevText: {
       type: String,
-      default: '上一页'
+      default: ''
     },
     nextText: {
       type: String,
-      default: '下一页'
+      default: ''
     },
     pageCount: {
       type: [String, Number],
@@ -149,7 +149,8 @@ export default create({
       countRef,
       mode,
       pages,
-      forceEllipses
+      forceEllipses,
+      translate
     };
   }
 });

+ 7 - 6
src/packages/__VUE/pagination/index.vue

@@ -5,7 +5,7 @@
       @click="select(modelValue - 1, true)"
     >
       <slot name="prev-text">
-        {{ prevText }}
+        {{ prevText || translate('prev') }}
       </slot>
     </view>
     <view class="nut-pagination-contain" v-if="mode == 'multi'">
@@ -28,7 +28,7 @@
       @click="select(modelValue + 1, true)"
     >
       <slot name="next-text">
-        {{ nextText }}
+        {{ nextText || translate('next') }}
       </slot>
     </view>
   </view>
@@ -36,7 +36,7 @@
 <script lang="ts">
 import { toRefs, onMounted, watchEffect, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('pagination');
+const { componentName, create, translate } = createComponent('pagination');
 
 export default create({
   props: {
@@ -50,11 +50,11 @@ export default create({
     },
     prevText: {
       type: String,
-      default: '上一页'
+      default: ''
     },
     nextText: {
       type: String,
-      default: '下一页'
+      default: ''
     },
     pageCount: {
       type: [String, Number],
@@ -150,7 +150,8 @@ export default create({
       countRef,
       mode,
       pages,
-      forceEllipses
+      forceEllipses,
+      translate
     };
   }
 });

+ 9 - 6
src/packages/__VUE/picker/index.taro.vue

@@ -10,10 +10,12 @@
       :round="true"
     >
       <view class="nut-picker__bar">
-        <view class="nut-picker__cancel nut-picker__left nut-picker__button" @click="close">{{ cancelText }}</view>
+        <view class="nut-picker__cancel nut-picker__left nut-picker__button" @click="close">{{
+          cancelText || translate('cancel')
+        }}</view>
         <view class="nut-picker__title"> {{ title }}</view>
         <view class="nut-picker__confirm nut-picker__right nut-picker__button" @click="confirmHandler()">{{
-          okText
+          okText || translate('confirm')
         }}</view>
       </view>
 
@@ -43,7 +45,7 @@ import { createComponent } from '../../utils/create';
 import { popupProps } from '../popup/index.taro.vue';
 import column from './ColumnTaro.vue';
 import { PickerOption } from './types';
-const { componentName, create } = createComponent('picker');
+const { componentName, create, translate } = createComponent('picker');
 export default create({
   components: {
     [column.name]: column
@@ -60,11 +62,11 @@ export default create({
     },
     cancelText: {
       type: String,
-      default: '取消'
+      default: ''
     },
     okText: {
       type: String,
-      default: '确定'
+      default: ''
     },
     columns: {
       type: Array,
@@ -243,7 +245,8 @@ export default create({
       close,
       changeHandler,
       confirmHandler,
-      defaultValues
+      defaultValues,
+      translate
     };
   }
 });

+ 9 - 6
src/packages/__VUE/picker/index.vue

@@ -11,10 +11,12 @@
       :isWrapTeleport="isWrapTeleport"
     >
       <view class="nut-picker__bar">
-        <view class="nut-picker__cancel nut-picker__left nut-picker__button" @click="close">{{ cancelText }}</view>
+        <view class="nut-picker__cancel nut-picker__left nut-picker__button" @click="close">{{
+          cancelText || translate('cancel')
+        }}</view>
         <view class="nut-picker__title"> {{ title }}</view>
         <view class="nut-picker__confirm nut-picker__right nut-picker__button" @click="confirmHandler()">{{
-          okText
+          okText || translate('confirm')
         }}</view>
       </view>
 
@@ -44,7 +46,7 @@ import { createComponent } from '../../utils/create';
 import popup, { popupProps } from '../popup/index.vue';
 import column from './Column.vue';
 import { PickerOption } from './types';
-const { componentName, create } = createComponent('picker');
+const { componentName, create, translate } = createComponent('picker');
 export default create({
   components: {
     [column.name]: column,
@@ -62,11 +64,11 @@ export default create({
     },
     cancelText: {
       type: String,
-      default: '取消'
+      default: ''
     },
     okText: {
       type: String,
-      default: '确定'
+      default: ''
     },
     columns: {
       type: Array,
@@ -245,7 +247,8 @@ export default create({
       close,
       changeHandler,
       confirmHandler,
-      defaultValues
+      defaultValues,
+      translate
     };
   }
 });

+ 5 - 4
src/packages/__VUE/searchbar/index.taro.vue

@@ -13,7 +13,7 @@
             class="nut-searchbar__input-bar"
             :type="inputType"
             :maxlength="maxLength"
-            :placeholder="placeholder"
+            :placeholder="placeholder || translate('placeholder')"
             :value="modelValue"
             @input="valueChange"
             @focus="valueFocus"
@@ -38,7 +38,7 @@
 <script lang="ts">
 import { toRefs, reactive, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('searchbar');
+const { create, translate } = createComponent('searchbar');
 interface Events {
   eventName: 'change' | 'focus' | 'blur' | 'clear' | 'update:modelValue';
   params: (string | number | Event)[];
@@ -59,7 +59,7 @@ export default create({
     },
     placeholder: {
       type: String,
-      default: '请输入'
+      default: ''
     },
     clearable: {
       type: Boolean,
@@ -142,7 +142,8 @@ export default create({
       handleClear,
       handleSubmit,
       searchbarStyle,
-      inputSearchbarStyle
+      inputSearchbarStyle,
+      translate
     };
   }
 });

+ 5 - 4
src/packages/__VUE/searchbar/index.vue

@@ -13,7 +13,7 @@
             class="nut-searchbar__input-bar"
             :type="inputType"
             :maxlength="maxLength"
-            :placeholder="placeholder"
+            :placeholder="placeholder || translate('placeholder')"
             :value="modelValue"
             @input="valueChange"
             @focus="valueFocus"
@@ -37,7 +37,7 @@
 <script lang="ts">
 import { toRefs, reactive, computed } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('searchbar');
+const { create, translate } = createComponent('searchbar');
 interface Events {
   eventName: 'change' | 'focus' | 'blur' | 'clear' | 'update:modelValue';
   params: (string | number | Event)[];
@@ -58,7 +58,7 @@ export default create({
     },
     placeholder: {
       type: String,
-      default: '请输入'
+      default: ''
     },
     clearable: {
       type: Boolean,
@@ -141,7 +141,8 @@ export default create({
       handleClear,
       handleSubmit,
       searchbarStyle,
-      inputSearchbarStyle
+      inputSearchbarStyle,
+      translate
     };
   }
 });

+ 12 - 11
src/packages/__VUE/shortpassword/index.taro.vue

@@ -12,8 +12,8 @@
       :close-on-click-overlay="closeOnClickOverlay"
       @click-overlay="close"
     >
-      <view class="nut-shortpsd-title">{{ title }}</view>
-      <view class="nut-shortpsdWx-subtitle">{{ desc }}</view>
+      <view class="nut-shortpsd-title">{{ title || translate('title') }}</view>
+      <view class="nut-shortpsd-subtitle">{{ desc || translate('desc') }}</view>
       <input
         v-if="isWx && visible"
         class="nut-input-real-taro"
@@ -42,14 +42,14 @@
       </view>
       <view class="nut-shortpsd-message">
         <view class="nut-shortpsd-error">{{ errorMsg }}</view>
-        <view class="nut-shortpsd-forget" @click="onTips" v-if="tips">
+        <view class="nut-shortpsd-forget" @click="onTips" v-if="tips || translate('tips')">
           <nut-icon class="icon" size="11px" name="tips"></nut-icon>
-          <view>{{ tips }}</view>
+          <view>{{ tips || translate('tips') }}</view>
         </view>
       </view>
       <view v-if="!noButton" class="nut-shortpsd-footer">
-        <view class="nut-shortpsd-cancle" @click="close">取消</view>
-        <view class="nut-shortpsd-sure" @click="sureClick">确认</view>
+        <view class="nut-shortpsd-cancle" @click="close">{{ translate('cancel') }}</view>
+        <view class="nut-shortpsd-sure" @click="sureClick">{{ translate('confirm') }}</view>
       </view>
     </nut-popup>
   </view>
@@ -57,21 +57,21 @@
 <script lang="ts">
 import { ref, computed, watch, onMounted } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('shortpassword');
+const { create, translate } = createComponent('shortpassword');
 import Taro, { eventCenter, getCurrentInstance } from '@tarojs/taro';
 export default create({
   props: {
     title: {
       type: String,
-      default: '请输入密码'
+      default: ''
     },
     desc: {
       type: String,
-      default: '您使用了虚拟资产,请进行验证'
+      default: ''
     },
     tips: {
       type: String,
-      default: '忘记密码'
+      default: ''
     },
     visible: {
       type: Boolean,
@@ -197,7 +197,8 @@ export default create({
       closeIcon,
       isWx,
       refRandomId,
-      randRef
+      randRef,
+      translate
     };
   }
 });

+ 12 - 11
src/packages/__VUE/shortpassword/index.vue

@@ -13,8 +13,8 @@
       @click-overlay="close"
       :isWrapTeleport="isWrapTeleport"
     >
-      <view class="nut-shortpsd-title">{{ title }}</view>
-      <view class="nut-shortpsd-subtitle">{{ desc }}</view>
+      <view class="nut-shortpsd-title">{{ title || translate('title') }}</view>
+      <view class="nut-shortpsd-subtitle">{{ desc || translate('desc') }}</view>
 
       <div class="nut-input-normalw">
         <input
@@ -35,14 +35,14 @@
       </div>
       <view class="nut-shortpsd-message">
         <view class="nut-shortpsd-error">{{ errorMsg }}</view>
-        <view class="nut-shortpsd-forget" v-if="tips">
+        <view class="nut-shortpsd-forget" v-if="tips || translate('tips')">
           <nut-icon class="icon" size="11px" name="tips"></nut-icon>
-          <view @click="onTips">{{ tips }}</view>
+          <view @click="onTips">{{ tips || translate('tips') }}</view>
         </view>
       </view>
       <view v-if="!noButton" class="nut-shortpsd-footer">
-        <view class="nut-shortpsd-cancle" @click="close">取消</view>
-        <view class="nut-shortpsd-sure" @click="sureClick">确认</view>
+        <view class="nut-shortpsd-cancle" @click="close">{{ translate('cancel') }}</view>
+        <view class="nut-shortpsd-sure" @click="sureClick">{{ translate('confirm') }}</view>
       </view>
     </nut-popup>
   </view>
@@ -50,20 +50,20 @@
 <script lang="ts">
 import { ref, computed, watch, onMounted } from 'vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('shortpassword');
+const { create, translate } = createComponent('shortpassword');
 export default create({
   props: {
     title: {
       type: String,
-      default: '请输入密码'
+      default: ''
     },
     desc: {
       type: String,
-      default: '您使用了虚拟资产,请进行验证'
+      default: ''
     },
     tips: {
       type: String,
-      default: '忘记密码'
+      default: ''
     },
     visible: {
       type: Boolean,
@@ -179,7 +179,8 @@ export default create({
       onTips,
       show,
       systemStyle,
-      closeIcon
+      closeIcon,
+      translate
     };
   }
 });

+ 3 - 2
src/packages/__VUE/table/common.ts

@@ -2,7 +2,7 @@ import { computed, PropType, reactive, toRefs, watch } from 'vue';
 import RenderColumn from './renderColumn';
 import { TableColumnProps } from './types';
 
-export const component = (componentName: string) => {
+export const component = (componentName: string, translate: Function) => {
   return {
     components: {
       RenderColumn
@@ -87,7 +87,8 @@ export const component = (componentName: string) => {
         cellClasses,
         getColumnItem,
         handleSorterClick,
-        sortDataItem
+        sortDataItem,
+        translate
       };
     }
   };

+ 3 - 3
src/packages/__VUE/table/index.taro.vue

@@ -36,7 +36,7 @@
     <view class="nut-table__nodata" v-if="!curData.length">
       <div class="nut-table__nodata" :class="{ 'nut-table__nodata--border': bordered }">
         <slot name="nodata"></slot>
-        <div v-if="!$slots.nodata" class="nut-table__nodata__text"> 暂无数据 </div>
+        <div v-if="!$slots.nodata" class="nut-table__nodata__text"> {{ translate('noData') }} </div>
       </div>
     </view>
   </view>
@@ -44,7 +44,7 @@
 
 <script lang="ts">
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('table');
+const { componentName, create, translate } = createComponent('table');
 import { component } from './common';
-export default create(component(componentName));
+export default create(component(componentName, translate));
 </script>

+ 3 - 3
src/packages/__VUE/table/index.vue

@@ -36,7 +36,7 @@
     <view class="nut-table__nodata" v-if="!curData.length">
       <div class="nut-table__nodata" :class="{ 'nut-table__nodata--border': bordered }">
         <slot name="nodata"></slot>
-        <div v-if="!$slots.nodata" class="nut-table__nodata__text"> 暂无数据 </div>
+        <div v-if="!$slots.nodata" class="nut-table__nodata__text"> {{ translate('noData') }} </div>
       </div>
     </view>
   </view>
@@ -44,7 +44,7 @@
 
 <script lang="ts">
 import { createComponent } from '../../utils/create';
-const { componentName, create } = createComponent('table');
+const { componentName, create, translate } = createComponent('table');
 import { component } from './common';
-export default create(component(componentName));
+export default create(component(componentName, translate));
 </script>

+ 5 - 5
src/packages/__VUE/textarea/demo.vue

@@ -1,17 +1,17 @@
 <template>
   <div class="demo full">
-    <!-- <h2>基础用法</h2> -->
-    <!-- <nut-textarea v-model="value" />
+    <h2>基础用法</h2>
+    <nut-textarea v-model="value" />
     <h2>显示字数统计</h2>
-    <nut-textarea v-model="value2" limit-show max-length="20" /> -->
+    <nut-textarea v-model="value2" limit-show max-length="20" />
     <h2>高度自定义,拉伸</h2>
     <nut-textarea v-model="value3" autosize />
-    <!-- <h2>只读</h2>
+    <h2>只读</h2>
     <nut-textarea readonly model-value="textarea只读状态" />
     <h2>禁用</h2>
     <nut-textarea disabled model-value="textarea禁用状态" limit-show max-length="20" />
     <h2>自动获取焦点</h2>
-    <nut-textarea autofocus v-model="value4" /> -->
+    <nut-textarea autofocus v-model="value4" />
   </div>
 </template>
 

+ 5 - 4
src/packages/__VUE/textarea/index.taro.vue

@@ -16,7 +16,7 @@ onMounted, nextTick, , watch, ref<template>
       @blur="blur"
       @focus="focus"
       :maxlength="maxLength"
-      :placeholder="placeholder"
+      :placeholder="placeholder || translate('placeholder')"
       :auto-focus="autofocus"
     />
     <view class="nut-textarea__limit" v-if="limitShow"> {{ modelValue ? modelValue.length : 0 }}/{{ maxLength }}</view>
@@ -26,7 +26,7 @@ onMounted, nextTick, , watch, ref<template>
 import { computed, nextTick, onMounted, ref, watch } from 'vue';
 import { createComponent } from '../../utils/create';
 
-const { componentName, create } = createComponent('textarea');
+const { componentName, create, translate } = createComponent('textarea');
 
 export default create({
   props: {
@@ -52,7 +52,7 @@ export default create({
     },
     placeholder: {
       type: String,
-      default: '请输入内容'
+      default: ''
     },
     readonly: {
       type: Boolean,
@@ -160,7 +160,8 @@ export default create({
       styles,
       change,
       focus,
-      blur
+      blur,
+      translate
     };
   }
 });

+ 5 - 4
src/packages/__VUE/textarea/index.vue

@@ -12,7 +12,7 @@
       @blur="blur"
       @focus="focus"
       :maxlength="maxLength"
-      :placeholder="placeholder"
+      :placeholder="placeholder || translate('placeholder')"
       :autofocus="autofocus"
     />
     <view class="nut-textarea__limit" v-if="limitShow"> {{ modelValue ? modelValue.length : 0 }}/{{ maxLength }}</view>
@@ -22,7 +22,7 @@
 import { watch, ref, computed, onMounted, nextTick } from 'vue';
 import { createComponent } from '../../utils/create';
 
-const { componentName, create } = createComponent('textarea');
+const { componentName, create, translate } = createComponent('textarea');
 
 export default create({
   props: {
@@ -48,7 +48,7 @@ export default create({
     },
     placeholder: {
       type: String,
-      default: '请输入内容'
+      default: ''
     },
     readonly: {
       type: Boolean,
@@ -159,7 +159,8 @@ export default create({
       styles,
       change,
       focus,
-      blur
+      blur,
+      translate
     };
   }
 });

+ 10 - 10
src/packages/__VUE/toast/demo.vue

@@ -16,44 +16,44 @@
   </div>
 </template>
 
-<script>
+<script lang="ts">
 import { createComponent } from '../../utils/create';
 const { createDemo } = createComponent('toast');
 import { Toast } from '@/packages/nutui.vue';
 export default createDemo({
   setup() {
-    const textToast = (msg) => {
+    const textToast = (msg: string) => {
       Toast.text(msg);
     };
-    const titleToast = (msg) => {
+    const titleToast = (msg: string) => {
       Toast.text(msg, {
         title: '标题文字'
       });
     };
-    const successToast = (msg) => {
+    const successToast = (msg: string) => {
       Toast.success(msg);
     };
-    const errorToast = (msg) => {
+    const errorToast = (msg: string) => {
       Toast.fail(msg);
     };
-    const warningToast = (msg) => {
+    const warningToast = (msg: string) => {
       Toast.warn(msg);
     };
-    const loadingToast = (msg) => {
+    const loadingToast = (msg: string) => {
       Toast.loading(msg);
     };
-    const NoToast = (msg) => {
+    const NoToast = (msg: string) => {
       Toast.text(msg, {
         duration: 0
       });
     };
-    const BottomToast = (msg) => {
+    const BottomToast = (msg: string) => {
       Toast.text(msg, {
         center: false,
         bottom: '10%'
       });
     };
-    const NoLoading = (msg) => {
+    const NoLoading = (msg: string) => {
       Toast.loading(msg, {
         cover: true
       });

+ 8 - 8
src/packages/__VUE/uploader/index.taro.vue

@@ -71,12 +71,12 @@
 import { computed, PropType, reactive } from 'vue';
 import { createComponent } from '../../utils/create';
 import { Uploader, UploadOptions } from './uploader';
-const { componentName, create } = createComponent('uploader');
+const { componentName, create, translate } = createComponent('uploader');
 import Taro from '@tarojs/taro';
 export type FileItemStatus = 'ready' | 'uploading' | 'success' | 'error';
 export class FileItem {
   status: FileItemStatus = 'ready';
-  message: string = '准备完成';
+  message: string = translate('ready');
   uid: string = new Date().getTime().toString();
   url?: string;
   path?: string;
@@ -184,20 +184,20 @@ export default create({
       uploadOption.taroFilePath = fileItem.path;
       uploadOption.onStart = (option: UploadOptions) => {
         fileItem.status = 'ready';
-        fileItem.message = '准备上传';
+        fileItem.message = translate('readyUpload');
         clearUploadQueue(index);
         emit('start', option);
       };
       uploadOption.onProgress = (event: any, option: UploadOptions) => {
         fileItem.status = 'uploading';
-        fileItem.message = '上传中';
+        fileItem.message = translate('uploading');
         fileItem.percentage = event.progress;
         emit('progress', { event, option, percentage: fileItem.percentage });
       };
 
       uploadOption.onSuccess = (data: Taro.uploadFile.SuccessCallbackResult, option: UploadOptions) => {
         fileItem.status = 'success';
-        fileItem.message = '上传成功';
+        fileItem.message = translate('success');
         emit('success', {
           data,
           option,
@@ -207,7 +207,7 @@ export default create({
       };
       uploadOption.onFailure = (data: Taro.uploadFile.SuccessCallbackResult, option: UploadOptions) => {
         fileItem.status = 'error';
-        fileItem.message = '上传失败';
+        fileItem.message = translate('error');
         emit('failure', {
           data,
           option,
@@ -250,7 +250,7 @@ export default create({
         fileItem.path = file.path;
         fileItem.name = file.path;
         fileItem.status = 'ready';
-        fileItem.message = '等待上传';
+        fileItem.message = translate('waitingUpload');
         fileItem.type = fileType;
         if (props.isPreview) {
           fileItem.url = file.path;
@@ -291,7 +291,7 @@ export default create({
           index
         });
       } else {
-        console.log('用户阻止了删除!');
+        // console.log('用户阻止了删除!');
       }
     };
 

+ 7 - 7
src/packages/__VUE/uploader/index.vue

@@ -111,11 +111,11 @@
 import { computed, reactive } from 'vue';
 import { createComponent } from '../../utils/create';
 import { Uploader, UploadOptions } from './uploader';
-const { componentName, create } = createComponent('uploader');
+const { componentName, create, translate } = createComponent('uploader');
 export type FileItemStatus = 'ready' | 'uploading' | 'success' | 'error';
 export class FileItem {
   status: FileItemStatus = 'ready';
-  message: string = '准备完成';
+  message: string = translate('ready');
   uid: string = new Date().getTime().toString();
   name?: string;
   url?: string;
@@ -203,20 +203,20 @@ export default create({
       uploadOption.withCredentials = props.withCredentials;
       uploadOption.onStart = (option: UploadOptions) => {
         fileItem.status = 'ready';
-        fileItem.message = '准备上传';
+        fileItem.message = translate('readyUpload');
         clearUploadQueue(index);
         emit('start', option);
       };
       uploadOption.onProgress = (event: ProgressEvent<XMLHttpRequestEventTarget>, option: UploadOptions) => {
         fileItem.status = 'uploading';
-        fileItem.message = '上传中';
+        fileItem.message = translate('uploading');
         fileItem.percentage = ((event.loaded / event.total) * 100).toFixed(0);
         emit('progress', { event, option, percentage: fileItem.percentage });
       };
 
       uploadOption.onSuccess = (responseText: XMLHttpRequest['responseText'], option: UploadOptions) => {
         fileItem.status = 'success';
-        fileItem.message = '上传成功';
+        fileItem.message = translate('success');
         emit('success', {
           responseText,
           option,
@@ -226,7 +226,7 @@ export default create({
       };
       uploadOption.onFailure = (responseText: XMLHttpRequest['responseText'], option: UploadOptions) => {
         fileItem.status = 'error';
-        fileItem.message = '上传失败';
+        fileItem.message = translate('error');
         emit('failure', {
           responseText,
           option,
@@ -270,7 +270,7 @@ export default create({
         fileItem.status = 'ready';
         fileItem.type = file.type;
         fileItem.formData = formData;
-        fileItem.message = '等待上传';
+        fileItem.message = translate('waitingUpload');
         executeUpload(fileItem, index);
 
         if (props.isPreview && file.type.includes('image')) {

+ 5 - 4
src/packages/__VUE/video/index.vue

@@ -51,8 +51,8 @@
     </div>
     <!-- 错误弹窗 -->
     <div class="nut-video-error" v-show="state.isError">
-      <p class="lose">视频加载失败</p>
-      <p class="retry" @click="retry">点击重试</p>
+      <p class="lose">{{ translate('errorTip') }}</p>
+      <p class="retry" @click="retry">{{ translate('clickRetry') }}</p>
     </div>
   </div>
 </template>
@@ -60,7 +60,7 @@
 import { computed, reactive, ref, toRefs, watch, nextTick, onMounted } from 'vue';
 import { createComponent } from '../../utils/create';
 import { throttle } from '../../utils/throttle.js';
-const { create } = createComponent('video');
+const { create, translate } = createComponent('video');
 
 export default create({
   props: {
@@ -364,7 +364,8 @@ export default create({
       touchSlidMove,
       touchSlidEnd,
       retry,
-      fullScreen
+      fullScreen,
+      translate
     };
   }
 });

+ 24 - 0
src/packages/locale/README.md

@@ -0,0 +1,24 @@
+### nutui 语言包
+
+目前支持的语言:
+
+| 语言           | 文件名 | 版本      |
+|----------------|--------|-----------|
+| 英语           | en-US  | `v3.1.19` |
+| 印度尼西亚语   | id-ID  | 等待 PR   |
+| 泰语           | th-TH  | 等待 PR   |
+| 简体中文       | zh-CN  | `v3.1.19` |
+| 繁體中文(港) | zh-HK  | `v3.1.19` |
+| 繁體中文(台) | zh-TW  | `v3.1.19` |
+
+> 在 [这里](https://github.com/jdf2e/nutui/tree/next/src/vant/src/packages/locale/lang) 查看所有的语言包源文件。
+
+## 常见问题
+
+### 找不到所需的语言包?
+
+如果上方列表中没有你需要的语言,欢迎给我们提 Pull Request 来增加新的语言包。
+
+### 业务代码如何实现国际化?
+
+可以使用 [vue-i18n](https://github.com/kazupon/vue-i18n) 来实现。

+ 20 - 0
src/packages/locale/index.ts

@@ -0,0 +1,20 @@
+import { ref, reactive } from 'vue';
+import lang from './lang/zh-CN';
+import defaultLang from './lang/zh-CN';
+import enUSLang from './lang/en-US';
+const currentLang = ref('zh-CN');
+
+type lang = Record<string, any>;
+const langs = reactive<lang>({
+  'zh-CN': defaultLang,
+  'en-US': enUSLang
+});
+export class Locale {
+  static languages(): lang {
+    return langs[currentLang.value];
+  }
+  static use(lang: string) {
+    currentLang.value = lang;
+  }
+}
+export default Locale;

+ 48 - 0
src/packages/locale/lang/baseLang.ts

@@ -0,0 +1,48 @@
+export interface BaseLang {
+  save: string;
+  confirm: string;
+  cancel: string;
+  done: string;
+  noData: string;
+  placeholder: string;
+  select: string;
+  video: {
+    errorTip: string;
+    clickRetry: string;
+  };
+  fixednav: {
+    activeText: string;
+    unActiveText: string;
+  };
+  pagination: {
+    prev: string;
+    next: string;
+  };
+  calendaritem: {
+    weekdays: Array<string>;
+    end: string;
+    start: string;
+    title: string;
+    monthTitle: Function;
+    today: string;
+  };
+  shortpassword: {
+    title: string;
+    desc: string;
+    tips: string;
+  };
+  uploader: {
+    ready: string;
+    readyUpload: string;
+    waitingUpload: string;
+    uploading: string;
+    success: string;
+    error: string;
+  };
+  countdown: {
+    day: string;
+    hour: string;
+    minute: string;
+    second: string;
+  };
+}

+ 50 - 0
src/packages/locale/lang/en-US.ts

@@ -0,0 +1,50 @@
+import { BaseLang } from './baseLang';
+const lang: BaseLang = {
+  save: 'Save',
+  confirm: 'Confirm',
+  cancel: 'Cancel',
+  done: 'Done',
+  noData: 'No Data',
+  placeholder: 'Placeholder',
+  select: 'Select',
+  video: {
+    errorTip: 'Error Tip',
+    clickRetry: 'Click Retry'
+  },
+  fixednav: {
+    activeText: 'Close Nav',
+    unActiveText: 'Open Nav'
+  },
+  pagination: {
+    prev: 'Previous',
+    next: 'Next'
+  },
+  calendaritem: {
+    weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+    end: 'End',
+    start: 'Start',
+    title: 'Calendar',
+    monthTitle: (year: number, month: number) => `${year}/${month}`,
+    today: 'Today'
+  },
+  shortpassword: {
+    title: 'Please input a password',
+    desc: 'Verify',
+    tips: 'Forget password'
+  },
+  uploader: {
+    ready: 'Ready',
+    readyUpload: 'Ready to upload',
+    waitingUpload: 'Waiting for upload',
+    uploading: 'Uploading',
+    success: 'Upload successful',
+    error: 'Upload failed'
+  },
+  countdown: {
+    day: ' Day ',
+    hour: ' Hour ',
+    minute: ' Minute ',
+    second: ' Second '
+  }
+};
+export default lang;

+ 50 - 0
src/packages/locale/lang/zh-CN.ts

@@ -0,0 +1,50 @@
+import { BaseLang } from './baseLang';
+const lang: BaseLang = {
+  save: '保存',
+  confirm: '确认',
+  cancel: '取消',
+  done: '完成',
+  noData: '暂无数据',
+  placeholder: '请输入',
+  select: '请选择',
+  video: {
+    errorTip: '视频加载失败',
+    clickRetry: '点击重试'
+  },
+  fixednav: {
+    activeText: '收起导航',
+    unActiveText: '快速导航'
+  },
+  pagination: {
+    prev: '上一页',
+    next: '下一页'
+  },
+  calendaritem: {
+    weekdays: ['日', '一', '二', '三', '四', '五', '六'],
+    end: '结束',
+    start: '开始',
+    title: '日历选择',
+    monthTitle: (year: number, month: number) => `${year}年${month}月`,
+    today: '今天'
+  },
+  shortpassword: {
+    title: '请输入密码',
+    desc: '您使用了虚拟资产,请进行验证',
+    tips: '忘记密码'
+  },
+  uploader: {
+    ready: '准备完成',
+    readyUpload: '准备上传',
+    waitingUpload: '等待上传',
+    uploading: '上传中',
+    success: '上传成功',
+    error: '上传失败'
+  },
+  countdown: {
+    day: '天',
+    hour: '时',
+    minute: '分',
+    second: '秒'
+  }
+};
+export default lang;

+ 7 - 0
src/packages/utils/create/component.ts

@@ -7,10 +7,17 @@ import {
   RenderFunction,
   Component
 } from 'vue';
+import locale from '../../locale';
+import { getPropByPath, isFunction } from '../util';
 export function createComponent(name: string) {
+  const languages = locale.languages();
   const componentName = 'nut-' + name;
   return {
     componentName,
+    translate(keyPath: string, ...args: unknown[]) {
+      const text = getPropByPath(languages, `${name.replace('-', '')}.${keyPath}`) || getPropByPath(languages, keyPath);
+      return isFunction(text) ? text(...args) : text;
+    },
     create: function <
       PropsOptions extends Readonly<ComponentPropsOptions>,
       Props extends Readonly<ExtractPropTypes<PropsOptions>>

+ 8 - 0
src/packages/utils/util.ts

@@ -58,3 +58,11 @@ export const isObject = (val: unknown): val is Record<any, any> => val !== null
 export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
   return isObject(val) && isFunction(val.then) && isFunction(val.catch);
 };
+
+export const getPropByPath = (obj: any, keyPath: string) => {
+  try {
+    return keyPath.split('.').reduce((prev, curr) => prev[curr], obj);
+  } catch (error) {
+    return '';
+  }
+};