I18n: list of language siblings, and links to them

Is there any “official” way to obtain a list of all alternate, localized versions of the current page? And ideally also a function to easily generate links to them… I want to add a language switcher to my site, so I need to be able to switch to, say, the french version of the current page.

Of course I could mess with the URL to look for, say “/en/” and substitute it by "/fr/. But this won’t work anymore if one uses “Localized Paths” as described on http://middlemanapp.com/advanced/localization/

Any hints greatly appreciated :-).

1 Like

There’s no “official” way. If you wanted to do it properly, you would have to read the options from the Internationalization extension, the paths from the translation, and figure out the equivalent alternative path. As far as I can tell, there’s simply no way to do the former. (The Localization extension is difficult to deal with in general.)

If you’re happy making expectations about how the paths are formed, you can jerry-rig something without much effort. Inside config.rb

helpers do

  def translated_url(locale)
    # Assuming /:locale/page.html
    page_name = @page_id.split("/", 2).last.sub(/\..*$/, '')
    untranslated_path = t(:paths).index(page_name)
    
    begin
      translated = I18n.translate!("paths.#{untranslated_path}", locale: locale)
    rescue I18n::MissingTranslationData
      translated = untranslated_path
    end

    "/#{locale}/#{translated}.html"
  end

  def other_langs
    langs - [I18n.locale]
  end

end

Inside your view:

<p>Translations for this page:</p>

<ul>
  <% other_langs.each do |lang| %>
    <li><%= translated_url(lang) %></li>
  <% end %>
</ul>

That would give you something that looks like:

Translations for this page:

• /en/hello.html
• /es/hola.html
1 Like

Thank you very much for the reply and the code snippet, much appreciated!

Indeed, I could do that or something like it. But I must say I am quite surprised (and disappointed) that Middleman’s i18n feature doesn’t have something like this built-in. It seems to me to be one of the central features of proper i18n support – allowing your users to switch to another language of a page seems to me to be quite a core feature…

Indeed, one of the primary reasons I was looking at Middleman instead of e.g. Jekyll or other tools was that it seemed to provide all the features I need out of the box. But this and other problems I run into now make me think that’s not actually the case at all. So I’ll first re-evaluate whether I want to use Middleman at all – if I have to roll the code for this mysefl anyway, I can just as well use jekyll… Hmm, or perhaps I could try to extend the Middleman code to provide this feature out of the box (but that only makes sense if I also find a way to also resolve my other issues, e.g. this one.

I’m right there with you, @Fingolfin. I find the i18n support in Middleman to be too hard to work with. There’s an issue on GitHub to refactor it.

I ended up writing my own i18n extension for Middleman for the site I run. I recommend you do the same. It’s not too difficult, and you can use the existing one as a starting point.

This seems only to work with deactivated :directory_indexes. Can you please give me a hint how i can get this running with directory indexes turned on (for url’s like: http://products/firstproduct/ && http://de/products/firstproduct/ ) ?

@dennschu Sure. Change the bottom line of translated_url to match what you’d like to see:

def translated_url(locale)
  ...snip...
  "/#{locale}/#{translated}.html" # replace this
end

For instance, in your example, something like this might work:

"http://#{locale}/#{translated}"

I also wrote this helper: https://github.com/middleman/middleman/issues/1306#issuecomment-123013223, but I haven’t tested it in various setups.

Apparently, @page_id is not a thing in blog pages so I ended up adding something like this:
... elsif current_resource.is_a? Blog::BlogArticle localized_path = "#{locale}/#{current_resource.path.split('/').last}" return blog.articles.select { |a| a.path == localized_path }[0].url ...
It fits my blog configuration because I use blog.sources = "{lang}/{day}-{month}-{year}.html" but it’s far from ideal. How do you deal with links for blog pages and regular localizable pages at the same time? I feel like I’m missing something.

1 Like

I am trying to come up with a solution to this issue too.

This topic is too old and there were other, more recent, with some solutions. Unfortunately I haven’t found a complete and fully automatic solution to this in the current MM v4, if I use localized paths. I just use the frontmatter data i18n_source for storing the original source-url of the file. The langs switcher then is simple, in the layout.haml I have a DIV for this partial:

= insert_langs_menu_item(current_page.data.i18n_source, :pl, "PL", "Przełącz na polski")
= insert_langs_menu_item(current_page.data.i18n_source, :en, "EN", "Switch to English")
= insert_langs_menu_item(current_page.data.i18n_source, :de, "DE", "Auf Deutsch umschalten")

In the helpers/custom_helpers.rb I have:

	# insert active link language-menu-item <A>, or <SPAN> if it’s for current locale
  # MM4 version

	def insert_langs_menu_item(page_url, new_locale, menu_text, title_attr=nil)
		if I18n.locale == new_locale
			return content_tag :span do menu_text end
		else
      return link_to menu_text, page_url, locale: new_locale, title: (title_attr || menu_text)
		end
	end

I assume that ALL pages have localized versions of the content. If your layout/header/footer/overall structure is localized then you can just think that all pages are kind-of localized.