From c513725f26b5c08f8a25884517ca365bd15b81ef Mon Sep 17 00:00:00 2001
From: Jonathan Allard <jonathan@allard.io>
Date: Fri, 7 Feb 2014 22:24:10 -0500
Subject: [PATCH 1/2] Allow users to toggle interface language in their
 preferences

---
 .../discourse/controllers/preferences_controller.js |  6 ++++++
 app/assets/javascripts/discourse/models/user.js     |  1 +
 .../templates/user/preferences.js.handlebars        | 10 ++++++++++
 app/controllers/application_controller.rb           |  6 +++++-
 app/models/site_setting.rb                          |  5 +++++
 app/serializers/user_serializer.rb                  |  1 +
 app/services/user_updater.rb                        |  1 +
 config/locales/client.en.yml                        |  5 +++++
 config/locales/client.fr.yml                        |  5 +++++
 db/migrate/20140206044818_add_locale_to_user.rb     |  5 +++++
 spec/controllers/application_controller_spec.rb     | 13 +++++++++++++
 11 files changed, 57 insertions(+), 1 deletion(-)
 create mode 100644 db/migrate/20140206044818_add_locale_to_user.rb

diff --git a/app/assets/javascripts/discourse/controllers/preferences_controller.js b/app/assets/javascripts/discourse/controllers/preferences_controller.js
index f34439265..18d87d9b3 100644
--- a/app/assets/javascripts/discourse/controllers/preferences_controller.js
+++ b/app/assets/javascripts/discourse/controllers/preferences_controller.js
@@ -30,6 +30,12 @@ Discourse.PreferencesController = Discourse.ObjectController.extend({
     return Discourse.SiteSettings.enable_names;
   }.property(),
 
+  availableLocales: function() {
+    return Discourse.SiteSettings.available_locales.split('|').map( function(s) {
+      return {name: s, value: s};
+    });
+  }.property(),
+
   digestFrequencies: [{ name: I18n.t('user.email_digests.daily'), value: 1 },
                       { name: I18n.t('user.email_digests.weekly'), value: 7 },
                       { name: I18n.t('user.email_digests.bi_weekly'), value: 14 }],
diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js
index b17e8c62a..56df28465 100644
--- a/app/assets/javascripts/discourse/models/user.js
+++ b/app/assets/javascripts/discourse/models/user.js
@@ -170,6 +170,7 @@ Discourse.User = Discourse.Model.extend({
                                'bio_raw',
                                'website',
                                'name',
+                               'locale',
                                'email_digests',
                                'email_direct',
                                'email_always',
diff --git a/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars b/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars
index b65bbd149..4a0560aa0 100644
--- a/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars
@@ -66,6 +66,16 @@
       </div>
     </div>
 
+    <div class="control-group">
+      <label class="control-label">{{i18n user.locale.title}}</label>
+      <div class="controls">
+        {{combobox valueAttribute="value" content=availableLocales value=locale none="user.locale.default"}}
+      </div>
+      <div class='instructions'>
+        {{i18n user.locale.instructions}}
+      </div>
+    </div>
+
     <div class="control-group">
       <label class="control-label">{{i18n user.bio}}</label>
       <div class="controls">
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 091cac085..79d3d48aa 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -109,7 +109,11 @@ class ApplicationController < ActionController::Base
   end
 
   def set_locale
-    I18n.locale = SiteSetting.default_locale
+    I18n.locale = if current_user && current_user.locale.present?
+                    current_user.locale
+                  else
+                    SiteSetting.default_locale
+                  end
   end
 
   def store_preloaded(key, json)
diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb
index 25b92d0ed..8365c3bf6 100644
--- a/app/models/site_setting.rb
+++ b/app/models/site_setting.rb
@@ -23,6 +23,11 @@ class SiteSetting < ActiveRecord::Base
     load_settings(file)
   end
 
+  SiteSettingExtension.class_variable_get(:@@client_settings) << :available_locales
+
+  def self.available_locales
+    LocaleSiteSetting.values.map{ |e| e[:value] }.join('|')
+  end
 
   def self.call_discourse_hub?
     self.enforce_global_nicknames? && self.discourse_org_access_key.present?
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 7d93ffee7..4b55a2fd9 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -47,6 +47,7 @@ class UserSerializer < BasicUserSerializer
   end
 
   private_attributes :email,
+                     :locale,
                      :email_digests,
                      :email_private_messages,
                      :email_direct,
diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb
index ade9dd64c..025f0b376 100644
--- a/app/services/user_updater.rb
+++ b/app/services/user_updater.rb
@@ -27,6 +27,7 @@ class UserUpdater
 
     user.bio_raw = attributes.fetch(:bio_raw) { user.bio_raw }
     user.name = attributes.fetch(:name) { user.name }
+    user.locale = attributes.fetch(:locale) { user.locale }
     user.digest_after_days = attributes.fetch(:digest_after_days) { user.digest_after_days }
 
     if attributes[:auto_track_topics_after_msecs]
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index db9f1095f..dd209023b 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -325,6 +325,11 @@ en:
         enter_email: 'Username found. Enter matching email.'
         prefilled: "Email matches this registered username."
 
+      locale:
+        title: "Interface language"
+        instructions: "The language used by the forum interface. It will change when you refresh the page."
+        default: "(default)"
+
       password_confirmation:
         title: "Password Again"
 
diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml
index 3e277d146..af5196b5a 100644
--- a/config/locales/client.fr.yml
+++ b/config/locales/client.fr.yml
@@ -299,6 +299,11 @@ fr:
         enter_email: 'Pseudo trouvé. Entrez l''adresse email correspondante.'
         prefilled: "L'adresse email correspond à ce pseudo enregistré."
 
+      locale:
+        title: "Langue de l'interface"
+        instructions: "La langue utilisée dans l'interface du forum. Le changement se fait lorsque vous rechargez la page."
+        default: "(par défaut)"
+
       password_confirmation:
         title: "Confirmation"
 
diff --git a/db/migrate/20140206044818_add_locale_to_user.rb b/db/migrate/20140206044818_add_locale_to_user.rb
new file mode 100644
index 000000000..c0f2431b3
--- /dev/null
+++ b/db/migrate/20140206044818_add_locale_to_user.rb
@@ -0,0 +1,5 @@
+class AddLocaleToUser < ActiveRecord::Migration
+  def change
+    add_column :users, :locale, :string, limit: 10
+  end
+end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 00ba7824b..0cfc4ea80 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -44,6 +44,19 @@ describe TopicsController do
 
   end
 
+  describe 'set_locale' do
+    it 'sets the one the user prefers' do
+      SiteSetting.stubs(:allow_user_locale).returns(true)
+
+      user = Fabricate(:user, locale: :fr)
+      log_in_user(user)
+
+      get :show, {topic_id: topic.id}
+
+      I18n.locale.should == :fr
+    end
+  end
+
 end
 
 describe 'api' do

From 0592420e527bb8cedb168211f7223cd270fcf5da Mon Sep 17 00:00:00 2001
From: Jonathan Allard <jonathan@allard.io>
Date: Sat, 8 Feb 2014 20:35:46 -0500
Subject: [PATCH 2/2] Add a site setting to allow users to toggle I18n.locale

It is false by default.
---
 .../controllers/preferences_controller.js      |  4 ++++
 .../templates/user/preferences.js.handlebars   | 18 ++++++++++--------
 app/controllers/application_controller.rb      |  2 +-
 config/locales/server.en.yml                   |  1 +
 config/locales/server.fr.yml                   |  1 +
 config/site_settings.yml                       |  3 +++
 .../controllers/application_controller_spec.rb |  9 +++++++++
 7 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/app/assets/javascripts/discourse/controllers/preferences_controller.js b/app/assets/javascripts/discourse/controllers/preferences_controller.js
index 18d87d9b3..fa800b0a1 100644
--- a/app/assets/javascripts/discourse/controllers/preferences_controller.js
+++ b/app/assets/javascripts/discourse/controllers/preferences_controller.js
@@ -11,6 +11,10 @@ Discourse.PreferencesController = Discourse.ObjectController.extend({
     return Discourse.SiteSettings.allow_uploaded_avatars;
   }.property(),
 
+  allowUserLocale: function() {
+    return Discourse.SiteSettings.allow_user_locale;
+  }.property(),
+
   // By default we haven't saved anything
   saved: false,
 
diff --git a/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars b/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars
index 4a0560aa0..a29de784f 100644
--- a/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars
@@ -66,15 +66,17 @@
       </div>
     </div>
 
-    <div class="control-group">
-      <label class="control-label">{{i18n user.locale.title}}</label>
-      <div class="controls">
-        {{combobox valueAttribute="value" content=availableLocales value=locale none="user.locale.default"}}
+    {{#if allowUserLocale}}
+      <div class="control-group">
+        <label class="control-label">{{i18n user.locale.title}}</label>
+        <div class="controls">
+          {{combobox valueAttribute="value" content=availableLocales value=locale none="user.locale.default"}}
+        </div>
+        <div class='instructions'>
+          {{i18n user.locale.instructions}}
+        </div>
       </div>
-      <div class='instructions'>
-        {{i18n user.locale.instructions}}
-      </div>
-    </div>
+    {{/if}}
 
     <div class="control-group">
       <label class="control-label">{{i18n user.bio}}</label>
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 79d3d48aa..16a7e7196 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -109,7 +109,7 @@ class ApplicationController < ActionController::Base
   end
 
   def set_locale
-    I18n.locale = if current_user && current_user.locale.present?
+    I18n.locale = if SiteSetting.allow_user_locale && current_user && current_user.locale.present?
                     current_user.locale
                   else
                     SiteSetting.default_locale
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 74d7cc3c3..326d4aab9 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -549,6 +549,7 @@ en:
 
   site_settings:
     default_locale: "The default language of this Discourse instance (ISO 639-1 Code)"
+    allow_user_locale: "Allow users to choose their own language interface preference"
     min_post_length: "Minimum post length in characters"
     min_private_message_post_length: "Minimum post length in characters for private messages"
     max_post_length: "Maximum post length in characters"
diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml
index 71be63bd0..0826f1456 100644
--- a/config/locales/server.fr.yml
+++ b/config/locales/server.fr.yml
@@ -484,6 +484,7 @@ fr:
       description: "HTML qui sera ajouté au pied de toutes les pages"
   site_settings:
     default_locale: "Le langage par défaut de cette instance de Discourse (code ISO 639-1)"
+    allow_user_locale: "Permettre aux utilisateurs de choisir leur propre préférence de langue pour l'interface"
     min_post_length: "Longueur minimale des messages en nombre de caractères"
     min_private_message_post_length: "Longueur minimale des messages privés en nombre de caractères"
     max_post_length: "Longueur maximale des messages en nombres de caractères"
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 5f1579fce..5c3241c28 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -24,6 +24,9 @@ basic:
   default_locale:
     default: 'en'
     enum: 'LocaleSiteSetting'
+  allow_user_locale:
+    client: true
+    default: false
   ga_tracking_code:
     client: true
     default: ''
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 0cfc4ea80..082717646 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -55,6 +55,15 @@ describe TopicsController do
 
       I18n.locale.should == :fr
     end
+
+    it 'is sets the default locale when the setting not enabled' do
+      user = Fabricate(:user, locale: :fr)
+      log_in_user(user)
+
+      get :show, {topic_id: topic.id}
+
+      I18n.locale.should == :en
+    end
   end
 
 end