How to check if a certain path/URL exists in the generated output by jekyll?

Hi,

Is it possible to check whether or not a certain path or URL will be generated by jekyll?

The reason I need this is for multilingual sites: here I have a language changer in the navigation area of the site. Some pages are not available in all languages. So if a certain page does NOT exist in the alternative language, I do not want to show the language buttons.
The default language pages are at, say, /foo/ or /bar/, while the alternative language pages are at /da/foo/ or /da/bar/.

So on /foo/, I do not want to show the language buttons if /da/foo/ is not to be generated.

Is something like this possible?

Here is the relevant code:

Language buttons:

        <button id="language-btn" class="btn btn-light text-muted" type="button" >
          {% if page.lang == "en" %}
          <svg width="22" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37 28"><path fill="#c60c30" d="M0,0H37V28H0Z"/><path fill="#fff" d="M0,12H12V0H16V12H37V16H16V28H12V16H0Z"/></svg>
          <!--
          Dansk
          -->
          {% else %}
          <svg width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" >
          <clipPath id="s">
                  <path d="M0,0 v30 h60 v-30 z"/>
          </clipPath>
          <clipPath id="t">
                  <path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/>
          </clipPath>
          <g clip-path="url(#s)">
                  <path d="M0,0 v30 h60 v-30 z" fill="#012169"/>
                  <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
                  <path d="M0,0 L60,30 M60,0 L0,30" clip-path="url(#t)" stroke="#C8102E" stroke-width="4"/>
                  <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
                  <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/>
          </g>
          </svg>
          <!--
          English
          -->
          {% endif %}
        </button>

And the javascript to make the buttons work:

// Locales
const path = window.location.pathname,
    langUrl = path.indexOf('/da/') !== -1
// Language switch
document.getElementById('language-btn').onclick = () => {
    if ( path == "/da/") {

        // Redirect to /index.html instead of '/' in order not to trigger language detection on the server
        window.location = "/index.html"
    } else {
        window.location = langUrl ? path.replace('/da', '') : '/da'.concat(path);
    }
    document.getElementById('language-img').src = '';
}

Given that you know the name of the expected file, you can check if the file in another language exists during build. I created a plugin to check if a file exists in my template site.

module Jekyll
  class FileExistsTag < Liquid::Tag

    def initialize(tag_name, path, tokens)
      super
      @path = path
    end

    def render(context)
      # Pipe parameter through Liquid to make additional replacements possible
      url = Liquid::Template.parse(@path).render context

      # Adds the site source, so that it also works with a custom one
      site_source = context.registers[:site].config['source']
      file_path = site_source + '/' + url

      # Check if file exists (returns true or false)
      "#{File.exist?(file_path.strip!)}"
    end
  end
end

Liquid::Template.register_tag('file_exists', Jekyll::FileExistsTag)

In my case I use it to check if a jupyter notebook exists, and display a message if it doesn’t.

1 Like

Not sure I fully understand this code. But am I right that this plugin introduces a new Liquid tag called file_exists ?

And that it works like this {% if file_exists foo.md %}?

If this is the case, how do I load this, and how do I do something like {% if file_exists foo.md %} where foo.md is instead current page’s filename prefixed by /da? That is, if current page is /foo/bar/page/, then it should check if /da/foo/bar/page/ exists.

Also, is there a way to do this without plugins? I currently use no plugins and would really like to keep it simple like this if possible.

can you use an extra front matter variable to say which files have other languages and then check that?

That might be possible, although this approach seems error prone, because I would have to manually add all those front matter vars and be sure not to have a typo in them. And then going forward, I will have to remember to add this to all new pages without making errors. And if I move/rename a page, I will have to update all references to it… Seems like a lot of manual work that can easily be forgotten or mistakes made here and there… So I would really like an automated way if possible.

Is the information needed to do this automatically simply not available at runtime in jekyll - which is why it needs a plugin like @george-gca suggests? I mean, do I need to escape out of jekyll to do some Ruby code to be able to access the info needed for this?

have you tried playing with site.documents or site.pages?

then maybe you loop over that to see if your page exists?

1 Like

If @rdyar suggestion doesn’t work, by looping through site.pages for example, the only solution available I believe is doing the plugin.

Yes, it creates a new liquid tag file_exists, and its usage is:
1 - create the file _plugins/file-exists.rb with this content. This directory is the default plugins directory.
2 - I used like this in my code:

{% capture notebook_exists %}{% file_exists assets/jupyter/blog.ipynb %}{% endcapture %}
{% if notebook_exists == 'true' %}

This way I can check at build time if a file exists.

Thanks! I tried just this, and I believe this is indeed the right way to go about it.
However, SOMETHING is not working as I expect it.
This is my code:

    {% assign da_page_exists = false %}                                      
    {% for page2 in site.pages %}                                            
      '{{   page2.url }}' == '{{ page.url | prepend: "/da" }}':              
      {% if page2.url     ==     page.url | prepend: "/da" %}                
        {% assign da_page_exists = true %}                                   
        Found corresponding Danish page for '{{ page.url }}' on '{{ "/da" | append: page.url }}'
        {{ page2.url }}                                                      
        {% break %}                                                          
      {% else %}                                                             
        '{{ page2.url }}' does not match '{{ "/da" | append: page.url }}'    
      {% endif %}                                                            
    {% endfor %}                                                             
                                                                             
    {% if da_page_exists %}                                                  
      {{ page.url }}:                                                        
      DANISH PAGE EXISTS ON.                                                 
      {{ "/da" | append: page.url }}                                         
    {% else %}                                                               
        '{{ page2.url }}':                                                   
        '{{ page.url }}':                                                    
      DANISH PAGE DOES NOT EXIST ON.                                         
      {{ "/da" | append: page.url }}                                         
    {% endif %}                            

It now finds matches on all pages. Even those that do NOT have a Danish page.

This is where it goes wrong:

  {% if page2.url     ==     page.url | prepend: "/da" %}                

The issue seems to be that when it encounters e.g., ‘/contact/’, it compares page2 to page. It does not seem to take in the prepend operation before doing the comparison.

How do I fix this?

I have tried using parenthesis like this:

 {% if page2.url     ==     ( page.url | prepend: "/da")  %}

But this does not work as it fails to detect when danish pages actually exist.
It results in output like this:

'/da/contact/' == '/da/contact/':
        '/da/contact/' does not match '/da/contact/'


        '':
        '/contact/':
      DANISH PAGE DOES NOT EXIST ON 
      /da/contact/

I think I fixed it using capture:

    {% assign da_page_exists = false %}                                      
    {%- capture da_url -%}                                                   
      {{ page.url |prepend: "/da" }}                                         
    {%-  endcapture -%}                                                      
    {% for page2 in site.pages %}                                            
      {% if page2.url == da_url  %}                                          
        {% assign da_page_exists = true %}                                   
        Found corresponding Danish page for '{{ page.url }}' on '{{ "/da" | append: page.url }}'
        {{ page2.url }}                                                      
        {% break %}                                                          
      {% endif %}                                                            
    {% endfor %}                                                             
                                                                             
    {% if da_page_exists %}                                                  
      {{ page.url }}:                                                        
      DANISH PAGE EXISTS ON.                                                 
      {{ "/da" | append: page.url }}                                         
    {% else %}                                                               
        '{{ page2.url }}':                                                   
        '{{ page.url }}':                                                    
      DANISH PAGE DOES NOT EXIST ON.                                         
      {{ "/da" | append: page.url }}                                         
    {% endif %}                             

Not sure if this is the best way to do it…

I’ve not done something like that before but I guess it looks good.

The main issue is going to be performance - for loops can be slow if you have a lot of pages (100s?) and then call that loop on a lot of pages. If it is just in one place then it probably isn’t too big a deal.

If you are creating a multilingual site I would recommend using polyglot to help you handle different languages from your site. Its default solution for this problem is to fallback to your default language, which I think is a nice solution.