Skip to content

Commit

Permalink
[new feature] add i18n support (youzan#310)
Browse files Browse the repository at this point in the history
* fix: Tabbar icon line-height

* [new feature] progress add showPivot prop

* [new feature] TabItem support vue-router

* [new feature] update document header style

* [Doc] add toast english ducoment

* [new feature] add i18n support

* feat: Extract demos from markdown

* feat: Base components demos

* [new feature] complete demo extract & translate

* [fix] text cases

* fix: add deepAssign test cases

* fix: changelog detail

* [new feature] AddressEdit support i18n
  • Loading branch information
chenjiahan authored Nov 16, 2017
1 parent 05abf0d commit d8b6ad7
Show file tree
Hide file tree
Showing 210 changed files with 5,563 additions and 5,530 deletions.
8 changes: 4 additions & 4 deletions .github/CONTRIBUTING.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ npm run dev

- 仓库的组件代码都位于 `packages` 下,每个组件一个文件夹
- 所有的组件样式代码都位于 `packages/vant-css/src` 下,`vant-css` 也会在发布时单独发包
- `docs/examples-docs` 目录下是文档网站的代码,根据语言划分为 zh-CN 和 en-US,本地开发时可以在根目录下运行 `npm run dev` 开启文档网站。
- `docs/markdown` 目录下是文档网站的代码,根据语言划分为 zh-CN 和 en-US,本地开发时可以在根目录下运行 `npm run dev` 开启文档网站。

项目目录大致如下:

Expand Down Expand Up @@ -58,7 +58,7 @@ vant

- 添加文档

新组件的文档编写,需要在 `docs/examples-docs` 下各个语言中新建对应同名文档 `button.md`,并在 `docs/src/doc.config.js` 中进行文档路径配置
新组件的文档编写,需要在 `docs/markdown` 下各个语言中新建对应同名文档 `button.md``docs/demos` 下创建组件示例,并在 `docs/src/doc.config.js` 中进行配置组件名称

- 添加测试代码

Expand All @@ -67,7 +67,7 @@ vant

## 组件文档如何编写

`docs/examples-docs` 下根据语言划分了组件文档,每个组件需要在各语言中编辑对应的文档。组件文档采用 markdown 格式,内容包括使用示例以及 `API` 等。具体书写规范请参考 [组件文档书写规范](./MARKDOWN.md)
`docs/markdown` 下根据语言划分了组件文档,每个组件需要在各语言中编辑对应的文档。组件文档采用 markdown 格式,内容包括使用示例以及 `API` 等。具体书写规范请参考 [组件文档书写规范](./MARKDOWN.md)

#### API 说明

Expand Down Expand Up @@ -106,5 +106,5 @@ vant
`z-index` 优先级(从高到低):

* 特殊组件:Toast 永远在最上面,[3000, +∞)
* ‘用完就关’ 的组件:Dialog, Pop, Actionsheet, image-preview[2000, 3000)
* ‘用完就关’ 的组件:Dialog, Pop, Actionsheet, ImagePreview[2000, 3000)
* 其他:组件内部用来控制层次的 z-index 的区间 [-10, 10],尽可能写小,一般1,2,3这种就够了。
38 changes: 17 additions & 21 deletions .github/MARKDOWN.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,24 @@

#### 代码演示

另起一个二级标题,正文可以是 markdown 和示例的混合。示例的结构如下:

// 在 demo 端之外放置的 script 会直接运行
// 在 script 中声明的 vue 变量,在 demo 中都可以直接使用
<script>
export default {
data() {
return {
size: 'large'
};
}
};
</script>
另起一个二级标题,示例的结构如下:

```javascript
export default {
data() {
return {
size: 'large'
};
}
};
```

```html
<van-button :size="size">
Large
</van-button>
```

:::demo 基础用法(必须以:::demo开头,后面的描述可选,如果有的话控制在一两句话,不要过长)
```html // :::demo后面必须接代码段,否则不会识别为示例
<van-button :size="size"> // 在内容区直接写 vue 中 template 段代码即可
Large
</van-button>
```
::: // 示例结束的标记,必须接在代码段之后,否则不会识别为示例

代码演示的几个书写原则:

- 从简单用法开始介绍,不要上来就同时使用一大堆 API,会让人觉得难以上手
Expand Down
137 changes: 82 additions & 55 deletions build/bin/build-entry.js
Original file line number Diff line number Diff line change
@@ -1,83 +1,110 @@
var Components = require('./get-components')();
var fs = require('fs');
var render = require('json-templater/string');
var uppercamelcase = require('uppercamelcase');
var path = require('path');

var OUTPUT_PATH = path.join(__dirname, '../../packages/index.js');
var IMPORT_TEMPLATE = 'import {{name}} from \'./{{package}}\';';
var ISNTALL_COMPONENT_TEMPLATE = ' {{name}}';
var MAIN_TEMPLATE = `{{include}}
const version = '{{version}}';
const Components = require('./get-components')();
const fs = require('fs');
const path = require('path');
const uppercamelize = require('uppercamelcase');
const version = process.env.VERSION || require('../../package.json').version;
const tips = '// This file is auto gererated by build/bin/build-entry.js';

function buildVantEntry() {
const uninstallComponents = [
'Lazyload',
'Waterfall',
'Dialog',
'Toast',
'ImagePreview',
'Locale'
];

const importList = Components.map(name => `import ${uppercamelize(name)} from './${name}';`);
const exportList = Components.map(name => `${uppercamelize(name)}`);
const intallList = exportList.filter(name => !~uninstallComponents.indexOf(uppercamelize(name)));
const content = `${tips}
${importList.join('\n')}
const version = '${version}';
const components = [
{{components}}
${intallList.join(',\n ')}
];
const install = function(Vue) {
if (install.installed) return;
const install = Vue => {
components.forEach(component => {
Vue.component(component.name, component);
});
};
/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
export {
install,
version,
{{list}}
${exportList.join(',\n ')}
};
export default {
install,
version
};
`;

delete Components.font;
fs.writeFileSync(path.join(__dirname, '../../packages/index.js'), content);
}

var includeComponentTemplate = [];
var installTemplate = [];
var listTemplate = [];
function buildDemoEntry() {
const dir = path.join(__dirname, '../../docs/demos/views');
const demos = fs.readdirSync(dir)
.filter(name => ~name.indexOf('.vue'))
.map(name => name.replace('.vue', ''))
.map(name => `'${name}': r => require.ensure([], () => r(wrapper(require('./views/${name}'), '${name}')), '${name}')`);

Components.forEach(name => {
var componentName = uppercamelcase(name);
const content = `${tips}
import './common';
includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
name: componentName,
package: name
}));
function wrapper(component, name) {
component = component.default;
component.name = 'demo-' + name;
return component;
}
if ([
// directives
'Lazyload',
'Waterfall',
export default {
${demos.join(',\n ')}
};
`;
fs.writeFileSync(path.join(dir, '../index.js'), content);
}

// services
'Dialog',
'Toast',
'ImagePreview'
].indexOf(componentName) === -1) {
installTemplate.push(render(ISNTALL_COMPONENT_TEMPLATE, {
name: componentName,
component: name
}));
}

listTemplate.push(` ${componentName}`);
});

var template = render(MAIN_TEMPLATE, {
include: includeComponentTemplate.join('\n'),
list: listTemplate.join(',\n'),
components: installTemplate.join(',\n') || ' ',
version: process.env.VERSION || require('../../package.json').version
});

fs.writeFileSync(OUTPUT_PATH, template);
console.log('[build entry] DONE:' + OUTPUT_PATH);
function buildDocsEntry() {
const dir = path.join(__dirname, '../../docs/markdown');
const cnDocs = fs.readdirSync(path.join(dir, 'zh-CN')).map(name => 'zh-CN/' + name);
const enDocs = fs.readdirSync(path.join(dir, 'en-US')).map(name => 'en-US/' + name);
const docs = [...cnDocs, ...enDocs]
.filter(name => name.indexOf('.md') !== -1)
.map(name => name.replace('.md', ''))
.map(name => `'${name}': wrapper(r => require.ensure([], () => r(require('./${name}.md')), '${name}'))`);

const content = `${tips}
import progress from 'nprogress';
import 'nprogress/nprogress.css';
function wrapper(component) {
return function(r) {
progress.start();
component(r).then(() => {
progress.done();
}).catch(() => {
progress.done();
});
};
}
export default {
${docs.join(',\n ')}
};
`;
fs.writeFileSync(path.join(dir, './index.js'), content);
}

buildVantEntry();
buildDemoEntry();
buildDocsEntry();
19 changes: 1 addition & 18 deletions build/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,11 @@ const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const isProduction = process.env.NODE_ENV === 'production';
const docConfig = require('../docs/src/doc.config');
const { extractExample } = require('zan-doc/src/helper');
const styleLoaders = [
{ loader: 'css-loader' },
{ loader: 'postcss-loader', options: { sourceMap: true } }
];

// extract [components].vue from [components].md
extractExample({
src: path.resolve(__dirname, '../docs/examples-docs'),
dist: path.resolve(__dirname, '../docs/examples-dist'),
nav: docConfig,
watch: !isProduction
});

module.exports = {
entry: {
vendor: ['packages'],
Expand Down Expand Up @@ -89,15 +80,7 @@ module.exports = {
test: /\.md/,
loader: 'vue-markdown-loader',
options: {
preventExtract: true,
use: [[require('markdown-it-container'), 'demo']],
preprocess(MarkdownIt, source) {
const styleRegexp = /<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/i;
const scriptRegexp = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/i;
MarkdownIt.renderer.rules.table_open = () =>
'<table class="zan-doc-table">';
return source.replace(styleRegexp, '').replace(scriptRegexp, '');
}
preventExtract: true
}
},
{
Expand Down
72 changes: 72 additions & 0 deletions docs/demos/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Demo Common Mixin && i18n
*/

import Vue from 'vue';
import { Locale, Toast, Dialog } from 'packages';
import { DemoBlock, DemoSection } from 'vant-doc';
import camelize from 'packages/utils/camelize';

const demoBaseMixin = {
beforeCreate() {
const { name, i18n } = this.$options;
if (name && i18n) {
const formattedI18n = {};
const camelizedName = camelize(name);
Object.keys(i18n).forEach(key => {
formattedI18n[key] = { [camelizedName]: i18n[key] };
});
Locale.add(formattedI18n);
}
}
};

window.Toast = Toast;
window.Dialog = Dialog;
Vue.mixin(Locale.i18n);
Vue.mixin(demoBaseMixin);
Vue.component('demo-block', DemoBlock);
Vue.component('demo-section', DemoSection);

Locale.add({
'zh-CN': {
red: '红色',
orange: '橙色',
yellow: '黄色',
tab: '标签',
tag: '标签',
desc: '描述信息',
back: '返回',
title: '标题',
status: '状态',
button: '按钮',
option: '选项',
search: '搜索',
content: '内容',
custom: '自定义',
loading: '加载状态',
disabled: '禁用状态',
basicUsage: '基础用法',
advancedUsage: '高级用法'
},
'en-US': {
red: 'Red',
orange: 'Orange',
yellow: 'Yellow',
tab: 'Tab',
tag: 'Tag',
desc: 'Description',
back: 'Back',
title: 'Title',
status: 'Status',
button: 'Button',
option: 'Option',
search: 'Search',
content: 'Content',
custom: 'Custom',
loading: 'Loading',
disabled: 'Disabled',
basicUsage: 'Basic Usage',
advancedUsage: 'Advanced Usage'
}
});
Loading

0 comments on commit d8b6ad7

Please sign in to comment.