mirror of
https://github.com/youzan/vant.git
synced 2025-10-20 10:44:59 +00:00
Docs: add English language support (#170)
* feat: support lang entry build * feat: vant support lang switch * move lang iframe-router to utils & fix router link bug * add en-US config && add some translation * chang async. to async_ (support superman cdn) * add layout translation * change nav style * upgrade zan-doc * fix: doc config * upgrade zan-doc && remove useless code * fix changelog generate path
This commit is contained in:
@@ -4,6 +4,6 @@
|
||||
|
||||
<style lang="postcss">
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<zan-doc :simulator="simulator" :config="config">
|
||||
<zan-doc :simulator="simulator" :config="config" :base="base">
|
||||
<router-view></router-view>
|
||||
</zan-doc>
|
||||
</div>
|
||||
@@ -8,28 +8,36 @@
|
||||
|
||||
<script>
|
||||
import docConfig from './doc.config';
|
||||
import { getLang } from './utils/lang';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
if (location.host === 'www.youzanyun.com') {
|
||||
if (window.location.host === 'www.youzanyun.com') {
|
||||
const group = docConfig['zh-CN'].nav[0].groups[0];
|
||||
group.list = group.list.filter(item => item.title !== '业务组件');
|
||||
}
|
||||
|
||||
const hash = window.location.hash;
|
||||
|
||||
return {
|
||||
simulator: this.getSimulatorPath(),
|
||||
config: docConfig['zh-CN']
|
||||
simulator: `/zanui/vue/examples${hash}`,
|
||||
lang: getLang()
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
getSimulatorPath() {
|
||||
const dir = location.hash.split('/').pop();
|
||||
if (dir === 'quickstart' || dir === 'changelog') {
|
||||
return '/zanui/vue/examples';
|
||||
} else {
|
||||
return `/zanui/vue/examples#/component/${dir}`;
|
||||
}
|
||||
computed: {
|
||||
base() {
|
||||
return `/${this.lang}/component`;
|
||||
},
|
||||
|
||||
config() {
|
||||
return docConfig[this.lang];
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
'$route'(to) {
|
||||
this.lang = to.meta.lang;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<h1 class="zanui-title">Zan UI</h1>
|
||||
<h2 class="zanui-desc">有赞移动端 Vue 组件库</h2>
|
||||
<div class="mobile-navs">
|
||||
<div class="mobile-nav-item" v-for="(item, index) in data" v-if="item.showInMobile" :key="index">
|
||||
<div class="mobile-nav-item" v-for="(item, index) in navList" v-if="item.showInMobile" :key="index">
|
||||
<mobile-nav v-for="(group, index) in item.groups" :group="group" :base="base" :nav-key="index" :key="index" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -13,17 +13,36 @@
|
||||
<script>
|
||||
import docConfig from '../doc.config';
|
||||
import MobileNav from './mobile-nav';
|
||||
import { getLang } from '../utils/lang';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data: docConfig['zh-CN'].nav,
|
||||
base: '/component'
|
||||
docConfig,
|
||||
lang: getLang()
|
||||
};
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => vm.lang = to.meta.lang);
|
||||
},
|
||||
|
||||
beforeRouteUpdate(to, from, next) {
|
||||
this.lang = to.meta.lang;
|
||||
},
|
||||
|
||||
components: {
|
||||
MobileNav
|
||||
},
|
||||
|
||||
computed: {
|
||||
base() {
|
||||
return `${this.lang}/component`;
|
||||
},
|
||||
|
||||
navList() {
|
||||
return this.docConfig[this.lang].nav || [];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -17,7 +17,7 @@
|
||||
v-if="!navItem.disabled">
|
||||
<router-link
|
||||
active-class="active"
|
||||
:to="base + navItem.path">
|
||||
:to="'/' + base + navItem.path">
|
||||
<p>
|
||||
{{ navItem.title }}
|
||||
</p>
|
||||
|
@@ -5,7 +5,8 @@ module.exports = {
|
||||
'首页': 'https://www.youzanyun.com/zanui',
|
||||
'PC端': 'https://www.youzanyun.com/zanui/react',
|
||||
'移动端': 'https://www.youzanyun.com/zanui/vue',
|
||||
'微信小程序': 'https://github.com/youzan/zanui-weapp'
|
||||
'微信小程序': 'https://github.com/youzan/zanui-weapp',
|
||||
'English': '#/en-US/'
|
||||
},
|
||||
footer: {
|
||||
github: 'https://github.com/youzan/vant',
|
||||
@@ -48,87 +49,87 @@ module.exports = {
|
||||
"list": [
|
||||
{
|
||||
"path": "/layout",
|
||||
"title": "Layout 布局"
|
||||
"title": "Layout - 布局"
|
||||
},
|
||||
{
|
||||
"path": "/badge",
|
||||
"title": "Badge 徽章"
|
||||
"title": "Badge - 徽章"
|
||||
},
|
||||
{
|
||||
"path": "/button",
|
||||
"title": "Button 按钮"
|
||||
"title": "Button - 按钮"
|
||||
},
|
||||
{
|
||||
"path": "/card",
|
||||
"title": "Card 卡片"
|
||||
"title": "Card - 卡片"
|
||||
},
|
||||
{
|
||||
"path": "/cell",
|
||||
"title": "Cell 单元格"
|
||||
"title": "Cell - 单元格"
|
||||
},
|
||||
{
|
||||
"path": "/icon",
|
||||
"title": "Icon 图标"
|
||||
"title": "Icon - 图标"
|
||||
},
|
||||
{
|
||||
"path": "/image-preview",
|
||||
"title": "ImagePreview 图片预览"
|
||||
"title": "ImagePreview - 图片预览"
|
||||
},
|
||||
{
|
||||
"path": "/lazyload",
|
||||
"title": "Lazyload 图片懒加载"
|
||||
"title": "Lazyload - 图片懒加载"
|
||||
},
|
||||
{
|
||||
"path": "/loading",
|
||||
"title": "Loading 加载"
|
||||
"title": "Loading - 加载"
|
||||
},
|
||||
{
|
||||
"path": "/nav-bar",
|
||||
"title": "NavBar 导航栏"
|
||||
"title": "NavBar - 导航栏"
|
||||
},
|
||||
{
|
||||
"path": "/notice-bar",
|
||||
"title": "NoticeBar 通告栏"
|
||||
"title": "NoticeBar - 通告栏"
|
||||
},
|
||||
{
|
||||
"path": "/panel",
|
||||
"title": "Panel 面板"
|
||||
"title": "Panel - 面板"
|
||||
},
|
||||
{
|
||||
"path": "/popup",
|
||||
"title": "Popup 弹出层"
|
||||
"title": "Popup - 弹出层"
|
||||
},
|
||||
{
|
||||
"path": "/progress",
|
||||
"title": "Progress 进度条"
|
||||
"title": "Progress - 进度条"
|
||||
},
|
||||
{
|
||||
"path": "/search",
|
||||
"title": "Search 搜索"
|
||||
"title": "Search - 搜索"
|
||||
},
|
||||
{
|
||||
"path": "/stepper",
|
||||
"title": "Stepper 步进器"
|
||||
"title": "Stepper - 步进器"
|
||||
},
|
||||
{
|
||||
"path": "/steps",
|
||||
"title": "Steps 步骤条"
|
||||
"title": "Steps - 步骤条"
|
||||
},
|
||||
{
|
||||
"path": "/swipe",
|
||||
"title": "Swipe 轮播"
|
||||
"title": "Swipe - 轮播"
|
||||
},
|
||||
{
|
||||
"path": "/tab",
|
||||
"title": "Tab 标签"
|
||||
"title": "Tab - 标签"
|
||||
},
|
||||
{
|
||||
"path": "/tag",
|
||||
"title": "Tag 标记"
|
||||
"title": "Tag - 标记"
|
||||
},
|
||||
{
|
||||
"path": "/waterfall",
|
||||
"title": "Waterfall 瀑布流"
|
||||
"title": "Waterfall - 瀑布流"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -137,31 +138,31 @@ module.exports = {
|
||||
"list": [
|
||||
{
|
||||
"path": "/checkbox",
|
||||
"title": "Checkbox 复选框"
|
||||
"title": "Checkbox - 复选框"
|
||||
},
|
||||
{
|
||||
"path": "/field",
|
||||
"title": "Field 输入框"
|
||||
"title": "Field - 输入框"
|
||||
},
|
||||
{
|
||||
"path": "/number-keyboard",
|
||||
"title": "NumberKeyboard 数字键盘"
|
||||
"title": "NumberKeyboard - 数字键盘"
|
||||
},
|
||||
{
|
||||
"path": "/password-input",
|
||||
"title": "PasswordInput 密码输入框"
|
||||
"title": "PasswordInput - 密码输入框"
|
||||
},
|
||||
{
|
||||
"path": "/radio",
|
||||
"title": "Radio 单选框"
|
||||
"title": "Radio - 单选框"
|
||||
},
|
||||
{
|
||||
"path": "/switch",
|
||||
"title": "Switch 开关"
|
||||
"title": "Switch - 开关"
|
||||
},
|
||||
{
|
||||
"path": "/uploader",
|
||||
"title": "Uploader 图片上传"
|
||||
"title": "Uploader - 图片上传"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -170,27 +171,27 @@ module.exports = {
|
||||
"list": [
|
||||
{
|
||||
"path": "/actionsheet",
|
||||
"title": "Actionsheet 行动按钮"
|
||||
"title": "Actionsheet - 行动按钮"
|
||||
},
|
||||
{
|
||||
"path": "/datetime-picker",
|
||||
"title": "DatetimePicker 时间选择"
|
||||
"title": "DatetimePicker - 时间选择"
|
||||
},
|
||||
{
|
||||
"path": "/dialog",
|
||||
"title": "Dialog 弹出框"
|
||||
"title": "Dialog - 弹出框"
|
||||
},
|
||||
{
|
||||
"path": "/picker",
|
||||
"title": "Picker 选择器"
|
||||
"title": "Picker - 选择器"
|
||||
},
|
||||
{
|
||||
"path": "/pull-refresh",
|
||||
"title": "PullRefresh 下拉刷新"
|
||||
"title": "PullRefresh - 下拉刷新"
|
||||
},
|
||||
{
|
||||
"path": "/toast",
|
||||
"title": "Toast 轻提示"
|
||||
"title": "Toast - 轻提示"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -199,15 +200,15 @@ module.exports = {
|
||||
"list": [
|
||||
{
|
||||
"path": "/cell-swipe",
|
||||
"title": "CellSwipe 滑动单元格"
|
||||
"title": "CellSwipe - 滑动单元格"
|
||||
},
|
||||
{
|
||||
"path": "/switch-cell",
|
||||
"title": "SwitchCell 开关单元格"
|
||||
"title": "SwitchCell - 开关单元格"
|
||||
},
|
||||
{
|
||||
"path": "/tree-select",
|
||||
"title": "TreeSelect 分类选择"
|
||||
"title": "TreeSelect - 分类选择"
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -216,40 +217,94 @@ module.exports = {
|
||||
"list": [
|
||||
{
|
||||
"path": "/address-edit",
|
||||
"title": "AddressEdit 地址编辑"
|
||||
"title": "AddressEdit - 地址编辑"
|
||||
},
|
||||
{
|
||||
"path": "/address-list",
|
||||
"title": "AddressList 地址列表"
|
||||
"title": "AddressList - 地址列表"
|
||||
},
|
||||
{
|
||||
"path": "/area",
|
||||
"title": "Area 省市区选择"
|
||||
"title": "Area - 省市区选择"
|
||||
},
|
||||
{
|
||||
"path": "/contact",
|
||||
"title": "Contact 联系人"
|
||||
"title": "Contact - 联系人"
|
||||
},
|
||||
{
|
||||
"path": "/coupon",
|
||||
"title": "Coupon 优惠券选择器"
|
||||
"title": "Coupon - 优惠券选择器"
|
||||
},
|
||||
{
|
||||
"path": "/goods-action",
|
||||
"title": "GoodsAction 商品页行动点"
|
||||
"title": "GoodsAction - 商品页行动点"
|
||||
},
|
||||
{
|
||||
"path": "/submit-bar",
|
||||
"title": "SubmitBar 提交订单栏"
|
||||
"title": "SubmitBar - 提交订单栏"
|
||||
},
|
||||
{
|
||||
"path": "/sku",
|
||||
"title": "Sku 商品规格弹层"
|
||||
"title": "Sku - 商品规格弹层"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"en-US": {
|
||||
header: {
|
||||
'Homepage': 'https://www.youzanyun.com/zanui',
|
||||
'PC': 'https://www.youzanyun.com/zanui/react',
|
||||
'Mobile': 'https://www.youzanyun.com/zanui/vue',
|
||||
'Weapp': 'https://github.com/youzan/zanui-weapp',
|
||||
'中文': '#/zh-CN'
|
||||
},
|
||||
footer: {
|
||||
github: 'https://github.com/youzan/vant',
|
||||
nav: {
|
||||
'Youzan': 'https://www.youzan.com/',
|
||||
'Join us': 'https://job.youzan.com/',
|
||||
'Feedback': 'https://github.com/youzan/vant/issues'
|
||||
}
|
||||
},
|
||||
nav: [
|
||||
{
|
||||
"name": "Essentials",
|
||||
"groups": [
|
||||
{
|
||||
"list": [
|
||||
{
|
||||
"path": "/quickstart",
|
||||
"title": "Getting Started",
|
||||
noExample: true
|
||||
},
|
||||
{
|
||||
"path": "/changelog",
|
||||
"title": "Changelog",
|
||||
noExample: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
// translation needed
|
||||
{
|
||||
"name": "Components",
|
||||
"showInMobile": true,
|
||||
"groups": [
|
||||
{
|
||||
"groupName": "Base Components",
|
||||
"list": [
|
||||
{
|
||||
"path": "/layout",
|
||||
"title": "Layout"
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -2,9 +2,9 @@ import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import App from './ExamplesApp';
|
||||
import routes from './router.config';
|
||||
import { setLang } from './utils/lang';
|
||||
import Vant, { Lazyload } from 'packages';
|
||||
import ZanDoc from 'zan-doc';
|
||||
import DemoList from './components/demo-list';
|
||||
import 'packages/vant-css/src/index.css';
|
||||
import 'zan-doc/src/helper/touch-simulator';
|
||||
|
||||
@@ -16,22 +16,18 @@ Vue.use(Lazyload, {
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const routesConfig = routes(true);
|
||||
routesConfig.push({
|
||||
path: '/',
|
||||
component: DemoList
|
||||
});
|
||||
const router = new VueRouter({
|
||||
mode: 'hash',
|
||||
base: '/zanui/vue/examples',
|
||||
routes: routesConfig
|
||||
});
|
||||
|
||||
router.afterEach(() => {
|
||||
router.afterEach((route) => {
|
||||
const container = document.querySelector('.examples-container');
|
||||
if (container) {
|
||||
document.querySelector('.examples-container').scrollTop = 0;
|
||||
}
|
||||
window.syncPath();
|
||||
setLang(route.meta.lang);
|
||||
});
|
||||
|
||||
window.vueRouter = router;
|
||||
|
@@ -3,16 +3,12 @@ import VueRouter from 'vue-router';
|
||||
import App from './ExamplesDocsApp';
|
||||
import routes from './router.config';
|
||||
import ZanDoc from 'zan-doc';
|
||||
import isMobile from './is-mobile';
|
||||
import isMobile from './utils/is-mobile';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(ZanDoc);
|
||||
|
||||
const routesConfig = routes();
|
||||
routesConfig.push({
|
||||
path: '/',
|
||||
redirect: '/component/quickstart'
|
||||
});
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'hash',
|
||||
@@ -30,7 +26,7 @@ router.beforeEach((route, redirect, next) => {
|
||||
|
||||
router.afterEach(() => {
|
||||
window.scrollTo(0, 0);
|
||||
window.syncPath();
|
||||
Vue.nextTick(() => window.syncPath());
|
||||
});
|
||||
|
||||
window.vueRouter = router;
|
||||
|
@@ -1,43 +1,67 @@
|
||||
import docConfig from './doc.config';
|
||||
import { getLang } from './utils/lang';
|
||||
import DemoList from './components/demo-list';
|
||||
import componentDocs from '../examples-dist/entry-docs';
|
||||
import componentDemos from '../examples-dist/entry-demos';
|
||||
import './iframe-router';
|
||||
|
||||
const navs = docConfig['zh-CN'].nav;
|
||||
import './utils/iframe-router';
|
||||
|
||||
const registerRoute = (isExample) => {
|
||||
const route = [{
|
||||
path: '/',
|
||||
redirect: to => {
|
||||
return `/${getLang()}/`;
|
||||
}
|
||||
}, {
|
||||
path: '*',
|
||||
redirect: '/'
|
||||
redirect: to => {
|
||||
return `/${getLang()}/`;
|
||||
}
|
||||
}];
|
||||
|
||||
navs.forEach(nav => {
|
||||
if (isExample && !nav.showInMobile) {
|
||||
return;
|
||||
Object.keys(docConfig).forEach((lang, index) => {
|
||||
if (isExample) {
|
||||
route.push({
|
||||
path: `/${lang}`,
|
||||
component: DemoList,
|
||||
meta: { lang }
|
||||
});
|
||||
} else {
|
||||
route.push({
|
||||
path: `/${lang}`,
|
||||
redirect: `/${lang}/component/quickstart`
|
||||
});
|
||||
}
|
||||
|
||||
if (nav.groups) {
|
||||
nav.groups.forEach(group => {
|
||||
group.list.forEach(addRoute);
|
||||
});
|
||||
} else if (nav.children) {
|
||||
nav.children.forEach(addRoute);
|
||||
} else {
|
||||
addRoute(nav);
|
||||
const navs = docConfig[lang].nav || [];
|
||||
navs.forEach(nav => {
|
||||
if (isExample && !nav.showInMobile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nav.groups) {
|
||||
nav.groups.forEach(group => {
|
||||
group.list.forEach(page => addRoute(page, lang));
|
||||
});
|
||||
} else if (nav.children) {
|
||||
nav.children.forEach(page => addRoute(page, lang));
|
||||
} else {
|
||||
addRoute(nav, lang);
|
||||
}
|
||||
});
|
||||
|
||||
function addRoute(page, lang) {
|
||||
const { path } = page;
|
||||
if (path) {
|
||||
const name = lang + '/' + path.replace('/', '');
|
||||
route.push({
|
||||
path: `/${lang}/component${path}`,
|
||||
component: isExample ? componentDemos[name] : componentDocs[name],
|
||||
meta: { lang }
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function addRoute(page) {
|
||||
const { path } = page;
|
||||
if (path) {
|
||||
const name = path.replace('/', '');
|
||||
route.push({
|
||||
path: '/component' + path,
|
||||
component: isExample ? componentDemos[name] : componentDocs[name]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return route;
|
||||
};
|
||||
|
||||
|
@@ -3,6 +3,8 @@
|
||||
*/
|
||||
|
||||
import isMobile from './is-mobile';
|
||||
import { setLang } from './lang';
|
||||
import { iframeReady } from './iframe';
|
||||
|
||||
window.syncPath = function(dir) {
|
||||
const router = window.vueRouter;
|
||||
@@ -17,25 +19,13 @@ window.syncPath = function(dir) {
|
||||
}
|
||||
};
|
||||
|
||||
window.changePath = function(path) {
|
||||
window.changePath = function(path = '') {
|
||||
const pathParts = path.split('/');
|
||||
let lang = pathParts[0];
|
||||
if (path[0] === '/') {
|
||||
lang = pathParts[1];
|
||||
}
|
||||
|
||||
setLang(lang);
|
||||
window.vueRouter.replace(path);
|
||||
};
|
||||
|
||||
function iframeReady(iframe, callback) {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const interval = () => {
|
||||
if (iframe.contentWindow.changePath) {
|
||||
callback();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
interval();
|
||||
}, 50);
|
||||
}
|
||||
};
|
||||
|
||||
if (doc.readyState === 'complete') {
|
||||
interval();
|
||||
} else {
|
||||
iframe.onload = interval;
|
||||
}
|
||||
}
|
18
docs/src/utils/iframe.js
Normal file
18
docs/src/utils/iframe.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export function iframeReady(iframe, callback) {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const interval = () => {
|
||||
if (iframe.contentWindow.changePath) {
|
||||
callback();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
interval();
|
||||
}, 50);
|
||||
}
|
||||
};
|
||||
|
||||
if (doc.readyState === 'complete') {
|
||||
interval();
|
||||
} else {
|
||||
iframe.onload = interval;
|
||||
}
|
||||
}
|
16
docs/src/utils/lang.js
Normal file
16
docs/src/utils/lang.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const userLang = window.localStorage.getItem('VANT_LANGUAGE') || window.navigator.language || 'en-US';
|
||||
let defaultLang = 'en-US';
|
||||
if (userLang.indexOf('zh-') !== -1) {
|
||||
defaultLang = 'zh-CN';
|
||||
}
|
||||
|
||||
let currentLang = defaultLang;
|
||||
|
||||
export function getLang() {
|
||||
return currentLang;
|
||||
}
|
||||
|
||||
export function setLang(lang) {
|
||||
window.localStorage.setItem('VANT_LANGUAGE', lang);
|
||||
currentLang = lang;
|
||||
}
|
Reference in New Issue
Block a user