├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── README_zh.md
├── index.js
├── lib
├── helpers.js
└── i18n.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test/
2 | tmp/
3 | coverage/
4 | *.log
5 | .jshintrc
6 | .travis.yml
7 | gulpfile.js
8 | .idea/
9 | appveyor.yml
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node-js:
3 | - "4"
4 |
5 | cache:
6 | apt: true
7 | directories:
8 | - node_modules
9 |
10 | before_install:
11 | - npm install lodash --save
12 |
13 | script:
14 | - npm test
15 |
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Jamling
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hexo-generator-i18n
2 |
3 | [](https://travis-ci.org/Jamling/hexo-generator-i18n)
4 | [](https://www.npmjs.com/package/hexo-generator-i18n)
5 | [](https://www.npmjs.com/package/hexo-generator-i18n)
6 | [](https://www.npmjs.com/package/hexo-generator-i18n)
7 | [](https://github.com/Jamling/hexo-generator-i18n/releases/latest)
8 |
9 | Multi-languages pages generator for [Hexo]. Here is a [demo](https://www.ieclipse.cn/other/)
10 |
11 | ## Installation
12 |
13 | ``` bash
14 | $ npm install hexo-generator-i18n --save
15 | ```
16 |
17 | ## Config
18 |
19 | _config.yml
20 | ``` yaml
21 | # hexo default is empty, change to exact languages, and add xx.yml under your theme languages dir.
22 | language: [zh,en]
23 | # config hexo-generator-i18n option (optional, this is default option)
24 | i18n:
25 | type: [page, post]
26 | generator: [archive, category, tag, index]
27 | ```
28 |
29 | - **type**: What type of model to be i18n generated
30 | - page: All page model under source
31 | - post: All post model under source/_post
32 | - **generator**: Which generator to be i18n generated, it's array of your installed generator names.
33 | - archive: Generate i18n archive page
34 | - category: Generate i18n category page
35 | - tag: Generate i18n tag page
36 | - index: Generate i18n index page
37 |
38 | ***And add xx.yml (such as zh.yml, en.yml) under your themes languages directory***
39 |
40 | Set language display name in source/_data/languages.yml (Optional)
41 | ```yaml
42 | zh: 简体中文
43 | en: English
44 | ```
45 |
46 | ## Order
47 |
48 | Make sure this plugin after other generators in dependencies of package.json
49 |
50 | ## Usage
51 | You may need to replace `url_for()` to `url_for_lang()` to output link under current language. Usually, in post/archive/tag/category pages.
52 |
53 | ## Helpers
54 |
55 | ### get_langs
56 | Return array of config languages, exclude `default` language
57 |
58 | ### default_lang
59 | Return default language, it's the first element of [get_langs](#get_langs)
60 |
61 | ### switch_lang
62 | Return the absolute url under lang
63 | ```js
64 | window.location = {{ switch_lang('en')}}
65 | ```
66 |
67 | ### url_for_lang
68 | Return url for path with language
69 | ```js
70 | ret += '
';
71 | this.site.tags.forEach(function(item){
72 | ret += '- ' + item.name + '
';
73 | });
74 | ret += '
';
75 | ```
76 |
77 | ## License
78 |
79 | MIT
80 |
81 | [Hexo]: http://hexo.io/
82 |
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
1 | # 简介
2 |
3 | [](https://travis-ci.org/Jamling/hexo-generator-i18n)
4 | [](https://www.npmjs.com/package/hexo-generator-i18n)
5 | [](https://www.npmjs.com/package/hexo-generator-i18n)
6 | [](https://www.npmjs.com/package/hexo-generator-i18n)
7 | [](https://github.com/Jamling/hexo-generator-i18n/releases/latest)
8 |
9 | [Hexo]国际化站点生成插件.
10 |
11 | ## 安装
12 |
13 | ``` bash
14 | $ npm install hexo-generator-i18n --save
15 | ```
16 |
17 | ## 设置
18 |
19 | _config.yml
20 | ``` yaml
21 | # 需修改Hexo默认的空值为确切的语言列表,记得在主题languages目录下添加对应的语言.yml文件
22 | language: [zh,en]
23 | # hexo-generator-i18n 选项(可选,默认使用如下设置)
24 | i18n:
25 | type: [page, post]
26 | generator: [archive, category, tag, index]
27 | ```
28 |
29 | - **type**: 想要生成国际化页面类型
30 | - page: source目录下的所有page页面
31 | - post: source/_post目录下所有的post页面
32 | - **generator**: 设置需要国际化的其它生成器。
33 | - archive: 生成国际化归档页
34 | - category: 生成国际化分类页
35 | - tag: 生成国际化标签页
36 | - index: 生成国际化首页
37 |
38 | ***在主题languages目录下添加对应的语言.yml文件(如zh.yml, en.yml)***
39 |
40 | 在source/_data/languages.yml中设置语言的显示名称(Optional)
41 | ```yaml
42 | zh: 简体中文
43 | en: English
44 | ```
45 |
46 | ## 顺序
47 |
48 | 确保i18n插件位于package.json中dependencies配置的其它生成插件之后。不然有可能导致找不到生成器错误。
49 |
50 | ## 使用
51 | 在您的主题中,您可能需要将`url_for()`辅助函数替换为`url_for_lang()`以确保点击链接时,仍然保持当前用户所选的语言。
52 |
53 | ## 辅助函数
54 |
55 | ### get_langs
56 | - Return Array
57 |
58 | 返回配置的语言列表(数组), 返回的列表不包含`default`。
59 |
60 | ### default_lang
61 | - Return string
62 |
63 | 返回默认语言,为[get_langs](#get_langs)的第一项。
64 |
65 | ### switch_lang
66 | - Param lang
67 | - Return string
68 | 返回其它语言下的当前页面URL。
69 | ```js
70 | window.location = {{ switch_lang('en')}}
71 | ```
72 |
73 | ### url_for_lang
74 | - Param path
75 | - Param lang
76 | - Return string
77 |
78 | 返回带语言的路径URL
79 | ```js
80 | ret += '';
81 | this.site.tags.forEach(function(item){
82 | ret += '- ' + item.name + '
';
83 | });
84 | ret += '
';
85 | ```
86 |
87 | ## License
88 |
89 | MIT
90 |
91 | [Hexo]: http://hexo.io/
92 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _ = require('lodash');
4 |
5 | //------------> Init langs
6 | if (!hexo.config.i18n) {
7 | hexo.log.info('i18n not config in _config.yml, use default config!\nPlease visit https://github.com/Jamling/hexo-generator-i18n for more information');
8 | hexo.config.i18n = {
9 | type: ["page", "post"],
10 | generator: ["archive", "category", "tag", "index"]
11 | }
12 | }
13 | if (!hexo.config.i18n.languages){
14 | var languages = hexo.config.language;
15 | if(!Array.isArray(languages)){
16 | languages = [languages];
17 | }
18 | _.pull(languages, 'default');
19 | hexo.config.i18n.languages = languages;
20 | }
21 |
22 | //------------> Helper
23 | var helper = require('./lib/helpers');
24 |
25 | hexo.extend.helper.register('get_langs', helper.langs);
26 | hexo.extend.helper.register('default_lang', helper.defaultLang);
27 | hexo.extend.helper.register('switch_lang', helper.switchLang);
28 | hexo.extend.helper.register('url_for_lang', helper.url);
29 |
30 | //------------> Generator
31 | var i18n = require('./lib/i18n');
32 | var type = hexo.config.i18n.type;
33 | if (type){
34 | if(!Array.isArray(type)){
35 | type = [type];
36 | }
37 |
38 | type.forEach(function(item){
39 | if (item == 'page') {
40 | hexo.extend.generator.register('page-i18n', i18n.page);
41 | }
42 | else if (item == 'post') {
43 | hexo.extend.generator.register('post-i18n', i18n.post);
44 | }
45 | });
46 | }
47 |
48 | var generator = hexo.config.i18n.generator;
49 | if (generator){
50 | if(!Array.isArray(generator)){
51 | generator = [generator];
52 | }
53 | hexo.config.i18n.generator = generator;
54 |
55 | var gs = hexo.extend.generator.list();
56 | generator.forEach(function(item){
57 | var g = gs[item];
58 | if (!g && !_.endsWith(item, '-i18n')){
59 | _.pull(hexo.config.i18n.generator, item);
60 | }
61 | });
62 | hexo.extend.generator.register('other-i18n', i18n.archive);
63 | }
64 |
--------------------------------------------------------------------------------
/lib/helpers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _ = require('lodash');
4 |
5 | function getLanguages(){
6 | var langs = this.config.i18n.languages;
7 | if (!langs){
8 | var languages = hexo.config.language;
9 | if(!Array.isArray(languages)){
10 | languages = [languages];
11 | }
12 | _.pull(languages, 'default');
13 | this.config.i18n.languages = languages;
14 | }
15 | return this.config.i18n.languages;
16 | }
17 |
18 | function defaultLang(){
19 | return this.get_langs()[0];
20 | }
21 |
22 | function switchLang(lang){
23 | var l = this.get_langs();
24 | var p = this.page.path;
25 | var root = this.config.root || '';
26 | if (_.startsWith(p, this.page.lang)) {
27 | p = p.substring(this.page.lang.length);
28 | }
29 | if (!_.startsWith(p, '/')){
30 | p = '/' + p;
31 | }
32 | var ret = '';
33 | if (l.indexOf(lang) == 0) {
34 | ret = root + p.substring(1);
35 | } else {
36 | ret = root + lang + p;
37 | }
38 | return ret;
39 | }
40 |
41 | function i18n_url(path, language) {
42 | var root = this.config.root || '';
43 | var lang = language ? language : this.page.lang;
44 | var url = this.url_for(path);
45 |
46 | // ignore from url_for.
47 | if (url === '#' || _.startsWith(url, '//') || _.includes(url, '://') || _.startsWith(url, 'mailto:')) {
48 | return url;
49 | }
50 |
51 | if (!_.startsWith(url, '/')){
52 | url = '/' + url;
53 | }
54 |
55 | var relativeUrl = url.replace(root, '/');
56 | var pathLang = relativeUrl.split('/')[1];
57 | var languages = this.get_langs();
58 |
59 | if (languages.indexOf(pathLang) != -1){
60 | return url;
61 | }
62 |
63 | if (lang && lang !== languages[0]){
64 | url = root + lang + relativeUrl;
65 | }
66 |
67 | return url;
68 | }
69 |
70 | exports.langs = getLanguages;
71 | exports.defaultLang = defaultLang;
72 | exports.switchLang = switchLang;
73 | exports.url = i18n_url;
74 |
--------------------------------------------------------------------------------
/lib/i18n.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _ = require('lodash');
4 |
5 | function getLang(hexo){
6 | if (!hexo.config.i18n.languages){
7 | var languages = hexo.config.language;
8 | if(!Array.isArray(languages)){
9 | languages = [languages];
10 | }
11 | _.pull(languages, 'default');
12 | hexo.config.i18n.languages = languages;
13 | }
14 | return hexo.config.i18n.languages;
15 | }
16 |
17 | function i18n_page(locals) {
18 | var _self = this;
19 | var languages = getLang(_self);
20 |
21 | var i18n = [];
22 | var langPaths = [];
23 |
24 | locals.pages.forEach(function(page){
25 | var lang = page.path.split('/')[0];
26 | if (languages.indexOf(lang) == -1){
27 | i18n.push(page);
28 | } else {
29 | langPaths.push(page.path);
30 | page.lang = lang;
31 | }
32 | });
33 |
34 | var result = [];
35 | i18n.forEach(function(page){
36 | var layouts = ['page', 'post', 'index'];
37 | var layout = page.layout;
38 | for (var i = 1; i< languages.length; i++){
39 | var l = languages[i];
40 | var path = l + '/' + page.path;
41 | if (langPaths.indexOf(path) != -1){
42 | continue;
43 | }
44 | if (!layout || layout === 'false' || layout === 'off') {
45 | result.push({path: path, data: page.content});
46 | }
47 | else {
48 | if (layout !== 'page') layouts.unshift(layout);
49 | var copy = {};
50 | if (languages.length <= 2) {
51 | _.extend(copy, page);
52 | } else {
53 | _.defaults(copy, page);
54 | }
55 | copy.lang = l;
56 | copy.__page = true;
57 | copy.path = path;
58 | _self.log.debug("generate i18n page " + copy.path);
59 | result.push({
60 | path: copy.path,
61 | layout: layouts,
62 | data: copy
63 | });
64 | }
65 | }
66 | });
67 | return result.length > 0 ? result : [];
68 | };
69 |
70 | function i18n_post(locals) {
71 | var _self = this;
72 | var languages = getLang(_self);
73 |
74 | var i18n = [];
75 | var langPaths = [];
76 |
77 | locals.posts.forEach(function(page){
78 | var lang = page.path.split('/')[0];
79 | if (languages.indexOf(lang) == -1){
80 | i18n.push(page);
81 | }else {
82 | langPaths.push(page.path);
83 | page.lang = lang;
84 | }
85 | });
86 |
87 | var result = [];
88 | i18n.forEach(function(page){
89 | var layouts = ['post', 'page', 'index'];
90 | var layout = page.layout;
91 | for (var i = 1; i< languages.length; i++){
92 | var l = languages[i];
93 | var path = l + '/' + page.path;
94 | if (langPaths.indexOf(path) != -1){
95 | continue;
96 | }
97 | if (!layout || layout === 'false' || layout === 'off') {
98 | result.push({path: path, data: page.content});
99 | }
100 | else {
101 | if (layout !== 'post') layouts.unshift(layout);
102 | var copy = {};
103 | if (languages.length <= 2) {
104 | _.extend(copy, page);
105 | } else {
106 | _.defaults(copy, page);
107 | }
108 | copy.lang = l;
109 | copy.__post = true;
110 | copy.path = path;
111 | _self.log.debug("generate i18n post " + copy.path);
112 | result.push({
113 | path: copy.path,
114 | layout: layouts,
115 | data: copy
116 | });
117 | }
118 | }
119 | });
120 | return result.length > 0 ? result : [];
121 | };
122 |
123 | function i18n_archive(locals) {
124 | var _self = this;
125 | var languages = getLang(_self);
126 |
127 | var i18n = [];
128 | var langPaths = [];
129 |
130 | var result = [];
131 | var gs = _self.extend.generator.list();
132 | var siteLocals = _self.locals.toObject();
133 |
134 | this.config.i18n.generator.forEach(function(g){
135 | var datas = gs[g].call(_self, siteLocals).then(function(data){
136 | _self.log.debug('i18n for generator: %s', g);
137 | return data;
138 | })
139 | .reduce(function(result, data) {
140 | return data ? result.concat(data) : result;
141 | }, [])
142 | .map(function(item){
143 | for (var i = 1; i< languages.length; i++){
144 | var l = languages[i];
145 |
146 | var copy = {};
147 | if (languages.length <= 2) {
148 | _.extend(copy, item);
149 | } else {
150 | _.defaultsDeep(copy, item);
151 | }
152 | copy.path = l + '/' + item.path;
153 | copy.data.base = l + '/' + item.data.base;
154 | copy.data.prev_link = l + '/' + item.data.prev_link;
155 | copy.data.current_url = l + '/' + item.data.current_url;
156 | copy.data.next_link = l + '/' + item.data.next_link;
157 | //console.log(copy);
158 | result.push(copy);
159 | }
160 | });
161 | });
162 | return result;
163 | };
164 |
165 | exports.page = i18n_page;
166 | exports.post = i18n_post;
167 | exports.archive = i18n_archive;
168 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hexo-generator-i18n",
3 | "version": "0.0.10",
4 | "description": "Multi-language pages generator for Hexo.",
5 | "main": "index",
6 | "directories": {
7 | "lib": "./lib"
8 | },
9 | "engines": {
10 | "node": ">= 0.10.0"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/Jamling/hexo-generator-i18n.git"
15 | },
16 | "homepage": "http://ieclipse.cn/",
17 | "keywords": [
18 | "hexo",
19 | "generator",
20 | "i18n"
21 | ],
22 | "author": {
23 | "name": "Jamling",
24 | "email": "li.jamling@gmail.com",
25 | "url": "http://ieclipse.cn"
26 | },
27 | "license": "MIT",
28 | "devDependencies": {},
29 | "dependencies": {
30 | "lodash": "^4.15.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------