Browse Source

2024-10-12

cmy 5 months ago
commit
cef7f4abc7
100 changed files with 25268 additions and 0 deletions
  1. 5 0
      .gitignore
  2. 311 0
      App.vue
  3. 0 0
      README.md
  4. 76 0
      api/game.js
  5. 159 0
      api/index.js
  6. 87 0
      api/login.js
  7. 34 0
      api/mypledge.js
  8. 147 0
      api/order.js
  9. 194 0
      api/product.js
  10. 56 0
      api/set.js
  11. 194 0
      api/user.js
  12. 159 0
      api/wallet.js
  13. 37 0
      api/wx.js
  14. 18 0
      components/empty.vue
  15. 77 0
      components/footer/footer.vue
  16. 34 0
      components/footer/list.js
  17. 123 0
      components/input-password/input-password.vue
  18. 814 0
      components/jyf-parser/jyf-parser.vue
  19. 102 0
      components/jyf-parser/libs/CssHandler.js
  20. 577 0
      components/jyf-parser/libs/MpHtmlParser.js
  21. 80 0
      components/jyf-parser/libs/config.js
  22. 35 0
      components/jyf-parser/libs/handler.sjs
  23. 44 0
      components/jyf-parser/libs/handler.wxs
  24. 476 0
      components/jyf-parser/libs/trees.vue
  25. 36 0
      components/page-pagination/changelog.md
  26. 35 0
      components/page-pagination/components/page-pagination/icon/iconfont.css
  27. 713 0
      components/page-pagination/components/page-pagination/page-pagination.vue
  28. 77 0
      components/page-pagination/package.json
  29. 136 0
      components/page-pagination/readme.md
  30. 132 0
      components/sidebar/index.vue
  31. 188 0
      components/topIndex/index.vue
  32. 1356 0
      components/wangding-pickerAddress/data.js
  33. 103 0
      components/wangding-pickerAddress/wangding-pickerAddress.vue
  34. 33 0
      index.html
  35. 25 0
      libs/i18n/index.js
  36. 126 0
      libs/i18n/lang/cn.json
  37. 127 0
      libs/i18n/lang/en.json
  38. 4 0
      libs/i18n/lang/tw.json
  39. 39 0
      libs/log.js
  40. 84 0
      libs/login.js
  41. 253 0
      libs/wechat.js
  42. 48 0
      main.js
  43. 117 0
      manifest.json
  44. 298 0
      package-lock.json
  45. 7 0
      package.json
  46. 109 0
      pages.json
  47. 96 0
      pages/components/topView.vue
  48. 239 0
      pages/index/common.scss
  49. 250 0
      pages/index/details.vue
  50. 310 0
      pages/index/detailsTpl.vue
  51. 238 0
      pages/index/exchange.vue
  52. 211 0
      pages/index/income.vue
  53. 241 0
      pages/index/incomeTpl.vue
  54. 413 0
      pages/index/index.vue
  55. 185 0
      pages/index/invite.vue
  56. 114 0
      pages/index/new_file.vue
  57. 150 0
      pages/index/propDetails.vue
  58. 210 0
      pages/index/property.vue
  59. 216 0
      pages/index/recharge.vue
  60. 206 0
      pages/index/transfer.vue
  61. 264 0
      pages/index/withdrawal.vue
  62. 15 0
      plugin/detect-provider/LICENSE
  63. 106 0
      plugin/detect-provider/README.md
  64. 66 0
      plugin/detect-provider/dist/detect-provider.js
  65. 1 0
      plugin/detect-provider/dist/detect-provider.min.js
  66. 31 0
      plugin/detect-provider/dist/index.d.ts
  67. 62 0
      plugin/detect-provider/dist/index.js
  68. 63 0
      plugin/detect-provider/package.json
  69. 0 0
      plugin/jweixin-module/index.js
  70. 1645 0
      plugin/vue-i18n/CHANGELOG.md
  71. 20 0
      plugin/vue-i18n/LICENSE
  72. 73 0
      plugin/vue-i18n/README.md
  73. 160 0
      plugin/vue-i18n/decls/i18n.js
  74. 30 0
      plugin/vue-i18n/decls/module.js
  75. 2151 0
      plugin/vue-i18n/dist/vue-i18n.common.js
  76. 2104 0
      plugin/vue-i18n/dist/vue-i18n.esm.browser.js
  77. 0 0
      plugin/vue-i18n/dist/vue-i18n.esm.browser.min.js
  78. 2149 0
      plugin/vue-i18n/dist/vue-i18n.esm.js
  79. 2157 0
      plugin/vue-i18n/dist/vue-i18n.js
  80. 5 0
      plugin/vue-i18n/dist/vue-i18n.min.js
  81. 176 0
      plugin/vue-i18n/package.json
  82. 101 0
      plugin/vue-i18n/src/components/interpolation.js
  83. 70 0
      plugin/vue-i18n/src/components/number.js
  84. 112 0
      plugin/vue-i18n/src/directive.js
  85. 33 0
      plugin/vue-i18n/src/extend.js
  86. 114 0
      plugin/vue-i18n/src/format.js
  87. 1065 0
      plugin/vue-i18n/src/index.js
  88. 40 0
      plugin/vue-i18n/src/install.js
  89. 139 0
      plugin/vue-i18n/src/mixin.js
  90. 302 0
      plugin/vue-i18n/src/path.js
  91. 169 0
      plugin/vue-i18n/src/util.js
  92. 276 0
      plugin/vue-i18n/types/index.d.ts
  93. 34 0
      plugin/vue-i18n/vetur/attributes.json
  94. 20 0
      plugin/vue-i18n/vetur/tags.json
  95. 551 0
      static/css/cmy.css
  96. BIN
      static/error/emptyCart.png
  97. BIN
      static/error/errorImage.jpg
  98. BIN
      static/error/missing-face.png
  99. BIN
      static/img/img01.png
  100. BIN
      static/img/img02.png

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+/unpackage/dist
+/unpackage/release
+/unpackage/cache
+/.hbuilderx
+/node_modules

+ 311 - 0
App.vue

@@ -0,0 +1,311 @@
+<script>
+	/**
+	 * vuex管理登陆状态,具体可以参考官方登陆模板示例
+	 */
+	import { mapMutations } from "vuex";
+	// #ifdef H5
+	import { weixindata, setRouter } from "./utils/wxAuthorized";
+	// #endif
+	// #ifdef APP-PLUS
+	import { getUpApp } from "./utils/upApp.js";
+	// #endif
+	export default {
+		globalData: {
+			globalData: false,
+			isIframe: false,
+			windowHeight: 0
+		},
+		data() {
+			return {
+				/* 保存微信信息 */
+				appData: {},
+			};
+		},
+		methods: {
+			...mapMutations("user", ["setUserInfo", "login", "hasLogin",'logout']),
+		},
+		onExit(){
+		},
+		onLaunch: async function (urlObj) {
+			let obj = this;
+			// 加载缓存中的用户信息
+			let token = uni.getStorageSync("token");
+			let userInfo = uni.getStorageSync("userInfo") || "";
+			// 判断是否拥有用户信息
+			if (userInfo.uid) {
+				//更新登陆状态
+				uni.getStorage({
+					key: "userInfo",
+					success: res => {
+						obj.setUserInfo(res.data);
+						obj.login(res.data);
+					},
+				});
+			}
+			const account =  await ethereum.request({
+						method: 'eth_requestAccounts'
+					})
+			if(ethereum.selectedAddress!=userInfo.address){
+				console.log(obj.logout);
+				obj.logout();
+			}
+			// #ifdef H5
+			// #ifdef APP-PLUS || H5
+			uni.getSystemInfo({
+				success: function(res) {
+					// 首页没有title获取的整个页面的高度,里面的页面有原生标题要减掉就是视口的高度
+					// 状态栏是动态的可以拿到 标题栏是固定写死的是44px
+					let height = res.windowHeight - res.statusBarHeight - 44
+					// #ifdef H5 || APP-PLUS
+					obj.globalData.windowHeight = res.windowHeight + 'px'
+					// #endif
+					// // #ifdef APP-PLUS
+					// that.globalData.windowHeight = height + 'px'
+					// // #endif
+			
+				}
+			});
+			// #endif	
+			// 保存路由对象
+			setRouter(this.$router);
+			//判断是否已经缓存浏览器
+			let bool = uni.getStorageSync("weichatBrowser") || "";
+			if (bool === "") {
+				//判断是否为微信浏览
+				bool =
+					navigator.userAgent
+						.toLowerCase()
+						.match(/MicroMessenger/i) == "micromessenger";
+				// 保存当前是否为微信内核浏览器
+				uni.setStorageSync("weichatBrowser", bool);
+			}
+			if (bool) {
+				// 加载微信信息
+				weixindata();
+			}
+			// #endif
+			// #ifdef APP-PLUS
+			// 判断是否升级
+			getUpApp();
+			// 获取当前运行系统
+			let system = uni.getStorageSync("platform") || "";
+			if (!system) {
+				uni.setStorage({
+					key: "platform",
+					data: uni.getSystemInfoSync().platform,
+				});
+			}
+			// #endif
+		},
+		onShow: function () {
+			// 加载拦截
+			// console.log('App Show');
+		},
+		onHide: function () {
+			// console.log('App Hide');
+		},
+	};
+</script>
+
+<style lang="scss">
+	/*全局公共样式和字体图标*/
+	@import "/static/css/cmy.css";
+	@import "@/uni_modules/uview-ui/index.scss";
+	view,
+	scroll-view,
+	swiper,
+	swiper-item,
+	cover-view,
+	cover-image,
+	icon,
+	text,
+	rich-text,
+	progress,
+	button,
+	checkbox,
+	form,
+	input,
+	label,
+	radio,
+	slider,
+	switch,
+	textarea,
+	navigator,
+	audio,
+	camera,
+	image,
+	video {
+		box-sizing: border-box;
+	}
+	/* 骨架屏替代方案 */
+	.Skeleton {
+		background: #f3f3f3;
+		padding: 20rpx 0;
+		border-radius: 8rpx;
+	}
+
+	/* 图片载入替代方案 */
+	.image-wrapper {
+		font-size: 0;
+		background: #f3f3f3;
+		border-radius: 4px;
+		image {
+			width: 100%;
+			height: 100%;
+			transition: 0.6s;
+			opacity: 0;
+			&.loaded {
+				opacity: 1;
+			}
+		}
+	}
+
+	// 设置富文本中图片最大宽度
+	uni-rich-text img {
+		max-width: 100% !important;
+	}
+	/*边框*/
+	.b-b:after,
+	.b-t:after {
+		position: absolute;
+		z-index: 3;
+		left: 0;
+		right: 0;
+		height: 0;
+		content: "";
+		transform: scaleY(0.5);
+		border-bottom: 1px solid $border-color-base;
+	}
+
+	.b-b:after {
+		bottom: 0;
+	}
+
+	.b-t:after {
+		top: 0;
+	}
+
+	/* button样式改写 */
+	uni-button,
+	button {
+		height: 80rpx;
+		line-height: 80rpx;
+		font-size: $font-lg + 2rpx;
+		font-weight: normal;
+
+		&.no-border:before,
+		&.no-border:after {
+			border: 0;
+		}
+	}
+
+	uni-button[type="default"],
+	button[type="default"] {
+		color: $font-color-dark;
+	}
+
+	/* input 样式 */
+	.input-placeholder {
+		color: #999999;
+	}
+
+	.placeholder {
+		color: #999999;
+	}
+	// 边距样式
+	@for $i from 1 to 4 {
+		.margin-l-#{$i * 10} {
+			margin-left: $i * 10rpx !important;
+		}
+		.margin-r-#{$i * 10} {
+			margin-right: $i * 10rpx !important;
+		}
+		.margin-t-#{$i * 10} {
+			margin-top: $i * 10rpx !important;
+		}
+		.margin-b-#{$i * 10} {
+			margin-bottom: $i * 10rpx !important;
+		}
+		.margin-#{$i * 10} {
+			margin: $i * 10rpx !important;
+		}
+		.margin-v-#{$i * 10} {
+			margin-top: $i * 10rpx !important;
+			margin-bottom: $i * 10rpx !important;
+		}
+		.margin-c-#{$i * 10} {
+			margin-left: $i * 10rpx !important;
+			margin-right: $i * 10rpx !important;
+		}
+		.padding-l-#{$i * 10} {
+			padding-left: $i * 10rpx !important;
+		}
+		.padding-r-#{$i * 10} {
+			padding-right: $i * 10rpx !important;
+		}
+		.padding-t-#{$i * 10} {
+			padding-top: $i * 10rpx !important;
+		}
+		.padding-b-#{$i * 10} {
+			padding-bottom: $i * 10rpx !important;
+		}
+		.padding-#{$i * 10} {
+			padding: $i * 10rpx !important;
+		}
+		.padding-v-#{$i * 10} {
+			padding-top: $i * 10rpx !important;
+			padding-bottom: $i * 10rpx !important;
+		}
+		.padding-c-#{$i * 10} {
+			padding-left: $i * 10rpx !important;
+			padding-right: $i * 10rpx !important;
+		}
+	}
+	// 字体大小
+	.font-size-sm {
+		font-size: $font-sm;
+	}
+	.font-size-base {
+		font-size: $font-base;
+	}
+	.font-size-lg {
+		font-size: $font-lg;
+	}
+	// 字体颜色
+	.font-color-yellow {
+		color: $color-yellow;
+	}
+	.font-color-gray {
+		color: $color-gray;
+	}
+	.font-color-red {
+		color: $color-red;
+	}
+	// 边框颜色
+	.border-color-yellow {
+		border: 1rpx solid $color-yellow;
+	}
+
+	page {
+		background-color: $page-color-base;
+		// 设置默认字体
+		font-family: PingFang SC, STHeitiSC-Light, Helvetica-Light, arial,
+			sans-serif, Droid Sans Fallback;
+	}
+	/*边框*/
+	.b-b:after,
+	.b-t:after {
+		position: absolute;
+		z-index: 3;
+		left: 0;
+		right: 0;
+		height: 0;
+		content: "";
+		transform: scaleY(0.5);
+		border-bottom: 1px solid $border-color-base;
+	}
+
+	.b-b:after {
+		bottom: 0;
+	}
+</style>

+ 0 - 0
README.md


+ 76 - 0
api/game.js

@@ -0,0 +1,76 @@
+import request from '@/utils/request'
+
+// 互娱  游戏列表
+export  function getGameList(data) {
+	return request({
+		url: `/api/game`,
+		method: 'get',
+		data
+	});
+}
+
+// 互娱 获取当前游戏历史记录
+export function getGame(data,id) {
+	return request({
+		url: `/api/game/room/${id}`,
+		method: 'get',
+		data
+	});
+}
+
+// 互娱  游戏k线
+export  function gameKline(data,type) {
+	return request({
+		url: `/api/kline/${type}`,
+		method: 'get',
+		data
+	});
+}
+// 互娱 获取钱包
+export  function gameWallet() {
+	return request({
+		url: `/api/wallet`,
+		method: 'get',
+	});
+}
+// 互娱  获取交易记录
+export  function gameBetList(data) {
+	return request({
+		url: `/api/game/bet/${data.id}`,
+		method: 'get',
+		data
+	});
+}
+
+// 互娱  押注
+export  function gameBetIn(data) {
+	return request({
+		url: `/api/game/bet/${data.id}`,
+		method: 'post',
+		data
+	});
+}
+// 互娱  测试
+export  function gameTest() {
+	return request({
+		url: `/api/test`,
+		method: 'post',
+	});
+}
+// 充值
+export  function gamecharge(data) {
+	return request({
+		url: `/api/charge`,
+		method: 'get',
+		data
+	});
+}
+
+// 充值完成
+export  function gamechargePost(data) {
+	return request({
+		url: `/api/charge`,
+		method: 'post',
+		data
+	});
+}

+ 159 - 0
api/index.js

@@ -0,0 +1,159 @@
+import request from '@/utils/request'
+
+export function spreadList(data) {
+	return request({
+		url: '/api/user/spreadList',
+		method: 'get',
+		data
+	});
+}
+export function moneyInfo(id) {
+	return request({
+		url: `/api/user/moneyInfo/token/${id}`,
+		method: 'get',
+	});
+}
+export function exchange(data) {
+	return request({
+		url: '/api/user/exchange',
+		method: 'post',
+		data
+	});
+}
+export function mining(data) {
+	return request({
+		url: '/api/mining/lst',
+		method: 'get',
+		data
+	});
+}
+export function detail(id) {
+	return request({
+		url: `/api/mining/detail/id/${id}`,
+		method: 'get',
+	});
+}
+export function myList(data) {
+	return request({
+		url: '/api/mining/myList',
+		method: 'get',
+		data
+	});
+}
+export function order(data) {
+	return request({
+		url: '/api/mining/order',
+		method: 'POST',
+		data
+	});
+}
+
+export function getTokenLog(data) {
+	return request({
+		url: '/api/user/getTokenLog',
+		method: 'get',
+		data
+	});
+}
+// 首页代币价格
+export function prices(data) {
+	return request({
+		url: '/api/prices',
+		method: 'get',
+		data
+	});
+}
+
+
+export function getIndex(data) {
+	return request({
+		url: '/api/index',
+		method: 'get',
+		data
+	});
+}
+// #ifndef MP
+/**
+ * 版本号
+ */
+export function getAppVersion(data) {
+	return request({
+		url: '/api/version',
+		method: 'get',
+		data
+	});
+}
+// #endif
+export function getIndexs(data) {
+	return request({
+		url: '/api/user/index',
+		method: 'get',
+		data
+	});
+}
+export function coin(data) {
+	return request({
+		url: '/api/user/chargeBNBCalculator',
+		method: 'POST',
+		data
+	});
+}
+export function chargeBNB(data) {
+	return request({
+		url: '/api/user/chargeBNB',
+		method: 'POST',
+		data
+	});
+}
+export function extractCalculator(data) {
+	return request({
+		url: '/api/user/extractCalculator',
+		method: 'POST',
+		data
+	});
+}
+export function extract(data) {
+	return request({
+		url: '/api/user/extract',
+		method: 'POST',
+		data
+	});
+}
+export function indexAll(data) {
+	return request({
+		url: '/api/index',
+		method: 'POST',
+		data
+	});
+}
+
+// mayi的铸币
+export function chargeCalculator(data) {
+	return request({
+		url: '/api/user/chargeCalculator',
+		method: 'POST',
+		data
+	});
+}
+export function charge(data) {
+	return request({
+		url: '/api/user/charge',
+		method: 'POST',
+		data
+	});
+}
+
+export function transferDate(data) {
+	return request({
+		url: '/api/index/transferDate',
+		method: 'POST',
+		data
+	});
+}
+export function childrenList(data) {
+	return request({
+		url: '/api/user/childrenList',
+		method: 'get',
+		data
+	});
+}

+ 87 - 0
api/login.js

@@ -0,0 +1,87 @@
+import request from '@/utils/request'
+
+// 登录
+export function login(data) {
+	return request({
+		url: '/api/user/login',
+		method: 'post',
+		data
+	});
+}
+export function trade(data) {
+	return request({
+		url: '/api/user/trade',
+		method: 'post',
+		data
+	});
+}
+// 注册
+export function register(data) {
+	return request({
+		url: '/api/register',
+		method: 'post',
+		data
+	});
+}
+// 验证码
+export function verify(data) {
+	// type=register为注册
+	// type=login为登录
+	return request({
+		url: '/api/register/verify',
+		method: 'post',
+		data
+	});
+}
+
+// 获取用户信息
+export function getUserInfo(data) {
+	return request({
+		url: '/api/user/info',
+		method: 'get',
+		data
+	});
+}
+
+// 短信登录
+export function loginMobile(data) {
+	return request({
+		url: '/api/login/mobile',
+		method: 'post',
+		data
+	});
+}
+// #ifdef APP-PLUS
+// 微信授权登录
+export function loginWx(data) {
+	return request({
+		url: '/api/wechat/appauth',
+		method: 'get',
+		data
+	});
+}
+// #endif
+//绑定手机号
+export function bangding(data) {
+	return request({
+		url: '/api/binding',
+		method: 'POST',
+		data
+	});
+}
+//绑定推荐人
+export function bandgingSpead(data) {
+	return request({
+		url: '/api/user/addSpread',
+		method: 'POST',
+		data
+	});
+}
+//绑定接点人
+export function addParent(data) {
+	return request({
+		url: '/api/user/addParent',
+		method: 'POST',
+		data
+	});
+}

+ 34 - 0
api/mypledge.js

@@ -0,0 +1,34 @@
+import request from '@/utils/request'
+
+// 我的质押
+export function getLock(data) {
+	return request({
+		url: '/api/lock/join',
+		method: 'GET',
+		data
+	});
+}
+
+// 质押项目列表
+export function lock(data) {
+	return request({
+		url: '/api/lock',
+		method: 'GET',
+		data
+	});
+}
+// 质押项目详情
+export function lockDetail(data) {
+	return request({
+		url: `/api/lock/${data.id}`,
+		method: 'GET',
+	});
+}
+// 参与质押
+export function lockJoin(data) {
+	return request({
+		url: `/api/lock/${data.id}`,
+		method: 'POST',
+		data
+	});
+}

+ 147 - 0
api/order.js

@@ -0,0 +1,147 @@
+import request from '@/utils/request'
+import {upFilse} from '@/utils/request'
+// 订单确认
+export function confirm(data) {
+	return request({
+		url: '/api/order/confirm',
+		method: 'post',
+		data
+	});
+}
+//获取可使用优惠券
+export function couponsOrder(data,price) {
+	return request({
+		url: '/api/coupons/order/'+price,
+		method: 'get',
+		data
+	});
+}
+//获取优惠券列表
+export function getCouponsList(data,types) {
+	//优惠券状态 0全部 1未使用 2已使用
+	return request({
+		url: '/api/coupons/user/'+types,
+		method: 'get',
+		data
+	});
+}
+//提交评论
+export function order_comment(data) {
+	return request({
+		url: '/api/order/comment',
+		method: 'post',
+		data
+	});
+}
+//订单产品信息
+export function product(data) {
+	return request({
+		url: '/api/order/product',
+		method: 'post',
+		data
+	});
+}
+// 快递查询
+export function express_query(data) {
+	return request({
+		url: '/api/order/express_query',
+		method: 'get',
+		data
+	});
+}
+//上传图片
+export function upload(data) {
+	return upFilse({
+		url: '/api/upload/image',
+		method: 'post',
+		data
+	});
+}
+// 订单列表
+export function orderList(data) {
+	return request({
+		url: '/api/order/list',
+		method: 'get',
+		data
+	});
+}
+// 订单详细
+export function orderDetail(data,orderid) {
+	return request({
+		url: '/api/order/detail/'+orderid,
+		method: 'get',
+		data
+	});
+}
+
+// 取消订单
+export function orderCancel(data) {
+	return request({
+		url: '/api/order/cancel',
+		method: 'post',
+		data
+	});
+}
+
+// 删除订单
+export function orderDel(data) {
+	return request({
+		url: '/api/order/del',
+		method: 'get',
+		data
+	});
+}
+// 申请退款
+export function refund(data) {
+	return request({
+		url: '/api/order/refund/verify',
+		method: 'post',
+		data
+	});
+}
+// 退款理由列表
+export function refundReason(data) {
+	return request({
+		url: '/api/order/refund/reason',
+		method: 'get',
+		data
+	});
+}
+
+// 确认收货
+export function orderTake(data) {
+	return request({
+		url: '/api/order/take',
+		method: 'post',
+		data
+	});
+}
+
+// 订单支付
+export function orderPay(data) {
+	return request({
+		url: '/api/order/pay',
+		method: 'post',
+		data
+	});
+}
+
+// 创建订单
+export function createOrderkey(data,key) {
+	return request({
+		url: '/api/order/create/'+key,
+		method: 'post',
+		data
+	});
+}
+
+// 统计订单金额
+export function computedOrderkey(data) {
+	return request({
+		url: '/api/order/computed/'+data.orderkey,
+		method: 'post',
+		data
+	});
+}
+
+

+ 194 - 0
api/product.js

@@ -0,0 +1,194 @@
+import request from '@/utils/request'
+
+// 获取商品列表
+export function getProducts(data) {
+	// 	{
+	// 参数名称	是否必须	示例	备注
+	// sid			否			二级分类编号
+	// cid			否			一级分类编号(!)
+	// keyword		否			搜索
+	// priceOrder	否			价格排序
+	// salesOrder	否			销量排序
+	// news			否			是否新品
+	// page			否			分页参数起始值
+	// limit		否			分页数步长值
+	// }
+	return request({
+		url: '/api/products',
+		method: 'get',
+		data
+	});
+}
+// 获取商品详情
+export function goodsDetail(data, id) {
+	return request({
+		url: '/api/product/detail/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 砍价列表
+export function getBargainList(data) {
+	return request({
+		url: '/api/bargain/list',
+		method: 'get',
+		data
+	});
+}
+// 加入购物车
+export function cartAdd(data) {
+	return request({
+		url: '/api/cart/add',
+		method: 'post',
+		data
+	});
+}
+
+// 收藏商品
+export function collectAdd(data) {
+	return request({
+		url: '/api/collect/add',
+		method: 'post',
+		data
+	});
+}
+
+// 取消收藏商品
+export function collectDel(data) {
+	return request({
+		url: '/api/collect/del',
+		method: 'post',
+		data
+	});
+}
+
+// 获取搜搜关键字
+export function searchKeyword(data) {
+	return request({
+		url: '/api/search/keyword',
+		method: 'get',
+		data
+	});
+}
+
+// 获取热门分类信息
+export function groomList(data, type) {
+	// 获取产品类型 1 精品推荐 2 热门榜单 3首发新品 4促销单品
+	return request({
+		url: '/api/groom/list/' + type,
+		method: 'get',
+		data
+	});
+}
+
+
+// 获取秒杀商品详细
+export function seckillGoods(data, id) {
+	return request({
+		url: '/api/seckill/detail/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 获取拼团商品详细
+export function groupGoods(data, id) {
+	return request({
+		url: '/api/combination/detail/' + id,
+		method: 'get',
+		data
+	});
+}
+
+
+// 获取商品分类
+export function getCategoryList(data) {
+	return request({
+		url: '/api/category',
+		method: 'get',
+		data
+	});
+}
+
+
+// 获取拼团列表
+export function getCombinationList(data) {
+	return request({
+		url: '/api/combination/list',
+		method: 'get',
+		data
+	});
+}
+
+//取消拼团
+export function getCombinationLisRemove(data, id) {
+	return request({
+		url: '/api/combination/remove' + id,
+		method: 'get',
+		data
+	});
+}
+
+//拼团开团页面数据
+export function getCombinationLisPink(data, id) {
+	return request({
+		url: '/api/combination/pink/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 获取拼团海报
+export function getCombinationPoster(data) {
+	return request({
+		url: '/api/combination/poster',
+		method: 'post',
+		data
+	});
+}
+
+
+// 获取秒杀分类
+export function getSeckillClass(data) {
+	return request({
+		url: '/api/seckill/index',
+		method: 'get',
+		data
+	});
+}
+// 获取秒杀列表
+export function getSeckillList(data, id) {
+	return request({
+		url: '/api/seckill/list/' + id,
+		method: 'get',
+		data
+	});
+}
+
+
+// 产品评价数量和好评度
+export function reply_config(data, id) {
+	return request({
+		url: '/api/reply/config/' + id,
+		method: 'get',
+		data
+	});
+}
+// 获取产品评论
+export function reply_list(data, id) {
+	return request({
+		url: '/api/reply/list/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 分享海报 
+export function poster(data) {
+	return request({
+		url: '/api/product/poster',
+		method: 'post',
+		data
+	});
+}

+ 56 - 0
api/set.js

@@ -0,0 +1,56 @@
+import request from '@/utils/request'
+
+// 修改用户信息
+export function userEdit(data) {
+	return request({
+		// url: '/api/password/reset/trade_password',
+		url: '/api/user/edit',
+		method: 'post',
+		data
+	});
+}
+
+//退出登录
+export function logout(data) {
+	return request({
+		url: '/api/logout',
+		method: 'get',
+		data
+	});
+}
+//修改密码
+export function registerReset(data) {
+	return request({
+		url: '/api/register/reset',
+		method: 'post',
+		data
+	});
+}
+
+//修改交易密码
+export function Reset(data) {
+	return request({
+		url: '/api/password/reset/trade_password',
+		method: 'post',
+		data
+	});
+}
+
+//绑定手机
+export function binding(data) {
+	return request({
+		url: '/api/binding',
+		method: 'post',
+		data
+	});
+}
+// #ifdef APP-PLUS
+//苹果生成账户
+export function applelogin(data) {
+	return request({
+		url: '/api/applelogin',
+		method: 'post',
+		data
+	});
+}
+// #endif

+ 194 - 0
api/user.js

@@ -0,0 +1,194 @@
+import request from '@/utils/request'
+
+// 获取用户信息
+export function getUserInfo(data) {
+	return request({
+		url: '/api/userinfo',
+		method: 'get',
+		data
+	});
+}
+
+// 
+export function getUser(data) {
+	return request({
+		url: '/api/user',
+		method: 'get',
+		data
+	});
+}
+
+//用户推荐用户列表
+export function spread_se(data,id) {
+	return request({
+		url: `/api/spread/people/${id}`,
+		method: 'get',
+		data
+	});
+}
+
+
+//用户推荐用户列表
+export function spread(data) {
+	return request({
+		url: `/api/spread/people`,
+		method: 'post',
+		data
+	});
+}
+
+// 订单统计信息
+export function orderData(data) {
+	return request({
+		url: '/api/order/data',
+		method: 'get',
+		data
+	});
+}
+
+
+
+// 用户分享图
+export function spreadBanner(data) {
+	return request({
+		url: '/api/spread/banner',
+		method: 'get',
+		data
+	});
+}
+
+// 获取地址列表
+export function getAddressList(data) {
+	return request({
+		url: '/api/address/list',
+		method: 'get',
+		data
+	});
+}
+// 修改地址
+export function addressEdit(data) {
+	return request({
+		url: '/api/address/edit',
+		method: 'post',
+		data
+	});
+}
+// 删除地址
+export function addressDel(data) {
+	return request({
+		url: '/api/address/del',
+		method: 'post',
+		data
+	});
+}
+// 设为默认地址
+export function setAddressDefault(data) {
+	return request({
+		url: '/api/address/default/set',
+		method: 'post',
+		data
+	});
+}
+// 购物车列表
+export function getCartList(data) {
+	return request({
+		url: '/api/cart/list',
+		method: 'get',
+		data
+	});
+}
+
+// 修改购物车数量
+export function getCartNum(data) {
+	return request({
+		url: '/api/cart/num',
+		method: 'post',
+		data
+	});
+}
+//删除购物车
+export function cartDel(data) {
+	return request({
+		url: '/api/cart/del',
+		method: 'post',
+		data
+	});
+}
+//获取收藏夹列表
+export function getcollectList(data) {
+	return request({
+		url: '/api/collect/user',
+		method: 'get',
+		data
+	});
+}
+// 取消收藏
+export function delcollect(data) {
+	return request({
+		url: '/api/collect/del',
+		method: 'post',
+		data
+	});
+}
+
+
+//http://hmd.frp.liuniu946.com/api/brokerage/list?page=1&limit=10&pm=1
+export function getCommissionInfo(data) {
+	return request({
+		url: '/api/brokerage/list',
+		method: 'get',
+		data
+	});
+}
+
+//我的推广
+export function myspread(data) {
+	return request({
+		url: '/api/spread/people',
+		method: 'POST',
+		data
+	});
+}
+
+
+//
+export function sqdl(data) {
+	return request({
+		url: '/api/user/apply',
+		method: 'POST',
+		data
+	});
+}
+
+export function details(data, id) {
+	return request({
+		url: '/api/article/details/' + id,
+		method: 'get',
+		data
+	});
+}
+
+export function yjzye(data) {
+	return request({
+		url: '/api/user/transformation',
+		method: 'post',
+		data
+	})
+}
+
+export function jfzz(data) {
+	return request({
+		url: '/api/brokeragePriceTransfer',
+		method: 'post',
+		data
+	})
+}
+
+export function getLevelList(data) {
+	// api/give_level_list
+	return request({
+		url: '/api/give_level_list',
+		method: 'get',
+		data
+	})
+}

+ 159 - 0
api/wallet.js

@@ -0,0 +1,159 @@
+import request from '@/utils/request'
+
+// 获取用户消费记录
+export function spreadCommission(data, state) {
+	return request({
+		url: '/api/spread/commission/' + state,
+		method: 'get',
+		data
+	});
+}
+
+// 获取账户余额
+export function userBalance(data) {
+	return request({
+		url: '/api/user/balance',
+		method: 'get',
+		data
+	});
+}
+
+// 提现
+export function extractCash(data) {
+	return request({
+		url: '/api/extract/cash',
+		method: 'post',
+		data
+	});
+}
+
+// 提现信息
+export function extractBank(data) {
+	return request({
+		url: '/api/extract/bank',
+		method: 'get',
+		data
+	});
+}
+// #ifdef H5
+// 公众号充值
+export function rechargeWechat(data) {
+	return request({
+		url: '/api/recharge/wechat',
+		method: 'post',
+		data
+	});
+}
+// #endif
+// #ifdef MP
+// 小程序充值
+export function rechargeRoutine(data) {
+	return request({
+		url: '/api/recharge/routine',
+		method: 'post',
+		data
+	});
+}
+// #endif
+// 获取提现支付宝账号
+export function aliInfo(data) {
+	return request({
+		url: '/api/ali/info',
+		method: 'get',
+		data
+	});
+}
+//获取默认银行卡账号
+export function bankInfo(data) {
+	return request({
+		url: '/api/bank/info',
+		method: 'get',
+		data
+	});
+}
+// 保存提现支付宝账号
+export function setAliInfo(data) {
+	return request({
+		url: '/api/ali/edit',
+		method: 'post',
+		data
+	});
+}
+//保存默认银行卡账号
+export function setBankInfo(data) {
+	return request({
+		url: '/api/bank/edit',
+		method: 'post',
+		data
+	});
+}
+
+
+// 账户余额
+export function balance(data) {
+	return request({
+		url: '/api/user/balance',
+		method: 'get',
+		data
+	});
+}
+
+//
+export function yue(data) {
+	return request({
+		url: '/api/now_money/list',
+		method: 'get',
+		data
+	});
+}
+
+export function integral(data) {
+	return request({
+		url: '/api/integral/list',
+		method: 'get',
+		data
+	});
+}
+
+export function getBank(data) {
+	return request({
+		url: '/api/auction/pay_list',
+		method: 'get',
+		data
+	});
+}
+
+export function setBank(data) {
+	return request({
+		url: '/api/auction/pay',
+		method: 'post',
+		data
+	});
+}
+
+export function getRechargePrice(data) {
+	return request({
+		url: '/api/getRechargePrice',
+		method: 'post',
+		data
+	});
+}
+
+// 竞猜项目api
+export function qianBao(data) {
+	return request({
+		url: '/api/wallet',
+		method: 'get',
+		data
+	});
+}
+
+
+// 钱包USDT类型交易明细
+export function getMoneyLog(data,money_type='USDT') {
+	return request({
+		url: `/api/money/log/${money_type}`,
+		method: 'get',
+		data
+	});
+}

+ 37 - 0
api/wx.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+// #ifdef H5
+// 微信分享信息
+export function share(data) {
+	return request({
+		url: '/api/share',
+		method: 'get',
+		data
+	});
+}
+//微信配置
+export function wechatConfig(data) {
+	return request({
+		url: '/api/wechat/config',
+		method: 'get',
+		data
+	});
+}
+// 微信code地址
+export function wechatAuth(data) {
+	return request({
+		url: '/api/wechat/auth',
+		method: 'get',
+		data
+	});
+}
+// #endif
+// #ifdef MP-WEIXIN
+// 微信code地址
+export function wechatMpAuth(data) {
+	return request({
+		url: '/api/wechat/mp_auth',
+		method: 'post',
+		data
+	});
+}
+// #endif

File diff suppressed because it is too large
+ 18 - 0
components/empty.vue


+ 77 - 0
components/footer/footer.vue

@@ -0,0 +1,77 @@
+<template>
+	<view class="footer flex p-y-xs2 bg-tab-nav">
+		<view class="flex-fill" :class="{ 'router-link-active': tab == item.tel }"
+			v-for="item in navList" :key="item.tel" @click="tabChange(item.tel)">
+			<view class="icon">
+				<img class="h-15" v-if="tab == item.tel" :src="item.activeIcon" alt="" />
+				<img class="h-15" v-else :src="item.icon" alt="" />
+			</view>
+			<view class="fn-12">{{ $t(item.label) }}</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		navList
+	} from "./list.js";
+	export default {
+		props: {
+			tab: {
+				type: String,
+				default: "index",
+			},
+		},
+		data() {
+			return {
+				navList: navList(this),
+			};
+		},
+		created() {
+			// console.log(navList,'navList');
+		},
+		methods: {
+			tabChange(v) {
+				uni.switchTab({
+					url: "/pages/index/" + v,
+				});
+				// console.log(v);
+				// console.log(this.$router);
+			},
+		},
+	};
+</script>
+<style lang="scss" scoped>
+	.flex-fill{
+		text-align: center;
+		line-height: 1;
+	}
+	.p-y-xs2 {
+		padding: 5px 10px;
+	}
+	.h-15{
+		height: 20px;
+		width: 20px;
+	}
+	.footer {
+		box-shadow:  0px -7px 20px 0px rgba(37, 37, 48, 0.83);
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		z-index: 999 !important;
+		// height: 102rpx;
+		color: rgba(#fff, 0.3);
+		justify-content: space-around !important;
+	}
+	.fn-12{
+		margin-top: 4px;
+		font-size: 10px;
+	}
+	.router-link-active {
+		// color: $theme-1;
+		color:#FEB041;
+	}
+
+	.bg-tab-nav {
+		background-color: #000000;
+	}
+</style>

+ 34 - 0
components/footer/list.js

@@ -0,0 +1,34 @@
+export function navList(that) {
+  return [
+    {
+      label: "home.d0",
+      tel: "index",
+      icon: "static/tabBar/shouye.png",
+      activeIcon: "static/tabBar/shouye-home.png",
+    },
+    {
+      label: "home.d1",
+      tel: "pledge",
+      icon: "static/tabBar/baoya.png",
+      activeIcon: "static/tabBar/baoya-home.png",
+    },
+	// {
+	//   label: "home.d2",
+	//   tel: "entertainment",
+	//   icon: "static/tabBar/huyu.png",
+	//   activeIcon: "static/tabBar/huyu-home.png",
+	// },
+    {
+      label: "home.d3",
+      tel: "information",
+      icon: "static/tabBar/zixun.png",
+      activeIcon: "static/tabBar/zixun-home.png",
+    },
+    {
+      label: "home.d4",
+      tel: "user",
+      icon: "static/tabBar/my.png",
+      activeIcon: "static/tabBar/my-home.png",
+    },
+  ];
+}

+ 123 - 0
components/input-password/input-password.vue

@@ -0,0 +1,123 @@
+<template>
+	<view class="page">
+		<view>
+			<view class="pay-title">
+				<text>{{$t('enter.b3')}}</text>
+			</view>
+			<view class="pay-password">
+				<view class="list" v-for="item in 6">
+					<text v-show="passwordArr.length >= item">●</text>
+				</view>
+			</view>
+			<navigator url="/pages/user/set/transaction">
+				<view class="hint">
+					<text>{{$t('login.b8')}}</text>
+				</view>
+			</navigator>
+		</view>
+		<cc-defineKeyboard ref="CodeKeyboard" passwrdType="pay" @KeyInfo="KeyInfo" :viewShow="true"></cc-defineKeyboard>
+	</view>
+</template>
+<script>
+	export default {
+		data() {
+			return {
+				passwordArr: []
+			};
+		},
+		mounted() {
+			this.$refs.CodeKeyboard.show();
+		},
+		// 关闭循环
+		methods: {
+			// 点击触发支付事件
+			KeyInfo(val) {
+				console.log(val);
+				let arr = this.passwordArr;
+				if (val.index >= 6) {
+					return;
+				}
+				// 判断是否输入的是删除键
+				if (val.keyCode === 8) {
+					// 删除最后一位
+					arr.splice(val.index + 1, 1)
+				}
+				// 判断是否输入的是取消案件
+				else if (val.keyCode == 190) {
+					this.$emit('colse')
+				} else {
+					arr.push(val.key);
+				}
+				// 开始交易
+				if (arr.length == 6) {
+					this.$emit('commit',this.passwordArr.join(''))
+					//初始化对象
+					this.passwordArr = [];
+				}
+			},
+		},
+	};
+</script>
+
+<style lang="scss">
+	$base: orangered; // 基础颜色
+
+	.page {
+		width: 100%;
+		background-color: #FFFFFF;
+
+		.pay-title {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			width: 100%;
+			height: 200rpx;
+
+			text {
+				font-size: 28rpx;
+				color: #555555;
+			}
+		}
+
+		.pay-password {
+			display: flex;
+			align-items: center;
+			width: 90%;
+			height: 80rpx;
+			margin: 20rpx auto;
+			border: 2rpx solid $base;
+
+			.list {
+				color: #555555;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width: 16.666%;
+				height: 100%;
+				border-right: 2rpx solid #EEEEEE;
+
+				text {
+					font-size: 32rpx;
+				}
+			}
+
+			.list:nth-child(6) {
+				border-right: none;
+			}
+		}
+
+		.hint {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			width: 100%;
+			height: 100rpx;
+
+			text {
+				font-size: 28rpx;
+				color: $base;
+			}
+		}
+	}
+
+</style>

+ 814 - 0
components/jyf-parser/jyf-parser.vue

@@ -0,0 +1,814 @@
+<!--
+  parser 主模块组件
+  github:https://github.com/jin-yufeng/Parser 
+  docs:https://jin-yufeng.github.io/Parser
+  插件市场:https://ext.dcloud.net.cn/plugin?id=805
+  author:JinYufeng
+  update:2020/04/14
+-->
+<template>
+	<view>
+		<slot v-if="!nodes.length" />
+		<!--#ifdef APP-PLUS-NVUE-->
+		<web-view id="top" ref="web" :src="src" :style="'margin-top:-2px;height:'+height+'px'" @onPostMessage="_message" />
+		<!--#endif-->
+		<!--#ifndef APP-PLUS-NVUE-->
+		<view id="top" :style="showAm+(selectable?';user-select:text;-webkit-user-select:text':'')" :animation="scaleAm" @tap="_tap"
+		 @touchstart="_touchstart" @touchmove="_touchmove">
+			<!--#ifdef H5-->
+			<div :id="'rtf'+uid"></div>
+			<!--#endif-->
+			<!--#ifndef H5-->
+			<trees :nodes="nodes" :lazy-load="lazyLoad" :loadVideo="loadVideo" />
+			<image v-for="(item, index) in imgs" v-bind:key="index" :id="index" :src="item" hidden @load="_load" />
+			<!--#endif-->
+		</view>
+		<!--#endif-->
+	</view>
+</template>
+
+<script>
+	// #ifndef H5 || APP-PLUS-NVUE
+	import trees from './libs/trees';
+	var cache = {},
+		// #ifdef MP-WEIXIN || MP-TOUTIAO
+		fs = uni.getFileSystemManager ? uni.getFileSystemManager() : null,
+		// #endif
+		Parser = require('./libs/MpHtmlParser.js');
+	var document; // document 补丁包 https://jin-yufeng.github.io/Parser/#/instructions?id=document
+	// 计算 cache 的 key
+	function hash(str) {
+		for (var i = str.length, val = 5381; i--;)
+			val += (val << 5) + str.charCodeAt(i);
+		return val;
+	}
+	// #endif
+	// #ifdef H5 || APP-PLUS-NVUE
+	var rpx = uni.getSystemInfoSync().screenWidth / 750,
+		cfg = require('./libs/config.js');
+	// #endif
+	// #ifdef APP-PLUS-NVUE
+	var dom = weex.requireModule('dom');
+	// #endif
+	export default {
+		name: 'parser',
+		data() {
+			return {
+				// #ifdef APP-PLUS
+				loadVideo: false,
+				// #endif
+				// #ifdef H5
+				uid: this._uid,
+				// #endif
+				// #ifdef APP-PLUS-NVUE
+				src: '',
+				height: 1,
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				scaleAm: '',
+				showAm: '',
+				imgs: [],
+				// #endif
+				nodes: []
+			}
+		},
+		// #ifndef H5 || APP-PLUS-NVUE
+		components: {
+			trees
+		},
+		// #endif
+		props: {
+			'html': null,
+			// #ifndef MP-ALIPAY
+			'autopause': {
+				type: Boolean,
+				default: true
+			},
+			// #endif
+			'autosetTitle': {
+				type: Boolean,
+				default: true
+			},
+			// #ifndef H5 || APP-PLUS-NVUE
+			'compress': Number,
+			'useCache': Boolean,
+			'xml': Boolean,
+			// #endif
+			'domain': String,
+			// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+			'gestureZoom': Boolean,
+			// #endif
+			// #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
+			'lazyLoad': Boolean,
+			// #endif
+			'selectable': Boolean,
+			'tagStyle': Object,
+			'showWithAnimation': Boolean,
+			'useAnchor': Boolean
+		},
+		watch: {
+			html(html) {
+				this.setContent(html);
+			}
+		},
+		mounted() {
+			// 图片数组
+			this.imgList = [];
+			this.imgList.each = function(f) {
+				for (var i = 0, len = this.length; i < len; i++)
+					this.setItem(i, f(this[i], i, this));
+			}
+			this.imgList.setItem = function(i, src) {
+				if (i == void 0 || !src) return;
+				// #ifndef MP-ALIPAY || APP-PLUS
+				// 去重
+				if (src.indexOf('http') == 0 && this.includes(src)) {
+					var newSrc = '';
+					for (var j = 0, c; c = src[j]; j++) {
+						if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
+						newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
+					}
+					newSrc += src.substr(j);
+					return this[i] = newSrc;
+				}
+				// #endif
+				this[i] = src;
+				// 暂存 data src
+				if (src.includes('data:image')) {
+					var filePath, info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
+					if (!info) return;
+					// #ifdef MP-WEIXIN || MP-TOUTIAO
+					filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
+					fs && fs.writeFile({
+						filePath,
+						data: info[3],
+						encoding: info[2],
+						success: () => this[i] = filePath
+					})
+					// #endif
+					// #ifdef APP-PLUS
+					filePath = `_doc/parser_tmp/${Date.now()}.${info[1]}`;
+					var bitmap = new plus.nativeObj.Bitmap();
+					bitmap.loadBase64Data(src, () => {
+						bitmap.save(filePath, {}, () => {
+							bitmap.clear()
+							this[i] = filePath;
+						})
+					})
+					// #endif
+				}
+			}
+			if (this.html) this.setContent(this.html);
+		},
+		beforeDestroy() {
+			// #ifdef H5
+			if (this._observer) this._observer.disconnect();
+			// #endif
+			this.imgList.each(src => {
+				// #ifdef APP-PLUS
+				if (src && src.includes('_doc')) {
+					plus.io.resolveLocalFileSystemURL(src, entry => {
+						entry.remove();
+					});
+				}
+				// #endif
+				// #ifdef MP-WEIXIN || MP-TOUTIAO
+				if (src && uni.env && src.includes(uni.env.USER_DATA_PATH))
+					fs && fs.unlink({
+						filePath: src
+					})
+				// #endif
+			})
+			clearInterval(this._timer);
+		},
+		methods: {
+			// #ifdef H5 || APP-PLUS-NVUE
+			_Dom2Str(nodes) {
+				var str = '';
+				for (var node of nodes) {
+					if (node.type == 'text')
+						str += node.text;
+					else {
+						str += ('<' + node.name);
+						for (var attr in node.attrs || {})
+							str += (' ' + attr + '="' + node.attrs[attr] + '"');
+						if (!node.children || !node.children.length) str += '>';
+						else str += ('>' + this._Dom2Str(node.children) + '</' + node.name + '>');
+					}
+				}
+				return str;
+			},
+			_handleHtml(html, append) {
+				if (typeof html != 'string') html = this._Dom2Str(html.nodes || html);
+				// 处理 rpx
+				if (html.includes('rpx'))
+					html = html.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * rpx + 'px');
+				if (!append) {
+					// 处理 tag-style 和 userAgentStyles
+					var style = '<style>@keyframes show{0%{opacity:0}100%{opacity:1}}';
+					for (var item in cfg.userAgentStyles)
+						style += `${item}{${cfg.userAgentStyles[item]}}`;
+					for (item in this.tagStyle)
+						style += `${item}{${this.tagStyle[item]}}`;
+					style += '</style>';
+					html = style + html;
+				}
+				return html;
+			},
+			// #endif
+			setContent(html, append) {
+				// #ifdef APP-PLUS-NVUE
+				if (!html) {
+					this.src = '';
+					this.height = 1;
+					return;
+				}
+				if (append) return;
+				plus.io.resolveLocalFileSystemURL('_doc', entry => {
+					entry.getDirectory('parser_tmp', {
+						create: true
+					}, entry => {
+						var fileName = Date.now() + '.html';
+						entry.getFile(fileName, {
+							create: true
+						}, entry => {
+							entry.createWriter(writer => {
+								writer.onwriteend = () => {
+									this.nodes = [1];
+									this.src = '_doc/parser_tmp/' + fileName;
+									this.$nextTick(function() {
+										entry.remove();
+									})
+								}
+								html =
+									'<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1' +
+									(this.selectable ? '' : ',user-scalable=no') +
+									'"><script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></' +
+									'script><base href="' + this.domain + '">' + this._handleHtml(html) +
+									'<script>"use strict";function post(t){uni.postMessage({data:t})}' +
+									(this.showWithAnimation ? 'document.body.style.animation="show .5s",' : '') +
+									'document.addEventListener("UniAppJSBridgeReady",function(){post({action:"load",text:document.body.innerText});var t=document.getElementsByTagName("title");t.length&&post({action:"getTitle",title:t[0].innerText});for(var e,o=document.getElementsByTagName("img"),n=[],i=0,r=0;e=o[i];i++)e.onerror=function(){post({action:"error",source:"img",target:this})},e.hasAttribute("ignore")||"A"==e.parentElement.nodeName||(e.i=r++,n.push(e.src),e.onclick=function(){post({action:"preview",img:{i:this.i,src:this.src}})});post({action:"getImgList",imgList:n});for(var a,s=document.getElementsByTagName("a"),c=0;a=s[c];c++)a.onclick=function(){var t,e=this.getAttribute("href");if("#"==e[0]){var r=document.getElementById(e.substr(1));r&&(t=r.offsetTop)}return post({action:"linkpress",href:e,offset:t}),!1};;for(var u,m=document.getElementsByTagName("video"),d=0;u=m[d];d++)u.style.maxWidth="100%",u.onerror=function(){post({action:"error",source:"video",target:this})}' +
+									(this.autopause ? ',u.onplay=function(){for(var t,e=0;t=m[e];e++)t!=this&&t.pause()}' : '') +
+									';for(var g,l=document.getElementsByTagName("audio"),p=0;g=l[p];p++)g.onerror=function(){post({action:"error",source:"audio",target:this})};window.onload=function(){post({action:"ready",height:document.body.scrollHeight})}});</' +
+									'script>';
+								writer.write(html);
+							});
+						})
+					})
+				})
+				// #endif
+				// #ifdef H5
+				if (!html) {
+					if (this.rtf && !append) this.rtf.parentNode.removeChild(this.rtf);
+					return;
+				}
+				var div = document.createElement('div');
+				if (!append) {
+					if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
+					this.rtf = div;
+				} else {
+					if (!this.rtf) this.rtf = div;
+					else this.rtf.appendChild(div);
+				}
+				div.innerHTML = this._handleHtml(html, append);
+				for (var styles = this.rtf.getElementsByTagName('style'), i = 0, style; style = styles[i++];) {
+					style.innerHTML = style.innerHTML.replace(/body/g, '#rtf' + this._uid);
+					style.setAttribute('scoped', 'true');
+				}
+				// 懒加载
+				if (!this._observer && this.lazyLoad && IntersectionObserver) {
+					this._observer = new IntersectionObserver(changes => {
+						for (let item, i = 0; item = changes[i++];) {
+							if (item.isIntersecting) {
+								item.target.src = item.target.getAttribute('data-src');
+								item.target.removeAttribute('data-src');
+								this._observer.unobserve(item.target);
+							}
+						}
+					}, {
+						rootMargin: '900px 0px 900px 0px'
+					})
+				}
+				var _ts = this;
+				// 获取标题
+				var title = this.rtf.getElementsByTagName('title');
+				if (title.length && this.autosetTitle)
+					uni.setNavigationBarTitle({
+						title: title[0].innerText
+					})
+				// 图片处理
+				this.imgList.length = 0;
+				var imgs = this.rtf.getElementsByTagName('img');
+				for (let i = 0, j = 0, img; img = imgs[i]; i++) {
+					img.style.maxWidth = '100%';
+					var src = img.getAttribute('src');
+					if (this.domain && src) {
+						if (src[0] == '/') {
+							if (src[1] == '/')
+								img.src = (this.domain.includes('://') ? this.domain.split('://')[0] : '') + ':' + src;
+							else img.src = this.domain + src;
+						} else if (!src.includes('://')) img.src = this.domain + '/' + src;
+					}
+					if (!img.hasAttribute('ignore') && img.parentElement.nodeName != 'A') {
+						img.i = j++;
+						_ts.imgList.push(img.src || img.getAttribute('data-src'));
+						img.onclick = function() {
+							var preview = true;
+							this.ignore = () => preview = false;
+							_ts.$emit('imgtap', this);
+							if (preview) {
+								uni.previewImage({
+									current: this.i,
+									urls: _ts.imgList
+								});
+							}
+						}
+					}
+					img.onerror = function() {
+						_ts.$emit('error', {
+							source: 'img',
+							target: this
+						});
+					}
+					if (_ts.lazyLoad && this._observer && img.src && img.i != 0) {
+						img.setAttribute('data-src', img.src);
+						img.removeAttribute('src');
+						this._observer.observe(img);
+					}
+				}
+				// 链接处理
+				var links = this.rtf.getElementsByTagName('a');
+				for (var link of links) {
+					link.onclick = function() {
+						var jump = true,
+							href = this.getAttribute('href');
+						_ts.$emit('linkpress', {
+							href,
+							ignore: () => jump = false
+						});
+						if (jump && href) {
+							if (href[0] == '#') {
+								if (_ts.useAnchor) {
+									_ts.navigateTo({
+										id: href.substr(1)
+									})
+								}
+							} else if (href.indexOf('http') == 0 || href.indexOf('//') == 0)
+								return true;
+							else {
+								uni.navigateTo({
+									url: href
+								})
+							}
+						}
+						return false;
+					}
+				}
+				// 视频处理
+				var videos = this.rtf.getElementsByTagName('video');
+				_ts.videoContexts = videos;
+				for (let video, i = 0; video = videos[i++];) {
+					video.style.maxWidth = '100%';
+					video.onerror = function() {
+						_ts.$emit('error', {
+							source: 'video',
+							target: this
+						});
+					}
+					video.onplay = function() {
+						if (_ts.autopause)
+							for (let item, i = 0; item = _ts.videoContexts[i++];)
+								if (item != this) item.pause();
+					}
+				}
+				// 音频处理
+				var audios = this.rtf.getElementsByTagName('audios');
+				for (var audio of audios)
+					audio.onerror = function() {
+						_ts.$emit('error', {
+							source: 'audio',
+							target: this
+						});
+					}
+				this.document = this.rtf;
+				if (!append) document.getElementById('rtf' + this._uid).appendChild(this.rtf);
+				this.$nextTick(() => {
+					this.nodes = [1];
+					this.$emit('load');
+				})
+				setTimeout(() => this.showAm = '', 500);
+				// #endif
+				// #ifndef H5 || APP-PLUS-NVUE
+				var nodes;
+				if (!html)
+					return this.nodes = [];
+				else if (typeof html == 'string') {
+					let parser = new Parser(html, this);
+					// 缓存读取
+					if (this.useCache) {
+						var hashVal = hash(html);
+						if (cache[hashVal])
+							nodes = cache[hashVal];
+						else {
+							nodes = parser.parse();
+							cache[hashVal] = nodes;
+						}
+					} else nodes = parser.parse();
+					this.$emit('parse', nodes);
+				} else if (Object.prototype.toString.call(html) == '[object Array]') {
+					// 非本插件产生的 array 需要进行一些转换
+					if (html.length && html[0].PoweredBy != 'Parser') {
+						let parser = new Parser(html, this);
+						(function f(ns) {
+							for (var i = 0, n; n = ns[i]; i++) {
+								if (n.type == 'text') continue;
+								n.attrs = n.attrs || {};
+								for (var item in n.attrs)
+									if (typeof n.attrs[item] != 'string') n.attrs[item] = n.attrs[item].toString();
+								parser.matchAttr(n, parser);
+								if (n.children && n.children.length) {
+									parser.STACK.push(n);
+									f(n.children);
+									parser.popNode(parser.STACK.pop());
+								} else n.children = void 0;
+							}
+						})(html);
+					}
+					nodes = html;
+				} else if (typeof html == 'object' && html.nodes) {
+					nodes = html.nodes;
+					console.warn('错误的 html 类型:object 类型已废弃');
+				} else
+					return console.warn('错误的 html 类型:' + typeof html);
+				// #ifdef APP-PLUS
+				this.loadVideo = false;
+				// #endif
+				if (document) this.document = new document(this.nodes, 'nodes', this);
+				if (append) this.nodes = this.nodes.concat(nodes);
+				else this.nodes = nodes;
+				if (nodes.length && nodes[0].title && this.autosetTitle)
+					uni.setNavigationBarTitle({
+						title: nodes[0].title
+					})
+				this.$nextTick(() => {
+					this.imgList.length = 0;
+					this.videoContexts = [];
+					// #ifdef MP-TOUTIAO
+					setTimeout(() => {
+						// #endif
+						var f = (cs) => {
+							for (let i = 0, c; c = cs[i++];) {
+								if (c.$options.name == 'trees') {
+									for (var j = c.nodes.length, item; item = c.nodes[--j];) {
+										if (item.c) continue;
+										if (item.name == 'img') {
+											this.imgList.setItem(item.attrs.i, item.attrs.src);
+											// #ifndef MP-ALIPAY
+											if (!c.observer && !c.imgLoad && item.attrs.i != '0') {
+												if (this.lazyLoad && uni.createIntersectionObserver) {
+													c.observer = uni.createIntersectionObserver(c);
+													c.observer.relativeToViewport({
+														top: 900,
+														bottom: 900
+													}).observe('._img', () => {
+														c.imgLoad = true;
+														c.observer.disconnect();
+													})
+												} else
+													c.imgLoad = true;
+											}
+											// #endif
+										}
+										// #ifndef MP-ALIPAY
+										else if (item.name == 'video') {
+											var ctx = uni.createVideoContext(item.attrs.id, c);
+											ctx.id = item.attrs.id;
+											this.videoContexts.push(ctx);
+										}
+										// #endif
+										// #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
+										if (item.attrs && item.attrs.id) {
+											this.anchors = this.anchors || [];
+											this.anchors.push({
+												id: item.attrs.id,
+												node: c
+											})
+										}
+										// #endif
+									}
+								}
+								if (c.$children.length)
+									f(c.$children)
+							}
+						}
+						f(this.$children);
+						// #ifdef MP-TOUTIAO
+					}, 200)
+					this.$emit('load');
+					// #endif
+					// #ifdef APP-PLUS
+					setTimeout(() => {
+						this.loadVideo = true;
+					}, 3000);
+					// #endif
+				})
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				var height;
+				clearInterval(this._timer);
+				this._timer = setInterval(() => {
+					// #ifdef H5
+					var res = [this.rtf.getBoundingClientRect()];
+					// #endif
+					// #ifndef H5
+					// #ifdef APP-PLUS
+					uni.createSelectorQuery().in(this)
+					// #endif
+					// #ifndef APP-PLUS
+					this.createSelectorQuery()
+						// #endif
+						.select('#top').boundingClientRect().exec(res => {
+							// #endif
+							this.width = res[0].width;
+							if (res[0].height == height) {
+								this.$emit('ready', res[0])
+								clearInterval(this._timer);
+							}
+							height = res[0].height;
+							// #ifndef H5
+						});
+					// #endif
+				}, 350)
+				if (this.showWithAnimation && !append) this.showAm = 'animation:show .5s';
+				// #endif
+			},
+			getText(ns = this.nodes) {
+				// #ifdef APP-PLUS-NVUE
+				return this._text;
+				// #endif
+				// #ifdef H5
+				return this.rtf.innerText;
+				// #endif
+				// #ifndef H5 || APP-PLUS-NVUE
+				var txt = '';
+				for (var i = 0, n; n = ns[i++];) {
+					if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
+						.replace(/&amp;/g, '&');
+					else if (n.type == 'br') txt += '\n';
+					else {
+						// 块级标签前后加换行
+						var block = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] >
+							'0' && n.name[1] < '7');
+						if (block && txt && txt[txt.length - 1] != '\n') txt += '\n';
+						if (n.children) txt += this.getText(n.children);
+						if (block && txt[txt.length - 1] != '\n') txt += '\n';
+						else if (n.name == 'td' || n.name == 'th') txt += '\t';
+					}
+				}
+				return txt;
+				// #endif
+			},
+			navigateTo(obj) {
+				if (!this.useAnchor)
+					return obj.fail && obj.fail({
+						errMsg: 'Anchor is disabled'
+					})
+				// #ifdef APP-PLUS-NVUE
+				if (!obj.id)
+					dom.scrollToElement(this.$refs.web);
+				else
+					this.$refs.web.evalJs('var pos=document.getElementById("' + obj.id +
+						'");if(pos)post({action:"linkpress",href:"#",offset:pos.offsetTop})');
+				return obj.success && obj.success({
+					errMsg: 'pageScrollTo:ok'
+				});
+				// #endif
+				// #ifdef H5
+				if (!obj.id) {
+					window.scrollTo(0, this.rtf.offsetTop);
+					return obj.success && obj.success({
+						errMsg: 'pageScrollTo:ok'
+					});
+				}
+				var target = document.getElementById(obj.id);
+				if (!target) return obj.fail && obj.fail({
+					errMsg: 'Label not found'
+				});
+				obj.scrollTop = this.rtf.offsetTop + target.offsetTop;
+				uni.pageScrollTo(obj);
+				// #endif
+				// #ifndef H5
+				var Scroll = (selector, component) => {
+					uni.createSelectorQuery().in(component ? component : this).select(selector).boundingClientRect().selectViewport()
+						.scrollOffset()
+						.exec(res => {
+							if (!res || !res[0])
+								return obj.fail && obj.fail({
+									errMsg: 'Label not found'
+								});
+							obj.scrollTop = res[1].scrollTop + res[0].top;
+							uni.pageScrollTo(obj);
+						})
+				}
+				if (!obj.id) Scroll('#top');
+				else {
+					// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+					Scroll('#top >>> #' + obj.id + ', #top >>> .' + obj.id);
+					// #endif
+					// #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
+					for (var anchor of this.anchors)
+						if (anchor.id == obj.id)
+							Scroll('#' + obj.id + ', .' + obj.id, anchor.node);
+					// #endif
+				}
+				// #endif
+			},
+			getVideoContext(id) {
+				// #ifndef APP-PLUS-NVUE
+				if (!id) return this.videoContexts;
+				else
+					for (var i = this.videoContexts.length; i--;)
+						if (this.videoContexts[i].id == id) return this.videoContexts[i];
+				// #endif
+			},
+			// 预加载
+			preLoad(html, num) {
+				// #ifdef H5 || APP-PLUS-NVUE
+				if (html.constructor == Array)
+					html = this._Dom2Str(html);
+				var script = "var contain=document.createElement('div');contain.innerHTML='" + html.replace(/'/g, "\\'") +
+					"';for(var imgs=contain.querySelectorAll('img'),i=imgs.length-1;i>=" + num +
+					";i--)imgs[i].removeAttribute('src');";
+				// #endif
+				// #ifdef APP-PLUS-NVUE
+				this.$refs.web.evalJs(script);
+				// #endif
+				// #ifdef H5
+				eval(script);
+				// #endif
+				// #ifndef H5 || APP-PLUS-NVUE
+				if (typeof html == 'string') {
+					var id = hash(html);
+					html = new Parser(html, this).parse();
+					cache[id] = html;
+				}
+				var wait = [];
+				(function f(ns) {
+					for (var i = 0, n; n = ns[i++];) {
+						if (n.name == 'img' && n.attrs.src && !wait.includes(n.attrs.src))
+							wait.push(n.attrs.src);
+						f(n.children || []);
+					}
+				})(html);
+				if (num) wait = wait.slice(0, num);
+				this._wait = (this._wait || []).concat(wait);
+				if (!this.imgs) this.imgs = this._wait.splice(0, 15);
+				else if (this.imgs.length < 15)
+					this.imgs = this.imgs.concat(this._wait.splice(0, 15 - this.imgs.length));
+				// #endif
+			},
+			// #ifdef APP-PLUS-NVUE
+			_message(e) {
+				// 接收 web-view 消息
+				var data = e.detail.data[0];
+				if (data.action == 'load') {
+					this.$emit('load');
+					this._text = data.text;
+				} else if (data.action == 'getTitle') {
+					if (this.autosetTitle)
+						uni.setNavigationBarTitle({
+							title: data.title
+						})
+				} else if (data.action == 'getImgList') {
+					this.imgList.length = 0;
+					for (var i = data.imgList.length; i--;)
+						this.imgList.setItem(i, data.imgList[i]);
+				} else if (data.action == 'preview') {
+					var preview = true;
+					data.img.ignore = () => preview = false;
+					this.$emit('imgtap', data.img);
+					if (preview)
+						uni.previewImage({
+							current: data.img.i,
+							urls: this.imgList
+						})
+				} else if (data.action == 'linkpress') {
+					var jump = true,
+						href = data.href;
+					this.$emit('linkpress', {
+						href,
+						ignore: () => jump = false
+					})
+					if (jump && href) {
+						if (href[0] == '#') {
+							if (this.useAnchor)
+								dom.scrollToElement(this.$refs.web, {
+									offset: data.offset
+								})
+						} else if (href.includes('://'))
+							plus.runtime.openWeb(href);
+						else
+							uni.navigateTo({
+								url: href
+							})
+					}
+				} else if (data.action == 'error')
+					this.$emit('error', {
+						source: data.source,
+						target: data.target
+					})
+				else if (data.action == 'ready') {
+					this.height = data.height;
+					this.$nextTick(() => {
+						uni.createSelectorQuery().in(this).select('#top').boundingClientRect().exec(res => {
+							this.rect = res[0];
+							this.$emit('ready', res[0]);
+						})
+					})
+				}
+			},
+			// #endif
+			// #ifndef APP-PLUS-NVUE
+			// #ifndef H5
+			_load(e) {
+				if (this._wait.length)
+					this.$set(this.imgs, e.target.id, this._wait.shift());
+			},
+			// #endif
+			_tap(e) {
+				// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+				if (this.gestureZoom && e.timeStamp - this._lastT < 300) {
+					var initY = e.touches[0].pageY - e.currentTarget.offsetTop;
+					if (this._zoom) {
+						this._scaleAm.translateX(0).scale(1).step();
+						uni.pageScrollTo({
+							scrollTop: (initY + this._initY) / 2 - e.touches[0].clientY,
+							duration: 400
+						})
+					} else {
+						var initX = e.touches[0].pageX - e.currentTarget.offsetLeft;
+						this._initY = initY;
+						this._scaleAm = uni.createAnimation({
+							transformOrigin: `${initX}px ${this._initY}px 0`,
+							timingFunction: 'ease-in-out'
+						});
+						// #ifdef MP-TOUTIAO
+						this._scaleAm.opacity(1);
+						// #endif
+						this._scaleAm.scale(2).step();
+						this._tMax = initX / 2;
+						this._tMin = (initX - this.width) / 2;
+						this._tX = 0;
+					}
+					this._zoom = !this._zoom;
+					this.scaleAm = this._scaleAm.export();
+				}
+				this._lastT = e.timeStamp;
+				// #endif
+			},
+			_touchstart(e) {
+				// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+				if (e.touches.length == 1)
+					this._initX = this._lastX = e.touches[0].pageX;
+				// #endif
+			},
+			_touchmove(e) {
+				// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+				var diff = e.touches[0].pageX - this._lastX;
+				if (this._zoom && e.touches.length == 1 && Math.abs(diff) > 20) {
+					this._lastX = e.touches[0].pageX;
+					if ((this._tX <= this._tMin && diff < 0) || (this._tX >= this._tMax && diff > 0))
+						return;
+					this._tX += (diff * Math.abs(this._lastX - this._initX) * 0.05);
+					if (this._tX < this._tMin) this._tX = this._tMin;
+					if (this._tX > this._tMax) this._tX = this._tMax;
+					this._scaleAm.translateX(this._tX).step();
+					this.scaleAm = this._scaleAm.export();
+				}
+				// #endif
+			}
+			// #endif
+		}
+	}
+</script>
+
+<style>
+	@keyframes show {
+		0% {
+			opacity: 0
+		}
+
+		100% {
+			opacity: 1;
+		}
+	}
+
+	/* #ifdef MP-WEIXIN */
+	:host {
+		display: block;
+		overflow: scroll;
+		-webkit-overflow-scrolling: touch;
+	}
+
+	/* #endif */
+</style>

+ 102 - 0
components/jyf-parser/libs/CssHandler.js

@@ -0,0 +1,102 @@
+/*
+  解析和匹配 Css 的选择器
+  github:https://github.com/jin-yufeng/Parser
+  docs:https://jin-yufeng.github.io/Parser
+  author:JinYufeng
+  update:2020/03/15
+*/
+var cfg = require('./config.js');
+class CssHandler {
+	constructor(tagStyle) {
+		var styles = Object.assign({}, cfg.userAgentStyles);
+		for (var item in tagStyle)
+			styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
+		this.styles = styles;
+	}
+	getStyle = data => this.styles = new CssParser(data, this.styles).parse();
+	match(name, attrs) {
+		var tmp, matched = (tmp = this.styles[name]) ? tmp + ';' : '';
+		if (attrs.class) {
+			var items = attrs.class.split(' ');
+			for (var i = 0, item; item = items[i]; i++)
+				if (tmp = this.styles['.' + item])
+					matched += tmp + ';';
+		}
+		if (tmp = this.styles['#' + attrs.id])
+			matched += tmp + ';';
+		return matched;
+	}
+}
+module.exports = CssHandler;
+class CssParser {
+	constructor(data, init) {
+		this.data = data;
+		this.floor = 0;
+		this.i = 0;
+		this.list = [];
+		this.res = init;
+		this.state = this.Space;
+	}
+	parse() {
+		for (var c; c = this.data[this.i]; this.i++)
+			this.state(c);
+		return this.res;
+	}
+	section = () => this.data.substring(this.start, this.i);
+	isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+	// 状态机
+	Space(c) {
+		if (c == '.' || c == '#' || this.isLetter(c)) {
+			this.start = this.i;
+			this.state = this.Name;
+		} else if (c == '/' && this.data[this.i + 1] == '*')
+			this.Comment();
+		else if (!cfg.blankChar[c] && c != ';')
+			this.state = this.Ignore;
+	}
+	Comment() {
+		this.i = this.data.indexOf('*/', this.i) + 1;
+		if (!this.i) this.i = this.data.length;
+		this.state = this.Space;
+	}
+	Ignore(c) {
+		if (c == '{') this.floor++;
+		else if (c == '}' && !--this.floor) this.state = this.Space;
+	}
+	Name(c) {
+		if (cfg.blankChar[c]) {
+			this.list.push(this.section());
+			this.state = this.NameSpace;
+		} else if (c == '{') {
+			this.list.push(this.section());
+			this.Content();
+		} else if (c == ',') {
+			this.list.push(this.section());
+			this.Comma();
+		} else if (!this.isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
+			this.state = this.Ignore;
+	}
+	NameSpace(c) {
+		if (c == '{') this.Content();
+		else if (c == ',') this.Comma();
+		else if (!cfg.blankChar[c]) this.state = this.Ignore;
+	}
+	Comma() {
+		while (cfg.blankChar[this.data[++this.i]]);
+		if (this.data[this.i] == '{') this.Content();
+		else {
+			this.start = this.i--;
+			this.state = this.Name;
+		}
+	}
+	Content() {
+		this.start = ++this.i;
+		if ((this.i = this.data.indexOf('}', this.i)) == -1) this.i = this.data.length;
+		var content = this.section();
+		for (var i = 0, item; item = this.list[i++];)
+			if (this.res[item]) this.res[item] += ';' + content;
+			else this.res[item] = content;
+		this.list = [];
+		this.state = this.Space;
+	}
+}

+ 577 - 0
components/jyf-parser/libs/MpHtmlParser.js

@@ -0,0 +1,577 @@
+/*
+  将 html 解析为适用于小程序 rich-text 的 DOM 结构
+  github:https://github.com/jin-yufeng/Parser
+  docs:https://jin-yufeng.github.io/Parser
+  author:JinYufeng
+  update:2020/04/13
+*/
+var cfg = require('./config.js'),
+	blankChar = cfg.blankChar,
+	CssHandler = require('./CssHandler.js'),
+	{
+		screenWidth,
+		system
+	} = wx.getSystemInfoSync();
+// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
+var entities = {
+	lt: '<',
+	gt: '>',
+	amp: '&',
+	quot: '"',
+	apos: "'",
+	nbsp: '\xA0',
+	ensp: '\u2002',
+	emsp: '\u2003',
+	ndash: '–',
+	mdash: '—',
+	middot: '·',
+	lsquo: '‘',
+	rsquo: '’',
+	ldquo: '“',
+	rdquo: '”',
+	bull: '•',
+	hellip: '…',
+	permil: '‰',
+	copy: '©',
+	reg: '®',
+	trade: '™',
+	times: '×',
+	divide: '÷',
+	cent: '¢',
+	pound: '£',
+	yen: '¥',
+	euro: '€',
+	sect: '§'
+};
+// #endif
+var emoji; // emoji 补丁包 https://jin-yufeng.github.io/Parser/#/instructions?id=emoji
+class MpHtmlParser {
+	constructor(data, options = {}) {
+		this.attrs = {};
+		this.compress = options.compress;
+		this.CssHandler = new CssHandler(options.tagStyle, screenWidth);
+		this.data = data;
+		this.domain = options.domain;
+		this.DOM = [];
+		this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
+		this.protocol = this.domain && this.domain.includes('://') ? this.domain.split('://')[0] : '';
+		this.state = this.Text;
+		this.STACK = [];
+		this.useAnchor = options.useAnchor;
+		this.xml = options.xml;
+	}
+	parse() {
+		if (emoji) this.data = emoji.parseEmoji(this.data);
+		for (var c; c = this.data[this.i]; this.i++)
+			this.state(c);
+		if (this.state == this.Text) this.setText();
+		while (this.STACK.length) this.popNode(this.STACK.pop());
+		// #ifdef MP-BAIDU || MP-TOUTIAO
+		// 将顶层标签的一些样式提取出来给 rich-text
+		(function f(ns) {
+			for (var i = ns.length, n; n = ns[--i];) {
+				if (n.type == 'text') continue;
+				if (!n.c) {
+					var style = n.attrs.style;
+					if (style) {
+						var j, k, res;
+						if ((j = style.indexOf('display')) != -1)
+							res = style.substring(j, (k = style.indexOf(';', j)) == -1 ? style.length : k);
+						if ((j = style.indexOf('float')) != -1)
+							res += ';' + style.substring(j, (k = style.indexOf(';', j)) == -1 ? style.length : k);
+						n.attrs.contain = res;
+					}
+				} else f(n.children);
+			}
+		})(this.DOM);
+		// #endif
+		if (this.DOM.length) {
+			this.DOM[0].PoweredBy = 'Parser';
+			if (this.title) this.DOM[0].title = this.title;
+		}
+		return this.DOM;
+	}
+	// 设置属性
+	setAttr() {
+		var name = this.getName(this.attrName);
+		if (cfg.trustAttrs[name]) {
+			if (!this.attrVal) {
+				if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
+			} else if (name == 'src') this.attrs[name] = this.getUrl(this.attrVal.replace(/&amp;/g, '&'));
+			else this.attrs[name] = this.attrVal;
+		}
+		this.attrVal = '';
+		while (blankChar[this.data[this.i]]) this.i++;
+		if (this.isClose()) this.setNode();
+		else {
+			this.start = this.i;
+			this.state = this.AttrName;
+		}
+	}
+	// 设置文本节点
+	setText() {
+		var back, text = this.section();
+		if (!text) return;
+		text = (cfg.onText && cfg.onText(text, () => back = true)) || text;
+		if (back) {
+			this.data = this.data.substr(0, this.start) + text + this.data.substr(this.i);
+			let j = this.start + text.length;
+			for (this.i = this.start; this.i < j; this.i++) this.state(this.data[this.i]);
+			return;
+		}
+		if (!this.pre) {
+			// 合并空白符
+			var tmp = [];
+			for (let i = text.length, c; c = text[--i];)
+				if (!blankChar[c] || (!blankChar[tmp[0]] && (c = ' '))) tmp.unshift(c);
+			text = tmp.join('');
+			if (text == ' ') return;
+		}
+		// 处理实体
+		var siblings = this.siblings(),
+			i = -1,
+			j, en;
+		while (1) {
+			if ((i = text.indexOf('&', i + 1)) == -1) break;
+			if ((j = text.indexOf(';', i + 2)) == -1) break;
+			if (text[i + 1] == '#') {
+				en = parseInt((text[i + 2] == 'x' ? '0' : '') + text.substring(i + 2, j));
+				if (!isNaN(en)) text = text.substr(0, i) + String.fromCharCode(en) + text.substring(j + 1);
+			} else {
+				en = text.substring(i + 1, j);
+				// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
+				if (en == 'nbsp') text = text.substr(0, i) + '\xA0' + text.substr(j + 1); // 解决 &nbsp; 失效
+				else if (en != 'lt' && en != 'gt' && en != 'amp' && en != 'ensp' && en != 'emsp' && en != 'quot' && en != 'apos') {
+					i && siblings.push({
+						type: 'text',
+						text: text.substr(0, i)
+					})
+					siblings.push({
+						type: 'text',
+						text: `&${en};`,
+						en: 1
+					})
+					text = text.substr(j + 1);
+					i = -1;
+				}
+				// #endif
+				// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
+				if (entities[en]) text = text.substr(0, i) + entities[en] + text.substr(j + 1);
+				// #endif
+			}
+		}
+		text && siblings.push({
+			type: 'text',
+			text
+		})
+	}
+	// 设置元素节点
+	setNode() {
+		var node = {
+				name: this.tagName.toLowerCase(),
+				attrs: this.attrs
+			},
+			close = cfg.selfClosingTags[node.name] || (this.xml && this.data[this.i] == '/');
+		this.attrs = {};
+		if (!cfg.ignoreTags[node.name]) {
+			this.matchAttr(node);
+			if (!close) {
+				node.children = [];
+				if (node.name == 'pre' && cfg.highlight) {
+					this.remove(node);
+					this.pre = node.pre = true;
+				}
+				this.siblings().push(node);
+				this.STACK.push(node);
+			} else if (!cfg.filter || cfg.filter(node, this) != false)
+				this.siblings().push(node);
+		} else {
+			if (!close) this.remove(node);
+			else if (node.name == 'source') {
+				var parent = this.STACK[this.STACK.length - 1],
+					attrs = node.attrs;
+				if (parent && attrs.src)
+					if (parent.name == 'video' || parent.name == 'audio')
+						parent.attrs.source.push(attrs.src);
+					else {
+						var i, media = attrs.media;
+						if (parent.name == 'picture' && !parent.attrs.src && !(attrs.src.indexOf('.webp') && system.includes('iOS')) &&
+							(!media || (media.includes('px') &&
+								(((i = media.indexOf('min-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth > parseInt(
+										media.substr(i + 1))) ||
+									((i = media.indexOf('max-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth < parseInt(
+										media.substr(i + 1)))))))
+							parent.attrs.src = attrs.src;
+					}
+			} else if (node.name == 'base' && !this.domain) this.domain = node.attrs.href;
+		}
+		if (this.data[this.i] == '/') this.i++;
+		this.start = this.i + 1;
+		this.state = this.Text;
+	}
+	// 移除标签
+	remove(node) {
+		var name = node.name,
+			j = this.i;
+		while (1) {
+			if ((this.i = this.data.indexOf('</', this.i + 1)) == -1) {
+				if (name == 'pre' || name == 'svg') this.i = j;
+				else this.i = this.data.length;
+				return;
+			}
+			this.start = (this.i += 2);
+			while (!blankChar[this.data[this.i]] && !this.isClose()) this.i++;
+			if (this.getName(this.section()) == name) {
+				// 代码块高亮
+				if (name == 'pre') {
+					this.data = this.data.substr(0, j + 1) + cfg.highlight(this.data.substring(j + 1, this.i - 5), node.attrs) +
+						this.data.substr(this.i - 5);
+					return this.i = j;
+				} else if (name == 'style')
+					this.CssHandler.getStyle(this.data.substring(j + 1, this.i - 7));
+				else if (name == 'title')
+					this.title = this.data.substring(j + 1, this.i - 7);
+				if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
+				// 处理 svg
+				if (name == 'svg') {
+					var src = this.data.substring(j, this.i + 1);
+					if (!node.attrs.xmlns) src = ' xmlns="http://www.w3.org/2000/svg"' + src;
+					var i = j;
+					while (this.data[j] != '<') j--;
+					src = this.data.substring(j, i) + src;
+					var parent = this.STACK[this.STACK.length - 1];
+					if (node.attrs.width == '100%' && parent && (parent.attrs.style || '').includes('inline'))
+						parent.attrs.style = 'width:300px;max-width:100%;' + parent.attrs.style;
+					this.siblings().push({
+						name: 'img',
+						attrs: {
+							src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+							ignore: 'T'
+						}
+					})
+				}
+				return;
+			}
+		}
+	}
+	// 处理属性
+	matchAttr(node) {
+		var attrs = node.attrs,
+			style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
+			styleObj = {};
+		if (attrs.id) {
+			if (this.compress & 1) attrs.id = void 0;
+			else if (this.useAnchor) this.bubble();
+		}
+		if ((this.compress & 2) && attrs.class) attrs.class = void 0;
+		switch (node.name) {
+			case 'img':
+				if (attrs['data-src']) {
+					attrs.src = attrs.src || attrs['data-src'];
+					attrs['data-src'] = void 0;
+				}
+				if (attrs.src && !attrs.ignore) {
+					if (this.bubble()) attrs.i = (this.imgNum++).toString();
+					else attrs.ignore = 'T';
+				}
+				break;
+			case 'a':
+			case 'ad':
+			// #ifdef APP-PLUS
+			case 'iframe':
+			case 'embed':
+			// #endif
+				this.bubble();
+				break;
+			case 'font':
+				if (attrs.color) {
+					styleObj['color'] = attrs.color;
+					attrs.color = void 0;
+				}
+				if (attrs.face) {
+					styleObj['font-family'] = attrs.face;
+					attrs.face = void 0;
+				}
+				if (attrs.size) {
+					var size = parseInt(attrs.size);
+					if (size < 1) size = 1;
+					else if (size > 7) size = 7;
+					var map = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
+					styleObj['font-size'] = map[size - 1];
+					attrs.size = void 0;
+				}
+				break;
+			case 'video':
+			case 'audio':
+				if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
+				else this[`${node.name}Num`]++;
+				if (node.name == 'video') {
+					if (attrs.width) {
+						style = `width:${parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')};${style}`;
+						attrs.width = void 0;
+					}
+					if (attrs.height) {
+						style = `height:${parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')};${style}`;
+						attrs.height = void 0;
+					}
+					if (this.videoNum > 3) node.lazyLoad = true;
+				}
+				attrs.source = [];
+				if (attrs.src) attrs.source.push(attrs.src);
+				if (!attrs.controls && !attrs.autoplay)
+					console.warn(`存在没有 controls 属性的 ${node.name} 标签,可能导致无法播放`, node);
+				this.bubble();
+				break;
+			case 'td':
+			case 'th':
+				if (attrs.colspan || attrs.rowspan)
+					for (var k = this.STACK.length, item; item = this.STACK[--k];)
+						if (item.name == 'table') {
+							item.c = void 0;
+							break;
+						}
+		}
+		if (attrs.align) {
+			styleObj['text-align'] = attrs.align;
+			attrs.align = void 0;
+		}
+		// 压缩 style
+		var styles = style.replace(/&quot;/g, '"').replace(/&amp;/g, '&').split(';');
+		style = '';
+		for (var i = 0, len = styles.length; i < len; i++) {
+			var info = styles[i].split(':');
+			if (info.length < 2) continue;
+			let key = info[0].trim().toLowerCase(),
+				value = info.slice(1).join(':').trim();
+			if (value.includes('-webkit') || value.includes('-moz') || value.includes('-ms') || value.includes('-o') || value
+				.includes(
+					'safe'))
+				style += `;${key}:${value}`;
+			else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
+				styleObj[key] = value;
+		}
+		if (node.name == 'img' && parseInt(styleObj.width || attrs.width) > screenWidth)
+			styleObj.height = 'auto';
+		for (var key in styleObj) {
+			var value = styleObj[key];
+			if (key.includes('flex') || key == 'order' || key == 'self-align') node.c = 1;
+			// 填充链接
+			if (value.includes('url')) {
+				var j = value.indexOf('(');
+				if (j++ != -1) {
+					while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) j++;
+					value = value.substr(0, j) + this.getUrl(value.substr(j));
+				}
+			}
+			// 转换 rpx
+			else if (value.includes('rpx'))
+				value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * screenWidth / 750 + 'px');
+			else if (key == 'white-space' && value.includes('pre'))
+				this.pre = node.pre = true;
+			style += `;${key}:${value}`;
+		}
+		style = style.substr(1);
+		if (style) attrs.style = style;
+	}
+	// 节点出栈处理
+	popNode(node) {
+		// 空白符处理
+		if (node.pre) {
+			node.pre = this.pre = void 0;
+			for (let i = this.STACK.length; i--;)
+				if (this.STACK[i].pre)
+					this.pre = true;
+		}
+		if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
+			return this.siblings().pop();
+		var attrs = node.attrs;
+		// 替换一些标签名
+		if (node.name == 'picture') {
+			node.name = 'img';
+			if (!attrs.src && (node.children[0] || '').name == 'img')
+				attrs.src = node.children[0].attrs.src;
+			if (attrs.src && !attrs.ignore)
+				attrs.i = (this.imgNum++).toString();
+			return node.children = void 0;
+		}
+		if (cfg.blockTags[node.name]) node.name = 'div';
+		else if (!cfg.trustTags[node.name]) node.name = 'span';
+		// 处理列表
+		if (node.c) {
+			if (node.name == 'ul') {
+				var floor = 1;
+				for (let i = this.STACK.length; i--;)
+					if (this.STACK[i].name == 'ul') floor++;
+				if (floor != 1)
+					for (let i = node.children.length; i--;)
+						node.children[i].floor = floor;
+			} else if (node.name == 'ol') {
+				for (let i = 0, num = 1, child; child = node.children[i++];)
+					if (child.name == 'li') {
+						child.type = 'ol';
+						child.num = ((num, type) => {
+							if (type == 'a') return String.fromCharCode(97 + (num - 1) % 26);
+							if (type == 'A') return String.fromCharCode(65 + (num - 1) % 26);
+							if (type == 'i' || type == 'I') {
+								num = (num - 1) % 99 + 1;
+								var one = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
+									ten = ['X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
+									res = (ten[Math.floor(num / 10) - 1] || '') + (one[num % 10 - 1] || '');
+								if (type == 'i') return res.toLowerCase();
+								return res;
+							}
+							return num;
+						})(num++, attrs.type) + '.';
+					}
+			}
+		}
+		// 处理表格的边框
+		if (node.name == 'table') {
+			var padding = attrs.cellpadding,
+				spacing = attrs.cellspacing,
+				border = attrs.border;
+			if (node.c) {
+				this.bubble();
+				if (!padding) padding = 2;
+				if (!spacing) spacing = 2;
+			}
+			if (border) attrs.style = `border:${border}px solid gray;${attrs.style || ''}`;
+			if (spacing) attrs.style = `border-spacing:${spacing}px;${attrs.style || ''}`;
+			if (border || padding)
+				(function f(ns) {
+					for (var i = 0, n; n = ns[i]; i++) {
+						if (n.name == 'th' || n.name == 'td') {
+							if (border) n.attrs.style = `border:${border}px solid gray;${n.attrs.style}`;
+							if (padding) n.attrs.style = `padding:${padding}px;${n.attrs.style}`;
+						} else f(n.children || []);
+					}
+				})(node.children)
+		}
+		this.CssHandler.pop && this.CssHandler.pop(node);
+		// 自动压缩
+		if (node.name == 'div' && !Object.keys(attrs).length) {
+			var siblings = this.siblings();
+			if (node.children.length == 1 && node.children[0].name == 'div')
+				siblings[siblings.length - 1] = node.children[0];
+		}
+	}
+	// 工具函数
+	bubble() {
+		for (var i = this.STACK.length, item; item = this.STACK[--i];) {
+			if (cfg.richOnlyTags[item.name]) {
+				if (item.name == 'table' && !Object.hasOwnProperty.call(item, 'c')) item.c = 1;
+				return false;
+			}
+			item.c = 1;
+		}
+		return true;
+	}
+	getName = val => this.xml ? val : val.toLowerCase();
+	getUrl(url) {
+		if (url[0] == '/') {
+			if (url[1] == '/') url = this.protocol + ':' + url;
+			else if (this.domain) url = this.domain + url;
+		} else if (this.domain && url.indexOf('data:') != 0 && !url.includes('://'))
+			url = this.domain + '/' + url;
+		return url;
+	}
+	isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
+	section = () => this.data.substring(this.start, this.i);
+	siblings = () => this.STACK.length ? this.STACK[this.STACK.length - 1].children : this.DOM;
+	// 状态机
+	Text(c) {
+		if (c == '<') {
+			var next = this.data[this.i + 1],
+				isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+			if (isLetter(next)) {
+				this.setText();
+				this.start = this.i + 1;
+				this.state = this.TagName;
+			} else if (next == '/') {
+				this.setText();
+				if (isLetter(this.data[++this.i + 1])) {
+					this.start = this.i + 1;
+					this.state = this.EndTag;
+				} else
+					this.Comment();
+			} else if (next == '!') {
+				this.setText();
+				this.Comment();
+			}
+		}
+	}
+	Comment() {
+		var key;
+		if (this.data.substring(this.i + 2, this.i + 4) == '--') key = '-->';
+		else if (this.data.substring(this.i + 2, this.i + 9) == '[CDATA[') key = ']]>';
+		else key = '>';
+		if ((this.i = this.data.indexOf(key, this.i + 2)) == -1) this.i = this.data.length;
+		else this.i += key.length - 1;
+		this.start = this.i + 1;
+		this.state = this.Text;
+	}
+	TagName(c) {
+		if (blankChar[c]) {
+			this.tagName = this.section();
+			while (blankChar[this.data[this.i]]) this.i++;
+			if (this.isClose()) this.setNode();
+			else {
+				this.start = this.i;
+				this.state = this.AttrName;
+			}
+		} else if (this.isClose()) {
+			this.tagName = this.section();
+			this.setNode();
+		}
+	}
+	AttrName(c) {
+		var blank = blankChar[c];
+		if (blank) {
+			this.attrName = this.section();
+			c = this.data[this.i];
+		}
+		if (c == '=') {
+			if (!blank) this.attrName = this.section();
+			while (blankChar[this.data[++this.i]]);
+			this.start = this.i--;
+			this.state = this.AttrValue;
+		} else if (blank) this.setAttr();
+		else if (this.isClose()) {
+			this.attrName = this.section();
+			this.setAttr();
+		}
+	}
+	AttrValue(c) {
+		if (c == '"' || c == "'") {
+			this.start++;
+			if ((this.i = this.data.indexOf(c, this.i + 1)) == -1) return this.i = this.data.length;
+			this.attrVal = this.section();
+			this.i++;
+		} else {
+			for (; !blankChar[this.data[this.i]] && !this.isClose(); this.i++);
+			this.attrVal = this.section();
+		}
+		this.setAttr();
+	}
+	EndTag(c) {
+		if (blankChar[c] || c == '>' || c == '/') {
+			var name = this.getName(this.section());
+			for (var i = this.STACK.length; i--;)
+				if (this.STACK[i].name == name) break;
+			if (i != -1) {
+				var node;
+				while ((node = this.STACK.pop()).name != name);
+				this.popNode(node);
+			} else if (name == 'p' || name == 'br')
+				this.siblings().push({
+					name,
+					attrs: {}
+				});
+			this.i = this.data.indexOf('>', this.i);
+			this.start = this.i + 1;
+			if (this.i == -1) this.i = this.data.length;
+			else this.state = this.Text;
+		}
+	}
+}
+module.exports = MpHtmlParser;

+ 80 - 0
components/jyf-parser/libs/config.js

@@ -0,0 +1,80 @@
+/* 配置文件 */
+// #ifdef MP-WEIXIN
+const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
+// #endif
+module.exports = {
+	// 过滤器函数
+	filter: null,
+	// 代码高亮函数
+	highlight: null,
+	// 文本处理函数
+	onText: null,
+	blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
+	// 块级标签,将被转为 div
+	blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,section' + (
+		// #ifdef MP-WEIXIN
+		canIUse ? '' :
+		// #endif
+		',pre')),
+	// 将被移除的标签
+	ignoreTags: makeMap(
+		'area,base,basefont,canvas,command,frame,input,isindex,keygen,link,map,meta,param,script,source,style,svg,textarea,title,track,use,wbr'
+		// #ifdef MP-WEIXIN
+		+ (canIUse ? ',rp' : '')
+		// #endif
+		// #ifndef APP-PLUS
+		+ ',embed,iframe'
+		// #endif
+	),
+	// 只能被 rich-text 显示的标签
+	richOnlyTags: makeMap('a,colgroup,fieldset,legend,picture,table'
+		// #ifdef MP-WEIXIN
+		+ (canIUse ? ',bdi,bdo,caption,rt,ruby' : '')
+		// #endif
+	),
+	// 自闭合的标签
+	selfClosingTags: makeMap(
+		'area,base,basefont,br,col,circle,ellipse,embed,frame,hr,img,input,isindex,keygen,line,link,meta,param,path,polygon,rect,source,track,use,wbr'
+	),
+	// 信任的属性
+	trustAttrs: makeMap(
+		'align,alt,app-id,author,autoplay,border,cellpadding,cellspacing,class,color,colspan,controls,data-src,dir,face,height,href,id,ignore,loop,media,muted,name,path,poster,rowspan,size,span,src,start,style,type,unit-id,width,xmlns'
+	),
+	// bool 型的属性
+	boolAttrs: makeMap('autoplay,controls,ignore,loop,muted'),
+	// 信任的标签
+	trustTags: makeMap(
+		'a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'
+		// #ifdef MP-WEIXIN
+		+ (canIUse ? ',bdi,bdo,caption,pre,rt,ruby' : '')
+		// #endif
+		// #ifdef APP-PLUS
+		+ ',embed,iframe'
+		// #endif
+	),
+	// 默认的标签样式
+	userAgentStyles: {
+		address: 'font-style:italic',
+		big: 'display:inline;font-size:1.2em',
+		blockquote: 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px',
+		caption: 'display:table-caption;text-align:center',
+		center: 'text-align:center',
+		cite: 'font-style:italic',
+		dd: 'margin-left:40px',
+		img: 'max-width:100%',
+		mark: 'background-color:yellow',
+		picture: 'max-width:100%',
+		pre: 'font-family:monospace;white-space:pre;overflow:scroll',
+		s: 'text-decoration:line-through',
+		small: 'display:inline;font-size:0.8em',
+		u: 'text-decoration:underline'
+	}
+}
+
+function makeMap(maprichee55text9oppplugin) {
+	var map = {},
+		list = maprichee55text9oppplugin.split(',');
+	for (var i = list.length; i--;)
+		map[list[i]] = true;
+	return map;
+}

+ 35 - 0
components/jyf-parser/libs/handler.sjs

@@ -0,0 +1,35 @@
+var inlineTags = {
+	abbr: 1,
+	b: 1,
+	big: 1,
+	code: 1,
+	del: 1,
+	em: 1,
+	i: 1,
+	ins: 1,
+	label: 1,
+	q: 1,
+	small: 1,
+	span: 1,
+	strong: 1
+}
+export default {
+	// 从顶层标签的样式中取出一些给 rich-text
+	getStyle: function(style) {
+		if (style) {
+			var i, j, res = '';
+			if ((i = style.indexOf('display')) != -1)
+				res = style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			if ((i = style.indexOf('float')) != -1)
+				res += ';' + style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			return res;
+		}
+	},
+	getNode: function(item) {
+		return [item];
+	},
+	// 是否通过 rich-text 显示
+	useRichText: function(item) {
+		return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1;
+	}
+}

+ 44 - 0
components/jyf-parser/libs/handler.wxs

@@ -0,0 +1,44 @@
+var inlineTags = {
+	abbr: 1,
+	b: 1,
+	big: 1,
+	code: 1,
+	del: 1,
+	em: 1,
+	i: 1,
+	ins: 1,
+	label: 1,
+	q: 1,
+	small: 1,
+	span: 1,
+	strong: 1
+}
+module.exports = {
+	// 从顶层标签的样式中取出一些给 rich-text
+	getStyle: function(style) {
+		if (style) {
+			var i, j, res = '';
+			if ((i = style.indexOf('display')) != -1)
+				res = style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			if ((i = style.indexOf('float')) != -1)
+				res += ';' + style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			return res;
+		}
+	},
+	// 处理懒加载
+	getNode: function(item, imgLoad) {
+		if (!imgLoad && item.attrs.i != '0') {
+			var img = {
+				name: 'img',
+				attrs: JSON.parse(JSON.stringify(item.attrs))
+			}
+			delete img.attrs.src;
+			img.attrs.style += ';width:20px;height:20px';
+			return [img];
+		} else return [item];
+	},
+	// 是否通过 rich-text 显示
+	useRichText: function(item) {
+		return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1;
+	}
+}

+ 476 - 0
components/jyf-parser/libs/trees.vue

@@ -0,0 +1,476 @@
+<!--
+  trees 递归显示组件
+  github:https://github.com/jin-yufeng/Parser 
+  docs:https://jin-yufeng.github.io/Parser
+  插件市场:https://ext.dcloud.net.cn/plugin?id=805
+  author:JinYufeng
+  update:2020/04/13
+-->
+<template>
+	<view class="interlayer">
+		<block v-for="(n, index) in nodes" v-bind:key="index">
+			<!--图片-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
+			<rich-text v-if="n.name=='img'" :id="n.attrs.id" class="_img" :style="''+handler.getStyle(n.attrs.style)" :nodes="handler.getNode(n,!lazyLoad||imgLoad)"
+			 :data-attrs="n.attrs" @tap="imgtap" @longpress="imglongtap" />
+			<!--#endif-->
+			<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
+			<rich-text v-if="n.name=='img'" :id="n.attrs.id" class="_img" :style="n.attrs.contain" :nodes='[n]' :data-attrs="n.attrs"
+			 @tap="imgtap" @longpress="imglongtap" />
+			<!--#endif-->
+			<!--文本-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
+			<rich-text v-else-if="n.decode" class="_entity" :nodes="[n]"></rich-text>
+			<!--#endif-->
+			<text v-else-if="n.type=='text'" decode>{{n.text}}</text>
+			<text v-else-if="n.name=='br'">\n</text>
+			<!--视频-->
+			<view v-else-if="n.name=='video'">
+				<view v-if="(!loadVideo||n.lazyLoad)&&!(controls[n.attrs.id]&&controls[n.attrs.id].play)" :id="n.attrs.id" :class="'_video '+(n.attrs.class||'')"
+				 :style="n.attrs.style" @tap="_loadVideo" />
+				<video v-else :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay||(controls[n.attrs.id]&&controls[n.attrs.id].play)"
+				 :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.attrs.source[(controls[n.attrs.id]&&controls[n.attrs.id].index)||0]"
+				 :unit-id="n.attrs['unit-id']" :data-id="n.attrs.id" data-from="video" data-source="source" @error="error" @play="play" />
+			</view>
+			<!--音频-->
+			<audio v-else-if="n.name=='audio'" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :autoplay="n.attrs.autoplay"
+			 :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.attrs.source[(controls[n.attrs.id]&&controls[n.attrs.id].index)||0]"
+			 :data-id="n.attrs.id" data-from="audio" data-source="source" @error="error" @play="play" />
+			<!--链接-->
+			<view v-else-if="n.name=='a'" :class="'_a '+(n.attrs.class||'')" hover-class="_hover" :style="n.attrs.style"
+			 :data-attrs="n.attrs" @tap="linkpress">
+				<trees class="_span" :nodes="n.children" />
+			</view>
+			<!--广告(按需打开注释)-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO-->
+			<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :unit-id="n.attrs['unit-id']"
+			 data-from="ad" @error="error" />-->
+			<!--#endif-->
+			<!--#ifdef MP-BAIDU-->
+			<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :appid="n.attrs.appid"
+			 :apid="n.attrs.apid" :type="n.attrs.type" data-from="ad" @error="error" />-->
+			<!--#endif-->
+			<!--#ifdef APP-PLUS-->
+			<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :adpid="n.attrs.adpid"
+			 data-from="ad" @error="error" />-->
+			<!--#endif-->
+			<!--列表-->
+			<view v-else-if="n.name=='li'" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:flex'">
+				<view v-if="n.type=='ol'" class="_ol-bef">{{n.num}}</view>
+				<view v-else class="_ul-bef">
+					<view v-if="n.floor%3==0" class="_ul-p1">█</view>
+					<view v-else-if="n.floor%3==2" class="_ul-p2" />
+					<view v-else class="_ul-p1" style="border-radius:50%">█</view>
+				</view>
+				<!--#ifdef MP-ALIPAY-->
+				<view class="_li">
+					<trees :nodes="n.children" />
+				</view>
+				<!--#endif-->
+				<!--#ifndef MP-ALIPAY-->
+				<trees class="_li" :nodes="n.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+				<!--#endif-->
+			</view>
+			<!--表格-->
+			<view v-else-if="n.name=='table'&&n.c" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:table'">
+				<view v-for="(tbody, i) in n.children" v-bind:key="i" :class="tbody.attrs.class" :style="(tbody.attrs.style||'')+(tbody.name[0]=='t'?';display:table-'+(tbody.name=='tr'?'row':'row-group'):'')">
+					<view v-for="(tr, j) in tbody.children" v-bind:key="j" :class="tr.attrs.class" :style="(tr.attrs.style||'')+(tr.name[0]=='t'?';display:table-'+(tr.name=='tr'?'row':'cell'):'')">
+						<trees v-if="tr.name=='td'" :nodes="tr.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+						<block v-else>
+							<!--#ifdef MP-ALIPAY-->
+							<view v-for="(td, k) in tr.children" v-bind:key="k" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')">
+								<trees :nodes="td.children" />
+							</view>
+							<!--#endif-->
+							<!--#ifndef MP-ALIPAY-->
+							<trees v-for="(td, k) in tr.children" v-bind:key="k" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')"
+							 :nodes="td.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+							<!--#endif-->
+						</block>
+					</view>
+				</view>
+			</view>
+			<!--#ifdef APP-PLUS-->
+			<iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder"
+			 :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
+			<embed v-else-if="n.name=='embed'" :style="n.attrs.style" :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
+			<!--#endif-->
+			<!--富文本-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
+			<rich-text v-else-if="handler.useRichText(n)" :id="n.attrs.id" :class="'_p __'+n.name" :nodes="[n]" />
+			<!--#endif-->
+			<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
+			<rich-text v-else-if="!(n.c||n.continue)" :id="n.attrs.id" :class="_p" :style="n.attrs.contain" :nodes="[n]" />
+			<!--#endif-->
+			<!--#ifdef MP-ALIPAY-->
+			<view v-else :id="n.attrs.id" :class="'_'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style">
+				<trees :nodes="n.children" />
+			</view>
+			<!--#endif-->
+			<!--#ifndef MP-ALIPAY-->
+			<trees v-else :class="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style" :nodes="n.children"
+			 :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+			<!--#endif-->
+		</block>
+	</view>
+</template>
+<script module="handler" lang="wxs" src="./handler.wxs"></script>
+<script module="handler" lang="sjs" src="./handler.sjs"></script>
+<script>
+	global.Parser = {};
+	import trees from './trees'
+	export default {
+		components: {
+			trees
+		},
+		name: 'trees',
+		data() {
+			return {
+				controls: {},
+				// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
+				imgLoad: false,
+				// #endif
+				// #ifndef APP-PLUS
+				loadVideo: true
+				// #endif
+			}
+		},
+		props: {
+			nodes: Array,
+			// #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
+			lazyLoad: Boolean,
+			// #endif
+			// #ifdef APP-PLUS
+			loadVideo: Boolean
+			// #endif
+		},
+		mounted() {
+			// 获取顶层组件
+			this.top = this.$parent;
+			while (this.top.$options.name != 'parser') {
+				if (this.top.top) {
+					this.top = this.top.top;
+					break;
+				}
+				this.top = this.top.$parent;
+			}
+		},
+		// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
+		beforeDestroy() {
+			if (this.observer)
+				this.observer.disconnect();
+		},
+		// #endif
+		methods: {
+			// #ifndef MP-ALIPAY
+			play(e) {
+				if (this.top.videoContexts.length > 1 && this.top.autopause)
+					for (var i = this.top.videoContexts.length; i--;)
+						if (this.top.videoContexts[i].id != e.currentTarget.dataset.id)
+							this.top.videoContexts[i].pause();
+			},
+			// #endif
+			imgtap(e) {
+				var attrs = e.currentTarget.dataset.attrs;
+				if (!attrs.ignore) {
+					var preview = true, data = {
+						id: e.target.id,
+						src: attrs.src,
+						ignore: () => preview = false
+					};
+					global.Parser.onImgtap && global.Parser.onImgtap(data);
+					this.top.$emit('imgtap', data);
+					if (preview) {
+						var urls = this.top.imgList,
+							current = urls[attrs.i] ? parseInt(attrs.i) : (urls = [attrs.src], 0);
+						uni.previewImage({
+							current,
+							urls
+						})
+					}
+				}
+			},
+			imglongtap(e) {
+				var attrs = e.item.dataset.attrs;
+				if (!attrs.ignore)
+					this.top.$emit('imglongtap', {
+						id: e.target.id,
+						src: attrs.src
+					})
+			},
+			linkpress(e) {
+				var jump = true,
+					attrs = e.currentTarget.dataset.attrs;
+				attrs.ignore = () => jump = false;
+				global.Parser.onLinkpress && global.Parser.onLinkpress(attrs);
+				this.top.$emit('linkpress', attrs);
+				if (jump) {
+					// #ifdef MP
+					if (attrs['app-id']) {
+						return uni.navigateToMiniProgram({
+							appId: attrs['app-id'],
+							path: attrs.path
+						})
+					}
+					// #endif
+					if (attrs.href) {
+						if (attrs.href[0] == '#') {
+							if (this.top.useAnchor)
+								this.top.navigateTo({
+									id: attrs.href.substring(1)
+								})
+						} else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0) {
+							// #ifdef APP-PLUS
+							plus.runtime.openWeb(attrs.href);
+							// #endif
+							// #ifndef APP-PLUS
+							uni.setClipboardData({
+								data: attrs.href,
+								success: () =>
+									uni.showToast({
+										title: '链接已复制'
+									})
+							})
+							// #endif
+						} else
+							uni.navigateTo({
+								url: attrs.href
+							})
+					}
+				}
+			},
+			error(e) {
+				var context, target = e.currentTarget,
+					source = target.dataset.from;
+				if (source == 'video' || source == 'audio') {
+					// 加载其他 source
+					var index = this.controls[target.id] ? this.controls[target.id].index + 1 : 1;
+					if (index < target.dataset.source.length)
+						this.$set(this.controls, target.id + '.index', index);
+					if (source == 'video') context = uni.createVideoContext(target.id, this);
+				}
+				this.top && this.top.$emit('error', {
+					source,
+					target,
+					errMsg: e.detail.errMsg,
+					errCode: e.detail.errCode,
+					context
+				});
+			},
+			_loadVideo(e) {
+				this.$set(this.controls, e.currentTarget.id, {
+					play: true,
+					index: 0
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+	/* 在这里引入自定义样式 */
+
+	/* 链接和图片效果 */
+	._a {
+		display: inline;
+		color: #366092;
+		word-break: break-all;
+		padding: 1.5px 0 1.5px 0;
+	}
+
+	._hover {
+		opacity: 0.7;
+		text-decoration: underline;
+	}
+
+	._img {
+		display: inline-block;
+		text-indent: 0;
+	}
+
+	/* #ifdef MP-WEIXIN */
+	:host {
+		display: inline;
+	}
+
+	/* #endif */
+
+	/* #ifdef MP */
+	.interlayer {
+		align-content: inherit;
+		align-items: inherit;
+		display: inherit;
+		flex-direction: inherit;
+		flex-wrap: inherit;
+		justify-content: inherit;
+		width: 100%;
+		white-space: inherit;
+	}
+
+	/* #endif */
+
+	._b,
+	._strong {
+		font-weight: bold;
+	}
+
+	._blockquote,
+	._div,
+	._p,
+	._ol,
+	._ul,
+	._li {
+		display: block;
+	}
+
+	._code {
+		font-family: monospace;
+	}
+
+	._del {
+		text-decoration: line-through;
+	}
+
+	._em,
+	._i {
+		font-style: italic;
+	}
+
+	._h1 {
+		font-size: 2em;
+	}
+
+	._h2 {
+		font-size: 1.5em;
+	}
+
+	._h3 {
+		font-size: 1.17em;
+	}
+
+	._h5 {
+		font-size: 0.83em;
+	}
+
+	._h6 {
+		font-size: 0.67em;
+	}
+
+	._h1,
+	._h2,
+	._h3,
+	._h4,
+	._h5,
+	._h6 {
+		display: block;
+		font-weight: bold;
+	}
+
+	._ins {
+		text-decoration: underline;
+	}
+
+	._li {
+		flex: 1;
+		width: 0;
+	}
+
+	._ol-bef {
+		margin-right: 5px;
+		text-align: right;
+		width: 36px;
+	}
+
+	._ul-bef {
+		line-height: normal;
+		margin: 0 12px 0 23px;
+	}
+
+	._ol-bef,
+	._ul_bef {
+		flex: none;
+		user-select: none;
+	}
+
+	._ul-p1 {
+		display: inline-block;
+		height: 0.3em;
+		line-height: 0.3em;
+		overflow: hidden;
+		width: 0.3em;
+	}
+
+	._ul-p2 {
+		border: 0.05em solid black;
+		border-radius: 50%;
+		display: inline-block;
+		height: 0.23em;
+		width: 0.23em;
+	}
+
+	._q::before {
+		content: '"';
+	}
+
+	._q::after {
+		content: '"';
+	}
+
+	._sub {
+		font-size: smaller;
+		vertical-align: sub;
+	}
+
+	._sup {
+		font-size: smaller;
+		vertical-align: super;
+	}
+
+	/* #ifndef MP-WEIXIN */
+	._abbr,
+	._b,
+	._code,
+	._del,
+	._em,
+	._i,
+	._ins,
+	._label,
+	._q,
+	._span,
+	._strong,
+	._sub,
+	._sup {
+		display: inline;
+	}
+
+	/* #endif */
+
+	/* #ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY */
+	.__bdo,
+	.__bdi,
+	.__ruby,
+	.__rt,
+	._entity {
+		display: inline-block;
+	}
+
+	/* #endif */
+	._video {
+		background-color: black;
+		display: inline-block;
+		height: 225px;
+		position: relative;
+		width: 300px;
+	}
+
+	._video::after {
+		border-color: transparent transparent transparent white;
+		border-style: solid;
+		border-width: 15px 0 15px 30px;
+		content: '';
+		left: 50%;
+		margin: -15px 0 0 -15px;
+		position: absolute;
+		top: 50%;
+	}
+</style>

+ 36 - 0
components/page-pagination/changelog.md

@@ -0,0 +1,36 @@
+## 1.2.0(2023-06-21)
+1. 更改样式使用scss,未安装的请自行安装;
+2. 样式单位rpx更改为px;
+3. 新增 不显示页码边框 属性;
+4. 新增属性layout,自定义分页排版;
+5. 新增每页条数预设值属性(pageSizes),可选择每页条数,同时新增每页条数切换时触发的事件;
+6. 移除showAround、showPageInfo和showGoPage属性,功能合并到layout中;
+## 1.1.2(2022-11-09)
+解决nvue页面样式错乱问题
+## 1.1.0(2021-09-07)
+1. 解决 currentPage设置初始值时,页码不准确;
+2. 解决打开非第一页后再重新查询 ,当前页和跳转页都变成0;
+## 1.0.9(2021-08-26)
+1. 解决支付宝小程序在真机中,上页下页图标不显问题;
+2. 优化支付宝小程序中自定义分页显示;
+3. 优化图标,只取需要的图标,减小icon图标文件;
+## 1.0.8(2021-07-29)
+解决总页数为1时,仍可以点击下页或尾页的问题
+## 1.0.6(2021-06-25)
+1. 解决在 nvue 页面下显示不正常问题;
+2. 去掉 css 部分使用的scss,方便在没有安装scss编译插件下也能够运行项目;
+## 1.0.5(2021-05-25)
+解决页数从0开始的问题;
+## 1.0.4(2021-05-10)
+1. 新增 设置显示页码的个数;
+2. 新增 是否显示分页信息(总条数、每页条数等);
+3. 新增 页码input输入框,方便跳转页面;
+## 1.0.3(2021-05-09)
+修改部分样式
+## 1.0.2(2021-05-09)
+1. 新增 可设置页码按钮大小 ,设置属性 size;
+2. 新增 可自定义页码颜色,设置属性 color;
+## 1.0.1(2021-04-27)
+修改事件的返回值,去掉上页下页等事件,合并为一个 change 事件,详细信息见下面事件说明。
+## 1.0.0(2021-04-22)
+第一次提交

+ 35 - 0
components/page-pagination/components/page-pagination/icon/iconfont.css

@@ -0,0 +1,35 @@
+@font-face {
+	font-family: "icon";
+	/* Project id 2498003 */
+	src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAN0AAsAAAAACIwAAAMlAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDQAqEAIMbATYCJAMYCw4ABCAFhGcHThtVB8iemjxpZAwBC7GBVYDAaYggGvvf7Nl3a6pJDIsmiUz6kChFLAUankgFqpfw/ldn6oBYhwiGL8UJyzos2a9b0gloBx62o6mAuHXr2HdEw7tPMwtANtEZzawY/uHexS1L9FEfgJpz4ccsD6hAxpoWybix7OiA/BbZnUFe1DUCvXY1cb/WNTCBgi8EbkXxPFnVQRofFwoVkK8oUVriQ7fQoFOLeIcq6bn7Q7wNXz7+m/iS1Mza6fbjThnt//bZK1k70DzKH93pinD3hIqMzY3J4lm75imP2LPJQ2+dy9MVSHd1S9KkuT178Lut00EwtpVxNF6vrSFN8o9XVKKxYvvBdeyt/PJxERK//BAyv2oQgl+tCIVfbQUqaBN26cELvEeJm5pkISSpB337SFh4eFTkvHNb23Ht6btBkZLSG16KM0e+quXCtUJonkqu63uuDEur+O3OXT1//YwWOTF55vzVi9HkrB5Dz7kEqaXptBY1OXH6/IVrl64IogS9soJzhVKY5+TTtL8dNnp/NAy9MzGUVglVadpWelW613haFVSmIagJKf07GTsH1o9bziZ/vE5P+pTzESd/yg4KUuXoUden3H4/dP/lLTPGH9IiZVptkS49zLorf3d7Tsej8mlIQj53f4y59BdB8IN/OrEc0vCvuwv4OfDhFLJ0PA3zdkuAKb+VC4hadCut1JV3dxBo1niAxO8r+pOGUZ+n4o2BXYTuHFWApMscZN3mMYWwDlWffWi6nUKvDZ2z+4yx8xClB8uOIwjDrkEy6Btkw+5gCuENVJP+QDPsP/R6GEHL9VmO1M5A4CBjcRMrks1KHD4P6x0DumoWWJVzCpiqYDsuLSop+rvBCmyMEeoaLeOcYMJsFtzFdwOz2YbtzGYEiRcZOLfXFReTupsUSTYLascAAQdkmGgTppDYWBEfneff3hhArTITMCBmSsWolEzqx0oVKQFgujlWUNmNrKJaQ5XhOAIjGPJZYF2iCpg1jw1mr+9kBCRcEcMAl12dYhuNQDlFy/Msr7gGelnHtUiRo0SNFl3KExsDTzMQ7sUUavDJiaTUm6tLEQIAAAA=') format('woff2'),
+		url('//at.alicdn.com/t/font_2498003_mr6aek1y1y8.woff?t=1629946644041') format('woff'),
+		url('//at.alicdn.com/t/font_2498003_mr6aek1y1y8.ttf?t=1629946644041') format('truetype');
+}
+
+.icon {
+	font-family: "icon" !important;
+	font-size: 16px;
+	font-style: normal;
+	-webkit-font-smoothing: antialiased;
+	-moz-osx-font-smoothing: grayscale;
+}
+
+.icon-more:before {
+	content: "\e638";
+}
+
+.icon-left:before {
+	content: "\e607";
+}
+
+.icon-right:before {
+	content: "\e609";
+}
+
+.icon-left1:before {
+	content: "\e641";
+}
+
+.icon-right1:before {
+	content: "\e642";
+}

+ 713 - 0
components/page-pagination/components/page-pagination/page-pagination.vue

@@ -0,0 +1,713 @@
+<template>
+	<view class="contact page-pagination" :class="size=='large'?'size-large':size=='small'?'size-small':''">
+		<view class="page-con" :class="!showBorder&&'no-border'">
+			<view class="page-scroll">
+				<!-- #ifdef APP-NVUE -->
+				<scroll-view scroll-x="true">
+					<!-- #endif -->
+					<view class="page-scroll-child" style="position: absolute;right: 25rpx;">
+						<view v-for="(item, index) in setLayout" :key="index">
+							<template v-if="item == 'total'">
+								<!-- 总条数 -->
+								<text class="page-total pc-page">共 {{total}} 条</text>
+							</template>
+							<template v-if="item == 'first'">
+								<!-- 首页 -->
+								<view class="pag-btn start" v-if="!numAround" :style="{color}"
+									:class="[nowPage==1&&'btn-ban',btnText&&'btn-text']" @click="clickStart">
+									<!-- #ifdef APP-NVUE -->
+									<text>首页</text>
+									<!-- #endif -->
+									<!-- #ifndef APP-NVUE -->
+									<text v-if="btnText">首页</text>
+									<text v-else class="icon icon-left1"></text>
+									<!-- #endif -->
+								</view>
+							</template>
+							<template v-if="item == 'page'">
+								<!-- 简单模式 -->
+								<view v-if="mode=='simple'" class="page-num simple">
+									<text :style="{color}" style="margin-right: 5px;">{{nowPage}}</text>/ {{pageNum}}
+								</view>
+								<!-- 复杂模式 -->
+								<view class="page-num" v-else>
+									<template v-if="pageNum <= showPageSize">
+										<view v-for="page in pageNumArr" :key="page" class="pag-btn" :class="page==nowPage&&'active'"
+											:style="{color, backgroundColor:(page==nowPage?color:''), borderColor:(page==nowPage?color:'')}"
+											@click="clickPage(page)">{{page}}</view>
+									</template>
+									<template v-else>
+										<!-- 第一页页码 -->
+										<view class="pag-btn" :class="nowPage==1&&'active'"
+											:style="{color, backgroundColor:(nowPage==1?color:''), borderColor:(nowPage==1?color:'')}"
+											v-if="numAround" @click="clickPage(1)">1</view>
+										<!-- 左侧省略号 -->
+										<view class="pag-btn ellipsis-btn"
+											v-if="(forceEllipses && getFirstPage != 1) || (numAround && getFirstPage != 2)">
+											<text class="icon icon-more"></text>
+										</view>
+										<template v-for="(p, i) in showPageSize">
+											<template v-if="i<showPageSize-2">
+												<!-- 中间页码 -->
+												<view :key="i" class="pag-btn" :class="(getFirstPage+i)==nowPage&&'active'"
+													:style="{color, backgroundColor:((getFirstPage+i)==nowPage?color:''),borderColor:((getFirstPage+i)==nowPage?color:'')}"
+													@click="clickPage(getFirstPage+i)">
+													{{getFirstPage+i}}
+												</view>
+											</template>
+											<template v-else>
+												<!-- 若显示省略号,则页码只显示 showPageSize-2 页 -->
+												<view :key="i" v-if="!forceEllipses && !numAround" class="pag-btn"
+													:class="(getFirstPage+i)==nowPage&&'active'"
+													:style="{color, backgroundColor:((getFirstPage+i)==nowPage?color:''), borderColor:((getFirstPage+i)==nowPage?color:'')}"
+													@click="clickPage(getFirstPage+i)">
+													{{getFirstPage+i}}
+												</view>
+											</template>
+										</template>
+										<!-- 右侧省略号 -->
+										<view class="pag-btn ellipsis-btn"
+											v-if="(forceEllipses && getFirstPage<pageNum-(showPageSize-3)) || (numAround && getFirstPage < pageNum-(showPageSize-2))">
+											<text class="icon icon-more"></text>
+										</view>
+										<!-- 最后一页页码 -->
+										<view class="pag-btn last-page" :class="nowPage==pageNum&&'active'"
+											:style="{color,backgroundColor:(nowPage==pageNum?color:''),borderColor:(nowPage==pageNum?color:'')}"
+											v-if="numAround && getFirstPage < pageNum-(showPageSize-3)" @click="clickPage(pageNum)">{{pageNum}}</view>
+									</template>
+								</view>
+							</template>
+							<template v-if="item == 'last'">
+								<!-- 尾页 -->
+								<view class="pag-btn end" v-if="!numAround" :style="{color}"
+									:class="[pageNum<=nowPage&&'btn-ban',btnText&&'btn-text']" @click="clickEnd">
+									<!-- #ifdef APP-NVUE -->
+									<text>尾页</text>
+									<!-- #endif -->
+									<!-- #ifndef APP-NVUE -->
+									<text v-if="btnText">尾页</text>
+									<text v-else class="icon icon-right1"></text>
+									<!-- #endif -->
+								</view>
+							</template>
+							<template v-if="item == 'limit'">
+								<!-- 选择每页条数 -->
+								<view class="page-limit pc-page">
+									<picker v-model="pageSizeIndex" :range="newPageSizes" @change="changePageSize">
+										<text class="page-input">{{newPageSizes[pageSizeIndex]}}</text>
+										<text class="icon icon-left"></text>
+									</picker>
+								</view>
+							</template>
+							<template v-if="item == 'jumper'">
+								<!-- 前往页数 -->
+								<view class="page-go pc-page">前往第
+									<template v-if="trigger=='blur'">
+										<input class="page-input" v-model="inputPage" @blur="goPage" @input="onInput" />页
+									</template>
+									<template v-else>
+										<input class="page-input" v-model="inputPage" @input="onInput" />页
+										<text class="page-input-btn" @click="goPage">跳转</text>
+									</template>
+								</view>
+							</template>
+						</view>
+						<!-- #ifndef MP-WEIXIN -->
+						<view class="custom-info pc-page">
+							<slot />
+						</view>
+						<!-- #endif -->
+					</view>
+					<!-- #ifdef APP-NVUE -->
+				</scroll-view>
+				<!-- #endif -->
+			</view>
+		</view>
+		<view class="mobile-page">
+			<view class="custom-info">
+				<slot />
+			</view>
+			<view v-for="(item, index) in mLayout" :key="index">
+				<template v-if="item == 'total'">
+					<!-- 总条数 -->
+					<view class="page-total">共 {{total}} 条</view>
+				</template>
+				<template v-if="item == 'limit'">
+					<!-- 选择每页条数 -->
+					<view class="page-limit">
+						<picker v-model="pageSizeIndex" :range="newPageSizes" @change="changePageSize">
+							<text class="page-input">{{newPageSizes[pageSizeIndex]}}</text>
+							<text class="icon icon-left"></text>
+						</picker>
+					</view>
+				</template>
+				<template v-if="item == 'jumper'">
+					<!-- 前往页数 -->
+					<view class="page-go">前往第
+						<template v-if="trigger=='blur'">
+							<input class="page-input" v-model="inputPage" @blur="goPage" @input="onInput" />页
+						</template>
+						<template v-else>
+							<input class="page-input" v-model="inputPage" @input="onInput" />页
+							<text class="page-input-btn" @click="goPage">跳转</text>
+						</template>
+					</view>
+				</template>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	/**
+	 * @property {Number} total 数据总条数,默认0
+	 * @property {Number} pageSize 每页显示条数,默认10
+	 * @property {Number} currentPage 当前页,默认1
+	 * @property {String} mode = [multi|simple] 显示模式,默认multi
+	 *  @value multi 标准模式(默认)
+	 *  @value simple 简单模式,只显示当前页/总页数
+	 * @property {Boolean} btnText = [true|false] 上页、下页、首页和尾页按钮是否显示汉字,默认false使用箭头表示
+	 * @property {Boolean} numAround = [true|false] 是否用页码中的第一页和最后一页表示首尾页,将不在显示首尾页,默认false
+	 * @property {Boolean} forceEllipses = [true|false] 是否显示省略号,默认false
+	 * @property {Number} showPageSize 中间页码显示的个数,默认5
+	 * @property {String} size = [large|normal|small] 按钮大小,默认normal
+	 *  @value large 大号按钮
+	 *  @value normal 普通按钮(默认)
+	 *  @value small 小型按钮
+	 * @property {String} color 自定义页码颜色,默认#1989FA
+	 * @property {Boolean} showBorder = [true|false] 是否显示页码边框,默认true
+	 * @property {String} layout 自定义分页排版,及是否显示 总条数(total)、首页(first)、上页(prev)、页码(page)、下页(next)、尾页(last)、每页条数选项(limit)和页码跳转(jumper),默认顺序和排版为:first,prev,page,next,last
+	 * @property {String} trigger = [blur|click] 页码跳转触发方式,默认blur
+	 *  @value blur 失去焦点时触发(默认)
+	 *  @value click 点击跳转按钮触发
+	 * @property {Array} pageSizes 每页条数选项预设值,默认[10, 20, 50]
+	 * @event {Function} change 当页码改变时触发事件,返回参数e={currentPage,type},详情参数见文档
+	 * @event {Function} sizeChange 每页条数(pageSize)改变时会触发,返回参数e={pageSize}
+	 */
+	export default {
+		name: 'PagePagination',
+		props: {
+			total: { // 总条数
+				type: Number,
+				default: 0
+			},
+			pageSize: { // 每页条数
+				type: Number,
+				default: 8
+			},
+			currentPage: { // 当前页
+				type: Number,
+				default: 1
+			},
+			showPageSize: { // 显示的页码个数
+				type: Number,
+				default: 7
+			},
+			mode: { //显示模式
+				type: String,
+				default: "multi"
+			},
+			forceEllipses: { // 是否显示省略号
+				type: Boolean,
+				default: false
+			},
+			btnText: { // 是否显示汉字
+				type: Boolean,
+				default: false
+			},
+			numAround: { // 是否用页码表示首尾页
+				type: Boolean,
+				default: false
+			},
+			size: { // 按钮大小
+				type: String,
+				default: "normal"
+			},
+			color: { // 页码颜色
+				type: String,
+				default: ""
+			},
+			trigger: { // 跳转页码触发方式
+				type: String,
+				default: "blur"
+			},
+			layout: { // 自定义布局
+				type: String,
+				default: "first,prev,page,next,last"
+			},
+			showBorder: { // 是否显示页码边框
+				type: Boolean,
+				default: true
+			},
+			pageSizes: { // 每页条数选项预设值
+				type: Array,
+				default () {
+					return [10, 20, 50]
+				}
+			}
+		},
+		data() {
+			return {
+				nowPage: this.currentPage, //当前页
+				inputPage: this.currentPage, //input输入框绑定值
+				mLayout: [],
+				nowPageSize: this.pageSize, // 当前每页条数
+				pageSizeIndex: this.pageSizes.indexOf(this.pageSize),
+				newPageSizes: this.pageSizes.map(item => item + "条/页"),
+			}
+		},
+		watch: {
+			currentPage(val) {
+				this.nowPage = val > this.pageNum ? this.pageNum : val;
+				this.inputPage = val > this.pageNum ? this.pageNum : val;
+				if (val == 1) {
+					this.nowPage = 1;
+					this.inputPage = 1;
+				}
+			},
+			pageSize(val) {
+				this.nowPageSize = val;
+				this.pageSizeIndex = this.pageSizes.indexOf(val);
+				this.pageSizeEvent();
+			}
+		},
+		computed: {
+			// 总页数
+			pageNum() {
+				return Math.ceil(this.total / this.nowPageSize)
+			},
+			pageNumArr() { // 解决uni-app某些版本中,存在v-for中循环数字时从0开始
+				var pageNumArr = [];
+				for (let i = 0; i < this.pageNum; i++) {
+					pageNumArr.push(i + 1);
+				}
+				return pageNumArr;
+			},
+			// 计算显示的第一个页码
+			getFirstPage() {
+				let firstPage = 0;
+				let a = Math.floor((this.showPageSize - 1) / 2);
+				let b = Math.floor(this.showPageSize / 2) - 1;
+				if (this.nowPage <= a) {
+					if (!this.numAround) firstPage = 1;
+					else firstPage = 2;
+				} else if (this.nowPage >= this.pageNum - b) {
+					if (this.forceEllipses || this.numAround) firstPage = this.pageNum - this.showPageSize + 3;
+					else firstPage = this.pageNum - this.showPageSize + 1;
+				} else {
+					if (this.forceEllipses || this.numAround) firstPage = this.nowPage - a + 1;
+					else firstPage = this.nowPage - a;
+				}
+				return firstPage;
+			},
+			// 设置布局
+			setLayout() {
+				let layoutArr = this.layout.replace(/\s/g, "").split(",");
+				let arr = ["total", "limit", "jumper"];
+				this.mLayout = layoutArr.filter(item => arr.includes(item));
+				return layoutArr;
+			}
+		},
+		methods: {
+			//选择页码
+			clickPage(page) {
+				if (this.nowPage != page) {
+					this.nowPage = page;
+					this.change(page, 'page');
+				}
+			},
+			//点击上页
+			clickPrev() {
+				if (this.nowPage > 1) {
+					let nowPage = this.nowPage - 1;
+					this.nowPage = nowPage;
+					this.change(nowPage, 'prev');
+				}
+			},
+			//点击下页
+			clickNext() {
+				if (this.nowPage < this.pageNum) {
+					let nowPage = this.nowPage + 1;
+					this.nowPage = nowPage;
+					this.change(nowPage, 'next');
+				}
+			},
+			//点击首页
+			clickStart() {
+				if (this.nowPage != 1) {
+					let nowPage = 1;
+					this.nowPage = nowPage;
+					this.change(nowPage, 'homePage');
+				}
+			},
+			//点击尾页
+			clickEnd() {
+				if (this.nowPage != this.pageNum) {
+					let nowPage = this.pageNum;
+					this.nowPage = nowPage;
+					this.change(nowPage, 'endPage');
+				}
+			},
+			//页码改变时触发
+			change(nowPage, type) {
+				this.inputPage = nowPage;
+				this.$emit('change', nowPage, type);
+			},
+			onInput(e) {
+				let val = parseInt(e.target.value.replace(/[^\d]/g, ''));
+				setTimeout(_ => {
+					this.inputPage = val ? (val > this.pageNum ? this.pageNum : val) : '';
+				}, 10)
+			},
+			goPage() {
+				this.nowPage = parseInt(this.inputPage ? this.inputPage : '1');
+				this.inputPage = parseInt(this.inputPage ? this.inputPage : '1');
+				this.change(parseInt(this.inputPage), 'goPage');
+			},
+			changePageSize(e) { // 更改每页条数
+				this.pageSizeIndex = e.detail.value;
+				this.nowPageSize = this.pageSizes[e.detail.value];
+				this.pageSizeEvent();
+			},
+			pageSizeEvent() {
+				if (this.nowPage > this.pageNum) {
+					this.nowPage = this.pageNum;
+					this.inputPage = this.pageNum;
+				}
+				this.$emit('sizeChange', this.nowPageSize);
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@import 'icon/iconfont.css';
+	$font-size: 14px;
+	$font-color: #606266;
+	@mixin page-input {
+		font-size: $font-size;
+		color: $font-color;
+		height: 28px;
+		line-height: 28px;
+		border: 1px solid #DCDFE6;
+		background-color: #FFF;
+		border-radius: 3px;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		cursor: not-allowed;
+		/* #endif */
+	}
+
+	.page-pagination {
+		width: 100%;
+		/* #ifdef APP-NVUE */
+		width: 750rpx;
+		/* #endif */
+
+		.page-con {
+			width: 100%;
+			display: flex;
+			justify-content: center;
+			/* #ifdef APP-NVUE */
+			width: 750rpx;
+			/* #endif */
+
+			.page-scroll {
+				width: 100%;
+				display: flex;
+				/* #ifndef APP-NVUE */
+				overflow-x: auto;
+				overflow-y: hidden;
+				/* #endif */
+				/* #ifdef APP-NVUE */
+				width: 750rpx;
+				/* #endif */
+
+				.page-scroll-child {
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					height: 32px;
+					line-height: 30px;
+					position: absolute !important;
+					right: 25rpx !important;
+					margin-top: 25rpx !important;
+					/* #ifndef APP-NVUE */
+					margin: 0 auto;
+					padding: 0 0 0 10px;
+
+					/* #endif */
+
+					.pag-btn {
+						// background-color: #FFF;
+						color: #fff;
+						font-size: 12px;
+						border: 1px solid #EBEEF5;
+						padding: 0 11px;
+						/* #ifndef APP-NVUE */
+						cursor: pointer;
+						white-space: nowrap;
+						/* #endif */
+						margin-left: -1px;
+						display: flex;
+					}
+
+					.page-num {
+						display: flex;
+						flex-direction: row;
+					}
+
+					.pag-btn.active {
+						background-color: #2B3453;
+						color: #FFFFFF !important;
+						border: 1rpx solid #FFF !important;
+					}
+
+					.pag-btn.btn-ban {
+						color: #C0C4CC !important;
+						/* #ifndef APP-NVUE */
+						cursor: not-allowed;
+						/* #endif */
+					}
+
+					.ellipsis-btn {
+						padding: 0 1px;
+						color: #999999;
+						/* #ifndef APP-NVUE */
+						cursor: auto;
+						/* #endif */
+					}
+
+					.pag-btn.start,
+					.pag-btn.prev,
+					.pag-btn.next,
+					.pag-btn.end,
+					.pag-btn .icon-more {
+						padding: 0 7px;
+					}
+
+					.icon-left,
+					.icon-right {
+						font-size: 13px;
+					}
+
+					.icon-left1,
+					.icon-right1 {
+						font-size: 17px;
+					}
+
+					// 简单模式
+					.page-num.simple {
+						padding: 0 40px;
+						line-height: 34px;
+						color: #303133;
+						font-size: 16px;
+					}
+				}
+			}
+		}
+
+		// 无边框模式
+		.no-border .page-scroll .page-scroll-child {
+			position: absolute;
+			right: 25rpx;
+			.pag-btn {
+				/* #ifndef APP-NVUE */
+				border: none;
+				background: none;
+				/* #endif */
+				/* #ifdef APP-NVUE */
+				border: rgba(255, 255, 255, 0);
+				background-color: rgba(255, 255, 255, 0);
+				/* #endif */
+			}
+
+			.pag-btn.active {
+				border-radius: 4px;
+			}
+
+			.pag-btn.start,
+			.pag-btn.prev,
+			.pag-btn.next,
+			.pag-btn.end {
+				background-color: #F0F0F0 !important;
+				border-radius: 4px;
+				margin: 0 3px;
+			}
+
+			.page-num {
+				margin: 0 3px;
+			}
+		}
+
+		// 移动端
+		.mobile-page {
+			display: flex;
+			justify-content: center;
+			flex-direction: row;
+			align-items: center;
+			line-height: 28px;
+			margin-top: 5px;
+		}
+
+		/* 附加功能样式 */
+		// 总条数
+		.page-total {
+			display: flex;
+			color: $font-color;
+			font-size: $font-size;
+			margin: 0 5px;
+			/* #ifndef APP-NVUE */
+			white-space: nowrap;
+			/* #endif */
+		}
+
+		// 每页条数
+		.page-limit {
+			margin: 0 5px;
+			display: flex;
+			align-items: center;
+			position: relative;
+			/* #ifndef APP-NVUE */
+			white-space: nowrap;
+			/* #endif */
+
+			.page-input {
+				@include page-input;
+				/* #ifndef APP-NVUE */
+				outline: none;
+				/* #endif */
+				padding: 0 18px 0 6px;
+				display: flex;
+			}
+
+			.icon-left {
+				position: absolute;
+				right: 3px;
+				top: 1px;
+				color: #C0C4CC;
+				font-size: 12px;
+				font-weight: 100;
+				transform: rotate(-90deg);
+			}
+		}
+
+		// 跳转页码
+		.page-go {
+			color: $font-color;
+			font-size: $font-size;
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+			margin: 0 5px;
+			/* #ifndef APP-NVUE */
+			white-space: nowrap;
+			/* #endif */
+
+			.page-input {
+				@include page-input;
+				width: 46px;
+				margin: 0 5px;
+			}
+
+			.page-input-btn {
+				@include page-input;
+				padding: 0 10px;
+				margin-left: 5px;
+			}
+		}
+
+		// 自定义页码信息
+		.custom-info {
+			margin: 0 10px;
+			color: $font-color;
+			font-size: $font-size;
+		}
+	}
+
+	// 大号模式
+	.page-pagination.size-large .page-con .page-scroll-child {
+		height: 40px;
+		line-height: 38px;
+      position: absolute;
+		right: 25rpx;
+		.pag-btn {
+			padding: 0 15px;
+			font-size: 15px;
+		}
+
+		.pag-btn.start,
+		.pag-btn.prev,
+		.pag-btn.next,
+		.pag-btn.end,
+		.ellipsis-btn {
+			padding: 0 12px;
+		}
+
+		.pag-btn.btn-text {
+			padding: 0 6px;
+		}
+
+		.icon-left,
+		.icon-right {
+			font-size: 15px;
+		}
+
+		.icon-left1,
+		.icon-right1 {
+			font-size: 19px;
+		}
+	}
+
+	// 小号按钮
+	.page-pagination.size-small .page-con .page-scroll-child {
+		height: 24px;
+		line-height: 22px;
+      position: absolute;
+      right: 25rpx;
+		.pag-btn {
+			padding: 0 7px;
+		}
+
+		.pag-btn.start,
+		.pag-btn.prev,
+		.pag-btn.next,
+		.pag-btn.end {
+			padding: 0 5px;
+		}
+
+		.ellipsis-btn {
+			padding: 0 5px;
+		}
+
+		.icon-left,
+		.icon-right {
+			font-size: 12px;
+		}
+
+		.icon-left1,
+		.icon-right1 {
+			font-size: 15px;
+		}
+	}
+
+	/* #ifndef APP-NVUE */
+	.page-pagination .pc-page {
+		display: none;
+	}
+
+	.page-pagination .mobile-page {
+		display: flex;
+	}
+
+	@media screen and (min-width: 450px) {
+		.page-pagination .pc-page {
+			display: block;
+		}
+
+		.page-pagination .mobile-page {
+			display: none;
+		}
+	}
+
+	/* #endif */
+</style>

+ 77 - 0
components/page-pagination/package.json

@@ -0,0 +1,77 @@
+{
+  "id": "page-pagination",
+  "displayName": "简单实用的分页器",
+  "version": "1.2.0",
+  "description": "简单实用的分页器",
+  "keywords": [
+    "分页器"
+],
+  "repository": "https://gitee.com/lunc9932/pagination",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "插件不采集任何数据",
+      "permissions": "无"
+    },
+    "npmurl": "",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "u"
+        }
+      }
+    }
+  }
+}

+ 136 - 0
components/page-pagination/readme.md

@@ -0,0 +1,136 @@
+## 分页器
+### 简单介绍
+- 组件需要依赖 sass 插件 ,请自行安装;
+- 可自定义显示分页信息,如总条数,每页条数等;
+- 欢迎大家下载使用,项目源码示例:[https://gitee.com/lunc9932/pagination](https://gitee.com/lunc9932/pagination)
+- 若有插件导入失败,重启编辑器;
+
+### API
+#### 属性说明
+| 属性名           | 类型      | 默认值   | 说明              |
+|---------------|---------|---------|-----------------------|
+| total         | Number  | 0       | 数据总条数             |
+| pageSize      | Number  | 10      | 每页显示条数           |
+| currentPage   | Number  | 1       | 当前页数               |
+| mode          | String  | multi   | 显示模式,可选值:multi / simple<br>multi:标准模式;simple:简单模式,只显示当前页/总页数  |
+| btnText       | Boolean | false   | 上页、下页、首页和尾页是否使用汉字,默认使用箭头表示  |
+| numAround     | Boolean | false   | 是否保留页码中的第一页和最后一个,为true时不再显示首页和尾页,且中间默认使用省略号    |
+| forceEllipses | Boolean | false   | 总页码数 超过`showPageSize`时,两端页码是否显示省略号   |
+| showPageSize  | Number  | 5       | 显示的页码个数,当使用`numAround`和`forceEllipses`属性时,中间显示的页码个数将 减2   |
+| size          | String  | normal  | 按钮大小,可选值:large / normal / small<br>large:大号按钮;normal:普通按钮(默认);small:小型按钮    |
+| color         | String  | #1989FA | 自定义按钮文字颜色   |
+| showBorder    | Boolean | true    | 是否显示按钮页码边框     |
+| layout        | String  | first,prev,page,next,last    | 自定义分页排版,是否显示 总条数(total)、首页(first)、上页(prev)、页码(page)、下页(next)、尾页(last)、每页条数选项(limit)和页码跳转(jumper),中间用英文逗号分隔     |
+| trigger       | String  | blur    | 页码input输入框跳转页码触发方式,当`layout`值中包含 jumper 此项才生效,可选值:blur / click<br>blur:失去焦点时触发(不显示跳转按钮);click:点击跳转按钮触发   |
+| pageSizes     | Array   | [10, 20, 50]      | 每页条数选项预设值,可选择每页条数,当`layout`值中包含 limit 此项才生效        |
+
+注意:
+1. 当`showPageSize`属性数值过大时,可能会出现横向滚动条;
+2. 当`showPageSize`属性数值小于3,且`forceEllipses`或`numAround`为 true 时,中间页码将无法显示;
+3. 只有当 总页码数 超过`showPageSize`,且属性`forceEllipses`或`numAround`为 true 时,省略号才能显示出来;
+4. `layout`分页排版,同样可自定义分页信息显示顺序;
+5. 自定义分页信息只需在`<page-pagination>`标签内自定义即可,并不需要写属性;
+6. **原`showAround`(是否显示首尾页)、`showPageInfo`(是否显示分页信息)和`showGoPage`(是否显示input输入框)属性已失效,功能已合并到`layout`属性中;**
+7. 由于移动端屏幕宽度限制,total、limit和jumper 将显示在页码的下面;PC端(>=450px,可自行在组件样式中修改)显示一行且按照`layout`属性中的顺序显示;
+8. 总条数(total)和当前页数(currentPage)是可以动态改变的,请参考项目示例;
+
+#### 事件说明
+| 事件名        | 说明                | 返回值                            |
+|--------------|---------------------|-----------------------------------|
+| @change      | 页码改变时触发       | 返回值e={currentPage(Number),type(String)},currentPage:当前页;type:点击的类型,详细见下面说明      |
+| @sizeChange  | 切换每页条数选项预设值 改变`pageSize`时触发| 返回值e={pageSize(Number)},pageSize:每页条数                              |
+
+##### type返回类型
+| 类型     | 说明 |
+| -------- | ---- |
+| page     | 表示点击的是页码 |
+| prev     | 点击的是上页 |
+| next     | 点击的是下页 |
+| homePage | 点击的是首页 |
+| endPage  | 点击的是尾页 |
+| goPage   | 页码input输入框触发 |
+
+注意:
+1. 重复点击同一个页码,不会触发 change 事件,避免不必要的刷新;
+
+### 基本用法
+在`template`中使用组件
+
+```
+<view class="example-con">总页数:{{page.total}},当前第{{page.currentPage}}页</view>
+<view class="example-btn">
+  <view @click="changeTotal">更改总页数555条</view>
+  <view style="margin-left: 50px;" @click="changeCurrentPage">更改到第3页</view>
+</view>
+
+<view class="title">默认</view>
+<page-pagination :total="page.total" :pageSize="page.pageSize" :currentPage="page.currentPage"
+  @change="change"></page-pagination>
+
+<view class="title">简单模式(mode="simple")</view>
+<page-pagination :total="page.total" mode="simple"></page-pagination>
+
+<view class="title">无边框(showBorder="false")</view>
+<page-pagination :total="page.total" :currentPage="page.currentPage" :showBorder="false"></page-pagination>
+
+<view class="title">使用汉字表示(btnText)及显示省略号(forceEllipses)</view>
+<page-pagination :total="page.total" :btnText="true" :forceEllipses="true"></page-pagination>
+
+<view class="title">保留页码中的第一页和最后一页(numAround)</view>
+<page-pagination :total="page.total" :numAround="true" :showBorder="false"></page-pagination>
+
+<view class="title">附加功能(总条数、每页条数 及 页码跳转)(layout)</view>
+<page-pagination :total="page.total" layout="total,first,prev,page,next,last,limit,jumper"></page-pagination>
+
+<view class="title">附加功能自定义显示分页项及排列顺序 和<br>自定义每页显示个数选择器的选项设置(pageSizes)</view>
+<page-pagination class="test-pagination" :numAround="true" :total="page.total" layout="page,prev,next,limit,total"
+  :pageSizes="[10,50,60,100]" @sizeChange="sizeChange">
+</page-pagination>
+
+<view class="title">自定义显示分页信息</view>
+<page-pagination :total="page.total" :pageSize="page.pageSize" :showBorder="false" :numAround="true">
+  <view>total:{{page.total}},pageSize:{{page.pageSize}}</view>
+</page-pagination>
+
+<view class="title">大号按钮(size="large")</view>
+<page-pagination :total="page.total" :showBorder="false" :showPageSize="4" size="large"></page-pagination>
+
+<view class="title">小型按钮(size="small")</view>
+<page-pagination :total="page.total" :showBorder="false" :showPageSize="6" size="small"></page-pagination>
+
+<view class="title">自定义颜色(color="#F56C6C")</view>
+<page-pagination :total="page.total" :forceEllipses="true" :btnText="true" color="#F56C6C"></page-pagination>
+<page-pagination :total="page.total" :forceEllipses="true" :btnText="true" color="#67C23A"></page-pagination>
+<page-pagination :total="page.total" :forceEllipses="true" :btnText="true" color="#FF00E3"></page-pagination>
+```
+在`script`中使用
+
+```
+export default {
+  data() {
+    return {
+      page: {
+        total: 10000,
+        pageSize: 50,
+        currentPage: 2
+      }
+    }
+  },
+  methods: {
+    change(currentPage, type) { // 当前页改变
+      this.page.currentPage = currentPage;
+      console.log("点击了" + type + ",当前页:" + currentPage);
+    },
+    sizeChange(pageSize) { // 每页条数改变
+      this.page.pageSize = pageSize;
+      console.log("每页", pageSize, "条");
+    },
+    changeTotal() { // 更改总条数
+      this.page.total = 555
+    },
+    changeCurrentPage() { // 更改当前页
+      this.page.currentPage = 3
+    }
+  }
+}
+```

+ 132 - 0
components/sidebar/index.vue

@@ -0,0 +1,132 @@
+<template>
+	<u-popup :show="show" mode="left" @close="close" @open="open">
+		<view class="popupBox">
+			<view class="cancel" @click="close">
+				<image src="/static/img/img13.png"></image>
+			</view>
+			<view class="listBox">
+				<view class="listTpl flex_item"  v-for="item,index in list" :key="index" :class="{ atcive: currTab == index }" @click="navTo(item.url,index)">
+					<image class="icon" :src="item.icon" mode="widthFix"></image>
+					<view class="name">{{item.name}}</view>
+				</view>
+			</view>
+		</view>
+	</u-popup>
+</template>
+<script>
+	export default {
+		props:['currTab'],
+		data() {
+			return {
+				show: false,
+				list:[]
+			}
+		},
+		mounted() {},
+		methods: {
+			open(){
+				this.list = [{
+					name:'Home',
+					icon:'/static/img/img14.png',
+					url:'/pages/index/index'
+				},{
+					name:'BF swap',
+					icon:'/static/img/img15.png',
+					url:''
+				},{
+					name:this.$t('indexs.i2'),
+					icon:'/static/img/img16.png',
+					url:'/pages/supermarket/index'
+				},{
+					name:this.$t('indexs.i3'),
+					icon:'/static/img/img17.png',
+					url:''
+				},{
+					name:this.$t('indexs.i4'),
+					icon:'/static/img/img19.png',
+					url:'/pages/diagram/index'
+				},{
+					name:this.$t('indexs.i7'),
+					icon:'/static/img/img25.png',
+					url:''
+				},
+				{
+					name:this.$t('indexs.i5'),
+					icon:'/static/img/img18.png',
+					url:'https://t.me/Benfolds_cn'
+				}]
+				this.show = true
+			},
+			// 关闭弹窗
+			close() {
+				this.show = false
+			},
+			navTo(url,index){
+				this.show = false
+				if(index == 6){
+					window.open(url)
+				}else{
+					if (url == "") {
+						let url = '/pages/404/index?index='+index
+						uni.navigateTo({
+							url,
+							fail() {
+								uni.switchTab({
+									url,
+								});
+							},
+						});
+					} else {
+						uni.navigateTo({
+							url,
+							fail() {
+								uni.switchTab({
+									url,
+								});
+							},
+						});
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.popupBox{
+		width: 522rpx;
+		height: 100%;
+		background: #111111;
+		padding-left: 50rpx;
+		padding-right: 43rpx;
+		.cancel{
+			width: 100%;
+			text-align: right;
+			margin: 82rpx 0rpx 125rpx 0rpx;
+			image{
+				width: 30rpx;
+				height: 30rpx;
+			}
+		}
+		.listBox{
+			.listTpl{
+				font-size: 32rpx;
+				color: #FFFFFF;
+				padding: 25rpx 30rpx;
+				margin-bottom: 20rpx;
+				.icon{
+					width: 44rpx;
+					height: 43rpx;
+				}
+				.name{
+					padding-left: 29rpx;
+				}
+			}
+			.atcive{
+				box-shadow: -4rpx -4rpx 13rpx 0rpx rgba(168,168,206,0.13), 4rpx 4rpx 7rpx 3rpx rgba(0,0,0,0.4);
+				border-radius: 10rpx;
+				color: #FFCC12 !important;
+			}
+		}
+	}
+</style>

+ 188 - 0
components/topIndex/index.vue

@@ -0,0 +1,188 @@
+<template>
+	<view class="flex topBox">
+		<view class="userInfo flex_item">
+			<view class="flex_item user">
+				<image class="userIcon" src="/static/img/img01.png"></image>
+				<view class="userId">{{address}}</view>
+			</view>
+			<view class="ch" @click="selectLang">{{value}}</view>
+		</view>
+		<view class="navTab flex">
+			<view class="navItem flex_item" v-for="item,index in list" :key="index" 
+			:class="{ atcive: currTab == index }" @click="navTo(item.url,index)">
+				<!-- <image class="icon" :src="item.icon" mode="widthFix"></image> -->
+				<view class="name">{{item.name}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		mapState,
+		mapActions,
+		mapMutations
+	} from "vuex";
+	export default {
+		props:["currTab","address"],
+		data() {
+			return {
+				show: false,
+				list:[]
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		computed: {
+			...mapState({
+				langList: "langList",
+				lang: "lang",
+			}),
+			...mapState('user', ['userInfo']),
+			value() {
+				const value = this.langList.find((item) => {
+					console.log(this.lang, item.value);
+					return item.value == this.lang
+				}).value;
+				return value
+			}
+		},
+		methods: {
+			...mapActions({
+				setLang: "setLang",
+			}),
+			init(){
+				this.list = [{
+					name:'Home',
+					icon:'/static/img/img14.png',
+					url:'/pages/pc/index'
+				},{
+					name:'BF swap',
+					icon:'/static/img/img15.png',
+					url:'/pages/pc/404'
+				},{
+					name:this.$t('indexs.i2'),
+					icon:'/static/img/img16.png',
+					url:'/pages/pc/supermarket'
+				},{
+					name:this.$t('indexs.i3'),
+					icon:'/static/img/img17.png',
+					url:'/pages/pc/404'
+				},{
+					name:this.$t('indexs.i4'),
+					icon:'/static/img/img19.png',
+					url:'/pages/pc/diagram'
+				},{
+					name:this.$t('indexs.i7'),
+					icon:'/static/img/img25.png',
+					url:'/pages/pc/404'
+				},
+				{
+					name:this.$t('indexs.i5'),
+					icon:'/static/img/img18.png',
+					url:'https://t.me/Benfolds_cn'
+				}]
+			},
+			//切换语言
+			selectLang() {
+				let value = ''
+				if(this.value == 'cn'){
+					value = 0
+				}else{
+					value = 1
+				}
+				this.setLang(this.langList[value].value);
+				this.init();
+			},
+			navTo(url,index){
+				if(index == 6){
+					window.open(url)
+				}else{
+					if (url == "/pages/pc/404") {
+						let url = '/pages/pc/404?index='+ index
+						uni.navigateTo({
+							url,
+							fail() {
+								uni.switchTab({
+									url,
+								});
+							},
+						});
+					} else {
+						uni.navigateTo({
+							url,
+							fail() {
+								uni.switchTab({
+									url,
+								});
+							},
+						});
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{
+		min-width: 1800px;
+	}
+	.topBox{
+		border-bottom: 1rpx solid #4F5256;
+		padding: 10px 30px;
+		position: fixed;
+		width: 100%;
+		background: #080401;
+		z-index: 99;
+		.userInfo{
+			color: #FFD515;
+			min-width: 400px;
+			.ch{
+				border: 4rpx solid #FFD515;
+				border-radius: 7rpx;
+				font-size: 21rpx;
+				padding: 4rpx 4rpx;
+				margin-left: 30rpx;
+				cursor: pointer;
+			}
+			.user{
+				width: 83%;
+				background: #2B2929;
+				border-radius: 10rpx;
+				padding: 12rpx 15rpx;
+				.userIcon{
+					width: 34rpx;
+					height: 34rpx;
+					margin-right: 10rpx;
+				}
+				.userId{
+					width: calc(100% - 34rpx);
+					font-size: 25rpx;
+				}
+			}
+		}
+		.navTab{
+			min-width: 1000px;
+			.navItem{
+				font-size: 32rpx;
+				color: #FFFFFF;
+				padding: 25rpx 30rpx;
+				margin-bottom: 20rpx;
+				cursor: pointer;
+				.icon{
+					width: 44rpx;
+					height: 43rpx;
+				}
+				.name{
+					padding-left: 29rpx;
+				}
+			}
+			.atcive{
+				box-shadow: -4rpx -4rpx 13rpx 0rpx rgba(168,168,206,0.13), 4rpx 4rpx 7rpx 3rpx rgba(0,0,0,0.4);
+				border-radius: 10rpx;
+				color: #FFCC12 !important;
+			}
+		}
+	}
+</style>

+ 1356 - 0
components/wangding-pickerAddress/data.js

@@ -0,0 +1,1356 @@
+export default [{
+	"name": "北京市",
+	"city": [{
+		"name": "市辖区",
+		"area": ["东城区", "西城区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区",
+			"怀柔区", "平谷区", "密云区", "延庆区"
+		]
+	}]
+}, {
+	"name": "天津市",
+	"city": [{
+		"name": "市辖区",
+		"area": ["和平区", "河东区", "河西区", "南开区", "河北区", "红桥区", "东丽区", "西青区", "津南区", "北辰区", "武清区", "宝坻区", "滨海新区",
+			"宁河区", "静海区", "蓟州区"
+		]
+	}]
+}, {
+	"name": "河北省",
+	"city": [{
+		"name": "石家庄市",
+		"area": ["市辖区", "长安区", "桥西区", "新华区", "井陉矿区", "裕华区", "藁城区", "鹿泉区", "栾城区", "井陉县", "正定县", "行唐县", "灵寿县",
+			"高邑县", "深泽县", "赞皇县", "无极县", "平山县", "元氏县", "赵县", "晋州市", "新乐市"
+		]
+	}, {
+		"name": "唐山市",
+		"area": ["市辖区", "路南区", "路北区", "古冶区", "开平区", "丰南区", "丰润区", "曹妃甸区", "滦县", "滦南县", "乐亭县", "迁西县", "玉田县",
+			"遵化市", "迁安市"
+		]
+	}, {
+		"name": "秦皇岛市",
+		"area": ["市辖区", "海港区", "山海关区", "北戴河区", "抚宁区", "青龙满族自治县", "昌黎县", "卢龙县"]
+	}, {
+		"name": "邯郸市",
+		"area": ["市辖区", "邯山区", "丛台区", "复兴区", "峰峰矿区", "邯郸县", "临漳县", "成安县", "大名县", "涉县", "磁县", "肥乡县", "永年县",
+			"邱县", "鸡泽县", "广平县", "馆陶县", "魏县", "曲周县", "武安市"
+		]
+	}, {
+		"name": "邢台市",
+		"area": ["市辖区", "桥东区", "桥西区", "邢台县", "临城县", "内丘县", "柏乡县", "隆尧县", "任县", "南和县", "宁晋县", "巨鹿县", "新河县",
+			"广宗县", "平乡县", "威县", "清河县", "临西县", "南宫市", "沙河市"
+		]
+	}, {
+		"name": "保定市",
+		"area": ["市辖区", "竞秀区", "莲池区", "满城区", "清苑区", "徐水区", "涞水县", "阜平县", "定兴县", "唐县", "高阳县", "容城县", "涞源县",
+			"望都县", "安新县", "易县", "曲阳县", "蠡县", "顺平县", "博野县", "雄县", "涿州市", "安国市", "高碑店市"
+		]
+	}, {
+		"name": "张家口市",
+		"area": ["市辖区", "桥东区", "桥西区", "宣化区", "下花园区", "万全区", "崇礼区", "张北县", "康保县", "沽源县", "尚义县", "蔚县", "阳原县",
+			"怀安县", "怀来县", "涿鹿县", "赤城县"
+		]
+	}, {
+		"name": "承德市",
+		"area": ["市辖区", "双桥区", "双滦区", "鹰手营子矿区", "承德县", "兴隆县", "平泉县", "滦平县", "隆化县", "丰宁满族自治县", "宽城满族自治县",
+			"围场满族蒙古族自治县"
+		]
+	}, {
+		"name": "沧州市",
+		"area": ["市辖区", "新华区", "运河区", "沧县", "青县", "东光县", "海兴县", "盐山县", "肃宁县", "南皮县", "吴桥县", "献县", "孟村回族自治县",
+			"泊头市", "任丘市", "黄骅市", "河间市"
+		]
+	}, {
+		"name": "廊坊市",
+		"area": ["市辖区", "安次区", "广阳区", "固安县", "永清县", "香河县", "大城县", "文安县", "大厂回族自治县", "霸州市", "三河市"]
+	}, {
+		"name": "衡水市",
+		"area": ["市辖区", "桃城区", "冀州区", "枣强县", "武邑县", "武强县", "饶阳县", "安平县", "故城县", "景县", "阜城县", "深州市"]
+	}, {
+		"name": "直辖县",
+		"area": ["定州市", "辛集市"]
+	}]
+}, {
+	"name": "山西省",
+	"city": [{
+		"name": "太原市",
+		"area": ["市辖区", "小店区", "迎泽区", "杏花岭区", "尖草坪区", "万柏林区", "晋源区", "清徐县", "阳曲县", "娄烦县", "古交市"]
+	}, {
+		"name": "大同市",
+		"area": ["市辖区", "城区", "矿区", "南郊区", "新荣区", "阳高县", "天镇县", "广灵县", "灵丘县", "浑源县", "左云县", "大同县"]
+	}, {
+		"name": "阳泉市",
+		"area": ["市辖区", "城区", "矿区", "郊区", "平定县", "盂县"]
+	}, {
+		"name": "长治市",
+		"area": ["市辖区", "城区", "郊区", "长治县", "襄垣县", "屯留县", "平顺县", "黎城县", "壶关县", "长子县", "武乡县", "沁县", "沁源县",
+			"潞城市"
+		]
+	}, {
+		"name": "晋城市",
+		"area": ["市辖区", "城区", "沁水县", "阳城县", "陵川县", "泽州县", "高平市"]
+	}, {
+		"name": "朔州市",
+		"area": ["市辖区", "朔城区", "平鲁区", "山阴县", "应县", "右玉县", "怀仁县"]
+	}, {
+		"name": "晋中市",
+		"area": ["市辖区", "榆次区", "榆社县", "左权县", "和顺县", "昔阳县", "寿阳县", "太谷县", "祁县", "平遥县", "灵石县", "介休市"]
+	}, {
+		"name": "运城市",
+		"area": ["市辖区", "盐湖区", "临猗县", "万荣县", "闻喜县", "稷山县", "新绛县", "绛县", "垣曲县", "夏县", "平陆县", "芮城县", "永济市",
+			"河津市"
+		]
+	}, {
+		"name": "忻州市",
+		"area": ["市辖区", "忻府区", "定襄县", "五台县", "代县", "繁峙县", "宁武县", "静乐县", "神池县", "五寨县", "岢岚县", "河曲县", "保德县",
+			"偏关县", "原平市"
+		]
+	}, {
+		"name": "临汾市",
+		"area": ["市辖区", "尧都区", "曲沃县", "翼城县", "襄汾县", "洪洞县", "古县", "安泽县", "浮山县", "吉县", "乡宁县", "大宁县", "隰县",
+			"永和县", "蒲县", "汾西县", "侯马市", "霍州市"
+		]
+	}, {
+		"name": "吕梁市",
+		"area": ["市辖区", "离石区", "文水县", "交城县", "兴县", "临县", "柳林县", "石楼县", "岚县", "方山县", "中阳县", "交口县", "孝义市",
+			"汾阳市"
+		]
+	}]
+}, {
+	"name": "内蒙古自治区",
+	"city": [{
+		"name": "呼和浩特市",
+		"area": ["市辖区", "新城区", "回民区", "玉泉区", "赛罕区", "土默特左旗", "托克托县", "和林格尔县", "清水河县", "武川县"]
+	}, {
+		"name": "包头市",
+		"area": ["市辖区", "东河区", "昆都仑区", "青山区", "石拐区", "白云鄂博矿区", "九原区", "土默特右旗", "固阳县", "达尔罕茂明安联合旗"]
+	}, {
+		"name": "乌海市",
+		"area": ["市辖区", "海勃湾区", "海南区", "乌达区"]
+	}, {
+		"name": "赤峰市",
+		"area": ["市辖区", "红山区", "元宝山区", "松山区", "阿鲁科尔沁旗", "巴林左旗", "巴林右旗", "林西县", "克什克腾旗", "翁牛特旗", "喀喇沁旗",
+			"宁城县", "敖汉旗"
+		]
+	}, {
+		"name": "通辽市",
+		"area": ["市辖区", "科尔沁区", "科尔沁左翼中旗", "科尔沁左翼后旗", "开鲁县", "库伦旗", "奈曼旗", "扎鲁特旗", "霍林郭勒市"]
+	}, {
+		"name": "鄂尔多斯市",
+		"area": ["市辖区", "东胜区", "康巴什区", "达拉特旗", "准格尔旗", "鄂托克前旗", "鄂托克旗", "杭锦旗", "乌审旗", "伊金霍洛旗"]
+	}, {
+		"name": "呼伦贝尔市",
+		"area": ["市辖区", "海拉尔区", "扎赉诺尔区", "阿荣旗", "莫力达瓦达斡尔族自治旗", "鄂伦春自治旗", "鄂温克族自治旗", "陈巴尔虎旗", "新巴尔虎左旗",
+			"新巴尔虎右旗", "满洲里市", "牙克石市", "扎兰屯市", "额尔古纳市", "根河市"
+		]
+	}, {
+		"name": "巴彦淖尔市",
+		"area": ["市辖区", "临河区", "五原县", "磴口县", "乌拉特前旗", "乌拉特中旗", "乌拉特后旗", "杭锦后旗"]
+	}, {
+		"name": "乌兰察布市",
+		"area": ["市辖区", "集宁区", "卓资县", "化德县", "商都县", "兴和县", "凉城县", "察哈尔右翼前旗", "察哈尔右翼中旗", "察哈尔右翼后旗", "四子王旗",
+			"丰镇市"
+		]
+	}, {
+		"name": "兴安盟",
+		"area": ["乌兰浩特市", "阿尔山市", "科尔沁右翼前旗", "科尔沁右翼中旗", "扎赉特旗", "突泉县"]
+	}, {
+		"name": "锡林郭勒盟",
+		"area": ["二连浩特市", "锡林浩特市", "阿巴嘎旗", "苏尼特左旗", "苏尼特右旗", "东乌珠穆沁旗", "西乌珠穆沁旗", "太仆寺旗", "镶黄旗", "正镶白旗",
+			"正蓝旗", "多伦县"
+		]
+	}, {
+		"name": "阿拉善盟",
+		"area": ["阿拉善左旗", "阿拉善右旗", "额济纳旗"]
+	}]
+}, {
+	"name": "辽宁省",
+	"city": [{
+		"name": "沈阳市",
+		"area": ["市辖区", "和平区", "沈河区", "大东区", "皇姑区", "铁西区", "苏家屯区", "浑南区", "沈北新区", "于洪区", "辽中区", "康平县",
+			"法库县", "新民市"
+		]
+	}, {
+		"name": "大连市",
+		"area": ["市辖区", "中山区", "西岗区", "沙河口区", "甘井子区", "旅顺口区", "金州区", "普兰店区", "长海县", "瓦房店市", "庄河市"]
+	}, {
+		"name": "鞍山市",
+		"area": ["市辖区", "铁东区", "铁西区", "立山区", "千山区", "台安县", "岫岩满族自治县", "海城市"]
+	}, {
+		"name": "抚顺市",
+		"area": ["市辖区", "新抚区", "东洲区", "望花区", "顺城区", "抚顺县", "新宾满族自治县", "清原满族自治县"]
+	}, {
+		"name": "本溪市",
+		"area": ["市辖区", "平山区", "溪湖区", "明山区", "南芬区", "本溪满族自治县", "桓仁满族自治县"]
+	}, {
+		"name": "丹东市",
+		"area": ["市辖区", "元宝区", "振兴区", "振安区", "宽甸满族自治县", "东港市", "凤城市"]
+	}, {
+		"name": "锦州市",
+		"area": ["市辖区", "古塔区", "凌河区", "太和区", "黑山县", "义县", "凌海市", "北镇市"]
+	}, {
+		"name": "营口市",
+		"area": ["市辖区", "站前区", "西市区", "鲅鱼圈区", "老边区", "盖州市", "大石桥市"]
+	}, {
+		"name": "阜新市",
+		"area": ["市辖区", "海州区", "新邱区", "太平区", "清河门区", "细河区", "阜新蒙古族自治县", "彰武县"]
+	}, {
+		"name": "辽阳市",
+		"area": ["市辖区", "白塔区", "文圣区", "宏伟区", "弓长岭区", "太子河区", "辽阳县", "灯塔市"]
+	}, {
+		"name": "盘锦市",
+		"area": ["市辖区", "双台子区", "兴隆台区", "大洼区", "盘山县"]
+	}, {
+		"name": "铁岭市",
+		"area": ["市辖区", "银州区", "清河区", "铁岭县", "西丰县", "昌图县", "调兵山市", "开原市"]
+	}, {
+		"name": "朝阳市",
+		"area": ["市辖区", "双塔区", "龙城区", "朝阳县", "建平县", "喀喇沁左翼蒙古族自治县", "北票市", "凌源市"]
+	}, {
+		"name": "葫芦岛市",
+		"area": ["市辖区", "连山区", "龙港区", "南票区", "绥中县", "建昌县", "兴城市"]
+	}]
+}, {
+	"name": "吉林省",
+	"city": [{
+		"name": "长春市",
+		"area": ["市辖区", "南关区", "宽城区", "朝阳区", "二道区", "绿园区", "双阳区", "九台区", "农安县", "榆树市", "德惠市"]
+	}, {
+		"name": "吉林市",
+		"area": ["市辖区", "昌邑区", "龙潭区", "船营区", "丰满区", "永吉县", "蛟河市", "桦甸市", "舒兰市", "磐石市"]
+	}, {
+		"name": "四平市",
+		"area": ["市辖区", "铁西区", "铁东区", "梨树县", "伊通满族自治县", "公主岭市", "双辽市"]
+	}, {
+		"name": "辽源市",
+		"area": ["市辖区", "龙山区", "西安区", "东丰县", "东辽县"]
+	}, {
+		"name": "通化市",
+		"area": ["市辖区", "东昌区", "二道江区", "通化县", "辉南县", "柳河县", "梅河口市", "集安市"]
+	}, {
+		"name": "白山市",
+		"area": ["市辖区", "浑江区", "江源区", "抚松县", "靖宇县", "长白朝鲜族自治县", "临江市"]
+	}, {
+		"name": "松原市",
+		"area": ["市辖区", "宁江区", "前郭尔罗斯蒙古族自治县", "长岭县", "乾安县", "扶余市"]
+	}, {
+		"name": "白城市",
+		"area": ["市辖区", "洮北区", "镇赉县", "通榆县", "洮南市", "大安市"]
+	}, {
+		"name": "延边朝鲜族自治州",
+		"area": ["延吉市", "图们市", "敦化市", "珲春市", "龙井市", "和龙市", "汪清县", "安图县"]
+	}]
+}, {
+	"name": "黑龙江省",
+	"city": [{
+		"name": "哈尔滨市",
+		"area": ["市辖区", "道里区", "南岗区", "道外区", "平房区", "松北区", "香坊区", "呼兰区", "阿城区", "双城区", "依兰县", "方正县", "宾县",
+			"巴彦县", "木兰县", "通河县", "延寿县", "尚志市", "五常市"
+		]
+	}, {
+		"name": "齐齐哈尔市",
+		"area": ["市辖区", "龙沙区", "建华区", "铁锋区", "昂昂溪区", "富拉尔基区", "碾子山区", "梅里斯达斡尔族区", "龙江县", "依安县", "泰来县",
+			"甘南县", "富裕县", "克山县", "克东县", "拜泉县", "讷河市"
+		]
+	}, {
+		"name": "鸡西市",
+		"area": ["市辖区", "鸡冠区", "恒山区", "滴道区", "梨树区", "城子河区", "麻山区", "鸡东县", "虎林市", "密山市"]
+	}, {
+		"name": "鹤岗市",
+		"area": ["市辖区", "向阳区", "工农区", "南山区", "兴安区", "东山区", "兴山区", "萝北县", "绥滨县"]
+	}, {
+		"name": "双鸭山市",
+		"area": ["市辖区", "尖山区", "岭东区", "四方台区", "宝山区", "集贤县", "友谊县", "宝清县", "饶河县"]
+	}, {
+		"name": "大庆市",
+		"area": ["市辖区", "萨尔图区", "龙凤区", "让胡路区", "红岗区", "大同区", "肇州县", "肇源县", "林甸县", "杜尔伯特蒙古族自治县"]
+	}, {
+		"name": "伊春市",
+		"area": ["市辖区", "伊春区", "南岔区", "友好区", "西林区", "翠峦区", "新青区", "美溪区", "金山屯区", "五营区", "乌马河区", "汤旺河区",
+			"带岭区", "乌伊岭区", "红星区", "上甘岭区", "嘉荫县", "铁力市"
+		]
+	}, {
+		"name": "佳木斯市",
+		"area": ["市辖区", "向阳区", "前进区", "东风区", "郊区", "桦南县", "桦川县", "汤原县", "同江市", "富锦市", "抚远市"]
+	}, {
+		"name": "七台河市",
+		"area": ["市辖区", "新兴区", "桃山区", "茄子河区", "勃利县"]
+	}, {
+		"name": "牡丹江市",
+		"area": ["市辖区", "东安区", "阳明区", "爱民区", "西安区", "林口县", "绥芬河市", "海林市", "宁安市", "穆棱市", "东宁市"]
+	}, {
+		"name": "黑河市",
+		"area": ["市辖区", "爱辉区", "嫩江县", "逊克县", "孙吴县", "北安市", "五大连池市"]
+	}, {
+		"name": "绥化市",
+		"area": ["市辖区", "北林区", "望奎县", "兰西县", "青冈县", "庆安县", "明水县", "绥棱县", "安达市", "肇东市", "海伦市"]
+	}, {
+		"name": "大兴安岭地区",
+		"area": ["呼玛县", "塔河县", "漠河县"]
+	}]
+}, {
+	"name": "市辖区",
+	"city": [{
+		"name": "上海市",
+		"area": ["黄浦区", "徐汇区", "长宁区", "静安区", "普陀区", "虹口区", "杨浦区", "闵行区", "宝山区", "嘉定区", "浦东新区", "金山区", "松江区",
+			"青浦区", "奉贤区", "崇明区"
+		]
+	}]
+}, {
+	"name": "江苏省",
+	"city": [{
+		"name": "南京市",
+		"area": ["市辖区", "玄武区", "秦淮区", "建邺区", "鼓楼区", "浦口区", "栖霞区", "雨花台区", "江宁区", "六合区", "溧水区", "高淳区"]
+	}, {
+		"name": "无锡市",
+		"area": ["市辖区", "锡山区", "惠山区", "滨湖区", "梁溪区", "新吴区", "江阴市", "宜兴市"]
+	}, {
+		"name": "徐州市",
+		"area": ["市辖区", "鼓楼区", "云龙区", "贾汪区", "泉山区", "铜山区", "丰县", "沛县", "睢宁县", "新沂市", "邳州市"]
+	}, {
+		"name": "常州市",
+		"area": ["市辖区", "天宁区", "钟楼区", "新北区", "武进区", "金坛区", "溧阳市"]
+	}, {
+		"name": "苏州市",
+		"area": ["市辖区", "虎丘区", "吴中区", "相城区", "姑苏区", "吴江区", "常熟市", "张家港市", "昆山市", "太仓市"]
+	}, {
+		"name": "南通市",
+		"area": ["市辖区", "崇川区", "港闸区", "通州区", "海安县", "如东县", "启东市", "如皋市", "海门市"]
+	}, {
+		"name": "连云港市",
+		"area": ["市辖区", "连云区", "海州区", "赣榆区", "东海县", "灌云县", "灌南县"]
+	}, {
+		"name": "淮安市",
+		"area": ["市辖区", "淮安区", "淮阴区", "清江浦区", "洪泽区", "涟水县", "盱眙县", "金湖县"]
+	}, {
+		"name": "盐城市",
+		"area": ["市辖区", "亭湖区", "盐都区", "大丰区", "响水县", "滨海县", "阜宁县", "射阳县", "建湖县", "东台市"]
+	}, {
+		"name": "扬州市",
+		"area": ["市辖区", "广陵区", "邗江区", "江都区", "宝应县", "仪征市", "高邮市"]
+	}, {
+		"name": "镇江市",
+		"area": ["市辖区", "京口区", "润州区", "丹徒区", "丹阳市", "扬中市", "句容市"]
+	}, {
+		"name": "泰州市",
+		"area": ["市辖区", "海陵区", "高港区", "姜堰区", "兴化市", "靖江市", "泰兴市"]
+	}, {
+		"name": "宿迁市",
+		"area": ["市辖区", "宿城区", "宿豫区", "沭阳县", "泗阳县", "泗洪县"]
+	}]
+}, {
+	"name": "浙江省",
+	"city": [{
+		"name": "杭州市",
+		"area": ["市辖区", "上城区", "下城区", "江干区", "拱墅区", "西湖区", "滨江区", "萧山区", "余杭区", "富阳区", "桐庐县", "淳安县", "建德市",
+			"临安市"
+		]
+	}, {
+		"name": "宁波市",
+		"area": ["市辖区", "海曙区", "江东区", "江北区", "北仑区", "镇海区", "鄞州区", "象山县", "宁海县", "余姚市", "慈溪市", "奉化市"]
+	}, {
+		"name": "温州市",
+		"area": ["市辖区", "鹿城区", "龙湾区", "瓯海区", "洞头区", "永嘉县", "平阳县", "苍南县", "文成县", "泰顺县", "瑞安市", "乐清市"]
+	}, {
+		"name": "嘉兴市",
+		"area": ["市辖区", "南湖区", "秀洲区", "嘉善县", "海盐县", "海宁市", "平湖市", "桐乡市"]
+	}, {
+		"name": "湖州市",
+		"area": ["市辖区", "吴兴区", "南浔区", "德清县", "长兴县", "安吉县"]
+	}, {
+		"name": "绍兴市",
+		"area": ["市辖区", "越城区", "柯桥区", "上虞区", "新昌县", "诸暨市", "嵊州市"]
+	}, {
+		"name": "金华市",
+		"area": ["市辖区", "婺城区", "金东区", "武义县", "浦江县", "磐安县", "兰溪市", "义乌市", "东阳市", "永康市"]
+	}, {
+		"name": "衢州市",
+		"area": ["市辖区", "柯城区", "衢江区", "常山县", "开化县", "龙游县", "江山市"]
+	}, {
+		"name": "舟山市",
+		"area": ["市辖区", "定海区", "普陀区", "岱山县", "嵊泗县"]
+	}, {
+		"name": "台州市",
+		"area": ["市辖区", "椒江区", "黄岩区", "路桥区", "玉环县", "三门县", "天台县", "仙居县", "温岭市", "临海市"]
+	}, {
+		"name": "丽水市",
+		"area": ["市辖区", "莲都区", "青田县", "缙云县", "遂昌县", "松阳县", "云和县", "庆元县", "景宁畲族自治县", "龙泉市"]
+	}]
+}, {
+	"name": "安徽省",
+	"city": [{
+		"name": "合肥市",
+		"area": ["市辖区", "瑶海区", "庐阳区", "蜀山区", "包河区", "长丰县", "肥东县", "肥西县", "庐江县", "巢湖市"]
+	}, {
+		"name": "芜湖市",
+		"area": ["市辖区", "镜湖区", "弋江区", "鸠江区", "三山区", "芜湖县", "繁昌县", "南陵县", "无为县"]
+	}, {
+		"name": "蚌埠市",
+		"area": ["市辖区", "龙子湖区", "蚌山区", "禹会区", "淮上区", "怀远县", "五河县", "固镇县"]
+	}, {
+		"name": "淮南市",
+		"area": ["市辖区", "大通区", "田家庵区", "谢家集区", "八公山区", "潘集区", "凤台县", "寿县"]
+	}, {
+		"name": "马鞍山市",
+		"area": ["市辖区", "花山区", "雨山区", "博望区", "当涂县", "含山县", "和县"]
+	}, {
+		"name": "淮北市",
+		"area": ["市辖区", "杜集区", "相山区", "烈山区", "濉溪县"]
+	}, {
+		"name": "铜陵市",
+		"area": ["市辖区", "铜官区", "义安区", "郊区", "枞阳县"]
+	}, {
+		"name": "安庆市",
+		"area": ["市辖区", "迎江区", "大观区", "宜秀区", "怀宁县", "潜山县", "太湖县", "宿松县", "望江县", "岳西县", "桐城市"]
+	}, {
+		"name": "黄山市",
+		"area": ["市辖区", "屯溪区", "黄山区", "徽州区", "歙县", "休宁县", "黟县", "祁门县"]
+	}, {
+		"name": "滁州市",
+		"area": ["市辖区", "琅琊区", "南谯区", "来安县", "全椒县", "定远县", "凤阳县", "天长市", "明光市"]
+	}, {
+		"name": "阜阳市",
+		"area": ["市辖区", "颍州区", "颍东区", "颍泉区", "临泉县", "太和县", "阜南县", "颍上县", "界首市"]
+	}, {
+		"name": "宿州市",
+		"area": ["市辖区", "埇桥区", "砀山县", "萧县", "灵璧县", "泗县"]
+	}, {
+		"name": "六安市",
+		"area": ["市辖区", "金安区", "裕安区", "叶集区", "霍邱县", "舒城县", "金寨县", "霍山县"]
+	}, {
+		"name": "亳州市",
+		"area": ["市辖区", "谯城区", "涡阳县", "蒙城县", "利辛县"]
+	}, {
+		"name": "池州市",
+		"area": ["市辖区", "贵池区", "东至县", "石台县", "青阳县"]
+	}, {
+		"name": "宣城市",
+		"area": ["市辖区", "宣州区", "郎溪县", "广德县", "泾县", "绩溪县", "旌德县", "宁国市"]
+	}]
+}, {
+	"name": "福建省",
+	"city": [{
+		"name": "福州市",
+		"area": ["市辖区", "鼓楼区", "台江区", "仓山区", "马尾区", "晋安区", "闽侯县", "连江县", "罗源县", "闽清县", "永泰县", "平潭县", "福清市",
+			"长乐市"
+		]
+	}, {
+		"name": "厦门市",
+		"area": ["市辖区", "思明区", "海沧区", "湖里区", "集美区", "同安区", "翔安区"]
+	}, {
+		"name": "莆田市",
+		"area": ["市辖区", "城厢区", "涵江区", "荔城区", "秀屿区", "仙游县"]
+	}, {
+		"name": "三明市",
+		"area": ["市辖区", "梅列区", "三元区", "明溪县", "清流县", "宁化县", "大田县", "尤溪县", "沙县", "将乐县", "泰宁县", "建宁县", "永安市"]
+	}, {
+		"name": "泉州市",
+		"area": ["市辖区", "鲤城区", "丰泽区", "洛江区", "泉港区", "惠安县", "安溪县", "永春县", "德化县", "金门县", "石狮市", "晋江市", "南安市"]
+	}, {
+		"name": "漳州市",
+		"area": ["市辖区", "芗城区", "龙文区", "云霄县", "漳浦县", "诏安县", "长泰县", "东山县", "南靖县", "平和县", "华安县", "龙海市"]
+	}, {
+		"name": "南平市",
+		"area": ["市辖区", "延平区", "建阳区", "顺昌县", "浦城县", "光泽县", "松溪县", "政和县", "邵武市", "武夷山市", "建瓯市"]
+	}, {
+		"name": "龙岩市",
+		"area": ["市辖区", "新罗区", "永定区", "长汀县", "上杭县", "武平县", "连城县", "漳平市"]
+	}, {
+		"name": "宁德市",
+		"area": ["市辖区", "蕉城区", "霞浦县", "古田县", "屏南县", "寿宁县", "周宁县", "柘荣县", "福安市", "福鼎市"]
+	}]
+}, {
+	"name": "江西省",
+	"city": [{
+		"name": "南昌市",
+		"area": ["市辖区", "东湖区", "西湖区", "青云谱区", "湾里区", "青山湖区", "新建区", "南昌县", "安义县", "进贤县"]
+	}, {
+		"name": "景德镇市",
+		"area": ["市辖区", "昌江区", "珠山区", "浮梁县", "乐平市"]
+	}, {
+		"name": "萍乡市",
+		"area": ["市辖区", "安源区", "湘东区", "莲花县", "上栗县", "芦溪县"]
+	}, {
+		"name": "九江市",
+		"area": ["市辖区", "濂溪区", "浔阳区", "九江县", "武宁县", "修水县", "永修县", "德安县", "都昌县", "湖口县", "彭泽县", "瑞昌市", "共青城市",
+			"庐山市"
+		]
+	}, {
+		"name": "新余市",
+		"area": ["市辖区", "渝水区", "分宜县"]
+	}, {
+		"name": "鹰潭市",
+		"area": ["市辖区", "月湖区", "余江县", "贵溪市"]
+	}, {
+		"name": "赣州市",
+		"area": ["市辖区", "章贡区", "南康区", "赣县", "信丰县", "大余县", "上犹县", "崇义县", "安远县", "龙南县", "定南县", "全南县", "宁都县",
+			"于都县", "兴国县", "会昌县", "寻乌县", "石城县", "瑞金市"
+		]
+	}, {
+		"name": "吉安市",
+		"area": ["市辖区", "吉州区", "青原区", "吉安县", "吉水县", "峡江县", "新干县", "永丰县", "泰和县", "遂川县", "万安县", "安福县", "永新县",
+			"井冈山市"
+		]
+	}, {
+		"name": "宜春市",
+		"area": ["市辖区", "袁州区", "奉新县", "万载县", "上高县", "宜丰县", "靖安县", "铜鼓县", "丰城市", "樟树市", "高安市"]
+	}, {
+		"name": "抚州市",
+		"area": ["市辖区", "临川区", "南城县", "黎川县", "南丰县", "崇仁县", "乐安县", "宜黄县", "金溪县", "资溪县", "东乡县", "广昌县"]
+	}, {
+		"name": "上饶市",
+		"area": ["市辖区", "信州区", "广丰区", "上饶县", "玉山县", "铅山县", "横峰县", "弋阳县", "余干县", "鄱阳县", "万年县", "婺源县", "德兴市"]
+	}]
+}, {
+	"name": "山东省",
+	"city": [{
+		"name": "济南市",
+		"area": ["市辖区", "历下区", "市中区", "槐荫区", "天桥区", "历城区", "长清区", "平阴县", "济阳县", "商河县", "章丘市"]
+	}, {
+		"name": "青岛市",
+		"area": ["市辖区", "市南区", "市北区", "黄岛区", "崂山区", "李沧区", "城阳区", "胶州市", "即墨市", "平度市", "莱西市"]
+	}, {
+		"name": "淄博市",
+		"area": ["市辖区", "淄川区", "张店区", "博山区", "临淄区", "周村区", "桓台县", "高青县", "沂源县"]
+	}, {
+		"name": "枣庄市",
+		"area": ["市辖区", "市中区", "薛城区", "峄城区", "台儿庄区", "山亭区", "滕州市"]
+	}, {
+		"name": "东营市",
+		"area": ["市辖区", "东营区", "河口区", "垦利区", "利津县", "广饶县"]
+	}, {
+		"name": "烟台市",
+		"area": ["市辖区", "芝罘区", "福山区", "牟平区", "莱山区", "长岛县", "龙口市", "莱阳市", "莱州市", "蓬莱市", "招远市", "栖霞市", "海阳市"]
+	}, {
+		"name": "潍坊市",
+		"area": ["市辖区", "潍城区", "寒亭区", "坊子区", "奎文区", "临朐县", "昌乐县", "青州市", "诸城市", "寿光市", "安丘市", "高密市", "昌邑市"]
+	}, {
+		"name": "济宁市",
+		"area": ["市辖区", "任城区", "兖州区", "微山县", "鱼台县", "金乡县", "嘉祥县", "汶上县", "泗水县", "梁山县", "曲阜市", "邹城市"]
+	}, {
+		"name": "泰安市",
+		"area": ["市辖区", "泰山区", "岱岳区", "宁阳县", "东平县", "新泰市", "肥城市"]
+	}, {
+		"name": "威海市",
+		"area": ["市辖区", "环翠区", "文登区", "荣成市", "乳山市"]
+	}, {
+		"name": "日照市",
+		"area": ["市辖区", "东港区", "岚山区", "五莲县", "莒县"]
+	}, {
+		"name": "莱芜市",
+		"area": ["市辖区", "莱城区", "钢城区"]
+	}, {
+		"name": "临沂市",
+		"area": ["市辖区", "兰山区", "罗庄区", "河东区", "沂南县", "郯城县", "沂水县", "兰陵县", "费县", "平邑县", "莒南县", "蒙阴县", "临沭县"]
+	}, {
+		"name": "德州市",
+		"area": ["市辖区", "德城区", "陵城区", "宁津县", "庆云县", "临邑县", "齐河县", "平原县", "夏津县", "武城县", "乐陵市", "禹城市"]
+	}, {
+		"name": "聊城市",
+		"area": ["市辖区", "东昌府区", "阳谷县", "莘县", "茌平县", "东阿县", "冠县", "高唐县", "临清市"]
+	}, {
+		"name": "滨州市",
+		"area": ["市辖区", "滨城区", "沾化区", "惠民县", "阳信县", "无棣县", "博兴县", "邹平县"]
+	}, {
+		"name": "菏泽市",
+		"area": ["市辖区", "牡丹区", "定陶区", "曹县", "单县", "成武县", "巨野县", "郓城县", "鄄城县", "东明县"]
+	}]
+}, {
+	"name": "河南省",
+	"city": [{
+		"name": "郑州市",
+		"area": ["市辖区", "中原区", "二七区", "管城回族区", "金水区", "上街区", "惠济区", "中牟县", "巩义市", "荥阳市", "新密市", "新郑市",
+			"登封市"
+		]
+	}, {
+		"name": "开封市",
+		"area": ["市辖区", "龙亭区", "顺河回族区", "鼓楼区", "禹王台区", "金明区", "祥符区", "杞县", "通许县", "尉氏县", "兰考县"]
+	}, {
+		"name": "洛阳市",
+		"area": ["市辖区", "老城区", "西工区", "瀍河回族区", "涧西区", "吉利区", "洛龙区", "孟津县", "新安县", "栾川县", "嵩县", "汝阳县", "宜阳县",
+			"洛宁县", "伊川县", "偃师市"
+		]
+	}, {
+		"name": "平顶山市",
+		"area": ["市辖区", "新华区", "卫东区", "石龙区", "湛河区", "宝丰县", "叶县", "鲁山县", "郏县", "舞钢市", "汝州市"]
+	}, {
+		"name": "安阳市",
+		"area": ["市辖区", "文峰区", "北关区", "殷都区", "龙安区", "安阳县", "汤阴县", "滑县", "内黄县", "林州市"]
+	}, {
+		"name": "鹤壁市",
+		"area": ["市辖区", "鹤山区", "山城区", "淇滨区", "浚县", "淇县"]
+	}, {
+		"name": "新乡市",
+		"area": ["市辖区", "红旗区", "卫滨区", "凤泉区", "牧野区", "新乡县", "获嘉县", "原阳县", "延津县", "封丘县", "长垣县", "卫辉市", "辉县市"]
+	}, {
+		"name": "焦作市",
+		"area": ["市辖区", "解放区", "中站区", "马村区", "山阳区", "修武县", "博爱县", "武陟县", "温县", "沁阳市", "孟州市"]
+	}, {
+		"name": "濮阳市",
+		"area": ["市辖区", "华龙区", "清丰县", "南乐县", "范县", "台前县", "濮阳县"]
+	}, {
+		"name": "许昌市",
+		"area": ["市辖区", "魏都区", "许昌县", "鄢陵县", "襄城县", "禹州市", "长葛市"]
+	}, {
+		"name": "漯河市",
+		"area": ["市辖区", "源汇区", "郾城区", "召陵区", "舞阳县", "临颍县"]
+	}, {
+		"name": "三门峡市",
+		"area": ["市辖区", "湖滨区", "陕州区", "渑池县", "卢氏县", "义马市", "灵宝市"]
+	}, {
+		"name": "南阳市",
+		"area": ["市辖区", "宛城区", "卧龙区", "南召县", "方城县", "西峡县", "镇平县", "内乡县", "淅川县", "社旗县", "唐河县", "新野县", "桐柏县",
+			"邓州市"
+		]
+	}, {
+		"name": "商丘市",
+		"area": ["市辖区", "梁园区", "睢阳区", "民权县", "睢县", "宁陵县", "柘城县", "虞城县", "夏邑县", "永城市"]
+	}, {
+		"name": "信阳市",
+		"area": ["市辖区", "浉河区", "平桥区", "罗山县", "光山县", "新县", "商城县", "固始县", "潢川县", "淮滨县", "息县"]
+	}, {
+		"name": "周口市",
+		"area": ["市辖区", "川汇区", "扶沟县", "西华县", "商水县", "沈丘县", "郸城县", "淮阳县", "太康县", "鹿邑县", "项城市"]
+	}, {
+		"name": "驻马店市",
+		"area": ["市辖区", "驿城区", "西平县", "上蔡县", "平舆县", "正阳县", "确山县", "泌阳县", "汝南县", "遂平县", "新蔡县"]
+	}, {
+		"name": "直辖县",
+		"area": ["济源市"]
+	}]
+}, {
+	"name": "湖北省",
+	"city": [{
+		"name": "武汉市",
+		"area": ["市辖区", "江岸区", "江汉区", "硚口区", "汉阳区", "武昌区", "青山区", "洪山区", "东西湖区", "汉南区", "蔡甸区", "江夏区", "黄陂区",
+			"新洲区"
+		]
+	}, {
+		"name": "黄石市",
+		"area": ["市辖区", "黄石港区", "西塞山区", "下陆区", "铁山区", "阳新县", "大冶市"]
+	}, {
+		"name": "十堰市",
+		"area": ["市辖区", "茅箭区", "张湾区", "郧阳区", "郧西县", "竹山县", "竹溪县", "房县", "丹江口市"]
+	}, {
+		"name": "宜昌市",
+		"area": ["市辖区", "西陵区", "伍家岗区", "点军区", "猇亭区", "夷陵区", "远安县", "兴山县", "秭归县", "长阳土家族自治县", "五峰土家族自治县",
+			"宜都市", "当阳市", "枝江市"
+		]
+	}, {
+		"name": "襄阳市",
+		"area": ["市辖区", "襄城区", "樊城区", "襄州区", "南漳县", "谷城县", "保康县", "老河口市", "枣阳市", "宜城市"]
+	}, {
+		"name": "鄂州市",
+		"area": ["市辖区", "梁子湖区", "华容区", "鄂城区"]
+	}, {
+		"name": "荆门市",
+		"area": ["市辖区", "东宝区", "掇刀区", "京山县", "沙洋县", "钟祥市"]
+	}, {
+		"name": "孝感市",
+		"area": ["市辖区", "孝南区", "孝昌县", "大悟县", "云梦县", "应城市", "安陆市", "汉川市"]
+	}, {
+		"name": "荆州市",
+		"area": ["市辖区", "沙市区", "荆州区", "公安县", "监利县", "江陵县", "石首市", "洪湖市", "松滋市"]
+	}, {
+		"name": "黄冈市",
+		"area": ["市辖区", "黄州区", "团风县", "红安县", "罗田县", "英山县", "浠水县", "蕲春县", "黄梅县", "麻城市", "武穴市"]
+	}, {
+		"name": "咸宁市",
+		"area": ["市辖区", "咸安区", "嘉鱼县", "通城县", "崇阳县", "通山县", "赤壁市"]
+	}, {
+		"name": "随州市",
+		"area": ["市辖区", "曾都区", "随县", "广水市"]
+	}, {
+		"name": "恩施土家族苗族自治州",
+		"area": ["恩施市", "利川市", "建始县", "巴东县", "宣恩县", "咸丰县", "来凤县", "鹤峰县"]
+	}, {
+		"name": "直辖县",
+		"area": ["仙桃市", "潜江市", "天门市", "神农架林区"]
+	}]
+}, {
+	"name": "湖南省",
+	"city": [{
+		"name": "长沙市",
+		"area": ["市辖区", "芙蓉区", "天心区", "岳麓区", "开福区", "雨花区", "望城区", "长沙县", "宁乡县", "浏阳市"]
+	}, {
+		"name": "株洲市",
+		"area": ["市辖区", "荷塘区", "芦淞区", "石峰区", "天元区", "株洲县", "攸县", "茶陵县", "炎陵县", "醴陵市"]
+	}, {
+		"name": "湘潭市",
+		"area": ["市辖区", "雨湖区", "岳塘区", "湘潭县", "湘乡市", "韶山市"]
+	}, {
+		"name": "衡阳市",
+		"area": ["市辖区", "珠晖区", "雁峰区", "石鼓区", "蒸湘区", "南岳区", "衡阳县", "衡南县", "衡山县", "衡东县", "祁东县", "耒阳市", "常宁市"]
+	}, {
+		"name": "邵阳市",
+		"area": ["市辖区", "双清区", "大祥区", "北塔区", "邵东县", "新邵县", "邵阳县", "隆回县", "洞口县", "绥宁县", "新宁县", "城步苗族自治县",
+			"武冈市"
+		]
+	}, {
+		"name": "岳阳市",
+		"area": ["市辖区", "岳阳楼区", "云溪区", "君山区", "岳阳县", "华容县", "湘阴县", "平江县", "汨罗市", "临湘市"]
+	}, {
+		"name": "常德市",
+		"area": ["市辖区", "武陵区", "鼎城区", "安乡县", "汉寿县", "澧县", "临澧县", "桃源县", "石门县", "津市市"]
+	}, {
+		"name": "张家界市",
+		"area": ["市辖区", "永定区", "武陵源区", "慈利县", "桑植县"]
+	}, {
+		"name": "益阳市",
+		"area": ["市辖区", "资阳区", "赫山区", "南县", "桃江县", "安化县", "沅江市"]
+	}, {
+		"name": "郴州市",
+		"area": ["市辖区", "北湖区", "苏仙区", "桂阳县", "宜章县", "永兴县", "嘉禾县", "临武县", "汝城县", "桂东县", "安仁县", "资兴市"]
+	}, {
+		"name": "永州市",
+		"area": ["市辖区", "零陵区", "冷水滩区", "祁阳县", "东安县", "双牌县", "道县", "江永县", "宁远县", "蓝山县", "新田县", "江华瑶族自治县"]
+	}, {
+		"name": "怀化市",
+		"area": ["市辖区", "鹤城区", "中方县", "沅陵县", "辰溪县", "溆浦县", "会同县", "麻阳苗族自治县", "新晃侗族自治县", "芷江侗族自治县",
+			"靖州苗族侗族自治县", "通道侗族自治县", "洪江市"
+		]
+	}, {
+		"name": "娄底市",
+		"area": ["市辖区", "娄星区", "双峰县", "新化县", "冷水江市", "涟源市"]
+	}, {
+		"name": "湘西土家族苗族自治州",
+		"area": ["吉首市", "泸溪县", "凤凰县", "花垣县", "保靖县", "古丈县", "永顺县", "龙山县"]
+	}]
+}, {
+	"name": "广东省",
+	"city": [{
+		"name": "广州市",
+		"area": ["市辖区", "荔湾区", "越秀区", "海珠区", "天河区", "白云区", "黄埔区", "番禺区", "花都区", "南沙区", "从化区", "增城区"]
+	}, {
+		"name": "韶关市",
+		"area": ["市辖区", "武江区", "浈江区", "曲江区", "始兴县", "仁化县", "翁源县", "乳源瑶族自治县", "新丰县", "乐昌市", "南雄市"]
+	}, {
+		"name": "深圳市",
+		"area": ["市辖区", "罗湖区", "福田区", "南山区", "宝安区", "龙岗区", "盐田区"]
+	}, {
+		"name": "珠海市",
+		"area": ["市辖区", "香洲区", "斗门区", "金湾区"]
+	}, {
+		"name": "汕头市",
+		"area": ["市辖区", "龙湖区", "金平区", "濠江区", "潮阳区", "潮南区", "澄海区", "南澳县"]
+	}, {
+		"name": "佛山市",
+		"area": ["市辖区", "禅城区", "南海区", "顺德区", "三水区", "高明区"]
+	}, {
+		"name": "江门市",
+		"area": ["市辖区", "蓬江区", "江海区", "新会区", "台山市", "开平市", "鹤山市", "恩平市"]
+	}, {
+		"name": "湛江市",
+		"area": ["市辖区", "赤坎区", "霞山区", "坡头区", "麻章区", "遂溪县", "徐闻县", "廉江市", "雷州市", "吴川市"]
+	}, {
+		"name": "茂名市",
+		"area": ["市辖区", "茂南区", "电白区", "高州市", "化州市", "信宜市"]
+	}, {
+		"name": "肇庆市",
+		"area": ["市辖区", "端州区", "鼎湖区", "高要区", "广宁县", "怀集县", "封开县", "德庆县", "四会市"]
+	}, {
+		"name": "惠州市",
+		"area": ["市辖区", "惠城区", "惠阳区", "博罗县", "惠东县", "龙门县"]
+	}, {
+		"name": "梅州市",
+		"area": ["市辖区", "梅江区", "梅县区", "大埔县", "丰顺县", "五华县", "平远县", "蕉岭县", "兴宁市"]
+	}, {
+		"name": "汕尾市",
+		"area": ["市辖区", "城区", "海丰县", "陆河县", "陆丰市"]
+	}, {
+		"name": "河源市",
+		"area": ["市辖区", "源城区", "紫金县", "龙川县", "连平县", "和平县", "东源县"]
+	}, {
+		"name": "阳江市",
+		"area": ["市辖区", "江城区", "阳东区", "阳西县", "阳春市"]
+	}, {
+		"name": "清远市",
+		"area": ["市辖区", "清城区", "清新区", "佛冈县", "阳山县", "连山壮族瑶族自治县", "连南瑶族自治县", "英德市", "连州市"]
+	}, {
+		"name": "东莞市",
+		"area": ["东城街道办事处", "南城街道办事处", "万江街道办事处", "莞城街道办事处", "石碣镇", "石龙镇", "茶山镇", "石排镇", "企石镇", "横沥镇",
+			"桥头镇", "谢岗镇", "东坑镇", "常平镇", "寮步镇", "樟木头镇", "大朗镇", "黄江镇", "清溪镇", "塘厦镇", "凤岗镇", "大岭山镇", "长安镇",
+			"虎门镇", "厚街镇", "沙田镇", "道滘镇", "洪梅镇", "麻涌镇", "望牛墩镇", "中堂镇", "高埗镇", "松山湖管委会", "虎门港管委会", "东莞生态园"
+		]
+	}, {
+		"name": "中山市",
+		"area": ["石岐区街道办事处", "东区街道办事处", "火炬开发区街道办事处", "西区街道办事处", "南区街道办事处", "五桂山街道办事处", "小榄镇", "黄圃镇", "民众镇",
+			"东凤镇", "东升镇", "古镇镇", "沙溪镇", "坦洲镇", "港口镇", "三角镇", "横栏镇", "南头镇", "阜沙镇", "南朗镇", "三乡镇", "板芙镇",
+			"大涌镇", "神湾镇"
+		]
+	}, {
+		"name": "潮州市",
+		"area": ["市辖区", "湘桥区", "潮安区", "饶平县"]
+	}, {
+		"name": "揭阳市",
+		"area": ["市辖区", "榕城区", "揭东区", "揭西县", "惠来县", "普宁市"]
+	}, {
+		"name": "云浮市",
+		"area": ["市辖区", "云城区", "云安区", "新兴县", "郁南县", "罗定市"]
+	}]
+}, {
+	"name": "广西壮族自治区",
+	"city": [{
+		"name": "南宁市",
+		"area": ["市辖区", "兴宁区", "青秀区", "江南区", "西乡塘区", "良庆区", "邕宁区", "武鸣区", "隆安县", "马山县", "上林县", "宾阳县", "横县"]
+	}, {
+		"name": "柳州市",
+		"area": ["市辖区", "城中区", "鱼峰区", "柳南区", "柳北区", "柳江区", "柳城县", "鹿寨县", "融安县", "融水苗族自治县", "三江侗族自治县"]
+	}, {
+		"name": "桂林市",
+		"area": ["市辖区", "秀峰区", "叠彩区", "象山区", "七星区", "雁山区", "临桂区", "阳朔县", "灵川县", "全州县", "兴安县", "永福县", "灌阳县",
+			"龙胜各族自治县", "资源县", "平乐县", "荔浦县", "恭城瑶族自治县"
+		]
+	}, {
+		"name": "梧州市",
+		"area": ["市辖区", "万秀区", "长洲区", "龙圩区", "苍梧县", "藤县", "蒙山县", "岑溪市"]
+	}, {
+		"name": "北海市",
+		"area": ["市辖区", "海城区", "银海区", "铁山港区", "合浦县"]
+	}, {
+		"name": "防城港市",
+		"area": ["市辖区", "港口区", "防城区", "上思县", "东兴市"]
+	}, {
+		"name": "钦州市",
+		"area": ["市辖区", "钦南区", "钦北区", "灵山县", "浦北县"]
+	}, {
+		"name": "贵港市",
+		"area": ["市辖区", "港北区", "港南区", "覃塘区", "平南县", "桂平市"]
+	}, {
+		"name": "玉林市",
+		"area": ["市辖区", "玉州区", "福绵区", "容县", "陆川县", "博白县", "兴业县", "北流市"]
+	}, {
+		"name": "百色市",
+		"area": ["市辖区", "右江区", "田阳县", "田东县", "平果县", "德保县", "那坡县", "凌云县", "乐业县", "田林县", "西林县", "隆林各族自治县",
+			"靖西市"
+		]
+	}, {
+		"name": "贺州市",
+		"area": ["市辖区", "八步区", "平桂区", "昭平县", "钟山县", "富川瑶族自治县"]
+	}, {
+		"name": "河池市",
+		"area": ["市辖区", "金城江区", "南丹县", "天峨县", "凤山县", "东兰县", "罗城仫佬族自治县", "环江毛南族自治县", "巴马瑶族自治县", "都安瑶族自治县",
+			"大化瑶族自治县", "宜州市"
+		]
+	}, {
+		"name": "来宾市",
+		"area": ["市辖区", "兴宾区", "忻城县", "象州县", "武宣县", "金秀瑶族自治县", "合山市"]
+	}, {
+		"name": "崇左市",
+		"area": ["市辖区", "江州区", "扶绥县", "宁明县", "龙州县", "大新县", "天等县", "凭祥市"]
+	}]
+}, {
+	"name": "海南省",
+	"city": [{
+		"name": "海口市",
+		"area": ["市辖区", "秀英区", "龙华区", "琼山区", "美兰区"]
+	}, {
+		"name": "三亚市",
+		"area": ["市辖区", "海棠区", "吉阳区", "天涯区", "崖州区"]
+	}, {
+		"name": "三沙市",
+		"area": ["西沙群岛", "南沙群岛", "中沙群岛的岛礁及其海域"]
+	}, {
+		"name": "儋州市",
+		"area": ["那大镇", "和庆镇", "南丰镇", "大成镇", "雅星镇", "兰洋镇", "光村镇", "木棠镇", "海头镇", "峨蔓镇", "三都镇", "王五镇", "白马井镇",
+			"中和镇", "排浦镇", "东成镇", "新州镇", "国营西培农场", "国营西联农场", "国营蓝洋农场", "国营八一农场", "洋浦经济开发区", "华南热作学院"
+		]
+	}, {
+		"name": "省直辖县级行政区域",
+		"area": ["五指山市", "琼海市", "文昌市", "万宁市", "东方市", "定安县", "屯昌县", "澄迈县", "临高县", "白沙黎族自治县", "昌江黎族自治县",
+			"乐东黎族自治县", "陵水黎族自治县", "保亭黎族苗族自治县", "琼中黎族苗族自治县"
+		]
+	}]
+}, {
+	"name": "重庆市",
+	"city": [{
+		"name": "市辖区",
+		"area": ["万州区", "涪陵区", "渝中区", "大渡口区", "江北区", "沙坪坝区", "九龙坡区", "南岸区", "北碚区", "綦江区", "大足区", "渝北区",
+			"巴南区", "黔江区", "长寿区", "江津区", "合川区", "永川区", "南川区", "璧山区", "铜梁区", "潼南区", "荣昌区", "开州区"
+		]
+	}, {
+		"name": "县",
+		"area": ["梁平县", "城口县", "丰都县", "垫江县", "武隆县", "忠县", "云阳县", "奉节县", "巫山县", "巫溪县", "石柱土家族自治县",
+			"秀山土家族苗族自治县", "酉阳土家族苗族自治县", "彭水苗族土家族自治县"
+		]
+	}]
+}, {
+	"name": "四川省",
+	"city": [{
+		"name": "成都市",
+		"area": ["市辖区", "锦江区", "青羊区", "金牛区", "武侯区", "成华区", "龙泉驿区", "青白江区", "新都区", "温江区", "双流区", "金堂县", "郫县",
+			"大邑县", "蒲江县", "新津县", "都江堰市", "彭州市", "邛崃市", "崇州市", "简阳市"
+		]
+	}, {
+		"name": "自贡市",
+		"area": ["市辖区", "自流井区", "贡井区", "大安区", "沿滩区", "荣县", "富顺县"]
+	}, {
+		"name": "攀枝花市",
+		"area": ["市辖区", "东区", "西区", "仁和区", "米易县", "盐边县"]
+	}, {
+		"name": "泸州市",
+		"area": ["市辖区", "江阳区", "纳溪区", "龙马潭区", "泸县", "合江县", "叙永县", "古蔺县"]
+	}, {
+		"name": "德阳市",
+		"area": ["市辖区", "旌阳区", "中江县", "罗江县", "广汉市", "什邡市", "绵竹市"]
+	}, {
+		"name": "绵阳市",
+		"area": ["市辖区", "涪城区", "游仙区", "安州区", "三台县", "盐亭县", "梓潼县", "北川羌族自治县", "平武县", "江油市"]
+	}, {
+		"name": "广元市",
+		"area": ["市辖区", "利州区", "昭化区", "朝天区", "旺苍县", "青川县", "剑阁县", "苍溪县"]
+	}, {
+		"name": "遂宁市",
+		"area": ["市辖区", "船山区", "安居区", "蓬溪县", "射洪县", "大英县"]
+	}, {
+		"name": "内江市",
+		"area": ["市辖区", "市中区", "东兴区", "威远县", "资中县", "隆昌县"]
+	}, {
+		"name": "乐山市",
+		"area": ["市辖区", "市中区", "沙湾区", "五通桥区", "金口河区", "犍为县", "井研县", "夹江县", "沐川县", "峨边彝族自治县", "马边彝族自治县",
+			"峨眉山市"
+		]
+	}, {
+		"name": "南充市",
+		"area": ["市辖区", "顺庆区", "高坪区", "嘉陵区", "南部县", "营山县", "蓬安县", "仪陇县", "西充县", "阆中市"]
+	}, {
+		"name": "眉山市",
+		"area": ["市辖区", "东坡区", "彭山区", "仁寿县", "洪雅县", "丹棱县", "青神县"]
+	}, {
+		"name": "宜宾市",
+		"area": ["市辖区", "翠屏区", "南溪区", "宜宾县", "江安县", "长宁县", "高县", "珙县", "筠连县", "兴文县", "屏山县"]
+	}, {
+		"name": "广安市",
+		"area": ["市辖区", "广安区", "前锋区", "岳池县", "武胜县", "邻水县", "华蓥市"]
+	}, {
+		"name": "达州市",
+		"area": ["市辖区", "通川区", "达川区", "宣汉县", "开江县", "大竹县", "渠县", "万源市"]
+	}, {
+		"name": "雅安市",
+		"area": ["市辖区", "雨城区", "名山区", "荥经县", "汉源县", "石棉县", "天全县", "芦山县", "宝兴县"]
+	}, {
+		"name": "巴中市",
+		"area": ["市辖区", "巴州区", "恩阳区", "通江县", "南江县", "平昌县"]
+	}, {
+		"name": "资阳市",
+		"area": ["市辖区", "雁江区", "安岳县", "乐至县"]
+	}, {
+		"name": "阿坝藏族羌族自治州",
+		"area": ["马尔康市", "汶川县", "理县", "茂县", "松潘县", "九寨沟县", "金川县", "小金县", "黑水县", "壤塘县", "阿坝县", "若尔盖县", "红原县"]
+	}, {
+		"name": "甘孜藏族自治州",
+		"area": ["康定市", "泸定县", "丹巴县", "九龙县", "雅江县", "道孚县", "炉霍县", "甘孜县", "新龙县", "德格县", "白玉县", "石渠县", "色达县",
+			"理塘县", "巴塘县", "乡城县", "稻城县", "得荣县"
+		]
+	}, {
+		"name": "凉山彝族自治州",
+		"area": ["西昌市", "木里藏族自治县", "盐源县", "德昌县", "会理县", "会东县", "宁南县", "普格县", "布拖县", "金阳县", "昭觉县", "喜德县",
+			"冕宁县", "越西县", "甘洛县", "美姑县", "雷波县"
+		]
+	}]
+}, {
+	"name": "贵州省",
+	"city": [{
+		"name": "贵阳市",
+		"area": ["市辖区", "南明区", "云岩区", "花溪区", "乌当区", "白云区", "观山湖区", "开阳县", "息烽县", "修文县", "清镇市"]
+	}, {
+		"name": "六盘水市",
+		"area": ["钟山区", "六枝特区", "水城县", "盘县"]
+	}, {
+		"name": "遵义市",
+		"area": ["市辖区", "红花岗区", "汇川区", "播州区", "桐梓县", "绥阳县", "正安县", "道真仡佬族苗族自治县", "务川仡佬族苗族自治县", "凤冈县", "湄潭县",
+			"余庆县", "习水县", "赤水市", "仁怀市"
+		]
+	}, {
+		"name": "安顺市",
+		"area": ["市辖区", "西秀区", "平坝区", "普定县", "镇宁布依族苗族自治县", "关岭布依族苗族自治县", "紫云苗族布依族自治县"]
+	}, {
+		"name": "毕节市",
+		"area": ["市辖区", "七星关区", "大方县", "黔西县", "金沙县", "织金县", "纳雍县", "威宁彝族回族苗族自治县", "赫章县"]
+	}, {
+		"name": "铜仁市",
+		"area": ["市辖区", "碧江区", "万山区", "江口县", "玉屏侗族自治县", "石阡县", "思南县", "印江土家族苗族自治县", "德江县", "沿河土家族自治县",
+			"松桃苗族自治县"
+		]
+	}, {
+		"name": "黔西南布依族苗族自治州",
+		"area": ["兴义市", "兴仁县", "普安县", "晴隆县", "贞丰县", "望谟县", "册亨县", "安龙县"]
+	}, {
+		"name": "黔东南苗族侗族自治州",
+		"area": ["凯里市", "黄平县", "施秉县", "三穗县", "镇远县", "岑巩县", "天柱县", "锦屏县", "剑河县", "台江县", "黎平县", "榕江县", "从江县",
+			"雷山县", "麻江县", "丹寨县"
+		]
+	}, {
+		"name": "黔南布依族苗族自治州",
+		"area": ["都匀市", "福泉市", "荔波县", "贵定县", "瓮安县", "独山县", "平塘县", "罗甸县", "长顺县", "龙里县", "惠水县", "三都水族自治县"]
+	}]
+}, {
+	"name": "云南省",
+	"city": [{
+		"name": "昆明市",
+		"area": ["市辖区", "五华区", "盘龙区", "官渡区", "西山区", "东川区", "呈贡区", "晋宁县", "富民县", "宜良县", "石林彝族自治县", "嵩明县",
+			"禄劝彝族苗族自治县", "寻甸回族彝族自治县", "安宁市"
+		]
+	}, {
+		"name": "曲靖市",
+		"area": ["市辖区", "麒麟区", "沾益区", "马龙县", "陆良县", "师宗县", "罗平县", "富源县", "会泽县", "宣威市"]
+	}, {
+		"name": "玉溪市",
+		"area": ["市辖区", "红塔区", "江川区", "澄江县", "通海县", "华宁县", "易门县", "峨山彝族自治县", "新平彝族傣族自治县", "元江哈尼族彝族傣族自治县"]
+	}, {
+		"name": "保山市",
+		"area": ["市辖区", "隆阳区", "施甸县", "龙陵县", "昌宁县", "腾冲市"]
+	}, {
+		"name": "昭通市",
+		"area": ["市辖区", "昭阳区", "鲁甸县", "巧家县", "盐津县", "大关县", "永善县", "绥江县", "镇雄县", "彝良县", "威信县", "水富县"]
+	}, {
+		"name": "丽江市",
+		"area": ["市辖区", "古城区", "玉龙纳西族自治县", "永胜县", "华坪县", "宁蒗彝族自治县"]
+	}, {
+		"name": "普洱市",
+		"area": ["市辖区", "思茅区", "宁洱哈尼族彝族自治县", "墨江哈尼族自治县", "景东彝族自治县", "景谷傣族彝族自治县", "镇沅彝族哈尼族拉祜族自治县",
+			"江城哈尼族彝族自治县", "孟连傣族拉祜族佤族自治县", "澜沧拉祜族自治县", "西盟佤族自治县"
+		]
+	}, {
+		"name": "临沧市",
+		"area": ["市辖区", "临翔区", "凤庆县", "云县", "永德县", "镇康县", "双江拉祜族佤族布朗族傣族自治县", "耿马傣族佤族自治县", "沧源佤族自治县"]
+	}, {
+		"name": "楚雄彝族自治州",
+		"area": ["楚雄市", "双柏县", "牟定县", "南华县", "姚安县", "大姚县", "永仁县", "元谋县", "武定县", "禄丰县"]
+	}, {
+		"name": "红河哈尼族彝族自治州",
+		"area": ["个旧市", "开远市", "蒙自市", "弥勒市", "屏边苗族自治县", "建水县", "石屏县", "泸西县", "元阳县", "红河县", "金平苗族瑶族傣族自治县",
+			"绿春县", "河口瑶族自治县"
+		]
+	}, {
+		"name": "文山壮族苗族自治州",
+		"area": ["文山市", "砚山县", "西畴县", "麻栗坡县", "马关县", "丘北县", "广南县", "富宁县"]
+	}, {
+		"name": "西双版纳傣族自治州",
+		"area": ["景洪市", "勐海县", "勐腊县"]
+	}, {
+		"name": "大理白族自治州",
+		"area": ["大理市", "漾濞彝族自治县", "祥云县", "宾川县", "弥渡县", "南涧彝族自治县", "巍山彝族回族自治县", "永平县", "云龙县", "洱源县", "剑川县",
+			"鹤庆县"
+		]
+	}, {
+		"name": "德宏傣族景颇族自治州",
+		"area": ["瑞丽市", "芒市", "梁河县", "盈江县", "陇川县"]
+	}, {
+		"name": "怒江傈僳族自治州",
+		"area": ["泸水市", "福贡县", "贡山独龙族怒族自治县", "兰坪白族普米族自治县"]
+	}, {
+		"name": "迪庆藏族自治州",
+		"area": ["香格里拉市", "德钦县", "维西傈僳族自治县"]
+	}]
+}, {
+	"name": "西藏自治区",
+	"city": [{
+		"name": "拉萨市",
+		"area": ["市辖区", "城关区", "堆龙德庆区", "林周县", "当雄县", "尼木县", "曲水县", "达孜县", "墨竹工卡县"]
+	}, {
+		"name": "日喀则市",
+		"area": ["桑珠孜区", "南木林县", "江孜县", "定日县", "萨迦县", "拉孜县", "昂仁县", "谢通门县", "白朗县", "仁布县", "康马县", "定结县",
+			"仲巴县", "亚东县", "吉隆县", "聂拉木县", "萨嘎县", "岗巴县"
+		]
+	}, {
+		"name": "昌都市",
+		"area": ["卡若区", "江达县", "贡觉县", "类乌齐县", "丁青县", "察雅县", "八宿县", "左贡县", "芒康县", "洛隆县", "边坝县"]
+	}, {
+		"name": "林芝市",
+		"area": ["巴宜区", "工布江达县", "米林县", "墨脱县", "波密县", "察隅县", "朗县"]
+	}, {
+		"name": "山南市",
+		"area": ["市辖区", "乃东区", "扎囊县", "贡嘎县", "桑日县", "琼结县", "曲松县", "措美县", "洛扎县", "加查县", "隆子县", "错那县", "浪卡子县"]
+	}, {
+		"name": "那曲地区",
+		"area": ["那曲县", "嘉黎县", "比如县", "聂荣县", "安多县", "申扎县", "索县", "班戈县", "巴青县", "尼玛县", "双湖县"]
+	}, {
+		"name": "阿里地区",
+		"area": ["普兰县", "札达县", "噶尔县", "日土县", "革吉县", "改则县", "措勤县"]
+	}]
+}, {
+	"name": "陕西省",
+	"city": [{
+		"name": "西安市",
+		"area": ["市辖区", "新城区", "碑林区", "莲湖区", "灞桥区", "未央区", "雁塔区", "阎良区", "临潼区", "长安区", "高陵区", "蓝田县", "周至县",
+			"户县"
+		]
+	}, {
+		"name": "铜川市",
+		"area": ["市辖区", "王益区", "印台区", "耀州区", "宜君县"]
+	}, {
+		"name": "宝鸡市",
+		"area": ["市辖区", "渭滨区", "金台区", "陈仓区", "凤翔县", "岐山县", "扶风县", "眉县", "陇县", "千阳县", "麟游县", "凤县", "太白县"]
+	}, {
+		"name": "咸阳市",
+		"area": ["市辖区", "秦都区", "杨陵区", "渭城区", "三原县", "泾阳县", "乾县", "礼泉县", "永寿县", "彬县", "长武县", "旬邑县", "淳化县",
+			"武功县", "兴平市"
+		]
+	}, {
+		"name": "渭南市",
+		"area": ["市辖区", "临渭区", "华州区", "潼关县", "大荔县", "合阳县", "澄城县", "蒲城县", "白水县", "富平县", "韩城市", "华阴市"]
+	}, {
+		"name": "延安市",
+		"area": ["市辖区", "宝塔区", "安塞区", "延长县", "延川县", "子长县", "志丹县", "吴起县", "甘泉县", "富县", "洛川县", "宜川县", "黄龙县",
+			"黄陵县"
+		]
+	}, {
+		"name": "汉中市",
+		"area": ["市辖区", "汉台区", "南郑县", "城固县", "洋县", "西乡县", "勉县", "宁强县", "略阳县", "镇巴县", "留坝县", "佛坪县"]
+	}, {
+		"name": "榆林市",
+		"area": ["市辖区", "榆阳区", "横山区", "神木县", "府谷县", "靖边县", "定边县", "绥德县", "米脂县", "佳县", "吴堡县", "清涧县", "子洲县"]
+	}, {
+		"name": "安康市",
+		"area": ["市辖区", "汉滨区", "汉阴县", "石泉县", "宁陕县", "紫阳县", "岚皋县", "平利县", "镇坪县", "旬阳县", "白河县"]
+	}, {
+		"name": "商洛市",
+		"area": ["市辖区", "商州区", "洛南县", "丹凤县", "商南县", "山阳县", "镇安县", "柞水县"]
+	}]
+}, {
+	"name": "甘肃省",
+	"city": [{
+		"name": "兰州市",
+		"area": ["市辖区", "城关区", "七里河区", "西固区", "安宁区", "红古区", "永登县", "皋兰县", "榆中县"]
+	}, {
+		"name": "嘉峪关市",
+		"area": ["市辖区"]
+	}, {
+		"name": "金昌市",
+		"area": ["市辖区", "金川区", "永昌县"]
+	}, {
+		"name": "白银市",
+		"area": ["市辖区", "白银区", "平川区", "靖远县", "会宁县", "景泰县"]
+	}, {
+		"name": "天水市",
+		"area": ["市辖区", "秦州区", "麦积区", "清水县", "秦安县", "甘谷县", "武山县", "张家川回族自治县"]
+	}, {
+		"name": "武威市",
+		"area": ["市辖区", "凉州区", "民勤县", "古浪县", "天祝藏族自治县"]
+	}, {
+		"name": "张掖市",
+		"area": ["市辖区", "甘州区", "肃南裕固族自治县", "民乐县", "临泽县", "高台县", "山丹县"]
+	}, {
+		"name": "平凉市",
+		"area": ["市辖区", "崆峒区", "泾川县", "灵台县", "崇信县", "华亭县", "庄浪县", "静宁县"]
+	}, {
+		"name": "酒泉市",
+		"area": ["市辖区", "肃州区", "金塔县", "瓜州县", "肃北蒙古族自治县", "阿克塞哈萨克族自治县", "玉门市", "敦煌市"]
+	}, {
+		"name": "庆阳市",
+		"area": ["市辖区", "西峰区", "庆城县", "环县", "华池县", "合水县", "正宁县", "宁县", "镇原县"]
+	}, {
+		"name": "定西市",
+		"area": ["市辖区", "安定区", "通渭县", "陇西县", "渭源县", "临洮县", "漳县", "岷县"]
+	}, {
+		"name": "陇南市",
+		"area": ["市辖区", "武都区", "成县", "文县", "宕昌县", "康县", "西和县", "礼县", "徽县", "两当县"]
+	}, {
+		"name": "临夏回族自治州",
+		"area": ["临夏市", "临夏县", "康乐县", "永靖县", "广河县", "和政县", "东乡族自治县", "积石山保安族东乡族撒拉族自治县"]
+	}, {
+		"name": "甘南藏族自治州",
+		"area": ["合作市", "临潭县", "卓尼县", "舟曲县", "迭部县", "玛曲县", "碌曲县", "夏河县"]
+	}]
+}, {
+	"name": "青海省",
+	"city": [{
+		"name": "西宁市",
+		"area": ["市辖区", "城东区", "城中区", "城西区", "城北区", "大通回族土族自治县", "湟中县", "湟源县"]
+	}, {
+		"name": "海东市",
+		"area": ["乐都区", "平安区", "民和回族土族自治县", "互助土族自治县", "化隆回族自治县", "循化撒拉族自治县"]
+	}, {
+		"name": "海北藏族自治州",
+		"area": ["门源回族自治县", "祁连县", "海晏县", "刚察县"]
+	}, {
+		"name": "黄南藏族自治州",
+		"area": ["同仁县", "尖扎县", "泽库县", "河南蒙古族自治县"]
+	}, {
+		"name": "海南藏族自治州",
+		"area": ["共和县", "同德县", "贵德县", "兴海县", "贵南县"]
+	}, {
+		"name": "果洛藏族自治州",
+		"area": ["玛沁县", "班玛县", "甘德县", "达日县", "久治县", "玛多县"]
+	}, {
+		"name": "玉树藏族自治州",
+		"area": ["玉树市", "杂多县", "称多县", "治多县", "囊谦县", "曲麻莱县"]
+	}, {
+		"name": "海西蒙古族藏族自治州",
+		"area": ["格尔木市", "德令哈市", "乌兰县", "都兰县", "天峻县"]
+	}]
+}, {
+	"name": "宁夏回族自治区",
+	"city": [{
+		"name": "银川市",
+		"area": ["市辖区", "兴庆区", "西夏区", "金凤区", "永宁县", "贺兰县", "灵武市"]
+	}, {
+		"name": "石嘴山市",
+		"area": ["市辖区", "大武口区", "惠农区", "平罗县"]
+	}, {
+		"name": "吴忠市",
+		"area": ["市辖区", "利通区", "红寺堡区", "盐池县", "同心县", "青铜峡市"]
+	}, {
+		"name": "固原市",
+		"area": ["市辖区", "原州区", "西吉县", "隆德县", "泾源县", "彭阳县"]
+	}, {
+		"name": "中卫市",
+		"area": ["市辖区", "沙坡头区", "中宁县", "海原县"]
+	}]
+}, {
+	"name": "新疆维吾尔自治区",
+	"city": [{
+		"name": "乌鲁木齐市",
+		"area": ["市辖区", "天山区", "沙依巴克区", "新市区", "水磨沟区", "头屯河区", "达坂城区", "米东区", "乌鲁木齐县"]
+	}, {
+		"name": "克拉玛依市",
+		"area": ["市辖区", "独山子区", "克拉玛依区", "白碱滩区", "乌尔禾区"]
+	}, {
+		"name": "吐鲁番市",
+		"area": ["高昌区", "鄯善县", "托克逊县"]
+	}, {
+		"name": "哈密市",
+		"area": ["伊州区", "巴里坤哈萨克自治县", "伊吾县"]
+	}, {
+		"name": "昌吉回族自治州",
+		"area": ["昌吉市", "阜康市", "呼图壁县", "玛纳斯县", "奇台县", "吉木萨尔县", "木垒哈萨克自治县"]
+	}, {
+		"name": "博尔塔拉蒙古自治州",
+		"area": ["博乐市", "阿拉山口市", "精河县", "温泉县"]
+	}, {
+		"name": "巴音郭楞蒙古自治州",
+		"area": ["库尔勒市", "轮台县", "尉犁县", "若羌县", "且末县", "焉耆回族自治县", "和静县", "和硕县", "博湖县"]
+	}, {
+		"name": "阿克苏地区",
+		"area": ["阿克苏市", "温宿县", "库车县", "沙雅县", "新和县", "拜城县", "乌什县", "阿瓦提县", "柯坪县"]
+	}, {
+		"name": "克孜勒苏柯尔克孜自治州",
+		"area": ["阿图什市", "阿克陶县", "阿合奇县", "乌恰县"]
+	}, {
+		"name": "喀什地区",
+		"area": ["喀什市", "疏附县", "疏勒县", "英吉沙县", "泽普县", "莎车县", "叶城县", "麦盖提县", "岳普湖县", "伽师县", "巴楚县",
+			"塔什库尔干塔吉克自治县"
+		]
+	}, {
+		"name": "和田地区",
+		"area": ["和田市", "和田县", "墨玉县", "皮山县", "洛浦县", "策勒县", "于田县", "民丰县"]
+	}, {
+		"name": "伊犁哈萨克自治州",
+		"area": ["伊宁市", "奎屯市", "霍尔果斯市", "伊宁县", "察布查尔锡伯自治县", "霍城县", "巩留县", "新源县", "昭苏县", "特克斯县", "尼勒克县"]
+	}, {
+		"name": "塔城地区",
+		"area": ["塔城市", "乌苏市", "额敏县", "沙湾县", "托里县", "裕民县", "和布克赛尔蒙古自治县"]
+	}, {
+		"name": "阿勒泰地区",
+		"area": ["阿勒泰市", "布尔津县", "富蕴县", "福海县", "哈巴河县", "青河县", "吉木乃县"]
+	}, {
+		"name": "直辖县",
+		"area": ["石河子市", "阿拉尔市", "图木舒克市", "五家渠市", "铁门关市"]
+	}]
+}, {
+	"name": "香港特别行政区",
+	"city": [{
+		"name": "香港特别行政区",
+		"area": ["中西区", "东区", "九龙城区", "观塘区", "南区", "深水埗区", "湾仔区", "黄大仙区", "油尖旺区", "离岛区", "葵青区", "北区", "西贡区",
+			"沙田区", "屯门区", "大埔区", "荃湾区", "元朗区"
+		]
+	}]
+}, {
+	"name": "澳门特别行政区",
+	"city": [{
+		"name": "澳门特别行政区",
+		"area": ["澳门半岛", "凼仔", "路凼城", "路环"]
+	}]
+}, {
+	"name": "台湾",
+	"city": [{
+		"name": "彰化县",
+		"area": ["芳苑乡", "芬园乡", "福兴乡", "和美镇", "花坛乡", "鹿港镇", "埤头乡", "埔心乡", "埔盐乡", "伸港乡", "社头乡", "田尾乡", "田中镇",
+			"线西乡", "溪湖镇", "秀水乡", "溪州乡", "永靖乡", "员林市", "竹塘乡"
+		]
+	}, {
+		"name": "新北市",
+		"area": ["八里区", "板桥区", "贡寮区", "金山区", "林口区", "芦洲区", "坪林区", "平溪区", "瑞芳区", "三重区", "三峡区", "三芝区", "深坑区",
+			"石碇区", "石门区", "双溪区", "树林区", "泰山区", "淡水区", "土城区"
+		]
+	}, {
+		"name": "澎湖县",
+		"area": ["白沙乡", "湖西乡", "马公市", "七美乡", "望安乡", "西屿乡"]
+	}, {
+		"name": "屏东县",
+		"area": ["三地门乡", "狮子乡", "泰武乡", "万丹乡", "万峦乡", "雾臺乡", "新埤乡", "新园乡", "盐埔乡", "竹田乡", "长治乡", "潮州镇", "车城乡",
+			"春日乡", "东港镇", "枋寮乡", "枋山乡", "高树乡", "恆春镇", "佳冬乡"
+		]
+	}, {
+		"name": "臺中市",
+		"area": ["梧栖区", "乌日区", "新社区", "西屯区", "北屯区", "中区", "大肚区", "大甲区", "大里区", "大雅区", "大安区", "东势区", "东区",
+			"丰原区", "和平区", "后里区", "龙井区", "南屯区", "北区", "清水区"
+		]
+	}, {
+		"name": "臺南市",
+		"area": ["佳里区", "将军区", "六甲区", "柳营区", "龙崎区", "麻豆区", "南化区", "楠西区", "北区", "七股区", "仁德区", "善化区", "山上区",
+			"南区", "中西区", "下营区", "西港区", "新化区", "新市区", "新营区"
+		]
+	}, {
+		"name": "臺北市",
+		"area": ["北投区", "大同区", "大安区", "南港区", "内湖区", "士林区", "松山区", "万华区", "文山区", "信义区", "中山区", "中正区"]
+	}, {
+		"name": "臺东县",
+		"area": ["卑南乡", "长滨乡", "成功镇", "池上乡", "达仁乡", "大武乡", "东河乡", "关山镇", "海端乡", "金峰乡", "兰屿乡", "绿岛乡", "鹿野乡",
+			"太麻里乡", "臺东市", "延平乡"
+		]
+	}, {
+		"name": "桃园市",
+		"area": ["八德区", "大溪区", "大园区", "復兴区", "观音区", "龟山区", "龙潭区", "芦竹区", "平镇区", "桃园区", "新屋区", "杨梅区", "中坜区"]
+	}, {
+		"name": "宜兰县",
+		"area": ["大同乡", "钓鱼臺", "冬山乡", "礁溪乡", "罗东镇", "南澳乡", "三星乡", "苏澳镇", "头城镇", "五结乡", "宜兰市", "员山乡", "壮围乡"]
+	}, {
+		"name": "南投县",
+		"area": ["草屯镇", "国姓乡", "集集镇", "鹿谷乡", "名间乡", "南投市", "埔里镇", "仁爱乡", "水里乡", "信义乡", "鱼池乡", "中寮乡", "竹山镇"]
+	}, {
+		"name": "南海岛",
+		"area": ["东沙群岛", "南沙群岛"]
+	}, {
+		"name": "苗栗县",
+		"area": ["头屋乡", "西湖乡", "苑里镇", "造桥乡", "竹南镇", "卓兰镇", "大湖乡", "公馆乡", "后龙镇", "苗栗市", "南庄乡", "三湾乡", "三义乡",
+			"狮潭乡", "泰安乡", "铜锣乡", "通霄镇", "头份市"
+		]
+	}, {
+		"name": "嘉义市",
+		"area": ["东区", "西区"]
+	}, {
+		"name": "嘉义县",
+		"area": ["阿里山乡", "布袋镇", "大林镇", "大埔乡", "东石乡", "番路乡", "六脚乡", "鹿草乡", "梅山乡", "民雄乡", "朴子市", "水上乡", "太保市",
+			"溪口乡", "新港乡", "义竹乡", "中埔乡", "竹崎乡"
+		]
+	}, {
+		"name": "新竹市",
+		"area": ["东区", "北区"]
+	}, {
+		"name": "新竹县",
+		"area": ["峨眉乡", "关西镇", "横山乡", "湖口乡", "尖石乡", "芎林乡", "五峰乡", "新丰乡", "新埔镇", "竹北市", "竹东镇", "宝山乡", "北埔乡"]
+	}, {
+		"name": "花莲县",
+		"area": ["卓溪乡", "丰滨乡", "凤林镇", "富里乡", "光復乡", "花莲市", "吉安乡", "瑞穗乡", "寿丰乡", "万荣乡", "新城乡", "秀林乡", "玉里镇"]
+	}, {
+		"name": "高雄市",
+		"area": ["阿莲区", "大寮区", "大社区", "大树区", "凤山区", "冈山区", "鼓山区", "湖内区", "甲仙区", "苓雅区", "林园区", "六龟区", "路竹区",
+			"茂林区", "美浓区", "弥陀区", "那玛夏区", "楠梓区", "内门区", "鸟松区"
+		]
+	}, {
+		"name": "基隆市",
+		"area": ["安乐区", "暖暖区", "七堵区", "仁爱区", "信义区", "中山区", "中正区"]
+	}, {
+		"name": "金门县",
+		"area": ["金城镇", "金湖镇", "金宁乡", "金沙镇", "烈屿乡", "乌坵乡"]
+	}, {
+		"name": "连江县",
+		"area": ["北竿乡", "东引乡", "莒光乡", "南竿乡"]
+	}, {
+		"name": "云林县",
+		"area": ["褒忠乡", "北港镇", "莿桐乡", "大埤乡", "东势乡", "斗六市", "斗南镇", "二崙乡", "古坑乡", "虎尾镇", "口湖乡", "林内乡", "崙背乡",
+			"麦寮乡", "水林乡", "四湖乡", "臺西乡", "土库镇", "西螺镇", "元长乡"
+		]
+	}]
+}]

+ 103 - 0
components/wangding-pickerAddress/wangding-pickerAddress.vue

@@ -0,0 +1,103 @@
+<template>
+	<picker @change="bindPickerChange" @columnchange="columnchange" :range="array" range-key="name" :value="value" mode="multiSelector">
+		<slot></slot>
+	</picker>
+</template>
+
+<script>
+	import AllAddress from './data.js'
+	let selectVal = ['','',''];
+	
+	export default {
+		data() {
+			return{
+				value: [0,0,0],
+				array: [],
+				index: 0
+			}
+		},
+		created() {
+			this.initSelect()
+		},
+		methods:{
+			// 初始化地址选项
+			initSelect() {
+				this.updateSourceDate() // 更新源数据
+				.updateAddressDate() // 更新结果数据
+				.$forceUpdate()  // 触发双向绑定
+			},
+			// 地址控件改变控件
+			columnchange(d) {
+				this.updateSelectIndex(d.detail.column, d.detail.value) // 更新选择索引
+				.updateSourceDate() // 更新源数据
+				.updateAddressDate() // 更新结果数据
+				.$forceUpdate()  // 触发双向绑定
+			},
+			
+			/**
+			 * 更新源数据
+			 * */
+			updateSourceDate() {
+				this.array = []
+				this.array[0] = AllAddress.map(obj => {
+					return {
+						name: obj.name
+					}
+				})
+				this.array[1] = AllAddress[this.value[0]].city.map(obj => {
+					return {
+						name: obj.name
+					}
+				})
+				this.array[2] = AllAddress[this.value[0]].city[this.value[1]].area.map(obj => { 
+					return {
+						name: obj
+					}
+				})
+				return this
+			},
+			
+			/**
+			 * 更新索引
+			 * */
+			updateSelectIndex(column, value){
+				let arr = JSON.parse(JSON.stringify(this.value)) 
+				arr[column] = value
+				if(column === 0 ) {
+					arr[1] = 0
+					arr[2] = 0
+				}
+				if(column === 1 ) {
+					arr[2] = 0
+				}
+				this.value = arr
+				return this
+			},
+			
+			/**
+			 * 更新结果数据 
+			 * */
+			updateAddressDate() {
+				selectVal[0] = this.array[0][this.value[0]].name
+				selectVal[1] = this.array[1][this.value[1]].name 
+				selectVal[2] = this.array[2][this.value[2]].name 
+				return this
+			},
+			
+			/**
+			 * 点击确定
+			 * */
+			bindPickerChange(e) {
+				this.$emit('change', {
+					index: this.value,
+					data: selectVal
+				})
+				return this
+			}
+			
+		}
+	}
+</script>
+
+<style>
+</style>

+ 33 - 0
index.html

@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<title>
+			<%= htmlWebpackPlugin.options.title %>
+		</title>
+		<!-- Open Graph data -->
+		<!-- <meta property="og:title" content="Title Here" /> -->
+		<!-- <meta property="og:url" content="http://www.example.com/" /> -->
+		<!-- <meta property="og:image" content="http://example.com/image.jpg" /> -->
+		<!-- <meta property="og:description" content="Description Here" /> -->
+		<script>
+		window.wx = null;
+		//uniapp默认的wx重置
+		</script>
+		<script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
+		<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
+		<script>
+			var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
+			document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+		</script>
+		<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
+	</head>
+	<body>
+		<noscript>
+			<strong>Please enable JavaScript to continue.</strong>
+		</noscript>
+		<div id="app"></div>
+		<!-- built files will be auto injected -->
+	</body>
+</html>

+ 25 - 0
libs/i18n/index.js

@@ -0,0 +1,25 @@
+import vue from "vue";
+import VueI18n from "vue-i18n";
+vue.use(VueI18n)
+
+// 获取语言
+const requireComponent = require.context(
+    // 其组件目录的相对路径
+    './lang',
+    // 是否查询其子目录
+    true,
+    // 匹配基础组件文件名的正则表达式
+    /[a-zA-Z]\w+\.(json)$/
+)
+let messages = new Object();
+requireComponent.keys().forEach(fileName => {
+    // 获取组件的PascalCase命名
+    const componentName = fileName.split('/').pop().replace(/\.\w+$/, '');
+    messages[componentName] = requireComponent(fileName);
+})
+// 语言注入
+let i18n = new VueI18n({
+    locale: uni.getStorageSync('lang')||'tw',
+    messages: messages
+})
+export default i18n;

+ 126 - 0
libs/i18n/lang/cn.json

@@ -0,0 +1,126 @@
+{  "common":{
+	   "温馨提示":"温馨提示",
+		"您未登录":"您未登录",
+		"取消":"取消",
+		"确定":"确定",
+		"检测授权中":"检测授权中",
+		"插件":"请先安装MetaMask插件",
+		"安装了多个钱包":"安装了多个钱包,加载失败",
+		"加载错误请重试":"加载错误请重试",
+		"请先绑定推荐人":"请先绑定推荐人",
+		"内容为空":"内容为空",
+		"不支持":"不支持",
+		"复制成功":"复制成功",
+		"绑定成功":"绑定成功"
+	},
+	"index":{
+		"矿机购买":"矿机购买",
+		"购买更高":"购买更高矿机畅享更高挖矿收益",
+		"闪兑":"闪兑",
+		"立即前往":"立即前往",
+		"价格":"价格",
+		"总收益":"总收益",
+		"查看详情":"查看详情",
+		"我的矿机":"我的矿机",
+		"我的邀请":"我的邀请",
+		"邀请团队信息":"邀请团队信息",
+		"我的资产":"我的资产",
+		"资产明细":"资产明细",
+		"我的收益":"我的收益",
+		"收益明细":"收益明细",
+		"邀请地址":"邀请地址",
+		"复制":"复制",
+		"确认绑定":"确认绑定"
+	},
+	"details":{
+		"价格":"价格",
+		"总收益":"总收益",
+		"当前运作矿机":"当前运作矿机",
+		"今日挖矿收益":"今日挖矿收益",
+		"立即购买":"立即购买",
+		"矿机详情":"矿机详情",
+		"开通完成":"开通完成",
+		"尚有未出局的矿机":"尚有未出局的矿机,请等待挖矿完成"
+	},
+	"detailsTpl":{
+		"我的矿机":"我的矿机",
+		"当前矿机":"当前矿机",
+		"工作中":"工作中",
+		"已释放":"已释放",
+		"待释放":"待释放",
+		"暂无工作中的矿机列表":"暂无工作中的矿机列表",
+		"释放记录":"释放记录",
+		"购买记录":"购买记录"
+	},
+	"exchange":{
+		"闪兑":"闪兑",
+		"兑换":"兑换",
+		"余额":"余额",
+		"请填写兑换的NICE数量":"请填写兑换的NICE数量",
+		"实际到账的":"实际到账的",
+		"兑换比率":"兑换比率",
+		"立即兑换":"立即兑换",
+		"余额不足":"余额不足",
+		"兑换成功":"兑换成功",
+		"请勿频繁操作":"请勿频繁操作"
+	},
+	"income":{
+		"我的收益":"我的收益",
+		"收益统计":"收益统计",
+		"累计收益":"累计收益",
+		"累计提现":"累计提现",
+		"剩余可提现":"剩余可提现",
+		"收益明细":"收益明细",
+		"明细记录":"明细记录",
+		"直推奖":"直推奖",
+		"业绩奖":"业绩奖",
+		"平级奖":"平级奖"
+	},
+	"incomeTpl":{
+		"收支明细":"收支明细",
+		"流水":"流水",
+		"收入":"收入",
+		"支出":"支出",
+		"余额":"余额",
+		"全部":"全部"
+	},
+	"propDetails":{
+		"交易记录":"交易记录"
+	},
+	"property":{
+		"我的资产":"我的资产",
+		"提现":"提现",
+		"转账":"转账",
+		"充值":"充值"
+	},
+	"recharge":{
+		"充值数量":"充值数量",
+		"可用":"可用",
+		"请输入充值数量":"请输入充值数量",
+	   "充值成功":"充值成功",
+		"请等待审核":"交易已提交,请等待审核"
+	},
+	"transfer":{
+		"收款地址":"收款地址",
+		"请输入或长按粘贴地址":"请输入或长按粘贴地址",
+		"转账金额":"转账金额",
+		"请输入转账金额":"请输入转账金额",
+		"可转账余额":"可转账余额",
+		"全部":"全部",
+		"请输入收款地址":"请输入收款地址",
+		"转账成功":"转账成功",
+		"参数错误":"参数错误"
+	},
+	"withdrawal":{
+		"提币":"提币",
+		"提币數量":"提币數量",
+		"可用":"可用",
+		"请输入提币数量":"请输入提币数量",
+		"全部":"全部",
+		"手续费":"手续费",
+		"实际到账":"实际到账",
+		"提币数量大于100":"提币数量要大于100",
+		"提取成功":"提取成功",
+		"提取失败":"提取失败"
+	}
+}

+ 127 - 0
libs/i18n/lang/en.json

@@ -0,0 +1,127 @@
+{
+	"common":{
+		"温馨提示":"Warm reminder",
+		"您未登录":"You are not logged in",
+		"取消":"Cancel",
+		"确定":"Confirm",
+		"检测授权中":"Detection authorization",
+		"插件":"Please install the MetaMask plugin first",
+		"安装了多个钱包":"Multiple wallets installed, loading failed",
+		"加载错误请重试":"Loading error Please try again",
+		"请先绑定推荐人":"Please bind your referees first",
+		"内容为空":"Content is empty",
+		"不支持":"Not Supported",
+		"复制成功":"Successful replication",
+		"绑定成功":"Binding successful"
+	},
+	"index":{
+		"矿机购买":"Purchase of mining machines",
+		"购买更高":"Buy higher miners to enjoy higher mining benefits",
+		"闪兑":"Flash Exchange",
+		"立即前往":"Go now",
+		"价格":"price",
+		"总收益":"total revenue",
+		"查看详情":"Detail",
+		"我的矿机":"My mining machine",
+		"我的邀请":"My invitation",
+		"邀请团队信息":"Invite team information",
+		"我的资产":"My assets",
+		"资产明细":"Asset Details",
+		"我的收益":"My income",
+		"收益明细":"Income Details",
+		"邀请地址":"Invitation address",
+		"复制":"COPY",
+		"确认绑定":"Confirm binding"
+	},
+	"details":{
+		"价格":"price",
+		"总收益":"total revenue",
+		"当前运作矿机":"Currently operating miner",
+		"今日挖矿收益":"Mining earnings today",
+		"立即购买":"BUY NOW",
+		"矿机详情":"Miner details",
+		"开通完成":"Completion of opening",
+		"尚有未出局的矿机":"There are still miners, please wait until mining is complete"
+	},
+	"detailsTpl":{
+		"我的矿机":"My miner",
+		"当前矿机":"Current miner",
+		"工作中":"At work",
+		"已释放":"Released",
+		"待释放":"To be released",
+		"暂无工作中的矿机列表":"There is no list of working miners",
+		"释放记录":"Release",
+		"购买记录":"Purchase"
+	},
+	"exchange":{
+		"闪兑":"Flash Exchange",
+		"兑换":"exchange",
+		"余额":"balance",
+		"请填写兑换的NICE数量":"Fill in the NICE amount",
+		"实际到账的":"to the account",
+		"兑换比率":"exchange rate",
+		"立即兑换":"Redeem now",
+		"余额不足":"Insufficient balance",
+		"兑换成功":"Exchange successful",
+		"请勿频繁操作":"Do not operate frequently"
+	},
+	"income":{
+		"我的收益":"My earnings",
+		"收益统计":"Income statistics",
+		"累计收益":"Cumulative income",
+		"累计提现":"Cumulative withdrawal",
+		"剩余可提现":"The rest can be withdrawn",
+		"收益明细":"Income detail",
+		"明细记录":"Detail record",
+		"直推奖":"Direct push prize",
+		"业绩奖":"Merit award",
+		"平级奖":"Parallel award"
+	},
+	"incomeTpl":{
+		"收支明细":"Income and expenditure detail",
+		"流水":"Running water",
+		"收入":"income",
+		"支出":"disburse",
+		"余额":"balance",
+		"全部":"complete"
+	},
+	"propDetails":{
+		"交易记录":"Transaction record"
+	},
+	"property":{
+		"我的资产":"My assets",
+		"提现":"withdraw",
+		"转账":"Transfer",
+		"充值":"recharge"
+	},
+	"recharge":{
+		"充值数量":"Recharge quantity",
+		"可用":"usable",
+		"请输入充值数量":"enter amount of recharge",
+	   "充值成功":"Recharge successfully",
+		"请等待审核":"Wait for review"
+	},
+	"transfer":{
+		"收款地址":"Collection address",
+		"请输入或长按粘贴地址":"enter or paste the address",
+		"转账金额":"Transfer amount",
+		"请输入转账金额":"enter the transfer amount",
+		"可转账余额":"Transferable balance",
+		"全部":"All",
+		"请输入收款地址":"Please enter the collection address",
+		"转账成功":"Transfer successful",
+		"参数错误":"Parameter error"
+	},
+	"withdrawal":{
+		"提币":"Withdraw currency",
+		"提币數量":"Withdrawal amount",
+		"可用":"usable",
+		"请输入提币数量":"Please enter the withdrawal amount",
+		"全部":"All",
+		"手续费":"Handling charge",
+		"实际到账":"Actual arrival",
+		"提币数量大于100":"Withdraw more than 100 coins",
+		"提取成功":"Successful extraction",
+		"提取失败":"Fetch failure"
+	}
+}

+ 4 - 0
libs/i18n/lang/tw.json

@@ -0,0 +1,4 @@
+{
+	"home":{},
+	"details":{}
+}

+ 39 - 0
libs/log.js

@@ -0,0 +1,39 @@
+
+const logLength=100;//缓存存储上限
+const name = 'log';//缓存名字
+export function addLog (data,content='') {
+	let log = uni.getStorageSync(name)||[];
+	log.unshift({
+		title:data,
+		content:content
+	});
+	uni.setStorageSync(name,log);
+	initLog(log);
+}
+
+
+export function delLog () {
+	return uni.setStorageSync(name,'');
+}
+
+export function getLog () {
+	return uni.getStorageSync(name);
+}
+
+export function initLog (log) {
+	if(log.length>logLength){
+		const newarr = log.slice(log.length-logLength);
+		uni.setStorageSync(name,newarr);
+	}
+}
+export function showLog (log) {
+	
+	let str = '';
+	uni.getStorageSync(name).forEach((e) => {
+		str+=e.title+':'+JSON.stringify(e.content)
+	})
+	uni.showModal({
+		title:"日志",
+		content:str
+	})
+}

+ 84 - 0
libs/login.js

@@ -0,0 +1,84 @@
+import store from "../store";
+import Cache from '../utils/cache';
+// #ifdef H5 || APP-PLUS
+import {
+	isWeixin
+} from "../utils";
+import auth from './wechat';
+// #endif
+
+import {
+	LOGIN_STATUS,
+	USER_INFO,
+	EXPIRES_TIME,
+	STATE_R_KEY
+} from './../config/cache';
+
+function prePage() {
+	let pages = getCurrentPages();
+	let prePage = pages[pages.length - 2];
+	// #ifdef H5
+	return prePage;
+	// #endif
+	return prePage.$vm;
+}
+
+export function toLogin(push, pathLogin) {
+	// store.commit("LOGOUT");
+	let path = prePage();
+	if (path) {
+		path = path.router;
+		if (path == undefined) {
+			path = location.pathname;
+		}
+	}
+	// #ifdef H5
+	else {
+		path = location.pathname;
+	}
+	// #endif
+
+	if (!pathLogin)
+		pathLogin = '/page/users/login/index'
+	Cache.set('login_back_url', path);
+	// #ifdef H5 || APP-PLUS
+	if (isWeixin()) {
+		auth.oAuth();
+	} else {
+		if (path !== pathLogin) {
+			push ? uni.navigateTo({
+				url: '/pages/users/login/index'
+			}) : uni.reLaunch({
+				url: '/pages/users/login/index'
+			});
+		}
+	}
+	// #endif
+
+	// #ifdef MP 
+
+
+	// #endif
+}
+
+
+export function checkLogin() {
+	let token = Cache.get(LOGIN_STATUS);
+	let expiresTime = Cache.get(EXPIRES_TIME);
+	let newTime = Math.round(new Date() / 1000);
+	if (expiresTime < newTime || !token) {
+		Cache.clear(LOGIN_STATUS);
+		Cache.clear(EXPIRES_TIME);
+		Cache.clear(USER_INFO);
+		Cache.clear(STATE_R_KEY);
+		return false;
+	} else {
+		store.commit('UPDATE_LOGIN', token);
+		let userInfo = Cache.get(USER_INFO, true);
+		if (userInfo) {
+			store.commit('UPDATE_USERINFO', userInfo);
+		}
+		return true;
+	}
+
+}

+ 253 - 0
libs/wechat.js

@@ -0,0 +1,253 @@
+// #ifdef H5
+import WechatJSSDK from "@/plugin/jweixin-module/index.js";
+// #endif
+
+import {
+	wechatConfig,
+	wechatAuth
+} from "@/api/wx.js";
+import {
+	WX_AUTH,
+	STATE_KEY,
+	LOGINTYPE,
+	BACK_URL
+} from '@/config/cache';
+import {
+	parseQuery
+} from '@/utils';
+import store from '@/store';
+import Cache from '@/utils/cache';
+
+class AuthWechat {
+	// #ifdef H5
+	constructor() {
+		//微信实例化对象
+		this.instance = WechatJSSDK;
+		//是否实例化
+		this.status = false;
+
+		this.initConfig = {};
+
+	}
+	// #endif
+	
+	isAndroid(){
+		let u = navigator.userAgent;
+		return u.indexOf('Android') > -1 || u.indexOf('Adr') > -1;
+	}
+
+	/**
+	 * 初始化wechat(分享配置)
+	 */
+	wechat() {
+		return new Promise((resolve, reject) => {
+			// if (this.status && !this.isAndroid()) return resolve(this.instance);
+			wechatConfig()
+				.then(res => {
+					this.instance.config(res.data);
+					this.initConfig = res.data;
+					this.status = true;
+					this.instance.ready(() => {
+						resolve(this.instance);
+					})
+				}).catch(err => {
+					console.log(err);
+					this.status = false;
+					reject(err);
+				});
+		});
+	}
+
+	/**
+	 * 验证是否初始化
+	 */
+	verifyInstance() {
+		let that = this;
+		return new Promise((resolve, reject) => {
+			if (that.instance === null && !that.status) {
+				that.wechat().then(res => {
+					resolve(that.instance);
+				}).catch(() => {
+					return reject();
+				})
+			} else {
+				return resolve(that.instance);
+			}
+		})
+	}
+	// 微信公众号的共享地址
+	openAddress() {
+		return new Promise((resolve, reject) => {
+			this.wechat().then(wx => {
+				this.toPromise(wx.openAddress).then(res => {
+					resolve(res);
+				}).catch(err => {
+					reject(err);
+				});
+			}).catch(err => {
+				reject(err);
+			})
+		});
+	}
+
+	/**
+	 * 微信支付
+	 * @param {Object} config
+	 */
+	pay(config) {
+		return new Promise((resolve, reject) => {
+			this.wechat().then((wx) => {
+				this.toPromise(wx.chooseWXPay, config).then(res => {
+					resolve(res);
+				}).catch(res => {
+					reject(res);
+				});
+			}).catch(res => {
+				reject(res);
+			});
+		});
+	}
+
+	toPromise(fn, config = {}) {
+		return new Promise((resolve, reject) => {
+			fn({
+				...config,
+				success(res) {
+					resolve(res);
+				},
+				fail(err) {
+					reject(err);
+				},
+				complete(err) {
+					reject(err);
+				},
+				cancel(err) {
+					reject(err);
+				}
+			});
+		});
+	}
+
+	/**
+	 * 自动去授权
+	 */
+	oAuth() {
+		if (uni.getStorageSync(WX_AUTH) && store.state.app.token) return;
+		const {
+			code
+		} = parseQuery();
+		if (!code) return this.toAuth();
+	}
+
+	clearAuthStatus() {
+
+	}
+
+	/**
+	 * 授权登陆获取token
+	 * @param {Object} code
+	 */
+	auth(code) {
+		return new Promise((resolve, reject) => {
+			let loginType = Cache.get(LOGINTYPE);
+			wechatAuth(code, parseInt(Cache.get("spread")), loginType)
+				.then(({
+					data
+				}) => {
+					let expires_time = data.expires_time.substring(0, 19);
+					expires_time = expires_time.replace(/-/g, '/');
+					expires_time = new Date(expires_time).getTime();
+					let newTime = Math.round(new Date() / 1000);
+					store.commit("LOGIN", {
+						token: data.token,
+						time: expires_time - newTime
+					});
+					Cache.set(WX_AUTH, code);
+					Cache.clear(STATE_KEY);
+					loginType && Cache.clear(LOGINTYPE);
+					resolve();
+				})
+				.catch(reject);
+		});
+	}
+
+	/**
+	 * 获取跳转授权后的地址
+	 * @param {Object} appId
+	 */
+	getAuthUrl(appId) {
+		const redirect_uri = encodeURIComponent(
+			`${location.origin}/pages/auth/index?back_url=` +
+			encodeURIComponent(
+				encodeURIComponent(
+					uni.getStorageSync(BACK_URL) ?
+					uni.getStorageSync(BACK_URL) :
+					location.pathname + location.search
+				)
+			)
+		);
+		uni.removeStorageSync(BACK_URL);
+		const state = encodeURIComponent(
+			("" + Math.random()).split(".")[1] + "authorizestate"
+		);
+		uni.setStorageSync(STATE_KEY, state);
+		return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`;
+	}
+
+	/**
+	 * 跳转自动登陆
+	 */
+	toAuth() {
+		let that = this;
+		this.wechat().then(wx => {
+			location.href = this.getAuthUrl(that.initConfig.appId);
+		})
+	}
+
+	/**
+	 * 绑定事件
+	 * @param {Object} name 事件名
+	 * @param {Object} config 参数
+	 */
+	wechatEvevt(name, config) {
+		let that = this;
+		return new Promise((resolve, reject) => {
+			let configDefault = {
+				fail(res) {
+					console.log(res,11111);
+					if (that.instance) return reject({
+						is_ready: true,
+						wx: that.instance
+					});
+					that.verifyInstance().then(wx => {
+						return reject({
+							is_ready: true,
+							wx: wx
+						});
+					})
+				},
+				success(res) {
+					return resolve(res,2222);
+				}
+			};
+			Object.assign(configDefault, config);
+			that.wechat().then(wx => {
+				if (typeof name === 'object') {
+					name.forEach(item => {
+						wx[item] && wx[item](configDefault)
+					})
+				} else {
+					wx[name] && wx[name](configDefault)
+				}
+			})
+		});
+	}
+
+	isWeixin() {
+		return navigator.userAgent.toLowerCase().indexOf("micromessenger") !== -1;
+	}
+
+}
+
+export default new AuthWechat();
+

+ 48 - 0
main.js

@@ -0,0 +1,48 @@
+import Vue from 'vue'
+import store from './store'
+import App from './App'
+import i18n from './libs/i18n/index.js'
+import uView from '@/uni_modules/uview-ui'
+Vue.use(uView)
+/**
+ *  所有测试用数据均存放于根目录json.js
+ *  
+ *  css部分使用了App.vue下的全局样式和iconfont图标,有需要图标库的可以留言。
+ *  示例使用了uni.scss下的变量, 除变量外已尽量移除特有语法,可直接替换为其他预处理器使用
+ */
+const msg = (title, duration=1500, mask=false, icon='none')=>{
+	//统一提示方便全局修改
+	if(Boolean(title) === false){
+		return;
+	}
+	uni.showToast({
+		title,
+		duration,
+		mask,
+		icon
+	});
+}
+
+const prePage = ()=>{
+	// 获取当前页面
+	let pages = getCurrentPages();
+	let prePage = pages[pages.length - 2];
+	// #ifdef H5
+	return prePage;
+	// #endif
+	return prePage.$vm;
+}
+
+Vue.config.productionTip = false
+Vue.prototype.$fire = new Vue();
+Vue.prototype._i18n = i18n;
+Vue.prototype.$store = store;
+Vue.prototype.$api = {msg, prePage};
+
+App.mpType = 'app'
+
+const app = new Vue({
+    ...App,
+	i18n
+})
+app.$mount()

+ 117 - 0
manifest.json

@@ -0,0 +1,117 @@
+{
+    "name" : "NICE",
+    "appid" : "__UNI__F99BFF9",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : 100,
+    "transformPx" : false,
+    "app-plus" : {
+        /* 5+App特有相关 */
+        "usingComponents" : true,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "modules" : {},
+        /* 模块配置 */
+        "distribute" : {
+            /* 应用发布信息 */
+            "android" : {
+                /* android打包配置 */
+                "permissions" : [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
+            },
+            "ios" : {
+                "idfa" : false,
+                "dSYMs" : false
+            },
+            /* ios打包配置 */
+            "sdkConfigs" : {
+                "maps" : {},
+                "oauth" : {
+                    "weixin" : {
+                        "appid" : "",
+                        "appsecret" : "",
+                        "UniversalLinks" : ""
+                    }
+                },
+                "ad" : {}
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "",
+                    "xhdpi" : "",
+                    "xxhdpi" : "",
+                    "xxxhdpi" : ""
+                },
+                "ios" : {
+                    "appstore" : "",
+                    "ipad" : {
+                        "app" : "",
+                        "app@2x" : "",
+                        "notification" : "",
+                        "notification@2x" : "",
+                        "proapp@2x" : "",
+                        "settings" : "",
+                        "settings@2x" : "",
+                        "spotlight" : "",
+                        "spotlight@2x" : ""
+                    },
+                    "iphone" : {
+                        "app@2x" : "",
+                        "app@3x" : "",
+                        "notification@2x" : "",
+                        "notification@3x" : "",
+                        "settings@2x" : "",
+                        "settings@3x" : "",
+                        "spotlight@2x" : "",
+                        "spotlight@3x" : ""
+                    }
+                }
+            }
+        }
+    },
+    /* SDK配置 */
+    "quickapp" : {},
+    /* 快应用特有相关 */
+    "mp-weixin" : {
+        /* 小程序特有相关 */
+        "usingComponents" : true,
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : true,
+            "minified" : true
+        }
+    },
+    "h5" : {
+        "title" : "NICE",
+        "domain" : "",
+        "router" : {
+            "base" : "/index/",
+            "mode" : "hash"
+        },
+        // "devServer" : {
+        //     "proxy" : {
+        //         "/api" : {
+        //             "target" : "https://www.bscnice.com"
+        //         }
+        //     }
+        // },
+        "template" : "index.html"
+    }
+}

+ 298 - 0
package-lock.json

@@ -0,0 +1,298 @@
+{
+  "name": "BF",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "@metamask/detect-provider": "^2.0.0",
+        "uview-ui": "^2.0.36",
+        "vue-i18n": "^9.8.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.23.6",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.6.tgz",
+      "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
+      "peer": true,
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@intlify/core-base": {
+      "version": "9.8.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.8.0.tgz",
+      "integrity": "sha512-UxaSZVZ1DwqC/CltUZrWZNaWNhfmKtfyV4BJSt/Zt4Or/fZs1iFj0B+OekYk1+MRHfIOe3+x00uXGQI4PbO/9g==",
+      "dependencies": {
+        "@intlify/message-compiler": "9.8.0",
+        "@intlify/shared": "9.8.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "9.8.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.8.0.tgz",
+      "integrity": "sha512-McnYWhcoYmDJvssVu6QGR0shqlkJuL1HHdi5lK7fNqvQqRYaQ4lSLjYmZxwc8tRNMdIe9/KUKfyPxU9M6yCtNQ==",
+      "dependencies": {
+        "@intlify/shared": "9.8.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "9.8.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.8.0.tgz",
+      "integrity": "sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ==",
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.15",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+      "peer": true
+    },
+    "node_modules/@metamask/detect-provider": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@metamask/detect-provider/-/detect-provider-2.0.0.tgz",
+      "integrity": "sha512-sFpN+TX13E9fdBDh9lvQeZdJn4qYoRb/6QF2oZZK/Pn559IhCFacPMU1rMuqyXoFQF3JSJfii2l98B87QDPeCQ==",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.13.tgz",
+      "integrity": "sha512-bwi9HShGu7uaZLOErZgsH2+ojsEdsjerbf2cMXPwmvcgZfVPZ2BVZzCVnwZBxTAYd6Mzbmf6izcUNDkWnBBQ6A==",
+      "peer": true,
+      "dependencies": {
+        "@babel/parser": "^7.23.5",
+        "@vue/shared": "3.3.13",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.13.tgz",
+      "integrity": "sha512-EYRDpbLadGtNL0Gph+HoKiYqXLqZ0xSSpR5Dvnu/Ep7ggaCbjRDIus1MMxTS2Qm0koXED4xSlvTZaTnI8cYAsw==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-core": "3.3.13",
+        "@vue/shared": "3.3.13"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.13.tgz",
+      "integrity": "sha512-DQVmHEy/EKIgggvnGRLx21hSqnr1smUS9Aq8tfxiiot8UR0/pXKHN9k78/qQ7etyQTFj5em5nruODON7dBeumw==",
+      "peer": true,
+      "dependencies": {
+        "@babel/parser": "^7.23.5",
+        "@vue/compiler-core": "3.3.13",
+        "@vue/compiler-dom": "3.3.13",
+        "@vue/compiler-ssr": "3.3.13",
+        "@vue/reactivity-transform": "3.3.13",
+        "@vue/shared": "3.3.13",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.5",
+        "postcss": "^8.4.32",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.13.tgz",
+      "integrity": "sha512-d/P3bCeUGmkJNS1QUZSAvoCIW4fkOKK3l2deE7zrp0ypJEy+En2AcypIkqvcFQOcw3F0zt2VfMvNsA9JmExTaw==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-dom": "3.3.13",
+        "@vue/shared": "3.3.13"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
+      "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.13.tgz",
+      "integrity": "sha512-fjzCxceMahHhi4AxUBzQqqVhuA21RJ0COaWTbIBl1PruGW1CeY97louZzLi4smpYx+CHfFPPU/CS8NybbGvPKQ==",
+      "peer": true,
+      "dependencies": {
+        "@vue/shared": "3.3.13"
+      }
+    },
+    "node_modules/@vue/reactivity-transform": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.13.tgz",
+      "integrity": "sha512-oWnydGH0bBauhXvh5KXUy61xr9gKaMbtsMHk40IK9M4gMuKPJ342tKFarY0eQ6jef8906m35q37wwA8DMZOm5Q==",
+      "peer": true,
+      "dependencies": {
+        "@babel/parser": "^7.23.5",
+        "@vue/compiler-core": "3.3.13",
+        "@vue/shared": "3.3.13",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.5"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.13.tgz",
+      "integrity": "sha512-1TzA5TvGuh2zUwMJgdfvrBABWZ7y8kBwBhm7BXk8rvdx2SsgcGfz2ruv2GzuGZNvL1aKnK8CQMV/jFOrxNQUMA==",
+      "peer": true,
+      "dependencies": {
+        "@vue/reactivity": "3.3.13",
+        "@vue/shared": "3.3.13"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.13.tgz",
+      "integrity": "sha512-JJkpE8R/hJKXqVTgUoODwS5wqKtOsmJPEqmp90PDVGygtJ4C0PtOkcEYXwhiVEmef6xeXcIlrT3Yo5aQ4qkHhQ==",
+      "peer": true,
+      "dependencies": {
+        "@vue/runtime-core": "3.3.13",
+        "@vue/shared": "3.3.13",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.13.tgz",
+      "integrity": "sha512-vSnN+nuf6iSqTL3Qgx/9A+BT+0Zf/VJOgF5uMZrKjYPs38GMYyAU1coDyBNHauehXDaP+zl73VhwWv0vBRBHcg==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-ssr": "3.3.13",
+        "@vue/shared": "3.3.13"
+      },
+      "peerDependencies": {
+        "vue": "3.3.13"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.13.tgz",
+      "integrity": "sha512-/zYUwiHD8j7gKx2argXEMCUXVST6q/21DFU0sTfNX0URJroCe3b1UF6vLJ3lQDfLNIiiRl2ONp7Nh5UVWS6QnA==",
+      "peer": true
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "peer": true
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "peer": true
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.5",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz",
+      "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+      "peer": true,
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "peer": true
+    },
+    "node_modules/postcss": {
+      "version": "8.4.32",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.32.tgz",
+      "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
+      "peer": true,
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/uview-ui": {
+      "version": "2.0.36",
+      "resolved": "https://registry.npmmirror.com/uview-ui/-/uview-ui-2.0.36.tgz",
+      "integrity": "sha512-ASSZT6M8w3GTO1eFPbsgEFV0U5UujK+8pTNr+MSUbRNcRMC1u63DDTLJVeArV91kWM0bfAexK3SK9pnTqF9TtA==",
+      "engines": {
+        "HBuilderX": "^3.1.0"
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.3.13",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.13.tgz",
+      "integrity": "sha512-LDnUpQvDgsfc0u/YgtAgTMXJlJQqjkxW1PVcOnJA5cshPleULDjHi7U45pl2VJYazSSvLH8UKcid/kzH8I0a0Q==",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-dom": "3.3.13",
+        "@vue/compiler-sfc": "3.3.13",
+        "@vue/runtime-dom": "3.3.13",
+        "@vue/server-renderer": "3.3.13",
+        "@vue/shared": "3.3.13"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-i18n": {
+      "version": "9.8.0",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.8.0.tgz",
+      "integrity": "sha512-Izho+6PYjejsTq2mzjcRdBZ5VLRQoSuuexvR8029h5CpN03FYqiqBrShMyf2I1DKkN6kw/xmujcbvC+4QybpsQ==",
+      "dependencies": {
+        "@intlify/core-base": "9.8.0",
+        "@intlify/shared": "9.8.0",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    }
+  }
+}

+ 7 - 0
package.json

@@ -0,0 +1,7 @@
+{
+  "dependencies": {
+    "@metamask/detect-provider": "^2.0.0",
+    "uview-ui": "^2.0.36",
+    "vue-i18n": "^9.8.0"
+  }
+}

+ 109 - 0
pages.json

@@ -0,0 +1,109 @@
+
+{
+	"easycom": {
+			"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue"
+		},
+	"pages": [
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "NICE",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/index/exchange",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/details",
+			"style": {
+				"navigationBarTitleText": ""
+			}
+		},
+		{
+			"path": "pages/index/detailsTpl",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/invite",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/property",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/propDetails",
+			"style": {
+				"navigationBarTitleText": ""
+			}
+		},
+		{
+			"path": "pages/index/income",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/incomeTpl",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/withdrawal",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/recharge",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/index/transfer",
+			"style": {
+				"navigationBarTitleText": ""
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "white",
+		"navigationBarTitleText": "NICE",
+		"navigationBarBackgroundColor": "#100F0B"
+	},
+	"tabBar": {
+		"color": "#C0C4CC",
+		"selectedColor": "#FEB041 ",
+		"borderStyle": "black",
+		"backgroundColor": "#000000",
+		"list": []
+	}
+}

+ 96 - 0
pages/components/topView.vue

@@ -0,0 +1,96 @@
+<template>
+	<view class="flex topBox" :style="{background:backg}">
+		<view class="flex_item">
+			<image src="/static/img/img01.png" style="width: 56rpx;height: 49rpx;"></image>
+			<view style="color: #fff;font-size: 36rpx;padding-left: 15rpx;">NICE</view>
+		</view>
+		<view class="flex_item topTpl">
+			<view class="addr" style="margin-right: 15rpx;">L{{userInfo.level}}</view>
+			<view class="addr" v-if="userInfo.addresss">{{userInfo.addresss}}</view>
+			<view class="flex_item tplInfo">
+				<image src="/static/img/img14.png" style="width: 28rpx;height: 28rpx;margin: 0rpx 15rpx;"></image>
+				<picker :range="langList" range-key='label' @change="selectLang">
+					<view style="font-size: 22rpx;">{{langLabel}}</view>
+				</picker>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import { getUserInfo } from '@/api/login.js';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	export default {
+		props:{
+			backg: {
+				type: String,
+				default: '#15130E'
+			},
+		},
+		data() {
+			return{
+
+			};
+		},
+		mounted() {
+			this.getInfo();
+		},
+		computed: {
+			...mapState({
+				langList: "langList",
+				lang: "lang",
+			}),
+			...mapState('user', ['userInfo']),
+			langLabel () {
+				const langLabel = this.langList.find((item) => {
+					return item.value == this.lang
+				}).label;
+				return langLabel
+			}
+		
+		},
+		methods: {
+			...mapMutations('user', ['setUserInfo']),
+			...mapActions({
+				setLang: "setLang",
+			}),
+			getInfo(){
+				const obj = this
+				getUserInfo({}).then(({
+					data
+				}) => {
+					data.addresss = data.address.slice(0, 4) + "****" + data.address.slice(data.address.length - 4);
+					obj.setUserInfo(data);
+				}).catch(e => {
+					console.log(e.msg);
+				});
+			},
+			//切换语言
+			selectLang (value) {
+				this.setLang(this.langList[value.detail.value].value);
+				this.$router.go(0)
+			},
+		},
+	};
+</script>
+<style lang="scss" scoped>
+	.topBox {
+		color: #FFE0BD;
+		padding: 25rpx 25rpx 25rpx 25rpx;
+		position: fixed;
+		top: 0;
+		width: 100%;
+		z-index: 999;
+		.topTpl {
+			.addr {
+				padding: 4rpx 15rpx;
+				background: linear-gradient(-74deg, #CE9C6D, #FFECD6);
+				box-shadow: 3rpx 4rpx 5rpx 0rpx rgba(151, 118, 74, 0.5);
+				border-radius: 21rpx;
+				font-size: 24rpx;
+				font-family: PingFang SC;
+				font-weight: 500;
+				color: #986629;
+			}
+		}
+	}
+</style>

+ 239 - 0
pages/index/common.scss

@@ -0,0 +1,239 @@
+
+.d-table {
+	display: table;
+	width: 100%;
+	vertical-align: middle;
+	text-align: center;
+	table-layout: fixed;
+	font-size: 28rpx;
+	word-break: break-all;
+	background-color: white;
+	border-collapse: collapse;
+	&.primary {
+		&.dark {
+			.d-thead {
+				background-color: #2b85e4;
+			}
+		}
+		&.disabled {
+			.d-thead {
+				background-color: #a0cfff;
+			}
+		}
+		&.light {
+			.d-thead {
+				background-color: #ecf5ff;
+			}
+		}
+		.d-thead {
+			background-color: #2979ff;
+			color: #fff;
+		}
+	}
+	&.success {
+		&.dark {
+			.d-thead {
+				background-color: #18b566;
+			}
+		}
+		&.disabled {
+			.d-thead {
+				background-color: #71d5a1;
+			}
+		}
+		&.light {
+			.d-thead {
+				background-color: #dbf1e1;
+			}
+		}
+		.d-thead {
+			background-color: #19be6b;
+			color: #fff;
+		}
+	}
+	&.warning {
+		&.dark {
+			.d-thead {
+				background-color: #f29100;
+			}
+		}
+		&.disabled {
+			.d-thead {
+				background-color: #fcbd71;
+			}
+		}
+		&.light {
+			.d-thead {
+				background-color: #fdf6ec;
+			}
+		}
+		.d-thead {
+			background-color: #ff9900;
+			color: #fff;
+		}
+	}
+	&.danger {
+		&.dark {
+			.d-thead {
+				background-color: #dd6161;
+			}
+		}
+		&.disabled {
+			.d-thead {
+				background-color: #fab6b6;
+			}
+		}
+		&.light {
+			.d-thead {
+				background-color: #fef0f0;
+			}
+		}
+		.d-thead {
+			background-color: #fa3534;
+			color: #fff;
+		}
+	}
+	&.info {
+		&.dark {
+			.d-thead {
+				background-color: #82848a;
+			}
+		}
+		&.disabled {
+			.d-thead {
+				background-color: #c8c9cc;
+			}
+		}
+		&.light {
+			.d-thead {
+				background-color: #f4f4f5;
+			}
+		}
+		.d-thead {
+			background-color: #909399;
+			color: #fff;
+		}
+	}
+	&.gray {
+		.d-thead {
+			background-color: #aaa;
+			color: #fff;
+		}
+	}
+	&.border-under {
+		.d-td,
+		.d-th {
+			@extend .noBorder;
+		}
+		.d-tr {
+			@extend .border-bottom;
+		}
+		.d-table {
+			.d-tr {
+				@extend .noBorder;
+			}
+		}
+	}
+	&.large {
+		.d-td,
+		.d-th {
+			height: 80rpx;
+		}
+	}
+	&.middle {
+		.d-td,
+		.d-th {
+			height: 60rpx;
+		}
+	}
+	.d-td,
+	.d-th {
+		@extend .border;
+	}
+	.d-table {
+		height: 100%;
+		.d-td,
+		.d-th {
+			@extend .noBorder;
+		}
+		// .d-tr {
+		// 	&:first-child {
+		// 		.d-td{
+		// 			&.border-bottom {
+		// 				border-bottom: none;
+		// 			}
+		// 			&.border-top {
+		// 				border-top: none;
+		// 			}
+		// 		}
+		// 	}
+		// }
+	}
+	input.borderInput {
+		border: 1px solid #ccc;
+		border-radius: 6rpx;
+		width: 80%;
+		height: 80%;
+		display: inline-flex;
+	}
+}
+.d-caption {
+	display: table-caption;
+	background-color: inherit;
+}
+.d-thead {
+	display: table-header-group;
+	font-weight: 600;
+}
+.d-tbody {
+	display: table-row-group;
+}
+.d-tfoot {
+	display: table-footer-group;
+}
+.d-tr {
+	display: table-row;
+	width: 100%;
+}
+.d-td,
+.d-th {
+	display: table-cell;
+	vertical-align: middle;
+	height: 50rpx;
+	font-size: 28rpx;
+}
+.d-th {
+	font-weight: 600;
+}
+.border-top {
+	border-top: 1px solid #efefef !important;
+}
+.border-left {
+	border-left: 1px solid #efefef !important;
+}
+.border-bottom {
+	border-bottom: 1px solid #efefef !important;
+}
+.border-right {
+	border-right: 1px solid #efefef !important;
+}
+.border {
+	border: 1px solid #efefef;
+}
+.noBorder {
+	border: 0;
+}
+
+.flex {
+	display: flex;
+	align-items: center;
+	&-wrap {
+		flex-wrap: wrap;
+	}
+	&-nowrap {
+		flex-wrap: nowrap;
+	}
+	&-direction {
+		flex-direction: column;
+	}
+}

+ 250 - 0
pages/index/details.vue

@@ -0,0 +1,250 @@
+<template>
+	<view class="container">
+		<view class="imageBox">
+			<image :src="'https://www.bscnice.com'+info.image" ></image>
+		</view>
+		<view class="name">{{info.name}}</view>
+		<view class="info flex">
+			<view class="price">{{ $t("details.价格") }}:<text class="tip">{{info.price * 1}}NUSD</text></view>
+			<view class="price">{{ $t("details.总收益") }}:<text class="tip">{{info.send_all * 1}}NUSD</text></view>
+		</view>
+		<view class="partition"></view>
+		<view class="detailes" @click="navTo('/pages/index/detailsTpl')">
+			<view class="flex detaTpl">
+				<view class="flex_item tpl">
+					<view class="tip"></view>
+					<view class="nameTip">{{ $t("details.当前运作矿机") }}</view>
+				</view>
+				<view class="tpls">{{ $t("details.今日挖矿收益") }}</view>
+			</view>
+			<view class="flex detaTip">
+				<view class="text">{{info.name}}</view>
+				<view class="num">{{info.day_send * 1}}NUSD</view>
+			</view>
+			<view class="go"><u-icon name="arrow-right" size="14" color="#fff"></u-icon></view>
+		</view>
+		<view class="goBuy" :class="{ submitNo: !payOn }" @click="payOn ? buyOrder() : ''">{{ $t("details.立即购买") }}</view>
+	</view>
+</template>
+<script>
+	import { detail,order } from '@/api/index.js';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	export default {
+		data () {
+			return {
+				id:"",
+				info:'',
+				payOn:true
+			}
+		},
+		onLoad (option) {
+			this.id = option.id
+			uni.setNavigationBarTitle({
+				title: this.$t('details.矿机详情'),
+			});
+			this.loadDate()
+		},
+		onShow () {
+			
+		},
+		computed: {
+			...mapState({
+				langList: "langList",
+				lang: "lang",
+			}),
+		},
+		methods: {
+			buyOrder(){
+				let obj = this
+				obj.payOn = false
+				ethereum.request({
+					method: 'eth_requestAccounts'
+				}).then((account) => {
+					const PKR_ORDER = 'PKR_ORDER' + (new Date()).getTime();
+					ethereum.request({
+						"method": "personal_sign",
+						"params": [
+							PKR_ORDER,
+							account[0]
+						]
+					}).then((res) => {
+						obj.buyIndex(res,PKR_ORDER);
+					});
+				});
+			},
+			buyIndex(sign,msg){
+				let obj = this
+				order({
+					sign,
+					msg,
+					id:obj.id,
+				}).then(function(res){
+					if(res.code == 0){
+						obj.payOn = true
+						uni.showToast({
+							title: res.msg,
+							icon: "error",
+							duration:3000
+						});
+					}else{
+						obj.payOn = true
+						uni.showToast({
+							title:obj.$t('details.开通完成'),
+							icon: "success",
+						});
+						// setTimeout(function () {
+						// 	obj.$router.go(0)
+						// }, 1000);
+					}
+				}).catch(e => {
+					obj.payOn = true
+					console.log(e,11111);
+				});
+			},
+			loadDate(){
+				let obj = this
+				detail(obj.id).then(function(res){
+					if(res.code == 1){
+						obj.info = res.data.info
+					}else{
+						uni.showToast({
+							title: res.msg,
+							duration: 1500,
+							mask: false,
+							icon: 'none',
+						})
+					}
+				})
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+	}
+	.container {
+		width: 100%;
+		padding: 30rpx 30rpx;
+		color: #fff;
+		padding-bottom: 150rpx;
+	}
+	.imageBox{
+		width: 100%;
+		height: 690rpx;
+		background: url('../../static/img/img27.png') no-repeat;
+		background-size: 100% 100%;
+		text-align: center;
+		padding-top: 70rpx;
+		image{
+			width: 100%;
+			height: 100%;
+		}
+	}
+	.name{
+		font-weight: bold;
+		font-size: 36rpx;
+		color: #FFFFFF;
+		padding: 40rpx 0rpx;
+	}
+	.info{
+		margin-bottom: 45rpx;
+		.price{
+			font-weight: bold;
+			font-size: 28rpx;
+			.tip{
+				font-weight: bold;
+				font-size: 37rpx;
+				color: #E83F30;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+		}
+	}
+	.partition{
+		height: 15rpx;
+		background: #F8F6F6;
+		opacity: 0.11;
+		position: absolute;
+		width: 100%;
+		left: 0;
+	}
+	.detailes{
+		margin-top: 100rpx;
+		background: url('../../static/img/img26.png') no-repeat;
+		width: 100%;
+		height: 158rpx;
+		background-size: 100% 100%;
+		padding: 37rpx 26rpx;
+		padding-right: 70rpx;
+		position: relative;
+		.detaTpl{
+			.tpl{
+				.tip{
+					width: 8rpx;
+					height: 30rpx;
+					background: linear-gradient(80deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+					border-radius: 4rpx;
+				}
+				.nameTip{
+					font-weight: 500;
+					font-size: 23rpx;
+					color: #999999;
+					padding-left: 15rpx;
+				}
+			}
+			.tpls{
+				font-weight: 500;
+				font-size: 24rpx;
+				color: #999999;
+			}
+		}
+		.detaTip{
+			padding-top: 10rpx;
+			.text{
+				font-weight: bold;
+				font-size: 32rpx;
+				color: #999999;
+				line-height: 55rpx;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+			.num{
+				font-weight: bold;
+				font-size: 28rpx;
+				color: #FFFFFF;
+			}
+		}
+		.go{
+			position: absolute;
+			top: 43%;
+			right: 20rpx;
+		}
+	}
+	.goBuy{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		font-weight: 500;
+		font-size: 36rpx;
+		color: #31190B;
+		background: linear-gradient(258deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+		height: 98rpx;
+		line-height: 98rpx;
+		text-align: center;
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+	.submitNo {
+		background: #999999 !important;
+		color: #fff !important;
+	}
+</style>

+ 310 - 0
pages/index/detailsTpl.vue

@@ -0,0 +1,310 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">{{$t("detailsTpl.我的矿机")}}</view>
+			</view>
+			<view class="infoItem" v-for="item,index in myLists" :key="index" v-if="item.status == 1">
+				<view class="itemInfo">
+					<view class="itemName">{{$t("detailsTpl.当前矿机")}}</view>
+					<view class="flex infos">
+						<view class="infoTip flex_item">
+							<image :src="'https://www.bscnice.com'+item.machine.image" style="width: 130rpx;height:130rpx;border: 2rpx solid #8D7049;
+								border-radius: 25rpx;"></image>
+							<view class="nameBox">
+								<view class="nameTip">{{item.machine.name}}</view>
+								<view class="price">{{$t("details.价格")}}:<text class="priTip">{{item.machine.price * 1}}NUSD</text></view>
+							</view>
+						</view>
+						<view class="statusBox flex_item">
+							<view class="status">{{$t("detailsTpl.工作中")}}</view>
+							<view class="statusTip"></view>
+						</view>
+					</view>
+				</view>
+				<view class="flex numBox">
+					<view class="numTpl">
+						<view class="num">{{info.sent * 1}}</view>
+						<view class="name">{{$t("detailsTpl.已释放")}}</view>
+					</view>
+					<view class="partition"></view>
+					<view class="numTpl">
+						<view class="num">{{info.wait}}</view>
+						<view class="name">{{$t("detailsTpl.待释放")}}</view>
+					</view>
+				</view>
+			</view>
+			<view class="noDate" v-if="myLists.length == 0">{{$t("detailsTpl.暂无工作中的矿机列表")}}</view>
+			<view class="navList flex">
+				<view class="navItem" v-for="item,index in navList" :key="index" @click="clickTab(index)">
+					<view class="navName" :class="{ active: currTab == index }">{{item}}</view>
+					<image v-if="currTab == index" src="/static/img/img28.png" class="navTip"></image>
+				</view>
+			</view>
+			<view class="listBox" v-if="list.length > 0">
+				<view class="listTpl flex" v-for="item,index in list" :key="index">
+					<view class="tpl">
+						<view class="addr">{{currTab == 0?typeList[item.type]:item.machine.name}}</view>
+						<view class="time">{{item.createtime }}</view>
+					</view>
+					<view class="num add" v-if="currTab == 0">+{{item.money * 1}}</view>
+					<view class="num" v-if="currTab == 1">-{{item.machine.price * 1}}</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import { myList,getTokenLog} from '@/api/index.js';
+	import{ getTime } from '@/utils/rocessor.js';
+	export default {
+		components: {
+			topView
+		},
+		data () {
+			return {
+				myLists:[],//我的矿机列表
+				list:[],
+				navList:[this.$t("detailsTpl.释放记录"),this.$t("detailsTpl.购买记录")],
+				currTab:0,
+				info:'',
+				typeList:''
+			}
+		},
+		onShow () {
+			this.loadData();
+			this.getLog()
+		},
+		methods: {
+			clickTab(index){
+				this.currTab = index
+				this.list = []
+				if(index == 0){
+					this.getLog()
+				}else{
+					this.loadData()
+				}
+			},
+			loadData () {
+				let obj = this
+				myList({}).then(function(res){
+					res.data.list.forEach(item => {
+						item.createtime = getTime(item.createtime)
+					})
+					obj.myLists = res.data.list
+					obj.info = res.data
+					if(obj.currTab == 1){
+						obj.list = res.data.list
+					}
+				})
+			},
+			getLog(){
+				let obj = this
+				getTokenLog({
+					page:1,
+					limit:100000,
+					token:'NICE',
+					pm:'',
+					type:'day_mining'
+				}).then(function(res){
+					res.data.list.forEach(item => {
+						item.createtime = getTime(item.createtime)
+					})
+					obj.list = res.data.list
+					obj.typeList = res.data.type
+				})
+			},
+			change(currentPage,type) {
+				this.page.currentPage = currentPage;
+				this.loadData()
+			},
+			navTo(){
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+			padding-bottom: 25rpx;
+		}
+	}
+	.noDate{
+		text-align: center;
+		padding-top: 25rpx;
+		opacity: 0.3;
+	}
+	.infoBox{
+		padding: 0rpx 25rpx;
+		color: #fff;
+		.infoItem{
+			margin-bottom: 25rpx;
+			background: #1A1A17;
+			padding: 35rpx 0rpx;
+			border-radius: 20rpx;
+			.numBox{
+				padding-top: 25rpx;
+				.numTpl{
+					width: 45%;
+					text-align: center;
+					.name{
+						font-weight: 500;
+						font-size: 26rpx;
+						color: #FFFFFF;
+						padding-top: 10rpx;
+					}
+					.num{
+						font-weight: bold;
+						font-size: 34rpx;
+						color: #FFFFFF;
+					}
+				}
+				.partition{
+					width: 2rpx;
+					height: 119rpx;
+					background: linear-gradient(0deg, rgba(255,255,255,0), #FFFFFF, rgba(255,255,255,0));
+				}
+			}
+		}
+	}
+	.itemInfo{
+		padding: 0rpx 25rpx;
+		.itemName{
+			font-weight: bold;
+			font-size: 27rpx;
+			padding-bottom: 20rpx;
+		}
+		.infos{
+			border-bottom: 1rpx solid rgba(221, 225, 235, 0.2);
+			padding-bottom: 25rpx;
+			.infoTip{
+				align-items: flex-start;
+				.nameBox{
+					padding-left: 25rpx;
+					.nameTip{
+						font-weight: bold;
+						font-size: 32rpx;
+						color: #FFFFFF;
+					}
+					.price{
+						font-weight: 500;
+						font-size: 24rpx;
+						color: #999999;
+						padding-top: 30rpx;
+						.priTip{
+							font-weight: 500;
+							font-size: 26rpx;
+							color: #999999;
+							line-height: 44rpx;
+							background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+							-webkit-background-clip: text;
+							-webkit-text-fill-color: transparent;
+						}
+					}
+				}
+			}
+			.statusBox{
+				.status{
+					font-weight: bold;
+					font-size: 32rpx;
+					color: #999999;
+					background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+					-webkit-background-clip: text;
+					-webkit-text-fill-color: transparent;
+				}
+				.statusTip{
+					margin-left: 15rpx;
+					width: 15rpx;
+					height: 15rpx;
+					background: linear-gradient(137deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+					border-radius: 50%;
+				}
+			}
+			
+		}
+	}
+	.listBox{
+		margin-top: 25rpx;
+		background: #1A1A17;
+		padding: 0rpx 25rpx;
+		border: 4rpx solid #D7B381;
+		border-radius: 30rpx;
+		.listTpl{
+			padding: 30rpx 0rpx;
+			border-bottom: 1rpx solid rgba(240, 240, 240, 0.3);
+			.tpl{
+				.addr{
+					
+				}
+				.time{
+					font-weight: 500;
+					font-size: 22rpx;
+					padding-top: 15rpx;
+					color: #999999;
+				}
+			}
+			.num{
+				font-weight: 400;
+				font-size: 36rpx;
+				color: #fff;
+			}
+			.add{
+				color: #4C5D97;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+		}
+		.listTpl:last-child{
+			border-bottom: none !important;
+		}
+	}
+	.navList{
+		padding: 43rpx 120rpx;
+		.navItem{
+			width: 120rpx;
+			position: relative;
+			.navName{
+				font-weight: bold;
+				font-size: 29rpx;
+				color: #FFFFFF;
+			}
+			.active{
+				font-weight: bold;
+				font-size: 29rpx;
+				color: #FFFFFF;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+			.navTip{
+				position: absolute;
+				top: 45rpx;
+				left: 35rpx;
+				width: 45rpx;
+				height: 11rpx;
+			}
+		}
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+</style>

+ 238 - 0
pages/index/exchange.vue

@@ -0,0 +1,238 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo('/pages/index/index')">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">{{$t("exchange.闪兑")}}</view>
+			</view>
+			<view class="listBox">
+				<view class="name">{{$t("exchange.兑换")}}</view>
+				<view class="redion">
+					 <u-radio-group class="flex" v-model="value">
+					    <u-radio :customStyle="{marginBottom: '8px'}" v-for="(item, index) in currList"
+					      :key="index" :label="item" :name="item" activeColor="#C29963">
+					    </u-radio>
+					  </u-radio-group>
+				</view>
+				<view class="listTpl">
+					<view class="flex tplBox">
+						<view class="flex_item tpl">
+							<image src="/static/img/img23.png" style="width: 60rpx;height: 59rpx;"></image>
+							<view class="title">NICE</view>
+						</view>
+						<view class="money">{{$t("exchange.余额")}}:{{userInfo.NICE * 1}}</view>
+					</view>
+					<view class="flex inputBox">
+						<input type="number" v-model="money" @input="moneyInput" :placeholder='$t("exchange.请填写兑换的NICE数量")'/>
+						<view class="all">MAX</view>
+					</view>
+				</view>
+				<image class="reImg" src="../../static/img/img24.png" style="width: 85rpx;height: 86rpx;"></image>
+				<view class="listTpl">
+					<view class="flex tplBox">
+						<view class="flex_item tpl">
+							<image v-if="value == 'USDT'" src="/static/img/img25.png" style="width: 57rpx;height: 57rpx;"></image>
+							<image v-if="value == 'NUSD'" src="/static/img/img23.png" style="width: 57rpx;height: 57rpx;"></image>
+							<view class="title">{{value}}</view>
+						</view>
+						<view class="money" v-if="value == 'USDT'">{{$t("exchange.余额")}}:{{userInfo.USDT * 1}}</view>
+						<view class="money" v-if="value == 'NUSD'">{{$t("exchange.余额")}}:{{userInfo.NUSD * 1}}</view>
+					</view>
+					<view class="flex inputBox">
+						<input type="number" v-model="numUsdt" :placeholder='$t("exchange.实际到账的")+value' disabled/>
+						<view class="all">MAX</view>
+					</view>
+				</view>
+				<view class="tip">{{$t("exchange.兑换比率")}}:1NICE≈{{userInfo.nice_price}}{{value}}</view>
+				<view class="submitBtn" :class="{ submitNo: !payOn }" @click="payOn ? clickSubmit() : ''" >{{$t("exchange.立即兑换")}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	import {exchange} from "@/api/index.js";
+	export default {
+		components: {
+			topView
+		},
+		data() {
+			return {
+				money:'',
+				numUsdt:'',
+				payOn:true,
+				currList:['USDT','NUSD'],
+				value:'USDT',
+			}
+		},
+		onLoad () {},
+		onShow () {},
+		computed: {
+			...mapState('user', ['userInfo']),
+		},
+		methods: {
+			moneyInput(e){
+				this.numUsdt = (e.detail.value)*this.userInfo.nice_price
+			},
+			clickSubmit(){
+				let obj = this
+				if(obj.money == ''){
+					uni.showToast({
+						title: obj.$t('exchange.请填写兑换的NICE数量'),
+						icon: "none",
+					});
+					return;
+				}
+				if (obj.money > obj.userInfo.NICE) {
+					uni.showToast({
+						title: "NICE" + obj.$t('exchange.余额不足'),
+						icon: "none",
+					});
+					return;
+				}
+				obj.payOn = false
+				ethereum.request({
+					method: 'eth_requestAccounts'
+				}).then((account) => {
+					const PKR_EXCHANGE = 'PKR_EXCHANGE' + (new Date()).getTime();
+					ethereum.request({
+						"method": "personal_sign",
+						"params": [
+							PKR_EXCHANGE,
+							account[0]
+						]
+					}).then((res) => {
+						obj.exchange(res,PKR_EXCHANGE);
+					});
+				});
+			},
+			exchange(sign,msg){
+				let obj = this
+				exchange({
+					sign,
+					msg,
+					num:obj.money,
+					to_token:obj.value
+				}).then(function(res){
+					if(res.code == 0){
+						obj.payOn = true
+						uni.showToast({
+							title:res.msg,
+							icon: "error",
+						});
+					}else{
+						obj.payOn = true
+						uni.showToast({
+							title: obj.$t('exchange.兑换成功'),
+							icon: "success",
+						});
+						setTimeout(function () {
+							obj.$router.go(0)
+						}, 1000);
+					}
+				}).catch(e => {
+					obj.payOn = true
+				});
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.redion{
+		padding:25rpx 100rpx 25rpx 100rpx;
+	}
+	.infoBox{
+		padding: 0rpx 25rpx;
+	}
+	.listBox{
+		color: #fff;
+		border-radius: 30rpx;
+		border: 4rpx solid #DAB787;
+		padding: 42rpx 57rpx;
+		position: relative;
+		.name{
+			font-weight: bold;
+			font-size: 34rpx;
+			color: #FFFFFF;
+			padding-bottom: 30rpx;
+		}
+		.listTpl{
+			background: #2F2F2D;
+			padding: 55rpx 30rpx;
+			border-radius: 30rpx;
+			margin-bottom: 31rpx;
+			.tplBox{
+				.title{
+					font-size: 30rpx;
+					padding-left: 15rpx;
+				}
+				.money{
+					font-size: 30rpx;
+				}
+			}
+			.inputBox{
+				padding-top: 115rpx;
+				padding-left: 25rpx;
+				padding-bottom: 25rpx;
+				.all{
+					font-weight: 500;
+					font-size: 32rpx;
+					color: #1D69E2;
+					background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+					-webkit-background-clip: text;
+					-webkit-text-fill-color: transparent;
+				}
+			}
+		}
+		.reImg{
+			position: absolute;
+			left: 45%;
+			top:42%;
+		}
+	}
+	.tip{
+		font-size: 30rpx;
+		color: #FFFFFF;
+		padding: 40rpx 0;
+	}
+	.submitBtn{
+		background: linear-gradient(258deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+		border-radius: 10rpx;
+		font-weight: bold;
+		font-size: 32rpx;
+		color: #31190B;
+		line-height:90rpx;
+		text-align: center;
+		margin:25rpx 0;
+	}
+	.submitNo {
+		background: #999999 !important;
+		color: #fff !important;
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+</style>

+ 211 - 0
pages/index/income.vue

@@ -0,0 +1,211 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo('/pages/index/index')">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">{{$t("income.我的收益")}}</view>
+			</view>
+			<view class="infoItem">
+				<view class="itemInfo">
+					<view class="itemName">{{$t("income.收益统计")}}NICE</view>
+					<view class="infos">
+						<view class="infoName">{{$t("income.累计收益")}}</view>
+						<view class="infoNum">{{info.all_recommend + info.all_group+info.all_same+info.all_mining }}</view>
+					</view>
+				</view>
+				<view class="flex numBox">
+					<view class="numTpl">
+						<view class="num">{{info.all_extract}}</view>
+						<view class="name">{{$t("income.累计提现")}}</view>
+					</view>
+					<view class="partition"></view>
+					<view class="numTpl">
+						<view class="num">{{userInfo.NICE * 1}}</view>
+						<view class="name">{{$t("income.剩余可提现")}}</view>
+					</view>
+				</view>
+			</view>
+			<view class="listBox">
+				<view class="listTop flex">
+					<view class="tip">{{$t("income.收益明细")}}</view>
+					<view class="tip flex_item" @click="navTo('/pages/index/incomeTpl')">
+						<view style="font-size: 24rpx;">{{$t("income.明细记录")}}</view>
+						<view><u-icon name="arrow-right" size="12" color="#fff" style="margin-left: 5rpx;"></u-icon></view>
+					</view>
+				</view>
+				<view class="listTpl flex">
+					<view class="tpl">
+						<view class="addr">{{$t("income.直推奖")}}(NICE)</view>
+						<view class="time">{{info.all_recommend}}</view>
+					</view>
+				</view>
+				<view class="listTpl flex">
+					<view class="tpl">
+						<view class="addr">{{$t("income.业绩奖")}}(NICE)</view>
+						<view class="time">{{info.all_group}}</view>
+					</view>
+				</view>
+				<view class="listTpl flex">
+					<view class="tpl">
+						<view class="addr">{{$t("income.平级奖")}}(NICE)</view>
+						<view class="time">{{info.all_same}}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import { moneyInfo } from '@/api/index.js';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	export default {
+		components: {
+			topView
+		},
+		data () {
+			return {
+				info:''
+			}
+		},
+		onLoad () {
+			this.loadDate()
+		},
+		computed: {
+			...mapState('user', ['userInfo']),
+		},
+		methods: {
+			loadDate () {
+				let obj = this;
+				moneyInfo('NICE').then((res) => {
+					obj.info = res.data
+				})
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.infoBox{
+		padding: 0rpx 25rpx;
+		color: #fff;
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+	.infoItem{
+		background: #1A1A17;
+		padding: 35rpx 0rpx;
+		border-radius: 20rpx;
+		.numBox{
+			padding-top: 25rpx;
+			.numTpl{
+				width: 45%;
+				text-align: center;
+				.name{
+					font-weight: 500;
+					font-size: 26rpx;
+					color: #FFFFFF;
+					padding-top: 10rpx;
+				}
+				.num{
+					font-weight: bold;
+					font-size: 34rpx;
+					color: #FFFFFF;
+				}
+			}
+			.partition{
+				width: 2rpx;
+				height: 119rpx;
+				background: linear-gradient(0deg, rgba(255,255,255,0), #FFFFFF, rgba(255,255,255,0));
+			}
+		}
+	}
+	.itemInfo{
+		padding: 0rpx 25rpx;
+		.itemName{
+			font-weight: bold;
+			font-size: 27rpx;
+			padding-bottom: 20rpx;
+		}
+		.infos{
+			text-align: center;
+			border-bottom:1rpx solid rgba(221, 225, 235, 0.2);
+			padding: 25rpx 0rpx;
+			padding-bottom: 58rpx;
+			.infoName{
+				font-weight: bold;
+				font-size: 30rpx;
+				color: #FFFFFF;
+				line-height: 24rpx;
+			}
+			.infoNum{
+				padding-top: 20rpx;
+				font-weight: bold;
+				font-size: 34rpx;
+				color: #7760EE;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+		}
+	}
+	.listBox{
+		margin-top: 25rpx;
+		background: #1A1A17;
+		padding: 0rpx 25rpx;
+		border-radius: 30rpx;
+		.listTop{
+			padding: 35rpx 0rpx;
+			border-bottom: 1rpx solid rgba(83, 72, 72, 0.3);
+		}
+		.listTpl{
+			padding: 30rpx 0rpx;
+			border-bottom: 1rpx solid rgba(233, 233, 233, 0.3);
+			.tpl{
+				.addr{
+					font-weight: bold;
+					font-size: 24rpx;
+					color: #999999;
+				}
+				.time{
+					font-weight: 500;
+					font-size: 26rpx;
+					padding-top: 15rpx;
+				}
+			}
+			.num{
+				font-weight: bold;
+				font-size: 34rpx;
+				color: #7760EE;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+		}
+		.listTpl:last-child{
+			border-bottom: none !important;
+		}
+	}
+</style>

+ 241 - 0
pages/index/incomeTpl.vue

@@ -0,0 +1,241 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">{{$t("incomeTpl.收支明细")}}</view>
+			</view>
+			<view class="navBox">
+				<view class="navList flex_item">
+					<view class="navItem" v-for="item,index in navList" :key="index" @click="clickTab(index)">
+						<view class="navName" :class="{ active: currTab == index }">{{item}}</view>
+						<image v-if="currTab == index" src="/static/img/img28.png" class="navTip"></image>
+					</view>
+				</view>
+				<!-- <view class="moneyDay flex_item" @click="clcikDate">
+					<view class="dayValue"><text>{{monthValue}}</text>月</view>
+					<u-icon v-if="up" name="arrow-up-fill" color="#fff" size="14" style="position: relative;top: 10rpx;"></u-icon>
+					<u-icon v-if="!up" name="arrow-down-fill" color="#fff" size="14" style="position: relative;top: 10rpx;"></u-icon>
+				</view> -->
+			</view>
+			<view class="listBox">
+				<view class="flex tipBox">
+					<view class="tipText"><!-- 2024年05月 --></view>
+					<view class="tipText">{{currTab==0?$t("incomeTpl.流水"):currTab==1?$t("incomeTpl.收入"):$t("incomeTpl.支出")}}:{{info.sum}}NICE<!-- <text style="padding-left: 15rpx;">支出:2000</text> --></view>
+				</view>
+				<view class="listTpl flex" v-for="item,index in list" :key="index">
+					<view class="tpl">
+						<view class="name">{{typeList[item.type]}}</view>
+						<view class="time">{{item.createtime }}</view>
+					</view>
+					<view class="nomBox">
+						<view class="num add" v-if="item.pm == 1">+{{item.money * 1}}</view>
+						<view class="num" v-else>-{{item.money * 1}}</view>
+						<view class="numText">{{$t("incomeTpl.余额")}}:{{item.after * 1}}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<u-datetime-picker :show="dateShow" v-model="dayValue" mode="year-month" @confirm="confirm"></u-datetime-picker>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import {	getUserInfo } from '@/api/login.js';
+	import {getTokenLog} from "@/api/index.js";
+	import{ getTime } from '@/utils/rocessor.js';
+	export default {
+		components: {
+			topView
+		},
+		data () {
+			return {
+				list:[],
+				navList:[this.$t("incomeTpl.全部"),this.$t("incomeTpl.收入"),this.$t("incomeTpl.支出")],
+				currTab:0,
+				up:false,
+				dateShow:false,
+				monthValue:'',
+				yearValue:'',
+				dayValue:Number(new Date()),
+				info:''
+			}
+		},
+		onShow () {
+			const date = new Date();
+			this.yearValue = date.getFullYear();
+			this.monthValue = date.getMonth() + 1;
+			this.loadData();
+		},
+		methods: {
+			clcikDate(){
+				this.up = !this.up
+				if(this.up){
+					this.dateShow = true
+				}
+			},
+			confirm(e){
+				let data = e.value
+				let dateValue = new Date(data); // 将时间戳转换为Date对象
+				this.yearValue = dateValue.getFullYear(); // 获取年份
+				this.monthValue = dateValue.getMonth() + 1; // 获取月份,需要加1因为从0开始计数
+				//return year + '-' + month.toString().padStart(2, '0'); // 返回格式化的年-月字符串
+				console.log(this.yearValue,this.monthValue,'888')
+				this.dateShow = false
+				this.up = false
+			},
+			clickTab(index){
+				this.currTab = index
+				this.loadData()
+			},
+			loadData () {
+				let obj = this;
+				let pm = ''
+				if(obj.currTab == 0){
+					pm = ''
+				}else if(obj.currTab == 1){
+					pm = 1
+				}else{
+					pm = 0
+				}
+				getTokenLog({
+					page:1,
+					limit:100000,
+					token:'NICE',
+					pm:pm
+				}).then(function(res){
+					obj.info = res.data
+					res.data.list.forEach(item => {
+						item.createtime = getTime(item.createtime)
+					})
+					obj.list = res.data.list
+					obj.typeList = res.data.type
+				});
+			},
+			// change(currentPage,type) {
+			// 	this.page.currentPage = currentPage;
+			// 	this.loadData()
+			// },
+			navTo(){
+				uni.navigateTo({
+					url:'/pages/index/income'
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+			padding-bottom: 25rpx;
+		}
+	}
+	.infoBox{
+		padding: 0rpx 25rpx;
+		color: #fff;
+	}
+	.listBox{
+		margin-top: 25rpx;
+		background: #1A1A17;
+		padding: 0rpx 25rpx;
+		border-radius: 30rpx;
+		.tipBox{
+			.tipText{
+				font-weight: 500;
+				font-size: 22rpx;
+				padding-top: 36rpx;
+				padding-bottom: 20rpx;
+				color: #999999;
+			}
+		}
+		.listTpl{
+			padding: 30rpx 0rpx;
+			// border-bottom: 1rpx solid rgba(240, 240, 240, 0.3);
+			.tpl{
+				.addr{
+					
+				}
+				.time{
+					font-weight: 500;
+					font-size: 22rpx;
+					padding-top: 15rpx;
+					color: #999999;
+				}
+			}
+			.nomBox{
+				text-align: right;
+				.num{
+					font-weight: 400;
+					font-size: 36rpx;
+					color: #fff;
+				}
+				.add{
+					color: #4C5D97;
+					background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+					-webkit-background-clip: text;
+					-webkit-text-fill-color: transparent;
+				}
+				.numText{
+					font-weight: 500;
+					font-size: 22rpx;
+					padding-top: 15rpx;
+					color: #999999;
+				}
+			}
+		}
+		.listTpl:last-child{
+			border-bottom: none !important;
+		}
+	}
+	.navList{
+		padding: 15rpx 0rpx 40rpx 25rpx;
+		.navItem{
+			width: 150rpx;
+			position: relative;
+			text-align: center;
+			.navName{
+				font-weight: bold;
+				font-size: 32rpx;
+				color: #FFFFFF;
+			}
+			.active{
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+			.navTip{
+				position: absolute;
+				top: 45rpx;
+				left: 50rpx;
+				width: 45rpx;
+				height: 11rpx;
+			}
+		}
+	}
+	.dayValue{
+		font-family: PingFang SC;
+		font-weight: bold;
+		font-size: 31rpx;
+		color: #fff;
+		margin-right: 15rpx;
+		text{
+			font-size: 54rpx;
+		}
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+</style>

+ 413 - 0
pages/index/index.vue

@@ -0,0 +1,413 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#15130E'"></topView>
+		<view class="imageBox">
+			<image style="width: 100%;height: 450rpx;" src="/static/img/img02.png"></image>
+			<view class="flex_item titleBox">
+				<image src="../../static/img/img04.png" style="width: 41rpx;height: 23rpx;"></image>
+				<view class="title">{{ $t("index.矿机购买") }}</view>
+				<image src="../../static/img/img04.png" style="width: 41rpx;height: 23rpx;"></image>
+			</view>
+			<view class="tip">{{ $t("index.购买更高") }}</view>
+		</view>
+		<view class="infoBox">
+			<view class="flex reg" @click="navTo('/pages/index/exchange')">
+				<view class="flex_item">
+					<image src="../../static/img/img08.png" style="width: 86rpx;height: 100rpx;"></image>
+					<view class="name">{{ $t("index.闪兑") }}</view>
+				</view>
+				<view class="flex_item">
+					<view class="goText">{{ $t("index.立即前往") }}</view>
+					<view><u-icon name="arrow-right" size="16" style="position: relative;top: 3rpx;"></u-icon></view>
+				</view>
+			</view>
+			<view class="listBox">
+				<view class="itemTpl flex" v-for="item,index in list" 
+				:key="index" @click="navTo('/pages/index/details?id='+item.id)">
+					<view class="flex_item infoTpl">
+						<image style="width: 191rpx;height: 191rpx;border: 1rpx solid #9A7B52;border-radius: 25rpx;" 
+							:src="'https://www.bscnice.com'+item.image"></image>
+						<view class="info">
+							<view class="name">{{item.name}}</view>
+							<view class="tip">{{ $t("index.价格") }}:<text class="price">{{item.price * 1}}NUSD</text></view>
+							<view class="tip">{{ $t("index.总收益") }}:<text class="price">{{item.send_all * 1}}NUSD</text></view>
+						</view>
+					</view>
+					<view class="flex_item">
+						<view class="goText">{{ $t("index.查看详情") }}</view>
+						<view><u-icon name="arrow-right" size="14" style="position: relative;top: 3rpx;"></u-icon></view>
+					</view>
+				</view>
+			</view>
+			<view class="flex reg" @click="navTo('/pages/index/detailsTpl')">
+				<view class="flex_item">
+					<image src="../../static/img/img09.png" style="width: 96rpx;height:79rpx;"></image>
+					<view class="name">{{ $t("index.我的矿机") }}</view>
+				</view>
+				<view class="flex_item">
+					<view class="goText">{{ $t("index.查看详情") }}</view>
+					<view><u-icon name="arrow-right" size="16" style="position: relative;top: 3rpx;"></u-icon></view>
+				</view>
+			</view>
+			<view class="flex navList">
+				<view class="navItme" @click="navTo('/pages/index/invite')">
+					<image src="/static/img/img10.png" style="width: 103rpx;height: 97rpx;"></image>
+					<view class="navName">{{ $t("index.我的邀请") }}</view>
+					<view class="navText">{{ $t("index.邀请团队信息") }}</view>
+				</view>
+				<view class="navItme" @click="navTo('/pages/index/property')">
+					<image src="/static/img/img11.png" style="width: 95rpx;height: 110rpx;"></image>
+					<view class="navName">{{ $t("index.我的资产") }}</view>
+					<view class="navText">{{ $t("index.资产明细") }}</view>
+				</view>
+				<view class="navItme" @click="navTo('/pages/index/income')">
+					<image src="/static/img/img12.png" style="width: 146rpx;height:106rpx;"></image>
+					<view class="navName">{{ $t("index.我的收益") }}</view>
+					<view class="navText">{{ $t("index.收益明细") }}</view>
+				</view>
+			</view>
+			<view class="shareBox">
+				<view class="shareTitle">{{ $t("index.邀请地址") }}</view>
+				<view class="flex shareTpl">
+					<view class="addr clamp">{{userInfo.address}}</view>
+					<view class="copyBtn" @click="copyClick(userInfo.address)">{{ $t("index.复制") }}</view>
+				</view>
+			</view>
+			<view style="text-align: center;padding-top: 85rpx;">
+				<image src="/static/img/img13.png" style="width: 617rpx;height:382rpx;"></image>
+			</view>
+		</view>
+		<image src="/static/img/img03.png" style="width: 100%;height: 320rpx;"></image>
+		<u-popup :show="popShow" mode="center" class="popBox" @close="popShow = false" @open="popShow = true">
+			<view class="imgBox">
+				<image src="../../static/img/img29.png" style="width:492rpx;height: 458rpx;"></image>
+			</view>
+			<view class="inputBox">
+				<input type="text" v-model="address"/>
+			</view>
+			<view class="submitBnt" :class="{ submitNo: !payOn }" @click="payOn ? submit() : ''">{{$t("index.确认绑定")}}</view>
+		</u-popup>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import { mining } from '@/api/index.js';
+	import {bandgingSpead} from '@/api/login.js';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	export default {
+		components: {
+			topView
+		},
+		data () {
+			return {
+				list:[],
+				popShow:false,
+				address:'',
+				payOn:true
+			}
+		},
+		onLoad(option) {
+			if(option.address){
+				uni.setStorageSync('address', option.address);
+			}else{
+				uni.setStorageSync('address', '');
+			}
+		},
+		onShow() {
+			this.loadDate()
+		},
+		computed: {
+			...mapState('user', ['userInfo']),
+		},
+		methods: {
+			submit () {
+				let obj = this;
+				obj.payOn = false
+				ethereum.request({
+					method: 'eth_requestAccounts'
+				}).then((account) => {
+					const PKR_BIND = 'PKR_BIND' + (new Date()).getTime();
+					ethereum.request({
+						"method": "personal_sign",
+						"params": [
+							PKR_BIND,
+							account[0]
+						]
+					}).then((res) => {
+						obj.bing(res,PKR_BIND);
+					});
+				});
+			},
+			bing(sign,msg){
+				let obj = this;
+				bandgingSpead({
+					address: obj.address,
+					sign,
+					msg,
+				}).then((res) => {
+					obj.popShow = false
+					if(res.code == 1){
+						obj.payOn = true
+						uni.setStorageSync('address', '');
+						uni.showToast({
+							title: obj.$t('common.绑定成功'),
+							duration: 1500,
+							mask: false,
+							icon: 'none',
+						})
+						obj.loadDate()
+					}else{
+						obj.payOn = true
+						uni.showToast({
+							title: res.msg,
+							duration: 1500,
+							mask: false,
+							icon: 'none',
+						})
+					}
+				}).catch(e => {
+					obj.payOn = true
+				});
+			},
+			loadDate(){
+				let obj = this
+				mining({}).then(function(res){
+					if (res.code == 402) {
+						uni.showModal({
+							title: obj.$t('common.温馨提示'),
+							content: obj.$t('common.请先绑定推荐人'),
+							showCancel: false,//没有取消按钮的弹框
+							buttonText:obj.$t('common.确定'),
+							success: function (res) {
+								if (res.confirm) {
+									obj.address = uni.getStorageSync("address") || "";
+									obj.popShow = true
+								} else if (res.cancel) {
+									
+								}
+							}
+						});
+					}else {
+						obj.list = res.data.list
+						
+					}
+				})
+			},
+			copyClick (value) {
+				if (value == "" || value == null) {
+					uni.showToast({
+						title: this.$t('common.内容为空'),
+						icon: "none",
+					});
+					return;
+				}
+				let text = 'https://www.bscnice.com/index/#/pages/index/index?address='+value
+				const result = this.uniCopy(text);
+				if (result === false) {
+					uni.showToast({
+						title: this.$t("common.不支持"),
+					});
+				} else {
+					uni.showToast({
+						title: this.$t("common.复制成功"),
+						icon: "none",
+					});
+				}
+			},
+			uniCopy (content) {
+				/**
+				 * 小程序端 和 app端的复制逻辑
+				 */
+				//#ifndef H5
+				uni.setClipboardData({
+					data: content,
+					success: function () {
+						console.log("success");
+						return true;
+					},
+				});
+				//#endif
+			
+				/**
+				 * H5端的复制逻辑
+				 */
+				// #ifdef H5
+				if (!document.queryCommandSupported("copy")) {
+					//为了兼容有些浏览器 queryCommandSupported 的判断
+					// 不支持
+					return false;
+				}
+				let textarea = document.createElement("textarea");
+				textarea.value = content;
+				textarea.readOnly = "readOnly";
+				document.body.appendChild(textarea);
+				textarea.select(); // 选择对象
+				textarea.setSelectionRange(0, content.length); //核心
+				let result = document.execCommand("copy"); // 执行浏览器复制命令
+				textarea.remove();
+				return result;
+				// #endif
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.imageBox{
+		.titleBox{
+			justify-content: center;
+			.title{
+				font-weight: bold;
+				font-size: 36rpx;
+				color: #FFFFFF;
+				padding: 0rpx 25rpx;
+			}
+		}
+		.tip{
+			text-align: center;
+			color: #999999;
+			font-size: 22rpx;
+			padding: 18rpx;
+		}
+	}
+	.infoBox{
+		padding: 25rpx 30rpx;
+		color: #FFFFFF;
+		.reg{
+			background: #1A1A17;
+			padding: 23rpx 55rpx;
+			box-shadow: 1rpx 1rpx 1rpx 1rpx rgba(0,0,0,0.24);
+			border-radius: 30rpx;
+			.name{
+				padding-left: 25rpx;
+				font-weight: bold;
+				font-size: 35rpx;
+			}
+		}
+	}
+	.listBox{
+		padding-top: 30rpx;
+		.itemTpl{
+			margin-bottom: 25rpx;
+			background: #1A1A17;
+			padding: 25rpx 25rpx;
+			box-shadow: 1rpx 1rpx 1rpx 1rpx rgba(0,0,0,0.24);
+			border-radius: 30rpx;
+			.infoTpl{
+				align-items: flex-start;
+				.info{
+					padding-left: 20rpx;
+					.name{
+						font-weight: bold;
+						font-size: 36rpx;
+						color: #FFFFFF;
+						padding-bottom: 45rpx;
+					}
+					.tip{
+						font-size: 24rpx;
+						color: #999999;
+						padding-top: 15rpx;
+						.price{
+							color:#E3C49C;
+						}
+					}
+				}
+			}
+		}
+	}
+	.navList{
+		margin:25rpx 0;
+		.navItme{
+			background: #1A1A17;
+			padding: 25rpx 0rpx;
+			padding-bottom: 40rpx;
+			box-shadow: 1rpx 1rpx 1rpx 1rpx rgba(0,0,0,0.24);
+			border-radius: 30rpx;
+			text-align: center;
+			width: 31%;
+			.navName{
+				padding-top: 10rpx;
+				font-weight: bold;
+				font-size: 33rpx;
+			}
+			.navText{
+				padding-top: 10rpx;
+				opacity: 0.18;
+				font-size: 24rpx;
+			}
+		}
+	}
+	.shareBox{
+		.shareTitle{
+			font-weight: bold;
+			font-size: 29rpx;
+			color: #FFFFFF;
+			margin-bottom: 30rpx;
+		}
+		.shareTpl{
+			background: #1A1A17;
+			border-radius: 25rpx;
+			.addr{
+				padding: 18rpx 25rpx;
+				width: 80%;
+			}
+			.copyBtn{
+				padding: 18rpx 35rpx;
+				background: linear-gradient(258deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+				border-radius: 30rpx;
+				font-weight: bold;
+				font-size: 30rpx;
+				color: #31190B;
+			}
+		}
+	}
+	//绑定弹窗
+	/deep/.u-popup__content{
+		background: #1A1A17;
+		border-radius: 25rpx;
+		padding: 25rpx 25rpx;
+		width: 90%;
+	}
+	.imgBox{
+		text-align: center;
+	}
+	.inputBox{
+		background: #fff;
+		border-radius: 50rpx;
+		padding: 25rpx 25rpx;
+		width: 100%;
+	}
+	.submitBnt{
+		background: linear-gradient(258deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+		border-radius: 43rpx;
+		font-weight: bold;
+		font-size: 34rpx;
+		line-height: 90rpx;
+		text-align: center;
+		margin: 60rpx 0;
+	}
+	.submitNo {
+		background: #999999 !important;
+		color: #fff !important;
+	}
+	//
+	.goText{
+		font-size: 22rpx;
+		color: #FFFFFF;
+		opacity: 0.7;
+		padding-right: 15rpx;
+	}
+</style>

+ 185 - 0
pages/index/invite.vue

@@ -0,0 +1,185 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo('/pages/index/index')">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">我的邀请</view>
+			</view>
+			<view class="flex numBox">
+				<view class="numTpl">
+					<view class="name">直推人数</view>
+					<view class="num">{{info.count}}</view>
+				</view>
+				<view class="partition"></view>
+				<view class="numTpl">
+					<view class="name">团队业绩</view>
+					<view class="num">{{userInfo.achievement == null ? '0' : userInfo.achievement}}</view>
+				</view>
+			</view>
+			<view class="listBox" v-if="list.length > 0">
+				<view class="listTop flex">
+					<view class="tip">链接地址</view>
+					<view class="tip">收益</view>
+				</view>
+				<view class="listTpl flex" v-if="list.length > 0" v-for="item,index in list" :key="index">
+					<view class="tpl">
+						<view class="addr">{{item.address}}</view>
+						<view class="time">{{item.spread_time}}</view>
+					</view>
+					<!-- <view class="num">231.2361</view> -->
+				</view>
+			</view>
+			<view class="noDate" v-else>暂无数据</view>
+		</view>
+		<pagination v-if="list.length > 0" :total="page.total" :btnText="true"
+		:forceEllipses="true" @change="change"></pagination>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import pagination from '@/components/page-pagination/components/page-pagination/page-pagination.vue';
+	import { spreadList } from '@/api/index.js';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	import{ getTime } from '@/utils/rocessor.js';
+	export default {
+		components: {
+			pagination,
+			topView
+		},
+		data () {
+			return {
+				list:[],
+				info:'',
+				page: {
+					total: 100,
+					pageSize: 8,
+					currentPage: 1
+				}
+			}
+		},
+		onShow () {
+			this.loadData();
+		},
+		computed: {
+			...mapState('user', ['userInfo']),
+		},
+		methods: {
+			loadData () {
+				let obj = this;
+				spreadList({
+					page: obj.page.currentPage,
+					limit: obj.page.pageSize,
+				}).then(({ data }) => {
+					obj.page.total = data.data.count
+					obj.info = data.data
+					data.data.list.forEach(item => {
+						item.spread_time = getTime(item.spread_time)
+					})
+					obj.list = data.data.list
+				});
+			},
+			change(currentPage,type) {
+				this.page.currentPage = currentPage;
+				this.loadData()
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.infoBox{
+		padding: 0rpx 25rpx;
+		color: #fff;
+		.numBox{
+			background: #1A1A17;
+			padding: 35rpx 0rpx;
+			.numTpl{
+				width: 30%;
+				text-align: center;
+				.name{
+					font-weight: bold;
+					font-size: 30rpx;
+					color: #FFFFFF;
+				}
+				.num{
+					font-weight: bold;
+					font-size: 42rpx;
+					color: #7760EE;
+					margin-top: 15rpx;
+					background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+					-webkit-background-clip: text;
+					-webkit-text-fill-color: transparent;
+				}
+			}
+			.partition{
+				width: 2rpx;
+				height: 119rpx;
+				background: linear-gradient(0deg, rgba(255,255,255,0), #FFFFFF, rgba(255,255,255,0));
+			}
+		}
+	}
+	.listBox{
+		margin-top: 25rpx;
+		background: #1A1A17;
+		padding: 0rpx 25rpx;
+		.listTop{
+			padding: 25rpx 0rpx;
+			border-bottom: 1rpx solid rgba(83, 72, 72, 0.3);
+		}
+		.listTpl{
+			padding: 25rpx 0rpx;
+			border-bottom: 1rpx solid rgba(83, 72, 72, 0.3);
+			.tpl{
+				.addr{
+					
+				}
+				.time{
+					font-weight: 500;
+					font-size: 20rpx;
+					color: #FFFFFF;
+					opacity: 0.5;
+					padding-top: 15rpx;
+				}
+			}
+			.num{
+				font-size: 30rpx;
+				color: #7760EE;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+		}
+		.listTpl:last-child{
+			border-bottom: none !important;
+		}
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+	.noDate{
+		text-align: center;
+		opacity: 0.3;
+		padding-top: 50rpx;
+	}
+</style>

+ 114 - 0
pages/index/new_file.vue

@@ -0,0 +1,114 @@
+<template>
+	<view class="container">
+		<view class="flex topBox">
+			<view class="flex_item">
+				<image src="/static/img/img01.png" style="width: 82rpx;height: 82rpx;"></image>
+				<view style="color: #fff;font-size: 36rpx;padding-left: 15rpx;">NICE</view>
+			</view>
+			<view class="flex_item topTpl">
+				<view class="addr" v-if="userInfo.addresss">{{userInfo.addresss}}</view>
+				<view class="flex_item tplInfo">
+					<image src="/static/img/img14.png" style="width: 32rpx;height: 32rpx;margin: 0rpx 15rpx;"></image>
+					<picker :range="langList" range-key='label' @change="selectLang">
+						<view>{{langLabel}}</view>
+					</picker>
+				</view>
+			</view>
+		</view>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo('/pages/index/index')">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">我的邀请</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		getUserInfo
+	} from '@/api/login.js';
+	import {
+		mapState,
+		mapActions,
+		mapMutations
+	} from "vuex";
+	export default {
+		data () {
+			return {}
+		},
+		onLoad () {},
+		onShow () {
+			
+		},
+		computed: {
+			...mapState({
+				langList: "langList",
+				lang: "lang",
+			}),
+			...mapState('user', ['userInfo']),
+			langLabel () {
+				const langLabel = this.langList.find((item) => {
+					return item.value == this.lang
+				}).label;
+				return langLabel
+			}
+
+		},
+		methods: {
+			...mapMutations('user', ['setUserInfo']),
+			...mapActions({
+				setLang: "setLang",
+			}),
+			//切换语言
+			selectLang (value) {
+				this.setLang(this.langList[value.detail.value].value);
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.topBox {
+		color: #FFE0BD;
+		padding: 25rpx 27rpx;
+		background: #1A1A17;
+		.topTpl {
+			.addr {
+				padding: 6rpx 25rpx;
+				background: linear-gradient(-74deg, #CE9C6D, #FFECD6);
+				box-shadow: 3rpx 4rpx 5rpx 0rpx rgba(151, 118, 74, 0.5);
+				border-radius: 21rpx;
+				font-size: 24rpx;
+				font-family: PingFang SC;
+				font-weight: 500;
+				color: #986629;
+			}
+		}
+	}
+	.infoBox{
+		padding: 0rpx 25rpx;
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 47rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+</style>

+ 150 - 0
pages/index/propDetails.vue

@@ -0,0 +1,150 @@
+<template>
+	<view class="container">
+		<view class="topImage">
+			<image :src="type==1?'/static/img/img15.png':'/static/img/img16.png'" style="width: 119rpx;height: 119rpx;"></image>
+		   <view class="money">{{type==1?userInfo.USDT * 1:type==2?userInfo.NUSD*1 + userInfo.NUSD_OLY*1:userInfo.NICE * 1}}</view>
+		</view>
+		<view class="infoBox">
+			<view class="title">{{$t("propDetails.交易记录")}}</view>
+			<view class="listBox" v-if="list.length > 0">
+				<view class="listTpl flex" v-for="item,index in list" :key="index">
+					<view class="tplInfo flex_item">
+						<image src="/static/img/img20.png" style="width: 39rpx;height: 43rpx;"></image>
+						<view class="name">{{typeList[item.type]}}</view>
+						<view class="time">{{item.createtime }}</view>
+					</view>
+					<view class="num add" v-if="item.pm == 1">+{{item.money * 1}}</view>
+					<view class="num" v-else>-{{item.money * 1}}</view>
+				</view>
+			</view>
+			<view class="noDate" v-else>
+				<image src="../../static/img/noDate.png" style="width:500rpx;height: 500rpx;" mode="aspectFit"></image>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {getTokenLog} from "@/api/index.js";
+	import {getUserInfo} from '@/api/login.js';
+	import{ getTime } from '@/utils/rocessor.js';
+	export default {
+		data () {
+			return {
+				type:1,
+				title:'',
+				list:[],
+				typeList:'',
+				userInfo:''
+			}
+		},
+		onLoad (option) {
+			this.type = option.type;
+			if (this.type == 1) {
+				this.title = 'USDT'
+				uni.setNavigationBarTitle({
+					title: "USDT",
+				});
+			}else if(this.type == 2){
+				this.title = 'NUSD'
+				uni.setNavigationBarTitle({
+					title: "NUSD",
+				});
+			}else if(this.type == 3){
+				this.title = 'NICE'
+				uni.setNavigationBarTitle({
+					title: "NICE",
+				});
+			}else{
+				this.title = 'NUSD_OLY'
+				uni.setNavigationBarTitle({
+					title: "XNUSD",
+				});
+			}
+			this.getUserInfo()
+			this.loadDate()
+		},
+		methods: {
+			getUserInfo(){
+				let obj = this
+				getUserInfo({}).then(({data}) => {
+					obj.userInfo = data
+				}).catch(e => {
+					console.log(e.msg);
+				});
+			},
+			loadDate(){
+				let obj = this
+				getTokenLog({
+					page:1,
+					limit:100000,
+					token:obj.title,
+					pm:''
+				}).then(function(res){
+					res.data.list.forEach(item => {
+						item.createtime = getTime(item.createtime)
+					})
+					obj.list = res.data.list
+					obj.typeList = res.data.type
+				})
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		.container {
+			width: 100%;
+			padding-top: 50rpx;
+		}
+	}
+	.topImage{
+		text-align: center;
+		padding-bottom: 50rpx;
+		.money{
+			padding-top: 25rpx;
+			color: #fff;
+			font-size: 32rpx;
+		}
+	}
+	.noDate{
+		text-align: center;
+		padding-top: 25rpx;
+	}
+	.infoBox{
+		padding: 0rpx 30rpx;
+		.title{
+			font-size: 30rpx;
+			color: #FFFFFF;
+		}
+		.listBox{
+			color: #fff;
+			.listTpl{
+				background: #1A1A17;
+				margin-top: 30rpx;
+				padding: 25rpx 25rpx;
+				.tplInfo{
+					.name{
+						padding: 0rpx 15rpx;
+					}
+					.time{
+						font-size: 24rpx;
+						color: #999999;
+					}
+				}
+				.num{
+					font-weight: 400;
+					font-size: 36rpx;
+					color: #fff;
+				}
+				.add{
+					color: #4C5D97;
+					background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+					-webkit-background-clip: text;
+					-webkit-text-fill-color: transparent;
+				}
+			}
+		}
+	}
+</style>

+ 210 - 0
pages/index/property.vue

@@ -0,0 +1,210 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo('/pages/index/index')">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">{{$t("property.我的资产")}}</view>
+			</view>
+			<view class="moneyBox">
+				<view class="moneyTpl">
+					<view class="moneyName">{{$t("property.我的资产")}}</view>
+					<view class="moneyText">≈<text class="money">{{userInfo.usdt * 1}}U</text></view>
+				</view>
+				<view class="noneyNav flex">
+					<view class="moneyTip flex_item" @click="navTo('/pages/index/withdrawal')">
+						<image src="/static/img/img18.png" style="width: 30rpx;height: 29rpx;"></image>
+						<view class="moneyTitle">{{$t("property.提现")}}</view>
+					</view>
+					<view class="partition"></view>
+					<view class="moneyTip flex_item" @click="navTo('/pages/index/transfer')">
+						<image src="/static/img/img19.png" style="width: 31rpx;height: 30rpx;"></image>
+						<view class="moneyTitle">{{$t("property.转账")}}</view>
+					</view>
+					<view class="partition"></view>
+					<view class="moneyTip flex_item" @click="navTo('/pages/index/recharge')">
+						<image src="/static/img/img19.png" style="width: 31rpx;height: 30rpx;"></image>
+						<view class="moneyTitle">{{$t("property.充值")}}</view>
+					</view>
+				</view>
+			</view>
+			<view class="titleProp">{{$t("property.我的资产")}}</view>
+			<view class="propBox flex" @click="navTo('/pages/index/propDetails?type=1')">
+				<view class="flex_item propTpl">
+					<image src="../../static/img/img15.png" style="width: 81rpx;height: 81rpx;"></image>
+					<view class="name">USDT</view>
+				</view>
+				<view class="flex_item rightTip">
+					<view class="tipTpl">
+						<view class="num">{{userInfo.USDT * 1}}</view>
+						<!-- <view class="num1">≈0.00RMB</view> -->
+					</view>
+					<view><u-icon name="arrow-right" color="#fff" size="18"></u-icon></view>
+				</view>
+			</view>
+			<view class="propBox flex" @click="navTo('/pages/index/propDetails?type=2')">
+				<view class="flex_item propTpl">
+					<image src="../../static/img/img16.png" style="width: 81rpx;height: 81rpx;"></image>
+					<view class="name">NUSD</view>
+				</view>
+				<view class="flex_item rightTip">
+					<view class="tipTpl">
+						<view class="num">{{userInfo.NUSD*1 + userInfo.NUSD_OLY*1}}</view>
+						<!-- <view class="num1">≈0.00RMB</view> -->
+					</view>
+					<view><u-icon name="arrow-right" color="#fff" size="18"></u-icon></view>
+				</view>
+			</view>
+			<view class="propBox flex" @click="navTo('/pages/index/propDetails?type=3')">
+				<view class="flex_item propTpl">
+					<image src="../../static/img/img16.png" style="width: 81rpx;height: 81rpx;"></image>
+					<view class="name">NICE</view>
+				</view>
+				<view class="flex_item rightTip">
+					<view class="tipTpl">
+						<view class="num">{{userInfo.NICE * 1}}</view>
+						<!-- <view class="num1">≈0.00RMB</view> -->
+					</view>
+					<view><u-icon name="arrow-right" color="#fff" size="18"></u-icon></view>
+				</view>
+			</view>
+			<view class="propBox flex" @click="navTo('/pages/index/propDetails?type=4')">
+				<view class="flex_item propTpl">
+					<image src="../../static/img/img16.png" style="width: 81rpx;height: 81rpx;"></image>
+					<view class="name">XNUSD</view>
+				</view>
+				<view class="flex_item rightTip">
+					<view class="tipTpl">
+						<view class="num">{{userInfo.NUSD_OLY * 1}}</view>
+						<!-- <view class="num1">≈0.00RMB</view> -->
+					</view>
+					<view><u-icon name="arrow-right" color="#fff" size="18"></u-icon></view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	export default {
+		components: {
+			topView
+		},
+		data () {
+			return {}
+		},
+		onLoad () {},
+		onShow () {},
+		computed: {
+			...mapState('user', ['userInfo']),
+		},
+		methods: {
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.infoBox{
+		padding: 0rpx 37rpx;
+	}
+	.moneyBox{
+		background: #1A1A17;
+		border-radius: 25rpx;
+		.moneyTpl{
+			background: url('../../static/img/img17.png') no-repeat;
+			width: 100%;
+			height: 199rpx;
+			background-size: 100% 100%;
+			padding: 50rpx 45rpx;
+			.moneyName{
+				font-weight: bold;
+				font-size: 29rpx;
+				color: #31190B;
+			}
+			.moneyText{
+				color: #31190B;
+				font-size: 30rpx;
+				padding-top: 15rpx;
+				.money{
+					font-weight: bold;
+					font-size: 33rpx;
+					color: #31190B;
+					padding-left: 10rpx;
+				}
+			}
+		}
+		.noneyNav{
+			padding: 30rpx 0rpx;
+			color: #fff;
+			.moneyTip{
+				width: 30%;
+				justify-content: center;
+				.moneyTitle{
+					padding-left: 25rpx;
+				}
+			}
+			.partition{
+				width: 1rpx;
+				height: 43rpx;
+				background: #FFFFFF;
+				opacity: 0.51;
+			}
+		}
+	}
+	.titleProp{
+		padding: 55rpx 0rpx;
+		font-weight: 500;
+		font-size: 36rpx;
+		color: #FFFFFF;
+	}
+	.propBox{
+		color: #fff;
+		margin-bottom: 70rpx;
+		.propTpl{
+			.name{
+				padding-left: 31rpx;
+				font-weight: 500;
+				font-size: 32rpx;
+			}
+		}
+		.rightTip{
+			.tipTpl{
+				padding-right: 20rpx;
+				text-align: right;
+				.num{
+					
+				}
+				.num1{
+					font-weight: 500;
+					font-size: 26rpx;
+					color: #999999;
+					padding-top: 15rpx;
+				}
+			}
+		}
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+</style>

+ 216 - 0
pages/index/recharge.vue

@@ -0,0 +1,216 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo('/pages/index/property')">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">{{$t("property.充值")}}</view>
+			</view>
+			<view class="infoTpl">
+				<view class="flex tplTop">
+					<view class="name">{{$t("recharge.充值数量")}}</view>
+					<view class="tip">{{$t("recharge.可用")}}{{userInfo.NUSD * 1}}NUSD</view>
+				</view>
+				<view class="flex inputBox">
+					<input type="number" v-model="money" :placeholder='$t("recharge.请输入充值数量")'/>
+					<!-- <view class="clickAll" @click="money = userInfo.NUSD * 1">NUSD 全部</view> -->
+				</view>
+				<!-- <view class="name">手续费</view>
+				<view class="flex inputBox">
+					<input v-model="free"/>
+					<view class="freeTip">USTD</view>
+				</view> -->
+			</view>
+		</view>
+		<view class="footBox">
+			<!-- <view class="flex numBox">
+				<view class="numName">实际到账</view>
+				<view class="num">{{num}}</view>
+			</view> -->
+			<view class="btnBox">
+				<view class="submitBtn" :class="{ submitNo: !payOn }" @click="payOn ? recharge() : ''">{{$t("property.充值")}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import {mapState,mapActions,mapMutations} from "vuex";
+	import {charge,chargeCalculator} from "@/api/index.js";
+	export default {
+		components: {
+			topView
+		},
+		data () {
+			return {
+				money:"",
+				free:"--",
+				payOn:true
+			}
+		},
+		onLoad () {
+			
+		},
+		computed: {
+			...mapState('user', ['userInfo']),
+		},
+		methods: {
+			async recharge(){
+				let obj = this;
+				if (obj.money == '') {
+					uni.showToast({
+						title: obj.$t("recharge.请输入充值数量"),
+						icon: "none",
+					});
+					return;
+				}
+				obj.payOn = false
+				try {
+					const res = await chargeCalculator({
+						token: 'NUSD',
+						num: obj.money,
+					});
+					uni.showLoading({
+						title: "loading...",
+						mask: true,
+					});
+					const txHash = await ethereum.request({
+						method: "eth_sendTransaction",
+						params: [{
+							from: obj.userInfo.address,
+							to: res.data.data.to,
+							value: 0,
+							data: res.data.data.data,
+						}, ],
+					});
+					const req = await charge({
+						token: 'NUSD',
+						num: obj.money,
+						transactionHash: txHash
+					});
+					obj.payOn = true
+					uni.showToast({
+						title:obj.$t("recharge.请等待审核"),
+						icon:'success'
+					});
+					obj.money = ''
+					// setTimeout(function () {
+					// 	obj.$router.go(0)
+					// }, 1000);
+				} catch (e) {
+					obj.payOn = true
+					uni.showToast({
+						title: obj.$t("recharge.充值成功"),
+						icon:"error"
+					});
+				}
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.topBox {
+		color: #FFE0BD;
+		padding: 25rpx 27rpx;
+		background: #1A1A17;
+		.topTpl {
+			.addr {
+				padding: 6rpx 25rpx;
+				background: linear-gradient(-74deg, #CE9C6D, #FFECD6);
+				box-shadow: 3rpx 4rpx 5rpx 0rpx rgba(151, 118, 74, 0.5);
+				border-radius: 21rpx;
+				font-size: 24rpx;
+				font-family: PingFang SC;
+				font-weight: 500;
+				color: #986629;
+			}
+		}
+	}
+	.infoBox{
+		padding: 0rpx 30rpx;
+		color: #fff;
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+	.infoTpl{
+		background: #1A1A17;
+		padding: 85rpx 25rpx;
+		border-radius: 25rpx;
+		.tplTop{
+			.tip{
+				font-size: 20rpx;
+			}
+		}
+		.inputBox{
+			background: #2F2F2D;
+			padding: 25rpx 25rpx;
+			border-radius: 25rpx;
+			margin-bottom: 61rpx;
+			.clickAll{
+				font-size: 26rpx;
+			}
+			.freeTip{
+				font-size: 30rpx;
+				color: #888785;
+			}
+		}
+	}
+	.footBox{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		background: #1A1A17;
+		color: #fff;
+		padding: 50rpx 42rpx;
+		.numBox{
+			.numName{
+				font-weight: 500;
+				font-size: 26rpx;
+				color: #888785;
+			}
+		}
+		.btnBox{
+			// margin-top: 50rpx;
+			.submitBtn{
+				background: linear-gradient(258deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+				border-radius: 10rpx;
+				font-weight: bold;
+				font-size: 32rpx;
+				color: #31190B;
+				text-align: center;
+				padding: 20rpx 0rpx;
+			}
+			.submitNo {
+				background: #999999 !important;
+				color: #fff !important;
+			}
+		}
+	}
+	.name{
+		font-size: 32rpx;
+		font-weight: bold;
+		padding-bottom: 35rpx;
+	}
+</style>

+ 206 - 0
pages/index/transfer.vue

@@ -0,0 +1,206 @@
+<template>
+	<view class="container">
+		<view class="infoBox">
+			<view class="title">{{$t("transfer.收款地址")}}</view>
+			<view class="inputBox">
+				<view class="flex_item inpitTpl">
+					<image src="/static/img/img21.png" style="width: 33rpx;height: 36rpx;margin-right: 25rpx;"></image>
+			      <input type="text" :placeholder='$t("transfer.请输入或长按粘贴地址")' v-model="address" style="width: 100%;"/>
+				</view>
+			</view>
+		</view>
+		<view class="infoBox">
+			<view class="title">{{$t("transfer.转账金额")}}</view>
+			<view class="inputBox flex">
+				<view class="flex_item inpitTpl">
+					<image src="/static/img/img21.png" style="width: 33rpx;height: 36rpx;margin-right: 25rpx;"></image>
+			      <input type="text" :placeholder='$t("transfer.请输入转账金额")' v-model="money"/>
+				</view>
+				<view class="all" @click="money = userInfo.NUSD * 1">{{$t("transfer.全部")}}</view>
+			</view>
+			<view class="tipTpl">{{$t("transfer.可转账余额")}}:{{userInfo.NUSD * 1}}NUSD</view>
+		</view>
+		<!-- <view class="infoBox">
+			<view class="title">手续费</view>
+			<view class="inputBox flex">
+				 <input type="text" v-model="free"/>
+				 <view class="tip">USTD</view>
+			</view>
+		</view> -->
+		<view class="footBox">
+			<!-- <view class="flex numBox">
+				<view class="numName">实际到账</view>
+				<view class="num">{{num}}</view>
+			</view> -->
+			<view class="btnBox">
+				<view class="submitBtn" :class="{ submitNo: !payOn }" @click="payOn ? transfer() : ''">{{$t("property.转账")}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {getUserInfo,trade} from '@/api/login.js';
+	export default {
+		data () {
+			return {
+				address:"",
+				money:"",
+				free:"--",
+				num:"--",
+				userInfo:'',
+				payOn:true
+			}
+		},
+		onLoad () {
+			this.getUserInfo()
+		},
+		onShow () {
+			uni.setNavigationBarTitle({
+				title: this.$t("property.转账")
+			});
+		},
+		methods: {
+			transfer(){
+				let obj = this
+				if(obj.address == ''){
+					uni.showToast({
+						title: this.$t("transfer.请输入收款地址"),
+						icon: "none",
+					});
+					return;
+				}
+				if(obj.money == ''){
+					uni.showToast({
+						title: this.$t("transfer.请输入转账金额"),
+						icon: "none",
+					});
+					return;
+				}
+				obj.payOn = false
+				ethereum.request({
+					method: 'eth_requestAccounts'
+				}).then((account) => {
+					const PKR_TRADE = 'PKR_TRADE' + (new Date()).getTime();
+					ethereum.request({
+						"method": "personal_sign",
+						"params": [
+							PKR_TRADE,
+							account[0]
+						]
+					}).then((res) => {
+						obj.trade(res,PKR_TRADE);
+					});
+				});
+			},
+			trade(sign,msg){
+				let obj = this
+				trade({
+					sign,
+					msg,
+					address:obj.address,
+					token:'NUSD',
+					num:obj.money,
+				}).then(function(res){
+					if(res.code == 0){
+						obj.payOn = true
+						uni.showToast({
+							title: obj.$t("transfer.参数错误"),
+							icon: "error",
+						});
+					}else{
+						uni.showToast({
+							title:obj.$t("transfer.转账成功"),
+							icon: "success",
+						});
+						obj.payOn = true
+						setTimeout(function () {
+							obj.$router.go(0)
+						}, 1000);
+					}
+				}).catch(e => {
+					obj.payOn = true
+					console.log(e,11111);
+				});
+			},
+			getUserInfo(){
+				let obj = this
+				getUserInfo({}).then(({data}) => {
+					obj.userInfo = data
+				}).catch(e => {
+					console.log(e.msg);
+				});
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		.container {
+			width: 100%;
+		}
+	}
+	.infoBox{
+		color: #fff;
+		padding: 58rpx 38rpx 0rpx 38rpx;
+		.title{
+			font-weight: bold;
+			font-size: 32rpx;
+			padding-bottom: 10rpx;
+		}
+		.inputBox{
+			border-bottom: 1rpx solid #CBCBCA;
+			padding: 30rpx 0rpx;
+			.all{
+				font-weight: 500;
+				font-size: 28rpx;
+				color: #ABB1CC;
+				background: linear-gradient(258deg, #FFF0CF 0%, #CBA16B 30.0048828125%, #FCE9CF 67.67578125%, #C29963 100%);
+				-webkit-background-clip: text;
+				-webkit-text-fill-color: transparent;
+			}
+			.tip{
+				font-weight: 500;
+				font-size: 30rpx;
+				color: #888785;
+			}
+		}
+		.tipTpl{
+			font-weight: 500;
+			font-size: 24rpx;
+			color: #888785;
+			padding-top: 15rpx;
+		}
+	}
+	.footBox{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		background: #1A1A17;
+		color: #fff;
+		padding: 50rpx 42rpx;
+		.numBox{
+			.numName{
+				font-weight: 500;
+				font-size: 26rpx;
+				color: #888785;
+			}
+		}
+		.btnBox{
+			.submitBtn{
+				background: linear-gradient(258deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+				border-radius: 10rpx;
+				font-weight: bold;
+				font-size: 32rpx;
+				color: #31190B;
+				text-align: center;
+				padding: 20rpx 0rpx;
+			}
+			.submitNo {
+				background: #999999 !important;
+				color: #fff !important;
+			}
+		}
+	}
+</style>

+ 264 - 0
pages/index/withdrawal.vue

@@ -0,0 +1,264 @@
+<template>
+	<view class="container" style="padding-top: 100rpx;">
+		<topView :backg="'#1A1A17'"></topView>
+		<view class="infoBox">
+			<view class="navBack" @click="navTo('/pages/index/property')">
+				<u-icon name="arrow-left" size="25" color="#fff" style="position: absolute;left: 0;top: 0;font-weight: bold;"></u-icon>
+				<view class="title">{{$t("withdrawal.提币")}}</view>
+			</view>
+			<view class="redion">
+				 <u-radio-group class="flex" v-model="value">
+				    <u-radio :customStyle="{marginBottom: '8px'}" v-for="(item, index) in currList"
+				      :key="index" :label="item" :name="item" activeColor="#C29963">
+				    </u-radio>
+				  </u-radio-group>
+			</view>
+			<view class="infoTpl">
+				<view class="flex tplTop">
+					<view class="name">{{$t("withdrawal.提币數量")}}</view>
+					<view class="tip" v-if="value == 'USDT'">{{$t("withdrawal.可用")}}{{userInfo.USDT * 1}}USDT</view>
+					<view class="tip" v-if="value == 'NICE'">{{$t("withdrawal.可用")}}{{userInfo.NICE * 1}}NICE</view>
+				</view>
+				<view class="flex inputBox">
+					<input type="number" v-model="money" @input="inputMonry" :placeholder='$t("withdrawal.请输入提币数量")'/>
+					<view class="clickAll" v-if="value == 'USDT'" @click="money = userInfo.USDT * 1">USDT {{$t("withdrawal.全部")}}</view>
+					<view class="clickAll" v-if="value == 'NICE'" @click="money = userInfo.NICE * 1">NICE {{$t("withdrawal.全部")}}</view>
+				</view>
+				<view class="name">{{$t("withdrawal.手续费")}}</view>
+				<view class="flex inputBox">
+					<input v-model="free" disabled/>
+					<view class="freeTip">{{value}}</view>
+				</view>
+			</view>
+		</view>
+		<view class="footBox">
+			<view class="flex numBox">
+				<view class="numName">{{$t("withdrawal.实际到账")}}</view>
+				<view class="num">{{num}}</view>
+			</view>
+			<view class="btnBox">
+				<view class="submitBtn" :class="{ submitNo: !payOn }" @click="payOn ? clickExtract() : ''">{{$t("withdrawal.提币")}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import topView from '../components/topView.vue';
+	import {extract,extractCalculator} from "@/api/index.js";
+	import {mapState,mapActions,mapMutations} from "vuex";
+	export default {
+		components: {
+			topView
+		},
+		data () {
+			return {
+				money:"",
+				free:"--",
+				num:'0.00',
+				payOn:true,
+				value:'USDT',
+				currList:['USDT','NICE']
+			}
+		},
+		onLoad () {},
+		computed: {
+			...mapState('user', ['userInfo']),
+		},
+		methods: {
+			inputMonry(e){
+				this.free = e.detail.value * (this.userInfo.extract_fee/100)
+				this.num = e.detail.value - this.free
+			},
+			//提取
+			async clickExtract() {
+				let obj = this;
+				if (obj.money == '') {
+					uni.showToast({
+						title: obj.$t("withdrawal.请输入提币数量"),
+						icon: "none",
+					});
+					return;
+				}
+				if (obj.money < 100) {
+					uni.showToast({
+						title: obj.$t("withdrawal.提币数量大于100"),
+						icon: "none",
+					});
+					return;
+				}
+				if(obj.value == 'USDT'){
+					if (obj.money > obj.userInfo.USDT) {
+						uni.showToast({
+							title: "USDT" + obj.$t("exchange.余额不足"),
+							icon: "none",
+						});
+						return;
+					}
+				}
+				if(obj.value == 'NICE'){
+					if (obj.money > obj.userInfo.NICE) {
+						uni.showToast({
+							title: "NICE" + obj.$t("exchange.余额不足"),
+							icon: "none",
+						});
+						return;
+					}
+				}
+				obj.payOn = false
+				try {
+					const res = await extractCalculator({
+						token: obj.value,
+						number: obj.money,
+					});
+					if(res.data.msg == '提币数量大于100'){
+						obj.payOn = true
+						uni.showToast({
+							title: obj.$t("withdrawal.提币数量大于100"),
+							icon: "error",
+						});
+					}else{
+						const txHash = await ethereum.request({
+							method: "eth_sendTransaction",
+							params: [{
+								from: obj.userInfo.address,
+								to: res.data.to,
+								value: (res.data.gas*1).toString(16),
+							}, ],
+						});
+						uni.showLoading({
+							title: "loading...",
+							mask: true,
+						});
+						const req = await extract({
+							token: obj.value,
+							number: obj.money,
+							transactionHash: txHash,
+						});
+						obj.payOn = true
+						uni.showToast({
+							title: this.$t("withdrawal.提取成功"),
+						});
+						setTimeout(function () {
+							obj.$router.go(0)
+						}, 1000);
+					}
+				} catch (e) {
+					obj.payOn = true
+					uni.showToast({
+						title: this.$t("withdrawal.提取失败"),
+						icon: "error",
+					});
+				}
+			},
+			navTo(url){
+				uni.navigateTo({
+					url:url
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page {
+		width: 100%;
+		min-height: 100vh;
+		.container {
+			width: 100%;
+			min-height: 100vh;
+		}
+	}
+	.topBox {
+		color: #FFE0BD;
+		padding: 25rpx 27rpx;
+		background: #1A1A17;
+		.topTpl {
+			.addr {
+				padding: 6rpx 25rpx;
+				background: linear-gradient(-74deg, #CE9C6D, #FFECD6);
+				box-shadow: 3rpx 4rpx 5rpx 0rpx rgba(151, 118, 74, 0.5);
+				border-radius: 21rpx;
+				font-size: 24rpx;
+				font-family: PingFang SC;
+				font-weight: 500;
+				color: #986629;
+			}
+		}
+	}
+	.infoBox{
+		padding: 0rpx 30rpx;
+		color: #fff;
+	}
+	.navBack{
+		position: relative;
+		color: #fff;
+		text-align: center;
+		margin: 30rpx 0rpx 40rpx 0rpx;
+		.title{
+			font-weight: bold;
+			font-size: 34rpx;
+		}
+	}
+	.redion{
+		padding:25rpx 150rpx 25rpx 150rpx;
+	}
+	.infoTpl{
+		background: #1A1A17;
+		padding: 85rpx 25rpx;
+		border-radius: 25rpx;
+		.tplTop{
+			.tip{
+				font-size: 20rpx;
+			}
+		}
+		.inputBox{
+			background: #2F2F2D;
+			padding: 25rpx 25rpx;
+			border-radius: 25rpx;
+			margin-bottom: 61rpx;
+			.clickAll{
+				font-size: 26rpx;
+			}
+			.freeTip{
+				font-size: 30rpx;
+				color: #888785;
+			}
+		}
+	}
+	.footBox{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		background: #1A1A17;
+		color: #fff;
+		padding: 50rpx 42rpx;
+		.numBox{
+			.numName{
+				font-weight: 500;
+				font-size: 26rpx;
+				color: #888785;
+			}
+		}
+		.btnBox{
+			margin-top: 50rpx;
+			.submitBtn{
+				background: linear-gradient(258deg, #FFF0CF, #CBA16B, #FCE9CF, #C29963);
+				border-radius: 10rpx;
+				font-weight: bold;
+				font-size: 32rpx;
+				color: #31190B;
+				text-align: center;
+				padding: 20rpx 0rpx;
+			}
+			.submitNo {
+				background: #999999 !important;
+				color: #fff !important;
+			}
+		}
+	}
+	.name{
+		font-size: 32rpx;
+		font-weight: bold;
+		padding-bottom: 35rpx;
+	}
+</style>

+ 15 - 0
plugin/detect-provider/LICENSE

@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2020 MetaMask
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+ 106 - 0
plugin/detect-provider/README.md

@@ -0,0 +1,106 @@
+# @metamask/detect-provider
+
+A tiny utility for detecting the MetaMask Ethereum provider, or any provider injected at `window.ethereum`.
+
+It has 0 dependencies and works out of the box in any modern browser, for synchronously and asynchronously injected providers.
+
+## Usage
+
+Keep in mind that the providers detected by this package may or may not support [the Ethereum JavaScript Provider API](https://eips.ethereum.org/EIPS/eip-1193).
+Please consult [the MetaMask documentation](https://docs.metamask.io/guide/ethereum-provider.html) to learn how to use our provider.
+
+### Node.js
+
+```javascript
+import detectEthereumProvider from '@metamask/detect-provider'
+
+const provider = await detectEthereumProvider()
+
+if (provider) {
+
+  console.log('Ethereum successfully detected!')
+
+  // From now on, this should always be true:
+  // provider === window.ethereum
+
+  // Access the decentralized web!
+
+  // Legacy providers may only have ethereum.sendAsync
+  const chainId = await provider.request({
+    method: 'eth_chainId'
+  })
+} else {
+
+  // if the provider is not detected, detectEthereumProvider resolves to null
+  console.error('Please install MetaMask!', error)
+}
+```
+
+### HTML
+
+```html
+<script src="https://unpkg.com/@metamask/detect-provider/dist/detect-provider.min.js"></script>
+<script type="text/javascript">
+  const provider = await detectEthereumProvider()
+
+  if (provider) {
+    // handle provider
+  } else {
+    // handle no provider
+  }
+</script>
+```
+
+### Options
+
+The exported function takes an optional `options` object.
+If invalid options are provided, an error will be thrown.
+All options have default values.
+
+#### `options.mustBeMetaMask`
+
+Type: `boolean`
+
+Default: `false`
+
+Whether `window.ethereum.isMetaMask === true` is required for the returned Promise to resolve.
+
+#### `options.silent`
+
+Type: `boolean`
+
+Default: `false`
+
+Whether error messages should be logged to the console.
+Does not affect errors thrown due to invalid options.
+
+#### `options.timeout`
+
+Type: `number`
+
+Default: `3000`
+
+How many milliseconds to wait for asynchronously injected providers.
+
+## Advanced Topics
+
+### Synchronous and Asynchronous Injection
+
+Providers can be either synchronously or asynchronously injected:
+
+- _Synchronously_ injected providers will be available by the time website code starts executing.
+- _Asynchronously_ injected providers may not become available until later in the page lifecycle.
+
+The MetaMask _extension_ provider is synchronously injected, while the MetaMask _mobile_ provider is asynchronously injected.
+
+To notify sites of asynchronous injection, MetaMask dispatches the `ethereum#initialized` event on `window` immediately after the provider has been set as `window.ethereum`.
+This package relies on that event to detect asynchronous injection.
+
+### Overwriting or Modifying `window.ethereum`
+
+The detected provider object returned by this package will strictly equal (`===`) `window.ethereum` for the entire page lifecycle, unless `window.ethereum` is overwritten.
+In general, consumers should never overwrite `window.ethereum` or attempt to modify the provider object.
+
+If, as a dapp developer, you notice that the provider returned by this package does not strictly equal `window.ethereum`, something is wrong.
+This may happen, for example, if the user has multiple wallets installed.
+After confirming that your code and dependencies are not modifying or overwriting `window.ethereum`, you should ask the user to ensure that they only have a single provider-injecting wallet enabled at any one time.

+ 66 - 0
plugin/detect-provider/dist/detect-provider.js

@@ -0,0 +1,66 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.detectEthereumProvider = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+"use strict";
+/**
+ * Returns a Promise that resolves to the value of window.ethereum if it is
+ * set within the given timeout, or null.
+ * The Promise will not reject, but an error will be thrown if invalid options
+ * are provided.
+ *
+ * @param options - Options bag.
+ * @param options.mustBeMetaMask - Whether to only look for MetaMask providers.
+ * Default: false
+ * @param options.silent - Whether to silence console errors. Does not affect
+ * thrown errors. Default: false
+ * @param options.timeout - Milliseconds to wait for 'ethereum#initialized' to
+ * be dispatched. Default: 3000
+ * @returns A Promise that resolves with the Provider if it is detected within
+ * given timeout, otherwise null.
+ */
+function detectEthereumProvider({ mustBeMetaMask = false, silent = false, timeout = 3000, } = {}) {
+    _validateInputs();
+    let handled = false;
+    return new Promise((resolve) => {
+        if (window.ethereum) {
+            handleEthereum();
+        }
+        else {
+            window.addEventListener('ethereum#initialized', handleEthereum, { once: true });
+            setTimeout(() => {
+                handleEthereum();
+            }, timeout);
+        }
+        function handleEthereum() {
+            if (handled) {
+                return;
+            }
+            handled = true;
+            window.removeEventListener('ethereum#initialized', handleEthereum);
+            const { ethereum } = window;
+            if (ethereum && (!mustBeMetaMask || ethereum.isMetaMask)) {
+                resolve(ethereum);
+            }
+            else {
+                const message = mustBeMetaMask && ethereum
+                    ? 'Non-MetaMask window.ethereum detected.'
+                    : 'Unable to detect window.ethereum.';
+                !silent && console.error('@metamask/detect-provider:', message);
+                resolve(null);
+            }
+        }
+    });
+    function _validateInputs() {
+        if (typeof mustBeMetaMask !== 'boolean') {
+            throw new Error(`@metamask/detect-provider: Expected option 'mustBeMetaMask' to be a boolean.`);
+        }
+        if (typeof silent !== 'boolean') {
+            throw new Error(`@metamask/detect-provider: Expected option 'silent' to be a boolean.`);
+        }
+        if (typeof timeout !== 'number') {
+            throw new Error(`@metamask/detect-provider: Expected option 'timeout' to be a number.`);
+        }
+    }
+}
+module.exports = detectEthereumProvider;
+
+},{}]},{},[1])(1)
+});

+ 1 - 0
plugin/detect-provider/dist/detect-provider.min.js

@@ -0,0 +1 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).detectEthereumProvider=e()}}((function(){return function({mustBeMetaMask:e=!1,silent:t=!1,timeout:o=3e3}={}){!function(){if("boolean"!=typeof e)throw new Error("@metamask/detect-provider: Expected option 'mustBeMetaMask' to be a boolean.");if("boolean"!=typeof t)throw new Error("@metamask/detect-provider: Expected option 'silent' to be a boolean.");if("number"!=typeof o)throw new Error("@metamask/detect-provider: Expected option 'timeout' to be a number.")}();let n=!1;return new Promise(i=>{function r(){if(n)return;n=!0,window.removeEventListener("ethereum#initialized",r);const{ethereum:o}=window;if(!o||e&&!o.isMetaMask){const n=e&&o?"Non-MetaMask window.ethereum detected.":"Unable to detect window.ethereum.";!t&&console.error("@metamask/detect-provider:",n),i(null)}else i(o)}window.ethereum?r():(window.addEventListener("ethereum#initialized",r,{once:!0}),setTimeout(()=>{r()},o))})}}));

+ 31 - 0
plugin/detect-provider/dist/index.d.ts

@@ -0,0 +1,31 @@
+interface MetaMaskEthereumProvider {
+    isMetaMask?: boolean;
+    once(eventName: string | symbol, listener: (...args: any[]) => void): this;
+    on(eventName: string | symbol, listener: (...args: any[]) => void): this;
+    off(eventName: string | symbol, listener: (...args: any[]) => void): this;
+    addListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
+    removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
+    removeAllListeners(event?: string | symbol): this;
+}
+export = detectEthereumProvider;
+/**
+ * Returns a Promise that resolves to the value of window.ethereum if it is
+ * set within the given timeout, or null.
+ * The Promise will not reject, but an error will be thrown if invalid options
+ * are provided.
+ *
+ * @param options - Options bag.
+ * @param options.mustBeMetaMask - Whether to only look for MetaMask providers.
+ * Default: false
+ * @param options.silent - Whether to silence console errors. Does not affect
+ * thrown errors. Default: false
+ * @param options.timeout - Milliseconds to wait for 'ethereum#initialized' to
+ * be dispatched. Default: 3000
+ * @returns A Promise that resolves with the Provider if it is detected within
+ * given timeout, otherwise null.
+ */
+declare function detectEthereumProvider<T = MetaMaskEthereumProvider>({ mustBeMetaMask, silent, timeout, }?: {
+    mustBeMetaMask?: boolean | undefined;
+    silent?: boolean | undefined;
+    timeout?: number | undefined;
+}): Promise<T | null>;

File diff suppressed because it is too large
+ 62 - 0
plugin/detect-provider/dist/index.js


+ 63 - 0
plugin/detect-provider/package.json

@@ -0,0 +1,63 @@
+{
+  "name": "@metamask/detect-provider",
+  "version": "2.0.0",
+  "description": "A tiny utility for detecting the MetaMask Ethereum provider, or any EIP 1193-compliant provider.",
+  "main": "dist/index.js",
+  "typings": "dist/index.d.ts",
+  "files": [
+    "dist/"
+  ],
+  "engines": {
+    "node": ">=14.0.0"
+  },
+  "scripts": {
+    "pretest": "yarn build",
+    "test": "node test/*",
+    "lint": "eslint . --ext ts,js,json",
+    "lint:fix": "yarn lint --fix",
+    "build": "./build.sh",
+    "setup": "yarn install && yarn allow-scripts"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/MetaMask/detect-provider.git"
+  },
+  "author": "Erik Marks <rekmarks@protonmail.com>",
+  "license": "ISC",
+  "private": false,
+  "publishConfig": {
+    "access": "public"
+  },
+  "keywords": [
+    "ethereum",
+    "provider",
+    "metamask",
+    "web3"
+  ],
+  "bugs": {
+    "url": "https://github.com/MetaMask/detect-provider/issues"
+  },
+  "homepage": "https://github.com/MetaMask/detect-provider#readme",
+  "devDependencies": {
+    "@lavamoat/allow-scripts": "^2.0.3",
+    "@metamask/auto-changelog": "^2.6.0",
+    "@metamask/eslint-config": "^4.1.0",
+    "@typescript-eslint/eslint-plugin": "^4.5.0",
+    "@typescript-eslint/parser": "^4.5.0",
+    "browserify": "^16.5.1",
+    "eslint": "^7.7.0",
+    "eslint-plugin-import": "^2.22.0",
+    "eslint-plugin-json": "^2.1.1",
+    "eslint-plugin-node": "^11.1.0",
+    "sinon": "^9.0.2",
+    "tape": "^5.0.0",
+    "tinyify": "^3.0.0",
+    "typescript": "^4.0.3"
+  },
+  "dependencies": {},
+  "lavamoat": {
+    "allowScripts": {
+      "@lavamoat/preinstall-always-fail": false
+    }
+  }
+}

File diff suppressed because it is too large
+ 0 - 0
plugin/jweixin-module/index.js


+ 1645 - 0
plugin/vue-i18n/CHANGELOG.md

@@ -0,0 +1,1645 @@
+
+## v8.21.0 (2020-08-13)
+
+#### :star: New Features
+* [#972](https://github.com/kazupon/vue-i18n/pull/972) feat: message function ([@kazupon](https://github.com/kazupon))
+
+#### :pencil: Documentation
+* [#961](https://github.com/kazupon/vue-i18n/pull/961) Update link to Formatter Interface ([@JohJohan](https://github.com/JohJohan))
+
+#### Committers: 3
+- Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+- Johan ([@JohJohan](https://github.com/JohJohan))
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+
+## v8.20.0 (2020-07-30)
+
+#### :star: New Features
+* [#959](https://github.com/kazupon/vue-i18n/pull/959) i18n-n component local components passing ([@kazupon](https://github.com/kazupon))
+* [#928](https://github.com/kazupon/vue-i18n/pull/928) :zap: improvement(interpolation): enable passage of local components to tag prop ([@vhoyer](https://github.com/vhoyer))
+
+#### Committers: 2
+- Vinícius Hoyer ([@vhoyer](https://github.com/vhoyer))
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+
+## v8.19.0 (2020-07-25)
+
+#### :star: New Features
+* [#942](https://github.com/kazupon/vue-i18n/pull/942) Add vetur support for tags and attributes ([@phiter](https://github.com/phiter))
+
+#### :pencil: Documentation
+* [#925](https://github.com/kazupon/vue-i18n/pull/925) Added missing quote ([@fschlag](https://github.com/fschlag))
+* [#921](https://github.com/kazupon/vue-i18n/pull/921) Add lost pluralizationRules option to documentation ([@AleksandrSl](https://github.com/AleksandrSl))
+* [#920](https://github.com/kazupon/vue-i18n/pull/920) Make link to API and Guide top level ([@AleksandrSl](https://github.com/AleksandrSl))
+
+#### Committers: 3
+- Aleksandr ([@AleksandrSl](https://github.com/AleksandrSl))
+- Florian Schlag ([@fschlag](https://github.com/fschlag))
+- Phiter Fernandes ([@phiter](https://github.com/phiter))
+
+
+## v8.18.2 (2020-06-08)
+
+#### :zap: Improved Features
+* [#917](https://github.com/kazupon/vue-i18n/pull/917) fix: improve IVueI18n interface ([@kazupon](https://github.com/kazupon))
+
+#### :pencil: Documentation
+* [#902](https://github.com/kazupon/vue-i18n/pull/902) docs: [RU] Translation update ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+* [#901](https://github.com/kazupon/vue-i18n/pull/901) docs: (zh) inverse $d $n ([@stan-chen](https://github.com/stan-chen))
+
+#### Committers: 4
+- Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+- Rafał Chłodnicki ([@rchl](https://github.com/rchl))
+- Stanley Chen ([@stan-chen](https://github.com/stan-chen))
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+
+## v8.18.1 (2020-05-27)
+
+#### :bug: Bug Fixes
+* [#896](https://github.com/kazupon/vue-i18n/pull/896) Fix flow types and enable flow types testing on CI ([@rchl](https://github.com/rchl))
+
+#### Committers: 1
+- Rafał Chłodnicki ([@rchl](https://github.com/rchl))
+
+
+## v8.18.0 (2020-05-26)
+
+#### :star: New Features
+* [#892](https://github.com/kazupon/vue-i18n/pull/892) Add onComponentInstanceCreated constructor option ([@rchl](https://github.com/rchl))
+
+#### :zap: Improved Features
+* [#890](https://github.com/kazupon/vue-i18n/pull/890) chore: set up linting for typescript definitions ([@rchl](https://github.com/rchl))
+
+#### Committers: 1
+- Rafał Chłodnicki ([@rchl](https://github.com/rchl))
+
+
+## v8.17.7 (2020-05-19)
+
+#### :bug: Bug Fixes
+* [#882](https://github.com/kazupon/vue-i18n/pull/882) fix v-t pluralisation when choice is 0 ([@mikejacoutot](https://github.com/mikejacoutot))
+
+#### Committers: 1
+- [@mikejacoutot](https://github.com/mikejacoutot)
+
+
+## v8.17.6 (2020-05-15)
+
+#### :bug: Bug Fixes
+* [#880](https://github.com/kazupon/vue-i18n/pull/880) Don't delete _i18n in beforeDestroy ([@danimoh](https://github.com/danimoh))
+
+#### :zap: Improved Features
+* [#878](https://github.com/kazupon/vue-i18n/pull/878) Allow component interpolation without root element ([@danimoh](https://github.com/danimoh))
+
+#### :pencil: Documentation
+* [#875](https://github.com/kazupon/vue-i18n/pull/875) Add new 3rd party tool ([@danigayosog](https://github.com/danigayosog))
+* [#872](https://github.com/kazupon/vue-i18n/pull/872) docs: fixes ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+* [#871](https://github.com/kazupon/vue-i18n/pull/871) update pluralization.md ([@Timibadass](https://github.com/Timibadass))
+
+#### Committers: 4
+- Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+- Daniel ([@danigayosog](https://github.com/danigayosog))
+- Timi Omoyeni ([@Timibadass](https://github.com/Timibadass))
+- [@danimoh](https://github.com/danimoh)
+
+
+## v8.17.5 (2020-05-10)
+
+#### :bug: Bug Fixes
+* [#869](https://github.com/kazupon/vue-i18n/pull/869) fix: not string method access error ([@kazupon](https://github.com/kazupon))
+
+#### :pencil: Documentation
+* [#867](https://github.com/kazupon/vue-i18n/pull/867) docs: [RU] Translation ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+* [#865](https://github.com/kazupon/vue-i18n/pull/865) improvement(docs): extend Hot reloading section ([@caugner](https://github.com/caugner))
+
+#### Committers: 3
+- Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+- Claas Augner ([@caugner](https://github.com/caugner))
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+
+## v8.17.4 (2020-04-26)
+
+#### :bug: Bug Fixes
+* [#859](https://github.com/kazupon/vue-i18n/pull/859) fix datetime format cache ([@kazupon](https://github.com/kazupon))
+* [#858](https://github.com/kazupon/vue-i18n/pull/858) fix datetime and number format fallbacking ([@kazupon](https://github.com/kazupon))
+* [#857](https://github.com/kazupon/vue-i18n/pull/857) fix: alternative array includes ([@kazupon](https://github.com/kazupon))
+
+#### Committers: 1
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+
+## v8.17.3 (2020-04-19)
+
+#### :zap: Improved Features
+* [#846](https://github.com/kazupon/vue-i18n/pull/846) add key to postTranslation ([@dmitryuk](https://github.com/dmitryuk))
+
+#### :pencil: Documentation
+* [#847](https://github.com/kazupon/vue-i18n/pull/847) docs: Update /api/README.md ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+
+#### Committers: 2
+- Alexander Dmitryuk ([@dmitryuk](https://github.com/dmitryuk))
+- Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov))
+
+
+## v8.17.2 (2020-04-18)
+
+#### :zap: Improved Features
+* [#844](https://github.com/kazupon/vue-i18n/pull/844) Use plain object instead of Map, which is not supported in IE9/10 ([@exoego](https://github.com/exoego))
+
+#### Committers: 1
+- TATSUNO Yasuhiro ([@exoego](https://github.com/exoego))
+
+
+## v8.17.1 (2020-04-16)
+
+#### :bug: Bug Fixes
+* [#840](https://github.com/kazupon/vue-i18n/pull/840) fix: altnative endsWidth ([@kazupon](https://github.com/kazupon))
+
+#### :pencil: Documentation
+* [#837](https://github.com/kazupon/vue-i18n/pull/837) Fix typo ([@ninofiliu](https://github.com/ninofiliu))
+
+#### Committers: 2
+- Nino Filiu ([@ninofiliu](https://github.com/ninofiliu))
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+
+## v8.17.0 (2020-04-11)
+
+#### :star: New Features
+* [#829](https://github.com/kazupon/vue-i18n/pull/829) #138 Fallback Locale as array for cascading fallbacks ([@mmokross](https://github.com/mmokross))
+
+#### :pencil: Documentation
+* [#834](https://github.com/kazupon/vue-i18n/pull/834) Add capitalize default modifier in doc ([@alexandreDavid](https://github.com/alexandreDavid))
+* [#832](https://github.com/kazupon/vue-i18n/pull/832) fix in examples of "Custom pluralization" ([@Perlover](https://github.com/Perlover))
+
+#### Committers: 4
+- Alexandre David ([@alexandreDavid](https://github.com/alexandreDavid))
+- Kobayashi Kazuhiro ([@kzhrk](https://github.com/kzhrk))
+- Michael Mokroß ([@mmokross](https://github.com/mmokross))
+- Perlover ([@Perlover](https://github.com/Perlover))
+
+
+## v8.16.0 (2020-03-27)
+
+#### :star: New Features
+* [#822](https://github.com/kazupon/vue-i18n/pull/822) post translation hooking feature ([@kazupon](https://github.com/kazupon))
+
+#### Committers: 1
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+
+## v8.15.7 (2020-03-25)
+
+#### :bug: Bug Fixes
+* [#819](https://github.com/kazupon/vue-i18n/pull/819) Fixed bug when changing number format runtime ([@slischka](https://github.com/slischka))
+
+#### Committers: 1
+- Jiří Slischka ([@slischka](https://github.com/slischka))
+
+
+## v8.15.6 (2020-03-23)
+
+#### :bug: Bug Fixes
+* [#817](https://github.com/kazupon/vue-i18n/pull/817) Bugfix : 'setLocaleMessage' / 'mergeLocaleMessage' doesn't work if 'warnHtmlInMessage' is set to 'error'  ([@aym3nb](https://github.com/aym3nb))
+
+#### :pencil: Documentation
+* [#816](https://github.com/kazupon/vue-i18n/pull/816) Update fallback.md ([@scoutrul](https://github.com/scoutrul))
+
+#### Committers: 3
+- Anton ([@scoutrul](https://github.com/scoutrul))
+- Aymen Bareche ([@aym3nb](https://github.com/aym3nb))
+- TATSUNO Yasuhiro ([@exoego](https://github.com/exoego))
+
+
+## v8.15.5 (2020-03-07)
+
+#### :star: Features
+* [#787](https://github.com/kazupon/vue-i18n/pull/787) Add a 'capitalize' default modifier for linked message ([@charlesmass](https://github.com/charlesmass))
+
+#### :zap: Improvement Features
+* [#794](https://github.com/kazupon/vue-i18n/pull/794) Support returning 'string' type for customized interpolation ([@sihyeonn](https://github.com/sihyeonn))
+
+#### :pencil: Documentation
+* [#791](https://github.com/kazupon/vue-i18n/pull/791) Revise fallback.md ([@jlebar](https://github.com/jlebar))
+
+#### Committers: 4
+- Justin Lebar ([@jlebar](https://github.com/jlebar))
+- L M ([@charlesmass](https://github.com/charlesmass))
+- Sihyeon Jang ([@sihyeonn](https://github.com/sihyeonn))
+- kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
+
+<a name="8.15.4"></a>
+## [8.15.4](https://github.com/kazupon/vue-i18n/compare/v8.15.3...v8.15.4) (2020-02-25)
+
+
+### :bug: Bug Fixes
+
+* **index:** improve formatFallbackMessages code (#779) (#783) by [@masongzhi](https://github.com/masongzhi) ([53895b9](https://github.com/kazupon/vue-i18n/commit/53895b9)))
+
+### :up: Updates
+
+* some fixes ([8a9a950](https://github.com/kazupon/vue-i18n/commit/8a9a950))
+
+
+
+<a name="8.15.3"></a>
+## [8.15.3](https://github.com/kazupon/vue-i18n/compare/v8.15.2...v8.15.3) (2019-12-18)
+
+
+### :zap: Improvements
+
+* **index:** fix mergeLocaleMessage. add changes notification on merging with an empty target object ([#752](https://github.com/kazupon/vue-i18n/issues/752)) by [@jekill](https://github.com/jekill) ([048eac5](https://github.com/kazupon/vue-i18n/commit/048eac5)), closes [#752](https://github.com/kazupon/vue-i18n/issues/752)
+
+
+
+<a name="8.15.2"></a>
+## [8.15.2](https://github.com/kazupon/vue-i18n/compare/v8.15.1...v8.15.2) (2019-12-18)
+
+
+### :bug: Bug Fixes
+
+* **index:** Fix exception when using unit number formatting by [@simonjodet](https://github.com/simonjodet) ([194b801](https://github.com/kazupon/vue-i18n/commit/194b801)), closes [#750](https://github.com/kazupon/vue-i18n/issues/750) [#751](https://github.com/kazupon/vue-i18n/issues/751)
+
+
+
+<a name="8.15.1"></a>
+## [8.15.1](https://github.com/kazupon/vue-i18n/compare/v8.15.0...v8.15.1) (2019-11-27)
+
+
+### :zap: Improvements
+
+* **mixin:** change to custom blocks parse error ([a9858be](https://github.com/kazupon/vue-i18n/commit/a9858be))
+
+
+
+<a name="8.15.0"></a>
+# [8.15.0](https://github.com/kazupon/vue-i18n/compare/v8.14.1...v8.15.0) (2019-10-16)
+
+
+### :star: New Features
+
+* Add constructor option for custom modifiers ([#724](https://github.com/kazupon/vue-i18n/issues/724)) by [@epaezrubio](https://github.com/epaezrubio) ([3217212](https://github.com/kazupon/vue-i18n/commit/3217212)), closes [#724](https://github.com/kazupon/vue-i18n/issues/724)
+
+
+
+<a name="8.14.1"></a>
+## [8.14.1](https://github.com/kazupon/vue-i18n/compare/v8.14.0...v8.14.1) (2019-09-12)
+
+
+### :bug: Bug Fixes
+
+* **path:** fix branket key error ([8d2aba7](https://github.com/kazupon/vue-i18n/commit/8d2aba7))
+* **component:** Fix interpolation component when there are empty text nodes ([547cdd1](https://github.com/kazupon/vue-i18n/commit/547cdd1)) by [@Demivan](https://github.com/Demivan)
+
+
+
+<a name="8.14.0"></a>
+# [8.14.0](https://github.com/kazupon/vue-i18n/compare/v8.13.0...v8.14.0) (2019-08-12)
+
+
+### :star: New Features
+
+* fallback formatting ([#637](https://github.com/kazupon/vue-i18n/issues/637)) by [@sebwas](https://github.com/sebwas) ([bf9929c](https://github.com/kazupon/vue-i18n/commit/bf9929c)), closes [#637](https://github.com/kazupon/vue-i18n/issues/637)
+* support slots syntax for component interpolation ([#685](https://github.com/kazupon/vue-i18n/issues/685)) by [@aavondet](https://github.com/aavondet) ([71ca843](https://github.com/kazupon/vue-i18n/commit/71ca843)), closes [#685](https://github.com/kazupon/vue-i18n/issues/685)
+
+
+
+<a name="8.13.0"></a>
+# [8.13.0](https://github.com/kazupon/vue-i18n/compare/v8.12.0...v8.13.0) (2019-08-09)
+
+
+### :star: New Features
+
+* datetime/number formats fallback warning filter ([46de19e](https://github.com/kazupon/vue-i18n/commit/46de19e)), closes [#558](https://github.com/kazupon/vue-i18n/issues/558)
+* fallback translation warning filter ([69fc798](https://github.com/kazupon/vue-i18n/commit/69fc798))
+* translation missing warning filter ([666dc9d](https://github.com/kazupon/vue-i18n/commit/666dc9d))
+
+
+
+<a name="8.12.0"></a>
+# [8.12.0](https://github.com/kazupon/vue-i18n/compare/v8.11.2...v8.12.0) (2019-07-09)
+
+
+### :star: New Features
+
+* **mixin:** shared locale messages feature ([82543de](https://github.com/kazupon/vue-i18n/commit/82543de))
+
+
+### :zap: Improvements
+
+* **typing:** sharedMessages option type ([6967a15](https://github.com/kazupon/vue-i18n/commit/6967a15))
+
+
+
+<a name="8.11.2"></a>
+## [8.11.2](https://github.com/kazupon/vue-i18n/compare/v8.11.1...v8.11.2) (2019-04-30)
+
+
+### :bug: Bug Fixes
+
+* bug(mixin): fix SSR memory leak by moving subscribeDataChanging calls into beforeMount ([#572](https://github.com/kazupon/vue-i18n/issues/572)) by [@Pindar](https://github.com/Pindar) ([32b5795](https://github.com/kazupon/vue-i18n/commit/32b5795)), closes [#572](https://github.com/kazupon/vue-i18n/issues/572)
+
+
+
+<a name="8.11.1"></a>
+## [8.11.1](https://github.com/kazupon/vue-i18n/compare/v8.11.0...v8.11.1) (2019-04-26)
+
+
+### :bug: Bug Fixes
+
+* fix ES Modules distribution ([bb631a1](https://github.com/kazupon/vue-i18n/commit/bb631a1))
+
+
+
+<a name="8.11.0"></a>
+# [8.11.0](https://github.com/kazupon/vue-i18n/compare/v8.10.0...v8.11.0) (2019-04-26)
+
+
+### :star: New Features
+
+* ES modules for browser ([#561](https://github.com/kazupon/vue-i18n/issues/561)) ([c9b9adf](https://github.com/kazupon/vue-i18n/commit/c9b9adf)), closes [#561](https://github.com/kazupon/vue-i18n/issues/561)
+* HTML locale message warning option ([#567](https://github.com/kazupon/vue-i18n/issues/567)) ([4aecf03](https://github.com/kazupon/vue-i18n/commit/4aecf03)), closes [#567](https://github.com/kazupon/vue-i18n/issues/567)
+
+
+
+<a name="8.10.0"></a>
+# [8.10.0](https://github.com/kazupon/vue-i18n/compare/v8.9.0...v8.10.0) (2019-03-28)
+
+
+### :star: New Features
+
+* **number:** i18n-n functional component ([#541](https://github.com/kazupon/vue-i18n/issues/541)) by [@bponomarenko](https://github.com/bponomarenko) ([b33579d](https://github.com/kazupon/vue-i18n/commit/b33579d)), closes [#541](https://github.com/kazupon/vue-i18n/issues/541)
+* **path:** Keypath should parse if sub path contains spaces. ([#533](https://github.com/kazupon/vue-i18n/issues/533)) by [@exoego](https://github.com/exoego) ([640daaf](https://github.com/kazupon/vue-i18n/commit/640daaf)), closes [#533](https://github.com/kazupon/vue-i18n/issues/533)
+
+
+### :zap: Improvements
+
+* **number:** support data fall through in i18n-n ([#545](https://github.com/kazupon/vue-i18n/issues/545)) ([71cadbf](https://github.com/kazupon/vue-i18n/commit/71cadbf)), closes [#545](https://github.com/kazupon/vue-i18n/issues/545)
+
+
+### :pencil: docs
+
+* **vuepress:** translate documents for chinese ([#536](https://github.com/kazupon/vue-i18n/issues/536)) by [@xuhongbo](https://github.com/xuhongbo) ([ccf29f8](https://github.com/kazupon/vue-i18n/commit/ccf29f8)), closes [#536](https://github.com/kazupon/vue-i18n/issues/536) [#531](https://github.com/kazupon/vue-i18n/issues/531) [#1](https://github.com/kazupon/vue-i18n/issues/1) [#533](https://github.com/kazupon/vue-i18n/issues/533) [#540](https://github.com/kazupon/vue-i18n/issues/540) [#541](https://github.com/kazupon/vue-i18n/issues/541) [#1](https://github.com/kazupon/vue-i18n/issues/1) [#2](https://github.com/kazupon/vue-i18n/issues/2)
+
+
+<a name="8.9.0"></a>
+# [8.9.0](https://github.com/kazupon/vue-i18n/compare/v8.8.2...v8.9.0) (2019-03-08)
+
+
+### :bug: Bug Fixes
+
+* **index:** Fix [#515](https://github.com/kazupon/vue-i18n/issues/515) empty string not returning true ([#525](https://github.com/kazupon/vue-i18n/issues/525)) by [@kimuraz](https://github.com/kimuraz) ([396c5ca](https://github.com/kazupon/vue-i18n/commit/396c5ca)), closes [#515](https://github.com/kazupon/vue-i18n/issues/515) [#525](https://github.com/kazupon/vue-i18n/issues/525) [#515](https://github.com/kazupon/vue-i18n/issues/515)
+
+
+### :star: New Features
+
+* **index:** add availableLocales (related issue [#193](https://github.com/kazupon/vue-i18n/issues/193), PR [#528](https://github.com/kazupon/vue-i18n/issues/528)) by [@exoego](https://github.com/exoego) ([8f75b1f](https://github.com/kazupon/vue-i18n/commit/8f75b1f)), closes [#193](https://github.com/kazupon/vue-i18n/issues/193) [#528](https://github.com/kazupon/vue-i18n/issues/528) [#193](https://github.com/kazupon/vue-i18n/issues/193) [#193](https://github.com/kazupon/vue-i18n/issues/193) [#193](https://github.com/kazupon/vue-i18n/issues/193) [#193](https://github.com/kazupon/vue-i18n/issues/193)
+
+
+### :zap: Improvements
+
+* **flowtype:** Fix missing type declarations in flow type ([#529](https://github.com/kazupon/vue-i18n/issues/529)) by [@exoego](https://github.com/exoego) ([4173764](https://github.com/kazupon/vue-i18n/commit/4173764)), closes [#529](https://github.com/kazupon/vue-i18n/issues/529)
+
+
+
+<a name="8.8.2"></a>
+## [8.8.2](https://github.com/kazupon/vue-i18n/compare/v8.8.1...v8.8.2) (2019-02-17)
+
+
+### :bug: Bug Fixes
+
+* **mixin:** fix memory leak ([135058d](https://github.com/kazupon/vue-i18n/commit/135058d)), closes [#514](https://github.com/kazupon/vue-i18n/issues/514)
+
+
+
+<a name="8.8.1"></a>
+## [8.8.1](https://github.com/kazupon/vue-i18n/compare/v8.8.0...v8.8.1) (2019-02-10)
+
+
+### :bug: Bug Fixes
+
+* **index:** fixed [#478](https://github.com/kazupon/vue-i18n/issues/478) ([#518](https://github.com/kazupon/vue-i18n/issues/518)) by [@stroncium](https://github.com/stroncium) ([469edd9](https://github.com/kazupon/vue-i18n/commit/469edd9)), closes [#478](https://github.com/kazupon/vue-i18n/issues/478) [#518](https://github.com/kazupon/vue-i18n/issues/518) [#478](https://github.com/kazupon/vue-i18n/issues/478)
+
+
+### :zap: Improvements
+
+* **flowtype:** update typings ([44e04e7](https://github.com/kazupon/vue-i18n/commit/44e04e7))
+* **typescript:** update typings ([dee35b9](https://github.com/kazupon/vue-i18n/commit/dee35b9))
+
+
+
+<a name="8.8.0"></a>
+# [8.8.0](https://github.com/kazupon/vue-i18n/compare/v8.7.0...v8.8.0) (2019-01-29)
+
+
+### :bug: Bug Fixes
+
+* **index:** fix flat path based key issue ([bed9c39](https://github.com/kazupon/vue-i18n/commit/bed9c39)), closes [#349](https://github.com/kazupon/vue-i18n/issues/349)
+* **mixin:** fix beforeDestroy can not find this.$t ([#500](https://github.com/kazupon/vue-i18n/issues/500)) by [@masongzhi](https://github.com/masongzhi) ([311b8f3](https://github.com/kazupon/vue-i18n/commit/311b8f3)), closes [#500](https://github.com/kazupon/vue-i18n/issues/500)
+
+
+### :zap: Improvements
+
+* **directive:** Fix typo on warning message ([#509](https://github.com/kazupon/vue-i18n/issues/509)) by [@kimuraz](https://github.com/kimuraz) ([e879024](https://github.com/kazupon/vue-i18n/commit/e879024)), closes [#509](https://github.com/kazupon/vue-i18n/issues/509)
+* **index:** silence fallback warnings ([#510](https://github.com/kazupon/vue-i18n/issues/510)) by [@SzNagyMisu](https://github.com/SzNagyMisu) ([ddc0c79](https://github.com/kazupon/vue-i18n/commit/ddc0c79)), closes [#510](https://github.com/kazupon/vue-i18n/issues/510) [#139](https://github.com/kazupon/vue-i18n/issues/139)
+
+
+
+<a name="8.7.0"></a>
+# [8.7.0](https://github.com/kazupon/vue-i18n/compare/v8.6.0...v8.7.0) (2019-01-02)
+
+
+### :zap: Improvements
+
+* **directive:** Preserve directive content ([#495](https://github.com/kazupon/vue-i18n/issues/495)) by [@bponomarenko](https://github.com/bponomarenko) ([c29edba](https://github.com/kazupon/vue-i18n/commit/c29edba)), closes [#495](https://github.com/kazupon/vue-i18n/issues/495) [#408](https://github.com/kazupon/vue-i18n/issues/408) [#408](https://github.com/kazupon/vue-i18n/issues/408)
+
+
+
+<a name="8.6.0"></a>
+# [8.6.0](https://github.com/kazupon/vue-i18n/compare/v8.5.0...v8.6.0) (2018-12-25)
+
+
+### :bug: Bug Fixes
+
+* **pluralization:** inherit pluralization rules ⚠ ([#493](https://github.com/kazupon/vue-i18n/issues/493)) by [@Raiondesu](https://github.com/Raiondesu) ([7a23f32](https://github.com/kazupon/vue-i18n/commit/7a23f32)), closes [#493](https://github.com/kazupon/vue-i18n/issues/493)
+
+
+### :zap: Improvements
+
+* **format:** Add the path as argument to the custom formatter ([#489](https://github.com/kazupon/vue-i18n/issues/489)) by [@Raiondesu](https://github.com/Raiondesu) ([b9437ea](https://github.com/kazupon/vue-i18n/commit/b9437ea)), closes [#489](https://github.com/kazupon/vue-i18n/issues/489) [#484](https://github.com/kazupon/vue-i18n/issues/484) [#484](https://github.com/kazupon/vue-i18n/issues/484)
+
+
+
+<a name="8.5.0"></a>
+# [8.5.0](https://github.com/kazupon/vue-i18n/compare/v8.4.0...v8.5.0) (2018-12-17)
+
+
+### :bug: Bug Fixes
+
+* **index:** evaluate availabilities lazily (fix [#477](https://github.com/kazupon/vue-i18n/issues/477)) ([#483](https://github.com/kazupon/vue-i18n/issues/483)) by [@gamtiq](https://github.com/gamtiq) ([b66f02e](https://github.com/kazupon/vue-i18n/commit/b66f02e)), closes [#477](https://github.com/kazupon/vue-i18n/issues/477) [#483](https://github.com/kazupon/vue-i18n/issues/483)
+
+
+### :zap: Improvements
+
+* **index:** Allow pluralization customization via constructor options (closes [#464](https://github.com/kazupon/vue-i18n/issues/464)) ([#482](https://github.com/kazupon/vue-i18n/issues/482)) by [@Raiondesu](https://github.com/Raiondesu) ([ef4b1a6](https://github.com/kazupon/vue-i18n/commit/ef4b1a6)), closes [#464](https://github.com/kazupon/vue-i18n/issues/464) [#482](https://github.com/kazupon/vue-i18n/issues/482) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#451](https://github.com/kazupon/vue-i18n/issues/451)
+* **index:** make silentTranslationWarn work for dates and numbers too ([#481](https://github.com/kazupon/vue-i18n/issues/481)) by [@Raiondesu](https://github.com/Raiondesu) ([402092b](https://github.com/kazupon/vue-i18n/commit/402092b)), closes [#481](https://github.com/kazupon/vue-i18n/issues/481)
+* **types:** typed autocomplete in date and number format options ([#485](https://github.com/kazupon/vue-i18n/issues/485)) by [@Raiondesu](https://github.com/Raiondesu) ([e2e5993](https://github.com/kazupon/vue-i18n/commit/e2e5993)), closes [#485](https://github.com/kazupon/vue-i18n/issues/485)
+
+
+
+<a name="8.4.0"></a>
+# [8.4.0](https://github.com/kazupon/vue-i18n/compare/v8.3.2...v8.4.0) (2018-11-30)
+
+
+### :star: New Features
+
+* **index:** Add linked message formatting ([#467](https://github.com/kazupon/vue-i18n/issues/467)) by [@exoego](https://github.com/exoego) ([776b81b](https://github.com/kazupon/vue-i18n/commit/776b81b)), closes [#467](https://github.com/kazupon/vue-i18n/issues/467)
+
+
+
+<a name="8.3.2"></a>
+## [8.3.2](https://github.com/kazupon/vue-i18n/compare/v8.3.1...v8.3.2) (2018-11-16)
+
+
+### :chart_with_upwards_trend: Performance Fixes
+
+* **index:** Optimize unnecessary capturing. ([#462](https://github.com/kazupon/vue-i18n/issues/462)) by [@exoego](https://github.com/exoego) ([116845e](https://github.com/kazupon/vue-i18n/commit/116845e)), closes [#462](https://github.com/kazupon/vue-i18n/issues/462)
+
+
+
+<a name="8.3.1"></a>
+## [8.3.1](https://github.com/kazupon/vue-i18n/compare/v8.3.0...v8.3.1) (2018-11-08)
+
+
+### :bug: Bug Fixes
+
+* **directive:** fix cannnot update with v-t when had been changed locale message ([4895a2e](https://github.com/kazupon/vue-i18n/commit/4895a2e)), closes [#450](https://github.com/kazupon/vue-i18n/issues/450)
+* **index:** fix merge bug ([1798490](https://github.com/kazupon/vue-i18n/commit/1798490)), closes [#458](https://github.com/kazupon/vue-i18n/issues/458)
+* **missing:** fix vm argument passing ([dc48099](https://github.com/kazupon/vue-i18n/commit/dc48099)), closes [#453](https://github.com/kazupon/vue-i18n/issues/453)
+
+
+### :zap: Improvements
+
+* Optimize path.js and format.js ([#456](https://github.com/kazupon/vue-i18n/issues/456)) by [@exoego](https://github.com/exoego) ([639453c](https://github.com/kazupon/vue-i18n/commit/639453c)), closes [#456](https://github.com/kazupon/vue-i18n/issues/456)
+
+
+
+<a name="8.3.0"></a>
+# [8.3.0](https://github.com/kazupon/vue-i18n/compare/v8.2.1...v8.3.0) (2018-10-29)
+
+
+### :zap: Improvements
+
+* **pluralization:** Extendable pluralization by [@Raiondesu](https://github.com/Raiondesu) ([bbab90b](https://github.com/kazupon/vue-i18n/commit/bbab90b))
+
+
+
+<a name="8.2.1"></a>
+## [8.2.1](https://github.com/kazupon/vue-i18n/compare/v8.2.0...v8.2.1) (2018-10-15)
+
+
+### :bug: Bug Fixes
+
+* **extend:** fix TypeError: Cannot redefine property: $i18n ([#422](https://github.com/kazupon/vue-i18n/issues/422)) by [@HadiChen](https://github.com/HadiChen) ([cb19082](https://github.com/kazupon/vue-i18n/commit/cb19082)), closes [#422](https://github.com/kazupon/vue-i18n/issues/422)
+
+
+### :zap: Improvements
+
+* **index:** Suppress some warnings in production: smaller min.js and performance gain. ([#441](https://github.com/kazupon/vue-i18n/issues/441)) by @	exoego ([43931f5](https://github.com/kazupon/vue-i18n/commit/43931f5)), closes [#441](https://github.com/kazupon/vue-i18n/issues/441)
+
+
+
+<a name="8.2.0"></a>
+# [8.2.0](https://github.com/kazupon/vue-i18n/compare/v8.1.1...v8.2.0) (2018-10-13)
+
+
+### :bug: Bug Fixes
+
+* **index:** Add warning for circular reference in linked message ([#438](https://github.com/kazupon/vue-i18n/issues/438)) by [@exoego](https://github.com/exoego) ([7583485](https://github.com/kazupon/vue-i18n/commit/7583485)), closes [#438](https://github.com/kazupon/vue-i18n/issues/438)
+
+
+### :zap: Improvements
+
+* **index:** Allow escaping link key like @:(foo.bar). ([#437](https://github.com/kazupon/vue-i18n/issues/437)) by [@exoego](https://github.com/exoego) ([acfc458](https://github.com/kazupon/vue-i18n/commit/acfc458)), closes [#437](https://github.com/kazupon/vue-i18n/issues/437)
+* **index:** Pre-defined named arguments for Pluraization ([#440](https://github.com/kazupon/vue-i18n/issues/440)) by [@exoego](https://github.com/exoego) ([e84f0fb](https://github.com/kazupon/vue-i18n/commit/e84f0fb)), closes [#440](https://github.com/kazupon/vue-i18n/issues/440)
+* **path:** Allow non-ascii chars including numbers. ([#436](https://github.com/kazupon/vue-i18n/issues/436)) by [@exoego](https://github.com/exoego) ([a556c58](https://github.com/kazupon/vue-i18n/commit/a556c58)), closes [#436](https://github.com/kazupon/vue-i18n/issues/436)
+
+
+
+<a name="8.1.1"></a>
+## [8.1.1](https://github.com/kazupon/vue-i18n/compare/v8.1.0...v8.1.1) (2018-10-12)
+
+
+### :bug: Bug Fixes
+
+* **build:** fix rollup building issues ([1a1958a](https://github.com/kazupon/vue-i18n/commit/1a1958a))
+* **format:** Should warn as unknown if named format is not closed. ([#435](https://github.com/kazupon/vue-i18n/issues/435)) by [@exoego](https://github.com/exoego) ([d1f6ed0](https://github.com/kazupon/vue-i18n/commit/d1f6ed0)), closes [#435](https://github.com/kazupon/vue-i18n/issues/435)
+* **install:** fix cannot redfine error ([6d5ec61](https://github.com/kazupon/vue-i18n/commit/6d5ec61))
+
+
+### :zap: Improvements
+
+* **package.json:** tree shaking optimization ([38948c5](https://github.com/kazupon/vue-i18n/commit/38948c5))
+
+
+
+<a name="8.1.0"></a>
+# [8.1.0](https://github.com/kazupon/vue-i18n/compare/v8.0.0...v8.1.0) (2018-09-03)
+
+
+### :bug: Bug Fixes
+
+* **install:** add support for Vue.extend vue-i18n instance ([#420](https://github.com/kazupon/vue-i18n/issues/420)) by [@jaredzhu1993](https://github.com/jaredzhu1993) ([a60ea8b](https://github.com/kazupon/vue-i18n/commit/a60ea8b)), closes [#420](https://github.com/kazupon/vue-i18n/issues/420)
+
+
+### :zap: Improvements
+
+* **warnings:** make warning messages clearer ([#396](https://github.com/kazupon/vue-i18n/issues/396)) by [@kimuraz](https://github.com/kimuraz) ([79eee1b](https://github.com/kazupon/vue-i18n/commit/79eee1b)), closes [#396](https://github.com/kazupon/vue-i18n/issues/396)
+
+
+
+<a name="8.0.0"></a>
+# [8.0.0](https://github.com/kazupon/vue-i18n/compare/v7.8.1...v8.0.0) (2018-06-23)
+
+
+### :boom: Breaking changes
+
+* **extend:** fix this context binding ([aa0e831](https://github.com/kazupon/vue-i18n/commit/aa0e831)), closes [#306](https://github.com/kazupon/vue-i18n/issues/306) [#286](https://github.com/kazupon/vue-i18n/issues/286) [#259](https://github.com/kazupon/vue-i18n/issues/259), revert [#260](https://github.com/kazupon/vue-i18n/issues/260)
+
+Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in `data` options, `const $t = this.$t.bind(this)`).
+
+```js
+export default {
+  data () {
+    const $t = this.$t.bind(this)
+    return { msg: $t('msg') }
+  }
+}
+```
+
+see the [API docs](https://kazupon.github.io/vue-i18n/api/)
+
+### :bug: Bug Fixes
+
+* bug(directive): fix guard checking at unbind ([c74888c](https://github.com/kazupon/vue-i18n/commit/c74888c)), closes [#340](https://github.com/kazupon/vue-i18n/issues/340)
+
+
+### NOTE
+
+* extend:
+
+
+
+<a name="7.8.1"></a>
+## [7.8.1](https://github.com/kazupon/vue-i18n/compare/v7.8.0...v7.8.1) (2018-06-18)
+
+
+### :bug: Bug Fixes
+
+* **directive:** fix cannot unbind bug ([105888d](https://github.com/kazupon/vue-i18n/commit/105888d)), closes [#377](https://github.com/kazupon/vue-i18n/issues/377)
+
+
+
+<a name="7.8.0"></a>
+# [7.8.0](https://github.com/kazupon/vue-i18n/compare/v7.7.0...v7.8.0) (2018-06-01)
+
+
+### :zap: Improvements
+
+* **typescript:** add type exportings ([a7cb8da](https://github.com/kazupon/vue-i18n/commit/a7cb8da))
+
+
+
+<a name="7.7.0"></a>
+# [7.7.0](https://github.com/kazupon/vue-i18n/compare/v7.6.0...v7.7.0) (2018-05-20)
+
+
+### :zap: Improvements
+
+* **index:** resource reactivity ([887a137](https://github.com/kazupon/vue-i18n/commit/887a137)), closes [#253](https://github.com/kazupon/vue-i18n/issues/253)
+* **typescript:** Fix typings in components ([#344](https://github.com/kazupon/vue-i18n/issues/344)) by [@Demivan](https://github.com/Demivan) ([2402893](https://github.com/kazupon/vue-i18n/commit/2402893)), closes [#344](https://github.com/kazupon/vue-i18n/issues/344)
+
+
+
+<a name="7.6.0"></a>
+# [7.6.0](https://github.com/kazupon/vue-i18n/compare/v7.5.0...v7.6.0) (2018-03-13)
+
+
+### :zap: Improvements
+
+* **index:** support retunable missing handler ([#256](https://github.com/kazupon/vue-i18n/issues/256)) by [@houd1ni](https://github.com/houd1ni) ([9fbe467](https://github.com/kazupon/vue-i18n/commit/9fbe467))
+* **typescript:** update TranslateResult type interface ([dffc678](https://github.com/kazupon/vue-i18n/commit/dffc678))
+
+
+
+<a name="7.5.0"></a>
+# [7.5.0](https://github.com/kazupon/vue-i18n/compare/v7.4.2...v7.5.0) (2018-03-11)
+
+
+### :star: New Features
+
+* **directive:** Add pluralization feature to directive ([#304](https://github.com/kazupon/vue-i18n/issues/304)) by [@SirLamer](https://github.com/SirLamer) ([8378859](https://github.com/kazupon/vue-i18n/commit/8378859))
+
+
+### :zap: Improvements
+
+* **flow:** update TranslateResult type interface ([59f4658](https://github.com/kazupon/vue-i18n/commit/59f4658))
+* **index:** support object localization ([#311](https://github.com/kazupon/vue-i18n/issues/311)) by [@manniL](https://github.com/manniL) ([99e5006](https://github.com/kazupon/vue-i18n/commit/99e5006))
+* **missing:** Add interpolation values to missing handler ([#308](https://github.com/kazupon/vue-i18n/issues/308)) by [@sebwas](https://github.com/sebwas) ([b912d8a](https://github.com/kazupon/vue-i18n/commit/b912d8a))
+* **numberformat:** Explicit number format options ([#305](https://github.com/kazupon/vue-i18n/issues/305)) by [@bponomarenko](https://github.com/bponomarenko) ([aa07450](https://github.com/kazupon/vue-i18n/commit/aa07450))
+
+
+
+<a name="7.4.2"></a>
+## [7.4.2](https://github.com/kazupon/vue-i18n/compare/v7.4.1...v7.4.2) (2018-02-01)
+
+
+### :zap: Improvements
+
+* **index:** Fixes global auto installation ([#291](https://github.com/kazupon/vue-i18n/issues/291)) by [@emileber](https://github.com/emileber) ([2f016ff](https://github.com/kazupon/vue-i18n/commit/2f016ff)), closes [#291](https://github.com/kazupon/vue-i18n/issues/291)
+
+
+
+<a name="7.4.1"></a>
+## [7.4.1](https://github.com/kazupon/vue-i18n/compare/v7.4.0...v7.4.1) (2018-01-25)
+
+
+### :bug: Bug Fixes
+
+* fix cannot react ([2a8ea1c](https://github.com/kazupon/vue-i18n/commit/2a8ea1c)), closes [#261](https://github.com/kazupon/vue-i18n/issues/261)
+
+
+### :zap: Improvements
+
+* **formatter:** interpolate messages without values ([#282](https://github.com/kazupon/vue-i18n/issues/282)) by [@cb8](https://github.com/cb8) ([b792ce2](https://github.com/kazupon/vue-i18n/commit/b792ce2))
+
+
+
+<a name="7.4.0"></a>
+# [7.4.0](https://github.com/kazupon/vue-i18n/compare/v7.3.4...v7.4.0) (2018-01-10)
+
+
+### :star: New Features
+
+* **typescript:** Allow module augmentation ([#273](https://github.com/kazupon/vue-i18n/issues/273)) by [@CKGrafico](https://github.com/CKGrafico) ([4371344](https://github.com/kazupon/vue-i18n/commit/4371344))
+
+
+
+<a name="7.3.4"></a>
+## [7.3.4](https://github.com/kazupon/vue-i18n/compare/v7.3.3...v7.3.4) (2018-01-07)
+
+
+### :bug: Bug Fixes
+
+* **formatter:** Inherit formatter ([#269](https://github.com/kazupon/vue-i18n/issues/269)) by [@podkot](https://github.com/podkot) ([26a33ad](https://github.com/kazupon/vue-i18n/commit/26a33ad))
+
+
+
+<a name="7.3.3"></a>
+## [7.3.3](https://github.com/kazupon/vue-i18n/compare/v7.3.2...v7.3.3) (2017-12-19)
+
+
+### :bug: Bug Fixes
+
+* **extend:** Fix this not found [#259](https://github.com/kazupon/vue-i18n/issues/259) ([#260](https://github.com/kazupon/vue-i18n/issues/260)) by [@lzxb](https://github.com/lzxb) ([c29007e](https://github.com/kazupon/vue-i18n/commit/c29007e)), closes [#259](https://github.com/kazupon/vue-i18n/issues/259) [#260](https://github.com/kazupon/vue-i18n/issues/260)
+* **types:** fix using old export ([#263](https://github.com/kazupon/vue-i18n/issues/263)) by [@jmigual](https://github.com/jmigual) ([b295fee](https://github.com/kazupon/vue-i18n/commit/b295fee)), closes [#263](https://github.com/kazupon/vue-i18n/issues/263)
+
+
+<a name="7.3.2"></a>
+## [7.3.2](https://github.com/kazupon/vue-i18n/compare/v7.3.1...v7.3.2) (2017-10-19)
+
+
+### :zap: Improvements
+
+* **typescript:** fix import problem of vue2.5 because of the types update ([#238](https://github.com/kazupon/vue-i18n/issues/238)) by [@peterchealse](https://github.com/peterchealse) ([cb98347](https://github.com/kazupon/vue-i18n/commit/cb98347)), closes [#238](https://github.com/kazupon/vue-i18n/issues/238)
+
+
+
+<a name="7.3.1"></a>
+## [7.3.1](https://github.com/kazupon/vue-i18n/compare/v7.3.0...v7.3.1) (2017-10-04)
+
+
+### :bug: Bug Fixes
+
+* **directive:** fix cannot locale reactivity ([e1fc12e](https://github.com/kazupon/vue-i18n/commit/e1fc12e)), closes [#227](https://github.com/kazupon/vue-i18n/issues/227)
+
+
+
+<a name="7.3.0"></a>
+# [7.3.0](https://github.com/kazupon/vue-i18n/compare/v7.2.0...v7.3.0) (2017-09-22)
+
+
+### :star: New Features
+
+* **directives:** support v-t custom directive (welcome back!) ([af9a2e7](https://github.com/kazupon/vue-i18n/commit/af9a2e7))
+
+
+### :up: Updates
+
+* **typing:** fix flowtype ([fa06f44](https://github.com/kazupon/vue-i18n/commit/fa06f44))
+
+
+
+<a name="7.2.0"></a>
+# [7.2.0](https://github.com/kazupon/vue-i18n/compare/v7.1.2...v7.2.0) (2017-08-28)
+
+
+### :star: New Features
+
+* **interpolation:** list formatting refactor and places/place feature ([#218](https://github.com/kazupon/vue-i18n/issues/218)) by [@myst729](https://github.com/myst729) ([0f0f3ff](https://github.com/kazupon/vue-i18n/commit/0f0f3ff))
+
+
+
+<a name="7.1.2"></a>
+## [7.1.2](https://github.com/kazupon/vue-i18n/compare/v7.1.1...v7.1.2) (2017-08-25)
+
+
+### :zap: Improvements
+
+* **interpolation:** skip non-element VNode in interpolation ([#211](https://github.com/kazupon/vue-i18n/issues/211)) by [@myst729](https://github.com/myst729) ([6be1756](https://github.com/kazupon/vue-i18n/commit/6be1756))
+
+
+
+<a name="7.1.1"></a>
+## [7.1.1](https://github.com/kazupon/vue-i18n/compare/v7.1.0...v7.1.1) (2017-08-03)
+
+
+### :bug: Bug Fixes
+
+* **mixin:** fix cannot setup VueI18n instance ([13585a4](https://github.com/kazupon/vue-i18n/commit/13585a4)), closes [#203](https://github.com/kazupon/vue-i18n/issues/203)
+
+
+
+<a name="7.1.0"></a>
+# [7.1.0](https://github.com/kazupon/vue-i18n/compare/v7.0.5...v7.1.0) (2017-07-30)
+
+
+### :zap: Improvements
+
+* **custom-block:** support multiple custom blocks ([ab955a5](https://github.com/kazupon/vue-i18n/commit/ab955a5)), closes [#189](https://github.com/kazupon/vue-i18n/issues/189)
+
+
+
+<a name="7.0.5"></a>
+## [7.0.5](https://github.com/kazupon/vue-i18n/compare/v7.0.4...v7.0.5) (2017-07-08)
+
+
+### :bug: Bug Fixes
+
+* **format:** fix cannot collectly parse percent ([fc71eda](https://github.com/kazupon/vue-i18n/commit/fc71eda)), closes [#191](https://github.com/kazupon/vue-i18n/issues/191)
+
+
+
+<a name="7.0.4"></a>
+## [7.0.4](https://github.com/kazupon/vue-i18n/compare/v7.0.3...v7.0.4) (2017-07-01)
+
+
+### :bug: Bug Fixes
+
+* **link:** fix ie traverse custom Array.prototype method ([#188](https://github.com/kazupon/vue-i18n/issues/188)) by [@632781460](https://github.com/632781460) ([d3b308b](https://github.com/kazupon/vue-i18n/commit/d3b308b)), closes [#188](https://github.com/kazupon/vue-i18n/issues/188)
+
+
+### :chart_with_upwards_trend: Performance Fixes
+
+* fix blocking at beforeDestroy ([570b215](https://github.com/kazupon/vue-i18n/commit/570b215)), closes [#187](https://github.com/kazupon/vue-i18n/issues/187)
+
+
+
+<a name="7.0.3"></a>
+## [7.0.3](https://github.com/kazupon/vue-i18n/compare/v7.0.2...v7.0.3) (2017-06-13)
+
+
+### :bug: Bug Fixes
+
+* **fallback:** fix cannot fallabck localization ([694e6f2](https://github.com/kazupon/vue-i18n/commit/694e6f2)), closes [#176](https://github.com/kazupon/vue-i18n/issues/176)
+* **fallback:** fix fallback locale issue ([d9ceddc](https://github.com/kazupon/vue-i18n/commit/d9ceddc)), closes [#174](https://github.com/kazupon/vue-i18n/issues/174)
+* **linked:** fix cannot fallback linked localization ([0c572f3](https://github.com/kazupon/vue-i18n/commit/0c572f3)), closes [#172](https://github.com/kazupon/vue-i18n/issues/172)
+
+
+
+<a name="7.0.2"></a>
+## [7.0.2](https://github.com/kazupon/vue-i18n/compare/v7.0.1...v7.0.2) (2017-06-10)
+
+
+### :bug: Bug Fixes
+
+* **sfc:** fix cannot parse custom block locale messages ([32eb3a7](https://github.com/kazupon/vue-i18n/commit/32eb3a7)), closes [#173](https://github.com/kazupon/vue-i18n/issues/173)
+
+
+
+<a name="7.0.1"></a>
+## [7.0.1](https://github.com/kazupon/vue-i18n/compare/v7.0.0...v7.0.1) (2017-06-04)
+
+
+### :bug: Bug Fixes
+
+* fix cannat single file component translation ([687d406](https://github.com/kazupon/vue-i18n/commit/687d406)), closes [#169](https://github.com/kazupon/vue-i18n/issues/169)
+* fix cannnot resolve linked localization with component interpolation ([c973619](https://github.com/kazupon/vue-i18n/commit/c973619)), closes [#171](https://github.com/kazupon/vue-i18n/issues/171)
+* fix datetime and number fallback localization ([be9e1bd](https://github.com/kazupon/vue-i18n/commit/be9e1bd)), closes [#168](https://github.com/kazupon/vue-i18n/issues/168)
+* fix linked translation with using hyphen or underscore keypath ([6e9f151](https://github.com/kazupon/vue-i18n/commit/6e9f151)), closes [#170](https://github.com/kazupon/vue-i18n/issues/170)
+
+
+<a name="7.0.0"></a>
+# [7.0.0](https://github.com/kazupon/vue-i18n/compare/v7.0.0-rc.1...v7.0.0) (2017-05-29)
+
+:tada: :tada: :tada:
+
+See the [docs](https://kazupon.github.io/vue-i18n/en/)
+
+
+### :star: New Features
+
+* **datetime localization:**
+    * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/datetime.md)
+    * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/datetime)
+* **number localization:**
+    * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/number.md)
+    * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/number)
+* **component interpolation:**
+    * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/interpolation.md)
+    * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation)
+* **typescript:**
+    * [type definitions](https://github.com/kazupon/vue-i18n/blob/dev/types/index.d.ts)
+
+
+### :chart_with_upwards_trend: Performance Fixes
+
+* fix translation performance issue ([6032a51](https://github.com/kazupon/vue-i18n/commit/6032a51))
+
+
+### :zap: Improvements
+
+* **path:** tweak for ssr
+
+
+### :boom: Breaking changes
+
+* **format:** re-impelement formatter
+* **formatter:** change method nam
+* **flowtype:** fix locale message related type changing and remove underscore type
+
+
+### :bug: Bug Fixes
+
+* **examples:** fix ssr demo ([059034f](https://github.com/kazupon/vue-i18n/commit/059034f))
+* **pluralization:** fix default choice ([240cfed](https://github.com/kazupon/vue-i18n/commit/240cfed))
+
+
+<a name="7.0.0-rc.1"></a>
+# [7.0.0-rc.1](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.4...v7.0.0-rc.1) (2017-05-26)
+
+
+### :chart_with_upwards_trend: Performance Fixes
+
+* fix translation performance issue ([6032a51](https://github.com/kazupon/vue-i18n/commit/6032a51)), closes [#165](https://github.com/kazupon/vue-i18n/issues/165)
+
+
+### :up: Updates
+
+* **flowtype:** remove unneccesary type ([eb60156](https://github.com/kazupon/vue-i18n/commit/eb60156))
+
+
+
+<a name="7.0.0-beta.4"></a>
+# [7.0.0-beta.4](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.3...v7.0.0-beta.4) (2017-05-23)
+
+
+### :bug: Bug Fixes
+
+* **pluralization:** fix default choice ([240cfed](https://github.com/kazupon/vue-i18n/commit/240cfed)), closes [#164](https://github.com/kazupon/vue-i18n/issues/164)
+
+
+
+<a name="7.0.0-beta.3"></a>
+# [7.0.0-beta.3](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.2...v7.0.0-beta.3) (2017-05-15)
+
+### :up: Updates
+
+* bring back from bug fix ([95be4ea](https://github.com/kazupon/vue-i18n/commit/95be4ea))
+
+
+<a name="7.0.0-beta.2"></a>
+# [7.0.0-beta.2](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.1...v7.0.0-beta.2) (2017-05-14)
+
+
+### :zap: Improvements
+
+* **path:** tweak for ssr ([eb21921](https://github.com/kazupon/vue-i18n/commit/eb21921))
+* **typescript:** change custom formatter method name ([c5f043f](https://github.com/kazupon/vue-i18n/commit/c5f043f))
+
+
+
+<a name="7.0.0-beta.1"></a>
+# [7.0.0-beta.1](https://github.com/kazupon/vue-i18n/compare/v6.1.1...v7.0.0-beta.1) (2017-05-11)
+
+### :star: New Features
+
+* **datetime localization:** add datetime localization ([3282075](https://github.com/kazupon/vue-i18n/commit/3282075))
+    * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/datetime.md)
+    * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/datetime)
+* **number localization:** add number localization ([87ee7b3](https://github.com/kazupon/vue-i18n/commit/87ee7b3))
+    * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/number.md)
+    * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/number)
+* **component interpolation:** ([23f7d34](https://github.com/kazupon/vue-i18n/commit/23f7d34)), closes [#145](https://github.com/kazupon/vue-i18n/issues/145) [#144](https://github.com/kazupon/vue-i18n/issues/144) [#37](https://github.com/kazupon/vue-i18n/issues/37)
+    * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/interpolation.md)
+    * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation)
+* **typescript:** add TypeScript type definitions ([#161](https://github.com/kazupon/vue-i18n/issues/161)) by [@aicest](https://github.com/aicest) ([61cebca](https://github.com/kazupon/vue-i18n/commit/61cebca))
+    * [type definitions](https://github.com/kazupon/vue-i18n/blob/dev/types/index.d.ts)
+
+
+### :boom: Breaking changes
+
+* **format:** re-impelement formatter ([a8c046d](https://github.com/kazupon/vue-i18n/commit/a8c046d))
+* **formatter:** change method name ([6eed51c](https://github.com/kazupon/vue-i18n/commit/6eed51c))
+* **flowtype:** fix locale message related type changing ([c30d576](https://github.com/kazupon/vue-i18n/commit/c30d576))
+
+
+### :bug: Bug Fixes
+
+* **examples:** fix ssr demo ([059034f](https://github.com/kazupon/vue-i18n/commit/059034f)), closes [#151](https://github.com/kazupon/vue-i18n/issues/151)
+
+
+<a name="6.1.3"></a>
+## [6.1.3](https://github.com/kazupon/vue-i18n/compare/v6.1.1...v6.1.3) (2017-05-15)
+
+
+### :bug: Bug Fixes
+
+* fix memory leaks ([95be4ea](https://github.com/kazupon/vue-i18n/commit/95be4ea)), closes [#162](https://github.com/kazupon/vue-i18n/issues/162)
+
+
+
+<a name="6.1.2"></a>
+## [6.1.2](https://github.com/kazupon/vue-i18n/compare/v6.1.1...v6.1.2) (2017-05-15)
+
+
+<a name="6.1.1"></a>
+## [6.1.1](https://github.com/kazupon/vue-i18n/compare/v6.1.0...v6.1.1) (2017-04-19)
+
+
+### :bug: Bug Fixes
+
+* **te:** Fix `te()` that always uses `this.locale`, even when `locale` supplied ([#147](https://github.com/kazupon/vue-i18n/issues/147)) by [@aicest](https://github.com/aicest) ([bf15eeb](https://github.com/kazupon/vue-i18n/commit/bf15eeb)), closes [#147](https://github.com/kazupon/vue-i18n/issues/147)
+
+
+
+<a name="6.1.0"></a>
+# [6.1.0](https://github.com/kazupon/vue-i18n/compare/v6.0.0...v6.1.0) (2017-04-14)
+
+
+### :star: New Features
+
+* **api:** add 'mergeLocaleMessage' method ([ef21621](https://github.com/kazupon/vue-i18n/commit/ef21621)), closes [#131](https://github.com/kazupon/vue-i18n/issues/131)
+* **silent:** add silent translation missing option ([29b3a17](https://github.com/kazupon/vue-i18n/commit/29b3a17)), closes [#139](https://github.com/kazupon/vue-i18n/issues/139)
+
+
+### :zap: Improvements
+
+* change to method from computed property ([9135a59](https://github.com/kazupon/vue-i18n/commit/9135a59)), closes [#141](https://github.com/kazupon/vue-i18n/issues/141)
+
+
+
+<a name="6.0.0"></a>
+# [6.0.0](https://github.com/kazupon/vue-i18n/compare/v6.0.0-beta.1...v6.0.0) (2017-04-05)
+
+:tada: :tada: :tada:
+
+See the [docs](https://kazupon.github.io/vue-i18n/en/)
+
+### :zap: Improvements
+
+- Server-Side Rendering: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/ssr)
+- Custom formatter: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom)
+
+
+### :star: NEW Features
+
+- Single File Components: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc)
+
+
+### :boom: Breaking changes
+
+- API
+- Dynamic locale <sup>DEPRECATED</sup>
+
+
+<a name="6.0.0-beta.1"></a>
+# [6.0.0-beta.1](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.6...v6.0.0-beta.1) (2017-03-22)
+
+
+### :boom: Breaking changes
+
+* change `fallbackRoot` and `sync` option default `true` value ([0890b44](https://github.com/kazupon/vue-i18n/commit/0890b44))
+* remove messages settter, and add getLocaleMessage API ([0f0914d](https://github.com/kazupon/vue-i18n/commit/0f0914d))
+
+
+### :bug: Bug Fixes
+
+* **mixin:** fix computed props errors ([a6b7e37](https://github.com/kazupon/vue-i18n/commit/a6b7e37))
+
+
+### :up: Updates
+
+* **flowtype:** argument names ([cf14425](https://github.com/kazupon/vue-i18n/commit/cf14425))
+
+
+### :zap: Improvements
+
+* **fallbackLocale:** support reactivity ([ed758be](https://github.com/kazupon/vue-i18n/commit/ed758be))
+* **warn:** suppress warning messages for production ([6e417d2](https://github.com/kazupon/vue-i18n/commit/6e417d2))
+
+
+
+<a name="6.0.0-alpha.6"></a>
+# [6.0.0-alpha.6](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.5...v6.0.0-alpha.6) (2017-03-16)
+
+
+### :star: New Features
+
+* add 'setLocaleMessage' API ([8b71eda](https://github.com/kazupon/vue-i18n/commit/8b71eda))
+
+
+
+<a name="6.0.0-alpha.5"></a>
+# [6.0.0-alpha.5](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.3...v6.0.0-alpha.5) (2017-03-11)
+
+
+### :bug: Bug Fixes
+
+* **mixin:** fix cannot create VueI18n instance error for minify production ([7eeb29f](https://github.com/kazupon/vue-i18n/commit/7eeb29f))
+
+
+
+<a name="6.0.0-alpha.4"></a>
+# [6.0.0-alpha.4](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.3...v6.0.0-alpha.4) (2017-03-11)
+
+
+
+<a name="6.0.0-alpha.3"></a>
+# [6.0.0-alpha.3](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.2...v6.0.0-alpha.3) (2017-03-08)
+
+
+### :star: New Features
+
+* add `sync` option ([5c46c07](https://github.com/kazupon/vue-i18n/commit/5c46c07))
+
+
+### :zap: Improvements
+
+* **mixin:** add error throwings and a warning ([0e4ac39](https://github.com/kazupon/vue-i18n/commit/0e4ac39))
+
+
+
+<a name="6.0.0-alpha.2"></a>
+# [6.0.0-alpha.2](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.1...v6.0.0-alpha.2) (2017-02-27)
+
+
+### :zap: Improvements
+
+* **mixin:** release i18n instance ([cc362a3](https://github.com/kazupon/vue-i18n/commit/cc362a3))
+* **vue:** support vue 2.2 ([5e7bf5e](https://github.com/kazupon/vue-i18n/commit/5e7bf5e))
+
+
+
+<a name="6.0.0-alpha.1"></a>
+# [6.0.0-alpha.1](https://github.com/kazupon/vue-i18n/compare/v5.0.2...v6.0.0-alpha.1) (2017-02-23)
+
+This is the first release of 6.0.
+In this version, we are some big breaking changes.
+
+- Recommended for: experiments, prototypes, upgrading small, non-critical apps
+- **NOT** recommended for: production use, upgrading production apps
+
+:warning: Documentation still needs to be worked on. And also, we might change some APIs and features.
+
+In the examples, please refer to this [examples](https://github.com/kazupon/vue-i18n/tree/dev/examples) directory.
+
+
+## Improvements
+- Server-Side Rendering: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom)
+- Custom formatter: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/ssr)
+
+## Features
+- Formatting <sup>support</sup>
+- Pluralization <sup>support</sup>
+- Locale and KeyPath Syntax <sup>support</sup>
+- Linked translation <sup>support</sup>
+- Fallback translation <sup>support</sup>
+- Component locale <sup>support</sup>
+- Dynamic locale <sup>DEPRECATED</sup>
+- Hot reload <sup>support</sup>
+
+## API
+
+### Global Config
+- Vue.config.lang <sup>DEPRECATED, use VueI18n constructor `locale` option, or VueI18n#locale</sup>
+- Vue.config.fallbackLang <sup>DEPRECATED, use VueI18n constructor `fallbackLocale` option, or VueI18n#fallbackLocale</sup>
+- Vue.config.missingHandler <sup>DEPRECATED, use VueI18n constructor `missing` option, or VueI18n#missing</sup>
+- Vue.config.i18nFormatter <sup>DEPRECATED, use VueI18n constructor `formatter` option, or VueI18n#formatter</sup>
+
+### Global Method
+- Vue.locale <sup>DEPRECATED, use VueI18n constructor `messages` option, or VueI18n#messages</sup>
+- Vue.t <sup>DEPRECATED, use VueI18n#t</sup>
+- Vue.tc <sup>DEPRECATED, use VueI18n#tc</sup>
+- Vue.te <sup>DEPRECATED, use VueI18n#te</sup>
+
+### Constructor Options
+- locales <sup>DEPRECATED, use `messages` of `i18n` option (e.g `{ i18n: { messaes: ... } }`)</sup>
+
+### Instance Properties
+- $lang <sup>DEPRECATED, use `locale` of Vue instance property `$i18n` (e.g `vm.$i18n.locale = 'en'`)
+
+### VueI18n class <sup>NEW</sup>
+- constructor options: See the [`I18nOptions` type](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L7-L15) of flowtype.
+- methods / properties: See the [`I18n` interface definition](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L17-L33) of flowtype.
+
+
+<a name="5.0.2"></a>
+## [5.0.2](https://github.com/kazupon/vue-i18n/compare/v5.0.1...v5.0.2) (2017-02-18)
+
+
+### :zap: Improvements
+
+* **npm:** revert node >= 6.0 engine restriction ([#110](https://github.com/kazupon/vue-i18n/issues/110)) by [@syxolk](https://github.com/syxolk) ([92b1bd1](https://github.com/kazupon/vue-i18n/commit/92b1bd1)), closes [#109](https://github.com/kazupon/vue-i18n/issues/109)
+
+
+
+<a name="5.0.1"></a>
+## [5.0.1](https://github.com/kazupon/vue-i18n/compare/v5.0.0...v5.0.1) (2017-02-16)
+
+
+### :zap: Improvements
+
+* **asset:** update locale reactivity setting ([b42fd9a](https://github.com/kazupon/vue-i18n/commit/b42fd9a))
+
+
+
+<a name="5.0.0"></a>
+# [5.0.0](https://github.com/kazupon/vue-i18n/compare/v4.10.0...v5.0.0) (2017-02-04)
+
+
+### :boom: Breaking changes
+
+* drop vue 1.0 supporting ([4da26cf](https://github.com/kazupon/vue-i18n/commit/4da26cf)), closes [#105](https://github.com/kazupon/vue-i18n/issues/105)
+
+
+
+<a name="4.10.0"></a>
+# [4.10.0](https://github.com/kazupon/vue-i18n/compare/v4.9.0...v4.10.0) (2017-01-01)
+
+
+### :star: New Features
+
+* `$lang` property for all component ([#99](https://github.com/kazupon/vue-i18n/issues/99)) by [@albert](https://github.com/albert)-zhang ([5ed69f8](https://github.com/kazupon/vue-i18n/commit/5ed69f8))
+
+
+### :up: Updates
+
+* **override:** change langVM keeping variable name ([3ec1bb2](https://github.com/kazupon/vue-i18n/commit/3ec1bb2))
+
+
+
+<a name="4.9.0"></a>
+# [4.9.0](https://github.com/kazupon/vue-i18n/compare/v4.8.0...v4.9.0) (2016-12-17)
+
+
+### :bug: Bug Fixes
+
+* **path:** fix nested key translation ([e15ead4](https://github.com/kazupon/vue-i18n/commit/e15ead4)), closes [#97](https://github.com/kazupon/vue-i18n/issues/97)
+
+
+### :star: New Features
+
+* add globally locale checking ([4cac8b9](https://github.com/kazupon/vue-i18n/commit/4cac8b9))
+* locale checking ([#98](https://github.com/kazupon/vue-i18n/issues/98)) by [@long](https://github.com/long)-long-float ([0bc0a6b](https://github.com/kazupon/vue-i18n/commit/0bc0a6b))
+
+
+<a name="4.8.0"></a>
+# [4.8.0](https://github.com/kazupon/vue-i18n/compare/v4.7.4...v4.8.0) (2016-12-08)
+
+
+### :zap: Improvements
+
+* **extend:** disable no translation warning when set missingHandler ([168a97c](https://github.com/kazupon/vue-i18n/commit/168a97c)), closes [#96](https://github.com/kazupon/vue-i18n/issues/96)
+
+
+
+<a name="4.7.4"></a>
+## [4.7.4](https://github.com/kazupon/vue-i18n/compare/v4.7.3...v4.7.4) (2016-11-29)
+
+
+### :bug: Bug Fixes
+
+* **extend:** fix interpolate error [@tariq86](https://github.com/tariq86) ([5f24e17](https://github.com/kazupon/vue-i18n/commit/5f24e17))
+
+
+
+<a name="4.7.3"></a>
+## [4.7.3](https://github.com/kazupon/vue-i18n/compare/v4.7.2...v4.7.3) (2016-11-24)
+
+
+### :bug: Bug Fixes
+
+* **extend:** fix array local ([35c268a](https://github.com/kazupon/vue-i18n/commit/35c268a)), closes [#91](https://github.com/kazupon/vue-i18n/issues/91) [#59](https://github.com/kazupon/vue-i18n/issues/59)
+
+
+
+<a name="4.7.2"></a>
+## [4.7.2](https://github.com/kazupon/vue-i18n/compare/v4.7.1...v4.7.2) (2016-11-19)
+
+
+### :bug: Bug Fixes
+
+* **observer:** fix dep undefined error ([#88](https://github.com/kazupon/vue-i18n/issues/88)) by [@fandaa](https://github.com/fandaa) ([724974e](https://github.com/kazupon/vue-i18n/commit/724974e)), closes [#88](https://github.com/kazupon/vue-i18n/issues/88)
+
+
+### :zap: Improvements
+
+* **extend:** support translate empty string ([#86](https://github.com/kazupon/vue-i18n/issues/86)) by [@QingWei](https://github.com/QingWei)-Li ([8e6d154](https://github.com/kazupon/vue-i18n/commit/8e6d154))
+
+
+
+<a name="4.7.1"></a>
+## [4.7.1](https://github.com/kazupon/vue-i18n/compare/v4.7.0...v4.7.1) (2016-10-29)
+
+
+### :bug: Bug Fixes
+
+* **interpolate:** named formatting: use name if value is missing ([#77](https://github.com/kazupon/vue-i18n/issues/77)) by [@SebastianS90](https://github.com/SebastianS90) ([a0cc343](https://github.com/kazupon/vue-i18n/commit/a0cc343))
+
+
+### :zap: Improvements
+
+* **named:** using default use nmae when value is missing ([c34e8f1](https://github.com/kazupon/vue-i18n/commit/c34e8f1))
+
+
+
+<a name="4.7.0"></a>
+# [4.7.0](https://github.com/kazupon/vue-i18n/compare/v4.6.0...v4.7.0) (2016-10-28)
+
+
+### :star: New Features
+
+* hot reloading ([#71](https://github.com/kazupon/vue-i18n/issues/71)) by [@gglnx](https://github.com/gglnx) ([7bb94ac](https://github.com/kazupon/vue-i18n/commit/7bb94ac))
+
+
+### :zap: Improvements
+
+* **pluralization:** zero choice ([#70](https://github.com/kazupon/vue-i18n/issues/70)) by [@sebwas](https://github.com/sebwas) ([5f0004f](https://github.com/kazupon/vue-i18n/commit/5f0004f))
+
+
+
+<a name="4.6.0"></a>
+# [4.6.0](https://github.com/kazupon/vue-i18n/compare/v4.5.0...v4.6.0) (2016-09-24)
+
+
+### :star: New Features
+
+* **config:** custom message formatter ([#57](https://github.com/kazupon/vue-i18n/issues/57)) by [@jvmccarthy](https://github.com/jvmccarthy) ([2748eb4](https://github.com/kazupon/vue-i18n/commit/2748eb4))
+
+
+
+<a name="4.5.0"></a>
+# [4.5.0](https://github.com/kazupon/vue-i18n/compare/v4.4.1...v4.5.0) (2016-09-15)
+
+
+### :star: New Features
+
+* **config:** translation miss capturing configration ([aca0ed6](https://github.com/kazupon/vue-i18n/commit/aca0ed6)), closes [#54](https://github.com/kazupon/vue-i18n/issues/54)
+
+
+
+<a name="4.4.1"></a>
+## [4.4.1](https://github.com/kazupon/vue-i18n/compare/v4.4.0...v4.4.1) (2016-09-10)
+
+
+### :zap: Improvements
+
+* **translate:** support hyphenated key ([#52](https://github.com/kazupon/vue-i18n/issues/52)) by [@tariq86](https://github.com/tariq86) ([a40acfd](https://github.com/kazupon/vue-i18n/commit/a40acfd))
+
+
+
+<a name="4.4.0"></a>
+# [4.4.0](https://github.com/kazupon/vue-i18n/compare/v4.3.1...v4.4.0) (2016-08-29)
+
+
+### :star: New Features
+
+* add linked translations ([#50](https://github.com/kazupon/vue-i18n/issues/50)) by [@mmochetti](https://github.com/mmochetti) ([f7ae073](https://github.com/kazupon/vue-i18n/commit/f7ae073))
+
+
+
+<a name="4.3.1"></a>
+## [4.3.1](https://github.com/kazupon/vue-i18n/compare/v4.3.0...v4.3.1) (2016-08-26)
+
+
+### :bug: Bug Fixes
+
+* **npm:** fix installing bug ([57e66aa](https://github.com/kazupon/vue-i18n/commit/57e66aa)), closes [#46](https://github.com/kazupon/vue-i18n/issues/46)
+
+
+
+<a name="4.3.0"></a>
+# [4.3.0](https://github.com/kazupon/vue-i18n/compare/v4.2.3...v4.3.0) (2016-08-26)
+
+
+### :star: New Features
+
+* add pluralization ([#44](https://github.com/kazupon/vue-i18n/issues/44)) by [@mmochetti](https://github.com/mmochetti) ([b5b84d8](https://github.com/kazupon/vue-i18n/commit/b5b84d8))
+
+
+
+<a name="4.2.3"></a>
+## [4.2.3](https://github.com/kazupon/vue-i18n/compare/v4.2.2...v4.2.3) (2016-08-23)
+
+
+### :chart_with_upwards_trend: Performance Fixes
+
+* improve re-rendering cost when change the lang ([0707338](https://github.com/kazupon/vue-i18n/commit/0707338))
+
+
+
+<a name="4.2.2"></a>
+## [4.2.2](https://github.com/kazupon/vue-i18n/compare/v4.2.1...v4.2.2) (2016-08-15)
+
+
+### :bug: Bug Fixes
+
+* **path:** fix array path syntax error ([bc9dbee](https://github.com/kazupon/vue-i18n/commit/bc9dbee)), closes [#42](https://github.com/kazupon/vue-i18n/issues/42) [#43](https://github.com/kazupon/vue-i18n/issues/43)
+
+
+
+<a name="4.2.1"></a>
+## [4.2.1](https://github.com/kazupon/vue-i18n/compare/v4.2.0...v4.2.1) (2016-08-13)
+
+
+### :zap: Improvements
+
+* **translate:** fallback translation warning ([5f6b271](https://github.com/kazupon/vue-i18n/commit/5f6b271))
+
+
+
+<a name="4.2.0"></a>
+# [4.2.0](https://github.com/kazupon/vue-i18n/compare/v4.1.0...v4.2.0) (2016-08-12)
+
+
+### :chart_with_upwards_trend: Performance Fixes
+
+* **format:** use hasOwn function of Vue.util ([a8a19a0](https://github.com/kazupon/vue-i18n/commit/a8a19a0))
+
+
+### :star: New Features
+
+* **fallback:** add fallback translation feature ([1d1f0f2](https://github.com/kazupon/vue-i18n/commit/1d1f0f2)), closes [#36](https://github.com/kazupon/vue-i18n/issues/36)
+
+
+
+<a name="4.1.0"></a>
+# [4.1.0](https://github.com/kazupon/vue-i18n/compare/v4.0.1...v4.1.0) (2016-07-25)
+
+
+### :bug: Bug Fixes
+
+* **util:** fixed isArray reference errors ([0c6f6a0](https://github.com/kazupon/vue-i18n/commit/0c6f6a0))
+
+
+### :star: New Features
+
+* support vue 2.0.0.beta later ([0e1d2f7](https://github.com/kazupon/vue-i18n/commit/0e1d2f7))
+
+
+
+<a name="4.0.1"></a>
+## [4.0.1](https://github.com/kazupon/vue-i18n/compare/v4.0.0...v4.0.1) (2016-06-06)
+
+
+### :bug: Bug Fixes
+
+* **translate:** fix underscore named argument translate issue ([eeaf936](https://github.com/kazupon/vue-i18n/commit/eeaf936))
+
+
+
+<a name="4.0.0"></a>
+# [4.0.0](https://github.com/kazupon/vue-i18n/compare/v3.1.1...v4.0.0) (2016-05-10)
+
+
+### :zap: Improvements
+
+* support vue 2.0-pre-alpha ([f6517bc](https://github.com/kazupon/vue-i18n/commit/f6517bc))
+
+
+
+<a name="3.1.1"></a>
+## [3.1.1](https://github.com/kazupon/vue-i18n/compare/v3.1.0...v3.1.1) (2016-05-09)
+
+
+### :star: New Features
+
+* auto installation for standalone ([2b0dc09](https://github.com/kazupon/vue-i18n/commit/2b0dc09))
+
+
+
+<a name="3.1.0"></a>
+# [3.1.0](https://github.com/kazupon/vue-i18n/compare/v3.0.0...v3.1.0) (2016-05-09)
+
+
+### :star: New Features
+
+* component locales ([12fe695](https://github.com/kazupon/vue-i18n/commit/12fe695)), closes [#29](https://github.com/kazupon/vue-i18n/issues/29)
+
+
+### :warning: Depcreted
+
+* **options:** remove Vue.use options ([d87b59b](https://github.com/kazupon/vue-i18n/commit/d87b59b))
+
+
+### :zap: Improvements
+
+* **keypath:** port the object path parser ([3ae04b7](https://github.com/kazupon/vue-i18n/commit/3ae04b7))
+* **translation:** fix hypenate included key translating ([d0a415f](https://github.com/kazupon/vue-i18n/commit/d0a415f)), closes [#24](https://github.com/kazupon/vue-i18n/issues/24)
+* **translation:** warning outputing when cannot translate with keypath ([b4c7c0e](https://github.com/kazupon/vue-i18n/commit/b4c7c0e)), closes [#22](https://github.com/kazupon/vue-i18n/issues/22)
+
+
+
+<a name="3.0.0"></a>
+# [3.0.0](https://github.com/kazupon/vue-i18n/compare/v2.4.1...v3.0.0) (2016-04-18)
+
+
+### Features
+
+* **lang:** support lang reactive changing ([203ee85](https://github.com/kazupon/vue-i18n/commit/203ee85)), closes [#2](https://github.com/kazupon/vue-i18n/issues/2) [#15](https://github.com/kazupon/vue-i18n/issues/15)
+* **locale:** support dynamic local ([4d61e8d](https://github.com/kazupon/vue-i18n/commit/4d61e8d)), closes [#6](https://github.com/kazupon/vue-i18n/issues/6) [#21](https://github.com/kazupon/vue-i18n/issues/21)
+
+### DEPRECATED
+
+* **index:** plugin install `Vue.use` options (`options.locales`, `options.lang`). See [README](https://github.com/kazupon/vue-i18n/blob/dev/README.md)
+
+
+<a name="2.4.1"></a>
+## [2.4.1](https://github.com/kazupon/vue-i18n/compare/v2.4.0...v2.4.1) (2016-02-29)
+
+### Features
+
+* **i18n:** support ruby on rails i18n interpolation format ([b6b2490](https://github.com/kazupon/vue-i18n/commit/b6b2490))
+
+
+
+<a name="2.4.0"></a>
+# [2.4.0](https://github.com/kazupon/vue-i18n/compare/v2.3.3...v2.4.0) (2016-02-06)
+
+
+### Features
+
+* **i18n:** add Vue.t function ([68935e3](https://github.com/kazupon/vue-i18n/commit/68935e3)), closes [#17](https://github.com/kazupon/vue-i18n/issues/17)
+
+
+
+<a name="2.3.3"></a>
+## [2.3.3](https://github.com/kazupon/vue-i18n/compare/v2.3.2...v2.3.3) (2015-12-09)
+
+
+### Bug Fixes
+
+* **npm:** npm install error ([e31e89e](https://github.com/kazupon/vue-i18n/commit/e31e89e))
+
+### Features
+
+* **bower:** good-bye bower :wink: ([d99eb15](https://github.com/kazupon/vue-i18n/commit/d99eb15))
+
+
+### BREAKING CHANGES
+
+* bower: not support `bower` package manager
+
+I think that bower is dead. :no_good:
+
+
+
+<a name="2.3.2"></a>
+## [2.3.2](https://github.com/kazupon/vue-i18n/compare/v2.3.1...v2.3.2) (2015-12-09)
+
+
+### Features
+
+* **bundle:** more compact the vue-i18n distribution file ([2f32ecc](https://github.com/kazupon/vue-i18n/commit/2f32ecc))
+
+
+
+<a name="2.3.1"></a>
+## [2.3.1](https://github.com/kazupon/vue-i18n/compare/v2.3.0...v2.3.1) (2015-12-01)
+
+### Reverts
+
+* **index:** automatically install for standalone ([25b8059](https://github.com/kazupon/vue-i18n/commit/25b8059))
+
+
+
+<a name="2.3.0"></a>
+# [2.3.0](https://github.com/kazupon/vue-i18n/compare/v2.2.0...v2.3.0) (2015-11-26)
+
+
+### Bug Fixes
+
+* **index:** cannot work at Vue 1.0.10 later ([6fd543e](https://github.com/kazupon/vue-i18n/commit/6fd543e)), closes [#9](https://github.com/kazupon/vue-i18n/issues/9)
+
+### Features
+
+* **index:** support automatically install for standalone ([ada2673](https://github.com/kazupon/vue-i18n/commit/ada2673))
+
+
+
+# v2.2.0 / 2015-09-16
+
+* Re-implemetation with ES6 (babel)
+
+# v2.1.0 / 2015-07-03
+
+* Add global local language setting with `Vue.config.lang`
+
+# v2.0.0 / 2015-06-29
+
+* Support Vue.js 0.12
+* Remove the followings (Breaking Changes)
+    * `Vue.t` function
+    * `v-t` directive
+
+# v1.1.1 / 2015-04-21
+
+* Fix unit test error
+
+# v1.1.0 / 2015-01-10
+
+* Support template string in `$t` method
+* Support language changing in `$t` method
+
+# v1.0.0 / 2015-01-10
+
+* Add `$t` method
+
+# v0.11.0 / 2014-11-07
+
+* Bump to 0.11.0
+
+# v0.2.0 / 2014-10-08
+
+* Support Vue.js 0.11.0-rc
+
+# v0.1.2 / 2014-10-07
+
+* Support bower
+
+# v0.1.1 / 2014-10-06
+
+* Add `Vue.t` function
+
+# v0.1.0 / 2014-05-06
+
+* Release first
+
+# v0.0.0 / 2014-05-03
+
+* Initial project

+ 20 - 0
plugin/vue-i18n/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 kazuya kawaguchi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 73 - 0
plugin/vue-i18n/README.md

@@ -0,0 +1,73 @@
+<p align="center"><img width="128px" height="112px" src="./assets/vue-i18n-logo.png" alt="Vue I18n logo"></p>
+<h1 align="center">vue-i18n</h1>
+<p align="center">
+  <a href="https://circleci.com/gh/kazupon/vue-i18n/tree/dev"><img src="https://circleci.com/gh/kazupon/vue-i18n/tree/dev.svg?style=shield" alt="Build Status"></a>
+  <a href="https://codecov.io/gh/kazupon/vue-i18n"><img src="https://codecov.io/gh/kazupon/vue-i18n/branch/dev/graph/badge.svg" alt="Coverage Status"></a>
+  <a href="http://badge.fury.io/js/vue-i18n"><img src="https://badge.fury.io/js/vue-i18n.svg" alt="NPM version"></a>
+  <a href="https://discord.gg/4yCnk2m"><img src="https://img.shields.io/badge/Discord-join%20chat-738bd7.svg" alt="vue-i18n channel on Discord"></a>
+  <a href="https://devtoken.rocks/package/vue-i18n"><img src="https://badge.devtoken.rocks/vue-i18n" alt="vue-i18n Dev Token"></a>
+</p>
+
+<p align="center">Internationalization plugin for Vue.js</p>
+
+<br/>
+
+<h3 align="center">Silver Sponsors</h3>
+
+<p align="center">
+  <a href="https://www.codeandweb.com/babeledit?utm_campaign=vue-i18n-2019-01" target="_blank">
+    <img src="https://raw.githubusercontent.com/kazupon/vue-i18n/dev/vuepress/.vuepress/public/patrons/babeledit.png">
+  </a>
+</p>
+
+<h3 align="center">Bronze Sponsors</h3>
+
+<p align="center">
+  <a href="https://zenarchitects.co.jp/" target="_blank">
+    <img src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/zenarchitects.png">
+  </a>
+</p>
+
+<br/>
+
+<p align="center">
+  <a href="https://www.patreon.com/kazupon" target="_blank">
+    <img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patreon">
+  </a>
+</p>
+
+<br/>
+
+## :loudspeaker: Notice
+
+vue-i18n will soon be transferred to [intlify organization](https://github.com/intlify). After that, it will be developed and maintained on intlify.
+
+The `vue-i18n` that has been released on npm will be released as `@intlify/vue-i18n` in near future.
+
+`@intlify/vue-i18n` repo is [here](https://github.com/intlify/vue-i18n-next)
+
+Intlify is a new i18n project kickoff by @kazupon. 😉
+
+## :book: Documentation
+
+See [here](http://kazupon.github.io/vue-i18n/)
+
+
+## :scroll: Changelog
+
+Detailed changes for each release are documented in the [CHANGELOG.md](https://github.com/kazupon/vue-i18n/blob/dev/CHANGELOG.md).
+
+
+## :exclamation: Issues
+
+Please make sure to read the [Issue Reporting Checklist](https://github.com/kazupon/vue-i18n/blob/dev/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
+
+
+## :muscle: Contribution
+
+Please make sure to read the [Contributing Guide](https://github.com/kazupon/vue-i18n/blob/dev/CONTRIBUTING.md) before making a pull request.
+
+
+## :copyright: License
+
+[MIT](http://opensource.org/licenses/MIT)

+ 160 - 0
plugin/vue-i18n/decls/i18n.js

@@ -0,0 +1,160 @@
+declare var Intl: any;
+
+declare type Path = string;
+declare type Locale = string;
+declare type MessageContext = {
+  list: (index: number) => mixed,
+  named: (key: string) => mixed
+}
+declare type MessageFunction = (ctx: MessageContext) => string
+declare type FallbackLocale = string | string[] | false | { [locale: string]: string[] };
+declare type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray;
+declare type LocaleMessageObject = { [key: Path]: LocaleMessage };
+declare type LocaleMessageArray = Array<LocaleMessage>;
+declare type LocaleMessages = { [key: Locale]: LocaleMessageObject };
+
+// This options is the same as Intl.DateTimeFormat constructor options:
+// http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor
+declare type DateTimeFormatOptions = {
+  year?: 'numeric' | '2-digit',
+  month?: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long',
+  day?: 'numeric' | '2-digit',
+  hour?: 'numeric' | '2-digit',
+  minute?: 'numeric' | '2-digit',
+  second?: 'numeric' | '2-digit',
+  weekday?: 'narrow' | 'short' | 'long',
+  hour12?: boolean,
+  era?: 'narrow' | 'short' | 'long',
+  timeZone?: string, // IANA time zone
+  timeZoneName?: 'short' | 'long',
+  localeMatcher?: 'lookup' | 'best fit',
+  formatMatcher?: 'basic' | 'best fit'
+};
+declare type DateTimeFormat = { [key: string]: DateTimeFormatOptions };
+declare type DateTimeFormats = { [key: Locale]: DateTimeFormat };
+
+// This options is the same as Intl.NumberFormat constructor options:
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
+declare type NumberFormatOptions = {
+  style?: 'decimal' | 'currency' | 'percent',
+  currency?: string, // ISO 4217 currency codes
+  currencyDisplay?: 'symbol' | 'code' | 'name',
+  useGrouping?: boolean,
+  minimumIntegerDigits?: number,
+  minimumFractionDigits?: number,
+  maximumFractionDigits?: number,
+  minimumSignificantDigits?: number,
+  maximumSignificantDigits?: number,
+  localeMatcher?: 'lookup' | 'best fit',
+  formatMatcher?: 'basic' | 'best fit'
+};
+declare type NumberFormat = { [key: string]: NumberFormatOptions };
+declare type NumberFormats = { [key: Locale]: NumberFormat };
+declare type Modifiers = { [key: string]: (str: string) => string };
+
+declare type TranslateResult = string | LocaleMessages;
+declare type DateTimeFormatResult = string;
+declare type NumberFormatResult = string;
+declare type MissingHandler = (locale: Locale, key: Path, vm?: any) => string | void;
+declare type PostTranslationHandler = (str: string, key?: string) => string;
+declare type GetChoiceIndex = (choice: number, choicesLength: number) => number
+declare type ComponentInstanceCreatedListener = (newI18n: I18n, rootI18n: I18n) => void;
+
+declare type FormattedNumberPartType = 'currency' | 'decimal' | 'fraction' | 'group' | 'infinity' | 'integer' | 'literal' | 'minusSign' | 'nan' | 'plusSign' | 'percentSign';
+declare type FormattedNumberPart = {
+  type: FormattedNumberPartType,
+  value: string,
+};
+// This array is the same as Intl.NumberFormat.formatToParts() return value:
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts#Return_value
+declare type NumberFormatToPartsResult = Array<FormattedNumberPart>;
+
+declare type WarnHtmlInMessageLevel = 'off' | 'warn' | 'error';
+
+declare type I18nOptions = {
+  locale?: Locale,
+  fallbackLocale?: FallbackLocale,
+  messages?: LocaleMessages,
+  dateTimeFormats?: DateTimeFormats,
+  numberFormats?: NumberFormats,
+  formatter?: Formatter,
+  missing?: MissingHandler,
+  modifiers?: Modifiers,
+  root?: I18n, // for internal
+  fallbackRoot?: boolean,
+  formatFallbackMessages?: boolean,
+  sync?: boolean,
+  silentTranslationWarn?: boolean | RegExp,
+  silentFallbackWarn?: boolean | RegExp,
+  pluralizationRules?: PluralizationRules,
+  preserveDirectiveContent?: boolean,
+  warnHtmlInMessage?: WarnHtmlInMessageLevel,
+  sharedMessages?: LocaleMessage,
+  postTranslation?: PostTranslationHandler,
+  componentInstanceCreatedListener?: ComponentInstanceCreatedListener,
+};
+
+declare type IntlAvailability = {
+  dateTimeFormat: boolean,
+  numberFormat: boolean
+};
+
+declare type PluralizationRules = {
+  [lang: string]: GetChoiceIndex,
+}
+
+declare interface I18n {
+  static install: () => void, // for Vue plugin interface
+  static version: string,
+  static availabilities: IntlAvailability,
+  get vm (): any, // for internal
+  get locale (): Locale,
+  set locale (locale: Locale): void,
+  get fallbackLocale (): FallbackLocale,
+  set fallbackLocale (locale: FallbackLocale): void,
+  get messages (): LocaleMessages,
+  get dateTimeFormats (): DateTimeFormats,
+  get numberFormats (): NumberFormats,
+  get availableLocales (): Locale[],
+  get missing (): ?MissingHandler,
+  set missing (handler: MissingHandler): void,
+  get formatter (): Formatter,
+  set formatter (formatter: Formatter): void,
+  get formatFallbackMessages (): boolean,
+  set formatFallbackMessages (fallback: boolean): void,
+  get silentTranslationWarn (): boolean | RegExp,
+  set silentTranslationWarn (silent: boolean | RegExp): void,
+  get silentFallbackWarn (): boolean | RegExp,
+  set silentFallbackWarn (slient: boolean | RegExp): void,
+  get pluralizationRules (): PluralizationRules,
+  set pluralizationRules (rules: PluralizationRules): void,
+  get preserveDirectiveContent (): boolean,
+  set preserveDirectiveContent (preserve: boolean): void,
+  get warnHtmlInMessage (): WarnHtmlInMessageLevel,
+  set warnHtmlInMessage (level: WarnHtmlInMessageLevel): void,
+  get postTranslation (): ?PostTranslationHandler,
+  set postTranslation (handler: PostTranslationHandler): void,
+
+  getLocaleMessage (locale: Locale): LocaleMessageObject,
+  setLocaleMessage (locale: Locale, message: LocaleMessageObject): void,
+  mergeLocaleMessage (locale: Locale, message: LocaleMessageObject): void,
+  t (key: Path, ...values: any): TranslateResult,
+  i (key: Path, locale: Locale, values: Object): TranslateResult,
+  tc (key: Path, choice?: number, ...values: any): TranslateResult,
+  te (key: Path, locale?: Locale): boolean,
+  getDateTimeFormat (locale: Locale): DateTimeFormat,
+  setDateTimeFormat (locale: Locale, format: DateTimeFormat): void,
+  mergeDateTimeFormat (locale: Locale, format: DateTimeFormat): void,
+  d (value: number | Date, ...args: any): DateTimeFormatResult,
+  getNumberFormat (locale: Locale): NumberFormat,
+  setNumberFormat (locale: Locale, format: NumberFormat): void,
+  mergeNumberFormat (locale: Locale, format: NumberFormat): void,
+  n (value: number, ...args: any): NumberFormatResult,
+  getChoiceIndex: GetChoiceIndex,
+  pluralizationRules: PluralizationRules,
+  preserveDirectiveContent: boolean
+};
+
+declare interface Formatter {
+  interpolate (message: string, values: any, path: string): (Array<any> | null)
+};

+ 30 - 0
plugin/vue-i18n/decls/module.js

@@ -0,0 +1,30 @@
+declare type $npm$Vue$Dictionaly<T> = { [key: string]: T }
+
+declare type Util = {
+  extend: (to: Object, from: ?Object) => Object,
+  hasOwn: (obj: Object, key: string) => boolean,
+  isPlainObject: (obj: any) => boolean,
+  isObject: (obj: mixed) => boolean,
+}
+
+declare type Config = {
+  optionMergeStrategies: $npm$Vue$Dictionaly<Function>,
+  silent: boolean,
+  productionTip: boolean,
+  performance: boolean,
+  devtools: boolean,
+  errorHandler: ?(err: Error, vm: Vue, info: string) => void,
+  ignoredElements: Array<string>,
+  keyCodes: $npm$Vue$Dictionaly<number>,
+  isReservedTag: (x?: string) => boolean,
+  parsePlatformTagName: (x: string) => string,
+  isUnknownElement: (x?: string) => boolean,
+  getTagNamespace: (x?: string) => string | void,
+  mustUseProp: (tag: string, type: ?string, name: string) => boolean,
+}
+
+declare interface Vue {
+  static config: Config,
+  static util: Util,
+  static version: string,
+}

+ 2151 - 0
plugin/vue-i18n/dist/vue-i18n.common.js

@@ -0,0 +1,2151 @@
+/*!
+ * vue-i18n v8.21.0 
+ * (c) 2020 kazuya kawaguchi
+ * Released under the MIT License.
+ */
+'use strict';
+
+/*  */
+
+/**
+ * constants
+ */
+
+var numberFormatKeys = [
+  'style',
+  'currency',
+  'currencyDisplay',
+  'useGrouping',
+  'minimumIntegerDigits',
+  'minimumFractionDigits',
+  'maximumFractionDigits',
+  'minimumSignificantDigits',
+  'maximumSignificantDigits',
+  'localeMatcher',
+  'formatMatcher',
+  'unit'
+];
+
+/**
+ * utilities
+ */
+
+function warn (msg, err) {
+  if (typeof console !== 'undefined') {
+    console.warn('[vue-i18n] ' + msg);
+    /* istanbul ignore if */
+    if (err) {
+      console.warn(err.stack);
+    }
+  }
+}
+
+function error (msg, err) {
+  if (typeof console !== 'undefined') {
+    console.error('[vue-i18n] ' + msg);
+    /* istanbul ignore if */
+    if (err) {
+      console.error(err.stack);
+    }
+  }
+}
+
+var isArray = Array.isArray;
+
+function isObject (obj) {
+  return obj !== null && typeof obj === 'object'
+}
+
+function isBoolean (val) {
+  return typeof val === 'boolean'
+}
+
+function isString (val) {
+  return typeof val === 'string'
+}
+
+var toString = Object.prototype.toString;
+var OBJECT_STRING = '[object Object]';
+function isPlainObject (obj) {
+  return toString.call(obj) === OBJECT_STRING
+}
+
+function isNull (val) {
+  return val === null || val === undefined
+}
+
+function isFunction (val) {
+  return typeof val === 'function'
+}
+
+function parseArgs () {
+  var args = [], len = arguments.length;
+  while ( len-- ) args[ len ] = arguments[ len ];
+
+  var locale = null;
+  var params = null;
+  if (args.length === 1) {
+    if (isObject(args[0]) || isArray(args[0])) {
+      params = args[0];
+    } else if (typeof args[0] === 'string') {
+      locale = args[0];
+    }
+  } else if (args.length === 2) {
+    if (typeof args[0] === 'string') {
+      locale = args[0];
+    }
+    /* istanbul ignore if */
+    if (isObject(args[1]) || isArray(args[1])) {
+      params = args[1];
+    }
+  }
+
+  return { locale: locale, params: params }
+}
+
+function looseClone (obj) {
+  return JSON.parse(JSON.stringify(obj))
+}
+
+function remove (arr, item) {
+  if (arr.length) {
+    var index = arr.indexOf(item);
+    if (index > -1) {
+      return arr.splice(index, 1)
+    }
+  }
+}
+
+function includes (arr, item) {
+  return !!~arr.indexOf(item)
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+function hasOwn (obj, key) {
+  return hasOwnProperty.call(obj, key)
+}
+
+function merge (target) {
+  var arguments$1 = arguments;
+
+  var output = Object(target);
+  for (var i = 1; i < arguments.length; i++) {
+    var source = arguments$1[i];
+    if (source !== undefined && source !== null) {
+      var key = (void 0);
+      for (key in source) {
+        if (hasOwn(source, key)) {
+          if (isObject(source[key])) {
+            output[key] = merge(output[key], source[key]);
+          } else {
+            output[key] = source[key];
+          }
+        }
+      }
+    }
+  }
+  return output
+}
+
+function looseEqual (a, b) {
+  if (a === b) { return true }
+  var isObjectA = isObject(a);
+  var isObjectB = isObject(b);
+  if (isObjectA && isObjectB) {
+    try {
+      var isArrayA = isArray(a);
+      var isArrayB = isArray(b);
+      if (isArrayA && isArrayB) {
+        return a.length === b.length && a.every(function (e, i) {
+          return looseEqual(e, b[i])
+        })
+      } else if (!isArrayA && !isArrayB) {
+        var keysA = Object.keys(a);
+        var keysB = Object.keys(b);
+        return keysA.length === keysB.length && keysA.every(function (key) {
+          return looseEqual(a[key], b[key])
+        })
+      } else {
+        /* istanbul ignore next */
+        return false
+      }
+    } catch (e) {
+      /* istanbul ignore next */
+      return false
+    }
+  } else if (!isObjectA && !isObjectB) {
+    return String(a) === String(b)
+  } else {
+    return false
+  }
+}
+
+/*  */
+
+function extend (Vue) {
+  if (!Vue.prototype.hasOwnProperty('$i18n')) {
+    // $FlowFixMe
+    Object.defineProperty(Vue.prototype, '$i18n', {
+      get: function get () { return this._i18n }
+    });
+  }
+
+  Vue.prototype.$t = function (key) {
+    var values = [], len = arguments.length - 1;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
+
+    var i18n = this.$i18n;
+    return i18n._t.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this ].concat( values ))
+  };
+
+  Vue.prototype.$tc = function (key, choice) {
+    var values = [], len = arguments.length - 2;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
+
+    var i18n = this.$i18n;
+    return i18n._tc.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this, choice ].concat( values ))
+  };
+
+  Vue.prototype.$te = function (key, locale) {
+    var i18n = this.$i18n;
+    return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
+  };
+
+  Vue.prototype.$d = function (value) {
+    var ref;
+
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+    return (ref = this.$i18n).d.apply(ref, [ value ].concat( args ))
+  };
+
+  Vue.prototype.$n = function (value) {
+    var ref;
+
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+    return (ref = this.$i18n).n.apply(ref, [ value ].concat( args ))
+  };
+}
+
+/*  */
+
+var mixin = {
+  beforeCreate: function beforeCreate () {
+    var options = this.$options;
+    options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            var localeMessages = {};
+            options.__i18n.forEach(function (resource) {
+              localeMessages = merge(localeMessages, JSON.parse(resource));
+            });
+            Object.keys(localeMessages).forEach(function (locale) {
+              options.i18n.mergeLocaleMessage(locale, localeMessages[locale]);
+            });
+          } catch (e) {
+            if (process.env.NODE_ENV !== 'production') {
+              error("Cannot parse locale messages via custom blocks.", e);
+            }
+          }
+        }
+        this._i18n = options.i18n;
+        this._i18nWatcher = this._i18n.watchI18nData();
+      } else if (isPlainObject(options.i18n)) {
+        var rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n
+          ? this.$root.$i18n
+          : null;
+        // component local i18n
+        if (rootI18n) {
+          options.i18n.root = this.$root;
+          options.i18n.formatter = rootI18n.formatter;
+          options.i18n.fallbackLocale = rootI18n.fallbackLocale;
+          options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages;
+          options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn;
+          options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn;
+          options.i18n.pluralizationRules = rootI18n.pluralizationRules;
+          options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent;
+        }
+
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            var localeMessages$1 = {};
+            options.__i18n.forEach(function (resource) {
+              localeMessages$1 = merge(localeMessages$1, JSON.parse(resource));
+            });
+            options.i18n.messages = localeMessages$1;
+          } catch (e) {
+            if (process.env.NODE_ENV !== 'production') {
+              warn("Cannot parse locale messages via custom blocks.", e);
+            }
+          }
+        }
+
+        var ref = options.i18n;
+        var sharedMessages = ref.sharedMessages;
+        if (sharedMessages && isPlainObject(sharedMessages)) {
+          options.i18n.messages = merge(options.i18n.messages, sharedMessages);
+        }
+
+        this._i18n = new VueI18n(options.i18n);
+        this._i18nWatcher = this._i18n.watchI18nData();
+
+        if (options.i18n.sync === undefined || !!options.i18n.sync) {
+          this._localeWatcher = this.$i18n.watchLocale();
+        }
+
+        if (rootI18n) {
+          rootI18n.onComponentInstanceCreated(this._i18n);
+        }
+      } else {
+        if (process.env.NODE_ENV !== 'production') {
+          warn("Cannot be interpreted 'i18n' option.");
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      // root i18n
+      this._i18n = this.$root.$i18n;
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      // parent i18n
+      this._i18n = options.parent.$i18n;
+    }
+  },
+
+  beforeMount: function beforeMount () {
+    var options = this.$options;
+    options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      } else if (isPlainObject(options.i18n)) {
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      } else {
+        if (process.env.NODE_ENV !== 'production') {
+          warn("Cannot be interpreted 'i18n' option.");
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this);
+      this._subscribing = true;
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this);
+      this._subscribing = true;
+    }
+  },
+
+  beforeDestroy: function beforeDestroy () {
+    if (!this._i18n) { return }
+
+    var self = this;
+    this.$nextTick(function () {
+      if (self._subscribing) {
+        self._i18n.unsubscribeDataChanging(self);
+        delete self._subscribing;
+      }
+
+      if (self._i18nWatcher) {
+        self._i18nWatcher();
+        self._i18n.destroyVM();
+        delete self._i18nWatcher;
+      }
+
+      if (self._localeWatcher) {
+        self._localeWatcher();
+        delete self._localeWatcher;
+      }
+    });
+  }
+};
+
+/*  */
+
+var interpolationComponent = {
+  name: 'i18n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    path: {
+      type: String,
+      required: true
+    },
+    locale: {
+      type: String
+    },
+    places: {
+      type: [Array, Object]
+    }
+  },
+  render: function render (h, ref) {
+    var data = ref.data;
+    var parent = ref.parent;
+    var props = ref.props;
+    var slots = ref.slots;
+
+    var $i18n = parent.$i18n;
+    if (!$i18n) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot find VueI18n instance!');
+      }
+      return
+    }
+
+    var path = props.path;
+    var locale = props.locale;
+    var places = props.places;
+    var params = slots();
+    var children = $i18n.i(
+      path,
+      locale,
+      onlyHasDefaultPlace(params) || places
+        ? useLegacyPlaces(params.default, places)
+        : params
+    );
+
+    var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+    return tag ? h(tag, data, children) : children
+  }
+};
+
+function onlyHasDefaultPlace (params) {
+  var prop;
+  for (prop in params) {
+    if (prop !== 'default') { return false }
+  }
+  return Boolean(prop)
+}
+
+function useLegacyPlaces (children, places) {
+  var params = places ? createParamsFromPlaces(places) : {};
+
+  if (!children) { return params }
+
+  // Filter empty text nodes
+  children = children.filter(function (child) {
+    return child.tag || child.text.trim() !== ''
+  });
+
+  var everyPlace = children.every(vnodeHasPlaceAttribute);
+  if (process.env.NODE_ENV !== 'production' && everyPlace) {
+    warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.');
+  }
+
+  return children.reduce(
+    everyPlace ? assignChildPlace : assignChildIndex,
+    params
+  )
+}
+
+function createParamsFromPlaces (places) {
+  if (process.env.NODE_ENV !== 'production') {
+    warn('`places` prop is deprecated in next major version. Please switch to Vue slots.');
+  }
+
+  return Array.isArray(places)
+    ? places.reduce(assignChildIndex, {})
+    : Object.assign({}, places)
+}
+
+function assignChildPlace (params, child) {
+  if (child.data && child.data.attrs && child.data.attrs.place) {
+    params[child.data.attrs.place] = child;
+  }
+  return params
+}
+
+function assignChildIndex (params, child, index) {
+  params[index] = child;
+  return params
+}
+
+function vnodeHasPlaceAttribute (vnode) {
+  return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place)
+}
+
+/*  */
+
+var numberComponent = {
+  name: 'i18n-n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    value: {
+      type: Number,
+      required: true
+    },
+    format: {
+      type: [String, Object]
+    },
+    locale: {
+      type: String
+    }
+  },
+  render: function render (h, ref) {
+    var props = ref.props;
+    var parent = ref.parent;
+    var data = ref.data;
+
+    var i18n = parent.$i18n;
+
+    if (!i18n) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot find VueI18n instance!');
+      }
+      return null
+    }
+
+    var key = null;
+    var options = null;
+
+    if (isString(props.format)) {
+      key = props.format;
+    } else if (isObject(props.format)) {
+      if (props.format.key) {
+        key = props.format.key;
+      }
+
+      // Filter out number format options only
+      options = Object.keys(props.format).reduce(function (acc, prop) {
+        var obj;
+
+        if (includes(numberFormatKeys, prop)) {
+          return Object.assign({}, acc, ( obj = {}, obj[prop] = props.format[prop], obj ))
+        }
+        return acc
+      }, null);
+    }
+
+    var locale = props.locale || i18n.locale;
+    var parts = i18n._ntp(props.value, locale, key, options);
+
+    var values = parts.map(function (part, index) {
+      var obj;
+
+      var slot = data.scopedSlots && data.scopedSlots[part.type];
+      return slot ? slot(( obj = {}, obj[part.type] = part.value, obj.index = index, obj.parts = parts, obj )) : part.value
+    });
+
+    var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+    return tag
+      ? h(tag, {
+        attrs: data.attrs,
+        'class': data['class'],
+        staticClass: data.staticClass
+      }, values)
+      : values
+  }
+};
+
+/*  */
+
+function bind (el, binding, vnode) {
+  if (!assert(el, vnode)) { return }
+
+  t(el, binding, vnode);
+}
+
+function update (el, binding, vnode, oldVNode) {
+  if (!assert(el, vnode)) { return }
+
+  var i18n = vnode.context.$i18n;
+  if (localeEqual(el, vnode) &&
+    (looseEqual(binding.value, binding.oldValue) &&
+     looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }
+
+  t(el, binding, vnode);
+}
+
+function unbind (el, binding, vnode, oldVNode) {
+  var vm = vnode.context;
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context');
+    return
+  }
+
+  var i18n = vnode.context.$i18n || {};
+  if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {
+    el.textContent = '';
+  }
+  el._vt = undefined;
+  delete el['_vt'];
+  el._locale = undefined;
+  delete el['_locale'];
+  el._localeMessage = undefined;
+  delete el['_localeMessage'];
+}
+
+function assert (el, vnode) {
+  var vm = vnode.context;
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context');
+    return false
+  }
+
+  if (!vm.$i18n) {
+    warn('VueI18n instance does not exists in Vue instance');
+    return false
+  }
+
+  return true
+}
+
+function localeEqual (el, vnode) {
+  var vm = vnode.context;
+  return el._locale === vm.$i18n.locale
+}
+
+function t (el, binding, vnode) {
+  var ref$1, ref$2;
+
+  var value = binding.value;
+
+  var ref = parseValue(value);
+  var path = ref.path;
+  var locale = ref.locale;
+  var args = ref.args;
+  var choice = ref.choice;
+  if (!path && !locale && !args) {
+    warn('value type not supported');
+    return
+  }
+
+  if (!path) {
+    warn('`path` is required in v-t directive');
+    return
+  }
+
+  var vm = vnode.context;
+  if (choice != null) {
+    el._vt = el.textContent = (ref$1 = vm.$i18n).tc.apply(ref$1, [ path, choice ].concat( makeParams(locale, args) ));
+  } else {
+    el._vt = el.textContent = (ref$2 = vm.$i18n).t.apply(ref$2, [ path ].concat( makeParams(locale, args) ));
+  }
+  el._locale = vm.$i18n.locale;
+  el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale);
+}
+
+function parseValue (value) {
+  var path;
+  var locale;
+  var args;
+  var choice;
+
+  if (isString(value)) {
+    path = value;
+  } else if (isPlainObject(value)) {
+    path = value.path;
+    locale = value.locale;
+    args = value.args;
+    choice = value.choice;
+  }
+
+  return { path: path, locale: locale, args: args, choice: choice }
+}
+
+function makeParams (locale, args) {
+  var params = [];
+
+  locale && params.push(locale);
+  if (args && (Array.isArray(args) || isPlainObject(args))) {
+    params.push(args);
+  }
+
+  return params
+}
+
+var Vue;
+
+function install (_Vue) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && install.installed && _Vue === Vue) {
+    warn('already installed.');
+    return
+  }
+  install.installed = true;
+
+  Vue = _Vue;
+
+  var version = (Vue.version && Number(Vue.version.split('.')[0])) || -1;
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && version < 2) {
+    warn(("vue-i18n (" + (install.version) + ") need to use Vue 2.0 or later (Vue: " + (Vue.version) + ")."));
+    return
+  }
+
+  extend(Vue);
+  Vue.mixin(mixin);
+  Vue.directive('t', { bind: bind, update: update, unbind: unbind });
+  Vue.component(interpolationComponent.name, interpolationComponent);
+  Vue.component(numberComponent.name, numberComponent);
+
+  // use simple mergeStrategies to prevent i18n instance lose '__proto__'
+  var strats = Vue.config.optionMergeStrategies;
+  strats.i18n = function (parentVal, childVal) {
+    return childVal === undefined
+      ? parentVal
+      : childVal
+  };
+}
+
+/*  */
+
+var BaseFormatter = function BaseFormatter () {
+  this._caches = Object.create(null);
+};
+
+BaseFormatter.prototype.interpolate = function interpolate (message, values) {
+  if (!values) {
+    return [message]
+  }
+  var tokens = this._caches[message];
+  if (!tokens) {
+    tokens = parse(message);
+    this._caches[message] = tokens;
+  }
+  return compile(tokens, values)
+};
+
+
+
+var RE_TOKEN_LIST_VALUE = /^(?:\d)+/;
+var RE_TOKEN_NAMED_VALUE = /^(?:\w)+/;
+
+function parse (format) {
+  var tokens = [];
+  var position = 0;
+
+  var text = '';
+  while (position < format.length) {
+    var char = format[position++];
+    if (char === '{') {
+      if (text) {
+        tokens.push({ type: 'text', value: text });
+      }
+
+      text = '';
+      var sub = '';
+      char = format[position++];
+      while (char !== undefined && char !== '}') {
+        sub += char;
+        char = format[position++];
+      }
+      var isClosed = char === '}';
+
+      var type = RE_TOKEN_LIST_VALUE.test(sub)
+        ? 'list'
+        : isClosed && RE_TOKEN_NAMED_VALUE.test(sub)
+          ? 'named'
+          : 'unknown';
+      tokens.push({ value: sub, type: type });
+    } else if (char === '%') {
+      // when found rails i18n syntax, skip text capture
+      if (format[(position)] !== '{') {
+        text += char;
+      }
+    } else {
+      text += char;
+    }
+  }
+
+  text && tokens.push({ type: 'text', value: text });
+
+  return tokens
+}
+
+function compile (tokens, values) {
+  var compiled = [];
+  var index = 0;
+
+  var mode = Array.isArray(values)
+    ? 'list'
+    : isObject(values)
+      ? 'named'
+      : 'unknown';
+  if (mode === 'unknown') { return compiled }
+
+  while (index < tokens.length) {
+    var token = tokens[index];
+    switch (token.type) {
+      case 'text':
+        compiled.push(token.value);
+        break
+      case 'list':
+        compiled.push(values[parseInt(token.value, 10)]);
+        break
+      case 'named':
+        if (mode === 'named') {
+          compiled.push((values)[token.value]);
+        } else {
+          if (process.env.NODE_ENV !== 'production') {
+            warn(("Type of token '" + (token.type) + "' and format of value '" + mode + "' don't match!"));
+          }
+        }
+        break
+      case 'unknown':
+        if (process.env.NODE_ENV !== 'production') {
+          warn("Detect 'unknown' type of token!");
+        }
+        break
+    }
+    index++;
+  }
+
+  return compiled
+}
+
+/*  */
+
+/**
+ *  Path parser
+ *  - Inspired:
+ *    Vue.js Path parser
+ */
+
+// actions
+var APPEND = 0;
+var PUSH = 1;
+var INC_SUB_PATH_DEPTH = 2;
+var PUSH_SUB_PATH = 3;
+
+// states
+var BEFORE_PATH = 0;
+var IN_PATH = 1;
+var BEFORE_IDENT = 2;
+var IN_IDENT = 3;
+var IN_SUB_PATH = 4;
+var IN_SINGLE_QUOTE = 5;
+var IN_DOUBLE_QUOTE = 6;
+var AFTER_PATH = 7;
+var ERROR = 8;
+
+var pathStateMachine = [];
+
+pathStateMachine[BEFORE_PATH] = {
+  'ws': [BEFORE_PATH],
+  'ident': [IN_IDENT, APPEND],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[IN_PATH] = {
+  'ws': [IN_PATH],
+  '.': [BEFORE_IDENT],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[BEFORE_IDENT] = {
+  'ws': [BEFORE_IDENT],
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND]
+};
+
+pathStateMachine[IN_IDENT] = {
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND],
+  'ws': [IN_PATH, PUSH],
+  '.': [BEFORE_IDENT, PUSH],
+  '[': [IN_SUB_PATH, PUSH],
+  'eof': [AFTER_PATH, PUSH]
+};
+
+pathStateMachine[IN_SUB_PATH] = {
+  "'": [IN_SINGLE_QUOTE, APPEND],
+  '"': [IN_DOUBLE_QUOTE, APPEND],
+  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+  ']': [IN_PATH, PUSH_SUB_PATH],
+  'eof': ERROR,
+  'else': [IN_SUB_PATH, APPEND]
+};
+
+pathStateMachine[IN_SINGLE_QUOTE] = {
+  "'": [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_SINGLE_QUOTE, APPEND]
+};
+
+pathStateMachine[IN_DOUBLE_QUOTE] = {
+  '"': [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_DOUBLE_QUOTE, APPEND]
+};
+
+/**
+ * Check if an expression is a literal value.
+ */
+
+var literalValueRE = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;
+function isLiteral (exp) {
+  return literalValueRE.test(exp)
+}
+
+/**
+ * Strip quotes from a string
+ */
+
+function stripQuotes (str) {
+  var a = str.charCodeAt(0);
+  var b = str.charCodeAt(str.length - 1);
+  return a === b && (a === 0x22 || a === 0x27)
+    ? str.slice(1, -1)
+    : str
+}
+
+/**
+ * Determine the type of a character in a keypath.
+ */
+
+function getPathCharType (ch) {
+  if (ch === undefined || ch === null) { return 'eof' }
+
+  var code = ch.charCodeAt(0);
+
+  switch (code) {
+    case 0x5B: // [
+    case 0x5D: // ]
+    case 0x2E: // .
+    case 0x22: // "
+    case 0x27: // '
+      return ch
+
+    case 0x5F: // _
+    case 0x24: // $
+    case 0x2D: // -
+      return 'ident'
+
+    case 0x09: // Tab
+    case 0x0A: // Newline
+    case 0x0D: // Return
+    case 0xA0:  // No-break space
+    case 0xFEFF:  // Byte Order Mark
+    case 0x2028:  // Line Separator
+    case 0x2029:  // Paragraph Separator
+      return 'ws'
+  }
+
+  return 'ident'
+}
+
+/**
+ * Format a subPath, return its plain form if it is
+ * a literal string or number. Otherwise prepend the
+ * dynamic indicator (*).
+ */
+
+function formatSubPath (path) {
+  var trimmed = path.trim();
+  // invalid leading 0
+  if (path.charAt(0) === '0' && isNaN(path)) { return false }
+
+  return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
+}
+
+/**
+ * Parse a string path into an array of segments
+ */
+
+function parse$1 (path) {
+  var keys = [];
+  var index = -1;
+  var mode = BEFORE_PATH;
+  var subPathDepth = 0;
+  var c;
+  var key;
+  var newChar;
+  var type;
+  var transition;
+  var action;
+  var typeMap;
+  var actions = [];
+
+  actions[PUSH] = function () {
+    if (key !== undefined) {
+      keys.push(key);
+      key = undefined;
+    }
+  };
+
+  actions[APPEND] = function () {
+    if (key === undefined) {
+      key = newChar;
+    } else {
+      key += newChar;
+    }
+  };
+
+  actions[INC_SUB_PATH_DEPTH] = function () {
+    actions[APPEND]();
+    subPathDepth++;
+  };
+
+  actions[PUSH_SUB_PATH] = function () {
+    if (subPathDepth > 0) {
+      subPathDepth--;
+      mode = IN_SUB_PATH;
+      actions[APPEND]();
+    } else {
+      subPathDepth = 0;
+      if (key === undefined) { return false }
+      key = formatSubPath(key);
+      if (key === false) {
+        return false
+      } else {
+        actions[PUSH]();
+      }
+    }
+  };
+
+  function maybeUnescapeQuote () {
+    var nextChar = path[index + 1];
+    if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
+      (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
+      index++;
+      newChar = '\\' + nextChar;
+      actions[APPEND]();
+      return true
+    }
+  }
+
+  while (mode !== null) {
+    index++;
+    c = path[index];
+
+    if (c === '\\' && maybeUnescapeQuote()) {
+      continue
+    }
+
+    type = getPathCharType(c);
+    typeMap = pathStateMachine[mode];
+    transition = typeMap[type] || typeMap['else'] || ERROR;
+
+    if (transition === ERROR) {
+      return // parse error
+    }
+
+    mode = transition[0];
+    action = actions[transition[1]];
+    if (action) {
+      newChar = transition[2];
+      newChar = newChar === undefined
+        ? c
+        : newChar;
+      if (action() === false) {
+        return
+      }
+    }
+
+    if (mode === AFTER_PATH) {
+      return keys
+    }
+  }
+}
+
+
+
+
+
+var I18nPath = function I18nPath () {
+  this._cache = Object.create(null);
+};
+
+/**
+ * External parse that check for a cache hit first
+ */
+I18nPath.prototype.parsePath = function parsePath (path) {
+  var hit = this._cache[path];
+  if (!hit) {
+    hit = parse$1(path);
+    if (hit) {
+      this._cache[path] = hit;
+    }
+  }
+  return hit || []
+};
+
+/**
+ * Get path value from path string
+ */
+I18nPath.prototype.getPathValue = function getPathValue (obj, path) {
+  if (!isObject(obj)) { return null }
+
+  var paths = this.parsePath(path);
+  if (paths.length === 0) {
+    return null
+  } else {
+    var length = paths.length;
+    var last = obj;
+    var i = 0;
+    while (i < length) {
+      var value = last[paths[i]];
+      if (value === undefined) {
+        return null
+      }
+      last = value;
+      i++;
+    }
+
+    return last
+  }
+};
+
+/*  */
+
+
+
+var htmlTagMatcher = /<\/?[\w\s="/.':;#-\/]+>/;
+var linkKeyMatcher = /(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g;
+var linkKeyPrefixMatcher = /^@(?:\.([a-z]+))?:/;
+var bracketsMatcher = /[()]/g;
+var defaultModifiers = {
+  'upper': function (str) { return str.toLocaleUpperCase(); },
+  'lower': function (str) { return str.toLocaleLowerCase(); },
+  'capitalize': function (str) { return ("" + (str.charAt(0).toLocaleUpperCase()) + (str.substr(1))); }
+};
+
+var defaultFormatter = new BaseFormatter();
+
+var VueI18n = function VueI18n (options) {
+  var this$1 = this;
+  if ( options === void 0 ) options = {};
+
+  // Auto install if it is not done yet and `window` has `Vue`.
+  // To allow users to avoid auto-installation in some cases,
+  // this code should be placed here. See #290
+  /* istanbul ignore if */
+  if (!Vue && typeof window !== 'undefined' && window.Vue) {
+    install(window.Vue);
+  }
+
+  var locale = options.locale || 'en-US';
+  var fallbackLocale = options.fallbackLocale === false
+    ? false
+    : options.fallbackLocale || 'en-US';
+  var messages = options.messages || {};
+  var dateTimeFormats = options.dateTimeFormats || {};
+  var numberFormats = options.numberFormats || {};
+
+  this._vm = null;
+  this._formatter = options.formatter || defaultFormatter;
+  this._modifiers = options.modifiers || {};
+  this._missing = options.missing || null;
+  this._root = options.root || null;
+  this._sync = options.sync === undefined ? true : !!options.sync;
+  this._fallbackRoot = options.fallbackRoot === undefined
+    ? true
+    : !!options.fallbackRoot;
+  this._formatFallbackMessages = options.formatFallbackMessages === undefined
+    ? false
+    : !!options.formatFallbackMessages;
+  this._silentTranslationWarn = options.silentTranslationWarn === undefined
+    ? false
+    : options.silentTranslationWarn;
+  this._silentFallbackWarn = options.silentFallbackWarn === undefined
+    ? false
+    : !!options.silentFallbackWarn;
+  this._dateTimeFormatters = {};
+  this._numberFormatters = {};
+  this._path = new I18nPath();
+  this._dataListeners = [];
+  this._componentInstanceCreatedListener = options.componentInstanceCreatedListener || null;
+  this._preserveDirectiveContent = options.preserveDirectiveContent === undefined
+    ? false
+    : !!options.preserveDirectiveContent;
+  this.pluralizationRules = options.pluralizationRules || {};
+  this._warnHtmlInMessage = options.warnHtmlInMessage || 'off';
+  this._postTranslation = options.postTranslation || null;
+
+  /**
+   * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
+   * @param choicesLength {number} an overall amount of available choices
+   * @returns a final choice index
+  */
+  this.getChoiceIndex = function (choice, choicesLength) {
+    var thisPrototype = Object.getPrototypeOf(this$1);
+    if (thisPrototype && thisPrototype.getChoiceIndex) {
+      var prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex);
+      return (prototypeGetChoiceIndex).call(this$1, choice, choicesLength)
+    }
+
+    // Default (old) getChoiceIndex implementation - english-compatible
+    var defaultImpl = function (_choice, _choicesLength) {
+      _choice = Math.abs(_choice);
+
+      if (_choicesLength === 2) {
+        return _choice
+          ? _choice > 1
+            ? 1
+            : 0
+          : 1
+      }
+
+      return _choice ? Math.min(_choice, 2) : 0
+    };
+
+    if (this$1.locale in this$1.pluralizationRules) {
+      return this$1.pluralizationRules[this$1.locale].apply(this$1, [choice, choicesLength])
+    } else {
+      return defaultImpl(choice, choicesLength)
+    }
+  };
+
+
+  this._exist = function (message, key) {
+    if (!message || !key) { return false }
+    if (!isNull(this$1._path.getPathValue(message, key))) { return true }
+    // fallback for flat key
+    if (message[key]) { return true }
+    return false
+  };
+
+  if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+    Object.keys(messages).forEach(function (locale) {
+      this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);
+    });
+  }
+
+  this._initVM({
+    locale: locale,
+    fallbackLocale: fallbackLocale,
+    messages: messages,
+    dateTimeFormats: dateTimeFormats,
+    numberFormats: numberFormats
+  });
+};
+
+var prototypeAccessors = { vm: { configurable: true },messages: { configurable: true },dateTimeFormats: { configurable: true },numberFormats: { configurable: true },availableLocales: { configurable: true },locale: { configurable: true },fallbackLocale: { configurable: true },formatFallbackMessages: { configurable: true },missing: { configurable: true },formatter: { configurable: true },silentTranslationWarn: { configurable: true },silentFallbackWarn: { configurable: true },preserveDirectiveContent: { configurable: true },warnHtmlInMessage: { configurable: true },postTranslation: { configurable: true } };
+
+VueI18n.prototype._checkLocaleMessage = function _checkLocaleMessage (locale, level, message) {
+  var paths = [];
+
+  var fn = function (level, locale, message, paths) {
+    if (isPlainObject(message)) {
+      Object.keys(message).forEach(function (key) {
+        var val = message[key];
+        if (isPlainObject(val)) {
+          paths.push(key);
+          paths.push('.');
+          fn(level, locale, val, paths);
+          paths.pop();
+          paths.pop();
+        } else {
+          paths.push(key);
+          fn(level, locale, val, paths);
+          paths.pop();
+        }
+      });
+    } else if (isArray(message)) {
+      message.forEach(function (item, index) {
+        if (isPlainObject(item)) {
+          paths.push(("[" + index + "]"));
+          paths.push('.');
+          fn(level, locale, item, paths);
+          paths.pop();
+          paths.pop();
+        } else {
+          paths.push(("[" + index + "]"));
+          fn(level, locale, item, paths);
+          paths.pop();
+        }
+      });
+    } else if (isString(message)) {
+      var ret = htmlTagMatcher.test(message);
+      if (ret) {
+        var msg = "Detected HTML in message '" + message + "' of keypath '" + (paths.join('')) + "' at '" + locale + "'. Consider component interpolation with '<i18n>' to avoid XSS. See https://bit.ly/2ZqJzkp";
+        if (level === 'warn') {
+          warn(msg);
+        } else if (level === 'error') {
+          error(msg);
+        }
+      }
+    }
+  };
+
+  fn(level, locale, message, paths);
+};
+
+VueI18n.prototype._initVM = function _initVM (data) {
+  var silent = Vue.config.silent;
+  Vue.config.silent = true;
+  this._vm = new Vue({ data: data });
+  Vue.config.silent = silent;
+};
+
+VueI18n.prototype.destroyVM = function destroyVM () {
+  this._vm.$destroy();
+};
+
+VueI18n.prototype.subscribeDataChanging = function subscribeDataChanging (vm) {
+  this._dataListeners.push(vm);
+};
+
+VueI18n.prototype.unsubscribeDataChanging = function unsubscribeDataChanging (vm) {
+  remove(this._dataListeners, vm);
+};
+
+VueI18n.prototype.watchI18nData = function watchI18nData () {
+  var self = this;
+  return this._vm.$watch('$data', function () {
+    var i = self._dataListeners.length;
+    while (i--) {
+      Vue.nextTick(function () {
+        self._dataListeners[i] && self._dataListeners[i].$forceUpdate();
+      });
+    }
+  }, { deep: true })
+};
+
+VueI18n.prototype.watchLocale = function watchLocale () {
+  /* istanbul ignore if */
+  if (!this._sync || !this._root) { return null }
+  var target = this._vm;
+  return this._root.$i18n.vm.$watch('locale', function (val) {
+    target.$set(target, 'locale', val);
+    target.$forceUpdate();
+  }, { immediate: true })
+};
+
+VueI18n.prototype.onComponentInstanceCreated = function onComponentInstanceCreated (newI18n) {
+  if (this._componentInstanceCreatedListener) {
+    this._componentInstanceCreatedListener(newI18n, this);
+  }
+};
+
+prototypeAccessors.vm.get = function () { return this._vm };
+
+prototypeAccessors.messages.get = function () { return looseClone(this._getMessages()) };
+prototypeAccessors.dateTimeFormats.get = function () { return looseClone(this._getDateTimeFormats()) };
+prototypeAccessors.numberFormats.get = function () { return looseClone(this._getNumberFormats()) };
+prototypeAccessors.availableLocales.get = function () { return Object.keys(this.messages).sort() };
+
+prototypeAccessors.locale.get = function () { return this._vm.locale };
+prototypeAccessors.locale.set = function (locale) {
+  this._vm.$set(this._vm, 'locale', locale);
+};
+
+prototypeAccessors.fallbackLocale.get = function () { return this._vm.fallbackLocale };
+prototypeAccessors.fallbackLocale.set = function (locale) {
+  this._localeChainCache = {};
+  this._vm.$set(this._vm, 'fallbackLocale', locale);
+};
+
+prototypeAccessors.formatFallbackMessages.get = function () { return this._formatFallbackMessages };
+prototypeAccessors.formatFallbackMessages.set = function (fallback) { this._formatFallbackMessages = fallback; };
+
+prototypeAccessors.missing.get = function () { return this._missing };
+prototypeAccessors.missing.set = function (handler) { this._missing = handler; };
+
+prototypeAccessors.formatter.get = function () { return this._formatter };
+prototypeAccessors.formatter.set = function (formatter) { this._formatter = formatter; };
+
+prototypeAccessors.silentTranslationWarn.get = function () { return this._silentTranslationWarn };
+prototypeAccessors.silentTranslationWarn.set = function (silent) { this._silentTranslationWarn = silent; };
+
+prototypeAccessors.silentFallbackWarn.get = function () { return this._silentFallbackWarn };
+prototypeAccessors.silentFallbackWarn.set = function (silent) { this._silentFallbackWarn = silent; };
+
+prototypeAccessors.preserveDirectiveContent.get = function () { return this._preserveDirectiveContent };
+prototypeAccessors.preserveDirectiveContent.set = function (preserve) { this._preserveDirectiveContent = preserve; };
+
+prototypeAccessors.warnHtmlInMessage.get = function () { return this._warnHtmlInMessage };
+prototypeAccessors.warnHtmlInMessage.set = function (level) {
+    var this$1 = this;
+
+  var orgLevel = this._warnHtmlInMessage;
+  this._warnHtmlInMessage = level;
+  if (orgLevel !== level && (level === 'warn' || level === 'error')) {
+    var messages = this._getMessages();
+    Object.keys(messages).forEach(function (locale) {
+      this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);
+    });
+  }
+};
+
+prototypeAccessors.postTranslation.get = function () { return this._postTranslation };
+prototypeAccessors.postTranslation.set = function (handler) { this._postTranslation = handler; };
+
+VueI18n.prototype._getMessages = function _getMessages () { return this._vm.messages };
+VueI18n.prototype._getDateTimeFormats = function _getDateTimeFormats () { return this._vm.dateTimeFormats };
+VueI18n.prototype._getNumberFormats = function _getNumberFormats () { return this._vm.numberFormats };
+
+VueI18n.prototype._warnDefault = function _warnDefault (locale, key, result, vm, values, interpolateMode) {
+  if (!isNull(result)) { return result }
+  if (this._missing) {
+    var missingRet = this._missing.apply(null, [locale, key, vm, values]);
+    if (isString(missingRet)) {
+      return missingRet
+    }
+  } else {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+      warn(
+        "Cannot translate the value of keypath '" + key + "'. " +
+        'Use the value of keypath as default.'
+      );
+    }
+  }
+
+  if (this._formatFallbackMessages) {
+    var parsedArgs = parseArgs.apply(void 0, values);
+    return this._render(key, interpolateMode, parsedArgs.params, key)
+  } else {
+    return key
+  }
+};
+
+VueI18n.prototype._isFallbackRoot = function _isFallbackRoot (val) {
+  return !val && !isNull(this._root) && this._fallbackRoot
+};
+
+VueI18n.prototype._isSilentFallbackWarn = function _isSilentFallbackWarn (key) {
+  return this._silentFallbackWarn instanceof RegExp
+    ? this._silentFallbackWarn.test(key)
+    : this._silentFallbackWarn
+};
+
+VueI18n.prototype._isSilentFallback = function _isSilentFallback (locale, key) {
+  return this._isSilentFallbackWarn(key) && (this._isFallbackRoot() || locale !== this.fallbackLocale)
+};
+
+VueI18n.prototype._isSilentTranslationWarn = function _isSilentTranslationWarn (key) {
+  return this._silentTranslationWarn instanceof RegExp
+    ? this._silentTranslationWarn.test(key)
+    : this._silentTranslationWarn
+};
+
+VueI18n.prototype._interpolate = function _interpolate (
+  locale,
+  message,
+  key,
+  host,
+  interpolateMode,
+  values,
+  visitedLinkStack
+) {
+  if (!message) { return null }
+
+  var pathRet = this._path.getPathValue(message, key);
+  if (isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
+
+  var ret;
+  if (isNull(pathRet)) {
+    /* istanbul ignore else */
+    if (isPlainObject(message)) {
+      ret = message[key];
+      if (!(isString(ret) || isFunction(ret))) {
+        if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+          warn(("Value of key '" + key + "' is not a string or function !"));
+        }
+        return null
+      }
+    } else {
+      return null
+    }
+  } else {
+    /* istanbul ignore else */
+    if (isString(pathRet) || isFunction(pathRet)) {
+      ret = pathRet;
+    } else {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+        warn(("Value of key '" + key + "' is not a string or function!"));
+      }
+      return null
+    }
+  }
+
+  // Check for the existence of links within the translated string
+  if (isString(ret) && (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0)) {
+    ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack);
+  }
+
+  return this._render(ret, interpolateMode, values, key)
+};
+
+VueI18n.prototype._link = function _link (
+  locale,
+  message,
+  str,
+  host,
+  interpolateMode,
+  values,
+  visitedLinkStack
+) {
+  var ret = str;
+
+  // Match all the links within the local
+  // We are going to replace each of
+  // them with its translation
+  var matches = ret.match(linkKeyMatcher);
+  for (var idx in matches) {
+    // ie compatible: filter custom array
+    // prototype method
+    if (!matches.hasOwnProperty(idx)) {
+      continue
+    }
+    var link = matches[idx];
+    var linkKeyPrefixMatches = link.match(linkKeyPrefixMatcher);
+    var linkPrefix = linkKeyPrefixMatches[0];
+      var formatterName = linkKeyPrefixMatches[1];
+
+    // Remove the leading @:, @.case: and the brackets
+    var linkPlaceholder = link.replace(linkPrefix, '').replace(bracketsMatcher, '');
+
+    if (includes(visitedLinkStack, linkPlaceholder)) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn(("Circular reference found. \"" + link + "\" is already visited in the chain of " + (visitedLinkStack.reverse().join(' <- '))));
+      }
+      return ret
+    }
+    visitedLinkStack.push(linkPlaceholder);
+
+    // Translate the link
+    var translated = this._interpolate(
+      locale, message, linkPlaceholder, host,
+      interpolateMode === 'raw' ? 'string' : interpolateMode,
+      interpolateMode === 'raw' ? undefined : values,
+      visitedLinkStack
+    );
+
+    if (this._isFallbackRoot(translated)) {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(linkPlaceholder)) {
+        warn(("Fall back to translate the link placeholder '" + linkPlaceholder + "' with root locale."));
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      var root = this._root.$i18n;
+      translated = root._translate(
+        root._getMessages(), root.locale, root.fallbackLocale,
+        linkPlaceholder, host, interpolateMode, values
+      );
+    }
+    translated = this._warnDefault(
+      locale, linkPlaceholder, translated, host,
+      isArray(values) ? values : [values],
+      interpolateMode
+    );
+
+    if (this._modifiers.hasOwnProperty(formatterName)) {
+      translated = this._modifiers[formatterName](translated);
+    } else if (defaultModifiers.hasOwnProperty(formatterName)) {
+      translated = defaultModifiers[formatterName](translated);
+    }
+
+    visitedLinkStack.pop();
+
+    // Replace the link with the translated
+    ret = !translated ? ret : ret.replace(link, translated);
+  }
+
+  return ret
+};
+
+VueI18n.prototype._createMessageContext = function _createMessageContext (values) {
+  var _list = isArray(values) ? values : [];
+  var _named = isObject(values) ? values : {};
+  var list = function (index) { return _list[index]; };
+  var named = function (key) { return _named[key]; };
+  return {
+    list: list,
+    named: named
+  }
+};
+
+VueI18n.prototype._render = function _render (message, interpolateMode, values, path) {
+  if (isFunction(message)) {
+    return message(this._createMessageContext(values))
+  }
+
+  var ret = this._formatter.interpolate(message, values, path);
+
+  // If the custom formatter refuses to work - apply the default one
+  if (!ret) {
+    ret = defaultFormatter.interpolate(message, values, path);
+  }
+
+  // if interpolateMode is **not** 'string' ('row'),
+  // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
+  return interpolateMode === 'string' && !isString(ret) ? ret.join('') : ret
+};
+
+VueI18n.prototype._appendItemToChain = function _appendItemToChain (chain, item, blocks) {
+  var follow = false;
+  if (!includes(chain, item)) {
+    follow = true;
+    if (item) {
+      follow = item[item.length - 1] !== '!';
+      item = item.replace(/!/g, '');
+      chain.push(item);
+      if (blocks && blocks[item]) {
+        follow = blocks[item];
+      }
+    }
+  }
+  return follow
+};
+
+VueI18n.prototype._appendLocaleToChain = function _appendLocaleToChain (chain, locale, blocks) {
+  var follow;
+  var tokens = locale.split('-');
+  do {
+    var item = tokens.join('-');
+    follow = this._appendItemToChain(chain, item, blocks);
+    tokens.splice(-1, 1);
+  } while (tokens.length && (follow === true))
+  return follow
+};
+
+VueI18n.prototype._appendBlockToChain = function _appendBlockToChain (chain, block, blocks) {
+  var follow = true;
+  for (var i = 0; (i < block.length) && (isBoolean(follow)); i++) {
+    var locale = block[i];
+    if (isString(locale)) {
+      follow = this._appendLocaleToChain(chain, locale, blocks);
+    }
+  }
+  return follow
+};
+
+VueI18n.prototype._getLocaleChain = function _getLocaleChain (start, fallbackLocale) {
+  if (start === '') { return [] }
+
+  if (!this._localeChainCache) {
+    this._localeChainCache = {};
+  }
+
+  var chain = this._localeChainCache[start];
+  if (!chain) {
+    if (!fallbackLocale) {
+      fallbackLocale = this.fallbackLocale;
+    }
+    chain = [];
+
+    // first block defined by start
+    var block = [start];
+
+    // while any intervening block found
+    while (isArray(block)) {
+      block = this._appendBlockToChain(
+        chain,
+        block,
+        fallbackLocale
+      );
+    }
+
+    // last block defined by default
+    var defaults;
+    if (isArray(fallbackLocale)) {
+      defaults = fallbackLocale;
+    } else if (isObject(fallbackLocale)) {
+      /* $FlowFixMe */
+      if (fallbackLocale['default']) {
+        defaults = fallbackLocale['default'];
+      } else {
+        defaults = null;
+      }
+    } else {
+      defaults = fallbackLocale;
+    }
+
+    // convert defaults to array
+    if (isString(defaults)) {
+      block = [defaults];
+    } else {
+      block = defaults;
+    }
+    if (block) {
+      this._appendBlockToChain(
+        chain,
+        block,
+        null
+      );
+    }
+    this._localeChainCache[start] = chain;
+  }
+  return chain
+};
+
+VueI18n.prototype._translate = function _translate (
+  messages,
+  locale,
+  fallback,
+  key,
+  host,
+  interpolateMode,
+  args
+) {
+  var chain = this._getLocaleChain(locale, fallback);
+  var res;
+  for (var i = 0; i < chain.length; i++) {
+    var step = chain[i];
+    res =
+      this._interpolate(step, messages[step], key, host, interpolateMode, args, [key]);
+    if (!isNull(res)) {
+      if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to translate the keypath '" + key + "' with '" + step + "' locale."));
+      }
+      return res
+    }
+  }
+  return null
+};
+
+VueI18n.prototype._t = function _t (key, _locale, messages, host) {
+    var ref;
+
+    var values = [], len = arguments.length - 4;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 4 ];
+  if (!key) { return '' }
+
+  var parsedArgs = parseArgs.apply(void 0, values);
+  var locale = parsedArgs.locale || _locale;
+
+  var ret = this._translate(
+    messages, locale, this.fallbackLocale, key,
+    host, 'string', parsedArgs.params
+  );
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+      warn(("Fall back to translate the keypath '" + key + "' with root locale."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return (ref = this._root).$t.apply(ref, [ key ].concat( values ))
+  } else {
+    ret = this._warnDefault(locale, key, ret, host, values, 'string');
+    if (this._postTranslation && ret !== null && ret !== undefined) {
+      ret = this._postTranslation(ret, key);
+    }
+    return ret
+  }
+};
+
+VueI18n.prototype.t = function t (key) {
+    var ref;
+
+    var values = [], len = arguments.length - 1;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
+  return (ref = this)._t.apply(ref, [ key, this.locale, this._getMessages(), null ].concat( values ))
+};
+
+VueI18n.prototype._i = function _i (key, locale, messages, host, values) {
+  var ret =
+    this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+      warn(("Fall back to interpolate the keypath '" + key + "' with root locale."));
+    }
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n.i(key, locale, values)
+  } else {
+    return this._warnDefault(locale, key, ret, host, [values], 'raw')
+  }
+};
+
+VueI18n.prototype.i = function i (key, locale, values) {
+  /* istanbul ignore if */
+  if (!key) { return '' }
+
+  if (!isString(locale)) {
+    locale = this.locale;
+  }
+
+  return this._i(key, locale, this._getMessages(), null, values)
+};
+
+VueI18n.prototype._tc = function _tc (
+  key,
+  _locale,
+  messages,
+  host,
+  choice
+) {
+    var ref;
+
+    var values = [], len = arguments.length - 5;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 5 ];
+  if (!key) { return '' }
+  if (choice === undefined) {
+    choice = 1;
+  }
+
+  var predefined = { 'count': choice, 'n': choice };
+  var parsedArgs = parseArgs.apply(void 0, values);
+  parsedArgs.params = Object.assign(predefined, parsedArgs.params);
+  values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params];
+  return this.fetchChoice((ref = this)._t.apply(ref, [ key, _locale, messages, host ].concat( values )), choice)
+};
+
+VueI18n.prototype.fetchChoice = function fetchChoice (message, choice) {
+  /* istanbul ignore if */
+  if (!message && !isString(message)) { return null }
+  var choices = message.split('|');
+
+  choice = this.getChoiceIndex(choice, choices.length);
+  if (!choices[choice]) { return message }
+  return choices[choice].trim()
+};
+
+VueI18n.prototype.tc = function tc (key, choice) {
+    var ref;
+
+    var values = [], len = arguments.length - 2;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
+  return (ref = this)._tc.apply(ref, [ key, this.locale, this._getMessages(), null, choice ].concat( values ))
+};
+
+VueI18n.prototype._te = function _te (key, locale, messages) {
+    var args = [], len = arguments.length - 3;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 3 ];
+
+  var _locale = parseArgs.apply(void 0, args).locale || locale;
+  return this._exist(messages[_locale], key)
+};
+
+VueI18n.prototype.te = function te (key, locale) {
+  return this._te(key, this.locale, this._getMessages(), locale)
+};
+
+VueI18n.prototype.getLocaleMessage = function getLocaleMessage (locale) {
+  return looseClone(this._vm.messages[locale] || {})
+};
+
+VueI18n.prototype.setLocaleMessage = function setLocaleMessage (locale, message) {
+  if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+    this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+  }
+  this._vm.$set(this._vm.messages, locale, message);
+};
+
+VueI18n.prototype.mergeLocaleMessage = function mergeLocaleMessage (locale, message) {
+  if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+    this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+  }
+  this._vm.$set(this._vm.messages, locale, merge({}, this._vm.messages[locale] || {}, message));
+};
+
+VueI18n.prototype.getDateTimeFormat = function getDateTimeFormat (locale) {
+  return looseClone(this._vm.dateTimeFormats[locale] || {})
+};
+
+VueI18n.prototype.setDateTimeFormat = function setDateTimeFormat (locale, format) {
+  this._vm.$set(this._vm.dateTimeFormats, locale, format);
+  this._clearDateTimeFormat(locale, format);
+};
+
+VueI18n.prototype.mergeDateTimeFormat = function mergeDateTimeFormat (locale, format) {
+  this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format));
+  this._clearDateTimeFormat(locale, format);
+};
+
+VueI18n.prototype._clearDateTimeFormat = function _clearDateTimeFormat (locale, format) {
+  for (var key in format) {
+    var id = locale + "__" + key;
+
+    if (!this._dateTimeFormatters.hasOwnProperty(id)) {
+      continue
+    }
+
+    delete this._dateTimeFormatters[id];
+  }
+};
+
+VueI18n.prototype._localizeDateTime = function _localizeDateTime (
+  value,
+  locale,
+  fallback,
+  dateTimeFormats,
+  key
+) {
+  var _locale = locale;
+  var formats = dateTimeFormats[_locale];
+
+  var chain = this._getLocaleChain(locale, fallback);
+  for (var i = 0; i < chain.length; i++) {
+    var current = _locale;
+    var step = chain[i];
+    formats = dateTimeFormats[step];
+    _locale = step;
+    // fallback locale
+    if (isNull(formats) || isNull(formats[key])) {
+      if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to '" + step + "' datetime formats from '" + current + "' datetime formats."));
+      }
+    } else {
+      break
+    }
+  }
+
+  if (isNull(formats) || isNull(formats[key])) {
+    return null
+  } else {
+    var format = formats[key];
+    var id = _locale + "__" + key;
+    var formatter = this._dateTimeFormatters[id];
+    if (!formatter) {
+      formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format);
+    }
+    return formatter.format(value)
+  }
+};
+
+VueI18n.prototype._d = function _d (value, locale, key) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.dateTimeFormat) {
+    warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.');
+    return ''
+  }
+
+  if (!key) {
+    return new Intl.DateTimeFormat(locale).format(value)
+  }
+
+  var ret =
+    this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+      warn(("Fall back to datetime localization of root: key '" + key + "'."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n.d(value, key, locale)
+  } else {
+    return ret || ''
+  }
+};
+
+VueI18n.prototype.d = function d (value) {
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+
+  var locale = this.locale;
+  var key = null;
+
+  if (args.length === 1) {
+    if (isString(args[0])) {
+      key = args[0];
+    } else if (isObject(args[0])) {
+      if (args[0].locale) {
+        locale = args[0].locale;
+      }
+      if (args[0].key) {
+        key = args[0].key;
+      }
+    }
+  } else if (args.length === 2) {
+    if (isString(args[0])) {
+      key = args[0];
+    }
+    if (isString(args[1])) {
+      locale = args[1];
+    }
+  }
+
+  return this._d(value, locale, key)
+};
+
+VueI18n.prototype.getNumberFormat = function getNumberFormat (locale) {
+  return looseClone(this._vm.numberFormats[locale] || {})
+};
+
+VueI18n.prototype.setNumberFormat = function setNumberFormat (locale, format) {
+  this._vm.$set(this._vm.numberFormats, locale, format);
+  this._clearNumberFormat(locale, format);
+};
+
+VueI18n.prototype.mergeNumberFormat = function mergeNumberFormat (locale, format) {
+  this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format));
+  this._clearNumberFormat(locale, format);
+};
+
+VueI18n.prototype._clearNumberFormat = function _clearNumberFormat (locale, format) {
+  for (var key in format) {
+    var id = locale + "__" + key;
+
+    if (!this._numberFormatters.hasOwnProperty(id)) {
+      continue
+    }
+
+    delete this._numberFormatters[id];
+  }
+};
+
+VueI18n.prototype._getNumberFormatter = function _getNumberFormatter (
+  value,
+  locale,
+  fallback,
+  numberFormats,
+  key,
+  options
+) {
+  var _locale = locale;
+  var formats = numberFormats[_locale];
+
+  var chain = this._getLocaleChain(locale, fallback);
+  for (var i = 0; i < chain.length; i++) {
+    var current = _locale;
+    var step = chain[i];
+    formats = numberFormats[step];
+    _locale = step;
+    // fallback locale
+    if (isNull(formats) || isNull(formats[key])) {
+      if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to '" + step + "' number formats from '" + current + "' number formats."));
+      }
+    } else {
+      break
+    }
+  }
+
+  if (isNull(formats) || isNull(formats[key])) {
+    return null
+  } else {
+    var format = formats[key];
+
+    var formatter;
+    if (options) {
+      // If options specified - create one time number formatter
+      formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options));
+    } else {
+      var id = _locale + "__" + key;
+      formatter = this._numberFormatters[id];
+      if (!formatter) {
+        formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format);
+      }
+    }
+    return formatter
+  }
+};
+
+VueI18n.prototype._n = function _n (value, locale, key, options) {
+  /* istanbul ignore if */
+  if (!VueI18n.availabilities.numberFormat) {
+    if (process.env.NODE_ENV !== 'production') {
+      warn('Cannot format a Number value due to not supported Intl.NumberFormat.');
+    }
+    return ''
+  }
+
+  if (!key) {
+    var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+    return nf.format(value)
+  }
+
+  var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+  var ret = formatter && formatter.format(value);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+      warn(("Fall back to number localization of root: key '" + key + "'."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n.n(value, Object.assign({}, { key: key, locale: locale }, options))
+  } else {
+    return ret || ''
+  }
+};
+
+VueI18n.prototype.n = function n (value) {
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+
+  var locale = this.locale;
+  var key = null;
+  var options = null;
+
+  if (args.length === 1) {
+    if (isString(args[0])) {
+      key = args[0];
+    } else if (isObject(args[0])) {
+      if (args[0].locale) {
+        locale = args[0].locale;
+      }
+      if (args[0].key) {
+        key = args[0].key;
+      }
+
+      // Filter out number format options only
+      options = Object.keys(args[0]).reduce(function (acc, key) {
+          var obj;
+
+        if (includes(numberFormatKeys, key)) {
+          return Object.assign({}, acc, ( obj = {}, obj[key] = args[0][key], obj ))
+        }
+        return acc
+      }, null);
+    }
+  } else if (args.length === 2) {
+    if (isString(args[0])) {
+      key = args[0];
+    }
+    if (isString(args[1])) {
+      locale = args[1];
+    }
+  }
+
+  return this._n(value, locale, key, options)
+};
+
+VueI18n.prototype._ntp = function _ntp (value, locale, key, options) {
+  /* istanbul ignore if */
+  if (!VueI18n.availabilities.numberFormat) {
+    if (process.env.NODE_ENV !== 'production') {
+      warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.');
+    }
+    return []
+  }
+
+  if (!key) {
+    var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+    return nf.formatToParts(value)
+  }
+
+  var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+  var ret = formatter && formatter.formatToParts(value);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+      warn(("Fall back to format number to parts of root: key '" + key + "' ."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n._ntp(value, locale, key, options)
+  } else {
+    return ret || []
+  }
+};
+
+Object.defineProperties( VueI18n.prototype, prototypeAccessors );
+
+var availabilities;
+// $FlowFixMe
+Object.defineProperty(VueI18n, 'availabilities', {
+  get: function get () {
+    if (!availabilities) {
+      var intlDefined = typeof Intl !== 'undefined';
+      availabilities = {
+        dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
+        numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
+      };
+    }
+
+    return availabilities
+  }
+});
+
+VueI18n.install = install;
+VueI18n.version = '8.21.0';
+
+module.exports = VueI18n;

+ 2104 - 0
plugin/vue-i18n/dist/vue-i18n.esm.browser.js

@@ -0,0 +1,2104 @@
+/*  */
+
+/**
+ * constants
+ */
+
+const numberFormatKeys = [
+  'style',
+  'currency',
+  'currencyDisplay',
+  'useGrouping',
+  'minimumIntegerDigits',
+  'minimumFractionDigits',
+  'maximumFractionDigits',
+  'minimumSignificantDigits',
+  'maximumSignificantDigits',
+  'localeMatcher',
+  'formatMatcher',
+  'unit'
+];
+
+/**
+ * utilities
+ */
+
+function warn (msg, err) {
+  if (typeof console !== 'undefined') {
+    console.warn('[vue-i18n] ' + msg);
+    /* istanbul ignore if */
+    if (err) {
+      console.warn(err.stack);
+    }
+  }
+}
+
+function error (msg, err) {
+  if (typeof console !== 'undefined') {
+    console.error('[vue-i18n] ' + msg);
+    /* istanbul ignore if */
+    if (err) {
+      console.error(err.stack);
+    }
+  }
+}
+
+const isArray = Array.isArray;
+
+function isObject (obj) {
+  return obj !== null && typeof obj === 'object'
+}
+
+function isBoolean (val) {
+  return typeof val === 'boolean'
+}
+
+function isString (val) {
+  return typeof val === 'string'
+}
+
+const toString = Object.prototype.toString;
+const OBJECT_STRING = '[object Object]';
+function isPlainObject (obj) {
+  return toString.call(obj) === OBJECT_STRING
+}
+
+function isNull (val) {
+  return val === null || val === undefined
+}
+
+function isFunction (val) {
+  return typeof val === 'function'
+}
+
+function parseArgs (...args) {
+  let locale = null;
+  let params = null;
+  if (args.length === 1) {
+    if (isObject(args[0]) || isArray(args[0])) {
+      params = args[0];
+    } else if (typeof args[0] === 'string') {
+      locale = args[0];
+    }
+  } else if (args.length === 2) {
+    if (typeof args[0] === 'string') {
+      locale = args[0];
+    }
+    /* istanbul ignore if */
+    if (isObject(args[1]) || isArray(args[1])) {
+      params = args[1];
+    }
+  }
+
+  return { locale, params }
+}
+
+function looseClone (obj) {
+  return JSON.parse(JSON.stringify(obj))
+}
+
+function remove (arr, item) {
+  if (arr.length) {
+    const index = arr.indexOf(item);
+    if (index > -1) {
+      return arr.splice(index, 1)
+    }
+  }
+}
+
+function includes (arr, item) {
+  return !!~arr.indexOf(item)
+}
+
+const hasOwnProperty = Object.prototype.hasOwnProperty;
+function hasOwn (obj, key) {
+  return hasOwnProperty.call(obj, key)
+}
+
+function merge (target) {
+  const output = Object(target);
+  for (let i = 1; i < arguments.length; i++) {
+    const source = arguments[i];
+    if (source !== undefined && source !== null) {
+      let key;
+      for (key in source) {
+        if (hasOwn(source, key)) {
+          if (isObject(source[key])) {
+            output[key] = merge(output[key], source[key]);
+          } else {
+            output[key] = source[key];
+          }
+        }
+      }
+    }
+  }
+  return output
+}
+
+function looseEqual (a, b) {
+  if (a === b) { return true }
+  const isObjectA = isObject(a);
+  const isObjectB = isObject(b);
+  if (isObjectA && isObjectB) {
+    try {
+      const isArrayA = isArray(a);
+      const isArrayB = isArray(b);
+      if (isArrayA && isArrayB) {
+        return a.length === b.length && a.every((e, i) => {
+          return looseEqual(e, b[i])
+        })
+      } else if (!isArrayA && !isArrayB) {
+        const keysA = Object.keys(a);
+        const keysB = Object.keys(b);
+        return keysA.length === keysB.length && keysA.every((key) => {
+          return looseEqual(a[key], b[key])
+        })
+      } else {
+        /* istanbul ignore next */
+        return false
+      }
+    } catch (e) {
+      /* istanbul ignore next */
+      return false
+    }
+  } else if (!isObjectA && !isObjectB) {
+    return String(a) === String(b)
+  } else {
+    return false
+  }
+}
+
+/*  */
+
+function extend (Vue) {
+  if (!Vue.prototype.hasOwnProperty('$i18n')) {
+    // $FlowFixMe
+    Object.defineProperty(Vue.prototype, '$i18n', {
+      get () { return this._i18n }
+    });
+  }
+
+  Vue.prototype.$t = function (key, ...values) {
+    const i18n = this.$i18n;
+    return i18n._t(key, i18n.locale, i18n._getMessages(), this, ...values)
+  };
+
+  Vue.prototype.$tc = function (key, choice, ...values) {
+    const i18n = this.$i18n;
+    return i18n._tc(key, i18n.locale, i18n._getMessages(), this, choice, ...values)
+  };
+
+  Vue.prototype.$te = function (key, locale) {
+    const i18n = this.$i18n;
+    return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
+  };
+
+  Vue.prototype.$d = function (value, ...args) {
+    return this.$i18n.d(value, ...args)
+  };
+
+  Vue.prototype.$n = function (value, ...args) {
+    return this.$i18n.n(value, ...args)
+  };
+}
+
+/*  */
+
+var mixin = {
+  beforeCreate () {
+    const options = this.$options;
+    options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            let localeMessages = {};
+            options.__i18n.forEach(resource => {
+              localeMessages = merge(localeMessages, JSON.parse(resource));
+            });
+            Object.keys(localeMessages).forEach((locale) => {
+              options.i18n.mergeLocaleMessage(locale, localeMessages[locale]);
+            });
+          } catch (e) {
+            {
+              error(`Cannot parse locale messages via custom blocks.`, e);
+            }
+          }
+        }
+        this._i18n = options.i18n;
+        this._i18nWatcher = this._i18n.watchI18nData();
+      } else if (isPlainObject(options.i18n)) {
+        const rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n
+          ? this.$root.$i18n
+          : null;
+        // component local i18n
+        if (rootI18n) {
+          options.i18n.root = this.$root;
+          options.i18n.formatter = rootI18n.formatter;
+          options.i18n.fallbackLocale = rootI18n.fallbackLocale;
+          options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages;
+          options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn;
+          options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn;
+          options.i18n.pluralizationRules = rootI18n.pluralizationRules;
+          options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent;
+        }
+
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            let localeMessages = {};
+            options.__i18n.forEach(resource => {
+              localeMessages = merge(localeMessages, JSON.parse(resource));
+            });
+            options.i18n.messages = localeMessages;
+          } catch (e) {
+            {
+              warn(`Cannot parse locale messages via custom blocks.`, e);
+            }
+          }
+        }
+
+        const { sharedMessages } = options.i18n;
+        if (sharedMessages && isPlainObject(sharedMessages)) {
+          options.i18n.messages = merge(options.i18n.messages, sharedMessages);
+        }
+
+        this._i18n = new VueI18n(options.i18n);
+        this._i18nWatcher = this._i18n.watchI18nData();
+
+        if (options.i18n.sync === undefined || !!options.i18n.sync) {
+          this._localeWatcher = this.$i18n.watchLocale();
+        }
+
+        if (rootI18n) {
+          rootI18n.onComponentInstanceCreated(this._i18n);
+        }
+      } else {
+        {
+          warn(`Cannot be interpreted 'i18n' option.`);
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      // root i18n
+      this._i18n = this.$root.$i18n;
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      // parent i18n
+      this._i18n = options.parent.$i18n;
+    }
+  },
+
+  beforeMount () {
+    const options = this.$options;
+    options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      } else if (isPlainObject(options.i18n)) {
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      } else {
+        {
+          warn(`Cannot be interpreted 'i18n' option.`);
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this);
+      this._subscribing = true;
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this);
+      this._subscribing = true;
+    }
+  },
+
+  beforeDestroy () {
+    if (!this._i18n) { return }
+
+    const self = this;
+    this.$nextTick(() => {
+      if (self._subscribing) {
+        self._i18n.unsubscribeDataChanging(self);
+        delete self._subscribing;
+      }
+
+      if (self._i18nWatcher) {
+        self._i18nWatcher();
+        self._i18n.destroyVM();
+        delete self._i18nWatcher;
+      }
+
+      if (self._localeWatcher) {
+        self._localeWatcher();
+        delete self._localeWatcher;
+      }
+    });
+  }
+};
+
+/*  */
+
+var interpolationComponent = {
+  name: 'i18n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    path: {
+      type: String,
+      required: true
+    },
+    locale: {
+      type: String
+    },
+    places: {
+      type: [Array, Object]
+    }
+  },
+  render (h, { data, parent, props, slots }) {
+    const { $i18n } = parent;
+    if (!$i18n) {
+      {
+        warn('Cannot find VueI18n instance!');
+      }
+      return
+    }
+
+    const { path, locale, places } = props;
+    const params = slots();
+    const children = $i18n.i(
+      path,
+      locale,
+      onlyHasDefaultPlace(params) || places
+        ? useLegacyPlaces(params.default, places)
+        : params
+    );
+
+    const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+    return tag ? h(tag, data, children) : children
+  }
+};
+
+function onlyHasDefaultPlace (params) {
+  let prop;
+  for (prop in params) {
+    if (prop !== 'default') { return false }
+  }
+  return Boolean(prop)
+}
+
+function useLegacyPlaces (children, places) {
+  const params = places ? createParamsFromPlaces(places) : {};
+
+  if (!children) { return params }
+
+  // Filter empty text nodes
+  children = children.filter(child => {
+    return child.tag || child.text.trim() !== ''
+  });
+
+  const everyPlace = children.every(vnodeHasPlaceAttribute);
+  if (everyPlace) {
+    warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.');
+  }
+
+  return children.reduce(
+    everyPlace ? assignChildPlace : assignChildIndex,
+    params
+  )
+}
+
+function createParamsFromPlaces (places) {
+  {
+    warn('`places` prop is deprecated in next major version. Please switch to Vue slots.');
+  }
+
+  return Array.isArray(places)
+    ? places.reduce(assignChildIndex, {})
+    : Object.assign({}, places)
+}
+
+function assignChildPlace (params, child) {
+  if (child.data && child.data.attrs && child.data.attrs.place) {
+    params[child.data.attrs.place] = child;
+  }
+  return params
+}
+
+function assignChildIndex (params, child, index) {
+  params[index] = child;
+  return params
+}
+
+function vnodeHasPlaceAttribute (vnode) {
+  return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place)
+}
+
+/*  */
+
+var numberComponent = {
+  name: 'i18n-n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    value: {
+      type: Number,
+      required: true
+    },
+    format: {
+      type: [String, Object]
+    },
+    locale: {
+      type: String
+    }
+  },
+  render (h, { props, parent, data }) {
+    const i18n = parent.$i18n;
+
+    if (!i18n) {
+      {
+        warn('Cannot find VueI18n instance!');
+      }
+      return null
+    }
+
+    let key = null;
+    let options = null;
+
+    if (isString(props.format)) {
+      key = props.format;
+    } else if (isObject(props.format)) {
+      if (props.format.key) {
+        key = props.format.key;
+      }
+
+      // Filter out number format options only
+      options = Object.keys(props.format).reduce((acc, prop) => {
+        if (includes(numberFormatKeys, prop)) {
+          return Object.assign({}, acc, { [prop]: props.format[prop] })
+        }
+        return acc
+      }, null);
+    }
+
+    const locale = props.locale || i18n.locale;
+    const parts = i18n._ntp(props.value, locale, key, options);
+
+    const values = parts.map((part, index) => {
+      const slot = data.scopedSlots && data.scopedSlots[part.type];
+      return slot ? slot({ [part.type]: part.value, index, parts }) : part.value
+    });
+
+    const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+    return tag
+      ? h(tag, {
+        attrs: data.attrs,
+        'class': data['class'],
+        staticClass: data.staticClass
+      }, values)
+      : values
+  }
+};
+
+/*  */
+
+function bind (el, binding, vnode) {
+  if (!assert(el, vnode)) { return }
+
+  t(el, binding, vnode);
+}
+
+function update (el, binding, vnode, oldVNode) {
+  if (!assert(el, vnode)) { return }
+
+  const i18n = vnode.context.$i18n;
+  if (localeEqual(el, vnode) &&
+    (looseEqual(binding.value, binding.oldValue) &&
+     looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }
+
+  t(el, binding, vnode);
+}
+
+function unbind (el, binding, vnode, oldVNode) {
+  const vm = vnode.context;
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context');
+    return
+  }
+
+  const i18n = vnode.context.$i18n || {};
+  if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {
+    el.textContent = '';
+  }
+  el._vt = undefined;
+  delete el['_vt'];
+  el._locale = undefined;
+  delete el['_locale'];
+  el._localeMessage = undefined;
+  delete el['_localeMessage'];
+}
+
+function assert (el, vnode) {
+  const vm = vnode.context;
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context');
+    return false
+  }
+
+  if (!vm.$i18n) {
+    warn('VueI18n instance does not exists in Vue instance');
+    return false
+  }
+
+  return true
+}
+
+function localeEqual (el, vnode) {
+  const vm = vnode.context;
+  return el._locale === vm.$i18n.locale
+}
+
+function t (el, binding, vnode) {
+  const value = binding.value;
+
+  const { path, locale, args, choice } = parseValue(value);
+  if (!path && !locale && !args) {
+    warn('value type not supported');
+    return
+  }
+
+  if (!path) {
+    warn('`path` is required in v-t directive');
+    return
+  }
+
+  const vm = vnode.context;
+  if (choice != null) {
+    el._vt = el.textContent = vm.$i18n.tc(path, choice, ...makeParams(locale, args));
+  } else {
+    el._vt = el.textContent = vm.$i18n.t(path, ...makeParams(locale, args));
+  }
+  el._locale = vm.$i18n.locale;
+  el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale);
+}
+
+function parseValue (value) {
+  let path;
+  let locale;
+  let args;
+  let choice;
+
+  if (isString(value)) {
+    path = value;
+  } else if (isPlainObject(value)) {
+    path = value.path;
+    locale = value.locale;
+    args = value.args;
+    choice = value.choice;
+  }
+
+  return { path, locale, args, choice }
+}
+
+function makeParams (locale, args) {
+  const params = [];
+
+  locale && params.push(locale);
+  if (args && (Array.isArray(args) || isPlainObject(args))) {
+    params.push(args);
+  }
+
+  return params
+}
+
+let Vue;
+
+function install (_Vue) {
+  /* istanbul ignore if */
+  if (install.installed && _Vue === Vue) {
+    warn('already installed.');
+    return
+  }
+  install.installed = true;
+
+  Vue = _Vue;
+
+  const version = (Vue.version && Number(Vue.version.split('.')[0])) || -1;
+  /* istanbul ignore if */
+  if (version < 2) {
+    warn(`vue-i18n (${install.version}) need to use Vue 2.0 or later (Vue: ${Vue.version}).`);
+    return
+  }
+
+  extend(Vue);
+  Vue.mixin(mixin);
+  Vue.directive('t', { bind, update, unbind });
+  Vue.component(interpolationComponent.name, interpolationComponent);
+  Vue.component(numberComponent.name, numberComponent);
+
+  // use simple mergeStrategies to prevent i18n instance lose '__proto__'
+  const strats = Vue.config.optionMergeStrategies;
+  strats.i18n = function (parentVal, childVal) {
+    return childVal === undefined
+      ? parentVal
+      : childVal
+  };
+}
+
+/*  */
+
+class BaseFormatter {
+  
+
+  constructor () {
+    this._caches = Object.create(null);
+  }
+
+  interpolate (message, values) {
+    if (!values) {
+      return [message]
+    }
+    let tokens = this._caches[message];
+    if (!tokens) {
+      tokens = parse(message);
+      this._caches[message] = tokens;
+    }
+    return compile(tokens, values)
+  }
+}
+
+
+
+const RE_TOKEN_LIST_VALUE = /^(?:\d)+/;
+const RE_TOKEN_NAMED_VALUE = /^(?:\w)+/;
+
+function parse (format) {
+  const tokens = [];
+  let position = 0;
+
+  let text = '';
+  while (position < format.length) {
+    let char = format[position++];
+    if (char === '{') {
+      if (text) {
+        tokens.push({ type: 'text', value: text });
+      }
+
+      text = '';
+      let sub = '';
+      char = format[position++];
+      while (char !== undefined && char !== '}') {
+        sub += char;
+        char = format[position++];
+      }
+      const isClosed = char === '}';
+
+      const type = RE_TOKEN_LIST_VALUE.test(sub)
+        ? 'list'
+        : isClosed && RE_TOKEN_NAMED_VALUE.test(sub)
+          ? 'named'
+          : 'unknown';
+      tokens.push({ value: sub, type });
+    } else if (char === '%') {
+      // when found rails i18n syntax, skip text capture
+      if (format[(position)] !== '{') {
+        text += char;
+      }
+    } else {
+      text += char;
+    }
+  }
+
+  text && tokens.push({ type: 'text', value: text });
+
+  return tokens
+}
+
+function compile (tokens, values) {
+  const compiled = [];
+  let index = 0;
+
+  const mode = Array.isArray(values)
+    ? 'list'
+    : isObject(values)
+      ? 'named'
+      : 'unknown';
+  if (mode === 'unknown') { return compiled }
+
+  while (index < tokens.length) {
+    const token = tokens[index];
+    switch (token.type) {
+      case 'text':
+        compiled.push(token.value);
+        break
+      case 'list':
+        compiled.push(values[parseInt(token.value, 10)]);
+        break
+      case 'named':
+        if (mode === 'named') {
+          compiled.push((values)[token.value]);
+        } else {
+          {
+            warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`);
+          }
+        }
+        break
+      case 'unknown':
+        {
+          warn(`Detect 'unknown' type of token!`);
+        }
+        break
+    }
+    index++;
+  }
+
+  return compiled
+}
+
+/*  */
+
+/**
+ *  Path parser
+ *  - Inspired:
+ *    Vue.js Path parser
+ */
+
+// actions
+const APPEND = 0;
+const PUSH = 1;
+const INC_SUB_PATH_DEPTH = 2;
+const PUSH_SUB_PATH = 3;
+
+// states
+const BEFORE_PATH = 0;
+const IN_PATH = 1;
+const BEFORE_IDENT = 2;
+const IN_IDENT = 3;
+const IN_SUB_PATH = 4;
+const IN_SINGLE_QUOTE = 5;
+const IN_DOUBLE_QUOTE = 6;
+const AFTER_PATH = 7;
+const ERROR = 8;
+
+const pathStateMachine = [];
+
+pathStateMachine[BEFORE_PATH] = {
+  'ws': [BEFORE_PATH],
+  'ident': [IN_IDENT, APPEND],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[IN_PATH] = {
+  'ws': [IN_PATH],
+  '.': [BEFORE_IDENT],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[BEFORE_IDENT] = {
+  'ws': [BEFORE_IDENT],
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND]
+};
+
+pathStateMachine[IN_IDENT] = {
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND],
+  'ws': [IN_PATH, PUSH],
+  '.': [BEFORE_IDENT, PUSH],
+  '[': [IN_SUB_PATH, PUSH],
+  'eof': [AFTER_PATH, PUSH]
+};
+
+pathStateMachine[IN_SUB_PATH] = {
+  "'": [IN_SINGLE_QUOTE, APPEND],
+  '"': [IN_DOUBLE_QUOTE, APPEND],
+  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+  ']': [IN_PATH, PUSH_SUB_PATH],
+  'eof': ERROR,
+  'else': [IN_SUB_PATH, APPEND]
+};
+
+pathStateMachine[IN_SINGLE_QUOTE] = {
+  "'": [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_SINGLE_QUOTE, APPEND]
+};
+
+pathStateMachine[IN_DOUBLE_QUOTE] = {
+  '"': [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_DOUBLE_QUOTE, APPEND]
+};
+
+/**
+ * Check if an expression is a literal value.
+ */
+
+const literalValueRE = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;
+function isLiteral (exp) {
+  return literalValueRE.test(exp)
+}
+
+/**
+ * Strip quotes from a string
+ */
+
+function stripQuotes (str) {
+  const a = str.charCodeAt(0);
+  const b = str.charCodeAt(str.length - 1);
+  return a === b && (a === 0x22 || a === 0x27)
+    ? str.slice(1, -1)
+    : str
+}
+
+/**
+ * Determine the type of a character in a keypath.
+ */
+
+function getPathCharType (ch) {
+  if (ch === undefined || ch === null) { return 'eof' }
+
+  const code = ch.charCodeAt(0);
+
+  switch (code) {
+    case 0x5B: // [
+    case 0x5D: // ]
+    case 0x2E: // .
+    case 0x22: // "
+    case 0x27: // '
+      return ch
+
+    case 0x5F: // _
+    case 0x24: // $
+    case 0x2D: // -
+      return 'ident'
+
+    case 0x09: // Tab
+    case 0x0A: // Newline
+    case 0x0D: // Return
+    case 0xA0:  // No-break space
+    case 0xFEFF:  // Byte Order Mark
+    case 0x2028:  // Line Separator
+    case 0x2029:  // Paragraph Separator
+      return 'ws'
+  }
+
+  return 'ident'
+}
+
+/**
+ * Format a subPath, return its plain form if it is
+ * a literal string or number. Otherwise prepend the
+ * dynamic indicator (*).
+ */
+
+function formatSubPath (path) {
+  const trimmed = path.trim();
+  // invalid leading 0
+  if (path.charAt(0) === '0' && isNaN(path)) { return false }
+
+  return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
+}
+
+/**
+ * Parse a string path into an array of segments
+ */
+
+function parse$1 (path) {
+  const keys = [];
+  let index = -1;
+  let mode = BEFORE_PATH;
+  let subPathDepth = 0;
+  let c;
+  let key;
+  let newChar;
+  let type;
+  let transition;
+  let action;
+  let typeMap;
+  const actions = [];
+
+  actions[PUSH] = function () {
+    if (key !== undefined) {
+      keys.push(key);
+      key = undefined;
+    }
+  };
+
+  actions[APPEND] = function () {
+    if (key === undefined) {
+      key = newChar;
+    } else {
+      key += newChar;
+    }
+  };
+
+  actions[INC_SUB_PATH_DEPTH] = function () {
+    actions[APPEND]();
+    subPathDepth++;
+  };
+
+  actions[PUSH_SUB_PATH] = function () {
+    if (subPathDepth > 0) {
+      subPathDepth--;
+      mode = IN_SUB_PATH;
+      actions[APPEND]();
+    } else {
+      subPathDepth = 0;
+      if (key === undefined) { return false }
+      key = formatSubPath(key);
+      if (key === false) {
+        return false
+      } else {
+        actions[PUSH]();
+      }
+    }
+  };
+
+  function maybeUnescapeQuote () {
+    const nextChar = path[index + 1];
+    if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
+      (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
+      index++;
+      newChar = '\\' + nextChar;
+      actions[APPEND]();
+      return true
+    }
+  }
+
+  while (mode !== null) {
+    index++;
+    c = path[index];
+
+    if (c === '\\' && maybeUnescapeQuote()) {
+      continue
+    }
+
+    type = getPathCharType(c);
+    typeMap = pathStateMachine[mode];
+    transition = typeMap[type] || typeMap['else'] || ERROR;
+
+    if (transition === ERROR) {
+      return // parse error
+    }
+
+    mode = transition[0];
+    action = actions[transition[1]];
+    if (action) {
+      newChar = transition[2];
+      newChar = newChar === undefined
+        ? c
+        : newChar;
+      if (action() === false) {
+        return
+      }
+    }
+
+    if (mode === AFTER_PATH) {
+      return keys
+    }
+  }
+}
+
+
+
+
+
+class I18nPath {
+  
+
+  constructor () {
+    this._cache = Object.create(null);
+  }
+
+  /**
+   * External parse that check for a cache hit first
+   */
+  parsePath (path) {
+    let hit = this._cache[path];
+    if (!hit) {
+      hit = parse$1(path);
+      if (hit) {
+        this._cache[path] = hit;
+      }
+    }
+    return hit || []
+  }
+
+  /**
+   * Get path value from path string
+   */
+  getPathValue (obj, path) {
+    if (!isObject(obj)) { return null }
+
+    const paths = this.parsePath(path);
+    if (paths.length === 0) {
+      return null
+    } else {
+      const length = paths.length;
+      let last = obj;
+      let i = 0;
+      while (i < length) {
+        const value = last[paths[i]];
+        if (value === undefined) {
+          return null
+        }
+        last = value;
+        i++;
+      }
+
+      return last
+    }
+  }
+}
+
+/*  */
+
+
+
+const htmlTagMatcher = /<\/?[\w\s="/.':;#-\/]+>/;
+const linkKeyMatcher = /(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g;
+const linkKeyPrefixMatcher = /^@(?:\.([a-z]+))?:/;
+const bracketsMatcher = /[()]/g;
+const defaultModifiers = {
+  'upper': str => str.toLocaleUpperCase(),
+  'lower': str => str.toLocaleLowerCase(),
+  'capitalize': str => `${str.charAt(0).toLocaleUpperCase()}${str.substr(1)}`
+};
+
+const defaultFormatter = new BaseFormatter();
+
+class VueI18n {
+  
+  
+  
+
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+
+  constructor (options = {}) {
+    // Auto install if it is not done yet and `window` has `Vue`.
+    // To allow users to avoid auto-installation in some cases,
+    // this code should be placed here. See #290
+    /* istanbul ignore if */
+    if (!Vue && typeof window !== 'undefined' && window.Vue) {
+      install(window.Vue);
+    }
+
+    const locale = options.locale || 'en-US';
+    const fallbackLocale = options.fallbackLocale === false
+      ? false
+      : options.fallbackLocale || 'en-US';
+    const messages = options.messages || {};
+    const dateTimeFormats = options.dateTimeFormats || {};
+    const numberFormats = options.numberFormats || {};
+
+    this._vm = null;
+    this._formatter = options.formatter || defaultFormatter;
+    this._modifiers = options.modifiers || {};
+    this._missing = options.missing || null;
+    this._root = options.root || null;
+    this._sync = options.sync === undefined ? true : !!options.sync;
+    this._fallbackRoot = options.fallbackRoot === undefined
+      ? true
+      : !!options.fallbackRoot;
+    this._formatFallbackMessages = options.formatFallbackMessages === undefined
+      ? false
+      : !!options.formatFallbackMessages;
+    this._silentTranslationWarn = options.silentTranslationWarn === undefined
+      ? false
+      : options.silentTranslationWarn;
+    this._silentFallbackWarn = options.silentFallbackWarn === undefined
+      ? false
+      : !!options.silentFallbackWarn;
+    this._dateTimeFormatters = {};
+    this._numberFormatters = {};
+    this._path = new I18nPath();
+    this._dataListeners = [];
+    this._componentInstanceCreatedListener = options.componentInstanceCreatedListener || null;
+    this._preserveDirectiveContent = options.preserveDirectiveContent === undefined
+      ? false
+      : !!options.preserveDirectiveContent;
+    this.pluralizationRules = options.pluralizationRules || {};
+    this._warnHtmlInMessage = options.warnHtmlInMessage || 'off';
+    this._postTranslation = options.postTranslation || null;
+
+    /**
+     * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
+     * @param choicesLength {number} an overall amount of available choices
+     * @returns a final choice index
+    */
+    this.getChoiceIndex = (choice, choicesLength) => {
+      const thisPrototype = Object.getPrototypeOf(this);
+      if (thisPrototype && thisPrototype.getChoiceIndex) {
+        const prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex);
+        return (prototypeGetChoiceIndex).call(this, choice, choicesLength)
+      }
+
+      // Default (old) getChoiceIndex implementation - english-compatible
+      const defaultImpl = (_choice, _choicesLength) => {
+        _choice = Math.abs(_choice);
+
+        if (_choicesLength === 2) {
+          return _choice
+            ? _choice > 1
+              ? 1
+              : 0
+            : 1
+        }
+
+        return _choice ? Math.min(_choice, 2) : 0
+      };
+
+      if (this.locale in this.pluralizationRules) {
+        return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
+      } else {
+        return defaultImpl(choice, choicesLength)
+      }
+    };
+
+
+    this._exist = (message, key) => {
+      if (!message || !key) { return false }
+      if (!isNull(this._path.getPathValue(message, key))) { return true }
+      // fallback for flat key
+      if (message[key]) { return true }
+      return false
+    };
+
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      Object.keys(messages).forEach(locale => {
+        this._checkLocaleMessage(locale, this._warnHtmlInMessage, messages[locale]);
+      });
+    }
+
+    this._initVM({
+      locale,
+      fallbackLocale,
+      messages,
+      dateTimeFormats,
+      numberFormats
+    });
+  }
+
+  _checkLocaleMessage (locale, level, message) {
+    const paths = [];
+
+    const fn = (level, locale, message, paths) => {
+      if (isPlainObject(message)) {
+        Object.keys(message).forEach(key => {
+          const val = message[key];
+          if (isPlainObject(val)) {
+            paths.push(key);
+            paths.push('.');
+            fn(level, locale, val, paths);
+            paths.pop();
+            paths.pop();
+          } else {
+            paths.push(key);
+            fn(level, locale, val, paths);
+            paths.pop();
+          }
+        });
+      } else if (isArray(message)) {
+        message.forEach((item, index) => {
+          if (isPlainObject(item)) {
+            paths.push(`[${index}]`);
+            paths.push('.');
+            fn(level, locale, item, paths);
+            paths.pop();
+            paths.pop();
+          } else {
+            paths.push(`[${index}]`);
+            fn(level, locale, item, paths);
+            paths.pop();
+          }
+        });
+      } else if (isString(message)) {
+        const ret = htmlTagMatcher.test(message);
+        if (ret) {
+          const msg = `Detected HTML in message '${message}' of keypath '${paths.join('')}' at '${locale}'. Consider component interpolation with '<i18n>' to avoid XSS. See https://bit.ly/2ZqJzkp`;
+          if (level === 'warn') {
+            warn(msg);
+          } else if (level === 'error') {
+            error(msg);
+          }
+        }
+      }
+    };
+
+    fn(level, locale, message, paths);
+  }
+
+  _initVM (data) {
+    const silent = Vue.config.silent;
+    Vue.config.silent = true;
+    this._vm = new Vue({ data });
+    Vue.config.silent = silent;
+  }
+
+  destroyVM () {
+    this._vm.$destroy();
+  }
+
+  subscribeDataChanging (vm) {
+    this._dataListeners.push(vm);
+  }
+
+  unsubscribeDataChanging (vm) {
+    remove(this._dataListeners, vm);
+  }
+
+  watchI18nData () {
+    const self = this;
+    return this._vm.$watch('$data', () => {
+      let i = self._dataListeners.length;
+      while (i--) {
+        Vue.nextTick(() => {
+          self._dataListeners[i] && self._dataListeners[i].$forceUpdate();
+        });
+      }
+    }, { deep: true })
+  }
+
+  watchLocale () {
+    /* istanbul ignore if */
+    if (!this._sync || !this._root) { return null }
+    const target = this._vm;
+    return this._root.$i18n.vm.$watch('locale', (val) => {
+      target.$set(target, 'locale', val);
+      target.$forceUpdate();
+    }, { immediate: true })
+  }
+
+  onComponentInstanceCreated (newI18n) {
+    if (this._componentInstanceCreatedListener) {
+      this._componentInstanceCreatedListener(newI18n, this);
+    }
+  }
+
+  get vm () { return this._vm }
+
+  get messages () { return looseClone(this._getMessages()) }
+  get dateTimeFormats () { return looseClone(this._getDateTimeFormats()) }
+  get numberFormats () { return looseClone(this._getNumberFormats()) }
+  get availableLocales () { return Object.keys(this.messages).sort() }
+
+  get locale () { return this._vm.locale }
+  set locale (locale) {
+    this._vm.$set(this._vm, 'locale', locale);
+  }
+
+  get fallbackLocale () { return this._vm.fallbackLocale }
+  set fallbackLocale (locale) {
+    this._localeChainCache = {};
+    this._vm.$set(this._vm, 'fallbackLocale', locale);
+  }
+
+  get formatFallbackMessages () { return this._formatFallbackMessages }
+  set formatFallbackMessages (fallback) { this._formatFallbackMessages = fallback; }
+
+  get missing () { return this._missing }
+  set missing (handler) { this._missing = handler; }
+
+  get formatter () { return this._formatter }
+  set formatter (formatter) { this._formatter = formatter; }
+
+  get silentTranslationWarn () { return this._silentTranslationWarn }
+  set silentTranslationWarn (silent) { this._silentTranslationWarn = silent; }
+
+  get silentFallbackWarn () { return this._silentFallbackWarn }
+  set silentFallbackWarn (silent) { this._silentFallbackWarn = silent; }
+
+  get preserveDirectiveContent () { return this._preserveDirectiveContent }
+  set preserveDirectiveContent (preserve) { this._preserveDirectiveContent = preserve; }
+
+  get warnHtmlInMessage () { return this._warnHtmlInMessage }
+  set warnHtmlInMessage (level) {
+    const orgLevel = this._warnHtmlInMessage;
+    this._warnHtmlInMessage = level;
+    if (orgLevel !== level && (level === 'warn' || level === 'error')) {
+      const messages = this._getMessages();
+      Object.keys(messages).forEach(locale => {
+        this._checkLocaleMessage(locale, this._warnHtmlInMessage, messages[locale]);
+      });
+    }
+  }
+
+  get postTranslation () { return this._postTranslation }
+  set postTranslation (handler) { this._postTranslation = handler; }
+
+  _getMessages () { return this._vm.messages }
+  _getDateTimeFormats () { return this._vm.dateTimeFormats }
+  _getNumberFormats () { return this._vm.numberFormats }
+
+  _warnDefault (locale, key, result, vm, values, interpolateMode) {
+    if (!isNull(result)) { return result }
+    if (this._missing) {
+      const missingRet = this._missing.apply(null, [locale, key, vm, values]);
+      if (isString(missingRet)) {
+        return missingRet
+      }
+    } else {
+      if (!this._isSilentTranslationWarn(key)) {
+        warn(
+          `Cannot translate the value of keypath '${key}'. ` +
+          'Use the value of keypath as default.'
+        );
+      }
+    }
+
+    if (this._formatFallbackMessages) {
+      const parsedArgs = parseArgs(...values);
+      return this._render(key, interpolateMode, parsedArgs.params, key)
+    } else {
+      return key
+    }
+  }
+
+  _isFallbackRoot (val) {
+    return !val && !isNull(this._root) && this._fallbackRoot
+  }
+
+  _isSilentFallbackWarn (key) {
+    return this._silentFallbackWarn instanceof RegExp
+      ? this._silentFallbackWarn.test(key)
+      : this._silentFallbackWarn
+  }
+
+  _isSilentFallback (locale, key) {
+    return this._isSilentFallbackWarn(key) && (this._isFallbackRoot() || locale !== this.fallbackLocale)
+  }
+
+  _isSilentTranslationWarn (key) {
+    return this._silentTranslationWarn instanceof RegExp
+      ? this._silentTranslationWarn.test(key)
+      : this._silentTranslationWarn
+  }
+
+  _interpolate (
+    locale,
+    message,
+    key,
+    host,
+    interpolateMode,
+    values,
+    visitedLinkStack
+  ) {
+    if (!message) { return null }
+
+    const pathRet = this._path.getPathValue(message, key);
+    if (isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
+
+    let ret;
+    if (isNull(pathRet)) {
+      /* istanbul ignore else */
+      if (isPlainObject(message)) {
+        ret = message[key];
+        if (!(isString(ret) || isFunction(ret))) {
+          if (!this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+            warn(`Value of key '${key}' is not a string or function !`);
+          }
+          return null
+        }
+      } else {
+        return null
+      }
+    } else {
+      /* istanbul ignore else */
+      if (isString(pathRet) || isFunction(pathRet)) {
+        ret = pathRet;
+      } else {
+        if (!this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+          warn(`Value of key '${key}' is not a string or function!`);
+        }
+        return null
+      }
+    }
+
+    // Check for the existence of links within the translated string
+    if (isString(ret) && (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0)) {
+      ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack);
+    }
+
+    return this._render(ret, interpolateMode, values, key)
+  }
+
+  _link (
+    locale,
+    message,
+    str,
+    host,
+    interpolateMode,
+    values,
+    visitedLinkStack
+  ) {
+    let ret = str;
+
+    // Match all the links within the local
+    // We are going to replace each of
+    // them with its translation
+    const matches = ret.match(linkKeyMatcher);
+    for (let idx in matches) {
+      // ie compatible: filter custom array
+      // prototype method
+      if (!matches.hasOwnProperty(idx)) {
+        continue
+      }
+      const link = matches[idx];
+      const linkKeyPrefixMatches = link.match(linkKeyPrefixMatcher);
+      const [linkPrefix, formatterName] = linkKeyPrefixMatches;
+
+      // Remove the leading @:, @.case: and the brackets
+      const linkPlaceholder = link.replace(linkPrefix, '').replace(bracketsMatcher, '');
+
+      if (includes(visitedLinkStack, linkPlaceholder)) {
+        {
+          warn(`Circular reference found. "${link}" is already visited in the chain of ${visitedLinkStack.reverse().join(' <- ')}`);
+        }
+        return ret
+      }
+      visitedLinkStack.push(linkPlaceholder);
+
+      // Translate the link
+      let translated = this._interpolate(
+        locale, message, linkPlaceholder, host,
+        interpolateMode === 'raw' ? 'string' : interpolateMode,
+        interpolateMode === 'raw' ? undefined : values,
+        visitedLinkStack
+      );
+
+      if (this._isFallbackRoot(translated)) {
+        if (!this._isSilentTranslationWarn(linkPlaceholder)) {
+          warn(`Fall back to translate the link placeholder '${linkPlaceholder}' with root locale.`);
+        }
+        /* istanbul ignore if */
+        if (!this._root) { throw Error('unexpected error') }
+        const root = this._root.$i18n;
+        translated = root._translate(
+          root._getMessages(), root.locale, root.fallbackLocale,
+          linkPlaceholder, host, interpolateMode, values
+        );
+      }
+      translated = this._warnDefault(
+        locale, linkPlaceholder, translated, host,
+        isArray(values) ? values : [values],
+        interpolateMode
+      );
+
+      if (this._modifiers.hasOwnProperty(formatterName)) {
+        translated = this._modifiers[formatterName](translated);
+      } else if (defaultModifiers.hasOwnProperty(formatterName)) {
+        translated = defaultModifiers[formatterName](translated);
+      }
+
+      visitedLinkStack.pop();
+
+      // Replace the link with the translated
+      ret = !translated ? ret : ret.replace(link, translated);
+    }
+
+    return ret
+  }
+
+  _createMessageContext (values) {
+    const _list = isArray(values) ? values : [];
+    const _named = isObject(values) ? values : {};
+    const list = (index) => _list[index];
+    const named = (key) => _named[key];
+    return {
+      list,
+      named
+    }
+  }
+
+  _render (message, interpolateMode, values, path) {
+    if (isFunction(message)) {
+      return message(this._createMessageContext(values))
+    }
+
+    let ret = this._formatter.interpolate(message, values, path);
+
+    // If the custom formatter refuses to work - apply the default one
+    if (!ret) {
+      ret = defaultFormatter.interpolate(message, values, path);
+    }
+
+    // if interpolateMode is **not** 'string' ('row'),
+    // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
+    return interpolateMode === 'string' && !isString(ret) ? ret.join('') : ret
+  }
+
+  _appendItemToChain (chain, item, blocks) {
+    let follow = false;
+    if (!includes(chain, item)) {
+      follow = true;
+      if (item) {
+        follow = item[item.length - 1] !== '!';
+        item = item.replace(/!/g, '');
+        chain.push(item);
+        if (blocks && blocks[item]) {
+          follow = blocks[item];
+        }
+      }
+    }
+    return follow
+  }
+
+  _appendLocaleToChain (chain, locale, blocks) {
+    let follow;
+    const tokens = locale.split('-');
+    do {
+      const item = tokens.join('-');
+      follow = this._appendItemToChain(chain, item, blocks);
+      tokens.splice(-1, 1);
+    } while (tokens.length && (follow === true))
+    return follow
+  }
+
+  _appendBlockToChain (chain, block, blocks) {
+    let follow = true;
+    for (let i = 0; (i < block.length) && (isBoolean(follow)); i++) {
+      const locale = block[i];
+      if (isString(locale)) {
+        follow = this._appendLocaleToChain(chain, locale, blocks);
+      }
+    }
+    return follow
+  }
+
+  _getLocaleChain (start, fallbackLocale) {
+    if (start === '') { return [] }
+
+    if (!this._localeChainCache) {
+      this._localeChainCache = {};
+    }
+
+    let chain = this._localeChainCache[start];
+    if (!chain) {
+      if (!fallbackLocale) {
+        fallbackLocale = this.fallbackLocale;
+      }
+      chain = [];
+
+      // first block defined by start
+      let block = [start];
+
+      // while any intervening block found
+      while (isArray(block)) {
+        block = this._appendBlockToChain(
+          chain,
+          block,
+          fallbackLocale
+        );
+      }
+
+      // last block defined by default
+      let defaults;
+      if (isArray(fallbackLocale)) {
+        defaults = fallbackLocale;
+      } else if (isObject(fallbackLocale)) {
+        /* $FlowFixMe */
+        if (fallbackLocale['default']) {
+          defaults = fallbackLocale['default'];
+        } else {
+          defaults = null;
+        }
+      } else {
+        defaults = fallbackLocale;
+      }
+
+      // convert defaults to array
+      if (isString(defaults)) {
+        block = [defaults];
+      } else {
+        block = defaults;
+      }
+      if (block) {
+        this._appendBlockToChain(
+          chain,
+          block,
+          null
+        );
+      }
+      this._localeChainCache[start] = chain;
+    }
+    return chain
+  }
+
+  _translate (
+    messages,
+    locale,
+    fallback,
+    key,
+    host,
+    interpolateMode,
+    args
+  ) {
+    const chain = this._getLocaleChain(locale, fallback);
+    let res;
+    for (let i = 0; i < chain.length; i++) {
+      const step = chain[i];
+      res =
+        this._interpolate(step, messages[step], key, host, interpolateMode, args, [key]);
+      if (!isNull(res)) {
+        if (step !== locale && "development" !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(("Fall back to translate the keypath '" + key + "' with '" + step + "' locale."));
+        }
+        return res
+      }
+    }
+    return null
+  }
+
+  _t (key, _locale, messages, host, ...values) {
+    if (!key) { return '' }
+
+    const parsedArgs = parseArgs(...values);
+    const locale = parsedArgs.locale || _locale;
+
+    let ret = this._translate(
+      messages, locale, this.fallbackLocale, key,
+      host, 'string', parsedArgs.params
+    );
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(`Fall back to translate the keypath '${key}' with root locale.`);
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$t(key, ...values)
+    } else {
+      ret = this._warnDefault(locale, key, ret, host, values, 'string');
+      if (this._postTranslation && ret !== null && ret !== undefined) {
+        ret = this._postTranslation(ret, key);
+      }
+      return ret
+    }
+  }
+
+  t (key, ...values) {
+    return this._t(key, this.locale, this._getMessages(), null, ...values)
+  }
+
+  _i (key, locale, messages, host, values) {
+    const ret =
+      this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key)) {
+        warn(`Fall back to interpolate the keypath '${key}' with root locale.`);
+      }
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.i(key, locale, values)
+    } else {
+      return this._warnDefault(locale, key, ret, host, [values], 'raw')
+    }
+  }
+
+  i (key, locale, values) {
+    /* istanbul ignore if */
+    if (!key) { return '' }
+
+    if (!isString(locale)) {
+      locale = this.locale;
+    }
+
+    return this._i(key, locale, this._getMessages(), null, values)
+  }
+
+  _tc (
+    key,
+    _locale,
+    messages,
+    host,
+    choice,
+    ...values
+  ) {
+    if (!key) { return '' }
+    if (choice === undefined) {
+      choice = 1;
+    }
+
+    const predefined = { 'count': choice, 'n': choice };
+    const parsedArgs = parseArgs(...values);
+    parsedArgs.params = Object.assign(predefined, parsedArgs.params);
+    values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params];
+    return this.fetchChoice(this._t(key, _locale, messages, host, ...values), choice)
+  }
+
+  fetchChoice (message, choice) {
+    /* istanbul ignore if */
+    if (!message && !isString(message)) { return null }
+    const choices = message.split('|');
+
+    choice = this.getChoiceIndex(choice, choices.length);
+    if (!choices[choice]) { return message }
+    return choices[choice].trim()
+  }
+
+  tc (key, choice, ...values) {
+    return this._tc(key, this.locale, this._getMessages(), null, choice, ...values)
+  }
+
+  _te (key, locale, messages, ...args) {
+    const _locale = parseArgs(...args).locale || locale;
+    return this._exist(messages[_locale], key)
+  }
+
+  te (key, locale) {
+    return this._te(key, this.locale, this._getMessages(), locale)
+  }
+
+  getLocaleMessage (locale) {
+    return looseClone(this._vm.messages[locale] || {})
+  }
+
+  setLocaleMessage (locale, message) {
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+    }
+    this._vm.$set(this._vm.messages, locale, message);
+  }
+
+  mergeLocaleMessage (locale, message) {
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+    }
+    this._vm.$set(this._vm.messages, locale, merge({}, this._vm.messages[locale] || {}, message));
+  }
+
+  getDateTimeFormat (locale) {
+    return looseClone(this._vm.dateTimeFormats[locale] || {})
+  }
+
+  setDateTimeFormat (locale, format) {
+    this._vm.$set(this._vm.dateTimeFormats, locale, format);
+    this._clearDateTimeFormat(locale, format);
+  }
+
+  mergeDateTimeFormat (locale, format) {
+    this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format));
+    this._clearDateTimeFormat(locale, format);
+  }
+
+  _clearDateTimeFormat (locale, format) {
+    for (let key in format) {
+      const id = `${locale}__${key}`;
+
+      if (!this._dateTimeFormatters.hasOwnProperty(id)) {
+        continue
+      }
+
+      delete this._dateTimeFormatters[id];
+    }
+  }
+
+  _localizeDateTime (
+    value,
+    locale,
+    fallback,
+    dateTimeFormats,
+    key
+  ) {
+    let _locale = locale;
+    let formats = dateTimeFormats[_locale];
+
+    const chain = this._getLocaleChain(locale, fallback);
+    for (let i = 0; i < chain.length; i++) {
+      const current = _locale;
+      const step = chain[i];
+      formats = dateTimeFormats[step];
+      _locale = step;
+      // fallback locale
+      if (isNull(formats) || isNull(formats[key])) {
+        if (step !== locale && "development" !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(`Fall back to '${step}' datetime formats from '${current}' datetime formats.`);
+        }
+      } else {
+        break
+      }
+    }
+
+    if (isNull(formats) || isNull(formats[key])) {
+      return null
+    } else {
+      const format = formats[key];
+      const id = `${_locale}__${key}`;
+      let formatter = this._dateTimeFormatters[id];
+      if (!formatter) {
+        formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format);
+      }
+      return formatter.format(value)
+    }
+  }
+
+  _d (value, locale, key) {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.dateTimeFormat) {
+      warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.');
+      return ''
+    }
+
+    if (!key) {
+      return new Intl.DateTimeFormat(locale).format(value)
+    }
+
+    const ret =
+      this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(`Fall back to datetime localization of root: key '${key}'.`);
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.d(value, key, locale)
+    } else {
+      return ret || ''
+    }
+  }
+
+  d (value, ...args) {
+    let locale = this.locale;
+    let key = null;
+
+    if (args.length === 1) {
+      if (isString(args[0])) {
+        key = args[0];
+      } else if (isObject(args[0])) {
+        if (args[0].locale) {
+          locale = args[0].locale;
+        }
+        if (args[0].key) {
+          key = args[0].key;
+        }
+      }
+    } else if (args.length === 2) {
+      if (isString(args[0])) {
+        key = args[0];
+      }
+      if (isString(args[1])) {
+        locale = args[1];
+      }
+    }
+
+    return this._d(value, locale, key)
+  }
+
+  getNumberFormat (locale) {
+    return looseClone(this._vm.numberFormats[locale] || {})
+  }
+
+  setNumberFormat (locale, format) {
+    this._vm.$set(this._vm.numberFormats, locale, format);
+    this._clearNumberFormat(locale, format);
+  }
+
+  mergeNumberFormat (locale, format) {
+    this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format));
+    this._clearNumberFormat(locale, format);
+  }
+
+  _clearNumberFormat (locale, format) {
+    for (let key in format) {
+      const id = `${locale}__${key}`;
+
+      if (!this._numberFormatters.hasOwnProperty(id)) {
+        continue
+      }
+
+      delete this._numberFormatters[id];
+    }
+  }
+
+  _getNumberFormatter (
+    value,
+    locale,
+    fallback,
+    numberFormats,
+    key,
+    options
+  ) {
+    let _locale = locale;
+    let formats = numberFormats[_locale];
+
+    const chain = this._getLocaleChain(locale, fallback);
+    for (let i = 0; i < chain.length; i++) {
+      const current = _locale;
+      const step = chain[i];
+      formats = numberFormats[step];
+      _locale = step;
+      // fallback locale
+      if (isNull(formats) || isNull(formats[key])) {
+        if (step !== locale && "development" !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(`Fall back to '${step}' number formats from '${current}' number formats.`);
+        }
+      } else {
+        break
+      }
+    }
+
+    if (isNull(formats) || isNull(formats[key])) {
+      return null
+    } else {
+      const format = formats[key];
+
+      let formatter;
+      if (options) {
+        // If options specified - create one time number formatter
+        formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options));
+      } else {
+        const id = `${_locale}__${key}`;
+        formatter = this._numberFormatters[id];
+        if (!formatter) {
+          formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format);
+        }
+      }
+      return formatter
+    }
+  }
+
+  _n (value, locale, key, options) {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.numberFormat) {
+      {
+        warn('Cannot format a Number value due to not supported Intl.NumberFormat.');
+      }
+      return ''
+    }
+
+    if (!key) {
+      const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+      return nf.format(value)
+    }
+
+    const formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+    const ret = formatter && formatter.format(value);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(`Fall back to number localization of root: key '${key}'.`);
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.n(value, Object.assign({}, { key, locale }, options))
+    } else {
+      return ret || ''
+    }
+  }
+
+  n (value, ...args) {
+    let locale = this.locale;
+    let key = null;
+    let options = null;
+
+    if (args.length === 1) {
+      if (isString(args[0])) {
+        key = args[0];
+      } else if (isObject(args[0])) {
+        if (args[0].locale) {
+          locale = args[0].locale;
+        }
+        if (args[0].key) {
+          key = args[0].key;
+        }
+
+        // Filter out number format options only
+        options = Object.keys(args[0]).reduce((acc, key) => {
+          if (includes(numberFormatKeys, key)) {
+            return Object.assign({}, acc, { [key]: args[0][key] })
+          }
+          return acc
+        }, null);
+      }
+    } else if (args.length === 2) {
+      if (isString(args[0])) {
+        key = args[0];
+      }
+      if (isString(args[1])) {
+        locale = args[1];
+      }
+    }
+
+    return this._n(value, locale, key, options)
+  }
+
+  _ntp (value, locale, key, options) {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.numberFormat) {
+      {
+        warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.');
+      }
+      return []
+    }
+
+    if (!key) {
+      const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+      return nf.formatToParts(value)
+    }
+
+    const formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+    const ret = formatter && formatter.formatToParts(value);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key)) {
+        warn(`Fall back to format number to parts of root: key '${key}' .`);
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n._ntp(value, locale, key, options)
+    } else {
+      return ret || []
+    }
+  }
+}
+
+let availabilities;
+// $FlowFixMe
+Object.defineProperty(VueI18n, 'availabilities', {
+  get () {
+    if (!availabilities) {
+      const intlDefined = typeof Intl !== 'undefined';
+      availabilities = {
+        dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
+        numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
+      };
+    }
+
+    return availabilities
+  }
+});
+
+VueI18n.install = install;
+VueI18n.version = '8.21.0';
+
+export default VueI18n;

File diff suppressed because it is too large
+ 0 - 0
plugin/vue-i18n/dist/vue-i18n.esm.browser.min.js


+ 2149 - 0
plugin/vue-i18n/dist/vue-i18n.esm.js

@@ -0,0 +1,2149 @@
+/*!
+ * vue-i18n v8.21.0 
+ * (c) 2020 kazuya kawaguchi
+ * Released under the MIT License.
+ */
+/*  */
+
+/**
+ * constants
+ */
+
+var numberFormatKeys = [
+  'style',
+  'currency',
+  'currencyDisplay',
+  'useGrouping',
+  'minimumIntegerDigits',
+  'minimumFractionDigits',
+  'maximumFractionDigits',
+  'minimumSignificantDigits',
+  'maximumSignificantDigits',
+  'localeMatcher',
+  'formatMatcher',
+  'unit'
+];
+
+/**
+ * utilities
+ */
+
+function warn (msg, err) {
+  if (typeof console !== 'undefined') {
+    console.warn('[vue-i18n] ' + msg);
+    /* istanbul ignore if */
+    if (err) {
+      console.warn(err.stack);
+    }
+  }
+}
+
+function error (msg, err) {
+  if (typeof console !== 'undefined') {
+    console.error('[vue-i18n] ' + msg);
+    /* istanbul ignore if */
+    if (err) {
+      console.error(err.stack);
+    }
+  }
+}
+
+var isArray = Array.isArray;
+
+function isObject (obj) {
+  return obj !== null && typeof obj === 'object'
+}
+
+function isBoolean (val) {
+  return typeof val === 'boolean'
+}
+
+function isString (val) {
+  return typeof val === 'string'
+}
+
+var toString = Object.prototype.toString;
+var OBJECT_STRING = '[object Object]';
+function isPlainObject (obj) {
+  return toString.call(obj) === OBJECT_STRING
+}
+
+function isNull (val) {
+  return val === null || val === undefined
+}
+
+function isFunction (val) {
+  return typeof val === 'function'
+}
+
+function parseArgs () {
+  var args = [], len = arguments.length;
+  while ( len-- ) args[ len ] = arguments[ len ];
+
+  var locale = null;
+  var params = null;
+  if (args.length === 1) {
+    if (isObject(args[0]) || isArray(args[0])) {
+      params = args[0];
+    } else if (typeof args[0] === 'string') {
+      locale = args[0];
+    }
+  } else if (args.length === 2) {
+    if (typeof args[0] === 'string') {
+      locale = args[0];
+    }
+    /* istanbul ignore if */
+    if (isObject(args[1]) || isArray(args[1])) {
+      params = args[1];
+    }
+  }
+
+  return { locale: locale, params: params }
+}
+
+function looseClone (obj) {
+  return JSON.parse(JSON.stringify(obj))
+}
+
+function remove (arr, item) {
+  if (arr.length) {
+    var index = arr.indexOf(item);
+    if (index > -1) {
+      return arr.splice(index, 1)
+    }
+  }
+}
+
+function includes (arr, item) {
+  return !!~arr.indexOf(item)
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+function hasOwn (obj, key) {
+  return hasOwnProperty.call(obj, key)
+}
+
+function merge (target) {
+  var arguments$1 = arguments;
+
+  var output = Object(target);
+  for (var i = 1; i < arguments.length; i++) {
+    var source = arguments$1[i];
+    if (source !== undefined && source !== null) {
+      var key = (void 0);
+      for (key in source) {
+        if (hasOwn(source, key)) {
+          if (isObject(source[key])) {
+            output[key] = merge(output[key], source[key]);
+          } else {
+            output[key] = source[key];
+          }
+        }
+      }
+    }
+  }
+  return output
+}
+
+function looseEqual (a, b) {
+  if (a === b) { return true }
+  var isObjectA = isObject(a);
+  var isObjectB = isObject(b);
+  if (isObjectA && isObjectB) {
+    try {
+      var isArrayA = isArray(a);
+      var isArrayB = isArray(b);
+      if (isArrayA && isArrayB) {
+        return a.length === b.length && a.every(function (e, i) {
+          return looseEqual(e, b[i])
+        })
+      } else if (!isArrayA && !isArrayB) {
+        var keysA = Object.keys(a);
+        var keysB = Object.keys(b);
+        return keysA.length === keysB.length && keysA.every(function (key) {
+          return looseEqual(a[key], b[key])
+        })
+      } else {
+        /* istanbul ignore next */
+        return false
+      }
+    } catch (e) {
+      /* istanbul ignore next */
+      return false
+    }
+  } else if (!isObjectA && !isObjectB) {
+    return String(a) === String(b)
+  } else {
+    return false
+  }
+}
+
+/*  */
+
+function extend (Vue) {
+  if (!Vue.prototype.hasOwnProperty('$i18n')) {
+    // $FlowFixMe
+    Object.defineProperty(Vue.prototype, '$i18n', {
+      get: function get () { return this._i18n }
+    });
+  }
+
+  Vue.prototype.$t = function (key) {
+    var values = [], len = arguments.length - 1;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
+
+    var i18n = this.$i18n;
+    return i18n._t.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this ].concat( values ))
+  };
+
+  Vue.prototype.$tc = function (key, choice) {
+    var values = [], len = arguments.length - 2;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
+
+    var i18n = this.$i18n;
+    return i18n._tc.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this, choice ].concat( values ))
+  };
+
+  Vue.prototype.$te = function (key, locale) {
+    var i18n = this.$i18n;
+    return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
+  };
+
+  Vue.prototype.$d = function (value) {
+    var ref;
+
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+    return (ref = this.$i18n).d.apply(ref, [ value ].concat( args ))
+  };
+
+  Vue.prototype.$n = function (value) {
+    var ref;
+
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+    return (ref = this.$i18n).n.apply(ref, [ value ].concat( args ))
+  };
+}
+
+/*  */
+
+var mixin = {
+  beforeCreate: function beforeCreate () {
+    var options = this.$options;
+    options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            var localeMessages = {};
+            options.__i18n.forEach(function (resource) {
+              localeMessages = merge(localeMessages, JSON.parse(resource));
+            });
+            Object.keys(localeMessages).forEach(function (locale) {
+              options.i18n.mergeLocaleMessage(locale, localeMessages[locale]);
+            });
+          } catch (e) {
+            if (process.env.NODE_ENV !== 'production') {
+              error("Cannot parse locale messages via custom blocks.", e);
+            }
+          }
+        }
+        this._i18n = options.i18n;
+        this._i18nWatcher = this._i18n.watchI18nData();
+      } else if (isPlainObject(options.i18n)) {
+        var rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n
+          ? this.$root.$i18n
+          : null;
+        // component local i18n
+        if (rootI18n) {
+          options.i18n.root = this.$root;
+          options.i18n.formatter = rootI18n.formatter;
+          options.i18n.fallbackLocale = rootI18n.fallbackLocale;
+          options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages;
+          options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn;
+          options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn;
+          options.i18n.pluralizationRules = rootI18n.pluralizationRules;
+          options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent;
+        }
+
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            var localeMessages$1 = {};
+            options.__i18n.forEach(function (resource) {
+              localeMessages$1 = merge(localeMessages$1, JSON.parse(resource));
+            });
+            options.i18n.messages = localeMessages$1;
+          } catch (e) {
+            if (process.env.NODE_ENV !== 'production') {
+              warn("Cannot parse locale messages via custom blocks.", e);
+            }
+          }
+        }
+
+        var ref = options.i18n;
+        var sharedMessages = ref.sharedMessages;
+        if (sharedMessages && isPlainObject(sharedMessages)) {
+          options.i18n.messages = merge(options.i18n.messages, sharedMessages);
+        }
+
+        this._i18n = new VueI18n(options.i18n);
+        this._i18nWatcher = this._i18n.watchI18nData();
+
+        if (options.i18n.sync === undefined || !!options.i18n.sync) {
+          this._localeWatcher = this.$i18n.watchLocale();
+        }
+
+        if (rootI18n) {
+          rootI18n.onComponentInstanceCreated(this._i18n);
+        }
+      } else {
+        if (process.env.NODE_ENV !== 'production') {
+          warn("Cannot be interpreted 'i18n' option.");
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      // root i18n
+      this._i18n = this.$root.$i18n;
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      // parent i18n
+      this._i18n = options.parent.$i18n;
+    }
+  },
+
+  beforeMount: function beforeMount () {
+    var options = this.$options;
+    options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      } else if (isPlainObject(options.i18n)) {
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      } else {
+        if (process.env.NODE_ENV !== 'production') {
+          warn("Cannot be interpreted 'i18n' option.");
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this);
+      this._subscribing = true;
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this);
+      this._subscribing = true;
+    }
+  },
+
+  beforeDestroy: function beforeDestroy () {
+    if (!this._i18n) { return }
+
+    var self = this;
+    this.$nextTick(function () {
+      if (self._subscribing) {
+        self._i18n.unsubscribeDataChanging(self);
+        delete self._subscribing;
+      }
+
+      if (self._i18nWatcher) {
+        self._i18nWatcher();
+        self._i18n.destroyVM();
+        delete self._i18nWatcher;
+      }
+
+      if (self._localeWatcher) {
+        self._localeWatcher();
+        delete self._localeWatcher;
+      }
+    });
+  }
+};
+
+/*  */
+
+var interpolationComponent = {
+  name: 'i18n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    path: {
+      type: String,
+      required: true
+    },
+    locale: {
+      type: String
+    },
+    places: {
+      type: [Array, Object]
+    }
+  },
+  render: function render (h, ref) {
+    var data = ref.data;
+    var parent = ref.parent;
+    var props = ref.props;
+    var slots = ref.slots;
+
+    var $i18n = parent.$i18n;
+    if (!$i18n) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot find VueI18n instance!');
+      }
+      return
+    }
+
+    var path = props.path;
+    var locale = props.locale;
+    var places = props.places;
+    var params = slots();
+    var children = $i18n.i(
+      path,
+      locale,
+      onlyHasDefaultPlace(params) || places
+        ? useLegacyPlaces(params.default, places)
+        : params
+    );
+
+    var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+    return tag ? h(tag, data, children) : children
+  }
+};
+
+function onlyHasDefaultPlace (params) {
+  var prop;
+  for (prop in params) {
+    if (prop !== 'default') { return false }
+  }
+  return Boolean(prop)
+}
+
+function useLegacyPlaces (children, places) {
+  var params = places ? createParamsFromPlaces(places) : {};
+
+  if (!children) { return params }
+
+  // Filter empty text nodes
+  children = children.filter(function (child) {
+    return child.tag || child.text.trim() !== ''
+  });
+
+  var everyPlace = children.every(vnodeHasPlaceAttribute);
+  if (process.env.NODE_ENV !== 'production' && everyPlace) {
+    warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.');
+  }
+
+  return children.reduce(
+    everyPlace ? assignChildPlace : assignChildIndex,
+    params
+  )
+}
+
+function createParamsFromPlaces (places) {
+  if (process.env.NODE_ENV !== 'production') {
+    warn('`places` prop is deprecated in next major version. Please switch to Vue slots.');
+  }
+
+  return Array.isArray(places)
+    ? places.reduce(assignChildIndex, {})
+    : Object.assign({}, places)
+}
+
+function assignChildPlace (params, child) {
+  if (child.data && child.data.attrs && child.data.attrs.place) {
+    params[child.data.attrs.place] = child;
+  }
+  return params
+}
+
+function assignChildIndex (params, child, index) {
+  params[index] = child;
+  return params
+}
+
+function vnodeHasPlaceAttribute (vnode) {
+  return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place)
+}
+
+/*  */
+
+var numberComponent = {
+  name: 'i18n-n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    value: {
+      type: Number,
+      required: true
+    },
+    format: {
+      type: [String, Object]
+    },
+    locale: {
+      type: String
+    }
+  },
+  render: function render (h, ref) {
+    var props = ref.props;
+    var parent = ref.parent;
+    var data = ref.data;
+
+    var i18n = parent.$i18n;
+
+    if (!i18n) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot find VueI18n instance!');
+      }
+      return null
+    }
+
+    var key = null;
+    var options = null;
+
+    if (isString(props.format)) {
+      key = props.format;
+    } else if (isObject(props.format)) {
+      if (props.format.key) {
+        key = props.format.key;
+      }
+
+      // Filter out number format options only
+      options = Object.keys(props.format).reduce(function (acc, prop) {
+        var obj;
+
+        if (includes(numberFormatKeys, prop)) {
+          return Object.assign({}, acc, ( obj = {}, obj[prop] = props.format[prop], obj ))
+        }
+        return acc
+      }, null);
+    }
+
+    var locale = props.locale || i18n.locale;
+    var parts = i18n._ntp(props.value, locale, key, options);
+
+    var values = parts.map(function (part, index) {
+      var obj;
+
+      var slot = data.scopedSlots && data.scopedSlots[part.type];
+      return slot ? slot(( obj = {}, obj[part.type] = part.value, obj.index = index, obj.parts = parts, obj )) : part.value
+    });
+
+    var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+    return tag
+      ? h(tag, {
+        attrs: data.attrs,
+        'class': data['class'],
+        staticClass: data.staticClass
+      }, values)
+      : values
+  }
+};
+
+/*  */
+
+function bind (el, binding, vnode) {
+  if (!assert(el, vnode)) { return }
+
+  t(el, binding, vnode);
+}
+
+function update (el, binding, vnode, oldVNode) {
+  if (!assert(el, vnode)) { return }
+
+  var i18n = vnode.context.$i18n;
+  if (localeEqual(el, vnode) &&
+    (looseEqual(binding.value, binding.oldValue) &&
+     looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }
+
+  t(el, binding, vnode);
+}
+
+function unbind (el, binding, vnode, oldVNode) {
+  var vm = vnode.context;
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context');
+    return
+  }
+
+  var i18n = vnode.context.$i18n || {};
+  if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {
+    el.textContent = '';
+  }
+  el._vt = undefined;
+  delete el['_vt'];
+  el._locale = undefined;
+  delete el['_locale'];
+  el._localeMessage = undefined;
+  delete el['_localeMessage'];
+}
+
+function assert (el, vnode) {
+  var vm = vnode.context;
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context');
+    return false
+  }
+
+  if (!vm.$i18n) {
+    warn('VueI18n instance does not exists in Vue instance');
+    return false
+  }
+
+  return true
+}
+
+function localeEqual (el, vnode) {
+  var vm = vnode.context;
+  return el._locale === vm.$i18n.locale
+}
+
+function t (el, binding, vnode) {
+  var ref$1, ref$2;
+
+  var value = binding.value;
+
+  var ref = parseValue(value);
+  var path = ref.path;
+  var locale = ref.locale;
+  var args = ref.args;
+  var choice = ref.choice;
+  if (!path && !locale && !args) {
+    warn('value type not supported');
+    return
+  }
+
+  if (!path) {
+    warn('`path` is required in v-t directive');
+    return
+  }
+
+  var vm = vnode.context;
+  if (choice != null) {
+    el._vt = el.textContent = (ref$1 = vm.$i18n).tc.apply(ref$1, [ path, choice ].concat( makeParams(locale, args) ));
+  } else {
+    el._vt = el.textContent = (ref$2 = vm.$i18n).t.apply(ref$2, [ path ].concat( makeParams(locale, args) ));
+  }
+  el._locale = vm.$i18n.locale;
+  el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale);
+}
+
+function parseValue (value) {
+  var path;
+  var locale;
+  var args;
+  var choice;
+
+  if (isString(value)) {
+    path = value;
+  } else if (isPlainObject(value)) {
+    path = value.path;
+    locale = value.locale;
+    args = value.args;
+    choice = value.choice;
+  }
+
+  return { path: path, locale: locale, args: args, choice: choice }
+}
+
+function makeParams (locale, args) {
+  var params = [];
+
+  locale && params.push(locale);
+  if (args && (Array.isArray(args) || isPlainObject(args))) {
+    params.push(args);
+  }
+
+  return params
+}
+
+var Vue;
+
+function install (_Vue) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && install.installed && _Vue === Vue) {
+    warn('already installed.');
+    return
+  }
+  install.installed = true;
+
+  Vue = _Vue;
+
+  var version = (Vue.version && Number(Vue.version.split('.')[0])) || -1;
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && version < 2) {
+    warn(("vue-i18n (" + (install.version) + ") need to use Vue 2.0 or later (Vue: " + (Vue.version) + ")."));
+    return
+  }
+
+  extend(Vue);
+  Vue.mixin(mixin);
+  Vue.directive('t', { bind: bind, update: update, unbind: unbind });
+  Vue.component(interpolationComponent.name, interpolationComponent);
+  Vue.component(numberComponent.name, numberComponent);
+
+  // use simple mergeStrategies to prevent i18n instance lose '__proto__'
+  var strats = Vue.config.optionMergeStrategies;
+  strats.i18n = function (parentVal, childVal) {
+    return childVal === undefined
+      ? parentVal
+      : childVal
+  };
+}
+
+/*  */
+
+var BaseFormatter = function BaseFormatter () {
+  this._caches = Object.create(null);
+};
+
+BaseFormatter.prototype.interpolate = function interpolate (message, values) {
+  if (!values) {
+    return [message]
+  }
+  var tokens = this._caches[message];
+  if (!tokens) {
+    tokens = parse(message);
+    this._caches[message] = tokens;
+  }
+  return compile(tokens, values)
+};
+
+
+
+var RE_TOKEN_LIST_VALUE = /^(?:\d)+/;
+var RE_TOKEN_NAMED_VALUE = /^(?:\w)+/;
+
+function parse (format) {
+  var tokens = [];
+  var position = 0;
+
+  var text = '';
+  while (position < format.length) {
+    var char = format[position++];
+    if (char === '{') {
+      if (text) {
+        tokens.push({ type: 'text', value: text });
+      }
+
+      text = '';
+      var sub = '';
+      char = format[position++];
+      while (char !== undefined && char !== '}') {
+        sub += char;
+        char = format[position++];
+      }
+      var isClosed = char === '}';
+
+      var type = RE_TOKEN_LIST_VALUE.test(sub)
+        ? 'list'
+        : isClosed && RE_TOKEN_NAMED_VALUE.test(sub)
+          ? 'named'
+          : 'unknown';
+      tokens.push({ value: sub, type: type });
+    } else if (char === '%') {
+      // when found rails i18n syntax, skip text capture
+      if (format[(position)] !== '{') {
+        text += char;
+      }
+    } else {
+      text += char;
+    }
+  }
+
+  text && tokens.push({ type: 'text', value: text });
+
+  return tokens
+}
+
+function compile (tokens, values) {
+  var compiled = [];
+  var index = 0;
+
+  var mode = Array.isArray(values)
+    ? 'list'
+    : isObject(values)
+      ? 'named'
+      : 'unknown';
+  if (mode === 'unknown') { return compiled }
+
+  while (index < tokens.length) {
+    var token = tokens[index];
+    switch (token.type) {
+      case 'text':
+        compiled.push(token.value);
+        break
+      case 'list':
+        compiled.push(values[parseInt(token.value, 10)]);
+        break
+      case 'named':
+        if (mode === 'named') {
+          compiled.push((values)[token.value]);
+        } else {
+          if (process.env.NODE_ENV !== 'production') {
+            warn(("Type of token '" + (token.type) + "' and format of value '" + mode + "' don't match!"));
+          }
+        }
+        break
+      case 'unknown':
+        if (process.env.NODE_ENV !== 'production') {
+          warn("Detect 'unknown' type of token!");
+        }
+        break
+    }
+    index++;
+  }
+
+  return compiled
+}
+
+/*  */
+
+/**
+ *  Path parser
+ *  - Inspired:
+ *    Vue.js Path parser
+ */
+
+// actions
+var APPEND = 0;
+var PUSH = 1;
+var INC_SUB_PATH_DEPTH = 2;
+var PUSH_SUB_PATH = 3;
+
+// states
+var BEFORE_PATH = 0;
+var IN_PATH = 1;
+var BEFORE_IDENT = 2;
+var IN_IDENT = 3;
+var IN_SUB_PATH = 4;
+var IN_SINGLE_QUOTE = 5;
+var IN_DOUBLE_QUOTE = 6;
+var AFTER_PATH = 7;
+var ERROR = 8;
+
+var pathStateMachine = [];
+
+pathStateMachine[BEFORE_PATH] = {
+  'ws': [BEFORE_PATH],
+  'ident': [IN_IDENT, APPEND],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[IN_PATH] = {
+  'ws': [IN_PATH],
+  '.': [BEFORE_IDENT],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[BEFORE_IDENT] = {
+  'ws': [BEFORE_IDENT],
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND]
+};
+
+pathStateMachine[IN_IDENT] = {
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND],
+  'ws': [IN_PATH, PUSH],
+  '.': [BEFORE_IDENT, PUSH],
+  '[': [IN_SUB_PATH, PUSH],
+  'eof': [AFTER_PATH, PUSH]
+};
+
+pathStateMachine[IN_SUB_PATH] = {
+  "'": [IN_SINGLE_QUOTE, APPEND],
+  '"': [IN_DOUBLE_QUOTE, APPEND],
+  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+  ']': [IN_PATH, PUSH_SUB_PATH],
+  'eof': ERROR,
+  'else': [IN_SUB_PATH, APPEND]
+};
+
+pathStateMachine[IN_SINGLE_QUOTE] = {
+  "'": [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_SINGLE_QUOTE, APPEND]
+};
+
+pathStateMachine[IN_DOUBLE_QUOTE] = {
+  '"': [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_DOUBLE_QUOTE, APPEND]
+};
+
+/**
+ * Check if an expression is a literal value.
+ */
+
+var literalValueRE = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;
+function isLiteral (exp) {
+  return literalValueRE.test(exp)
+}
+
+/**
+ * Strip quotes from a string
+ */
+
+function stripQuotes (str) {
+  var a = str.charCodeAt(0);
+  var b = str.charCodeAt(str.length - 1);
+  return a === b && (a === 0x22 || a === 0x27)
+    ? str.slice(1, -1)
+    : str
+}
+
+/**
+ * Determine the type of a character in a keypath.
+ */
+
+function getPathCharType (ch) {
+  if (ch === undefined || ch === null) { return 'eof' }
+
+  var code = ch.charCodeAt(0);
+
+  switch (code) {
+    case 0x5B: // [
+    case 0x5D: // ]
+    case 0x2E: // .
+    case 0x22: // "
+    case 0x27: // '
+      return ch
+
+    case 0x5F: // _
+    case 0x24: // $
+    case 0x2D: // -
+      return 'ident'
+
+    case 0x09: // Tab
+    case 0x0A: // Newline
+    case 0x0D: // Return
+    case 0xA0:  // No-break space
+    case 0xFEFF:  // Byte Order Mark
+    case 0x2028:  // Line Separator
+    case 0x2029:  // Paragraph Separator
+      return 'ws'
+  }
+
+  return 'ident'
+}
+
+/**
+ * Format a subPath, return its plain form if it is
+ * a literal string or number. Otherwise prepend the
+ * dynamic indicator (*).
+ */
+
+function formatSubPath (path) {
+  var trimmed = path.trim();
+  // invalid leading 0
+  if (path.charAt(0) === '0' && isNaN(path)) { return false }
+
+  return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
+}
+
+/**
+ * Parse a string path into an array of segments
+ */
+
+function parse$1 (path) {
+  var keys = [];
+  var index = -1;
+  var mode = BEFORE_PATH;
+  var subPathDepth = 0;
+  var c;
+  var key;
+  var newChar;
+  var type;
+  var transition;
+  var action;
+  var typeMap;
+  var actions = [];
+
+  actions[PUSH] = function () {
+    if (key !== undefined) {
+      keys.push(key);
+      key = undefined;
+    }
+  };
+
+  actions[APPEND] = function () {
+    if (key === undefined) {
+      key = newChar;
+    } else {
+      key += newChar;
+    }
+  };
+
+  actions[INC_SUB_PATH_DEPTH] = function () {
+    actions[APPEND]();
+    subPathDepth++;
+  };
+
+  actions[PUSH_SUB_PATH] = function () {
+    if (subPathDepth > 0) {
+      subPathDepth--;
+      mode = IN_SUB_PATH;
+      actions[APPEND]();
+    } else {
+      subPathDepth = 0;
+      if (key === undefined) { return false }
+      key = formatSubPath(key);
+      if (key === false) {
+        return false
+      } else {
+        actions[PUSH]();
+      }
+    }
+  };
+
+  function maybeUnescapeQuote () {
+    var nextChar = path[index + 1];
+    if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
+      (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
+      index++;
+      newChar = '\\' + nextChar;
+      actions[APPEND]();
+      return true
+    }
+  }
+
+  while (mode !== null) {
+    index++;
+    c = path[index];
+
+    if (c === '\\' && maybeUnescapeQuote()) {
+      continue
+    }
+
+    type = getPathCharType(c);
+    typeMap = pathStateMachine[mode];
+    transition = typeMap[type] || typeMap['else'] || ERROR;
+
+    if (transition === ERROR) {
+      return // parse error
+    }
+
+    mode = transition[0];
+    action = actions[transition[1]];
+    if (action) {
+      newChar = transition[2];
+      newChar = newChar === undefined
+        ? c
+        : newChar;
+      if (action() === false) {
+        return
+      }
+    }
+
+    if (mode === AFTER_PATH) {
+      return keys
+    }
+  }
+}
+
+
+
+
+
+var I18nPath = function I18nPath () {
+  this._cache = Object.create(null);
+};
+
+/**
+ * External parse that check for a cache hit first
+ */
+I18nPath.prototype.parsePath = function parsePath (path) {
+  var hit = this._cache[path];
+  if (!hit) {
+    hit = parse$1(path);
+    if (hit) {
+      this._cache[path] = hit;
+    }
+  }
+  return hit || []
+};
+
+/**
+ * Get path value from path string
+ */
+I18nPath.prototype.getPathValue = function getPathValue (obj, path) {
+  if (!isObject(obj)) { return null }
+
+  var paths = this.parsePath(path);
+  if (paths.length === 0) {
+    return null
+  } else {
+    var length = paths.length;
+    var last = obj;
+    var i = 0;
+    while (i < length) {
+      var value = last[paths[i]];
+      if (value === undefined) {
+        return null
+      }
+      last = value;
+      i++;
+    }
+
+    return last
+  }
+};
+
+/*  */
+
+
+
+var htmlTagMatcher = /<\/?[\w\s="/.':;#-\/]+>/;
+var linkKeyMatcher = /(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g;
+var linkKeyPrefixMatcher = /^@(?:\.([a-z]+))?:/;
+var bracketsMatcher = /[()]/g;
+var defaultModifiers = {
+  'upper': function (str) { return str.toLocaleUpperCase(); },
+  'lower': function (str) { return str.toLocaleLowerCase(); },
+  'capitalize': function (str) { return ("" + (str.charAt(0).toLocaleUpperCase()) + (str.substr(1))); }
+};
+
+var defaultFormatter = new BaseFormatter();
+
+var VueI18n = function VueI18n (options) {
+  var this$1 = this;
+  if ( options === void 0 ) options = {};
+
+  // Auto install if it is not done yet and `window` has `Vue`.
+  // To allow users to avoid auto-installation in some cases,
+  // this code should be placed here. See #290
+  /* istanbul ignore if */
+  if (!Vue && typeof window !== 'undefined' && window.Vue) {
+    install(window.Vue);
+  }
+
+  var locale = options.locale || 'en-US';
+  var fallbackLocale = options.fallbackLocale === false
+    ? false
+    : options.fallbackLocale || 'en-US';
+  var messages = options.messages || {};
+  var dateTimeFormats = options.dateTimeFormats || {};
+  var numberFormats = options.numberFormats || {};
+
+  this._vm = null;
+  this._formatter = options.formatter || defaultFormatter;
+  this._modifiers = options.modifiers || {};
+  this._missing = options.missing || null;
+  this._root = options.root || null;
+  this._sync = options.sync === undefined ? true : !!options.sync;
+  this._fallbackRoot = options.fallbackRoot === undefined
+    ? true
+    : !!options.fallbackRoot;
+  this._formatFallbackMessages = options.formatFallbackMessages === undefined
+    ? false
+    : !!options.formatFallbackMessages;
+  this._silentTranslationWarn = options.silentTranslationWarn === undefined
+    ? false
+    : options.silentTranslationWarn;
+  this._silentFallbackWarn = options.silentFallbackWarn === undefined
+    ? false
+    : !!options.silentFallbackWarn;
+  this._dateTimeFormatters = {};
+  this._numberFormatters = {};
+  this._path = new I18nPath();
+  this._dataListeners = [];
+  this._componentInstanceCreatedListener = options.componentInstanceCreatedListener || null;
+  this._preserveDirectiveContent = options.preserveDirectiveContent === undefined
+    ? false
+    : !!options.preserveDirectiveContent;
+  this.pluralizationRules = options.pluralizationRules || {};
+  this._warnHtmlInMessage = options.warnHtmlInMessage || 'off';
+  this._postTranslation = options.postTranslation || null;
+
+  /**
+   * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
+   * @param choicesLength {number} an overall amount of available choices
+   * @returns a final choice index
+  */
+  this.getChoiceIndex = function (choice, choicesLength) {
+    var thisPrototype = Object.getPrototypeOf(this$1);
+    if (thisPrototype && thisPrototype.getChoiceIndex) {
+      var prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex);
+      return (prototypeGetChoiceIndex).call(this$1, choice, choicesLength)
+    }
+
+    // Default (old) getChoiceIndex implementation - english-compatible
+    var defaultImpl = function (_choice, _choicesLength) {
+      _choice = Math.abs(_choice);
+
+      if (_choicesLength === 2) {
+        return _choice
+          ? _choice > 1
+            ? 1
+            : 0
+          : 1
+      }
+
+      return _choice ? Math.min(_choice, 2) : 0
+    };
+
+    if (this$1.locale in this$1.pluralizationRules) {
+      return this$1.pluralizationRules[this$1.locale].apply(this$1, [choice, choicesLength])
+    } else {
+      return defaultImpl(choice, choicesLength)
+    }
+  };
+
+
+  this._exist = function (message, key) {
+    if (!message || !key) { return false }
+    if (!isNull(this$1._path.getPathValue(message, key))) { return true }
+    // fallback for flat key
+    if (message[key]) { return true }
+    return false
+  };
+
+  if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+    Object.keys(messages).forEach(function (locale) {
+      this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);
+    });
+  }
+
+  this._initVM({
+    locale: locale,
+    fallbackLocale: fallbackLocale,
+    messages: messages,
+    dateTimeFormats: dateTimeFormats,
+    numberFormats: numberFormats
+  });
+};
+
+var prototypeAccessors = { vm: { configurable: true },messages: { configurable: true },dateTimeFormats: { configurable: true },numberFormats: { configurable: true },availableLocales: { configurable: true },locale: { configurable: true },fallbackLocale: { configurable: true },formatFallbackMessages: { configurable: true },missing: { configurable: true },formatter: { configurable: true },silentTranslationWarn: { configurable: true },silentFallbackWarn: { configurable: true },preserveDirectiveContent: { configurable: true },warnHtmlInMessage: { configurable: true },postTranslation: { configurable: true } };
+
+VueI18n.prototype._checkLocaleMessage = function _checkLocaleMessage (locale, level, message) {
+  var paths = [];
+
+  var fn = function (level, locale, message, paths) {
+    if (isPlainObject(message)) {
+      Object.keys(message).forEach(function (key) {
+        var val = message[key];
+        if (isPlainObject(val)) {
+          paths.push(key);
+          paths.push('.');
+          fn(level, locale, val, paths);
+          paths.pop();
+          paths.pop();
+        } else {
+          paths.push(key);
+          fn(level, locale, val, paths);
+          paths.pop();
+        }
+      });
+    } else if (isArray(message)) {
+      message.forEach(function (item, index) {
+        if (isPlainObject(item)) {
+          paths.push(("[" + index + "]"));
+          paths.push('.');
+          fn(level, locale, item, paths);
+          paths.pop();
+          paths.pop();
+        } else {
+          paths.push(("[" + index + "]"));
+          fn(level, locale, item, paths);
+          paths.pop();
+        }
+      });
+    } else if (isString(message)) {
+      var ret = htmlTagMatcher.test(message);
+      if (ret) {
+        var msg = "Detected HTML in message '" + message + "' of keypath '" + (paths.join('')) + "' at '" + locale + "'. Consider component interpolation with '<i18n>' to avoid XSS. See https://bit.ly/2ZqJzkp";
+        if (level === 'warn') {
+          warn(msg);
+        } else if (level === 'error') {
+          error(msg);
+        }
+      }
+    }
+  };
+
+  fn(level, locale, message, paths);
+};
+
+VueI18n.prototype._initVM = function _initVM (data) {
+  var silent = Vue.config.silent;
+  Vue.config.silent = true;
+  this._vm = new Vue({ data: data });
+  Vue.config.silent = silent;
+};
+
+VueI18n.prototype.destroyVM = function destroyVM () {
+  this._vm.$destroy();
+};
+
+VueI18n.prototype.subscribeDataChanging = function subscribeDataChanging (vm) {
+  this._dataListeners.push(vm);
+};
+
+VueI18n.prototype.unsubscribeDataChanging = function unsubscribeDataChanging (vm) {
+  remove(this._dataListeners, vm);
+};
+
+VueI18n.prototype.watchI18nData = function watchI18nData () {
+  var self = this;
+  return this._vm.$watch('$data', function () {
+    var i = self._dataListeners.length;
+    while (i--) {
+      Vue.nextTick(function () {
+        self._dataListeners[i] && self._dataListeners[i].$forceUpdate();
+      });
+    }
+  }, { deep: true })
+};
+
+VueI18n.prototype.watchLocale = function watchLocale () {
+  /* istanbul ignore if */
+  if (!this._sync || !this._root) { return null }
+  var target = this._vm;
+  return this._root.$i18n.vm.$watch('locale', function (val) {
+    target.$set(target, 'locale', val);
+    target.$forceUpdate();
+  }, { immediate: true })
+};
+
+VueI18n.prototype.onComponentInstanceCreated = function onComponentInstanceCreated (newI18n) {
+  if (this._componentInstanceCreatedListener) {
+    this._componentInstanceCreatedListener(newI18n, this);
+  }
+};
+
+prototypeAccessors.vm.get = function () { return this._vm };
+
+prototypeAccessors.messages.get = function () { return looseClone(this._getMessages()) };
+prototypeAccessors.dateTimeFormats.get = function () { return looseClone(this._getDateTimeFormats()) };
+prototypeAccessors.numberFormats.get = function () { return looseClone(this._getNumberFormats()) };
+prototypeAccessors.availableLocales.get = function () { return Object.keys(this.messages).sort() };
+
+prototypeAccessors.locale.get = function () { return this._vm.locale };
+prototypeAccessors.locale.set = function (locale) {
+  this._vm.$set(this._vm, 'locale', locale);
+};
+
+prototypeAccessors.fallbackLocale.get = function () { return this._vm.fallbackLocale };
+prototypeAccessors.fallbackLocale.set = function (locale) {
+  this._localeChainCache = {};
+  this._vm.$set(this._vm, 'fallbackLocale', locale);
+};
+
+prototypeAccessors.formatFallbackMessages.get = function () { return this._formatFallbackMessages };
+prototypeAccessors.formatFallbackMessages.set = function (fallback) { this._formatFallbackMessages = fallback; };
+
+prototypeAccessors.missing.get = function () { return this._missing };
+prototypeAccessors.missing.set = function (handler) { this._missing = handler; };
+
+prototypeAccessors.formatter.get = function () { return this._formatter };
+prototypeAccessors.formatter.set = function (formatter) { this._formatter = formatter; };
+
+prototypeAccessors.silentTranslationWarn.get = function () { return this._silentTranslationWarn };
+prototypeAccessors.silentTranslationWarn.set = function (silent) { this._silentTranslationWarn = silent; };
+
+prototypeAccessors.silentFallbackWarn.get = function () { return this._silentFallbackWarn };
+prototypeAccessors.silentFallbackWarn.set = function (silent) { this._silentFallbackWarn = silent; };
+
+prototypeAccessors.preserveDirectiveContent.get = function () { return this._preserveDirectiveContent };
+prototypeAccessors.preserveDirectiveContent.set = function (preserve) { this._preserveDirectiveContent = preserve; };
+
+prototypeAccessors.warnHtmlInMessage.get = function () { return this._warnHtmlInMessage };
+prototypeAccessors.warnHtmlInMessage.set = function (level) {
+    var this$1 = this;
+
+  var orgLevel = this._warnHtmlInMessage;
+  this._warnHtmlInMessage = level;
+  if (orgLevel !== level && (level === 'warn' || level === 'error')) {
+    var messages = this._getMessages();
+    Object.keys(messages).forEach(function (locale) {
+      this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);
+    });
+  }
+};
+
+prototypeAccessors.postTranslation.get = function () { return this._postTranslation };
+prototypeAccessors.postTranslation.set = function (handler) { this._postTranslation = handler; };
+
+VueI18n.prototype._getMessages = function _getMessages () { return this._vm.messages };
+VueI18n.prototype._getDateTimeFormats = function _getDateTimeFormats () { return this._vm.dateTimeFormats };
+VueI18n.prototype._getNumberFormats = function _getNumberFormats () { return this._vm.numberFormats };
+
+VueI18n.prototype._warnDefault = function _warnDefault (locale, key, result, vm, values, interpolateMode) {
+  if (!isNull(result)) { return result }
+  if (this._missing) {
+    var missingRet = this._missing.apply(null, [locale, key, vm, values]);
+    if (isString(missingRet)) {
+      return missingRet
+    }
+  } else {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+      warn(
+        "Cannot translate the value of keypath '" + key + "'. " +
+        'Use the value of keypath as default.'
+      );
+    }
+  }
+
+  if (this._formatFallbackMessages) {
+    var parsedArgs = parseArgs.apply(void 0, values);
+    return this._render(key, interpolateMode, parsedArgs.params, key)
+  } else {
+    return key
+  }
+};
+
+VueI18n.prototype._isFallbackRoot = function _isFallbackRoot (val) {
+  return !val && !isNull(this._root) && this._fallbackRoot
+};
+
+VueI18n.prototype._isSilentFallbackWarn = function _isSilentFallbackWarn (key) {
+  return this._silentFallbackWarn instanceof RegExp
+    ? this._silentFallbackWarn.test(key)
+    : this._silentFallbackWarn
+};
+
+VueI18n.prototype._isSilentFallback = function _isSilentFallback (locale, key) {
+  return this._isSilentFallbackWarn(key) && (this._isFallbackRoot() || locale !== this.fallbackLocale)
+};
+
+VueI18n.prototype._isSilentTranslationWarn = function _isSilentTranslationWarn (key) {
+  return this._silentTranslationWarn instanceof RegExp
+    ? this._silentTranslationWarn.test(key)
+    : this._silentTranslationWarn
+};
+
+VueI18n.prototype._interpolate = function _interpolate (
+  locale,
+  message,
+  key,
+  host,
+  interpolateMode,
+  values,
+  visitedLinkStack
+) {
+  if (!message) { return null }
+
+  var pathRet = this._path.getPathValue(message, key);
+  if (isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
+
+  var ret;
+  if (isNull(pathRet)) {
+    /* istanbul ignore else */
+    if (isPlainObject(message)) {
+      ret = message[key];
+      if (!(isString(ret) || isFunction(ret))) {
+        if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+          warn(("Value of key '" + key + "' is not a string or function !"));
+        }
+        return null
+      }
+    } else {
+      return null
+    }
+  } else {
+    /* istanbul ignore else */
+    if (isString(pathRet) || isFunction(pathRet)) {
+      ret = pathRet;
+    } else {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+        warn(("Value of key '" + key + "' is not a string or function!"));
+      }
+      return null
+    }
+  }
+
+  // Check for the existence of links within the translated string
+  if (isString(ret) && (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0)) {
+    ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack);
+  }
+
+  return this._render(ret, interpolateMode, values, key)
+};
+
+VueI18n.prototype._link = function _link (
+  locale,
+  message,
+  str,
+  host,
+  interpolateMode,
+  values,
+  visitedLinkStack
+) {
+  var ret = str;
+
+  // Match all the links within the local
+  // We are going to replace each of
+  // them with its translation
+  var matches = ret.match(linkKeyMatcher);
+  for (var idx in matches) {
+    // ie compatible: filter custom array
+    // prototype method
+    if (!matches.hasOwnProperty(idx)) {
+      continue
+    }
+    var link = matches[idx];
+    var linkKeyPrefixMatches = link.match(linkKeyPrefixMatcher);
+    var linkPrefix = linkKeyPrefixMatches[0];
+      var formatterName = linkKeyPrefixMatches[1];
+
+    // Remove the leading @:, @.case: and the brackets
+    var linkPlaceholder = link.replace(linkPrefix, '').replace(bracketsMatcher, '');
+
+    if (includes(visitedLinkStack, linkPlaceholder)) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn(("Circular reference found. \"" + link + "\" is already visited in the chain of " + (visitedLinkStack.reverse().join(' <- '))));
+      }
+      return ret
+    }
+    visitedLinkStack.push(linkPlaceholder);
+
+    // Translate the link
+    var translated = this._interpolate(
+      locale, message, linkPlaceholder, host,
+      interpolateMode === 'raw' ? 'string' : interpolateMode,
+      interpolateMode === 'raw' ? undefined : values,
+      visitedLinkStack
+    );
+
+    if (this._isFallbackRoot(translated)) {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(linkPlaceholder)) {
+        warn(("Fall back to translate the link placeholder '" + linkPlaceholder + "' with root locale."));
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      var root = this._root.$i18n;
+      translated = root._translate(
+        root._getMessages(), root.locale, root.fallbackLocale,
+        linkPlaceholder, host, interpolateMode, values
+      );
+    }
+    translated = this._warnDefault(
+      locale, linkPlaceholder, translated, host,
+      isArray(values) ? values : [values],
+      interpolateMode
+    );
+
+    if (this._modifiers.hasOwnProperty(formatterName)) {
+      translated = this._modifiers[formatterName](translated);
+    } else if (defaultModifiers.hasOwnProperty(formatterName)) {
+      translated = defaultModifiers[formatterName](translated);
+    }
+
+    visitedLinkStack.pop();
+
+    // Replace the link with the translated
+    ret = !translated ? ret : ret.replace(link, translated);
+  }
+
+  return ret
+};
+
+VueI18n.prototype._createMessageContext = function _createMessageContext (values) {
+  var _list = isArray(values) ? values : [];
+  var _named = isObject(values) ? values : {};
+  var list = function (index) { return _list[index]; };
+  var named = function (key) { return _named[key]; };
+  return {
+    list: list,
+    named: named
+  }
+};
+
+VueI18n.prototype._render = function _render (message, interpolateMode, values, path) {
+  if (isFunction(message)) {
+    return message(this._createMessageContext(values))
+  }
+
+  var ret = this._formatter.interpolate(message, values, path);
+
+  // If the custom formatter refuses to work - apply the default one
+  if (!ret) {
+    ret = defaultFormatter.interpolate(message, values, path);
+  }
+
+  // if interpolateMode is **not** 'string' ('row'),
+  // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
+  return interpolateMode === 'string' && !isString(ret) ? ret.join('') : ret
+};
+
+VueI18n.prototype._appendItemToChain = function _appendItemToChain (chain, item, blocks) {
+  var follow = false;
+  if (!includes(chain, item)) {
+    follow = true;
+    if (item) {
+      follow = item[item.length - 1] !== '!';
+      item = item.replace(/!/g, '');
+      chain.push(item);
+      if (blocks && blocks[item]) {
+        follow = blocks[item];
+      }
+    }
+  }
+  return follow
+};
+
+VueI18n.prototype._appendLocaleToChain = function _appendLocaleToChain (chain, locale, blocks) {
+  var follow;
+  var tokens = locale.split('-');
+  do {
+    var item = tokens.join('-');
+    follow = this._appendItemToChain(chain, item, blocks);
+    tokens.splice(-1, 1);
+  } while (tokens.length && (follow === true))
+  return follow
+};
+
+VueI18n.prototype._appendBlockToChain = function _appendBlockToChain (chain, block, blocks) {
+  var follow = true;
+  for (var i = 0; (i < block.length) && (isBoolean(follow)); i++) {
+    var locale = block[i];
+    if (isString(locale)) {
+      follow = this._appendLocaleToChain(chain, locale, blocks);
+    }
+  }
+  return follow
+};
+
+VueI18n.prototype._getLocaleChain = function _getLocaleChain (start, fallbackLocale) {
+  if (start === '') { return [] }
+
+  if (!this._localeChainCache) {
+    this._localeChainCache = {};
+  }
+
+  var chain = this._localeChainCache[start];
+  if (!chain) {
+    if (!fallbackLocale) {
+      fallbackLocale = this.fallbackLocale;
+    }
+    chain = [];
+
+    // first block defined by start
+    var block = [start];
+
+    // while any intervening block found
+    while (isArray(block)) {
+      block = this._appendBlockToChain(
+        chain,
+        block,
+        fallbackLocale
+      );
+    }
+
+    // last block defined by default
+    var defaults;
+    if (isArray(fallbackLocale)) {
+      defaults = fallbackLocale;
+    } else if (isObject(fallbackLocale)) {
+      /* $FlowFixMe */
+      if (fallbackLocale['default']) {
+        defaults = fallbackLocale['default'];
+      } else {
+        defaults = null;
+      }
+    } else {
+      defaults = fallbackLocale;
+    }
+
+    // convert defaults to array
+    if (isString(defaults)) {
+      block = [defaults];
+    } else {
+      block = defaults;
+    }
+    if (block) {
+      this._appendBlockToChain(
+        chain,
+        block,
+        null
+      );
+    }
+    this._localeChainCache[start] = chain;
+  }
+  return chain
+};
+
+VueI18n.prototype._translate = function _translate (
+  messages,
+  locale,
+  fallback,
+  key,
+  host,
+  interpolateMode,
+  args
+) {
+  var chain = this._getLocaleChain(locale, fallback);
+  var res;
+  for (var i = 0; i < chain.length; i++) {
+    var step = chain[i];
+    res =
+      this._interpolate(step, messages[step], key, host, interpolateMode, args, [key]);
+    if (!isNull(res)) {
+      if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to translate the keypath '" + key + "' with '" + step + "' locale."));
+      }
+      return res
+    }
+  }
+  return null
+};
+
+VueI18n.prototype._t = function _t (key, _locale, messages, host) {
+    var ref;
+
+    var values = [], len = arguments.length - 4;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 4 ];
+  if (!key) { return '' }
+
+  var parsedArgs = parseArgs.apply(void 0, values);
+  var locale = parsedArgs.locale || _locale;
+
+  var ret = this._translate(
+    messages, locale, this.fallbackLocale, key,
+    host, 'string', parsedArgs.params
+  );
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+      warn(("Fall back to translate the keypath '" + key + "' with root locale."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return (ref = this._root).$t.apply(ref, [ key ].concat( values ))
+  } else {
+    ret = this._warnDefault(locale, key, ret, host, values, 'string');
+    if (this._postTranslation && ret !== null && ret !== undefined) {
+      ret = this._postTranslation(ret, key);
+    }
+    return ret
+  }
+};
+
+VueI18n.prototype.t = function t (key) {
+    var ref;
+
+    var values = [], len = arguments.length - 1;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
+  return (ref = this)._t.apply(ref, [ key, this.locale, this._getMessages(), null ].concat( values ))
+};
+
+VueI18n.prototype._i = function _i (key, locale, messages, host, values) {
+  var ret =
+    this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+      warn(("Fall back to interpolate the keypath '" + key + "' with root locale."));
+    }
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n.i(key, locale, values)
+  } else {
+    return this._warnDefault(locale, key, ret, host, [values], 'raw')
+  }
+};
+
+VueI18n.prototype.i = function i (key, locale, values) {
+  /* istanbul ignore if */
+  if (!key) { return '' }
+
+  if (!isString(locale)) {
+    locale = this.locale;
+  }
+
+  return this._i(key, locale, this._getMessages(), null, values)
+};
+
+VueI18n.prototype._tc = function _tc (
+  key,
+  _locale,
+  messages,
+  host,
+  choice
+) {
+    var ref;
+
+    var values = [], len = arguments.length - 5;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 5 ];
+  if (!key) { return '' }
+  if (choice === undefined) {
+    choice = 1;
+  }
+
+  var predefined = { 'count': choice, 'n': choice };
+  var parsedArgs = parseArgs.apply(void 0, values);
+  parsedArgs.params = Object.assign(predefined, parsedArgs.params);
+  values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params];
+  return this.fetchChoice((ref = this)._t.apply(ref, [ key, _locale, messages, host ].concat( values )), choice)
+};
+
+VueI18n.prototype.fetchChoice = function fetchChoice (message, choice) {
+  /* istanbul ignore if */
+  if (!message && !isString(message)) { return null }
+  var choices = message.split('|');
+
+  choice = this.getChoiceIndex(choice, choices.length);
+  if (!choices[choice]) { return message }
+  return choices[choice].trim()
+};
+
+VueI18n.prototype.tc = function tc (key, choice) {
+    var ref;
+
+    var values = [], len = arguments.length - 2;
+    while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
+  return (ref = this)._tc.apply(ref, [ key, this.locale, this._getMessages(), null, choice ].concat( values ))
+};
+
+VueI18n.prototype._te = function _te (key, locale, messages) {
+    var args = [], len = arguments.length - 3;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 3 ];
+
+  var _locale = parseArgs.apply(void 0, args).locale || locale;
+  return this._exist(messages[_locale], key)
+};
+
+VueI18n.prototype.te = function te (key, locale) {
+  return this._te(key, this.locale, this._getMessages(), locale)
+};
+
+VueI18n.prototype.getLocaleMessage = function getLocaleMessage (locale) {
+  return looseClone(this._vm.messages[locale] || {})
+};
+
+VueI18n.prototype.setLocaleMessage = function setLocaleMessage (locale, message) {
+  if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+    this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+  }
+  this._vm.$set(this._vm.messages, locale, message);
+};
+
+VueI18n.prototype.mergeLocaleMessage = function mergeLocaleMessage (locale, message) {
+  if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+    this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+  }
+  this._vm.$set(this._vm.messages, locale, merge({}, this._vm.messages[locale] || {}, message));
+};
+
+VueI18n.prototype.getDateTimeFormat = function getDateTimeFormat (locale) {
+  return looseClone(this._vm.dateTimeFormats[locale] || {})
+};
+
+VueI18n.prototype.setDateTimeFormat = function setDateTimeFormat (locale, format) {
+  this._vm.$set(this._vm.dateTimeFormats, locale, format);
+  this._clearDateTimeFormat(locale, format);
+};
+
+VueI18n.prototype.mergeDateTimeFormat = function mergeDateTimeFormat (locale, format) {
+  this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format));
+  this._clearDateTimeFormat(locale, format);
+};
+
+VueI18n.prototype._clearDateTimeFormat = function _clearDateTimeFormat (locale, format) {
+  for (var key in format) {
+    var id = locale + "__" + key;
+
+    if (!this._dateTimeFormatters.hasOwnProperty(id)) {
+      continue
+    }
+
+    delete this._dateTimeFormatters[id];
+  }
+};
+
+VueI18n.prototype._localizeDateTime = function _localizeDateTime (
+  value,
+  locale,
+  fallback,
+  dateTimeFormats,
+  key
+) {
+  var _locale = locale;
+  var formats = dateTimeFormats[_locale];
+
+  var chain = this._getLocaleChain(locale, fallback);
+  for (var i = 0; i < chain.length; i++) {
+    var current = _locale;
+    var step = chain[i];
+    formats = dateTimeFormats[step];
+    _locale = step;
+    // fallback locale
+    if (isNull(formats) || isNull(formats[key])) {
+      if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to '" + step + "' datetime formats from '" + current + "' datetime formats."));
+      }
+    } else {
+      break
+    }
+  }
+
+  if (isNull(formats) || isNull(formats[key])) {
+    return null
+  } else {
+    var format = formats[key];
+    var id = _locale + "__" + key;
+    var formatter = this._dateTimeFormatters[id];
+    if (!formatter) {
+      formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format);
+    }
+    return formatter.format(value)
+  }
+};
+
+VueI18n.prototype._d = function _d (value, locale, key) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.dateTimeFormat) {
+    warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.');
+    return ''
+  }
+
+  if (!key) {
+    return new Intl.DateTimeFormat(locale).format(value)
+  }
+
+  var ret =
+    this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+      warn(("Fall back to datetime localization of root: key '" + key + "'."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n.d(value, key, locale)
+  } else {
+    return ret || ''
+  }
+};
+
+VueI18n.prototype.d = function d (value) {
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+
+  var locale = this.locale;
+  var key = null;
+
+  if (args.length === 1) {
+    if (isString(args[0])) {
+      key = args[0];
+    } else if (isObject(args[0])) {
+      if (args[0].locale) {
+        locale = args[0].locale;
+      }
+      if (args[0].key) {
+        key = args[0].key;
+      }
+    }
+  } else if (args.length === 2) {
+    if (isString(args[0])) {
+      key = args[0];
+    }
+    if (isString(args[1])) {
+      locale = args[1];
+    }
+  }
+
+  return this._d(value, locale, key)
+};
+
+VueI18n.prototype.getNumberFormat = function getNumberFormat (locale) {
+  return looseClone(this._vm.numberFormats[locale] || {})
+};
+
+VueI18n.prototype.setNumberFormat = function setNumberFormat (locale, format) {
+  this._vm.$set(this._vm.numberFormats, locale, format);
+  this._clearNumberFormat(locale, format);
+};
+
+VueI18n.prototype.mergeNumberFormat = function mergeNumberFormat (locale, format) {
+  this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format));
+  this._clearNumberFormat(locale, format);
+};
+
+VueI18n.prototype._clearNumberFormat = function _clearNumberFormat (locale, format) {
+  for (var key in format) {
+    var id = locale + "__" + key;
+
+    if (!this._numberFormatters.hasOwnProperty(id)) {
+      continue
+    }
+
+    delete this._numberFormatters[id];
+  }
+};
+
+VueI18n.prototype._getNumberFormatter = function _getNumberFormatter (
+  value,
+  locale,
+  fallback,
+  numberFormats,
+  key,
+  options
+) {
+  var _locale = locale;
+  var formats = numberFormats[_locale];
+
+  var chain = this._getLocaleChain(locale, fallback);
+  for (var i = 0; i < chain.length; i++) {
+    var current = _locale;
+    var step = chain[i];
+    formats = numberFormats[step];
+    _locale = step;
+    // fallback locale
+    if (isNull(formats) || isNull(formats[key])) {
+      if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to '" + step + "' number formats from '" + current + "' number formats."));
+      }
+    } else {
+      break
+    }
+  }
+
+  if (isNull(formats) || isNull(formats[key])) {
+    return null
+  } else {
+    var format = formats[key];
+
+    var formatter;
+    if (options) {
+      // If options specified - create one time number formatter
+      formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options));
+    } else {
+      var id = _locale + "__" + key;
+      formatter = this._numberFormatters[id];
+      if (!formatter) {
+        formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format);
+      }
+    }
+    return formatter
+  }
+};
+
+VueI18n.prototype._n = function _n (value, locale, key, options) {
+  /* istanbul ignore if */
+  if (!VueI18n.availabilities.numberFormat) {
+    if (process.env.NODE_ENV !== 'production') {
+      warn('Cannot format a Number value due to not supported Intl.NumberFormat.');
+    }
+    return ''
+  }
+
+  if (!key) {
+    var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+    return nf.format(value)
+  }
+
+  var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+  var ret = formatter && formatter.format(value);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+      warn(("Fall back to number localization of root: key '" + key + "'."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n.n(value, Object.assign({}, { key: key, locale: locale }, options))
+  } else {
+    return ret || ''
+  }
+};
+
+VueI18n.prototype.n = function n (value) {
+    var args = [], len = arguments.length - 1;
+    while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+
+  var locale = this.locale;
+  var key = null;
+  var options = null;
+
+  if (args.length === 1) {
+    if (isString(args[0])) {
+      key = args[0];
+    } else if (isObject(args[0])) {
+      if (args[0].locale) {
+        locale = args[0].locale;
+      }
+      if (args[0].key) {
+        key = args[0].key;
+      }
+
+      // Filter out number format options only
+      options = Object.keys(args[0]).reduce(function (acc, key) {
+          var obj;
+
+        if (includes(numberFormatKeys, key)) {
+          return Object.assign({}, acc, ( obj = {}, obj[key] = args[0][key], obj ))
+        }
+        return acc
+      }, null);
+    }
+  } else if (args.length === 2) {
+    if (isString(args[0])) {
+      key = args[0];
+    }
+    if (isString(args[1])) {
+      locale = args[1];
+    }
+  }
+
+  return this._n(value, locale, key, options)
+};
+
+VueI18n.prototype._ntp = function _ntp (value, locale, key, options) {
+  /* istanbul ignore if */
+  if (!VueI18n.availabilities.numberFormat) {
+    if (process.env.NODE_ENV !== 'production') {
+      warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.');
+    }
+    return []
+  }
+
+  if (!key) {
+    var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+    return nf.formatToParts(value)
+  }
+
+  var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+  var ret = formatter && formatter.formatToParts(value);
+  if (this._isFallbackRoot(ret)) {
+    if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+      warn(("Fall back to format number to parts of root: key '" + key + "' ."));
+    }
+    /* istanbul ignore if */
+    if (!this._root) { throw Error('unexpected error') }
+    return this._root.$i18n._ntp(value, locale, key, options)
+  } else {
+    return ret || []
+  }
+};
+
+Object.defineProperties( VueI18n.prototype, prototypeAccessors );
+
+var availabilities;
+// $FlowFixMe
+Object.defineProperty(VueI18n, 'availabilities', {
+  get: function get () {
+    if (!availabilities) {
+      var intlDefined = typeof Intl !== 'undefined';
+      availabilities = {
+        dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
+        numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
+      };
+    }
+
+    return availabilities
+  }
+});
+
+VueI18n.install = install;
+VueI18n.version = '8.21.0';
+
+export default VueI18n;

+ 2157 - 0
plugin/vue-i18n/dist/vue-i18n.js

@@ -0,0 +1,2157 @@
+/*!
+ * vue-i18n v8.21.0 
+ * (c) 2020 kazuya kawaguchi
+ * Released under the MIT License.
+ */
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+  typeof define === 'function' && define.amd ? define(factory) :
+  (global.VueI18n = factory());
+}(this, (function () { 'use strict';
+
+  /*  */
+
+  /**
+   * constants
+   */
+
+  var numberFormatKeys = [
+    'style',
+    'currency',
+    'currencyDisplay',
+    'useGrouping',
+    'minimumIntegerDigits',
+    'minimumFractionDigits',
+    'maximumFractionDigits',
+    'minimumSignificantDigits',
+    'maximumSignificantDigits',
+    'localeMatcher',
+    'formatMatcher',
+    'unit'
+  ];
+
+  /**
+   * utilities
+   */
+
+  function warn (msg, err) {
+    if (typeof console !== 'undefined') {
+      console.warn('[vue-i18n] ' + msg);
+      /* istanbul ignore if */
+      if (err) {
+        console.warn(err.stack);
+      }
+    }
+  }
+
+  function error (msg, err) {
+    if (typeof console !== 'undefined') {
+      console.error('[vue-i18n] ' + msg);
+      /* istanbul ignore if */
+      if (err) {
+        console.error(err.stack);
+      }
+    }
+  }
+
+  var isArray = Array.isArray;
+
+  function isObject (obj) {
+    return obj !== null && typeof obj === 'object'
+  }
+
+  function isBoolean (val) {
+    return typeof val === 'boolean'
+  }
+
+  function isString (val) {
+    return typeof val === 'string'
+  }
+
+  var toString = Object.prototype.toString;
+  var OBJECT_STRING = '[object Object]';
+  function isPlainObject (obj) {
+    return toString.call(obj) === OBJECT_STRING
+  }
+
+  function isNull (val) {
+    return val === null || val === undefined
+  }
+
+  function isFunction (val) {
+    return typeof val === 'function'
+  }
+
+  function parseArgs () {
+    var args = [], len = arguments.length;
+    while ( len-- ) args[ len ] = arguments[ len ];
+
+    var locale = null;
+    var params = null;
+    if (args.length === 1) {
+      if (isObject(args[0]) || isArray(args[0])) {
+        params = args[0];
+      } else if (typeof args[0] === 'string') {
+        locale = args[0];
+      }
+    } else if (args.length === 2) {
+      if (typeof args[0] === 'string') {
+        locale = args[0];
+      }
+      /* istanbul ignore if */
+      if (isObject(args[1]) || isArray(args[1])) {
+        params = args[1];
+      }
+    }
+
+    return { locale: locale, params: params }
+  }
+
+  function looseClone (obj) {
+    return JSON.parse(JSON.stringify(obj))
+  }
+
+  function remove (arr, item) {
+    if (arr.length) {
+      var index = arr.indexOf(item);
+      if (index > -1) {
+        return arr.splice(index, 1)
+      }
+    }
+  }
+
+  function includes (arr, item) {
+    return !!~arr.indexOf(item)
+  }
+
+  var hasOwnProperty = Object.prototype.hasOwnProperty;
+  function hasOwn (obj, key) {
+    return hasOwnProperty.call(obj, key)
+  }
+
+  function merge (target) {
+    var arguments$1 = arguments;
+
+    var output = Object(target);
+    for (var i = 1; i < arguments.length; i++) {
+      var source = arguments$1[i];
+      if (source !== undefined && source !== null) {
+        var key = (void 0);
+        for (key in source) {
+          if (hasOwn(source, key)) {
+            if (isObject(source[key])) {
+              output[key] = merge(output[key], source[key]);
+            } else {
+              output[key] = source[key];
+            }
+          }
+        }
+      }
+    }
+    return output
+  }
+
+  function looseEqual (a, b) {
+    if (a === b) { return true }
+    var isObjectA = isObject(a);
+    var isObjectB = isObject(b);
+    if (isObjectA && isObjectB) {
+      try {
+        var isArrayA = isArray(a);
+        var isArrayB = isArray(b);
+        if (isArrayA && isArrayB) {
+          return a.length === b.length && a.every(function (e, i) {
+            return looseEqual(e, b[i])
+          })
+        } else if (!isArrayA && !isArrayB) {
+          var keysA = Object.keys(a);
+          var keysB = Object.keys(b);
+          return keysA.length === keysB.length && keysA.every(function (key) {
+            return looseEqual(a[key], b[key])
+          })
+        } else {
+          /* istanbul ignore next */
+          return false
+        }
+      } catch (e) {
+        /* istanbul ignore next */
+        return false
+      }
+    } else if (!isObjectA && !isObjectB) {
+      return String(a) === String(b)
+    } else {
+      return false
+    }
+  }
+
+  /*  */
+
+  function extend (Vue) {
+    if (!Vue.prototype.hasOwnProperty('$i18n')) {
+      // $FlowFixMe
+      Object.defineProperty(Vue.prototype, '$i18n', {
+        get: function get () { return this._i18n }
+      });
+    }
+
+    Vue.prototype.$t = function (key) {
+      var values = [], len = arguments.length - 1;
+      while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
+
+      var i18n = this.$i18n;
+      return i18n._t.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this ].concat( values ))
+    };
+
+    Vue.prototype.$tc = function (key, choice) {
+      var values = [], len = arguments.length - 2;
+      while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
+
+      var i18n = this.$i18n;
+      return i18n._tc.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this, choice ].concat( values ))
+    };
+
+    Vue.prototype.$te = function (key, locale) {
+      var i18n = this.$i18n;
+      return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
+    };
+
+    Vue.prototype.$d = function (value) {
+      var ref;
+
+      var args = [], len = arguments.length - 1;
+      while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+      return (ref = this.$i18n).d.apply(ref, [ value ].concat( args ))
+    };
+
+    Vue.prototype.$n = function (value) {
+      var ref;
+
+      var args = [], len = arguments.length - 1;
+      while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+      return (ref = this.$i18n).n.apply(ref, [ value ].concat( args ))
+    };
+  }
+
+  /*  */
+
+  var mixin = {
+    beforeCreate: function beforeCreate () {
+      var options = this.$options;
+      options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+      if (options.i18n) {
+        if (options.i18n instanceof VueI18n) {
+          // init locale messages via custom blocks
+          if (options.__i18n) {
+            try {
+              var localeMessages = {};
+              options.__i18n.forEach(function (resource) {
+                localeMessages = merge(localeMessages, JSON.parse(resource));
+              });
+              Object.keys(localeMessages).forEach(function (locale) {
+                options.i18n.mergeLocaleMessage(locale, localeMessages[locale]);
+              });
+            } catch (e) {
+              {
+                error("Cannot parse locale messages via custom blocks.", e);
+              }
+            }
+          }
+          this._i18n = options.i18n;
+          this._i18nWatcher = this._i18n.watchI18nData();
+        } else if (isPlainObject(options.i18n)) {
+          var rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n
+            ? this.$root.$i18n
+            : null;
+          // component local i18n
+          if (rootI18n) {
+            options.i18n.root = this.$root;
+            options.i18n.formatter = rootI18n.formatter;
+            options.i18n.fallbackLocale = rootI18n.fallbackLocale;
+            options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages;
+            options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn;
+            options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn;
+            options.i18n.pluralizationRules = rootI18n.pluralizationRules;
+            options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent;
+          }
+
+          // init locale messages via custom blocks
+          if (options.__i18n) {
+            try {
+              var localeMessages$1 = {};
+              options.__i18n.forEach(function (resource) {
+                localeMessages$1 = merge(localeMessages$1, JSON.parse(resource));
+              });
+              options.i18n.messages = localeMessages$1;
+            } catch (e) {
+              {
+                warn("Cannot parse locale messages via custom blocks.", e);
+              }
+            }
+          }
+
+          var ref = options.i18n;
+          var sharedMessages = ref.sharedMessages;
+          if (sharedMessages && isPlainObject(sharedMessages)) {
+            options.i18n.messages = merge(options.i18n.messages, sharedMessages);
+          }
+
+          this._i18n = new VueI18n(options.i18n);
+          this._i18nWatcher = this._i18n.watchI18nData();
+
+          if (options.i18n.sync === undefined || !!options.i18n.sync) {
+            this._localeWatcher = this.$i18n.watchLocale();
+          }
+
+          if (rootI18n) {
+            rootI18n.onComponentInstanceCreated(this._i18n);
+          }
+        } else {
+          {
+            warn("Cannot be interpreted 'i18n' option.");
+          }
+        }
+      } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+        // root i18n
+        this._i18n = this.$root.$i18n;
+      } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+        // parent i18n
+        this._i18n = options.parent.$i18n;
+      }
+    },
+
+    beforeMount: function beforeMount () {
+      var options = this.$options;
+      options.i18n = options.i18n || (options.__i18n ? {} : null);
+
+      if (options.i18n) {
+        if (options.i18n instanceof VueI18n) {
+          // init locale messages via custom blocks
+          this._i18n.subscribeDataChanging(this);
+          this._subscribing = true;
+        } else if (isPlainObject(options.i18n)) {
+          this._i18n.subscribeDataChanging(this);
+          this._subscribing = true;
+        } else {
+          {
+            warn("Cannot be interpreted 'i18n' option.");
+          }
+        }
+      } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+        this._i18n.subscribeDataChanging(this);
+        this._subscribing = true;
+      }
+    },
+
+    beforeDestroy: function beforeDestroy () {
+      if (!this._i18n) { return }
+
+      var self = this;
+      this.$nextTick(function () {
+        if (self._subscribing) {
+          self._i18n.unsubscribeDataChanging(self);
+          delete self._subscribing;
+        }
+
+        if (self._i18nWatcher) {
+          self._i18nWatcher();
+          self._i18n.destroyVM();
+          delete self._i18nWatcher;
+        }
+
+        if (self._localeWatcher) {
+          self._localeWatcher();
+          delete self._localeWatcher;
+        }
+      });
+    }
+  };
+
+  /*  */
+
+  var interpolationComponent = {
+    name: 'i18n',
+    functional: true,
+    props: {
+      tag: {
+        type: [String, Boolean, Object],
+        default: 'span'
+      },
+      path: {
+        type: String,
+        required: true
+      },
+      locale: {
+        type: String
+      },
+      places: {
+        type: [Array, Object]
+      }
+    },
+    render: function render (h, ref) {
+      var data = ref.data;
+      var parent = ref.parent;
+      var props = ref.props;
+      var slots = ref.slots;
+
+      var $i18n = parent.$i18n;
+      if (!$i18n) {
+        {
+          warn('Cannot find VueI18n instance!');
+        }
+        return
+      }
+
+      var path = props.path;
+      var locale = props.locale;
+      var places = props.places;
+      var params = slots();
+      var children = $i18n.i(
+        path,
+        locale,
+        onlyHasDefaultPlace(params) || places
+          ? useLegacyPlaces(params.default, places)
+          : params
+      );
+
+      var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+      return tag ? h(tag, data, children) : children
+    }
+  };
+
+  function onlyHasDefaultPlace (params) {
+    var prop;
+    for (prop in params) {
+      if (prop !== 'default') { return false }
+    }
+    return Boolean(prop)
+  }
+
+  function useLegacyPlaces (children, places) {
+    var params = places ? createParamsFromPlaces(places) : {};
+
+    if (!children) { return params }
+
+    // Filter empty text nodes
+    children = children.filter(function (child) {
+      return child.tag || child.text.trim() !== ''
+    });
+
+    var everyPlace = children.every(vnodeHasPlaceAttribute);
+    if (everyPlace) {
+      warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.');
+    }
+
+    return children.reduce(
+      everyPlace ? assignChildPlace : assignChildIndex,
+      params
+    )
+  }
+
+  function createParamsFromPlaces (places) {
+    {
+      warn('`places` prop is deprecated in next major version. Please switch to Vue slots.');
+    }
+
+    return Array.isArray(places)
+      ? places.reduce(assignChildIndex, {})
+      : Object.assign({}, places)
+  }
+
+  function assignChildPlace (params, child) {
+    if (child.data && child.data.attrs && child.data.attrs.place) {
+      params[child.data.attrs.place] = child;
+    }
+    return params
+  }
+
+  function assignChildIndex (params, child, index) {
+    params[index] = child;
+    return params
+  }
+
+  function vnodeHasPlaceAttribute (vnode) {
+    return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place)
+  }
+
+  /*  */
+
+  var numberComponent = {
+    name: 'i18n-n',
+    functional: true,
+    props: {
+      tag: {
+        type: [String, Boolean, Object],
+        default: 'span'
+      },
+      value: {
+        type: Number,
+        required: true
+      },
+      format: {
+        type: [String, Object]
+      },
+      locale: {
+        type: String
+      }
+    },
+    render: function render (h, ref) {
+      var props = ref.props;
+      var parent = ref.parent;
+      var data = ref.data;
+
+      var i18n = parent.$i18n;
+
+      if (!i18n) {
+        {
+          warn('Cannot find VueI18n instance!');
+        }
+        return null
+      }
+
+      var key = null;
+      var options = null;
+
+      if (isString(props.format)) {
+        key = props.format;
+      } else if (isObject(props.format)) {
+        if (props.format.key) {
+          key = props.format.key;
+        }
+
+        // Filter out number format options only
+        options = Object.keys(props.format).reduce(function (acc, prop) {
+          var obj;
+
+          if (includes(numberFormatKeys, prop)) {
+            return Object.assign({}, acc, ( obj = {}, obj[prop] = props.format[prop], obj ))
+          }
+          return acc
+        }, null);
+      }
+
+      var locale = props.locale || i18n.locale;
+      var parts = i18n._ntp(props.value, locale, key, options);
+
+      var values = parts.map(function (part, index) {
+        var obj;
+
+        var slot = data.scopedSlots && data.scopedSlots[part.type];
+        return slot ? slot(( obj = {}, obj[part.type] = part.value, obj.index = index, obj.parts = parts, obj )) : part.value
+      });
+
+      var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';
+      return tag
+        ? h(tag, {
+          attrs: data.attrs,
+          'class': data['class'],
+          staticClass: data.staticClass
+        }, values)
+        : values
+    }
+  };
+
+  /*  */
+
+  function bind (el, binding, vnode) {
+    if (!assert(el, vnode)) { return }
+
+    t(el, binding, vnode);
+  }
+
+  function update (el, binding, vnode, oldVNode) {
+    if (!assert(el, vnode)) { return }
+
+    var i18n = vnode.context.$i18n;
+    if (localeEqual(el, vnode) &&
+      (looseEqual(binding.value, binding.oldValue) &&
+       looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }
+
+    t(el, binding, vnode);
+  }
+
+  function unbind (el, binding, vnode, oldVNode) {
+    var vm = vnode.context;
+    if (!vm) {
+      warn('Vue instance does not exists in VNode context');
+      return
+    }
+
+    var i18n = vnode.context.$i18n || {};
+    if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {
+      el.textContent = '';
+    }
+    el._vt = undefined;
+    delete el['_vt'];
+    el._locale = undefined;
+    delete el['_locale'];
+    el._localeMessage = undefined;
+    delete el['_localeMessage'];
+  }
+
+  function assert (el, vnode) {
+    var vm = vnode.context;
+    if (!vm) {
+      warn('Vue instance does not exists in VNode context');
+      return false
+    }
+
+    if (!vm.$i18n) {
+      warn('VueI18n instance does not exists in Vue instance');
+      return false
+    }
+
+    return true
+  }
+
+  function localeEqual (el, vnode) {
+    var vm = vnode.context;
+    return el._locale === vm.$i18n.locale
+  }
+
+  function t (el, binding, vnode) {
+    var ref$1, ref$2;
+
+    var value = binding.value;
+
+    var ref = parseValue(value);
+    var path = ref.path;
+    var locale = ref.locale;
+    var args = ref.args;
+    var choice = ref.choice;
+    if (!path && !locale && !args) {
+      warn('value type not supported');
+      return
+    }
+
+    if (!path) {
+      warn('`path` is required in v-t directive');
+      return
+    }
+
+    var vm = vnode.context;
+    if (choice != null) {
+      el._vt = el.textContent = (ref$1 = vm.$i18n).tc.apply(ref$1, [ path, choice ].concat( makeParams(locale, args) ));
+    } else {
+      el._vt = el.textContent = (ref$2 = vm.$i18n).t.apply(ref$2, [ path ].concat( makeParams(locale, args) ));
+    }
+    el._locale = vm.$i18n.locale;
+    el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale);
+  }
+
+  function parseValue (value) {
+    var path;
+    var locale;
+    var args;
+    var choice;
+
+    if (isString(value)) {
+      path = value;
+    } else if (isPlainObject(value)) {
+      path = value.path;
+      locale = value.locale;
+      args = value.args;
+      choice = value.choice;
+    }
+
+    return { path: path, locale: locale, args: args, choice: choice }
+  }
+
+  function makeParams (locale, args) {
+    var params = [];
+
+    locale && params.push(locale);
+    if (args && (Array.isArray(args) || isPlainObject(args))) {
+      params.push(args);
+    }
+
+    return params
+  }
+
+  var Vue;
+
+  function install (_Vue) {
+    /* istanbul ignore if */
+    if (install.installed && _Vue === Vue) {
+      warn('already installed.');
+      return
+    }
+    install.installed = true;
+
+    Vue = _Vue;
+
+    var version = (Vue.version && Number(Vue.version.split('.')[0])) || -1;
+    /* istanbul ignore if */
+    if (version < 2) {
+      warn(("vue-i18n (" + (install.version) + ") need to use Vue 2.0 or later (Vue: " + (Vue.version) + ")."));
+      return
+    }
+
+    extend(Vue);
+    Vue.mixin(mixin);
+    Vue.directive('t', { bind: bind, update: update, unbind: unbind });
+    Vue.component(interpolationComponent.name, interpolationComponent);
+    Vue.component(numberComponent.name, numberComponent);
+
+    // use simple mergeStrategies to prevent i18n instance lose '__proto__'
+    var strats = Vue.config.optionMergeStrategies;
+    strats.i18n = function (parentVal, childVal) {
+      return childVal === undefined
+        ? parentVal
+        : childVal
+    };
+  }
+
+  /*  */
+
+  var BaseFormatter = function BaseFormatter () {
+    this._caches = Object.create(null);
+  };
+
+  BaseFormatter.prototype.interpolate = function interpolate (message, values) {
+    if (!values) {
+      return [message]
+    }
+    var tokens = this._caches[message];
+    if (!tokens) {
+      tokens = parse(message);
+      this._caches[message] = tokens;
+    }
+    return compile(tokens, values)
+  };
+
+
+
+  var RE_TOKEN_LIST_VALUE = /^(?:\d)+/;
+  var RE_TOKEN_NAMED_VALUE = /^(?:\w)+/;
+
+  function parse (format) {
+    var tokens = [];
+    var position = 0;
+
+    var text = '';
+    while (position < format.length) {
+      var char = format[position++];
+      if (char === '{') {
+        if (text) {
+          tokens.push({ type: 'text', value: text });
+        }
+
+        text = '';
+        var sub = '';
+        char = format[position++];
+        while (char !== undefined && char !== '}') {
+          sub += char;
+          char = format[position++];
+        }
+        var isClosed = char === '}';
+
+        var type = RE_TOKEN_LIST_VALUE.test(sub)
+          ? 'list'
+          : isClosed && RE_TOKEN_NAMED_VALUE.test(sub)
+            ? 'named'
+            : 'unknown';
+        tokens.push({ value: sub, type: type });
+      } else if (char === '%') {
+        // when found rails i18n syntax, skip text capture
+        if (format[(position)] !== '{') {
+          text += char;
+        }
+      } else {
+        text += char;
+      }
+    }
+
+    text && tokens.push({ type: 'text', value: text });
+
+    return tokens
+  }
+
+  function compile (tokens, values) {
+    var compiled = [];
+    var index = 0;
+
+    var mode = Array.isArray(values)
+      ? 'list'
+      : isObject(values)
+        ? 'named'
+        : 'unknown';
+    if (mode === 'unknown') { return compiled }
+
+    while (index < tokens.length) {
+      var token = tokens[index];
+      switch (token.type) {
+        case 'text':
+          compiled.push(token.value);
+          break
+        case 'list':
+          compiled.push(values[parseInt(token.value, 10)]);
+          break
+        case 'named':
+          if (mode === 'named') {
+            compiled.push((values)[token.value]);
+          } else {
+            {
+              warn(("Type of token '" + (token.type) + "' and format of value '" + mode + "' don't match!"));
+            }
+          }
+          break
+        case 'unknown':
+          {
+            warn("Detect 'unknown' type of token!");
+          }
+          break
+      }
+      index++;
+    }
+
+    return compiled
+  }
+
+  /*  */
+
+  /**
+   *  Path parser
+   *  - Inspired:
+   *    Vue.js Path parser
+   */
+
+  // actions
+  var APPEND = 0;
+  var PUSH = 1;
+  var INC_SUB_PATH_DEPTH = 2;
+  var PUSH_SUB_PATH = 3;
+
+  // states
+  var BEFORE_PATH = 0;
+  var IN_PATH = 1;
+  var BEFORE_IDENT = 2;
+  var IN_IDENT = 3;
+  var IN_SUB_PATH = 4;
+  var IN_SINGLE_QUOTE = 5;
+  var IN_DOUBLE_QUOTE = 6;
+  var AFTER_PATH = 7;
+  var ERROR = 8;
+
+  var pathStateMachine = [];
+
+  pathStateMachine[BEFORE_PATH] = {
+    'ws': [BEFORE_PATH],
+    'ident': [IN_IDENT, APPEND],
+    '[': [IN_SUB_PATH],
+    'eof': [AFTER_PATH]
+  };
+
+  pathStateMachine[IN_PATH] = {
+    'ws': [IN_PATH],
+    '.': [BEFORE_IDENT],
+    '[': [IN_SUB_PATH],
+    'eof': [AFTER_PATH]
+  };
+
+  pathStateMachine[BEFORE_IDENT] = {
+    'ws': [BEFORE_IDENT],
+    'ident': [IN_IDENT, APPEND],
+    '0': [IN_IDENT, APPEND],
+    'number': [IN_IDENT, APPEND]
+  };
+
+  pathStateMachine[IN_IDENT] = {
+    'ident': [IN_IDENT, APPEND],
+    '0': [IN_IDENT, APPEND],
+    'number': [IN_IDENT, APPEND],
+    'ws': [IN_PATH, PUSH],
+    '.': [BEFORE_IDENT, PUSH],
+    '[': [IN_SUB_PATH, PUSH],
+    'eof': [AFTER_PATH, PUSH]
+  };
+
+  pathStateMachine[IN_SUB_PATH] = {
+    "'": [IN_SINGLE_QUOTE, APPEND],
+    '"': [IN_DOUBLE_QUOTE, APPEND],
+    '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+    ']': [IN_PATH, PUSH_SUB_PATH],
+    'eof': ERROR,
+    'else': [IN_SUB_PATH, APPEND]
+  };
+
+  pathStateMachine[IN_SINGLE_QUOTE] = {
+    "'": [IN_SUB_PATH, APPEND],
+    'eof': ERROR,
+    'else': [IN_SINGLE_QUOTE, APPEND]
+  };
+
+  pathStateMachine[IN_DOUBLE_QUOTE] = {
+    '"': [IN_SUB_PATH, APPEND],
+    'eof': ERROR,
+    'else': [IN_DOUBLE_QUOTE, APPEND]
+  };
+
+  /**
+   * Check if an expression is a literal value.
+   */
+
+  var literalValueRE = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;
+  function isLiteral (exp) {
+    return literalValueRE.test(exp)
+  }
+
+  /**
+   * Strip quotes from a string
+   */
+
+  function stripQuotes (str) {
+    var a = str.charCodeAt(0);
+    var b = str.charCodeAt(str.length - 1);
+    return a === b && (a === 0x22 || a === 0x27)
+      ? str.slice(1, -1)
+      : str
+  }
+
+  /**
+   * Determine the type of a character in a keypath.
+   */
+
+  function getPathCharType (ch) {
+    if (ch === undefined || ch === null) { return 'eof' }
+
+    var code = ch.charCodeAt(0);
+
+    switch (code) {
+      case 0x5B: // [
+      case 0x5D: // ]
+      case 0x2E: // .
+      case 0x22: // "
+      case 0x27: // '
+        return ch
+
+      case 0x5F: // _
+      case 0x24: // $
+      case 0x2D: // -
+        return 'ident'
+
+      case 0x09: // Tab
+      case 0x0A: // Newline
+      case 0x0D: // Return
+      case 0xA0:  // No-break space
+      case 0xFEFF:  // Byte Order Mark
+      case 0x2028:  // Line Separator
+      case 0x2029:  // Paragraph Separator
+        return 'ws'
+    }
+
+    return 'ident'
+  }
+
+  /**
+   * Format a subPath, return its plain form if it is
+   * a literal string or number. Otherwise prepend the
+   * dynamic indicator (*).
+   */
+
+  function formatSubPath (path) {
+    var trimmed = path.trim();
+    // invalid leading 0
+    if (path.charAt(0) === '0' && isNaN(path)) { return false }
+
+    return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
+  }
+
+  /**
+   * Parse a string path into an array of segments
+   */
+
+  function parse$1 (path) {
+    var keys = [];
+    var index = -1;
+    var mode = BEFORE_PATH;
+    var subPathDepth = 0;
+    var c;
+    var key;
+    var newChar;
+    var type;
+    var transition;
+    var action;
+    var typeMap;
+    var actions = [];
+
+    actions[PUSH] = function () {
+      if (key !== undefined) {
+        keys.push(key);
+        key = undefined;
+      }
+    };
+
+    actions[APPEND] = function () {
+      if (key === undefined) {
+        key = newChar;
+      } else {
+        key += newChar;
+      }
+    };
+
+    actions[INC_SUB_PATH_DEPTH] = function () {
+      actions[APPEND]();
+      subPathDepth++;
+    };
+
+    actions[PUSH_SUB_PATH] = function () {
+      if (subPathDepth > 0) {
+        subPathDepth--;
+        mode = IN_SUB_PATH;
+        actions[APPEND]();
+      } else {
+        subPathDepth = 0;
+        if (key === undefined) { return false }
+        key = formatSubPath(key);
+        if (key === false) {
+          return false
+        } else {
+          actions[PUSH]();
+        }
+      }
+    };
+
+    function maybeUnescapeQuote () {
+      var nextChar = path[index + 1];
+      if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
+        (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
+        index++;
+        newChar = '\\' + nextChar;
+        actions[APPEND]();
+        return true
+      }
+    }
+
+    while (mode !== null) {
+      index++;
+      c = path[index];
+
+      if (c === '\\' && maybeUnescapeQuote()) {
+        continue
+      }
+
+      type = getPathCharType(c);
+      typeMap = pathStateMachine[mode];
+      transition = typeMap[type] || typeMap['else'] || ERROR;
+
+      if (transition === ERROR) {
+        return // parse error
+      }
+
+      mode = transition[0];
+      action = actions[transition[1]];
+      if (action) {
+        newChar = transition[2];
+        newChar = newChar === undefined
+          ? c
+          : newChar;
+        if (action() === false) {
+          return
+        }
+      }
+
+      if (mode === AFTER_PATH) {
+        return keys
+      }
+    }
+  }
+
+
+
+
+
+  var I18nPath = function I18nPath () {
+    this._cache = Object.create(null);
+  };
+
+  /**
+   * External parse that check for a cache hit first
+   */
+  I18nPath.prototype.parsePath = function parsePath (path) {
+    var hit = this._cache[path];
+    if (!hit) {
+      hit = parse$1(path);
+      if (hit) {
+        this._cache[path] = hit;
+      }
+    }
+    return hit || []
+  };
+
+  /**
+   * Get path value from path string
+   */
+  I18nPath.prototype.getPathValue = function getPathValue (obj, path) {
+    if (!isObject(obj)) { return null }
+
+    var paths = this.parsePath(path);
+    if (paths.length === 0) {
+      return null
+    } else {
+      var length = paths.length;
+      var last = obj;
+      var i = 0;
+      while (i < length) {
+        var value = last[paths[i]];
+        if (value === undefined) {
+          return null
+        }
+        last = value;
+        i++;
+      }
+
+      return last
+    }
+  };
+
+  /*  */
+
+
+
+  var htmlTagMatcher = /<\/?[\w\s="/.':;#-\/]+>/;
+  var linkKeyMatcher = /(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g;
+  var linkKeyPrefixMatcher = /^@(?:\.([a-z]+))?:/;
+  var bracketsMatcher = /[()]/g;
+  var defaultModifiers = {
+    'upper': function (str) { return str.toLocaleUpperCase(); },
+    'lower': function (str) { return str.toLocaleLowerCase(); },
+    'capitalize': function (str) { return ("" + (str.charAt(0).toLocaleUpperCase()) + (str.substr(1))); }
+  };
+
+  var defaultFormatter = new BaseFormatter();
+
+  var VueI18n = function VueI18n (options) {
+    var this$1 = this;
+    if ( options === void 0 ) options = {};
+
+    // Auto install if it is not done yet and `window` has `Vue`.
+    // To allow users to avoid auto-installation in some cases,
+    // this code should be placed here. See #290
+    /* istanbul ignore if */
+    if (!Vue && typeof window !== 'undefined' && window.Vue) {
+      install(window.Vue);
+    }
+
+    var locale = options.locale || 'en-US';
+    var fallbackLocale = options.fallbackLocale === false
+      ? false
+      : options.fallbackLocale || 'en-US';
+    var messages = options.messages || {};
+    var dateTimeFormats = options.dateTimeFormats || {};
+    var numberFormats = options.numberFormats || {};
+
+    this._vm = null;
+    this._formatter = options.formatter || defaultFormatter;
+    this._modifiers = options.modifiers || {};
+    this._missing = options.missing || null;
+    this._root = options.root || null;
+    this._sync = options.sync === undefined ? true : !!options.sync;
+    this._fallbackRoot = options.fallbackRoot === undefined
+      ? true
+      : !!options.fallbackRoot;
+    this._formatFallbackMessages = options.formatFallbackMessages === undefined
+      ? false
+      : !!options.formatFallbackMessages;
+    this._silentTranslationWarn = options.silentTranslationWarn === undefined
+      ? false
+      : options.silentTranslationWarn;
+    this._silentFallbackWarn = options.silentFallbackWarn === undefined
+      ? false
+      : !!options.silentFallbackWarn;
+    this._dateTimeFormatters = {};
+    this._numberFormatters = {};
+    this._path = new I18nPath();
+    this._dataListeners = [];
+    this._componentInstanceCreatedListener = options.componentInstanceCreatedListener || null;
+    this._preserveDirectiveContent = options.preserveDirectiveContent === undefined
+      ? false
+      : !!options.preserveDirectiveContent;
+    this.pluralizationRules = options.pluralizationRules || {};
+    this._warnHtmlInMessage = options.warnHtmlInMessage || 'off';
+    this._postTranslation = options.postTranslation || null;
+
+    /**
+     * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
+     * @param choicesLength {number} an overall amount of available choices
+     * @returns a final choice index
+    */
+    this.getChoiceIndex = function (choice, choicesLength) {
+      var thisPrototype = Object.getPrototypeOf(this$1);
+      if (thisPrototype && thisPrototype.getChoiceIndex) {
+        var prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex);
+        return (prototypeGetChoiceIndex).call(this$1, choice, choicesLength)
+      }
+
+      // Default (old) getChoiceIndex implementation - english-compatible
+      var defaultImpl = function (_choice, _choicesLength) {
+        _choice = Math.abs(_choice);
+
+        if (_choicesLength === 2) {
+          return _choice
+            ? _choice > 1
+              ? 1
+              : 0
+            : 1
+        }
+
+        return _choice ? Math.min(_choice, 2) : 0
+      };
+
+      if (this$1.locale in this$1.pluralizationRules) {
+        return this$1.pluralizationRules[this$1.locale].apply(this$1, [choice, choicesLength])
+      } else {
+        return defaultImpl(choice, choicesLength)
+      }
+    };
+
+
+    this._exist = function (message, key) {
+      if (!message || !key) { return false }
+      if (!isNull(this$1._path.getPathValue(message, key))) { return true }
+      // fallback for flat key
+      if (message[key]) { return true }
+      return false
+    };
+
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      Object.keys(messages).forEach(function (locale) {
+        this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);
+      });
+    }
+
+    this._initVM({
+      locale: locale,
+      fallbackLocale: fallbackLocale,
+      messages: messages,
+      dateTimeFormats: dateTimeFormats,
+      numberFormats: numberFormats
+    });
+  };
+
+  var prototypeAccessors = { vm: { configurable: true },messages: { configurable: true },dateTimeFormats: { configurable: true },numberFormats: { configurable: true },availableLocales: { configurable: true },locale: { configurable: true },fallbackLocale: { configurable: true },formatFallbackMessages: { configurable: true },missing: { configurable: true },formatter: { configurable: true },silentTranslationWarn: { configurable: true },silentFallbackWarn: { configurable: true },preserveDirectiveContent: { configurable: true },warnHtmlInMessage: { configurable: true },postTranslation: { configurable: true } };
+
+  VueI18n.prototype._checkLocaleMessage = function _checkLocaleMessage (locale, level, message) {
+    var paths = [];
+
+    var fn = function (level, locale, message, paths) {
+      if (isPlainObject(message)) {
+        Object.keys(message).forEach(function (key) {
+          var val = message[key];
+          if (isPlainObject(val)) {
+            paths.push(key);
+            paths.push('.');
+            fn(level, locale, val, paths);
+            paths.pop();
+            paths.pop();
+          } else {
+            paths.push(key);
+            fn(level, locale, val, paths);
+            paths.pop();
+          }
+        });
+      } else if (isArray(message)) {
+        message.forEach(function (item, index) {
+          if (isPlainObject(item)) {
+            paths.push(("[" + index + "]"));
+            paths.push('.');
+            fn(level, locale, item, paths);
+            paths.pop();
+            paths.pop();
+          } else {
+            paths.push(("[" + index + "]"));
+            fn(level, locale, item, paths);
+            paths.pop();
+          }
+        });
+      } else if (isString(message)) {
+        var ret = htmlTagMatcher.test(message);
+        if (ret) {
+          var msg = "Detected HTML in message '" + message + "' of keypath '" + (paths.join('')) + "' at '" + locale + "'. Consider component interpolation with '<i18n>' to avoid XSS. See https://bit.ly/2ZqJzkp";
+          if (level === 'warn') {
+            warn(msg);
+          } else if (level === 'error') {
+            error(msg);
+          }
+        }
+      }
+    };
+
+    fn(level, locale, message, paths);
+  };
+
+  VueI18n.prototype._initVM = function _initVM (data) {
+    var silent = Vue.config.silent;
+    Vue.config.silent = true;
+    this._vm = new Vue({ data: data });
+    Vue.config.silent = silent;
+  };
+
+  VueI18n.prototype.destroyVM = function destroyVM () {
+    this._vm.$destroy();
+  };
+
+  VueI18n.prototype.subscribeDataChanging = function subscribeDataChanging (vm) {
+    this._dataListeners.push(vm);
+  };
+
+  VueI18n.prototype.unsubscribeDataChanging = function unsubscribeDataChanging (vm) {
+    remove(this._dataListeners, vm);
+  };
+
+  VueI18n.prototype.watchI18nData = function watchI18nData () {
+    var self = this;
+    return this._vm.$watch('$data', function () {
+      var i = self._dataListeners.length;
+      while (i--) {
+        Vue.nextTick(function () {
+          self._dataListeners[i] && self._dataListeners[i].$forceUpdate();
+        });
+      }
+    }, { deep: true })
+  };
+
+  VueI18n.prototype.watchLocale = function watchLocale () {
+    /* istanbul ignore if */
+    if (!this._sync || !this._root) { return null }
+    var target = this._vm;
+    return this._root.$i18n.vm.$watch('locale', function (val) {
+      target.$set(target, 'locale', val);
+      target.$forceUpdate();
+    }, { immediate: true })
+  };
+
+  VueI18n.prototype.onComponentInstanceCreated = function onComponentInstanceCreated (newI18n) {
+    if (this._componentInstanceCreatedListener) {
+      this._componentInstanceCreatedListener(newI18n, this);
+    }
+  };
+
+  prototypeAccessors.vm.get = function () { return this._vm };
+
+  prototypeAccessors.messages.get = function () { return looseClone(this._getMessages()) };
+  prototypeAccessors.dateTimeFormats.get = function () { return looseClone(this._getDateTimeFormats()) };
+  prototypeAccessors.numberFormats.get = function () { return looseClone(this._getNumberFormats()) };
+  prototypeAccessors.availableLocales.get = function () { return Object.keys(this.messages).sort() };
+
+  prototypeAccessors.locale.get = function () { return this._vm.locale };
+  prototypeAccessors.locale.set = function (locale) {
+    this._vm.$set(this._vm, 'locale', locale);
+  };
+
+  prototypeAccessors.fallbackLocale.get = function () { return this._vm.fallbackLocale };
+  prototypeAccessors.fallbackLocale.set = function (locale) {
+    this._localeChainCache = {};
+    this._vm.$set(this._vm, 'fallbackLocale', locale);
+  };
+
+  prototypeAccessors.formatFallbackMessages.get = function () { return this._formatFallbackMessages };
+  prototypeAccessors.formatFallbackMessages.set = function (fallback) { this._formatFallbackMessages = fallback; };
+
+  prototypeAccessors.missing.get = function () { return this._missing };
+  prototypeAccessors.missing.set = function (handler) { this._missing = handler; };
+
+  prototypeAccessors.formatter.get = function () { return this._formatter };
+  prototypeAccessors.formatter.set = function (formatter) { this._formatter = formatter; };
+
+  prototypeAccessors.silentTranslationWarn.get = function () { return this._silentTranslationWarn };
+  prototypeAccessors.silentTranslationWarn.set = function (silent) { this._silentTranslationWarn = silent; };
+
+  prototypeAccessors.silentFallbackWarn.get = function () { return this._silentFallbackWarn };
+  prototypeAccessors.silentFallbackWarn.set = function (silent) { this._silentFallbackWarn = silent; };
+
+  prototypeAccessors.preserveDirectiveContent.get = function () { return this._preserveDirectiveContent };
+  prototypeAccessors.preserveDirectiveContent.set = function (preserve) { this._preserveDirectiveContent = preserve; };
+
+  prototypeAccessors.warnHtmlInMessage.get = function () { return this._warnHtmlInMessage };
+  prototypeAccessors.warnHtmlInMessage.set = function (level) {
+      var this$1 = this;
+
+    var orgLevel = this._warnHtmlInMessage;
+    this._warnHtmlInMessage = level;
+    if (orgLevel !== level && (level === 'warn' || level === 'error')) {
+      var messages = this._getMessages();
+      Object.keys(messages).forEach(function (locale) {
+        this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);
+      });
+    }
+  };
+
+  prototypeAccessors.postTranslation.get = function () { return this._postTranslation };
+  prototypeAccessors.postTranslation.set = function (handler) { this._postTranslation = handler; };
+
+  VueI18n.prototype._getMessages = function _getMessages () { return this._vm.messages };
+  VueI18n.prototype._getDateTimeFormats = function _getDateTimeFormats () { return this._vm.dateTimeFormats };
+  VueI18n.prototype._getNumberFormats = function _getNumberFormats () { return this._vm.numberFormats };
+
+  VueI18n.prototype._warnDefault = function _warnDefault (locale, key, result, vm, values, interpolateMode) {
+    if (!isNull(result)) { return result }
+    if (this._missing) {
+      var missingRet = this._missing.apply(null, [locale, key, vm, values]);
+      if (isString(missingRet)) {
+        return missingRet
+      }
+    } else {
+      if (!this._isSilentTranslationWarn(key)) {
+        warn(
+          "Cannot translate the value of keypath '" + key + "'. " +
+          'Use the value of keypath as default.'
+        );
+      }
+    }
+
+    if (this._formatFallbackMessages) {
+      var parsedArgs = parseArgs.apply(void 0, values);
+      return this._render(key, interpolateMode, parsedArgs.params, key)
+    } else {
+      return key
+    }
+  };
+
+  VueI18n.prototype._isFallbackRoot = function _isFallbackRoot (val) {
+    return !val && !isNull(this._root) && this._fallbackRoot
+  };
+
+  VueI18n.prototype._isSilentFallbackWarn = function _isSilentFallbackWarn (key) {
+    return this._silentFallbackWarn instanceof RegExp
+      ? this._silentFallbackWarn.test(key)
+      : this._silentFallbackWarn
+  };
+
+  VueI18n.prototype._isSilentFallback = function _isSilentFallback (locale, key) {
+    return this._isSilentFallbackWarn(key) && (this._isFallbackRoot() || locale !== this.fallbackLocale)
+  };
+
+  VueI18n.prototype._isSilentTranslationWarn = function _isSilentTranslationWarn (key) {
+    return this._silentTranslationWarn instanceof RegExp
+      ? this._silentTranslationWarn.test(key)
+      : this._silentTranslationWarn
+  };
+
+  VueI18n.prototype._interpolate = function _interpolate (
+    locale,
+    message,
+    key,
+    host,
+    interpolateMode,
+    values,
+    visitedLinkStack
+  ) {
+    if (!message) { return null }
+
+    var pathRet = this._path.getPathValue(message, key);
+    if (isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
+
+    var ret;
+    if (isNull(pathRet)) {
+      /* istanbul ignore else */
+      if (isPlainObject(message)) {
+        ret = message[key];
+        if (!(isString(ret) || isFunction(ret))) {
+          if (!this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+            warn(("Value of key '" + key + "' is not a string or function !"));
+          }
+          return null
+        }
+      } else {
+        return null
+      }
+    } else {
+      /* istanbul ignore else */
+      if (isString(pathRet) || isFunction(pathRet)) {
+        ret = pathRet;
+      } else {
+        if (!this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+          warn(("Value of key '" + key + "' is not a string or function!"));
+        }
+        return null
+      }
+    }
+
+    // Check for the existence of links within the translated string
+    if (isString(ret) && (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0)) {
+      ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack);
+    }
+
+    return this._render(ret, interpolateMode, values, key)
+  };
+
+  VueI18n.prototype._link = function _link (
+    locale,
+    message,
+    str,
+    host,
+    interpolateMode,
+    values,
+    visitedLinkStack
+  ) {
+    var ret = str;
+
+    // Match all the links within the local
+    // We are going to replace each of
+    // them with its translation
+    var matches = ret.match(linkKeyMatcher);
+    for (var idx in matches) {
+      // ie compatible: filter custom array
+      // prototype method
+      if (!matches.hasOwnProperty(idx)) {
+        continue
+      }
+      var link = matches[idx];
+      var linkKeyPrefixMatches = link.match(linkKeyPrefixMatcher);
+      var linkPrefix = linkKeyPrefixMatches[0];
+        var formatterName = linkKeyPrefixMatches[1];
+
+      // Remove the leading @:, @.case: and the brackets
+      var linkPlaceholder = link.replace(linkPrefix, '').replace(bracketsMatcher, '');
+
+      if (includes(visitedLinkStack, linkPlaceholder)) {
+        {
+          warn(("Circular reference found. \"" + link + "\" is already visited in the chain of " + (visitedLinkStack.reverse().join(' <- '))));
+        }
+        return ret
+      }
+      visitedLinkStack.push(linkPlaceholder);
+
+      // Translate the link
+      var translated = this._interpolate(
+        locale, message, linkPlaceholder, host,
+        interpolateMode === 'raw' ? 'string' : interpolateMode,
+        interpolateMode === 'raw' ? undefined : values,
+        visitedLinkStack
+      );
+
+      if (this._isFallbackRoot(translated)) {
+        if (!this._isSilentTranslationWarn(linkPlaceholder)) {
+          warn(("Fall back to translate the link placeholder '" + linkPlaceholder + "' with root locale."));
+        }
+        /* istanbul ignore if */
+        if (!this._root) { throw Error('unexpected error') }
+        var root = this._root.$i18n;
+        translated = root._translate(
+          root._getMessages(), root.locale, root.fallbackLocale,
+          linkPlaceholder, host, interpolateMode, values
+        );
+      }
+      translated = this._warnDefault(
+        locale, linkPlaceholder, translated, host,
+        isArray(values) ? values : [values],
+        interpolateMode
+      );
+
+      if (this._modifiers.hasOwnProperty(formatterName)) {
+        translated = this._modifiers[formatterName](translated);
+      } else if (defaultModifiers.hasOwnProperty(formatterName)) {
+        translated = defaultModifiers[formatterName](translated);
+      }
+
+      visitedLinkStack.pop();
+
+      // Replace the link with the translated
+      ret = !translated ? ret : ret.replace(link, translated);
+    }
+
+    return ret
+  };
+
+  VueI18n.prototype._createMessageContext = function _createMessageContext (values) {
+    var _list = isArray(values) ? values : [];
+    var _named = isObject(values) ? values : {};
+    var list = function (index) { return _list[index]; };
+    var named = function (key) { return _named[key]; };
+    return {
+      list: list,
+      named: named
+    }
+  };
+
+  VueI18n.prototype._render = function _render (message, interpolateMode, values, path) {
+    if (isFunction(message)) {
+      return message(this._createMessageContext(values))
+    }
+
+    var ret = this._formatter.interpolate(message, values, path);
+
+    // If the custom formatter refuses to work - apply the default one
+    if (!ret) {
+      ret = defaultFormatter.interpolate(message, values, path);
+    }
+
+    // if interpolateMode is **not** 'string' ('row'),
+    // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
+    return interpolateMode === 'string' && !isString(ret) ? ret.join('') : ret
+  };
+
+  VueI18n.prototype._appendItemToChain = function _appendItemToChain (chain, item, blocks) {
+    var follow = false;
+    if (!includes(chain, item)) {
+      follow = true;
+      if (item) {
+        follow = item[item.length - 1] !== '!';
+        item = item.replace(/!/g, '');
+        chain.push(item);
+        if (blocks && blocks[item]) {
+          follow = blocks[item];
+        }
+      }
+    }
+    return follow
+  };
+
+  VueI18n.prototype._appendLocaleToChain = function _appendLocaleToChain (chain, locale, blocks) {
+    var follow;
+    var tokens = locale.split('-');
+    do {
+      var item = tokens.join('-');
+      follow = this._appendItemToChain(chain, item, blocks);
+      tokens.splice(-1, 1);
+    } while (tokens.length && (follow === true))
+    return follow
+  };
+
+  VueI18n.prototype._appendBlockToChain = function _appendBlockToChain (chain, block, blocks) {
+    var follow = true;
+    for (var i = 0; (i < block.length) && (isBoolean(follow)); i++) {
+      var locale = block[i];
+      if (isString(locale)) {
+        follow = this._appendLocaleToChain(chain, locale, blocks);
+      }
+    }
+    return follow
+  };
+
+  VueI18n.prototype._getLocaleChain = function _getLocaleChain (start, fallbackLocale) {
+    if (start === '') { return [] }
+
+    if (!this._localeChainCache) {
+      this._localeChainCache = {};
+    }
+
+    var chain = this._localeChainCache[start];
+    if (!chain) {
+      if (!fallbackLocale) {
+        fallbackLocale = this.fallbackLocale;
+      }
+      chain = [];
+
+      // first block defined by start
+      var block = [start];
+
+      // while any intervening block found
+      while (isArray(block)) {
+        block = this._appendBlockToChain(
+          chain,
+          block,
+          fallbackLocale
+        );
+      }
+
+      // last block defined by default
+      var defaults;
+      if (isArray(fallbackLocale)) {
+        defaults = fallbackLocale;
+      } else if (isObject(fallbackLocale)) {
+        /* $FlowFixMe */
+        if (fallbackLocale['default']) {
+          defaults = fallbackLocale['default'];
+        } else {
+          defaults = null;
+        }
+      } else {
+        defaults = fallbackLocale;
+      }
+
+      // convert defaults to array
+      if (isString(defaults)) {
+        block = [defaults];
+      } else {
+        block = defaults;
+      }
+      if (block) {
+        this._appendBlockToChain(
+          chain,
+          block,
+          null
+        );
+      }
+      this._localeChainCache[start] = chain;
+    }
+    return chain
+  };
+
+  VueI18n.prototype._translate = function _translate (
+    messages,
+    locale,
+    fallback,
+    key,
+    host,
+    interpolateMode,
+    args
+  ) {
+    var chain = this._getLocaleChain(locale, fallback);
+    var res;
+    for (var i = 0; i < chain.length; i++) {
+      var step = chain[i];
+      res =
+        this._interpolate(step, messages[step], key, host, interpolateMode, args, [key]);
+      if (!isNull(res)) {
+        if (step !== locale && "development" !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(("Fall back to translate the keypath '" + key + "' with '" + step + "' locale."));
+        }
+        return res
+      }
+    }
+    return null
+  };
+
+  VueI18n.prototype._t = function _t (key, _locale, messages, host) {
+      var ref;
+
+      var values = [], len = arguments.length - 4;
+      while ( len-- > 0 ) values[ len ] = arguments[ len + 4 ];
+    if (!key) { return '' }
+
+    var parsedArgs = parseArgs.apply(void 0, values);
+    var locale = parsedArgs.locale || _locale;
+
+    var ret = this._translate(
+      messages, locale, this.fallbackLocale, key,
+      host, 'string', parsedArgs.params
+    );
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to translate the keypath '" + key + "' with root locale."));
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return (ref = this._root).$t.apply(ref, [ key ].concat( values ))
+    } else {
+      ret = this._warnDefault(locale, key, ret, host, values, 'string');
+      if (this._postTranslation && ret !== null && ret !== undefined) {
+        ret = this._postTranslation(ret, key);
+      }
+      return ret
+    }
+  };
+
+  VueI18n.prototype.t = function t (key) {
+      var ref;
+
+      var values = [], len = arguments.length - 1;
+      while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
+    return (ref = this)._t.apply(ref, [ key, this.locale, this._getMessages(), null ].concat( values ))
+  };
+
+  VueI18n.prototype._i = function _i (key, locale, messages, host, values) {
+    var ret =
+      this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key)) {
+        warn(("Fall back to interpolate the keypath '" + key + "' with root locale."));
+      }
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.i(key, locale, values)
+    } else {
+      return this._warnDefault(locale, key, ret, host, [values], 'raw')
+    }
+  };
+
+  VueI18n.prototype.i = function i (key, locale, values) {
+    /* istanbul ignore if */
+    if (!key) { return '' }
+
+    if (!isString(locale)) {
+      locale = this.locale;
+    }
+
+    return this._i(key, locale, this._getMessages(), null, values)
+  };
+
+  VueI18n.prototype._tc = function _tc (
+    key,
+    _locale,
+    messages,
+    host,
+    choice
+  ) {
+      var ref;
+
+      var values = [], len = arguments.length - 5;
+      while ( len-- > 0 ) values[ len ] = arguments[ len + 5 ];
+    if (!key) { return '' }
+    if (choice === undefined) {
+      choice = 1;
+    }
+
+    var predefined = { 'count': choice, 'n': choice };
+    var parsedArgs = parseArgs.apply(void 0, values);
+    parsedArgs.params = Object.assign(predefined, parsedArgs.params);
+    values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params];
+    return this.fetchChoice((ref = this)._t.apply(ref, [ key, _locale, messages, host ].concat( values )), choice)
+  };
+
+  VueI18n.prototype.fetchChoice = function fetchChoice (message, choice) {
+    /* istanbul ignore if */
+    if (!message && !isString(message)) { return null }
+    var choices = message.split('|');
+
+    choice = this.getChoiceIndex(choice, choices.length);
+    if (!choices[choice]) { return message }
+    return choices[choice].trim()
+  };
+
+  VueI18n.prototype.tc = function tc (key, choice) {
+      var ref;
+
+      var values = [], len = arguments.length - 2;
+      while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
+    return (ref = this)._tc.apply(ref, [ key, this.locale, this._getMessages(), null, choice ].concat( values ))
+  };
+
+  VueI18n.prototype._te = function _te (key, locale, messages) {
+      var args = [], len = arguments.length - 3;
+      while ( len-- > 0 ) args[ len ] = arguments[ len + 3 ];
+
+    var _locale = parseArgs.apply(void 0, args).locale || locale;
+    return this._exist(messages[_locale], key)
+  };
+
+  VueI18n.prototype.te = function te (key, locale) {
+    return this._te(key, this.locale, this._getMessages(), locale)
+  };
+
+  VueI18n.prototype.getLocaleMessage = function getLocaleMessage (locale) {
+    return looseClone(this._vm.messages[locale] || {})
+  };
+
+  VueI18n.prototype.setLocaleMessage = function setLocaleMessage (locale, message) {
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+    }
+    this._vm.$set(this._vm.messages, locale, message);
+  };
+
+  VueI18n.prototype.mergeLocaleMessage = function mergeLocaleMessage (locale, message) {
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);
+    }
+    this._vm.$set(this._vm.messages, locale, merge({}, this._vm.messages[locale] || {}, message));
+  };
+
+  VueI18n.prototype.getDateTimeFormat = function getDateTimeFormat (locale) {
+    return looseClone(this._vm.dateTimeFormats[locale] || {})
+  };
+
+  VueI18n.prototype.setDateTimeFormat = function setDateTimeFormat (locale, format) {
+    this._vm.$set(this._vm.dateTimeFormats, locale, format);
+    this._clearDateTimeFormat(locale, format);
+  };
+
+  VueI18n.prototype.mergeDateTimeFormat = function mergeDateTimeFormat (locale, format) {
+    this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format));
+    this._clearDateTimeFormat(locale, format);
+  };
+
+  VueI18n.prototype._clearDateTimeFormat = function _clearDateTimeFormat (locale, format) {
+    for (var key in format) {
+      var id = locale + "__" + key;
+
+      if (!this._dateTimeFormatters.hasOwnProperty(id)) {
+        continue
+      }
+
+      delete this._dateTimeFormatters[id];
+    }
+  };
+
+  VueI18n.prototype._localizeDateTime = function _localizeDateTime (
+    value,
+    locale,
+    fallback,
+    dateTimeFormats,
+    key
+  ) {
+    var _locale = locale;
+    var formats = dateTimeFormats[_locale];
+
+    var chain = this._getLocaleChain(locale, fallback);
+    for (var i = 0; i < chain.length; i++) {
+      var current = _locale;
+      var step = chain[i];
+      formats = dateTimeFormats[step];
+      _locale = step;
+      // fallback locale
+      if (isNull(formats) || isNull(formats[key])) {
+        if (step !== locale && "development" !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(("Fall back to '" + step + "' datetime formats from '" + current + "' datetime formats."));
+        }
+      } else {
+        break
+      }
+    }
+
+    if (isNull(formats) || isNull(formats[key])) {
+      return null
+    } else {
+      var format = formats[key];
+      var id = _locale + "__" + key;
+      var formatter = this._dateTimeFormatters[id];
+      if (!formatter) {
+        formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format);
+      }
+      return formatter.format(value)
+    }
+  };
+
+  VueI18n.prototype._d = function _d (value, locale, key) {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.dateTimeFormat) {
+      warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.');
+      return ''
+    }
+
+    if (!key) {
+      return new Intl.DateTimeFormat(locale).format(value)
+    }
+
+    var ret =
+      this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to datetime localization of root: key '" + key + "'."));
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.d(value, key, locale)
+    } else {
+      return ret || ''
+    }
+  };
+
+  VueI18n.prototype.d = function d (value) {
+      var args = [], len = arguments.length - 1;
+      while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+
+    var locale = this.locale;
+    var key = null;
+
+    if (args.length === 1) {
+      if (isString(args[0])) {
+        key = args[0];
+      } else if (isObject(args[0])) {
+        if (args[0].locale) {
+          locale = args[0].locale;
+        }
+        if (args[0].key) {
+          key = args[0].key;
+        }
+      }
+    } else if (args.length === 2) {
+      if (isString(args[0])) {
+        key = args[0];
+      }
+      if (isString(args[1])) {
+        locale = args[1];
+      }
+    }
+
+    return this._d(value, locale, key)
+  };
+
+  VueI18n.prototype.getNumberFormat = function getNumberFormat (locale) {
+    return looseClone(this._vm.numberFormats[locale] || {})
+  };
+
+  VueI18n.prototype.setNumberFormat = function setNumberFormat (locale, format) {
+    this._vm.$set(this._vm.numberFormats, locale, format);
+    this._clearNumberFormat(locale, format);
+  };
+
+  VueI18n.prototype.mergeNumberFormat = function mergeNumberFormat (locale, format) {
+    this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format));
+    this._clearNumberFormat(locale, format);
+  };
+
+  VueI18n.prototype._clearNumberFormat = function _clearNumberFormat (locale, format) {
+    for (var key in format) {
+      var id = locale + "__" + key;
+
+      if (!this._numberFormatters.hasOwnProperty(id)) {
+        continue
+      }
+
+      delete this._numberFormatters[id];
+    }
+  };
+
+  VueI18n.prototype._getNumberFormatter = function _getNumberFormatter (
+    value,
+    locale,
+    fallback,
+    numberFormats,
+    key,
+    options
+  ) {
+    var _locale = locale;
+    var formats = numberFormats[_locale];
+
+    var chain = this._getLocaleChain(locale, fallback);
+    for (var i = 0; i < chain.length; i++) {
+      var current = _locale;
+      var step = chain[i];
+      formats = numberFormats[step];
+      _locale = step;
+      // fallback locale
+      if (isNull(formats) || isNull(formats[key])) {
+        if (step !== locale && "development" !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(("Fall back to '" + step + "' number formats from '" + current + "' number formats."));
+        }
+      } else {
+        break
+      }
+    }
+
+    if (isNull(formats) || isNull(formats[key])) {
+      return null
+    } else {
+      var format = formats[key];
+
+      var formatter;
+      if (options) {
+        // If options specified - create one time number formatter
+        formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options));
+      } else {
+        var id = _locale + "__" + key;
+        formatter = this._numberFormatters[id];
+        if (!formatter) {
+          formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format);
+        }
+      }
+      return formatter
+    }
+  };
+
+  VueI18n.prototype._n = function _n (value, locale, key, options) {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.numberFormat) {
+      {
+        warn('Cannot format a Number value due to not supported Intl.NumberFormat.');
+      }
+      return ''
+    }
+
+    if (!key) {
+      var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+      return nf.format(value)
+    }
+
+    var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+    var ret = formatter && formatter.format(value);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(("Fall back to number localization of root: key '" + key + "'."));
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.n(value, Object.assign({}, { key: key, locale: locale }, options))
+    } else {
+      return ret || ''
+    }
+  };
+
+  VueI18n.prototype.n = function n (value) {
+      var args = [], len = arguments.length - 1;
+      while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
+
+    var locale = this.locale;
+    var key = null;
+    var options = null;
+
+    if (args.length === 1) {
+      if (isString(args[0])) {
+        key = args[0];
+      } else if (isObject(args[0])) {
+        if (args[0].locale) {
+          locale = args[0].locale;
+        }
+        if (args[0].key) {
+          key = args[0].key;
+        }
+
+        // Filter out number format options only
+        options = Object.keys(args[0]).reduce(function (acc, key) {
+            var obj;
+
+          if (includes(numberFormatKeys, key)) {
+            return Object.assign({}, acc, ( obj = {}, obj[key] = args[0][key], obj ))
+          }
+          return acc
+        }, null);
+      }
+    } else if (args.length === 2) {
+      if (isString(args[0])) {
+        key = args[0];
+      }
+      if (isString(args[1])) {
+        locale = args[1];
+      }
+    }
+
+    return this._n(value, locale, key, options)
+  };
+
+  VueI18n.prototype._ntp = function _ntp (value, locale, key, options) {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.numberFormat) {
+      {
+        warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.');
+      }
+      return []
+    }
+
+    if (!key) {
+      var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
+      return nf.formatToParts(value)
+    }
+
+    var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
+    var ret = formatter && formatter.formatToParts(value);
+    if (this._isFallbackRoot(ret)) {
+      if (!this._isSilentTranslationWarn(key)) {
+        warn(("Fall back to format number to parts of root: key '" + key + "' ."));
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n._ntp(value, locale, key, options)
+    } else {
+      return ret || []
+    }
+  };
+
+  Object.defineProperties( VueI18n.prototype, prototypeAccessors );
+
+  var availabilities;
+  // $FlowFixMe
+  Object.defineProperty(VueI18n, 'availabilities', {
+    get: function get () {
+      if (!availabilities) {
+        var intlDefined = typeof Intl !== 'undefined';
+        availabilities = {
+          dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
+          numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
+        };
+      }
+
+      return availabilities
+    }
+  });
+
+  VueI18n.install = install;
+  VueI18n.version = '8.21.0';
+
+  return VueI18n;
+
+})));

File diff suppressed because it is too large
+ 5 - 0
plugin/vue-i18n/dist/vue-i18n.min.js


+ 176 - 0
plugin/vue-i18n/package.json

@@ -0,0 +1,176 @@
+{
+  "_args": [
+    [
+      "vue-i18n@8.21.0",
+      "D:\\2023\\4\\wwbsr"
+    ]
+  ],
+  "_from": "vue-i18n@8.21.0",
+  "_id": "vue-i18n@8.21.0",
+  "_inBundle": false,
+  "_integrity": "sha1-UmRQUl/buch3aFtbpsuVc7c9OUA=",
+  "_location": "/vue-i18n",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "version",
+    "registry": true,
+    "raw": "vue-i18n@8.21.0",
+    "name": "vue-i18n",
+    "escapedName": "vue-i18n",
+    "rawSpec": "8.21.0",
+    "saveSpec": null,
+    "fetchSpec": "8.21.0"
+  },
+  "_requiredBy": [
+    "/"
+  ],
+  "_resolved": "https://registry.npm.taobao.org/vue-i18n/download/vue-i18n-8.21.0.tgz?cache=0&sync_timestamp=1597336528269&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-i18n%2Fdownload%2Fvue-i18n-8.21.0.tgz",
+  "_spec": "8.21.0",
+  "_where": "D:\\2023\\4\\wwbsr",
+  "author": {
+    "name": "kazuya kawaguchi",
+    "email": "kawakazu80@gmail.com"
+  },
+  "bugs": {
+    "url": "https://github.com/kazupon/vue-i18n/issues"
+  },
+  "changelog": {
+    "labels": {
+      "Type: Feature": ":star: New Features",
+      "Type: Bug": ":bug: Bug Fixes",
+      "Type: Security": ":lock: Security Fixes",
+      "Type: Performance": ":chart_with_upwards_trend: Performance Fixes",
+      "Type: Improvement": ":zap: Improved Features",
+      "Type: Breaking": ":boom: Breaking Change",
+      "Type: Deprecated": ":warning: Deprecated Features",
+      "Type: I18n": ":globe_with_meridians: Internationalization",
+      "Type: A11y": ":wheelchair: Accessibility",
+      "Type: Documentation": ":pencil: Documentation"
+    }
+  },
+  "description": "Internationalization plugin for Vue.js",
+  "devDependencies": {
+    "@babel/core": "^7.1.0",
+    "@babel/plugin-proposal-class-properties": "^7.1.0",
+    "@babel/plugin-syntax-flow": "^7.0.0",
+    "@babel/plugin-transform-flow-strip-types": "^7.0.0",
+    "@typescript-eslint/eslint-plugin": "^3.0.0",
+    "@typescript-eslint/parser": "^3.0.0",
+    "@vue/babel-preset-app": "^4.4.1",
+    "babel-eslint": "^10.1.0",
+    "babel-loader": "^8.1.0",
+    "babel-plugin-istanbul": "^6.0.0",
+    "babel-preset-power-assert": "^3.0.0",
+    "buble": "^0.19.3",
+    "chromedriver": "^83.0.0",
+    "core-js": "^3.6.5",
+    "cross-env": "^7.0.2",
+    "cross-spawn": "^7.0.3",
+    "eslint": "^6.8.0",
+    "eslint-loader": "^4.0.2",
+    "eslint-plugin-flowtype": "^4.7.0",
+    "eslint-plugin-ie11": "^1.0.0",
+    "eslint-plugin-no-autofix": "^1.0.1",
+    "eslint-plugin-vue": "^6.2.2",
+    "eslint-plugin-vue-libs": "^4.0.0",
+    "flow-bin": "^0.38.0",
+    "http-server": "^0.12.3",
+    "intl": "^1.2.5",
+    "karma": "^5.0.9",
+    "karma-chrome-launcher": "^3.1.0",
+    "karma-coverage": "^2.0.2",
+    "karma-firefox-launcher": "^1.1.0",
+    "karma-mocha": "^2.0.1",
+    "karma-mocha-reporter": "^2.2.5",
+    "karma-safari-launcher": "^1.0.0",
+    "karma-sauce-launcher": "^4.1.5",
+    "karma-sourcemap-loader": "^0.3.7",
+    "karma-webpack": "^4.0.2",
+    "lerna-changelog": "^1.0.0",
+    "lerna-changelog-label-schema": "^3.0.0",
+    "mocha": "^7.2.0",
+    "mocha-loader": "^5.0.0",
+    "nightwatch": "^1.3.5",
+    "nightwatch-helpers": "^1.2.0",
+    "power-assert": "^1.6.0",
+    "rollup": "^0.66.0",
+    "rollup-plugin-buble": "^0.19.2",
+    "rollup-plugin-commonjs": "^9.1.8",
+    "rollup-plugin-flow-no-whitespace": "^1.0.0",
+    "rollup-plugin-node-resolve": "^3.4.0",
+    "rollup-plugin-replace": "^2.0.0",
+    "selenium-server": "^3.141.59",
+    "shipjs": "^0.17.0",
+    "sinon": "^9.0.2",
+    "terser": "^3.17.0",
+    "typescript": "^3.9.3",
+    "vue": "^2.5.17",
+    "vue-github-button": "^1.1.2",
+    "vue-template-compiler": "^2.5.17",
+    "vuepress": "^1.5.0",
+    "webpack": "^4.43.0",
+    "webpack-cli": "^3.1.1",
+    "webpack-dev-middleware": "^3.7.2",
+    "webpack-dev-server": "^3.11.0"
+  },
+  "files": [
+    "dist/vue-i18n.js",
+    "dist/vue-i18n.min.js",
+    "dist/vue-i18n.common.js",
+    "dist/vue-i18n.esm.js",
+    "dist/vue-i18n.esm.browser.js",
+    "dist/vue-i18n.esm.browser.min.js",
+    "src/**/*.js",
+    "types/*.d.ts",
+    "decls",
+    "vetur/tags.json",
+    "vetur/attributes.json"
+  ],
+  "homepage": "https://github.com/kazupon/vue-i18n#readme",
+  "keywords": [
+    "i18n",
+    "internationalization",
+    "plugin",
+    "vue",
+    "vue.js"
+  ],
+  "license": "MIT",
+  "main": "dist/vue-i18n.common.js",
+  "module": "dist/vue-i18n.esm.js",
+  "name": "vue-i18n",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/kazupon/vue-i18n.git"
+  },
+  "scripts": {
+    "build": "node config/build.js",
+    "clean": "rm -rf coverage && rm -rf dist/*.js* && rm ./*.log",
+    "coverage": "cat ./coverage/lcov.info",
+    "dev": "cross-env BABEL_ENV=test webpack-dev-server --inline --hot --open --content-base ./test/unit/ --config config/webpack.dev.conf.js",
+    "docs:build": "cross-env NODE_ENV=production node config/version.js && cross-env NODE_ENV=production vuepress build vuepress -d docs",
+    "docs:clean": "rm -rf docs/**",
+    "docs:dev": "vuepress dev vuepress",
+    "flow": "flow check",
+    "lint": "eslint --fix src test types/**/*.ts",
+    "release:prepare": "shipjs prepare",
+    "release:trigger": "shipjs trigger",
+    "sauce": "npm run sauce:coolkids && npm run sauce:ie && npm run sauce:mobile",
+    "sauce:coolkids": "karma start config/karma.sauce.conf.js -- 0",
+    "sauce:ie": "karma start config/karma.sauce.conf.js -- 1",
+    "sauce:mobile": "karma start config/karma.sauce.conf.js -- 2",
+    "test": "npm run lint && npm run flow && npm run test:types && npm run test:cover && npm run test:e2e",
+    "test:cover": "cross-env BABEL_ENV=test karma start config/karma.cover.conf.js",
+    "test:e2e": "npm run build && node test/e2e/runner.js",
+    "test:types": "tsc -p types",
+    "test:unit": "cross-env BABEL_ENV=test karma start config/karma.unit.conf.js",
+    "test:unit:ci": "cross-env BABEL_ENV=test karma start config/karma.unit.ci.conf.js"
+  },
+  "sideEffects": false,
+  "types": "types/index.d.ts",
+  "unpkg": "dist/vue-i18n.js",
+  "version": "8.21.0",
+  "vetur": {
+    "tags": "vetur/tags.json",
+    "attributes": "vetur/attributes.json"
+  }
+}

+ 101 - 0
plugin/vue-i18n/src/components/interpolation.js

@@ -0,0 +1,101 @@
+/* @flow */
+
+import { warn } from '../util'
+
+export default {
+  name: 'i18n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    path: {
+      type: String,
+      required: true
+    },
+    locale: {
+      type: String
+    },
+    places: {
+      type: [Array, Object]
+    }
+  },
+  render (h: Function, { data, parent, props, slots }: Object) {
+    const { $i18n } = parent
+    if (!$i18n) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot find VueI18n instance!')
+      }
+      return
+    }
+
+    const { path, locale, places } = props
+    const params = slots()
+    const children = $i18n.i(
+      path,
+      locale,
+      onlyHasDefaultPlace(params) || places
+        ? useLegacyPlaces(params.default, places)
+        : params
+    )
+
+    const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span'
+    return tag ? h(tag, data, children) : children
+  }
+}
+
+function onlyHasDefaultPlace (params) {
+  let prop
+  for (prop in params) {
+    if (prop !== 'default') { return false }
+  }
+  return Boolean(prop)
+}
+
+function useLegacyPlaces (children, places) {
+  const params = places ? createParamsFromPlaces(places) : {}
+
+  if (!children) { return params }
+
+  // Filter empty text nodes
+  children = children.filter(child => {
+    return child.tag || child.text.trim() !== ''
+  })
+
+  const everyPlace = children.every(vnodeHasPlaceAttribute)
+  if (process.env.NODE_ENV !== 'production' && everyPlace) {
+    warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.')
+  }
+
+  return children.reduce(
+    everyPlace ? assignChildPlace : assignChildIndex,
+    params
+  )
+}
+
+function createParamsFromPlaces (places) {
+  if (process.env.NODE_ENV !== 'production') {
+    warn('`places` prop is deprecated in next major version. Please switch to Vue slots.')
+  }
+
+  return Array.isArray(places)
+    ? places.reduce(assignChildIndex, {})
+    : Object.assign({}, places)
+}
+
+function assignChildPlace (params, child) {
+  if (child.data && child.data.attrs && child.data.attrs.place) {
+    params[child.data.attrs.place] = child
+  }
+  return params
+}
+
+function assignChildIndex (params, child, index) {
+  params[index] = child
+  return params
+}
+
+function vnodeHasPlaceAttribute (vnode) {
+  return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place)
+}

+ 70 - 0
plugin/vue-i18n/src/components/number.js

@@ -0,0 +1,70 @@
+/* @flow */
+
+import { warn, isString, isObject, includes, numberFormatKeys } from '../util'
+
+export default {
+  name: 'i18n-n',
+  functional: true,
+  props: {
+    tag: {
+      type: [String, Boolean, Object],
+      default: 'span'
+    },
+    value: {
+      type: Number,
+      required: true
+    },
+    format: {
+      type: [String, Object]
+    },
+    locale: {
+      type: String
+    }
+  },
+  render (h: Function, { props, parent, data }: Object) {
+    const i18n = parent.$i18n
+
+    if (!i18n) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot find VueI18n instance!')
+      }
+      return null
+    }
+
+    let key: ?string = null
+    let options: ?NumberFormatOptions = null
+
+    if (isString(props.format)) {
+      key = props.format
+    } else if (isObject(props.format)) {
+      if (props.format.key) {
+        key = props.format.key
+      }
+
+      // Filter out number format options only
+      options = Object.keys(props.format).reduce((acc, prop) => {
+        if (includes(numberFormatKeys, prop)) {
+          return Object.assign({}, acc, { [prop]: props.format[prop] })
+        }
+        return acc
+      }, null)
+    }
+
+    const locale: Locale = props.locale || i18n.locale
+    const parts: NumberFormatToPartsResult = i18n._ntp(props.value, locale, key, options)
+
+    const values = parts.map((part, index) => {
+      const slot: ?Function = data.scopedSlots && data.scopedSlots[part.type]
+      return slot ? slot({ [part.type]: part.value, index, parts }) : part.value
+    })
+
+    const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span'
+    return tag
+      ? h(tag, {
+        attrs: data.attrs,
+        'class': data['class'],
+        staticClass: data.staticClass
+      }, values)
+      : values
+  }
+}

+ 112 - 0
plugin/vue-i18n/src/directive.js

@@ -0,0 +1,112 @@
+/* @flow */
+
+import { warn, isString, isPlainObject, looseEqual } from './util'
+
+export function bind (el: any, binding: Object, vnode: any): void {
+  if (!assert(el, vnode)) { return }
+
+  t(el, binding, vnode)
+}
+
+export function update (el: any, binding: Object, vnode: any, oldVNode: any): void {
+  if (!assert(el, vnode)) { return }
+
+  const i18n: any = vnode.context.$i18n
+  if (localeEqual(el, vnode) &&
+    (looseEqual(binding.value, binding.oldValue) &&
+     looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }
+
+  t(el, binding, vnode)
+}
+
+export function unbind (el: any, binding: Object, vnode: any, oldVNode: any): void {
+  const vm: any = vnode.context
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context')
+    return
+  }
+
+  const i18n: any = vnode.context.$i18n || {}
+  if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {
+    el.textContent = ''
+  }
+  el._vt = undefined
+  delete el['_vt']
+  el._locale = undefined
+  delete el['_locale']
+  el._localeMessage = undefined
+  delete el['_localeMessage']
+}
+
+function assert (el: any, vnode: any): boolean {
+  const vm: any = vnode.context
+  if (!vm) {
+    warn('Vue instance does not exists in VNode context')
+    return false
+  }
+
+  if (!vm.$i18n) {
+    warn('VueI18n instance does not exists in Vue instance')
+    return false
+  }
+
+  return true
+}
+
+function localeEqual (el: any, vnode: any): boolean {
+  const vm: any = vnode.context
+  return el._locale === vm.$i18n.locale
+}
+
+function t (el: any, binding: Object, vnode: any): void {
+  const value: any = binding.value
+
+  const { path, locale, args, choice } = parseValue(value)
+  if (!path && !locale && !args) {
+    warn('value type not supported')
+    return
+  }
+
+  if (!path) {
+    warn('`path` is required in v-t directive')
+    return
+  }
+
+  const vm: any = vnode.context
+  if (choice != null) {
+    el._vt = el.textContent = vm.$i18n.tc(path, choice, ...makeParams(locale, args))
+  } else {
+    el._vt = el.textContent = vm.$i18n.t(path, ...makeParams(locale, args))
+  }
+  el._locale = vm.$i18n.locale
+  el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale)
+}
+
+function parseValue (value: any): Object {
+  let path: ?string
+  let locale: ?Locale
+  let args: any
+  let choice: ?number
+
+  if (isString(value)) {
+    path = value
+  } else if (isPlainObject(value)) {
+    path = value.path
+    locale = value.locale
+    args = value.args
+    choice = value.choice
+  }
+
+  return { path, locale, args, choice }
+}
+
+function makeParams (locale: Locale, args: any): Array<any> {
+  const params: Array<any> = []
+
+  locale && params.push(locale)
+  if (args && (Array.isArray(args) || isPlainObject(args))) {
+    params.push(args)
+  }
+
+  return params
+}

+ 33 - 0
plugin/vue-i18n/src/extend.js

@@ -0,0 +1,33 @@
+/* @flow */
+
+export default function extend (Vue: any): void {
+  if (!Vue.prototype.hasOwnProperty('$i18n')) {
+    // $FlowFixMe
+    Object.defineProperty(Vue.prototype, '$i18n', {
+      get () { return this._i18n }
+    })
+  }
+
+  Vue.prototype.$t = function (key: Path, ...values: any): TranslateResult {
+    const i18n = this.$i18n
+    return i18n._t(key, i18n.locale, i18n._getMessages(), this, ...values)
+  }
+
+  Vue.prototype.$tc = function (key: Path, choice?: number, ...values: any): TranslateResult {
+    const i18n = this.$i18n
+    return i18n._tc(key, i18n.locale, i18n._getMessages(), this, choice, ...values)
+  }
+
+  Vue.prototype.$te = function (key: Path, locale?: Locale): boolean {
+    const i18n = this.$i18n
+    return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
+  }
+
+  Vue.prototype.$d = function (value: number | Date, ...args: any): DateTimeFormatResult {
+    return this.$i18n.d(value, ...args)
+  }
+
+  Vue.prototype.$n = function (value: number, ...args: any): NumberFormatResult {
+    return this.$i18n.n(value, ...args)
+  }
+}

+ 114 - 0
plugin/vue-i18n/src/format.js

@@ -0,0 +1,114 @@
+/* @flow */
+
+import { warn, isObject } from './util'
+
+export default class BaseFormatter {
+  _caches: { [key: string]: Array<Token> }
+
+  constructor () {
+    this._caches = Object.create(null)
+  }
+
+  interpolate (message: string, values: any): Array<any> {
+    if (!values) {
+      return [message]
+    }
+    let tokens: Array<Token> = this._caches[message]
+    if (!tokens) {
+      tokens = parse(message)
+      this._caches[message] = tokens
+    }
+    return compile(tokens, values)
+  }
+}
+
+type Token = {
+  type: 'text' | 'named' | 'list' | 'unknown',
+  value: string
+}
+
+const RE_TOKEN_LIST_VALUE: RegExp = /^(?:\d)+/
+const RE_TOKEN_NAMED_VALUE: RegExp = /^(?:\w)+/
+
+export function parse (format: string): Array<Token> {
+  const tokens: Array<Token> = []
+  let position: number = 0
+
+  let text: string = ''
+  while (position < format.length) {
+    let char: string = format[position++]
+    if (char === '{') {
+      if (text) {
+        tokens.push({ type: 'text', value: text })
+      }
+
+      text = ''
+      let sub: string = ''
+      char = format[position++]
+      while (char !== undefined && char !== '}') {
+        sub += char
+        char = format[position++]
+      }
+      const isClosed = char === '}'
+
+      const type = RE_TOKEN_LIST_VALUE.test(sub)
+        ? 'list'
+        : isClosed && RE_TOKEN_NAMED_VALUE.test(sub)
+          ? 'named'
+          : 'unknown'
+      tokens.push({ value: sub, type })
+    } else if (char === '%') {
+      // when found rails i18n syntax, skip text capture
+      if (format[(position)] !== '{') {
+        text += char
+      }
+    } else {
+      text += char
+    }
+  }
+
+  text && tokens.push({ type: 'text', value: text })
+
+  return tokens
+}
+
+export function compile (tokens: Array<Token>, values: Object | Array<any>): Array<any> {
+  const compiled: Array<any> = []
+  let index: number = 0
+
+  const mode: string = Array.isArray(values)
+    ? 'list'
+    : isObject(values)
+      ? 'named'
+      : 'unknown'
+  if (mode === 'unknown') { return compiled }
+
+  while (index < tokens.length) {
+    const token: Token = tokens[index]
+    switch (token.type) {
+      case 'text':
+        compiled.push(token.value)
+        break
+      case 'list':
+        compiled.push(values[parseInt(token.value, 10)])
+        break
+      case 'named':
+        if (mode === 'named') {
+          compiled.push((values: any)[token.value])
+        } else {
+          if (process.env.NODE_ENV !== 'production') {
+            warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`)
+          }
+        }
+        break
+      case 'unknown':
+        if (process.env.NODE_ENV !== 'production') {
+          warn(`Detect 'unknown' type of token!`)
+        }
+        break
+    }
+    index++
+  }
+
+  return compiled
+}

+ 1065 - 0
plugin/vue-i18n/src/index.js

@@ -0,0 +1,1065 @@
+/* @flow */
+
+import { install, Vue } from './install'
+import {
+  warn,
+  error,
+  isNull,
+  parseArgs,
+  isPlainObject,
+  isObject,
+  isArray,
+  isBoolean,
+  isString,
+  isFunction,
+  looseClone,
+  remove,
+  includes,
+  merge,
+  numberFormatKeys
+} from './util'
+import BaseFormatter from './format'
+import I18nPath from './path'
+
+import type { PathValue } from './path'
+
+const htmlTagMatcher = /<\/?[\w\s="/.':;#-\/]+>/
+const linkKeyMatcher = /(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g
+const linkKeyPrefixMatcher = /^@(?:\.([a-z]+))?:/
+const bracketsMatcher = /[()]/g
+const defaultModifiers = {
+  'upper': str => str.toLocaleUpperCase(),
+  'lower': str => str.toLocaleLowerCase(),
+  'capitalize': str => `${str.charAt(0).toLocaleUpperCase()}${str.substr(1)}`
+}
+
+const defaultFormatter = new BaseFormatter()
+
+export default class VueI18n {
+  static install: () => void
+  static version: string
+  static availabilities: IntlAvailability
+
+  _vm: any
+  _formatter: Formatter
+  _modifiers: Modifiers
+  _root: any
+  _sync: boolean
+  _fallbackRoot: boolean
+  _localeChainCache: { [key: string]: Array<Locale>; }
+  _missing: ?MissingHandler
+  _exist: Function
+  _silentTranslationWarn: boolean | RegExp
+  _silentFallbackWarn: boolean | RegExp
+  _formatFallbackMessages: boolean
+  _dateTimeFormatters: Object
+  _numberFormatters: Object
+  _path: I18nPath
+  _dataListeners: Array<any>
+  _componentInstanceCreatedListener: ?ComponentInstanceCreatedListener
+  _preserveDirectiveContent: boolean
+  _warnHtmlInMessage: WarnHtmlInMessageLevel
+  _postTranslation: ?PostTranslationHandler
+  pluralizationRules: {
+    [lang: string]: (choice: number, choicesLength: number) => number
+  }
+  getChoiceIndex: GetChoiceIndex
+
+  constructor (options: I18nOptions = {}) {
+    // Auto install if it is not done yet and `window` has `Vue`.
+    // To allow users to avoid auto-installation in some cases,
+    // this code should be placed here. See #290
+    /* istanbul ignore if */
+    if (!Vue && typeof window !== 'undefined' && window.Vue) {
+      install(window.Vue)
+    }
+
+    const locale: Locale = options.locale || 'en-US'
+    const fallbackLocale: FallbackLocale = options.fallbackLocale === false
+      ? false
+      : options.fallbackLocale || 'en-US'
+    const messages: LocaleMessages = options.messages || {}
+    const dateTimeFormats = options.dateTimeFormats || {}
+    const numberFormats = options.numberFormats || {}
+
+    this._vm = null
+    this._formatter = options.formatter || defaultFormatter
+    this._modifiers = options.modifiers || {}
+    this._missing = options.missing || null
+    this._root = options.root || null
+    this._sync = options.sync === undefined ? true : !!options.sync
+    this._fallbackRoot = options.fallbackRoot === undefined
+      ? true
+      : !!options.fallbackRoot
+    this._formatFallbackMessages = options.formatFallbackMessages === undefined
+      ? false
+      : !!options.formatFallbackMessages
+    this._silentTranslationWarn = options.silentTranslationWarn === undefined
+      ? false
+      : options.silentTranslationWarn
+    this._silentFallbackWarn = options.silentFallbackWarn === undefined
+      ? false
+      : !!options.silentFallbackWarn
+    this._dateTimeFormatters = {}
+    this._numberFormatters = {}
+    this._path = new I18nPath()
+    this._dataListeners = []
+    this._componentInstanceCreatedListener = options.componentInstanceCreatedListener || null
+    this._preserveDirectiveContent = options.preserveDirectiveContent === undefined
+      ? false
+      : !!options.preserveDirectiveContent
+    this.pluralizationRules = options.pluralizationRules || {}
+    this._warnHtmlInMessage = options.warnHtmlInMessage || 'off'
+    this._postTranslation = options.postTranslation || null
+
+    /**
+     * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
+     * @param choicesLength {number} an overall amount of available choices
+     * @returns a final choice index
+    */
+    this.getChoiceIndex = (choice: number, choicesLength: number): number => {
+      const thisPrototype = Object.getPrototypeOf(this)
+      if (thisPrototype && thisPrototype.getChoiceIndex) {
+        const prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex: any)
+        return (prototypeGetChoiceIndex: GetChoiceIndex).call(this, choice, choicesLength)
+      }
+
+      // Default (old) getChoiceIndex implementation - english-compatible
+      const defaultImpl = (_choice: number, _choicesLength: number) => {
+        _choice = Math.abs(_choice)
+
+        if (_choicesLength === 2) {
+          return _choice
+            ? _choice > 1
+              ? 1
+              : 0
+            : 1
+        }
+
+        return _choice ? Math.min(_choice, 2) : 0
+      }
+
+      if (this.locale in this.pluralizationRules) {
+        return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
+      } else {
+        return defaultImpl(choice, choicesLength)
+      }
+    }
+
+
+    this._exist = (message: Object, key: Path): boolean => {
+      if (!message || !key) { return false }
+      if (!isNull(this._path.getPathValue(message, key))) { return true }
+      // fallback for flat key
+      if (message[key]) { return true }
+      return false
+    }
+
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      Object.keys(messages).forEach(locale => {
+        this._checkLocaleMessage(locale, this._warnHtmlInMessage, messages[locale])
+      })
+    }
+
+    this._initVM({
+      locale,
+      fallbackLocale,
+      messages,
+      dateTimeFormats,
+      numberFormats
+    })
+  }
+
+  _checkLocaleMessage (locale: Locale, level: WarnHtmlInMessageLevel, message: LocaleMessageObject): void {
+    const paths: Array<string> = []
+
+    const fn = (level: WarnHtmlInMessageLevel, locale: Locale, message: any, paths: Array<string>) => {
+      if (isPlainObject(message)) {
+        Object.keys(message).forEach(key => {
+          const val = message[key]
+          if (isPlainObject(val)) {
+            paths.push(key)
+            paths.push('.')
+            fn(level, locale, val, paths)
+            paths.pop()
+            paths.pop()
+          } else {
+            paths.push(key)
+            fn(level, locale, val, paths)
+            paths.pop()
+          }
+        })
+      } else if (isArray(message)) {
+        message.forEach((item, index) => {
+          if (isPlainObject(item)) {
+            paths.push(`[${index}]`)
+            paths.push('.')
+            fn(level, locale, item, paths)
+            paths.pop()
+            paths.pop()
+          } else {
+            paths.push(`[${index}]`)
+            fn(level, locale, item, paths)
+            paths.pop()
+          }
+        })
+      } else if (isString(message)) {
+        const ret = htmlTagMatcher.test(message)
+        if (ret) {
+          const msg = `Detected HTML in message '${message}' of keypath '${paths.join('')}' at '${locale}'. Consider component interpolation with '<i18n>' to avoid XSS. See https://bit.ly/2ZqJzkp`
+          if (level === 'warn') {
+            warn(msg)
+          } else if (level === 'error') {
+            error(msg)
+          }
+        }
+      }
+    }
+
+    fn(level, locale, message, paths)
+  }
+
+  _initVM (data: {
+    locale: Locale,
+    fallbackLocale: FallbackLocale,
+    messages: LocaleMessages,
+    dateTimeFormats: DateTimeFormats,
+    numberFormats: NumberFormats
+  }): void {
+    const silent = Vue.config.silent
+    Vue.config.silent = true
+    this._vm = new Vue({ data })
+    Vue.config.silent = silent
+  }
+
+  destroyVM (): void {
+    this._vm.$destroy()
+  }
+
+  subscribeDataChanging (vm: any): void {
+    this._dataListeners.push(vm)
+  }
+
+  unsubscribeDataChanging (vm: any): void {
+    remove(this._dataListeners, vm)
+  }
+
+  watchI18nData (): Function {
+    const self = this
+    return this._vm.$watch('$data', () => {
+      let i = self._dataListeners.length
+      while (i--) {
+        Vue.nextTick(() => {
+          self._dataListeners[i] && self._dataListeners[i].$forceUpdate()
+        })
+      }
+    }, { deep: true })
+  }
+
+  watchLocale (): ?Function {
+    /* istanbul ignore if */
+    if (!this._sync || !this._root) { return null }
+    const target: any = this._vm
+    return this._root.$i18n.vm.$watch('locale', (val) => {
+      target.$set(target, 'locale', val)
+      target.$forceUpdate()
+    }, { immediate: true })
+  }
+
+  onComponentInstanceCreated (newI18n: I18n) {
+    if (this._componentInstanceCreatedListener) {
+      this._componentInstanceCreatedListener(newI18n, this)
+    }
+  }
+
+  get vm (): any { return this._vm }
+
+  get messages (): LocaleMessages { return looseClone(this._getMessages()) }
+  get dateTimeFormats (): DateTimeFormats { return looseClone(this._getDateTimeFormats()) }
+  get numberFormats (): NumberFormats { return looseClone(this._getNumberFormats()) }
+  get availableLocales (): Locale[] { return Object.keys(this.messages).sort() }
+
+  get locale (): Locale { return this._vm.locale }
+  set locale (locale: Locale): void {
+    this._vm.$set(this._vm, 'locale', locale)
+  }
+
+  get fallbackLocale (): FallbackLocale { return this._vm.fallbackLocale }
+  set fallbackLocale (locale: FallbackLocale): void {
+    this._localeChainCache = {}
+    this._vm.$set(this._vm, 'fallbackLocale', locale)
+  }
+
+  get formatFallbackMessages (): boolean { return this._formatFallbackMessages }
+  set formatFallbackMessages (fallback: boolean): void { this._formatFallbackMessages = fallback }
+
+  get missing (): ?MissingHandler { return this._missing }
+  set missing (handler: MissingHandler): void { this._missing = handler }
+
+  get formatter (): Formatter { return this._formatter }
+  set formatter (formatter: Formatter): void { this._formatter = formatter }
+
+  get silentTranslationWarn (): boolean | RegExp { return this._silentTranslationWarn }
+  set silentTranslationWarn (silent: boolean | RegExp): void { this._silentTranslationWarn = silent }
+
+  get silentFallbackWarn (): boolean | RegExp { return this._silentFallbackWarn }
+  set silentFallbackWarn (silent: boolean | RegExp): void { this._silentFallbackWarn = silent }
+
+  get preserveDirectiveContent (): boolean { return this._preserveDirectiveContent }
+  set preserveDirectiveContent (preserve: boolean): void { this._preserveDirectiveContent = preserve }
+
+  get warnHtmlInMessage (): WarnHtmlInMessageLevel { return this._warnHtmlInMessage }
+  set warnHtmlInMessage (level: WarnHtmlInMessageLevel): void {
+    const orgLevel = this._warnHtmlInMessage
+    this._warnHtmlInMessage = level
+    if (orgLevel !== level && (level === 'warn' || level === 'error')) {
+      const messages = this._getMessages()
+      Object.keys(messages).forEach(locale => {
+        this._checkLocaleMessage(locale, this._warnHtmlInMessage, messages[locale])
+      })
+    }
+  }
+
+  get postTranslation (): ?PostTranslationHandler { return this._postTranslation }
+  set postTranslation (handler: PostTranslationHandler): void { this._postTranslation = handler }
+
+  _getMessages (): LocaleMessages { return this._vm.messages }
+  _getDateTimeFormats (): DateTimeFormats { return this._vm.dateTimeFormats }
+  _getNumberFormats (): NumberFormats { return this._vm.numberFormats }
+
+  _warnDefault (locale: Locale, key: Path, result: ?any, vm: ?any, values: any, interpolateMode: string): ?string {
+    if (!isNull(result)) { return result }
+    if (this._missing) {
+      const missingRet = this._missing.apply(null, [locale, key, vm, values])
+      if (isString(missingRet)) {
+        return missingRet
+      }
+    } else {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+        warn(
+          `Cannot translate the value of keypath '${key}'. ` +
+          'Use the value of keypath as default.'
+        )
+      }
+    }
+
+    if (this._formatFallbackMessages) {
+      const parsedArgs = parseArgs(...values)
+      return this._render(key, interpolateMode, parsedArgs.params, key)
+    } else {
+      return key
+    }
+  }
+
+  _isFallbackRoot (val: any): boolean {
+    return !val && !isNull(this._root) && this._fallbackRoot
+  }
+
+  _isSilentFallbackWarn (key: Path): boolean {
+    return this._silentFallbackWarn instanceof RegExp
+      ? this._silentFallbackWarn.test(key)
+      : this._silentFallbackWarn
+  }
+
+  _isSilentFallback (locale: Locale, key: Path): boolean {
+    return this._isSilentFallbackWarn(key) && (this._isFallbackRoot() || locale !== this.fallbackLocale)
+  }
+
+  _isSilentTranslationWarn (key: Path): boolean {
+    return this._silentTranslationWarn instanceof RegExp
+      ? this._silentTranslationWarn.test(key)
+      : this._silentTranslationWarn
+  }
+
+  _interpolate (
+    locale: Locale,
+    message: LocaleMessageObject,
+    key: Path,
+    host: any,
+    interpolateMode: string,
+    values: any,
+    visitedLinkStack: Array<string>
+  ): any {
+    if (!message) { return null }
+
+    const pathRet: PathValue = this._path.getPathValue(message, key)
+    if (isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
+
+    let ret: mixed
+    if (isNull(pathRet)) {
+      /* istanbul ignore else */
+      if (isPlainObject(message)) {
+        ret = message[key]
+        if (!(isString(ret) || isFunction(ret))) {
+          if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+            warn(`Value of key '${key}' is not a string or function !`)
+          }
+          return null
+        }
+      } else {
+        return null
+      }
+    } else {
+      /* istanbul ignore else */
+      if (isString(pathRet) || isFunction(pathRet)) {
+        ret = pathRet
+      } else {
+        if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {
+          warn(`Value of key '${key}' is not a string or function!`)
+        }
+        return null
+      }
+    }
+
+    // Check for the existence of links within the translated string
+    if (isString(ret) && (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0)) {
+      ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack)
+    }
+
+    return this._render(ret, interpolateMode, values, key)
+  }
+
+  _link (
+    locale: Locale,
+    message: LocaleMessageObject,
+    str: string,
+    host: any,
+    interpolateMode: string,
+    values: any,
+    visitedLinkStack: Array<string>
+  ): any {
+    let ret: string = str
+
+    // Match all the links within the local
+    // We are going to replace each of
+    // them with its translation
+    const matches: any = ret.match(linkKeyMatcher)
+    for (let idx in matches) {
+      // ie compatible: filter custom array
+      // prototype method
+      if (!matches.hasOwnProperty(idx)) {
+        continue
+      }
+      const link: string = matches[idx]
+      const linkKeyPrefixMatches: any = link.match(linkKeyPrefixMatcher)
+      const [linkPrefix, formatterName] = linkKeyPrefixMatches
+
+      // Remove the leading @:, @.case: and the brackets
+      const linkPlaceholder: string = link.replace(linkPrefix, '').replace(bracketsMatcher, '')
+
+      if (includes(visitedLinkStack, linkPlaceholder)) {
+        if (process.env.NODE_ENV !== 'production') {
+          warn(`Circular reference found. "${link}" is already visited in the chain of ${visitedLinkStack.reverse().join(' <- ')}`)
+        }
+        return ret
+      }
+      visitedLinkStack.push(linkPlaceholder)
+
+      // Translate the link
+      let translated: any = this._interpolate(
+        locale, message, linkPlaceholder, host,
+        interpolateMode === 'raw' ? 'string' : interpolateMode,
+        interpolateMode === 'raw' ? undefined : values,
+        visitedLinkStack
+      )
+
+      if (this._isFallbackRoot(translated)) {
+        if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(linkPlaceholder)) {
+          warn(`Fall back to translate the link placeholder '${linkPlaceholder}' with root locale.`)
+        }
+        /* istanbul ignore if */
+        if (!this._root) { throw Error('unexpected error') }
+        const root: any = this._root.$i18n
+        translated = root._translate(
+          root._getMessages(), root.locale, root.fallbackLocale,
+          linkPlaceholder, host, interpolateMode, values
+        )
+      }
+      translated = this._warnDefault(
+        locale, linkPlaceholder, translated, host,
+        isArray(values) ? values : [values],
+        interpolateMode
+      )
+
+      if (this._modifiers.hasOwnProperty(formatterName)) {
+        translated = this._modifiers[formatterName](translated)
+      } else if (defaultModifiers.hasOwnProperty(formatterName)) {
+        translated = defaultModifiers[formatterName](translated)
+      }
+
+      visitedLinkStack.pop()
+
+      // Replace the link with the translated
+      ret = !translated ? ret : ret.replace(link, translated)
+    }
+
+    return ret
+  }
+
+  _createMessageContext (values: any): MessageContext {
+    const _list = isArray(values) ? values : []
+    const _named = isObject(values) ? values : {}
+    const list = (index: number): mixed => _list[index]
+    const named = (key: string): mixed => _named[key]
+    return {
+      list,
+      named
+    }
+  }
+
+  _render (message: string | MessageFunction, interpolateMode: string, values: any, path: string): any {
+    if (isFunction(message)) {
+      return message(this._createMessageContext(values))
+    }
+
+    let ret = this._formatter.interpolate(message, values, path)
+
+    // If the custom formatter refuses to work - apply the default one
+    if (!ret) {
+      ret = defaultFormatter.interpolate(message, values, path)
+    }
+
+    // if interpolateMode is **not** 'string' ('row'),
+    // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
+    return interpolateMode === 'string' && !isString(ret) ? ret.join('') : ret
+  }
+
+  _appendItemToChain (chain: Array<Locale>, item: Locale, blocks: any): any {
+    let follow = false
+    if (!includes(chain, item)) {
+      follow = true
+      if (item) {
+        follow = item[item.length - 1] !== '!'
+        item = item.replace(/!/g, '')
+        chain.push(item)
+        if (blocks && blocks[item]) {
+          follow = blocks[item]
+        }
+      }
+    }
+    return follow
+  }
+
+  _appendLocaleToChain (chain: Array<Locale>, locale: Locale, blocks: any): any {
+    let follow
+    const tokens = locale.split('-')
+    do {
+      const item = tokens.join('-')
+      follow = this._appendItemToChain(chain, item, blocks)
+      tokens.splice(-1, 1)
+    } while (tokens.length && (follow === true))
+    return follow
+  }
+
+  _appendBlockToChain (chain: Array<Locale>, block: Array<Locale> | Object, blocks: any): any {
+    let follow = true
+    for (let i = 0; (i < block.length) && (isBoolean(follow)); i++) {
+      const locale = block[i]
+      if (isString(locale)) {
+        follow = this._appendLocaleToChain(chain, locale, blocks)
+      }
+    }
+    return follow
+  }
+
+  _getLocaleChain (start: Locale, fallbackLocale: FallbackLocale): Array<Locale> {
+    if (start === '') { return [] }
+
+    if (!this._localeChainCache) {
+      this._localeChainCache = {}
+    }
+
+    let chain = this._localeChainCache[start]
+    if (!chain) {
+      if (!fallbackLocale) {
+        fallbackLocale = this.fallbackLocale
+      }
+      chain = []
+
+      // first block defined by start
+      let block = [start]
+
+      // while any intervening block found
+      while (isArray(block)) {
+        block = this._appendBlockToChain(
+          chain,
+          block,
+          fallbackLocale
+        )
+      }
+
+      // last block defined by default
+      let defaults
+      if (isArray(fallbackLocale)) {
+        defaults = fallbackLocale
+      } else if (isObject(fallbackLocale)) {
+        /* $FlowFixMe */
+        if (fallbackLocale['default']) {
+          defaults = fallbackLocale['default']
+        } else {
+          defaults = null
+        }
+      } else {
+        defaults = fallbackLocale
+      }
+
+      // convert defaults to array
+      if (isString(defaults)) {
+        block = [defaults]
+      } else {
+        block = defaults
+      }
+      if (block) {
+        this._appendBlockToChain(
+          chain,
+          block,
+          null
+        )
+      }
+      this._localeChainCache[start] = chain
+    }
+    return chain
+  }
+
+  _translate (
+    messages: LocaleMessages,
+    locale: Locale,
+    fallback: FallbackLocale,
+    key: Path,
+    host: any,
+    interpolateMode: string,
+    args: any
+  ): any {
+    const chain = this._getLocaleChain(locale, fallback)
+    let res
+    for (let i = 0; i < chain.length; i++) {
+      const step = chain[i]
+      res =
+        this._interpolate(step, messages[step], key, host, interpolateMode, args, [key])
+      if (!isNull(res)) {
+        if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(("Fall back to translate the keypath '" + key + "' with '" + step + "' locale."))
+        }
+        return res
+      }
+    }
+    return null
+  }
+
+  _t (key: Path, _locale: Locale, messages: LocaleMessages, host: any, ...values: any): any {
+    if (!key) { return '' }
+
+    const parsedArgs = parseArgs(...values)
+    const locale: Locale = parsedArgs.locale || _locale
+
+    let ret: any = this._translate(
+      messages, locale, this.fallbackLocale, key,
+      host, 'string', parsedArgs.params
+    )
+    if (this._isFallbackRoot(ret)) {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(`Fall back to translate the keypath '${key}' with root locale.`)
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$t(key, ...values)
+    } else {
+      ret = this._warnDefault(locale, key, ret, host, values, 'string')
+      if (this._postTranslation && ret !== null && ret !== undefined) {
+        ret = this._postTranslation(ret, key)
+      }
+      return ret
+    }
+  }
+
+  t (key: Path, ...values: any): TranslateResult {
+    return this._t(key, this.locale, this._getMessages(), null, ...values)
+  }
+
+  _i (key: Path, locale: Locale, messages: LocaleMessages, host: any, values: Object): any {
+    const ret: any =
+      this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values)
+    if (this._isFallbackRoot(ret)) {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+        warn(`Fall back to interpolate the keypath '${key}' with root locale.`)
+      }
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.i(key, locale, values)
+    } else {
+      return this._warnDefault(locale, key, ret, host, [values], 'raw')
+    }
+  }
+
+  i (key: Path, locale: Locale, values: Object): TranslateResult {
+    /* istanbul ignore if */
+    if (!key) { return '' }
+
+    if (!isString(locale)) {
+      locale = this.locale
+    }
+
+    return this._i(key, locale, this._getMessages(), null, values)
+  }
+
+  _tc (
+    key: Path,
+    _locale: Locale,
+    messages: LocaleMessages,
+    host: any,
+    choice?: number,
+    ...values: any
+  ): any {
+    if (!key) { return '' }
+    if (choice === undefined) {
+      choice = 1
+    }
+
+    const predefined = { 'count': choice, 'n': choice }
+    const parsedArgs = parseArgs(...values)
+    parsedArgs.params = Object.assign(predefined, parsedArgs.params)
+    values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params]
+    return this.fetchChoice(this._t(key, _locale, messages, host, ...values), choice)
+  }
+
+  fetchChoice (message: string, choice: number): ?string {
+    /* istanbul ignore if */
+    if (!message && !isString(message)) { return null }
+    const choices: Array<string> = message.split('|')
+
+    choice = this.getChoiceIndex(choice, choices.length)
+    if (!choices[choice]) { return message }
+    return choices[choice].trim()
+  }
+
+  tc (key: Path, choice?: number, ...values: any): TranslateResult {
+    return this._tc(key, this.locale, this._getMessages(), null, choice, ...values)
+  }
+
+  _te (key: Path, locale: Locale, messages: LocaleMessages, ...args: any): boolean {
+    const _locale: Locale = parseArgs(...args).locale || locale
+    return this._exist(messages[_locale], key)
+  }
+
+  te (key: Path, locale?: Locale): boolean {
+    return this._te(key, this.locale, this._getMessages(), locale)
+  }
+
+  getLocaleMessage (locale: Locale): LocaleMessageObject {
+    return looseClone(this._vm.messages[locale] || {})
+  }
+
+  setLocaleMessage (locale: Locale, message: LocaleMessageObject): void {
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      this._checkLocaleMessage(locale, this._warnHtmlInMessage, message)
+    }
+    this._vm.$set(this._vm.messages, locale, message)
+  }
+
+  mergeLocaleMessage (locale: Locale, message: LocaleMessageObject): void {
+    if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {
+      this._checkLocaleMessage(locale, this._warnHtmlInMessage, message)
+    }
+    this._vm.$set(this._vm.messages, locale, merge({}, this._vm.messages[locale] || {}, message))
+  }
+
+  getDateTimeFormat (locale: Locale): DateTimeFormat {
+    return looseClone(this._vm.dateTimeFormats[locale] || {})
+  }
+
+  setDateTimeFormat (locale: Locale, format: DateTimeFormat): void {
+    this._vm.$set(this._vm.dateTimeFormats, locale, format)
+    this._clearDateTimeFormat(locale, format)
+  }
+
+  mergeDateTimeFormat (locale: Locale, format: DateTimeFormat): void {
+    this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format))
+    this._clearDateTimeFormat(locale, format)
+  }
+
+  _clearDateTimeFormat (locale: Locale, format: DateTimeFormat): void {
+    for (let key in format) {
+      const id = `${locale}__${key}`
+
+      if (!this._dateTimeFormatters.hasOwnProperty(id)) {
+        continue
+      }
+
+      delete this._dateTimeFormatters[id]
+    }
+  }
+
+  _localizeDateTime (
+    value: number | Date,
+    locale: Locale,
+    fallback: FallbackLocale,
+    dateTimeFormats: DateTimeFormats,
+    key: string
+  ): ?DateTimeFormatResult {
+    let _locale: Locale = locale
+    let formats: DateTimeFormat = dateTimeFormats[_locale]
+
+    const chain = this._getLocaleChain(locale, fallback)
+    for (let i = 0; i < chain.length; i++) {
+      const current = _locale
+      const step = chain[i]
+      formats = dateTimeFormats[step]
+      _locale = step
+      // fallback locale
+      if (isNull(formats) || isNull(formats[key])) {
+        if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(`Fall back to '${step}' datetime formats from '${current}' datetime formats.`)
+        }
+      } else {
+        break
+      }
+    }
+
+    if (isNull(formats) || isNull(formats[key])) {
+      return null
+    } else {
+      const format: ?DateTimeFormatOptions = formats[key]
+      const id = `${_locale}__${key}`
+      let formatter = this._dateTimeFormatters[id]
+      if (!formatter) {
+        formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format)
+      }
+      return formatter.format(value)
+    }
+  }
+
+  _d (value: number | Date, locale: Locale, key: ?string): DateTimeFormatResult {
+    /* istanbul ignore if */
+    if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.dateTimeFormat) {
+      warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.')
+      return ''
+    }
+
+    if (!key) {
+      return new Intl.DateTimeFormat(locale).format(value)
+    }
+
+    const ret: ?DateTimeFormatResult =
+      this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key)
+    if (this._isFallbackRoot(ret)) {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(`Fall back to datetime localization of root: key '${key}'.`)
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.d(value, key, locale)
+    } else {
+      return ret || ''
+    }
+  }
+
+  d (value: number | Date, ...args: any): DateTimeFormatResult {
+    let locale: Locale = this.locale
+    let key: ?string = null
+
+    if (args.length === 1) {
+      if (isString(args[0])) {
+        key = args[0]
+      } else if (isObject(args[0])) {
+        if (args[0].locale) {
+          locale = args[0].locale
+        }
+        if (args[0].key) {
+          key = args[0].key
+        }
+      }
+    } else if (args.length === 2) {
+      if (isString(args[0])) {
+        key = args[0]
+      }
+      if (isString(args[1])) {
+        locale = args[1]
+      }
+    }
+
+    return this._d(value, locale, key)
+  }
+
+  getNumberFormat (locale: Locale): NumberFormat {
+    return looseClone(this._vm.numberFormats[locale] || {})
+  }
+
+  setNumberFormat (locale: Locale, format: NumberFormat): void {
+    this._vm.$set(this._vm.numberFormats, locale, format)
+    this._clearNumberFormat(locale, format)
+  }
+
+  mergeNumberFormat (locale: Locale, format: NumberFormat): void {
+    this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format))
+    this._clearNumberFormat(locale, format)
+  }
+
+  _clearNumberFormat (locale: Locale, format: NumberFormat): void {
+    for (let key in format) {
+      const id = `${locale}__${key}`
+
+      if (!this._numberFormatters.hasOwnProperty(id)) {
+        continue
+      }
+
+      delete this._numberFormatters[id]
+    }
+  }
+
+  _getNumberFormatter (
+    value: number,
+    locale: Locale,
+    fallback: FallbackLocale,
+    numberFormats: NumberFormats,
+    key: string,
+    options: ?NumberFormatOptions
+  ): ?Object {
+    let _locale: Locale = locale
+    let formats: NumberFormat = numberFormats[_locale]
+
+    const chain = this._getLocaleChain(locale, fallback)
+    for (let i = 0; i < chain.length; i++) {
+      const current = _locale
+      const step = chain[i]
+      formats = numberFormats[step]
+      _locale = step
+      // fallback locale
+      if (isNull(formats) || isNull(formats[key])) {
+        if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+          warn(`Fall back to '${step}' number formats from '${current}' number formats.`)
+        }
+      } else {
+        break
+      }
+    }
+
+    if (isNull(formats) || isNull(formats[key])) {
+      return null
+    } else {
+      const format: ?NumberFormatOptions = formats[key]
+
+      let formatter
+      if (options) {
+        // If options specified - create one time number formatter
+        formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options))
+      } else {
+        const id = `${_locale}__${key}`
+        formatter = this._numberFormatters[id]
+        if (!formatter) {
+          formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format)
+        }
+      }
+      return formatter
+    }
+  }
+
+  _n (value: number, locale: Locale, key: ?string, options: ?NumberFormatOptions): NumberFormatResult {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.numberFormat) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot format a Number value due to not supported Intl.NumberFormat.')
+      }
+      return ''
+    }
+
+    if (!key) {
+      const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options)
+      return nf.format(value)
+    }
+
+    const formatter: ?Object = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options)
+    const ret: ?NumberFormatResult = formatter && formatter.format(value)
+    if (this._isFallbackRoot(ret)) {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {
+        warn(`Fall back to number localization of root: key '${key}'.`)
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n.n(value, Object.assign({}, { key, locale }, options))
+    } else {
+      return ret || ''
+    }
+  }
+
+  n (value: number, ...args: any): NumberFormatResult {
+    let locale: Locale = this.locale
+    let key: ?string = null
+    let options: ?NumberFormatOptions = null
+
+    if (args.length === 1) {
+      if (isString(args[0])) {
+        key = args[0]
+      } else if (isObject(args[0])) {
+        if (args[0].locale) {
+          locale = args[0].locale
+        }
+        if (args[0].key) {
+          key = args[0].key
+        }
+
+        // Filter out number format options only
+        options = Object.keys(args[0]).reduce((acc, key) => {
+          if (includes(numberFormatKeys, key)) {
+            return Object.assign({}, acc, { [key]: args[0][key] })
+          }
+          return acc
+        }, null)
+      }
+    } else if (args.length === 2) {
+      if (isString(args[0])) {
+        key = args[0]
+      }
+      if (isString(args[1])) {
+        locale = args[1]
+      }
+    }
+
+    return this._n(value, locale, key, options)
+  }
+
+  _ntp (value: number, locale: Locale, key: ?string, options: ?NumberFormatOptions): NumberFormatToPartsResult {
+    /* istanbul ignore if */
+    if (!VueI18n.availabilities.numberFormat) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.')
+      }
+      return []
+    }
+
+    if (!key) {
+      const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options)
+      return nf.formatToParts(value)
+    }
+
+    const formatter: ?Object = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options)
+    const ret: ?NumberFormatToPartsResult = formatter && formatter.formatToParts(value)
+    if (this._isFallbackRoot(ret)) {
+      if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {
+        warn(`Fall back to format number to parts of root: key '${key}' .`)
+      }
+      /* istanbul ignore if */
+      if (!this._root) { throw Error('unexpected error') }
+      return this._root.$i18n._ntp(value, locale, key, options)
+    } else {
+      return ret || []
+    }
+  }
+}
+
+let availabilities: IntlAvailability
+// $FlowFixMe
+Object.defineProperty(VueI18n, 'availabilities', {
+  get () {
+    if (!availabilities) {
+      const intlDefined = typeof Intl !== 'undefined'
+      availabilities = {
+        dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
+        numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
+      }
+    }
+
+    return availabilities
+  }
+})
+
+VueI18n.install = install
+VueI18n.version = '__VERSION__'

+ 40 - 0
plugin/vue-i18n/src/install.js

@@ -0,0 +1,40 @@
+import { warn } from './util'
+import extend from './extend'
+import mixin from './mixin'
+import interpolationComponent from './components/interpolation'
+import numberComponent from './components/number'
+import { bind, update, unbind } from './directive'
+
+export let Vue
+
+export function install (_Vue) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && install.installed && _Vue === Vue) {
+    warn('already installed.')
+    return
+  }
+  install.installed = true
+
+  Vue = _Vue
+
+  const version = (Vue.version && Number(Vue.version.split('.')[0])) || -1
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production' && version < 2) {
+    warn(`vue-i18n (${install.version}) need to use Vue 2.0 or later (Vue: ${Vue.version}).`)
+    return
+  }
+
+  extend(Vue)
+  Vue.mixin(mixin)
+  Vue.directive('t', { bind, update, unbind })
+  Vue.component(interpolationComponent.name, interpolationComponent)
+  Vue.component(numberComponent.name, numberComponent)
+
+  // use simple mergeStrategies to prevent i18n instance lose '__proto__'
+  const strats = Vue.config.optionMergeStrategies
+  strats.i18n = function (parentVal, childVal) {
+    return childVal === undefined
+      ? parentVal
+      : childVal
+  }
+}

+ 139 - 0
plugin/vue-i18n/src/mixin.js

@@ -0,0 +1,139 @@
+/* @flow */
+
+import VueI18n from './index'
+import { isPlainObject, warn, error, merge } from './util'
+
+export default {
+  beforeCreate (): void {
+    const options: any = this.$options
+    options.i18n = options.i18n || (options.__i18n ? {} : null)
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            let localeMessages = {}
+            options.__i18n.forEach(resource => {
+              localeMessages = merge(localeMessages, JSON.parse(resource))
+            })
+            Object.keys(localeMessages).forEach((locale: Locale) => {
+              options.i18n.mergeLocaleMessage(locale, localeMessages[locale])
+            })
+          } catch (e) {
+            if (process.env.NODE_ENV !== 'production') {
+              error(`Cannot parse locale messages via custom blocks.`, e)
+            }
+          }
+        }
+        this._i18n = options.i18n
+        this._i18nWatcher = this._i18n.watchI18nData()
+      } else if (isPlainObject(options.i18n)) {
+        const rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n
+          ? this.$root.$i18n
+          : null
+        // component local i18n
+        if (rootI18n) {
+          options.i18n.root = this.$root
+          options.i18n.formatter = rootI18n.formatter
+          options.i18n.fallbackLocale = rootI18n.fallbackLocale
+          options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages
+          options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn
+          options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn
+          options.i18n.pluralizationRules = rootI18n.pluralizationRules
+          options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent
+        }
+
+        // init locale messages via custom blocks
+        if (options.__i18n) {
+          try {
+            let localeMessages = {}
+            options.__i18n.forEach(resource => {
+              localeMessages = merge(localeMessages, JSON.parse(resource))
+            })
+            options.i18n.messages = localeMessages
+          } catch (e) {
+            if (process.env.NODE_ENV !== 'production') {
+              warn(`Cannot parse locale messages via custom blocks.`, e)
+            }
+          }
+        }
+
+        const { sharedMessages } = options.i18n
+        if (sharedMessages && isPlainObject(sharedMessages)) {
+          options.i18n.messages = merge(options.i18n.messages, sharedMessages)
+        }
+
+        this._i18n = new VueI18n(options.i18n)
+        this._i18nWatcher = this._i18n.watchI18nData()
+
+        if (options.i18n.sync === undefined || !!options.i18n.sync) {
+          this._localeWatcher = this.$i18n.watchLocale()
+        }
+
+        if (rootI18n) {
+          rootI18n.onComponentInstanceCreated(this._i18n)
+        }
+      } else {
+        if (process.env.NODE_ENV !== 'production') {
+          warn(`Cannot be interpreted 'i18n' option.`)
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      // root i18n
+      this._i18n = this.$root.$i18n
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      // parent i18n
+      this._i18n = options.parent.$i18n
+    }
+  },
+
+  beforeMount (): void {
+    const options: any = this.$options
+    options.i18n = options.i18n || (options.__i18n ? {} : null)
+
+    if (options.i18n) {
+      if (options.i18n instanceof VueI18n) {
+        // init locale messages via custom blocks
+        this._i18n.subscribeDataChanging(this)
+        this._subscribing = true
+      } else if (isPlainObject(options.i18n)) {
+        this._i18n.subscribeDataChanging(this)
+        this._subscribing = true
+      } else {
+        if (process.env.NODE_ENV !== 'production') {
+          warn(`Cannot be interpreted 'i18n' option.`)
+        }
+      }
+    } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this)
+      this._subscribing = true
+    } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
+      this._i18n.subscribeDataChanging(this)
+      this._subscribing = true
+    }
+  },
+
+  beforeDestroy (): void {
+    if (!this._i18n) { return }
+
+    const self = this
+    this.$nextTick(() => {
+      if (self._subscribing) {
+        self._i18n.unsubscribeDataChanging(self)
+        delete self._subscribing
+      }
+
+      if (self._i18nWatcher) {
+        self._i18nWatcher()
+        self._i18n.destroyVM()
+        delete self._i18nWatcher
+      }
+
+      if (self._localeWatcher) {
+        self._localeWatcher()
+        delete self._localeWatcher
+      }
+    })
+  }
+}

+ 302 - 0
plugin/vue-i18n/src/path.js

@@ -0,0 +1,302 @@
+/* @flow */
+
+import { isObject } from './util'
+
+/**
+ *  Path parser
+ *  - Inspired:
+ *    Vue.js Path parser
+ */
+
+// actions
+const APPEND = 0
+const PUSH = 1
+const INC_SUB_PATH_DEPTH = 2
+const PUSH_SUB_PATH = 3
+
+// states
+const BEFORE_PATH = 0
+const IN_PATH = 1
+const BEFORE_IDENT = 2
+const IN_IDENT = 3
+const IN_SUB_PATH = 4
+const IN_SINGLE_QUOTE = 5
+const IN_DOUBLE_QUOTE = 6
+const AFTER_PATH = 7
+const ERROR = 8
+
+const pathStateMachine: any = []
+
+pathStateMachine[BEFORE_PATH] = {
+  'ws': [BEFORE_PATH],
+  'ident': [IN_IDENT, APPEND],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+}
+
+pathStateMachine[IN_PATH] = {
+  'ws': [IN_PATH],
+  '.': [BEFORE_IDENT],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+}
+
+pathStateMachine[BEFORE_IDENT] = {
+  'ws': [BEFORE_IDENT],
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND]
+}
+
+pathStateMachine[IN_IDENT] = {
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND],
+  'ws': [IN_PATH, PUSH],
+  '.': [BEFORE_IDENT, PUSH],
+  '[': [IN_SUB_PATH, PUSH],
+  'eof': [AFTER_PATH, PUSH]
+}
+
+pathStateMachine[IN_SUB_PATH] = {
+  "'": [IN_SINGLE_QUOTE, APPEND],
+  '"': [IN_DOUBLE_QUOTE, APPEND],
+  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+  ']': [IN_PATH, PUSH_SUB_PATH],
+  'eof': ERROR,
+  'else': [IN_SUB_PATH, APPEND]
+}
+
+pathStateMachine[IN_SINGLE_QUOTE] = {
+  "'": [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_SINGLE_QUOTE, APPEND]
+}
+
+pathStateMachine[IN_DOUBLE_QUOTE] = {
+  '"': [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_DOUBLE_QUOTE, APPEND]
+}
+
+/**
+ * Check if an expression is a literal value.
+ */
+
+const literalValueRE: RegExp = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/
+function isLiteral (exp: string): boolean {
+  return literalValueRE.test(exp)
+}
+
+/**
+ * Strip quotes from a string
+ */
+
+function stripQuotes (str: string): string | boolean {
+  const a: number = str.charCodeAt(0)
+  const b: number = str.charCodeAt(str.length - 1)
+  return a === b && (a === 0x22 || a === 0x27)
+    ? str.slice(1, -1)
+    : str
+}
+
+/**
+ * Determine the type of a character in a keypath.
+ */
+
+function getPathCharType (ch: ?string): string {
+  if (ch === undefined || ch === null) { return 'eof' }
+
+  const code: number = ch.charCodeAt(0)
+
+  switch (code) {
+    case 0x5B: // [
+    case 0x5D: // ]
+    case 0x2E: // .
+    case 0x22: // "
+    case 0x27: // '
+      return ch
+
+    case 0x5F: // _
+    case 0x24: // $
+    case 0x2D: // -
+      return 'ident'
+
+    case 0x09: // Tab
+    case 0x0A: // Newline
+    case 0x0D: // Return
+    case 0xA0:  // No-break space
+    case 0xFEFF:  // Byte Order Mark
+    case 0x2028:  // Line Separator
+    case 0x2029:  // Paragraph Separator
+      return 'ws'
+  }
+
+  return 'ident'
+}
+
+/**
+ * Format a subPath, return its plain form if it is
+ * a literal string or number. Otherwise prepend the
+ * dynamic indicator (*).
+ */
+
+function formatSubPath (path: string): boolean | string {
+  const trimmed: string = path.trim()
+  // invalid leading 0
+  if (path.charAt(0) === '0' && isNaN(path)) { return false }
+
+  return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
+}
+
+/**
+ * Parse a string path into an array of segments
+ */
+
+function parse (path: Path): ?Array<string> {
+  const keys: Array<string> = []
+  let index: number = -1
+  let mode: number = BEFORE_PATH
+  let subPathDepth: number = 0
+  let c: ?string
+  let key: any
+  let newChar: any
+  let type: string
+  let transition: number
+  let action: Function
+  let typeMap: any
+  const actions: Array<Function> = []
+
+  actions[PUSH] = function () {
+    if (key !== undefined) {
+      keys.push(key)
+      key = undefined
+    }
+  }
+
+  actions[APPEND] = function () {
+    if (key === undefined) {
+      key = newChar
+    } else {
+      key += newChar
+    }
+  }
+
+  actions[INC_SUB_PATH_DEPTH] = function () {
+    actions[APPEND]()
+    subPathDepth++
+  }
+
+  actions[PUSH_SUB_PATH] = function () {
+    if (subPathDepth > 0) {
+      subPathDepth--
+      mode = IN_SUB_PATH
+      actions[APPEND]()
+    } else {
+      subPathDepth = 0
+      if (key === undefined) { return false }
+      key = formatSubPath(key)
+      if (key === false) {
+        return false
+      } else {
+        actions[PUSH]()
+      }
+    }
+  }
+
+  function maybeUnescapeQuote (): ?boolean {
+    const nextChar: string = path[index + 1]
+    if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
+      (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
+      index++
+      newChar = '\\' + nextChar
+      actions[APPEND]()
+      return true
+    }
+  }
+
+  while (mode !== null) {
+    index++
+    c = path[index]
+
+    if (c === '\\' && maybeUnescapeQuote()) {
+      continue
+    }
+
+    type = getPathCharType(c)
+    typeMap = pathStateMachine[mode]
+    transition = typeMap[type] || typeMap['else'] || ERROR
+
+    if (transition === ERROR) {
+      return // parse error
+    }
+
+    mode = transition[0]
+    action = actions[transition[1]]
+    if (action) {
+      newChar = transition[2]
+      newChar = newChar === undefined
+        ? c
+        : newChar
+      if (action() === false) {
+        return
+      }
+    }
+
+    if (mode === AFTER_PATH) {
+      return keys
+    }
+  }
+}
+
+export type PathValue = PathValueObject | PathValueArray | Function | string | number | boolean | null
+export type PathValueObject = { [key: string]: PathValue }
+export type PathValueArray = Array<PathValue>
+
+export default class I18nPath {
+  _cache: Object
+
+  constructor () {
+    this._cache = Object.create(null)
+  }
+
+  /**
+   * External parse that check for a cache hit first
+   */
+  parsePath (path: Path): Array<string> {
+    let hit: ?Array<string> = this._cache[path]
+    if (!hit) {
+      hit = parse(path)
+      if (hit) {
+        this._cache[path] = hit
+      }
+    }
+    return hit || []
+  }
+
+  /**
+   * Get path value from path string
+   */
+  getPathValue (obj: mixed, path: Path): PathValue {
+    if (!isObject(obj)) { return null }
+
+    const paths: Array<string> = this.parsePath(path)
+    if (paths.length === 0) {
+      return null
+    } else {
+      const length: number = paths.length
+      let last: any = obj
+      let i: number = 0
+      while (i < length) {
+        const value: any = last[paths[i]]
+        if (value === undefined) {
+          return null
+        }
+        last = value
+        i++
+      }
+
+      return last
+    }
+  }
+}

+ 169 - 0
plugin/vue-i18n/src/util.js

@@ -0,0 +1,169 @@
+/* @flow */
+
+/**
+ * constants
+ */
+
+export const numberFormatKeys = [
+  'style',
+  'currency',
+  'currencyDisplay',
+  'useGrouping',
+  'minimumIntegerDigits',
+  'minimumFractionDigits',
+  'maximumFractionDigits',
+  'minimumSignificantDigits',
+  'maximumSignificantDigits',
+  'localeMatcher',
+  'formatMatcher',
+  'unit'
+]
+
+/**
+ * utilities
+ */
+
+export function warn (msg: string, err: ?Error): void {
+  if (typeof console !== 'undefined') {
+    console.warn('[vue-i18n] ' + msg)
+    /* istanbul ignore if */
+    if (err) {
+      console.warn(err.stack)
+    }
+  }
+}
+
+export function error (msg: string, err: ?Error): void {
+  if (typeof console !== 'undefined') {
+    console.error('[vue-i18n] ' + msg)
+    /* istanbul ignore if */
+    if (err) {
+      console.error(err.stack)
+    }
+  }
+}
+
+export const isArray = Array.isArray
+
+export function isObject (obj: mixed): boolean %checks {
+  return obj !== null && typeof obj === 'object'
+}
+
+export function isBoolean (val: mixed): boolean %checks {
+  return typeof val === 'boolean'
+}
+
+export function isString (val: mixed): boolean %checks {
+  return typeof val === 'string'
+}
+
+const toString: Function = Object.prototype.toString
+const OBJECT_STRING: string = '[object Object]'
+export function isPlainObject (obj: any): boolean {
+  return toString.call(obj) === OBJECT_STRING
+}
+
+export function isNull (val: mixed): boolean {
+  return val === null || val === undefined
+}
+
+export function isFunction (val: mixed): boolean %checks {
+  return typeof val === 'function'
+}
+
+export function parseArgs (...args: Array<mixed>): Object {
+  let locale: ?string = null
+  let params: mixed = null
+  if (args.length === 1) {
+    if (isObject(args[0]) || isArray(args[0])) {
+      params = args[0]
+    } else if (typeof args[0] === 'string') {
+      locale = args[0]
+    }
+  } else if (args.length === 2) {
+    if (typeof args[0] === 'string') {
+      locale = args[0]
+    }
+    /* istanbul ignore if */
+    if (isObject(args[1]) || isArray(args[1])) {
+      params = args[1]
+    }
+  }
+
+  return { locale, params }
+}
+
+export function looseClone (obj: Object): Object {
+  return JSON.parse(JSON.stringify(obj))
+}
+
+export function remove (arr: Array<any>, item: any): Array<any> | void {
+  if (arr.length) {
+    const index = arr.indexOf(item)
+    if (index > -1) {
+      return arr.splice(index, 1)
+    }
+  }
+}
+
+export function includes (arr: Array<any>, item: any): boolean {
+  return !!~arr.indexOf(item)
+}
+
+const hasOwnProperty = Object.prototype.hasOwnProperty
+export function hasOwn (obj: Object | Array<*>, key: string): boolean {
+  return hasOwnProperty.call(obj, key)
+}
+
+export function merge (target: Object): Object {
+  const output = Object(target)
+  for (let i = 1; i < arguments.length; i++) {
+    const source = arguments[i]
+    if (source !== undefined && source !== null) {
+      let key
+      for (key in source) {
+        if (hasOwn(source, key)) {
+          if (isObject(source[key])) {
+            output[key] = merge(output[key], source[key])
+          } else {
+            output[key] = source[key]
+          }
+        }
+      }
+    }
+  }
+  return output
+}
+
+export function looseEqual (a: any, b: any): boolean {
+  if (a === b) { return true }
+  const isObjectA: boolean = isObject(a)
+  const isObjectB: boolean = isObject(b)
+  if (isObjectA && isObjectB) {
+    try {
+      const isArrayA: boolean = isArray(a)
+      const isArrayB: boolean = isArray(b)
+      if (isArrayA && isArrayB) {
+        return a.length === b.length && a.every((e: any, i: number): boolean => {
+          return looseEqual(e, b[i])
+        })
+      } else if (!isArrayA && !isArrayB) {
+        const keysA: Array<string> = Object.keys(a)
+        const keysB: Array<string> = Object.keys(b)
+        return keysA.length === keysB.length && keysA.every((key: string): boolean => {
+          return looseEqual(a[key], b[key])
+        })
+      } else {
+        /* istanbul ignore next */
+        return false
+      }
+    } catch (e) {
+      /* istanbul ignore next */
+      return false
+    }
+  } else if (!isObjectA && !isObjectB) {
+    return String(a) === String(b)
+  } else {
+    return false
+  }
+}

+ 276 - 0
plugin/vue-i18n/types/index.d.ts

@@ -0,0 +1,276 @@
+import Vue, { PluginFunction } from 'vue';
+
+declare namespace VueI18n {
+  type Path = string;
+  type Locale = string;
+  type FallbackLocale = string | string[] | false | { [locale: string]: string[] }
+  type Values = any[] | { [key: string]: any };
+  type Choice = number;
+  interface MessageContext {
+    list(index: number): unknown
+    named(key: string): unknown
+  }
+  type MessageFunction = (ctx: MessageContext) => string;
+  type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray;
+  interface LocaleMessageObject { [key: string]: LocaleMessage; }
+  interface LocaleMessageArray { [index: number]: LocaleMessage; }
+  interface LocaleMessages { [key: string]: LocaleMessageObject; }
+  type TranslateResult = string | LocaleMessages;
+
+  type LocaleMatcher = 'lookup' | 'best-fit';
+  type FormatMatcher = 'basic' | 'best-fit';
+
+  type DateTimeHumanReadable = 'long' | 'short' | 'narrow';
+  type DateTimeDigital = 'numeric' | '2-digit';
+
+  interface SpecificDateTimeFormatOptions extends Intl.DateTimeFormatOptions {
+    year?: DateTimeDigital;
+    month?: DateTimeDigital | DateTimeHumanReadable;
+    day?: DateTimeDigital;
+    hour?: DateTimeDigital;
+    minute?: DateTimeDigital;
+    second?: DateTimeDigital;
+    weekday?: DateTimeHumanReadable;
+    era?: DateTimeHumanReadable;
+    timeZoneName?: 'long' | 'short';
+    localeMatcher?: LocaleMatcher;
+    formatMatcher?: FormatMatcher;
+  }
+
+  type DateTimeFormatOptions = Intl.DateTimeFormatOptions | SpecificDateTimeFormatOptions;
+
+  interface DateTimeFormat { [key: string]: DateTimeFormatOptions; }
+  interface DateTimeFormats { [locale: string]: DateTimeFormat; }
+  type DateTimeFormatResult = string;
+
+  type CurrencyDisplay = 'symbol' | 'code' | 'name';
+
+  interface SpecificNumberFormatOptions extends Intl.NumberFormatOptions {
+    style?: 'decimal' | 'percent';
+    currency?: string;
+    currencyDisplay?: CurrencyDisplay;
+    localeMatcher?: LocaleMatcher;
+    formatMatcher?: FormatMatcher;
+  }
+
+  interface CurrencyNumberFormatOptions extends Intl.NumberFormatOptions {
+    style: 'currency';
+    currency: string; // Obligatory if style is 'currency'
+    currencyDisplay?: CurrencyDisplay;
+    localeMatcher?: LocaleMatcher;
+    formatMatcher?: FormatMatcher;
+  }
+
+  type NumberFormatOptions = Intl.NumberFormatOptions | SpecificNumberFormatOptions | CurrencyNumberFormatOptions;
+
+  interface NumberFormat { [key: string]: NumberFormatOptions; }
+  interface NumberFormats { [locale: string]: NumberFormat; }
+  type NumberFormatResult = string;
+  type PluralizationRulesMap = {
+    /**
+     * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
+     * @param choicesLength {number} an overall amount of available choices
+     * @returns a final choice index
+    */
+    [lang: string]: (choice: number, choicesLength: number) => number;
+  };
+  type Modifiers = { [key: string]: (str : string) => string };
+
+  type FormattedNumberPartType = 'currency' | 'decimal' | 'fraction' | 'group' | 'infinity' | 'integer' | 'literal' | 'minusSign' | 'nan' | 'plusSign' | 'percentSign';
+
+  type WarnHtmlInMessageLevel = 'off' | 'warn' | 'error';
+
+  interface FormattedNumberPart {
+    type: FormattedNumberPartType;
+    value: string;
+  }
+  interface NumberFormatToPartsResult { [index: number]: FormattedNumberPart; }
+
+  interface Formatter {
+    interpolate(message: string, values: Values | undefined, path: string): (any[] | null);
+  }
+
+  type MissingHandler = (locale: Locale, key: Path, vm: Vue | null, values: any) => string | void;
+  type PostTranslationHandler = (str: string, key?: string) => string;
+  type ComponentInstanceCreatedListener = (newVm: VueI18n & IVueI18n, rootVm: VueI18n & IVueI18n) => void;
+
+  interface IntlAvailability {
+    dateTimeFormat: boolean;
+    numberFormat: boolean;
+  }
+
+  // tslint:disable-next-line:interface-name
+  interface I18nOptions {
+    locale?: Locale;
+    fallbackLocale?: FallbackLocale;
+    messages?: LocaleMessages;
+    dateTimeFormats?: DateTimeFormats;
+    numberFormats?: NumberFormats;
+    formatter?: Formatter;
+    modifiers?: Modifiers,
+    missing?: MissingHandler;
+    fallbackRoot?: boolean;
+    formatFallbackMessages?: boolean;
+    sync?: boolean;
+    silentTranslationWarn?: boolean | RegExp;
+    silentFallbackWarn?: boolean | RegExp;
+    preserveDirectiveContent?: boolean;
+    pluralizationRules?: PluralizationRulesMap;
+    warnHtmlInMessage?: WarnHtmlInMessageLevel;
+    sharedMessages?: LocaleMessages;
+    postTranslation?: PostTranslationHandler;
+    componentInstanceCreatedListener?: ComponentInstanceCreatedListener;
+  }
+}
+
+export type Path = VueI18n.Path;
+export type Locale = VueI18n.Locale;
+export type FallbackLocale = VueI18n.FallbackLocale;
+export type Values = VueI18n.Values;
+export type Choice = VueI18n.Choice;
+export type MessageContext = VueI18n.MessageContext;
+export type MessageFunction = VueI18n.MessageFunction;
+export type LocaleMessage = VueI18n.LocaleMessage;
+export type LocaleMessageObject = VueI18n.LocaleMessageObject;
+export type LocaleMessageArray = VueI18n.LocaleMessageArray;
+export type LocaleMessages = VueI18n.LocaleMessages;
+export type TranslateResult = VueI18n.TranslateResult;
+export type DateTimeFormatOptions = VueI18n.DateTimeFormatOptions;
+export type DateTimeFormat = VueI18n.DateTimeFormat;
+export type DateTimeFormats = VueI18n.DateTimeFormats;
+export type DateTimeFormatResult = VueI18n.DateTimeFormatResult;
+export type NumberFormatOptions = VueI18n.NumberFormatOptions;
+export type NumberFormat = VueI18n.NumberFormat;
+export type NumberFormats = VueI18n.NumberFormats;
+export type NumberFormatResult = VueI18n.NumberFormatResult;
+export type NumberFormatToPartsResult = VueI18n.NumberFormatToPartsResult;
+export type WarnHtmlInMessageLevel = VueI18n.WarnHtmlInMessageLevel;
+export type Formatter = VueI18n.Formatter;
+export type MissingHandler = VueI18n.MissingHandler;
+export type PostTranslationHandler = VueI18n.PostTranslationHandler;
+export type IntlAvailability = VueI18n.IntlAvailability;
+export type I18nOptions = VueI18n.I18nOptions;
+
+export declare interface IVueI18n {
+  readonly messages: VueI18n.LocaleMessages;
+  readonly dateTimeFormats: VueI18n.DateTimeFormats;
+  readonly numberFormats: VueI18n.NumberFormats;
+
+  locale: VueI18n.Locale;
+  fallbackLocale: VueI18n.FallbackLocale;
+  missing: VueI18n.MissingHandler;
+  formatter: VueI18n.Formatter;
+  formatFallbackMessages: boolean;
+  silentTranslationWarn: boolean | RegExp;
+  silentFallbackWarn: boolean | RegExp;
+  preserveDirectiveContent: boolean;
+  pluralizationRules: VueI18n.PluralizationRulesMap;
+  warnHtmlInMessage: VueI18n.WarnHtmlInMessageLevel;
+  postTranslation: VueI18n.PostTranslationHandler;
+  t(key: VueI18n.Path, values?: VueI18n.Values): VueI18n.TranslateResult;
+  t(key: VueI18n.Path, locale: VueI18n.Locale, values?: VueI18n.Values): VueI18n.TranslateResult;
+  tc(key: VueI18n.Path, choice?: VueI18n.Choice, values?: VueI18n.Values): string;
+  tc(
+    key: VueI18n.Path,
+    choice: VueI18n.Choice,
+    locale: VueI18n.Locale,
+    values?: VueI18n.Values,
+  ): string;
+  te(key: VueI18n.Path, locale?: VueI18n.Locale): boolean;
+  d(
+    value: number | Date,
+    key?: VueI18n.Path,
+    locale?: VueI18n.Locale,
+  ): VueI18n.DateTimeFormatResult;
+  d(value: number | Date, args?: { [key: string]: string }): VueI18n.DateTimeFormatResult;
+  n(value: number, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.NumberFormatResult;
+  n(value: number, args?: { [key: string]: string }): VueI18n.NumberFormatResult;
+  getLocaleMessage(locale: VueI18n.Locale): VueI18n.LocaleMessageObject;
+  setLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void;
+  mergeLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void;
+  getDateTimeFormat(locale: VueI18n.Locale): VueI18n.DateTimeFormat;
+  setDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void;
+  mergeDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void;
+  getNumberFormat(locale: VueI18n.Locale): VueI18n.NumberFormat;
+  setNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void;
+  mergeNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void;
+  getChoiceIndex: (choice: number, choicesLength: number) => number;
+}
+
+declare class VueI18n {
+  constructor(options?: VueI18n.I18nOptions)
+
+  readonly messages: VueI18n.LocaleMessages;
+  readonly dateTimeFormats: VueI18n.DateTimeFormats;
+  readonly numberFormats: VueI18n.NumberFormats;
+  readonly availableLocales: VueI18n.Locale[];
+
+  locale: VueI18n.Locale;
+  fallbackLocale: VueI18n.FallbackLocale;
+  missing: VueI18n.MissingHandler;
+  formatter: VueI18n.Formatter;
+  formatFallbackMessages: boolean;
+  silentTranslationWarn: boolean | RegExp;
+  silentFallbackWarn: boolean | RegExp;
+  preserveDirectiveContent: boolean;
+  pluralizationRules: VueI18n.PluralizationRulesMap;
+  warnHtmlInMessage: VueI18n.WarnHtmlInMessageLevel;
+  postTranslation: VueI18n.PostTranslationHandler;
+
+  t(key: VueI18n.Path, values?: VueI18n.Values): VueI18n.TranslateResult;
+  t(key: VueI18n.Path, locale: VueI18n.Locale, values?: VueI18n.Values): VueI18n.TranslateResult;
+  tc(key: VueI18n.Path, choice?: VueI18n.Choice, values?: VueI18n.Values): string;
+  tc(key: VueI18n.Path, choice: VueI18n.Choice, locale: VueI18n.Locale, values?: VueI18n.Values): string;
+  te(key: VueI18n.Path, locale?: VueI18n.Locale): boolean;
+  d(value: number | Date, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.DateTimeFormatResult;
+  d(value: number | Date, args?: { [key: string]: string }): VueI18n.DateTimeFormatResult;
+  n(value: number, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.NumberFormatResult;
+  n(value: number, args?: { [key: string]: string }): VueI18n.NumberFormatResult;
+
+  getLocaleMessage(locale: VueI18n.Locale): VueI18n.LocaleMessageObject;
+  setLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void;
+  mergeLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void;
+
+  getDateTimeFormat(locale: VueI18n.Locale): VueI18n.DateTimeFormat;
+  setDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void;
+  mergeDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void;
+
+  getNumberFormat(locale: VueI18n.Locale): VueI18n.NumberFormat;
+  setNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void;
+  mergeNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void;
+
+  /**
+   * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
+   * @param choicesLength {number} an overall amount of available choices
+   * @returns a final choice index
+  */
+  getChoiceIndex: (choice: number, choicesLength: number) => number;
+
+  static install: PluginFunction<never>;
+  static version: string;
+  static availabilities: VueI18n.IntlAvailability;
+}
+
+declare module 'vue/types/vue' {
+  interface Vue {
+    readonly $i18n: VueI18n & IVueI18n;
+    $t: typeof VueI18n.prototype.t;
+    $tc: typeof VueI18n.prototype.tc;
+    $te: typeof VueI18n.prototype.te;
+    $d: typeof VueI18n.prototype.d;
+    $n: typeof VueI18n.prototype.n;
+  }
+}
+
+declare module 'vue/types/options' {
+  interface ComponentOptions<V extends Vue> {
+    i18n?: {
+      messages?: VueI18n.LocaleMessages;
+      dateTimeFormats?: VueI18n.DateTimeFormats;
+      numberFormats?: VueI18n.NumberFormats;
+      sharedMessages?: VueI18n.LocaleMessages;
+    };
+  }
+}
+
+export default VueI18n;

+ 34 - 0
plugin/vue-i18n/vetur/attributes.json

@@ -0,0 +1,34 @@
+{
+  "i18n/path" : {
+    "description": "[required]\nKeypath of the locale message",
+    "type": "string"
+  },
+  "i18n/locale" : {
+    "description": "[optional]\nLocale to be used in this translation",
+    "type": "string"
+  },
+  "i18n/tag" : {
+    "description": "[optional]\nWhich tag to render, default is \"span\"",
+    "type": "string"
+  },
+  "i18n/places": {
+    "description": "[optional after v8.14]\nWill be removed in the next major version, use the slot syntax instead\n\nhttp://kazupon.github.io/vue-i18n/guide/interpolation.html#slots-syntax-usage",
+    "type": "array|object"
+  },
+  "i18n-n/value" : {
+    "description": "[required]\nNumber to be used in formatting",
+    "type": "number"
+  },
+  "i18n-n/format": {
+    "description": "[optional]\nNumber format name or object with explicit format options",
+    "type": "string|object"
+  },
+  "i18n-n/locale" : {
+    "description": "[optional]\nLocale to be used in this translation",
+    "type": "string"
+  },
+  "i18n-n/tag" : {
+    "description": "[optional]\nWhich tag to render, default is `span`",
+    "type": "string"
+  }
+}

+ 20 - 0
plugin/vue-i18n/vetur/tags.json

@@ -0,0 +1,20 @@
+{
+  "i18n": {
+    "attributes": [
+      "path",
+      "locale",
+      "tag",
+      "places"
+    ],
+    "description": "This is a functional component that can be used when HTML interpolation is needed.\n\nhttp://kazupon.github.io/vue-i18n/guide/interpolation.html#basic-usage"
+  },
+  "i18n-n": {
+    "attributes": [
+      "value",
+      "format",
+      "locale",
+      "tag"
+    ],
+    "description": "This functional component provides a way to use HTML interpolation in pair with number formatting.\n\nhttp://kazupon.github.io/vue-i18n/guide/number.html#custom-formatting"
+  }
+}

+ 551 - 0
static/css/cmy.css

@@ -0,0 +1,551 @@
+/*初始化类*/
+@font-face {
+	font-family: 'iconfont';
+	/* project id 1482221 */
+	src: url('https://at.alicdn.com/t/font_1482221_x9emymthrxs.eot');
+	src: url('https://at.alicdn.com/t/font_1482221_x9emymthrxs.eot?#iefix') format('embedded-opentype'),
+		url('https://at.alicdn.com/t/font_1482221_x9emymthrxs.woff2') format('woff2'),
+		url('https://at.alicdn.com/t/font_1482221_x9emymthrxs.woff') format('woff'),
+		url('https://at.alicdn.com/t/font_1482221_x9emymthrxs.ttf') format('truetype'),
+		url('https://at.alicdn.com/t/font_1482221_x9emymthrxs.svg#iconfont') format('svg');
+}
+
+.acea-row {
+	display: -webkit-box;
+	display: -moz-box;
+	display: -webkit-flex;
+	display: -ms-flexbox;
+	display: flex;
+	-webkit-box-lines: multiple;
+	-moz-box-lines: multiple;
+	-o-box-lines: multiple;
+	-webkit-flex-wrap: wrap;
+	-ms-flex-wrap: wrap;
+	flex-wrap: wrap
+}
+
+.acea-row.row-middle {
+	-webkit-box-align: center;
+	-moz-box-align: center;
+	-o-box-align: center;
+	-ms-flex-align: center;
+	-webkit-align-items: center;
+	align-items: center
+}
+
+.bg-color-red {
+	background-color: #e93323 !important;
+}
+
+.acea-row.row-right {
+	-webkit-box-pack: end;
+	-moz-box-pack: end;
+	-o-box-pack: end;
+	-ms-flex-pack: end;
+	-webkit-justify-content: flex-end;
+	justify-content: flex-end
+}
+
+.acea-row.row-between-wrapper {
+	-webkit-box-align: center;
+	-moz-box-align: center;
+	-o-box-align: center;
+	-ms-flex-align: center;
+	-webkit-align-items: center;
+	align-items: center;
+	-webkit-box-pack: justify;
+	-moz-box-pack: justify;
+	-o-box-pack: justify;
+	-ms-flex-pack: justify;
+	-webkit-justify-content: space-between;
+	justify-content: space-between
+}
+
+.acea-row.row-column-around {
+	-webkit-flex-direction: column;
+	-ms-flex-direction: column;
+	flex-direction: column;
+	justify-content: space-around;
+	-webkit-justify-content: space-around
+}
+
+.acea-row.row-center-wrapper {
+	-webkit-box-align: center;
+	-moz-box-align: center;
+	-o-box-align: center;
+	-ms-flex-align: center;
+	-webkit-align-items: center;
+	align-items: center;
+	-webkit-box-pack: center;
+	-moz-box-pack: center;
+	-o-box-pack: center;
+	-ms-flex-pack: center;
+	-webkit-justify-content: center;
+	justify-content: center
+}
+
+.iconfont {
+	font-family: "iconfont" !important;
+	font-size: 34rpx;
+	font-style: normal;
+	-webkit-font-smoothing: antialiased;
+	-webkit-text-stroke-width: 0rpx;
+	-moz-osx-font-smoothing: grayscale;
+}
+
+.iconedit:before {
+	content: "\e649";
+}
+
+.iconfavorfill:before {
+	content: "\e64b";
+}
+
+.iconfavor:before {
+	content: "\e64c";
+}
+
+.iconlocation:before {
+	content: "\e651";
+}
+
+.iconroundcheckfill:before {
+	content: "\e656";
+}
+
+.iconroundcheck:before {
+	content: "\e657";
+}
+
+.iconunfold:before {
+	content: "\e661";
+}
+
+.iconlikefill:before {
+	content: "\e668";
+}
+
+.iconlike:before {
+	content: "\e669";
+}
+
+.iconshop:before {
+	content: "\e676";
+}
+
+.iconcart:before {
+	content: "\e6af";
+}
+
+.icondelete:before {
+	content: "\e6b4";
+}
+
+.iconhome:before {
+	content: "\e6b8";
+}
+
+.iconcartfill:before {
+	content: "\e6b9";
+}
+
+.iconhomefill:before {
+	content: "\e6bb";
+}
+
+.iconlock:before {
+	content: "\e6c0";
+}
+
+.iconfriendadd:before {
+	content: "\e6ca";
+}
+
+.iconfold:before {
+	content: "\e6de";
+}
+
+.iconapps:before {
+	content: "\e729";
+}
+
+.iconadd:before {
+	content: "\e767";
+}
+
+.iconmove:before {
+	content: "\e768";
+}
+
+.icontriangledownfill:before {
+	content: "\e79b";
+}
+
+.icontriangleupfill:before {
+	content: "\e79c";
+}
+
+.iconshaixuan:before {
+	content: "\e74a";
+}
+
+.iconyanzhengma:before {
+	content: "\e684";
+}
+
+.iconjifen:before {
+	content: "\e60f";
+}
+
+.iconwuliuxinxi:before {
+	content: "\e62b";
+}
+
+.iconmessage:before {
+	content: "\e78a";
+}
+
+.iconsetting:before {
+	content: "\e78e";
+}
+
+.iconaddition:before {
+	content: "\e6e0";
+}
+
+.iconclose:before {
+	content: "\e6e9";
+}
+
+.iconenter:after {
+	content: "\e6f8";
+}
+
+.iconprompt:before {
+	content: "\e71b";
+}
+
+.iconreturn:before {
+	content: "\e720";
+}
+
+.iconsearch:before {
+	content: "\e741";
+}
+
+.iconpengyouquan:before {
+	content: "\e62c";
+}
+
+.iconweixin:before {
+	content: "\e60e";
+}
+
+.iconzhifubao:before {
+	content: "\e673";
+}
+
+.iconyue:before {
+	content: "\e618";
+}
+
+.iconweixin1:before {
+	content: "\e622";
+}
+
+.iconlock1:before {
+	content: "\e64d";
+}
+
+.iconuser:before {
+	content: "\e64e";
+}
+
+.iconchenggongtixianshouyi:before {
+	content: "\e64f";
+}
+
+.iconviptuiguangdingdan:before {
+	content: "\e650";
+}
+
+.icondaifukuan:before {
+	content: "\e652";
+}
+
+.icondaijiesuanshouyi:before {
+	content: "\e653";
+}
+
+.icondaidakuanshouyi:before {
+	content: "\e654";
+}
+
+.icondaifahuo:before {
+	content: "\e655";
+}
+
+.icondaishouhuoshouyi:before {
+	content: "\e658";
+}
+
+.icondaishouhuo:before {
+	content: "\e659";
+}
+
+.iconwuxiaoshouyi:before {
+	content: "\e65a";
+}
+
+.icontixianmingxi:before {
+	content: "\e65b";
+}
+
+.iconshouyi:before {
+	content: "\e65c";
+}
+
+.iconkouchutixianshouxufei:before {
+	content: "\e65d";
+}
+
+.iconyishenqingshouyi:before {
+	content: "\e65e";
+}
+
+.icontuihuanhuo:before {
+	content: "\e65f";
+}
+
+
+/*水平线*/
+.hr {
+	width: 100%;
+	position: relative;
+	border-bottom: 1px solid #dddddd;
+	/* height: 0.5rpx; */
+}
+
+/* 一行显示 */
+.clamp {
+	overflow: hidden;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	display: block;
+}
+
+/* 二行显示 */
+.clamp2 {
+	overflow: hidden;
+	text-overflow: ellipsis;
+	display: -webkit-box;
+	-webkit-line-clamp: 2;
+	-webkit-box-orient: vertical;
+}
+
+/* 二行显示 */
+.ellipsis {
+	overflow: hidden;
+	text-overflow: ellipsis;
+	display: -webkit-box;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 2;
+}
+
+.common-hover {
+	background: #f5f5f5;
+}
+
+/* 角标 */
+.corner {
+	background-color: #e51c23;
+	position: absolute;
+	right: -18rpx;
+	top: -18rpx;
+	color: #FFFFFF;
+	text-align: center;
+	border-radius: 999px;
+	font-size: 24rpx !important;
+	min-width: 35rpx;
+	min-height: 35rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	line-height: 1;
+}
+
+.flex_item {
+	display: flex;
+	align-items: center;
+	/* justify-content: space-between; */
+}
+
+/* 左右顶格加上下居中 */
+.flex-between-center {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+}
+
+/* flex布局-整体居中 */
+.flex-center {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+
+.flex-start {
+	display: flex;
+	align-items: center;
+	justify-content: flex-start;
+}
+
+/*文字对齐*/
+.text-left {
+	text-align: left !important;
+}
+
+.text-center {
+	text-align: center !important;
+}
+
+.text-justify {
+	text-align: justify !important;
+}
+
+.text-right {
+	text-align: right !important;
+}
+
+.text-default {
+	color: #212121 !important;
+}
+
+.text-white {
+	color: #ffffff !important;
+}
+
+.text-primary {
+	color: #00bcd4 !important;
+}
+
+.text-success {
+	color: #009688 !important;
+}
+
+.text-info {
+	color: #03a9f4 !important;
+}
+
+.text-warning {
+	color: #ffc107 !important;
+}
+
+.text-danger {
+	color: #e51c23 !important;
+}
+
+.text-pink {
+	color: #e91e63 !important;
+}
+
+.text-purple {
+	color: #673ab7 !important;
+}
+
+.text-indigo {
+	color: #3f51b5 !important;
+}
+
+.text-gray {
+	color: #999999 !important;
+}
+
+.bg-default {
+	background-color: #f5f5f5 !important;
+}
+
+.bg-primary {
+	background-color: #00bcd4 !important;
+}
+
+.bg-success {
+	background-color: #009688 !important;
+}
+
+.bg-info {
+	background-color: #03a9f4 !important;
+}
+
+.bg-warning {
+	background-color: #FFB238 !important;
+}
+
+.bg-danger {
+	background-color: #DC4D46 !important;
+}
+
+.bg-pink {
+	background-color: #e91e63 !important;
+}
+
+.bg-purple {
+	background-color: #673ab7 !important;
+}
+
+.bg-indigo {
+	background-color: #3f51b5 !important;
+}
+
+.bg-white {
+	background-color: white !important;
+}
+
+.bg-gray {
+	background-color: #e3e3e3 !important;
+}
+
+/* 边框 */
+.border-radius-15 {
+	border-radius: 15rpx;
+}
+
+.border-radius-10 {
+	border-radius: 10rpx;
+}
+
+.border-radius-all {
+	border-radius: 1000rpx;
+}
+
+/* 底部边线 */
+.borde-b {
+	border-bottom: 1px solid #dddddd;
+}
+
+/* 弹性盒子 */
+.flex {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+
+.items-left {
+	justify-content: flex-start;
+}
+
+.items-right {
+	justify-content: flex-end;
+}
+
+.flex-shrink-false {
+	flex-shrink: 0;
+}
+
+.flex-grow-true {
+	flex-grow: 1;
+}
+
+.position-relative {
+	position: relative;
+}

BIN
static/error/emptyCart.png


BIN
static/error/errorImage.jpg


BIN
static/error/missing-face.png


BIN
static/img/img01.png


BIN
static/img/img02.png


Some files were not shown because too many files changed in this diff