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

Add deduplication of routes generated by honoring the I18n.fallbacks #2

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/route_translator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ module RouteTranslator

DEFAULT_CONFIGURATION = {
available_locales: [],
fallback_locales: [],
disable_fallback: false,
force_locale: false,
generate_unlocalized_routes: false,
generate_unnamed_unlocalized_routes: false,
deduplicate_routes: false,
hide_locale: false,
host_locales: {},
locale_param_key: :locale,
Expand Down Expand Up @@ -68,6 +70,16 @@ def available_locales
end
end

def fallback_locales
locales = config.fallback_locales

if locales.empty?
I18n.fallbacks.values.map(&:last).uniq
else
locales.map(&:to_sym)
end
end

def locale_param_key
config.locale_param_key
end
Expand Down
10 changes: 9 additions & 1 deletion lib/route_translator/extensions/route_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class RouteSet
def add_localized_route(mapping, name, anchor, scope, path, controller, default_action, to, via, formatted, options_constraints, options)
route = RouteTranslator::Route.new(self, path, name, options_constraints, options, mapping)

RouteTranslator::Translator.translations_for(route) do |locale, translated_name, translated_path, translated_options_constraints, translated_options|
translations_for(route) do |locale, translated_name, translated_path, translated_options_constraints, translated_options|
translated_path_ast = ::ActionDispatch::Journey::Parser.parse(translated_path)
translated_mapping = translate_mapping(locale, self, translated_options, translated_path_ast, scope, controller, default_action, to, formatted, via, translated_options_constraints, anchor)

Expand All @@ -24,6 +24,14 @@ def add_localized_route(mapping, name, anchor, scope, path, controller, default_

private

def translations_for(route, &block)
if RouteTranslator.config.deduplicate_routes
RouteTranslator::Translator.deduplicated_translations_for(route, &block)
else
RouteTranslator::Translator.translations_for(route, &block)
end
end

def translate_mapping(locale, route_set, translated_options, translated_path_ast, scope, controller, default_action, to, formatted, via, translated_options_constraints, anchor)
scope_params = {
blocks: (scope[:blocks] || []).dup,
Expand Down
63 changes: 63 additions & 0 deletions lib/route_translator/translator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,23 @@ def translate_path(path, locale, scope)
rescue I18n::MissingTranslationData => e
raise e unless RouteTranslator.config.disable_fallback
end

def allowed_to_deduplicate(locale, translated_path, generated_routes)
return false unless RouteTranslator.config.deduplicate_routes
return false if I18n.default_locale == locale
return false if generated_routes[translated_path].nil? || generated_routes[translated_path].empty?
return true if generated_routes[translated_path] && !generated_routes[translated_path].empty?
return false if RouteTranslator.fallback_locales.include?(locale)

true
end
end

module_function

def available_locales
locales = RouteTranslator.available_locales

# Make sure the default locale is translated in last place to avoid
# problems with wildcards when default locale is omitted in paths. The
# default routes will catch all paths like wildcard if it is translated first.
Expand All @@ -65,16 +76,54 @@ def available_locales
def translations_for(route)
RouteTranslator::Translator::RouteHelpers.add route.name, route.route_set.named_routes

generated_routes = {}

available_locales.each do |locale|
translated_path = translate_path(route.path, locale, route.scope)
next unless translated_path
next if allowed_to_deduplicate(locale, translated_path, generated_routes)

translated_name = translate_name(route.name, locale, route.route_set.named_routes.names)
translated_options_constraints = translate_options_constraints(route.options_constraints, locale)
translated_options = translate_options(route.options, locale)
generated_routes[translated_path] = true

yield locale, translated_name, translated_path, translated_options_constraints, translated_options
end

generated_routes = nil
end

def deduplicated_translations_for(route)
RouteTranslator::Translator::RouteHelpers.add route.name, route.route_set.named_routes

generated_routes = {}
sorted_available_locales_for_deduplication = [I18n.default_locale] + (RouteTranslator.fallback_locales - [I18n.default_locale]) + (available_locales - RouteTranslator.fallback_locales)

sorted_available_locales_for_deduplication.each do |locale|
translated_path = translate_path(route.path, locale, route.scope)
next unless translated_path
next if allowed_to_deduplicate(locale, translated_path, generated_routes)

generated_routes[translated_path] ||= []
generated_routes[translated_path] << locale
end

sorted_available_locales_for_routing_pass = (available_locales - RouteTranslator.fallback_locales) + (RouteTranslator.fallback_locales - [I18n.default_locale]) + [I18n.default_locale]

sorted_available_locales_for_routing_pass.each do |locale|
translated_path = translate_path(route.path, locale, route.scope)
next unless translated_path
next unless generated_routes[translated_path].include?(locale)

translated_name = translate_name(route.name, locale, route.route_set.named_routes.names)
translated_options_constraints = translate_options_constraints(route.options_constraints, locale)
translated_options = translate_options(route.options, locale)

yield locale, translated_name, translated_path, translated_options_constraints, translated_options
end

generated_routes = nil
end

def route_name_for(args, old_name, suffix, kaller)
Expand All @@ -85,11 +134,25 @@ def route_name_for(args, old_name, suffix, kaller)
args_locale.to_s.underscore
elsif kaller.respond_to?("#{old_name}_#{current_locale_name}_#{suffix}")
current_locale_name
elsif fallback_locale = fallback_route_locale_name(current_locale_name, old_name, suffix, kaller)
fallback_locale
else
I18n.default_locale.to_s.underscore
end

"#{old_name}_#{locale}_#{suffix}"
end

def fallback_route_locale_name(current_locale_name, old_name, suffix, kaller)
return nil unless RouteTranslator.config.deduplicate_routes

available_fallback_locales = I18n.fallbacks[current_locale_name].select do |fallback_locale|
kaller.respond_to?("#{old_name}_#{fallback_locale}_#{suffix}")
end

return nil if available_fallback_locales.empty?

available_fallback_locales.first
end
end
end