Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Multilingual support in Hugo. #1734

Closed
wants to merge 5 commits into from
Closed

Conversation

abourget
Copy link
Contributor

The goal is to:

  • have support to write content/post/whatever.en.md to /en/2015/12/22/whatever/index.html
  • do the same with whatever.fr.md into /fr/2015/12/22
  • enable or disable with the "Multilingual" switch in the config
  • support having language switchers in templates, that know
    where the translated page is (with .Page.Translations)
    (when you're on /en/about/, there's a "Francais" link
    that points to /fr/a-propos)

Missing:

  • example usage of the .Page.Translations feature.
  • shortcode or function to do i18n of strings, without resorting to:
    {{ ( index $.Site.Data.translations $.Site.Params.locale ).topslogan }}
    • I'd like it to be based on data / JSON / YAML, but use a convention.
    • https://github.com/nicksnyder/go-i18n seems like a good candidate
      to do server-side translation if we want all the bells and whistles
      of a translation engine.. Has tooling to build translation workflows
      too.
  • how to run it with two configs (one for each language), yet having
    something that writes the root "index.html" and redirect to something
    that either considers the users' browser, or has a fixed language
    redirection.

This is basic code to open up a discussion as to how best we could handle that.

I was thinking of exposing more Translations functionalities directly in the .Site, to crawl through one set of pages (instead of looping through all Pages, even though some are duplicated). Something like BaseLanguagePages ? and all those Page would have pointers to their translations.

We could run the generation once for each language, and each time generating a /fr, or /en subtree.. consistent in itself.. and knowing about "the other side" of translations (through .Page.Translations). In that case, maybe Site.Pages could have only those of the current translation,
but .Page.Translations would know of the others.. that might simplify menu generation and all, as it
would never need to know about other translations, unless explicitly asked to.

So @spf13, what do you think of all that ?

@CLAassistant
Copy link

CLAassistant commented Dec 29, 2015

CLA assistant check
All committers have signed the CLA.

if p.lang == "" {
return outfile
}
return p.lang + "/" + outfile
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use filepath.Join here -- to get the filepath separator correct

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filepath uses an OS dependent path.. is that what we want here ? thought it was destined to build URLs.. I'll have to look into more details how URLs are generated.

What do you think of the general idea though ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On line 940, we take a url-style path and convert it to an OS dependent path with filepath.FromSlash. filepath.FromSlash will convert posts/index.html to posts\index.html on Windows.

@bep is correct. In this context, you want to use filepath.Join.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the general approach.
On Tue, Dec 29, 2015 at 11:11 PM Cameron Moore notifications@github.com
wrote:

In hugolib/page.go
#1734 (comment):

@@ -937,5 +953,12 @@ func (p *Page) TargetPath() (outfile string) {
outfile = helpers.ReplaceExtension(p.Source.LogicalName(), p.Extension())
}

  • return filepath.Join(strings.ToLower(helpers.MakePath(p.Source.Dir())), strings.TrimSpace(outfile))
  • return p.addLangPrefix(filepath.Join(strings.ToLower(helpers.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
    +}

+func (p *Page) addLangPrefix(outfile string) string {

  • if p.lang == "" {
  •   return outfile
    
  • }
  • return p.lang + "/" + outfile

On line 940, we take a url-style path and converts it to an OS dependent
path with filepath.FromSlash. filepath.FromSlash will convert
posts/index.html to posts\index.html on Windows.

@bep https://github.com/bep is correct. In this context, you want to
use filepath.Join.


Reply to this email directly or view it on GitHub
https://github.com/spf13/hugo/pull/1734/files#r48586001.

@abourget
Copy link
Contributor Author

@spf13 I was also looking at https://godoc.org/github.com/nicksnyder/go-i18n/i18n ...wouldn't it be awesome if, in addition to good support for i18n in terms of content, we had great translation capabilities?

We could load i18n/{{$.Site.RenderLanguage}}.yaml as an go-i18n translation on start, just like we load .Site.Data.. This would require Multilingual to be enabled.

I can see quite a nice mapping from the examples there to something Hugo-ish:

T("Hello {{.Person}}", map[string]interface{}{
    "Person": "Bob",
})

to something like {{ i18n "Hello {{.Person}}" (dict "Person" "Bob") }} or something like that.

Or T("You have {{.Count}} unread emails.", 2) -> {{ i18n "You.. {{.Count}} unread mails" .Site.MailCountWhatever }}

I'd add a native i18n function, or T (or both, one aliasing the other ?) to handle that.

What do you think ? What's your take on vendoring other libraries ?

@spf13
Copy link
Contributor

spf13 commented Dec 31, 2015

I like this idea a lot. I think it should be an independent commit/branch
though. Same goals, different functionality.
On Thu, Dec 31, 2015 at 1:41 AM Alexandre Bourget notifications@github.com
wrote:

@spf13 https://github.com/spf13 I was also looking at
https://godoc.org/github.com/nicksnyder/go-i18n/i18n ...wouldn't it be
awesome if, in addition to good support for i18n in terms of content, we
had great translation capabilities?

We could load i18n/{{$.Site.RenderLanguage}}.yaml as an go-i18n
translation on start, just like we load .Site.Data.. This would require
Multilingual to be enabled.

I can see quite a nice mapping from the examples there to something
Hugo-ish:

T("Hello {{.Person}}", map[string]interface{}{
"Person": "Bob",
})

to something like {{ i18n "Hello {{.Person}}" (dict "Person" "Bob") }} or
something like that.

Or T("You have {{.Count}} unread emails.", 2) -> {{ i18n "You..
{{.Count}} unread mails" .Site.MailCountWhatever }}

I'd add a native i18n function, or T (or both, one aliasing the other ?)
to handle that.

What do you think ? What's your take on vendoring other libraries ?


Reply to this email directly or view it on GitHub
#1734 (comment).

@bep
Copy link
Member

bep commented Dec 31, 2015

Also see golang/go#12750

@abourget
Copy link
Contributor Author

abourget commented Jan 3, 2016

@spf13 this PR is becoming more robust and more solidly tested.

It'd be great to merge in at some point.. can anyone review ? @bep or someone?

I've taken out the fixups and unrelated doc changes ... created other PRs for that.

@abourget
Copy link
Contributor Author

abourget commented Jan 3, 2016

Oh I thought appveyor was broken for other reasons, but Multilingual mode breaks under Windows.. will fix and push again.

@abourget
Copy link
Contributor Author

abourget commented Jan 5, 2016

@spf13 do you agree with the general approach ? Think it can be merged eventually ?

@spf13
Copy link
Contributor

spf13 commented Jan 5, 2016

Totally. Well done so far.

@abourget abourget changed the title Very first draft at a more native Multilingual support in Hugo. Native Multilingual support in Hugo. Jan 6, 2016
@abourget
Copy link
Contributor Author

abourget commented Jan 7, 2016

ok, this should be ready. works on Windows / Linux.. anything missing that you see ?

@spf13
Copy link
Contributor

spf13 commented Jan 7, 2016

This is very VERY good.

In reviewing I had a few ideas in ways we can further refine this so that it's a bit easier to use.

  1. It would be nice to be able to migrate from a single language to a multilanguage without needing to go in and modify the filenames of content files. Would it make sense that the defaultLanguage is the file without a language extension and we tell it which one that is.
  2. Another idea is the concept of a base for the content. I think it would make a lot of sense to have translations still share a lot of the metadata with the original piece of content.
  3. I think it would make a lot of sense for taxonomies (and possibly categories) to have translations for them which would allow content to easily keep in sync.

I would think perhaps the best accomplish to do this would be the following approach.

Each piece of content has a base file. This is the one without a language extension. It would specify the metadata and content that each enabled language would use when rendering if the given metadata or content wasn't present for that specific language. For things like categories and taxonomy it would mean that you would only need to specify them in the base and apply the translation to the terms. This would keep the content nicely in sync.

I believe that with translations that usually the case that a partially translated site is better than a partial site. By taking this approach any content that isn't yet translated will still be available, but in the base language.

While in my personal sites I have little experience with this and in particular the subtleties of the actual translation, I was responsible for the documentation of both MongoDB and Docker and we dealt with this a lot there. This follows more closely the approach we took with those sites.

Alternatively if you wish to keep the content independent you could just not provide the base and this approach would still work.

Does this make sense? I'm happy to merge the existing code and discuss this further as we look to implement this further.

@anthonyfok
Copy link
Member

Dear @abourget,

Thank you so much, really, from the bottom of my heart, for your bravery and devotion in tackling multilingual support for Hugo. Indeed, after seeing the multilingual efforts at Drupal, especially how it has evolved in Drupal 7, and now in D8MI (Drupal 8 Multilingual Initiative), I came to the appreciation how immense and complicated this subject can be, and I had been shying away from it. Thank you @abourget for stepping forward and facing the challenge head on, and thank you for your thoughtfulness and extensive work that have gone into it thus far.

There are several use cases that I am personally interested in about Hugo's multilingual initiative. For example, Hugo's multilingual support should:

  1. allow an extensive multilingual site with 35+ languages like http://www.debian.org/, with mechanism to allow translators to easily see changes between revisions of the source language (English). (www.debian.org has been using the venerable Website Meta Language (WML) as its static site generator with CVS as its main website infrastructure since around 1997. It still works, but it is very slow, taking about an hour to regenerate the entire Debian website.)
  2. handle multilingual documentation, e.g. http://gohugo.io/overview/introduction/, with ease, with accommodation for incomplete/outdated translation as it often takes translators a long time to catch up.
  3. handle bilingual or trilingual parish websites, for example, that are common in bilingual cities or countries like Hong Kong (Chinese and English) and Canada (English and French)
  4. handle variants of the same language, e.g. the flexibility to allow one Chinese translation source but generating both zh-Hant and zh-Hans, or even zh-CN, zh-TW, zh-CN, zh-MO and zh-SG like on http://zh.wikipedia.org/, with automatic and user-customizable conversion tables/engines. Or, alternatively, if the user would rather keep zh-Hant and zh-Hans totally separate translations like how it is on Apple, Google and Microsoft websites, they can do that too.

But indeed, instead of speculating, I should start actually trying out your excellent PR and try building test websites with the above scenarios and see how it works. 😁

I will go experiment and report back.

By the way, do you already have some example/test websites that can demonstrate the multilingual features that you have written thus far? :-)

Many thanks again!

Anthony

@spf13
Copy link
Contributor

spf13 commented Jan 7, 2016

For what it's worth, for us to do what I suggested above we need to first transition into a new content data model where a single file no longer is tied to representing a page. I'm currently working on the selective reading (only reading files that have changed) into Hugo, but I think I'm going to tackle that next and will do it with multilingual in mind.

@abourget
Copy link
Contributor Author

abourget commented Jan 7, 2016

quick note @spf13 .. the "DefaultContentLang" does your 1., if about.md stays about.md, it'll be considered DefaultContentLang, which by default is en. I think it's in the docs I put in there. I'll review the rest of your comments in a few moments...

@abourget
Copy link
Contributor Author

abourget commented Jan 7, 2016

Yeah a simple site is mine: https://blog.abourget.net

Le jeu. 7 janv. 2016 13:50, Steve Francia notifications@github.com a
écrit :

For what it's worth, for us to do what I suggested above we need to first
transition into a new content data model where a single file no longer is
tied to representing a page. I'm currently working on the selective reading
(only reading files that have changed) into Hugo, but I think I'm going to
tackle that next and will do it with multilingual in mind.


Reply to this email directly or view it on GitHub
#1734 (comment).

@abourget
Copy link
Contributor Author

abourget commented Jan 7, 2016

The core idea here is linking the translations using a base filename (about.md links to about.fr.md, same as about.en.md links to about.de.md). This solves huge complexities you normally see (in terms of UI, management), with IDs everywhere, and translation tables.

Regarding history and diffing, Git is the best we could bring to the table. Collaboration through GitHub and organizations there is really all you need, especially on large sites. Simple tooling could be built to diff the changes to the "base" language that occurred after the last translation occurred for each piece of content... so smart git-fu and we're done. Perhaps this could be added to hugo itself a bit later. All of that, thanks to filename-based linking.

The next thing most important I think is what we offer to the template designer. Right now, a Page has a Translations as map[string]*Page, mapping to corresponding translations (based on base-filename). This way, designers/devs could get the base translation directly, fall back to it as required.

Now there are many ways to address i18n in a CMS. My references come from Typo3 couple of years ago, where they offered two superbly divergent i18n models:

  • tree separation: you'd have a completely different tree for each language.. you'd manage page hierarchy separately.. so if you wanted a piece of content on a different language, you needed to add it to the tree in that language. More granular, but more job.
  • merged tree: the page hierarchy would imply that each page has or should have a translation. It would fall back to the "default" language.

In both cases, the pieces of content could be translated and linked together.

This PR currently implements this behavior: the Pages (used to render the lists, nodes, taxonomies, etc..) is a single-language list of content pages. It uses the RenderLanguage to filter out the translations.. The translations are however attached to the Page object themselves under the .Translations field, as cited before. If you have about.fr.md (and no about.md nor about.en.md), when you run with RenderLanguage: en, you will simply not see that article.

Now this isn't implemented here, but we could have a top-level switch for two different behaviors:

  1. content isolation (current behavior): allowing links between translated content pieces, but which would have a different tree, its own subset of tags and taxonomies..
  2. use a reference language: use the DefaultContentLang if no translation exists for the current run's RenderLanguage. This way, the Site.Pages would potentially have some pages in different languages (a partially translated site). To implement this, we'd probably want to have a .Page.ReferencePage or .Page.ReferenceTranslation which would be defined (and guaranteed to be defined ?) only in "reference language" mode. Template designers could check some .Page.IsTranslated or TranslationPresent or something that would tell if indeed the ReferenceTranslation is present.... I don't know.. something along those lines.

@anthonyfok regarding the 35+ translations, using this PR would simply mean you need 35 config files.

@spf13 regarding translation of the taxonomies.. I feel this should be solved using my other PR, i18n, which adds a sort of native place for translations. On my site, right now, I'm using a hacky data file to do some translation of the languages (see https://github.com/abourget/blog/blob/master/data/i18n/langlinks.yml and its use here: https://github.com/abourget/blog/blob/d5f5b9a2ef756eb2cd26c0db52f6547e03e010fa/themes/polymer/layouts/partials/header.html#L200-L221 ).. but I feel it could become a convention in i18n directories.
Like the translation string name could be taxonomies_tags_golang: "Golang" ..

@anthonyfok regarding the en-US and other things like that:

  • this PR will work if you specify RenderLanguage: en-US and you use something like about.en-US.md for your files.. what we do not have is a way to fallback to about.en.md .. I think this could be added later withoiut breaking things too much.
  • the other PR implementing i18n with go-i18n does that.. it parses the languages and does some matching (that I haven't looked how it works yet).. but it will return translations for en-US if you ask the language en for example. Maybe that could be reused to implement such a logic when deciding on which translations to show in which config.en.yaml rendition.

@anthonyfok also, regarding the zh.wikipedia.org .. this PR does not facilitate the generation of subdomains. For that I thought maybe in the future we could support a translatable BaseURL .. we'd need to wire that in deeply, and sorta trigger off the /en prefix to URLs, but it could work. I'd see that as a refinement, as some other options for the future.

@spf13 regarding base metadata, if we had .Page.ReferenceTranslation.. that'd mean some template designers could decide to use common params or stuff exposed on that Page.. or not.. it would be an additional indirection if you want to add that layer of complexity. Otherwise, would be kept simple.. with no sharing. What do you think of that ?

feew.. does that make sense ? sorry for the wall of text! :)

@anthonyfok
Copy link
Member

I have just taken the first step to test your PR: by trying to add multilingualization (m17n) to gohugo.io documentation.

What I have done so far (very little):

  1. Locally merge this PR for testing

  2. Compile Hugo with go get -v github.com/spf13/hugo

  3. Run hugo server -s docs and read http://localhost:1313/content/multilingual/ to figure out what I should do next.

  4. Edit docs/config.toml to add:

    Multilingual = true
    RenderLanguage = "en"

    (I used RenderLanguage = en the first time and got Error: While parsing config: Near line 6 (last key parsed 'RenderLanguage'): Expected value but found 'e' instead., so I went back and added quotation marks around en. Yes, minor differences between TOML and YAML.)

  5. Found this in the doc:

    You will also need two config files (config.en.yaml and config.fr.yaml for example) and you will need to run hugo twice, to render each language’s HTML.

    and got somewhat confused. Do I just run hugo twice? Or do I need to do two separate hugo runs, as in hugo --config config.en.yaml and hugo --config config.fr.yaml?

  6. Restart hugo server -s docs and tried to reload http://localhost:1313/content/multilingual/ in the browser, and got a 404 page not found error.

  7. Manually add en/ to the URL, making it http://localhost:1313/en/content/multilingual/, and could read the page again.

  8. Visit http://localhost:1313/. Got a blank page.

  9. Try http://localhost:1313/en/ and finally see the original home page.

  10. Ran hugo -s docs and realized that /index.html is no longer generated, only /en/index.html.

Speak for myself as a newbie and a somewhat lazy user, some of expectation at this stage (after merely enabling multilingual mode) would be:

  • The site should work as before, i.e. before the m17n migration, which means:
    • a working /index.html, be it a real file or an alias (redirection) to /en/index.html (optional, on by default)
    • /content/multilingual/ should not give 404 but be created as an automatic alias to /en/content/multilingual/ or whatever the DefaultContentLang is (optional, on by default)
  • The user would want to run Hugo only once, not twice, or 35+ times, when all these languages reside in different sub-directories under the same domain name. Besides, how is the user supposed to run hugo server multiple times in order to see the entire website in its full m17n glory?

That said, I think what you already have is wonderful and is a great step forward, but there are a few details that we need to "get it as right as we can (@spf13)" first before presenting it for the first time to our end users, "especially the interface, that's the hardest thing to change for users (@spf13)".

So, I think it is time that we start a new topic branch—let's call it m17n—so that we can merge all your m17n pull requests in one place, and all others can experiment and contribute to, so we can try to "get it as right as we can" before we push this m17n branch onto master. What do you think? :-)

@bep
Copy link
Member

bep commented Jan 8, 2016

The user would want to run Hugo only once, not twice, or 35+ times, when all these languages reside

Totally agree. Also with the other stuff you are saying, and you do a fantastic job with this thorough testing. But I don't think a topic branch is a way to go at this point. This is a complex problem to solve, but it should be possible to get this PR up to a working state before involving other people.

@abourget
Copy link
Contributor Author

abourget commented Jan 9, 2016

Adressing a couple of things here @anthonyfok:

  1. Regarding point 5. (having to run hugo multiple once per language with --config config.XX.yaml files):
    • I totally agree.
    • I see different solutions to this:
      1. Have hugo detect the config.XX.(yaml|toml|json) configs, and when it detects many configs, run many times. We'd have to merge the watchers so that serve continues to work. I don't know the implication of this in the code.
      2. Support a single config.yaml with many YAML documents inside (separated by ---). It could even "infer" Multilingual: true when it detects such multi-lang documents (we could remove that key altogether).. From an interface perspective, I think I'd prefer this one, as it would be much easier to convert your existing things to Multiliingual.. and I don't see any other use of such multi-config documents.
        1. From this strategy, we could also infer that the first section defines the DefaultContentLang value, removing yet another key. If we implement a LanguageBasePath (see below), it could have a default value of "", unless explicited changed, so transition from multilingual to non-multilingual would be even seamlesser (whatever that means).
        2. When multiple docs are detected, we could require the RenderLanguage to be present, otherwise fail upfront.
        3. If anything was eventually inherited from one yaml document to the next, it would make more sense if they are in the same file.. as order could be determined (the first being the most prominent). config.XX.yaml files don't really make that easy. Inheritance adds its complexity, I'd prefer being cautious about that.. even at the cost of duplicating a few lines in the config.
    • If we could agree on a first direction, I'd like to get a shot at implementing this in this PR.
  2. Regarding point 6. and 7., with the site moving to en/ and getting a 404 after running with Multilingual: true:
    • Right now, everything gets prefixed with the current language (RenderLanguage). I would be quite simple to have an override flag for that, where you'd select which base you want to add there, or none.
    • It wouldn't make sense for new sites to have a copy of everything from en/ to / (or a list of aliases ) .I think this concern is only valid for migrations. So in the case of a migration, perhaps the site owner could set something like LanguageBasePath to "" when we don't want en/ prepended. For this example, it could conserve the original tree, whereas the other translations would be mounted under fr/, etc..
      • This feature could eventually be extended to support LanguageBasePath: "https://fr.subdomain.com/blog" or something like that, where /blog would be used to prefix files on disk as well as links, and the domain part could replace the BaseURL for those links. This might be complex though, as on disk, it would all be rendered in the same directories as the other languages.. and there would be risks of overwriting the same files per translations. (like blog/tags/hello/index.html)
      • Subdomains are a bit tricky, as they don't map properly to what's on disk. It's simpler to render everything to the same public/ directory.
    • Such a feature could also allow people to mount the English translation to english/ yet keeping the terse post.en.md naming scheme.
  3. Regarding point 8. (empty root):
    • This draft blog post shows how to solve the empty root and empty sitemap problem.
    • This solution uses static files, where you or your theme plainly dumps those static files at root. This means you can't use any programmatic/templateable way to generate them, based on LinkLanguages or DefaultContentLang or something.
    • Also, the current PR does not expose knowledge about the other available config.XX.yaml language configs. Maybe there could be a convention in the layouts directory, like multilingual_root_index.html and multilingual_root_sitemap.xml and those would populate the absolute root when in Multilingual mode. They'd be ignored otherwise..

@anthonyfok
Copy link
Member

But I don't think a topic branch is a way to go at this point. This is a complex problem to solve, but it should be possible to get this PR up to a working state before involving other people.

Sorry, I wasn't clear in my previous comment. What I was thinking, first and foremost, was actually how to get abourget#1 into the fold. With the newly created m17n branch, which already has this PR #1734 merged, @abourget can make a pull request against the m17n branch.

Also, I was also thinking, because it is such a complex problem to solve, we might need several PRs together to get all the features in, and working in the m17n branch would facilitate that. We could involve other people, but we don't have to. We could keep m17n purely to keep track of @abourget's current and future m17n-related PRs.

(Though I was also hoping I could contribute a PR or two on top of @abourget's existing PR to address the minor issues that I pointed out above. 😉)

Nevertheless, whichever way we work doesn't really matter too much. Branches in Git are cheap, and we can create and delete them any time we wish. If you prefer working entirely within the master branch, that is fine with me too, though in that case I would like to have Hugo prints a warning saying that "the new Multilingual feature is currently experimental, and its interface may still change at any time without notice".

@martybr
Copy link

martybr commented Jun 7, 2016

I've got a problem when rendering menu's. If I enable "SectionPagesMenu" my sections are added to the menu but with an URL without the language prefix. For example the URL for the "post" section just becomes "/post/" instead of "/en/post/". The menu items defined in the front matter work correctly, i.e.. have the language prefix in the URL.

Also, I would expect the "menu" tree to be defined in the Multilingual block so it is possible to create custom localized menu entries. Or am I missing something?

One last item: There is a great example of using "apply" to generate tags and categories URL's. But because variables in a partial are limited to the scope you have supplied it is not possible to add the $.Site.CurrentLanguage variable to the URL you generate. I've now solved it with the "range" command but I really liked the simplicity of the "apply" example. Perhaps I'm missing something to get this working.

@bep
Copy link
Member

bep commented Jun 26, 2016

@abourget what is the status of this PR? I can understand people having life outside of open source development, but this is dragging out. If we cannot close this PR in a week or two I suggest we just close it, so we don't getting people's hopes filled up for no reason.

@abourget
Copy link
Contributor Author

abourget commented Jul 4, 2016

@jasongonzales23 I'm not sure I understand the content/getstarted/sample.pdf file.. is that in your content directly or is it the rendered path ? Wouldn't you put static assetes like a .pdf in static/ ? In that case it would only be rendered at the root of your site, and not a copy in each language. But I feel that's not what you meant.

@abourget
Copy link
Contributor Author

abourget commented Jul 4, 2016

Last time I checked, this PR was in good shape.. I just didn't have time to address the list of comments.. I think mostly they are about better documentation.. and I agree with that. I'd like those who test-drive the PR to add to the docs directly.. as it will come from experience.. I'm not 100% sure I'd cover all the things merely by reviewing or addressing a few comments.

The other thing is about exported/unexported vars. I don't have strong opinions about exported/unexported Go functions.. as I don't personnally use hugo as a library.. and I'd assume most people use the command-line program.

The only thing that I'd want to get right are the variables exposed to template developers.. like .TranslatedPages and .AllPages suggestion. Choosing the right name for what the current variables do, and perhaps leaving namespace room to augment the available translation helpers/vars..

I'd also need to rebase this PR.. Since 0.16 is released, can we merge it when I rebase it.. so everyone can start contributing to it ? in terms of docs, or tweaks.. I think we agree on the large chunks.. and having it in the master branch would allow everyone to start poking at it.

@abourget
Copy link
Contributor Author

abourget commented Jul 4, 2016

Checking at the conflicts just now.. and merging it after this rebase would be great, so I don't have to deal with things like that down the road:

-       switch v.(type) {
+       switch val := v.(type) {
        case bool:
++<<<<<<< 5388211c1158b81725af04152c6e73eddc1435a0
 +              return v
 +      case time.Time:
 +              return v
++=======
+               return val

@abourget
Copy link
Contributor Author

abourget commented Jul 4, 2016

Ok, just rebased. Not much was in conflict. Tests passed over here. We'll see how it goes.

@bep can you list the blockers to a merge ? I'll make sure I address them, and then let's merge and have the community jump in to fine tune with the different use cases.

I use this branch on 2 sites right now, and planning a third.. so I'll put my hands back into it soon. https://golangmontreal.org and https://blog.abourget.net/ you can check the source for both sites ( https://github.com/gomtl/golangmontreal.org and https://github.com/abourget/blog )

@abourget
Copy link
Contributor Author

abourget commented Jul 4, 2016

I've tweaked a few of my previous comments, perhaps you want to read them on Github instead of by e-mail :)

@jasongonzales23
Copy link

@abourget, thanks so much. I am using this branch in prod, so would very much see it merged in soon!

@bep
Copy link
Member

bep commented Jul 4, 2016

@abourget my main comment is still untouched:

You lost me a little with this example, and if you lost me -- it will be too complex for the regular Hugo.

This can be made much simpler, and we don't have to export this many nuts and bolts (variables and funcs) to the users.

For a given page or node I'm interested in created a list of available translations (excluding the page I'm currently on), if available.

So in pseudo code:

for range .Translations
   a href= .Permalink  .Language  // the page/node should know which language it represents

@abourget
Copy link
Contributor Author

abourget commented Jul 8, 2016

Ok @bep I pushed fixes and simplifications.


```
{{if .IsPage}}
{{ range $txLang := .Site.Languages }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and below isn't what I talked about in my previous commit, is it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.. that's the simplest form I found and use.. if you have something better to propose.. I'm all ears.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have proposed a simpler form above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see how the form I documented can be simplified. Your propositiin doesn't take into account the differences between Pages and Nodes and the potential different decisions a user might take.

I see this as complete , unless we want to abstract those decisions into yet another concept different than Pages and Nodes, something that would unite them into a Translatable or something.

Implements:
* support to render:
  * content/post/whatever.en.md to /en/2015/12/22/whatever/index.html
  * content/post/whatever.fr.md to /fr/2015/12/22/whatever/index.html
* gets enabled when `Multilingual:` is specified in config.
* support having language switchers in templates, that know
  where the translated page is (with .Page.Translations)
  (when you're on /en/about/, you can have a "Francais" link pointing to
   /fr/a-propos/)
  * all translations are in the `.Page.Translations` map, including the current one.
* easily tweak themes to support Multilingual mode
* renders in a single swift, no need for two config files.

Adds a couple of variables useful for multilingual sites

Adds documentation (content/multilingual.md)

Added language prefixing for all URL generation/permalinking see in the
code base.

Implements i18n. Leverages the great github.com/nicksnyder/go-i18n lib.. thanks Nick.
* Adds "i18n" and "T" template functions..
.Page.Translations now excludes the current page from in there..
Applied .AllPages throughout and fixed some tests, refactored a bit to
avoid massive duplication.

Got rid of unused `possibleTaxonomies`.
@abourget
Copy link
Contributor Author

abourget commented Jul 9, 2016

@jasongonzales23 the latest code should solve a bunch of your issues.. namely the LiveReloading not picking up translations (because of the way .Pages and .TranslatedPages worked).. Now .AllPages includes all translations and watch all those files for changes before rebuilding.

Everything is rebased on master now.

@doowonee
Copy link

I really want this feature.

When I didn't know about this issue. I've tried to make multilingual different way.

The section is the language type what I want to publish. example.com/en/who-am-i is in the en folder and example.com/ko/who-am-i is in the ko folder.
But this way has a many problem like index.html(homepage) for each languages or duplication Taxonomy (same content with tags but different languages)

@dukebarman
Copy link

When merge? :)

@spil-rkoopmans
Copy link

I really want this feature too. Is there an estimate when this will be merged? Will I be safe if I use @abourget his fork in production? I really need this for production sites or I am afraid I have to resort to something different

@bep
Copy link
Member

bep commented Jul 22, 2016

@spil-rkoopmans It will be merged when it is solid enough to merge, estimate is impossible to make in an open source project like this with contributions from unknown entities. The API will change, I can say that, so any use of this PR will have to adapt.

@spil-rkoopmans
Copy link

thank you @bep. I understand. I have decided to go with Hugo anyways using the method described in the v0.16 documentation. I really love this product and think you guys are doing a great job! We will see what happens in the future and adjust our project accordingly!

@bep
Copy link
Member

bep commented Jul 23, 2016

Merged into https://github.com/spf13/hugo/tree/multilingual

I will walk this one into master when I get some time to spare. Thanks for the great job.

@bep bep closed this Jul 23, 2016
@abourget
Copy link
Contributor Author

abourget commented Aug 1, 2016

Continued here: #2303

@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet