Skip to content

Commit

Permalink
Backport of i18n lookup table changes to 4.x, fixes misc url_for issu…
Browse files Browse the repository at this point in the history
…es (#2604)

* Improved lookup table backport to 4.x

Lookup table implementation backported from master. This solves
a bug where fully localized templates links can't be generated
by url_for and link_to helper methods.

* Fix lookup with "mount_at_root: false"

Helper methods url_for and link_to could not get the correct paths
when mount config option was set to "false".

Fixed it by adding an unlocalized path to the lookup table.

* Added url_for test with "mount_at_root: false"

New test to make sure "mount_at_root: false" works as intended,
includes previously failing case of fully localized templates
(e.g.: hello.en.html.erb).

---------

Co-authored-by: phernandez <pedrohdezmlg@gmail.com>
  • Loading branch information
guillerodriguez and phvega committed Feb 6, 2023
1 parent 1c27291 commit ae5581e
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 5 deletions.
94 changes: 94 additions & 0 deletions middleman-core/features/i18n_link_to.feature
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,97 @@ Feature: i18n Paths
Then I should see 'Current: /es/article.html'
Then I should see 'Other: /article.html'
Then I should see 'Current with anchor: /es/article.html#test-anchor'

Scenario: Using url_for with the no mount config
Given a fixture app "empty-app"
And a file named "data/pages.yml" with:
"""
- hello.html
"""
And a file named "locales/en.yml" with:
"""
---
en:
msg: Hello
"""
And a file named "locales/es.yml" with:
"""
---
es:
paths:
hello: "hola"
msg: Hola
"""
And a file named "source/localizable/hello.html.erb" with:
"""
Page: <%= t(:msg) %>
<% data.pages.each_with_index do |p, i| %>
Current: <%= url_for "/#{p}" %>
Other: <%= url_for "/#{p}", locale: ::I18n.locale == :en ? :es : :en %>
<% end %>
"""
And a file named "source/localizable/article.html.erb" with:
"""
Page Lang: Default
Current: <%= url_for "/article.html" %>
Other: <%= url_for "/article.html", locale: ::I18n.locale == :en ? :es : :en %>
Current with anchor: <%= url_for "/article.html", :anchor => "test-anchor" %>
"""
And a file named "source/localizable/article.es.html.erb" with:
"""
Page Lang: Spanish
Current: <%= url_for "/article.html" %>
Other: <%= url_for "/article.html", locale: :en %>
Current with anchor: <%= url_for "/article.html", :anchor => "test-anchor" %>
"""
And a file named "source/localizable/post.en.html.erb" with:
"""
Page Lang: English
Current: <%= url_for "/post.html" %>
Other: <%= url_for "/post.html", locale: :es %>
Current with anchor: <%= url_for "/post.html", :anchor => "test-anchor" %>
"""
And a file named "source/localizable/post.es.html.erb" with:
"""
Page Lang: Spanish
Current: <%= url_for "/post.html" %>
Other: <%= url_for "/post.html", locale: :en %>
Current with anchor: <%= url_for "/post.html", :anchor => "test-anchor" %>
"""
And a file named "config.rb" with:
"""
activate :i18n, mount_at_root: false
"""
Given the Server is running
When I go to "/en/hello.html"
Then I should see "Page: Hello"
Then I should see 'Current: /en/hello.html'
Then I should see 'Other: /es/hola.html'
When I go to "/es/hola.html"
Then I should see "Page: Hola"
Then I should see 'Current: /es/hola.html'
Then I should see 'Other: /en/hello.html'
When I go to "/en/article.html"
Then I should see "Page Lang: Default"
Then I should see 'Current: /en/article.html'
Then I should see 'Other: /es/article.html'
Then I should see 'Current with anchor: /en/article.html#test-anchor'
When I go to "/es/article.html"
Then I should see "Page Lang: Spanish"
Then I should see 'Current: /es/article.html'
Then I should see 'Other: /en/article.html'
Then I should see 'Current with anchor: /es/article.html#test-anchor'
When I go to "/en/post.html"
Then I should see "Page Lang: English"
Then I should see 'Current: /en/post.html'
Then I should see 'Other: /es/post.html'
Then I should see 'Current with anchor: /en/post.html#test-anchor'
When I go to "/es/post.html"
Then I should see "Page Lang: Spanish"
Then I should see 'Current: /es/post.html'
Then I should see 'Other: /en/post.html'
Then I should see 'Current with anchor: /es/post.html#test-anchor'
63 changes: 58 additions & 5 deletions middleman-core/lib/middleman-core/core_extensions/i18n.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ def manipulate_resource_list(resources)
new_resources = []

file_extension_resources = resources.select do |resource|
parse_locale_extension(resource.path)
# Ignore resources which are outside of the localizable directory
File.fnmatch?(File.join(options[:templates_dir], '**'), resource.path) &&
parse_locale_extension(resource.path)
end

localizable_folder_resources = resources.select do |resource|
Expand Down Expand Up @@ -190,10 +192,61 @@ def manipulate_resource_list(resources)
resource.ignore!
end

@lookup = new_resources.each_with_object({}) do |desc, sum|
abs_path = desc.source_path.sub(options[:templates_dir], '')
sum[abs_path] ||= {}
sum[abs_path][desc.locale] = '/' + desc.path
# This generates a lookup hash that maps the real path (as seen by the web
# page user) to the paths of the localized versions. The lookup is later
# used by `url_for '/some/page.html', :locale => :en` and other url
# helpers.
#
# For example (given :mount_at_root => :es) and localized paths:
#
# @lookup['/en/magic/stuff.html'] = {:en => '/en/magic/stuff.html', :de => '/de/magisches/zeug.html', :es => '/magico/cosas.html'}
# @lookup['/de/index.html'] = {:en => '/en/index.html', :de => '/de/index.html', :es => '/index.html'}
# @lookup['/en/index.html'] = {:en => '/en/index.html', :de => '/de/index.html', :es => '/index.html'}
# @lookup['/index.html'] = {:en => '/en/index.html', :de => '/de/index.html', :es => '/index.html'}
#
# We do this by grouping by the source paths with the locales removed. All
# the localized pages with the same content in different languages get the
# same key.
#
@source_path_group = new_resources.group_by do |resource|
# Try to get source path without extension
_locale, path, _page_id = parse_locale_extension(resource.source_path)

# If that fails, there is no extension, so we use the original path. We
# can not use resource.path here, because .path may be translated, so the
# file names do not match up.
path ||= resource.source_path

# This will contain the localizable/ directory, but that does not matter,
# because it will be contained in both alternatives above, so the
# grouping key will be correct.
path
end

# Then we walk this grouped hash and generate the lookup table as given
# above.
@lookup = {}
@source_path_group.each do |src_path, resources|
# For each group we generate a list of the paths the user really sees
# (e.g. ['/en/index.html', '/de/index.html', '/index.html'])
exposed_paths = resources.map(&:path)

# We also generate a map with the same infos, but with the locales as keys.
# e.g. {:en => '/en/index.html', :de => '/de/index.html', :es => '/index.html'}
locale_map = resources.each_with_object({}) do |resource, map|
map[resource.locale] = '/' + resource.path
end

# Then we add those to the lookup table, so every path has a
# cross-reference to any other path in other locales.
exposed_paths.each do |path|
@lookup['/' + path] = locale_map
end

if @mount_at_root == false
src_path = src_path.sub(options[:templates_dir] + '/', '')
@lookup["/#{src_path}"] = locale_map
end
end

new_resources.reduce(resources) do |sum, r|
Expand Down

0 comments on commit ae5581e

Please sign in to comment.