diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e3129760d..6d36e3ac4 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -28,6 +28,7 @@ class UsersController < ApplicationController params[:user].delete(:password) if params[:user][:password].blank? params[:user].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank? params[:user].delete(:language) if params[:user][:language].blank? + params[:user].delete(:grammatical_gender) if params[:user][:grammatical_gender].blank? if params[:user][:password] && params[:user][:password_confirmation] if @user.update_attributes(:password => params[:user][:password], :password_confirmation => params[:user][:password_confirmation]) @@ -37,7 +38,15 @@ class UsersController < ApplicationController end elsif params[:user][:language] if @user.update_attributes(:language => params[:user][:language]) - flash[:notice] = I18n.t 'users.update.language_changed' + if params[:user][:grammatical_gender] + if @user.update_attributes(:grammatical_gender => params[:user][:grammatical_gender]) + flash[:notice] = I18n.t 'users.update.language_changed' + else + flash[:error] = I18n.t 'users.update.language_not_changed' + end + else + flash[:notice] = I18n.t 'users.update.language_changed' + end else flash[:error] = I18n.t 'users.update.language_not_changed' end @@ -80,6 +89,7 @@ class UsersController < ApplicationController @step ||= 1 if @step == 4 + @user.grammatical_gender = I18n::Backend::Genderize.guess(@profile.gender) @user.getting_started = false @user.save end diff --git a/app/helpers/language_helper.rb b/app/helpers/language_helper.rb index 20a0db902..7bd5b3975 100644 --- a/app/helpers/language_helper.rb +++ b/app/helpers/language_helper.rb @@ -6,4 +6,30 @@ module LanguageHelper end options.sort_by { |o| o[0] } end -end \ No newline at end of file + + def options_for_gender_select(user) + grammatical_gender = user.grammatical_gender + genders_list = I18n::Backend::Genderize.known_genders.map do |gender| + [t("#{gender}"), gender] + end + if grammatical_gender.blank? + grammatical_gender = I18n::Backend::Genderize.guess(user.profile.gender) + end + options_for_select(genders_list, grammatical_gender.to_s) + end + + def gender_select_disabled(user) + not I18n::Backend::Genderize.supports?(user.language) + end + + def grammatical_gender_languages + @glang_cache ||= array_or_string_for_javascript(I18n::Backend::Genderize::SupportedLanguages) + end + + def options_for_grammatical_gender_block(user) + enabled = I18n::Backend::Genderize.supports? user.language + {:style => 'display: ' + (enabled ? 'inline' : 'none') + ';' + + ' margin-left: 1em; margin-right: 0.5em;' + } + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 039f7225c..8974e0be0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -29,6 +29,7 @@ class User key :getting_started, Boolean, :default => true key :language, String + key :grammatical_gender, String before_validation :strip_and_downcase_username, :on => :create before_validation :set_current_language, :on => :create @@ -38,6 +39,7 @@ class User validates_format_of :username, :with => /\A[A-Za-z0-9_.]+\z/ validates_length_of :username, :maximum => 32 validates_inclusion_of :language, :in => AVAILABLE_LANGUAGE_CODES + validates_inclusion_of :grammatical_gender, :in => I18n::Backend::Genderize::known_genders + [nil] validates_presence_of :person, :unless => proc {|user| user.invitation_token.present?} validates_associated :person @@ -61,7 +63,7 @@ class User person.save if person end - attr_accessible :getting_started, :password, :password_confirmation, :language, + attr_accessible :getting_started, :password, :password_confirmation, :language, :grammatical_gender def strip_and_downcase_username if username.present? diff --git a/app/views/users/edit.html.haml b/app/views/users/edit.html.haml index 9ecf06be0..d631c6df8 100644 --- a/app/views/users/edit.html.haml +++ b/app/views/users/edit.html.haml @@ -3,13 +3,29 @@ -# the COPYRIGHT file. :javascript - $("#settings_nav li > a").live("click", function() { - var target = "#"+$(this).attr('class'); - if( !$(target).is(":visible") ) { - $(".settings_pane").fadeOut(200, function() { - $(target).delay(200).fadeIn(200); - }); - } + var genderized_languages = #{grammatical_gender_languages} + + $(document).ready(function(){ + + $("#settings_nav li > a").live("click", function() { + var target = "#"+$(this).attr('class'); + if( !$(target).is(":visible") ) { + $(".settings_pane").fadeOut(200, function() { + $(target).delay(200).fadeIn(200); + }); + } + }); + + $("#user_language").change(function () { + var gselected = $("#user_language option:selected").val(); + if ( $.inArray(gselected, genderized_languages) < 0 ) { + $("#grammatical_gender_block").hide(800); + $("#user_grammatical_gender").attr("disabled", "disabled"); + } else { + $("#user_grammatical_gender").removeAttr("disabled"); + $("#grammatical_gender_block").show(800); + } + }); }); #section_header @@ -67,6 +83,9 @@ %p = f.select :language, available_language_options + %span#grammatical_gender_block{options_for_grammatical_gender_block(current_user)} + = " " + t('.address_me_as') + = select_tag 'user[grammatical_gender]', options_for_gender_select(current_user), :disabled => gender_select_disabled(current_user) = f.submit t('.change_language') %br diff --git a/config/initializers/locale.rb b/config/initializers/locale.rb index 86ae3c07b..93a99e5d1 100644 --- a/config/initializers/locale.rb +++ b/config/initializers/locale.rb @@ -1,3 +1,5 @@ +# Encoding: utf-8 +# # Copyright (c) 2010, Diaspora Inc. This file is # licensed under the Affero General Public License version 3 or later. See # the COPYRIGHT file. @@ -14,3 +16,90 @@ AVAILABLE_LANGUAGE_CODES.each do |c| I18n.fallbacks[c.to_sym] = [c.to_sym, DEFAULT_LANGUAGE.to_sym, :en] end end + +# Languages that require to specify biological gender (sex) +# in order to inflect properly when using nouns with second +# person in past tense. +module I18n + module Backend + module Genderize + + # Languages that need and support inflection. + SupportedLanguages = [ :ar, :lt, :pl, :ru, :sr, :uk ] + + # Genders table helps to initialize grammatical gender + # by looking at sociological gender entered by user. + Genders = { + :feminine => %w( f fem female feminine k kobieta pani woman + laska girl dziewczyna dziewucha chick lady mrs mrs. + miss missus missis mistress ms panna panienka ﺳﻴﺪۃ + dziewczynka żona zena sayyidah Пані Госпожа Г-жа ), + + :masculine => %w( m mal male masculine man dude guy gentleman + mr mister pan chłopak boy chłopiec koleś gość + lasek gostek monsieur hr herr Пан mr. سيد سادة + mężczyzna mąż chłopaczek facet sayyid Господин Г-н maleman ), + } + + Genders.default = :neuter + + end + end +end + +# Grammatical gender aware translate. +module I18n + module Backend + module Genderize + + def translate(locale, key, options = {}) + g = options.delete(:gender) + if not (g.nil? || key.is_a?(Enumerable)) + g = g.to_sym + subkey = Genders[g.to_sym] + key = "#{key}.#{subkey}".to_sym + end + super(locale, key, options) + end + + # Initialize fast mapping table using data from Genders. + def included(m) + return if instance_variable_defined?(:@genders_guesser) + @genders_guesser = {} + @known_genders = [] + Genders.each_pair do |gname,gtable| + @known_genders.push gname + gtable.each do |word| + @genders_guesser[word.to_sym] = gname + end + end + @genders_guesser.default = Genders.default + @known_genders.push Genders.default + @known_genders.map! { |g| g.to_s } + nil + end + module_function :included + + # Does language needs and supports inflection by gender? + def supports?(l=nil) + SupportedLanguages.include? l.nil? ? I18n.locale.to_sym : l.to_sym + end + module_function :supports? + + # Deduce grammatical gender using given gender and mapping. + def guess(gender_description="") + @genders_guesser[gender_description.downcase.to_sym] + end + module_function :guess + + # Array of strings with known grammatical genders. + def known_genders + @known_genders + end + module_function :known_genders + + end + end +end + +I18n::Backend::Simple.send(:include, I18n::Backend::Genderize)