decaporg / decap-cms

A Git-based CMS for Static Site Generators
https://decapcms.org
MIT License
17.93k stars 3.04k forks source link

i18n: multiple_files structure for file collections #5280

Open jf-home opened 3 years ago

jf-home commented 3 years ago

Just getting to grips with the Beta i18n capabilities and really like the multple_folders and multiple_files structures. However, I am confused by the decision to only support a single_file structure for file collections. I say this reservedly as, clearly, a lot of work must have gone into providing the i18n capabilities and therefore this is in no way a critisism and, quite possibly, may be down to a misunderstanding on my part. The problem that I have though is generating pages in gatsby-node when the i18n implementation involves a mixed structure of multiple_files (i.e. for templated content such as blog posts, product entries etc.) and single_file for one off pages (i.e. index, about, contact).

I'd like to see file collections also support a multiple_files structure as that would allow a single createPage mechanism in gatsby-node to be used. Naturally, that would require a single markdown file for language-specific page, using locale as the differentiating factor i.e. index.en.md, index.de.md, index.es.md and so on.

If this isn't technically viable, please advise how to go about page generation in gatsby-node when confronted with both multiple_files and single_file collections.

Thanks in advance for your help!

erezrokah commented 3 years ago

Hi @jf-home, the lack of support for file collections was part of scoping down the feature so we can launch it quickly and get more feedback.

The challenge for file collections is that you configure a specific file path for the entry. We'll need to think of how the configuration for that looks like when using multiple_files or multiple_folders.

Do you configure a folder? Not sure.

I suggest outlining a proposal on how you think this should look like from a configuration standpoint, based on our feature template.

jf-home commented 3 years ago

Hi @erezrokah,

I appreciate your prompt response and have outlined my thoughts as requested below...

Requirement i18n support for multiple_files is required for file collections to enable pages to be dynamically created via the Gatsby createPage api.

Possible solution From a configuration standpoint, it would be preferable for file collections to "inherit" the top level i18n configuration by default, unless specifically stated otherwise. The - file: configuration would need to allow an array of files to be specified, one for each locale. Example default configuration:

i18n:
  structure: multiple_files
  locales: [en, de, fr]

collections:
   i18n: true
    files:
      - file: ["src/pages/about.en.md","src/pages/about.de.md","src/pages/about.fr.md"]
        label: "About Page"
        name: "about"
        i18n: true
        fields:
          - {label: "Template Key", name: "templateKey", widget: "hidden", default: "index-page", i18n: duplicate}
          - {label: Title, name: title, widget: string, i18n: true}
          - {label: Heading, name: heading, widget: string, i18n: true}
          - {label: "Body", name: "body", widget: "markdown", i18n: true} 

What would be particularly useful is to have the ability to specifiy a different file name for each locale, as the file name is often used to influence the slug. This would provide a better end user experience through the Gatsby site and also improve SEO for each locale. Example configuration (using kebabcase where necessary):

i18n:
  structure: multiple_files
  locales: [en, de, fr]

collections:
   i18n: true
    files:
      - file: ["src/pages/about.en.md","src/pages/uber.de.md","src/pages/a-propos.fr.md"]
        label: "About Page"
        name: "about"
        i18n: true
        fields:
          - {label: "Template Key", name: "templateKey", widget: "hidden", default: "index-page", i18n: duplicate}
          - {label: Title, name: title, widget: string, i18n: true}
          - {label: Heading, name: heading, widget: string, i18n: true}
          - {label: "Body", name: "body", widget: "markdown", i18n: true}

Possible alternatives If the - file: configuration cannot be changed to support an array, then I'd propose that a single file name should be used as at present, however, the CMS would need to perform variable substitution for the locale. Example:

i18n:
  structure: multiple_files
  locales: [en, de, fr]

collections:
   i18n: true
    files:
      - file: "src/pages/about.{{locale}}.md"
        label: "About Page"
        name: "about"
        i18n: true
        fields:
          - {label: "Template Key", name: "templateKey", widget: "hidden", default: "index-page", i18n: duplicate}
          - {label: Title, name: title, widget: string, i18n: true}
          - {label: Heading, name: heading, widget: string, i18n: true}
          - {label: "Body", name: "body", widget: "markdown", i18n: true} 

The drawback with this approach is that it doesn't allow for a different file name (and therefore slug) across locales, so it wouldn't be as effective from an SEO and user experience perspective.

apriljunge commented 3 years ago

In my opinion changing file to an array could get a bit confusing and harder to maintain. I would suggest keeping the definition of file: "src/pages/about.md". When i18n: true with multiple_files enabled, the CMS could infer the localized names, just like it does with collections. (Basically your 2nd suggestion, but without variable substitution)

As you have already noted, it would mean that you need a different way to generate your slugs, but with collections it's the same. Sadly I've no knowledge of Gatsby. Maybe there's a different way to generate slugs?

What do you think @jf-home ?

jf-home commented 3 years ago

Hi @apriljunge,

You make a good point about limiting the potential for confusion and improving maintainability. Unfortunately, my use and understanding of Gatsby leads me to believe that the slug is generated by the markdown file name, so the suggestion of having a single file name with inferred localization would be limiting from an SEO perspective. That said, there may be others with more experience of Gatsby that can confirm or, alternatively, offer another suggestion @KyleAMathews?

I do like your suggestion of inferred localization though. It's nice and simple and may well suffice for those not so concerned with SEO.

Thanks for joining the conversation - much appreciated!

erezrokah commented 3 years ago

I'm loving this discussion ❤️

Personally I prefer if the CMS infer the slug to have the most minimal approach as the first iteration.

If we allow file to be an array, we'll need to validate that the length of the array matches the number of locales, which can be confusing. Also the information about translations is kind of duplicated between the file array and locales array.

As for the string template "src/pages/about.{{locale}}.md" similar confusion can come if someone configures multiple_folders and "src/pages/about.{{locale}}.md" or multiple_files and "src/pages/{{locale}}/about.md".

I think it might be better to handle slug customization via a more general approach as a part of https://github.com/netlify/netlify-cms/issues/445.

To emphasise, the CMS will only infer the base name part of the file path.

I think there is still some confusion with having file: "src/pages/about.md" and files actually being saved in different paths, but I'm not sure how to solve it. We can use the nested collections approach and configure an index_file to make it more explicit, but that might be even more confusing.

apriljunge commented 3 years ago

Valid point. I also thought about src/pages/about.md is interpreted as default locale and only infer other languages. But thats even more confusing, because it differs how folder collections are processed and harder to implement with site generators. And what happens when you change the default locale? :confused: I think its okay to infer the names directly because you have to manually activate i18n for each file.

jf-home commented 3 years ago

I don't think we need to take src/pages/about.md literally, instead, it should be viewed as the main path and file name construct that allows the locale inference to take place. This way, even the default locale would have inference, allowing the default to change at any time without repercussions. As an example, with three locales in play [en, de, fr], each file name would have inference:

This would seem to be the simplest solution initially which @erezrokah rightly suggests is an important consideration.

As for slug customization - I can see an arguement for this both ways. If you provide the ability for content editors to specify the alternate slug, hopefully they'll be meaningful and well-formed. However, if you make it an "admin" feature (i.e. customizable through the config.yml file), there is more potential for visibility, process and control around changes.

The control aspect shouldn't be overlooked imo as when a slug changes, a redirect really should be set up to ensure the old slug doesn't result in a 404. This may not be too important for small ad hoc sites, but for enterprise users, I can see it being quite important.

As for your last comment @erezrokah - I believe this really comes down to how well the feature is documented. As mentioned above, I don't think the file name should be taken literally in the case of i18n file collections. If this is communicated clearly then it shouldn't present a problem (imo).

@apriljunge - I'd be interested in your thoughts.

liminalfun commented 3 years ago

Found this thread while running into the same problem using Hugo, which also doesn't like to parse i18n content from single files.

Like Gatsby, Hugo prefers multiple filessrc/pages/about.en.md. Although it can also support multiple folders like src/pages/about/en/_index.md.

My work-around at the moment is to organize the content for each single page as a folder collection, resulting in longer links like example.com/en/about/about. This approach also requires organizing templates differently than I would for single pages.

I imagine that enabling an i18n multiple_files structure for file collections would enable the links to appear like example.com/en/about, which is much cleaner.

I agree with @jf-home's suggestion of the language codes being inferred. It appears to already work like that for folder collections, so the pattern would be consistent.

Happy to contribute more to this discussion, if you need the support! I really enjoy using Netlify CMS and would love to keep using it for multilingual sites.

liminalfun commented 3 years ago

Figured out a workaround for my last comment! Wanted to give an update in case any other devs with the same issue find this thread.

I got my pages to route to example.com/{{ locale }}/about in Hugo –– but it required some particular steps.

  1. In your config.yml file, create a folder collection for each individual page (rather than files within a page collection). Use create: true, just to create the first md file.

  2. Create your page at localhost:3000/admin/ (or whatever your local host is). This will automatically create markdown files in your content folder: content/about/{{title}}.{{locale}}.md

  3. Rename the slug of each new markdown file from content/about/{{title}}.{{locale}}.md –> content/about/_index.{{locale}}.md.

  4. In your config.ymlfile, change the folder collection's create option to create: false, so that your client won't be able to create more pages in that folder.

  5. Test it all out and see if it works!

Notes on referencing i18n pages using links set in the CMS:

Not sure how this transfers to Gatsby, but I'll be testing it soon! I'd be happy to share my process in this thread, since it seems to be the place to talk about i18n compatibility between Netlify CMS and different SSGs. If this isn't the right place for sharing workarounds, lmk where to go :)