hexojs/hexo — pull request #831 by jkuetemeier (2014-09-06 23:51:52+00:00)
Title: 'lang' for every 'page' - next step to multilanguage sites

## What we already have...

we already have optional 'lang' values for posts, e.g.:

```
title: Test English Post
lang: en
---
# My first english post
```

you can create them with `hexo new "Test English Post" --lang en`

This will create a post in `source/_posts/en/Test-English-Post.md`

## What I missed and is in this pull request

The `page.lang` can be referenced in the template - but *only* if it is specified in a post or page. If not the value is undefined.

### More than one language for i18n.__

I use the `config.languages` value to specify more than one language - to be exactly, all the languages my blog use:

old (and still working) version:

```
config.language = de
```

new version (language can be an array):

```
config.language
- de
- en
```
the first language will be the *default* language, the i18n module will load all languages (and `default.yml`) as you can verify with `__.languages` in you theme.

There is no need to specify a `config.language`, if you omit it, there will be no difference to hexo's default behavior!

### Add an optional language parameter to __

The biggest problem was, that with the use of `__` in your theme, you could only get a translation to the actual configured language (`default.yml` or `config.language`). I preserve this functionality for backward compatibility, but now you can add an optional parameter to the function call: `__('code', 'language')`, where `language` may be one of the configured languages in `config.language` (array or string) or `default.yml`.

So now this works:

```
themes/your-theme/languages/de.yml
hello: Hallo Welt

themes/your-theme/languages/en.yml
hello: Hello World

__('hello', 'de') => "Hallo Welt"
__('hello', 'en') => "Hello World"

__('hello') => "Hallo Welt"
with config.languages
- de
- en

with no config.languages
default behavior, return code when no language file available
__('hello') => 'hello'

or use default.yml as fallback - 'foo' is no key in de.yml nor en.yml
themes/your-theme/languages/default.yml
foo: bar

__('foo') => 'bar'
__('foo', 'de') => 'foo'
```

### ensure a `lang` key with usefull values in `page`

I added an `ensureLanguage` function to `lib/theme/index.js` to always have a `lang` key in the `page` object.

Additional there are two new configuration values, both boolean:

`config.language_detect_in_path` (default, not defined, so false)
`config.language_detect_first_level` (default, not defined, so false)

The behavior is as follows:

1) if a post or a page sets the `lang` value, use this (so, we can always override it in posts or pages) - this is like the actual hexo default bevavior

new:
2) if no `page.lang` is defined, default it to `config.language` (if it is a single value) or the first language of the `config.language` array

3) if `config.language_detect_in_path` is defined, the function will try to detect a language string configured in `config.language` in the actual `page.path`. e.g. :

```
config.language
- de
- en

path: /en/contact/index.html
look for /en/ => found => page.lang = 'en'

path: /my/blog/en/contect.html
look for /en/ => found => page.lang = 'en'

path: /de/
look for /de/ => found => page.lang = 'de'

path: /
look for /de/ => not found
look for /en/ => not found
default was config.language[0] => page.lang = 'de'

this will even work for:

path: /contact/
=> page.lang = 'de'
```

4) now you can guess how `config.language_detect_first_level` will alter the lookup:

```
path: /en/contact
^/en/ => found => page.lang = 'en'

path: /my/blog/en/contact
config.language_detect_first_level = true
^/en/ => NOT FOUND, "first level" is here /my/ => page.lang = 'de' (because it's config.language[0])
```

When you set `config.language_detect_first_level` to `true` it will only search the first level string (bevor the second /, with the first / as root). This is for the paranoid people, which may have posts, pages, tag, directories, and so on with the same name as their configured languages. But then you *must* set `:lang` as the first value of your `config.permalink` (but this ist even a recommendation for google).

5) if no `lang` can be detected - not set in post or page, not in config, not in path - it will be filled with the default value `default` (to be compatible with `default.yml`)

so... there will *always* be a `page.lang` available in themes

## What can we do with this?

_config.yml:
```
language:
- de
- en
language_detect_in_path: true
language_detect_first_level: true
permalink_defaults:
lang: de
new_post_name: :lang/:title.md
permalink: /:lang/:title/
```

Have a blog with `/` or `/de/` (optional) as the german version and `/en/` as the english version.

Remember my last pull request for helper functions in hexo's `list` functions?

### Use this:

themes/your-theme/scripts/ extend.js
```
hexo.extend.helper.register('category_transform', function(helper, lang) {
console.log(lang);
var f = function(s) {
return helper('category_'+s, lang);
};
return f;
});
```

```

{{__('categories', page.lang)}}

{{ list_categories({
show_count: false,
style: "list",
class: "category",
transform: category_transform(__, page.lang)
}) }}

```
### Results

/de/hallo-welt/ (or even) /impressum/
```
[...]
Kategorien

- IT-Sicherheit
- Deutsch
```

/en/hello-world/ (or any other page under /en)
```
[...]
Categories

- IT-Security
- English
```

The next step to a fully multi-lingual site in hexo.

If it's ready, I'll write a full blog post and wiki page.