Rework locale selection
authorTom Hughes <tom@compton.nu>
Wed, 6 Jan 2016 18:43:25 +0000 (18:43 +0000)
committerTom Hughes <tom@compton.nu>
Wed, 6 Jan 2016 18:43:25 +0000 (18:43 +0000)
Implement our own matching algorithm rather than trying to
patch the http_accept_language one and make sure everything is
using it in a consistent way.

Fixes #1125

12 files changed:
app/controllers/amf_controller.rb
app/controllers/application_controller.rb
app/models/notifier.rb
app/models/user.rb
app/views/site/_potlatch2.html.erb
app/views/site/id.html.erb
config/initializers/http_accept_language.rb [deleted file]
config/initializers/i18n.rb
lib/id.rb
lib/locale.rb [new file with mode: 0644]
test/integration/user_creation_test.rb
test/models/user_test.rb

index f6ae7f231443228d4a349f98b318d832d6b4aa26..f2b592b498830f36fd68f1243df03abf3594e2db 100644 (file)
@@ -190,11 +190,13 @@ class AmfController < ApplicationController
     user = getuser(usertoken)
 
     if user && !user.languages.empty?
-      http_accept_language.user_preferred_languages = user.languages
+      langs = Locale.list(user.languages)
+    else
+      langs = Locale.list(http_accept_language.user_preferred_languages)
     end
 
-    lang = http_accept_language.compatible_language_from(getlocales)
-    (real_lang, localised) = getlocalized(lang)
+    lang = getlocales.preferred(langs)
+    (real_lang, localised) = getlocalized(lang.to_s)
 
     # Tell Potlatch what language it's using
     localised["__potlatch_locale"] = real_lang
@@ -874,7 +876,7 @@ class AmfController < ApplicationController
   end
 
   def getlocales
-    Dir.glob("#{Rails.root}/config/potlatch/locales/*").collect { |f| File.basename(f, ".yml") }
+    @locales ||= Locale.list(Dir.glob("#{Rails.root}/config/potlatch/locales/*").collect { |f| File.basename(f, ".yml") })
   end
 
   ##
index 2ee701e9ceeb5c74e42f020d8676b2747a40cdb5..9954b775b7b25afa8e028b1d71d0f6902ba22717 100644 (file)
@@ -288,47 +288,30 @@ class ApplicationController < ActionController::Base
     end
   end
 
-  def set_locale
-    response.header["Vary"] = "Accept-Language"
-
-    if @user && !@user.languages.empty?
-      http_accept_language.user_preferred_languages = @user.languages
-      response.header["Vary"] = "*"
-    end
+  def preferred_languages
+    @languages ||= if params[:locale]
+                     Locale.list(params[:locale])
+                   elsif @user
+                     @user.preferred_languages
+                   else
+                     Locale.list(http_accept_language.user_preferred_languages)
+                   end
+  end
 
-    I18n.locale = select_locale
+  helper_method :preferred_languages
 
+  def set_locale
     if @user && @user.languages.empty? && !http_accept_language.user_preferred_languages.empty?
       @user.languages = http_accept_language.user_preferred_languages
       @user.save
     end
 
-    response.headers["Content-Language"] = I18n.locale.to_s
-  end
-
-  def select_locale(locales = I18n.available_locales)
-    if params[:locale]
-      http_accept_language.user_preferred_languages = [params[:locale]]
-    end
-
-    if http_accept_language.compatible_language_from(locales).nil?
-      http_accept_language.user_preferred_languages = http_accept_language.user_preferred_languages.collect do |pl|
-        pls = [pl]
-
-        while pl.match(/^(.*)-[^-]+$/)
-          pls.push($1) if locales.include?($1) || locales.include?($1.to_sym)
-          pl = $1
-        end
-
-        pls
-      end.flatten
-    end
+    I18n.locale = Locale.available.preferred(preferred_languages)
 
-    http_accept_language.compatible_language_from(locales) || I18n.default_locale
+    response.headers["Vary"] = "Accept-Language"
+    response.headers["Content-Language"] = I18n.locale.to_s
   end
 
-  helper_method :select_locale
-
   def api_call_handle_error
     yield
   rescue ActiveRecord::RecordNotFound => ex
index 01822656609cc58f710011be937492979b2bb210..a9bc0d10170a9aea10b6f68df9e01408edf6da1e 100644 (file)
@@ -175,14 +175,8 @@ class Notifier < ActionMailer::Base
   private
 
   def with_recipient_locale(recipient)
-    old_locale = I18n.locale
-
-    begin
-      I18n.locale = recipient.preferred_language_from(I18n.available_locales)
-
+    I18n.with_locale Locale.available.preferred(recipient.preferred_languages) do
       yield
-    ensure
-      I18n.locale = old_locale
     end
   end
 
index 0053333e2c3ed5e8ede766dba02d284cb11372a4..eae917af8dc26884a1d5e4a998827ea2b9c2ed7b 100644 (file)
@@ -131,8 +131,8 @@ class User < ActiveRecord::Base
     languages.find { |l| Language.exists?(:code => l) }
   end
 
-  def preferred_language_from(array)
-    (languages & array.collect(&:to_s)).first
+  def preferred_languages
+    @locales ||= Locale.list(languages)
   end
 
   def nearby(radius = NEARBY_RADIUS, num = NEARBY_USERS)
index 3aeafa9cf08c4cedac6a483da66e06d28c328ca3..1c203b2fb3e57ee6c9c5dfd2b80cdf5a559908bb 100644 (file)
@@ -10,7 +10,7 @@
 <script type="text/javascript">alert("<%= t 'site.edit.potlatch2_not_configured' %>")</script>
 <% end %>
 
-<% locale = select_locale(Potlatch2::LOCALES.keys).to_s %>
+<% locale = Locale.list(Potlatch2::LOCALES.keys).preferred(preferred_languages).to_s %>
 
 <script type="text/javascript" defer="defer">
   var changesaved=true;
index 27a3e835bf9c4d365d0f7004dc89eedecb04449c..7d186c5f618a9dab7d7221b0e831c742716867fe 100644 (file)
@@ -16,7 +16,7 @@
       'Please upgrade your browser or use Potlatch 2 to edit the map.';
     document.getElementById('id-container').className = 'unsupported';
   } else {
-    <% locale = select_locale(ID::LOCALES).to_s %>
+    <% locale = ID::LOCALES.preferred(preferred_languages).to_s %>
 
     var id = iD()
       .presets(iD.data.presets)
diff --git a/config/initializers/http_accept_language.rb b/config/initializers/http_accept_language.rb
deleted file mode 100644 (file)
index 2e5c92d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Monkey patch HttpAcceptLanguage pending integration of
-# https://github.com/iain/http_accept_language/pull/6
-#
-module HttpAcceptLanguage
-  class Parser
-    def compatible_language_from(available_languages)
-      user_preferred_languages.find do |x|
-        available_languages.find { |y| y.to_s == x.to_s } ||
-          available_languages.find { |y| y.to_s =~ /^#{Regexp.escape(x.to_s)}-/ }
-      end
-    end
-  end
-end
index 91efa2557d741185ef43088d91518c90a3f13872..a94618c7e3f374f580edd1cf6eca371bb723f452 100644 (file)
@@ -8,6 +8,16 @@ module I18n
         ex.entry[:other]
       end
     end
+
+    class Simple
+      def store_translations_with_normalisation(locale, data, options = {})
+        locale = I18n::Locale::Tag::Rfc4646.tag(locale).to_s
+
+        store_translations_without_normalisation(locale, data, options)
+      end
+
+      alias_method_chain :store_translations, :normalisation
+    end
   end
 
   module JS
index 15f918e799b9a15d16e31fe4ddefee2e1ed73c4b..858bc53c34aa8f7b09a32831c7a7db9e813d1c83 100644 (file)
--- a/lib/id.rb
+++ b/lib/id.rb
@@ -1,3 +1,3 @@
 module ID
-  LOCALES = Rails.root.join("vendor/assets/iD/iD/locales").entries.map { |p| p.basename.to_s[/(.*).json/] && $1 }.compact
+  LOCALES = Locale.list(Rails.root.join("vendor/assets/iD/iD/locales").entries.map { |p| p.basename.to_s[/(.*).json/] && $1 }.compact)
 end
diff --git a/lib/locale.rb b/lib/locale.rb
new file mode 100644 (file)
index 0000000..bb09ae3
--- /dev/null
@@ -0,0 +1,44 @@
+class Locale < I18n::Locale::Tag::Rfc4646
+  class List < Array
+    attr_reader :locales
+
+    def initialize(tags)
+      super(tags.map { |tag| Locale.tag(tag) })
+    end
+
+    def candidates(preferred)
+      preferred.expand & self
+    end
+
+    def preferred(preferred)
+      candidates(preferred).first
+    end
+
+    def expand
+      map(&:candidates).flatten.uniq << Locale.default
+    end
+  end
+
+  def self.list(*tags)
+    List.new(tags.flatten)
+  end
+
+  def self.default
+    tag(I18n.default_locale)
+  end
+
+  def self.available
+    @available ||= List.new(I18n.available_locales)
+  end
+
+  def candidates
+    [self.class.new(language, script, region, variant),
+     self.class.new(language, script, region),
+     self.class.new(language, script, nil, variant),
+     self.class.new(language, script),
+     self.class.new(language, nil, region, variant),
+     self.class.new(language, nil, region),
+     self.class.new(language, nil, nil, variant),
+     self.class.new(language)]
+  end
+end
index 1ff8f9a650409a99d0f4b9bda73405286d91e0cb..6c6a104478a2ffa5081f2da007772afcfab90871 100644 (file)
@@ -40,7 +40,7 @@ class UserCreationTest < ActionDispatch::IntegrationTest
       end
       assert_response :success
       assert_template "user/new"
-      assert_equal response.headers["Content-Language"][0..1], locale.to_s[0..1] unless locale == :root
+      assert_equal locale.to_s, response.headers["Content-Language"] unless locale == :root
       assert_select "form > fieldset > div.form-row > input.field_with_errors#user_email"
       assert_no_missing_translations
     end
index b3e6eae8f702f144bef52eb3f488346069b9ed3b..4a8d9a8948260a40e1affc44025eb3a69282fdf6 100644 (file)
@@ -199,7 +199,7 @@ class UserTest < ActiveSupport::TestCase
     assert_equal %w(de fr en), user.languages
     user.languages = %w(fr de sl)
     assert_equal "de", user.preferred_language
-    assert_equal "de", user.preferred_language_from(%w(en sl de es))
+    assert_equal %w(fr de sl), user.preferred_languages.map(&:to_s)
     user = users(:public_user)
     assert_equal %w(en de), user.languages
   end