diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7072396b..50ebfdf7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,11 @@ class ApplicationController < ActionController::Base + include FragmentLocalization + protect_from_forgery helper_method :can_use_image_mode?, :user_is? + + before_filter :set_locale def authenticate_user! # too lazy to change references to login_path redirect_to(login_path) unless user_signed_in? @@ -15,6 +19,18 @@ class ApplicationController < ActionController::Base true end + # This locale-inferrence method is designed for debugging. Once we're ready + # for production, we'll probably start inferring from domain. + def infer_locale + inference = params[:locale] + inference if inference && I18n.available_locales.include?(inference.to_sym) + end + + def localized_fragment_exist?(key) + localized_key = localize_fragment_key(key, locale) + fragment_exist?(localized_key) + end + def not_found(record_name='record') raise ActionController::RoutingError.new("#{record_name} not found") end @@ -30,6 +46,10 @@ class ApplicationController < ActionController::Base def redirect_back!(default=:back) redirect_to(params[:return_to] || default) end + + def set_locale + I18n.locale = infer_locale || I18n.default_locale + end def user_is?(user) user_signed_in? && user == current_user diff --git a/app/controllers/outfits_controller.rb b/app/controllers/outfits_controller.rb index 2404e243..7bd48327 100644 --- a/app/controllers/outfits_controller.rb +++ b/app/controllers/outfits_controller.rb @@ -48,20 +48,16 @@ class OutfitsController < ApplicationController end def new - unless fragment_exist?(:action_suffix => 'start_from_scratch_form_content') + unless localized_fragment_exist?(:action_suffix => 'start_from_scratch_form_content') @colors = Color.all_ordered_by_name @species = Species.all_ordered_by_name end - unless fragment_exist?(:action_suffix => 'top_contributors') - @top_contributors = User.top_contributors.limit(User::PreviewTopContributorsCount) - end - - unless fragment_exist?('outfits#new newest_items') + unless localized_fragment_exist?('outfits#new newest_items') @newest_items = Item.newest.select([:id, :name, :thumbnail_url]).limit(9) end - unless fragment_exist?('outfits#new latest_contribution') + unless localized_fragment_exist?('outfits#new latest_contribution') @latest_contribution = Contribution.recent.first Contribution.preload_contributeds_and_parents([@latest_contribution]) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 21ccfecd..411fe7e1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,6 @@ module ApplicationHelper + include FragmentLocalization + def absolute_url(path_or_url) if path_or_url.include?('://') # already an absolute URL path_or_url @@ -96,6 +98,11 @@ module ApplicationHelper html + javascript_include_tag(JAVASCRIPT_LIBRARIES[name]) end) end + + def localized_cache(key, &block) + localized_key = localize_fragment_key(key, locale) + cache(localized_key, &block) + end def login_path_with_return_to login_path :return_to => request.fullpath @@ -150,5 +157,14 @@ module ApplicationHelper def title(value) content_for :title, value end + + def userbar_contributions_summary(user) + contributions_link_content = translate('.userbar.contributions_link_content', + :user_points => user.points) + contributions_link = link_to(contributions_link_content, + user_contributions_path(user)) + translate '.userbar.contributions_summary_html', + :contributions_link => contributions_link + end end diff --git a/app/helpers/contribution_helper.rb b/app/helpers/contribution_helper.rb index ec9cbc94..a59afd06 100644 --- a/app/helpers/contribution_helper.rb +++ b/app/helpers/contribution_helper.rb @@ -2,13 +2,17 @@ module ContributionHelper def contributed_description(contributed, image = true) case contributed when Item - contributed_item(contributed, image, 'for the first time') + suffix = translate_contributed_description('item_suffix') + contributed_item(contributed, image, suffix) when SwfAsset - contributed_item(contributed.item, image, 'on a new body type') + suffix = translate_contributed_description('swf_asset_suffix') + contributed_item(contributed.item, image, suffix) when PetType - contributed_pet_type(contributed, image, :after => 'for the first time') + suffix = translate_contributed_description('pet_type_suffix') + contributed_pet_type(contributed, image, :after => suffix) when PetState - contributed_pet_type(contributed.pet_type, image, :before => 'a new pose for') + prefix = translate_contributed_description('pet_state_prefix') + contributed_pet_type(contributed.pet_type, image, :before => prefix) end end @@ -41,4 +45,8 @@ module ContributionHelper def output(&block) raw([].tap(&block).join(' ')) end + + def translate_contributed_description(key) + translate "contributions.contributed_description.#{key}" + end end diff --git a/app/helpers/outfits_helper.rb b/app/helpers/outfits_helper.rb index e1f87339..be0d95eb 100644 --- a/app/helpers/outfits_helper.rb +++ b/app/helpers/outfits_helper.rb @@ -2,6 +2,14 @@ module OutfitsHelper def destination_tag(value) hidden_field_tag 'destination', value, :id => nil end + + def latest_contribution_description(contribution) + user = contribution.user + contributed = contribution.contributed + t 'outfits.new.latest_contribution_description_html', + :user_link => link_to(user.name, user_contributions_path(user)), + :contributed_description => contributed_description(contributed, false) + end def link_to_edit_outfit(content_or_outfit, outfit_or_options, options={}) if block_given? diff --git a/app/models/contribution_observer.rb b/app/models/contribution_observer.rb index 8fd87b3b..50ddb798 100644 --- a/app/models/contribution_observer.rb +++ b/app/models/contribution_observer.rb @@ -1,16 +1,12 @@ class ContributionObserver < ActiveRecord::Observer + include FragmentExpiration + def after_create(contribution) - controller.expire_fragment('outfits#new latest_contribution') + expire_fragment_in_all_locales('outfits#new latest_contribution') if contribution.contributed_type == 'SwfAsset' item = contribution.contributed.item - controller.expire_fragment("items/#{item.id} contributors") + expire_fragment("items/#{item.id} contributors") end end - - private - - def controller - @controller ||= ActionController::Base.new - end end diff --git a/app/models/fragment_expiration.rb b/app/models/fragment_expiration.rb new file mode 100644 index 00000000..f124c8c2 --- /dev/null +++ b/app/models/fragment_expiration.rb @@ -0,0 +1,18 @@ +module FragmentExpiration + include FragmentLocalization + + delegate :expire_fragment, :to => :controller + + def expire_fragment_in_all_locales(key) + I18n.available_locales.each do |locale| + localized_key = localize_fragment_key(key, locale) + expire_fragment(localized_key) + end + end + + private + + def controller + @controller ||= ActionController::Base.new + end +end diff --git a/app/models/fragment_localization.rb b/app/models/fragment_localization.rb new file mode 100644 index 00000000..012e14a4 --- /dev/null +++ b/app/models/fragment_localization.rb @@ -0,0 +1,11 @@ +module FragmentLocalization + def localize_fragment_key(key, locale) + if key.is_a?(Hash) + {:locale => locale}.merge(key) + elsif key.is_a?(String) + "#{key} #{locale}" + else + raise TypeError, "unexpected fragment key type: #{key.class}" + end + end +end diff --git a/app/models/item_observer.rb b/app/models/item_observer.rb index fb259f84..204da4d9 100644 --- a/app/models/item_observer.rb +++ b/app/models/item_observer.rb @@ -1,4 +1,6 @@ class ItemObserver < ActionController::Caching::Sweeper + include FragmentExpiration + def after_create(item) Rails.logger.debug "Item #{item.id} was just created" expire_newest_items @@ -16,18 +18,14 @@ class ItemObserver < ActionController::Caching::Sweeper private - def controller - @controller ||= ActionController::Base.new - end - def expire_cache_for(item) - controller.expire_fragment("items/#{item.id}#item_link_partial") - controller.expire_fragment("items/#{item.id} header") - controller.expire_fragment("items/#{item.id} info") + expire_fragment("items/#{item.id}#item_link_partial") + expire_fragment("items/#{item.id} header") + expire_fragment("items/#{item.id} info") end def expire_newest_items - controller.expire_fragment('outfits#new newest_items') - controller.expire_fragment('items#index newest_items') + expire_fragment_in_all_locales('outfits#new newest_items') + expire_fragment('items#index newest_items') end end diff --git a/app/stylesheets/outfits/_new.sass b/app/stylesheets/outfits/_new.sass index ef873876..2e760c8e 100644 --- a/app/stylesheets/outfits/_new.sass +++ b/app/stylesheets/outfits/_new.sass @@ -203,6 +203,9 @@ body.outfits-new #recent-contributions-link font-weight: bold margin-right: .5em + + &::after + content: ":" #latest-contribution-created-at color: $soft-text-color diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 2c5437c3..72ece6a0 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -8,7 +8,7 @@ - if content_for? :title_category = yield :title_category - else - Dress to Impress: Preview customized Neopets' clothing and wearables + #{t 'app_name'}: #{t '.title_tagline'} /[if IE] = include_javascript_libraries :html5 = yield :stylesheets @@ -34,45 +34,34 @@ - if home_link? %a#home-link{:href => root_path} - %span Dress to Impress + %span= t 'app_name' #userbar - if user_signed_in? %span - Hey, #{current_user.name}! - You have - = succeed '.' do - = link_to "#{current_user.points} points", user_contributions_path(current_user) - = link_to 'Items', user_closet_hangers_path(current_user), :id => 'userbar-items-link' - = link_to 'Outfits', current_user_outfits_path - = link_to 'Settings', Openneo::Auth.remote_settings_url - = link_to 'Log out', logout_path_with_return_to + = t '.userbar.greeting', :user_name => current_user.name + = userbar_contributions_summary(current_user) + = link_to t('.userbar.items'), user_closet_hangers_path(current_user), :id => 'userbar-items-link' + = link_to t('.userbar.outfits'), current_user_outfits_path + = link_to t('.userbar.settings'), Openneo::Auth.remote_settings_url + = link_to t('.userbar.logout'), logout_path_with_return_to - else = link_to login_path_with_return_to, :id => 'userbar-log-in' do = image_tag auth_server_icon_url - %span Log in + %span= t('.userbar.login') #footer %ul - %li - %a{:href => "http://openneo.net/", :target => "_blank"} OpenNeo - %li - %a{:href => "http://blog.openneo.net/", :target => "_blank"} Blog - %li - %a{:href => "http://forum.openneo.net/", :target => "_blank"} Forum - %li - %a{:href => "http://github.com/matchu/openneo-impress-rails"} Source Code - %li - %a{:href => terms_path} Terms of Use + %li= link_to t('organization_name'), 'http://openneo.net/' + %li= link_to t('.footer.blog'), 'http://blog.openneo.net/' + %li= link_to t('.footer.source_code'), + 'http://github.com/matchu/openneo-impress-rails' + %li= link_to t('.footer.terms'), terms_path %div - Contact: + #{t('.footer.contact')}: %ul - %li - %a{:href => feedback_url} Suggestions - %li - %a{:href => "mailto:#{contact_email}"} Questions, comments, bugs - %p - Images © 2000–#{Date.today.year} Neopets, Inc. All Rights Reserved. - Used With Permission + %li= link_to t('.footer.suggestions'), feedback_url + %li= mail_to contact_email, t('.footer.email') + %p= t '.footer.copyright', :year => Date.today.year = yield(:javascripts) diff --git a/app/views/outfits/new.html.haml b/app/views/outfits/new.html.haml index 2f319032..1847d36e 100644 --- a/app/views/outfits/new.html.haml +++ b/app/views/outfits/new.html.haml @@ -3,100 +3,86 @@ = campaign_progress #outfit-forms - - cache :action_suffix => 'outfit_forms_intro' do + - localized_cache :action_suffix => 'outfit_forms_intro' do #pet-preview = image_tag 'default_preview.png', :alt => '' %span - %h1 Dress to Impress - %h2 Neopets wearables made easy! + %h1= t 'app_name' + %h2= t '.tagline' = form_tag load_pet_path, :id => 'load-pet-to-wardrobe' do - - cache :action_suffix => 'main_load_pet_form_content' do + - localized_cache :action_suffix => 'main_load_pet_form_content' do = origin_tag root_path = destination_tag 'wardrobe' %fieldset - %legend Enter your pet's name + %legend= t '.load_pet_legend' = pet_name_tag :id => 'main-pet-name' %button{:type => "submit"} - Plan my outfit! + = t '.load_pet_submit' = form_tag wardrobe_path, :method => 'get', :id => 'start-from-scratch' do - - cache :action_suffix => 'start_from_scratch_form_content' do + - localized_cache :action_suffix => 'start_from_scratch_form_content' do %fieldset - %legend Or start from scratch + %legend= t '.start_from_scratch_legend' = pet_attribute_select 'color', @colors, 8 = pet_attribute_select 'species', @species - %input{:type => "submit", :value => "Go"} + %input{:type => "submit", :value => t('.start_from_scratch_submit')} %ul#sections - - cache :action_suffix => 'your_items_module' do + - localized_cache :action_suffix => 'your_items_module' do %li#your-items-module = link_to image_tag('your_items.png'), your_items_path - %h3 - = link_to 'Your Items', your_items_path + %h3= link_to t('your_items'), your_items_path %div - %h4 Track and trade! - %p - Make lists of the items you own and want, and share them with the - world. + %h4= t '.your_items_tagline' + %p= t '.your_items_description' = form_tag users_path, :method => 'get' do - = text_field_tag 'name', '', :placeholder => raw('find a user…'), :type => 'search' - = submit_tag 'search' + = text_field_tag 'name', '', :type => 'search', + :placeholder => t('.your_items_user_search_placeholder') + = submit_tag t('.your_items_user_search_submit') - - cache :action_suffix => 'infinite_closet_module' do + - localized_cache :action_suffix => 'infinite_closet_module' do %li - %a{:href => items_path} - = image_tag 'items.png' - %h3 - %a{:href => items_path} - Infinite Closet + = link_to image_tag('items.png'), items_path + %h3= link_to t('infinite_closet'), items_path %div - %h4 Looking for something? - %p - Take a look through our wearables database! + %h4= t '.infinite_closet_tagline' + %p= t '.infinite_closet_description' = form_tag items_path, :method => 'get' do - = text_field_tag 'q', '', :placeholder => raw('find an item…'), :type => 'search' - = submit_tag 'search' + = text_field_tag 'q', '', :type => 'search', + :placeholder => t('.infinite_closet_item_search_placeholder') + = submit_tag t('.infinite_closet_item_search_submit') %li - %a{:href => bulk_pets_path} + = link_to bulk_pets_path do = image_tag 'http://images.neopets.com/items/mall_ac_garland_spotlight.gif' - %h3 - %a{:href => bulk_pets_path} - Modeling Hub + %h3= link_to t('modeling_hub'), bulk_pets_path %div - %h4 Found something? - %p - Enter a pet's name here and we'll keep a copy of what it's wearing. - Thanks so much! + %h4= t '.modeling_hub_tagline' + %p= t '.modeling_hub_description' = form_tag load_pet_path do = origin_tag root_path - = pet_name_tag :placeholder => raw('model a pet…') - = submit_tag 'submit' + = pet_name_tag :placeholder => t('.modeling_hub_load_pet_placeholder') + = submit_tag t('.modeling_hub_load_pet_submit') -- cache 'outfits#new latest_contribution' do +- localized_cache 'outfits#new latest_contribution' do #latest-contribution - = link_to 'Contributions:', contributions_path, :id => 'recent-contributions-link' - = link_to @latest_contribution.user.name, user_contributions_path(@latest_contribution.user) - showed us - = succeed '.' do - = contributed_description @latest_contribution.contributed, false - Thanks, - = succeed '!' do - = link_to @latest_contribution.user.name, user_contributions_path(@latest_contribution.user) + = link_to t('.latest_contribution_header'), contributions_path, :id => 'recent-contributions-link' + = latest_contribution_description(@latest_contribution) %abbr#latest-contribution-created-at{:title => @latest_contribution.created_at.getutc.iso8601} #whats-new - - cache :action_suffix => 'blog_preview' do + - localized_cache :action_suffix => 'blog_preview' do #blog-preview %h2 %div - %a#blog-preview-linkback{:href => 'http://blog.openneo.net/'} OpenNeo Blog + %a#blog-preview-linkback{:href => 'http://blog.openneo.net/'} + = t '.blog_linkback' - - cache 'outfits#new newest_items' do + - localized_cache 'outfits#new newest_items' do #newest-items - %h2 New Items + %h2= t '.newest_items_header' %ul - @newest_items.each do |item| = link_to image_tag(item.thumbnail_url), item diff --git a/config/environments/development.rb b/config/environments/development.rb index 03ae1808..4380159b 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -12,7 +12,7 @@ OpenneoImpressItems::Application.configure do # Show full error reports and disable caching config.consider_all_requests_local = true config.action_view.debug_rjs = true - config.action_controller.perform_caching = false + config.action_controller.perform_caching = true # Don't care if the mailer can't send config.action_mailer.raise_delivery_errors = false diff --git a/config/locales/en-meep.yml b/config/locales/en-meep.yml new file mode 100644 index 00000000..1edebc6f --- /dev/null +++ b/config/locales/en-meep.yml @@ -0,0 +1,67 @@ +en-meep: + app_name: Dreep to Impreep + organization_name: OpenMeep + your_items: Your Meeps + infinite_closet: Infinite Meepit + modeling_hub: Meepiting Hub + + layouts: + application: + title_tagline: Preview customized Neopets' meeps and meepits + + userbar: + greeting: Meep, %{user_name}! + contributions_summary_html: You have %{contributions_link}. + contributions_link_content: "%{user_points} meeps" + items: Iteeps + outfits: Outfeeps + settings: Setteeps + logout: Meep out + login: Meep in + + footer: + blog: Bleep + source_code: Source Meep + terms: Terms of Meep + contact: Meeptact + suggestions: Suggesteeps + email: Questions, comments, meepits + copyright: + Images © 2000–%{year} Neopets, Inc. All Rights Reserved. + Used With Permission. Meep. + + outfits: + new: + tagline: Meeps made meepy! + load_pet_legend: Enter your pet's meep + load_pet_submit: Meep my outfit! + start_from_scratch_legend: Or meep from scratch + start_from_scratch_submit: Meep + your_items_tagline: Meep and meep! + your_items_description: + Meep lists of the items you own and want, and meep them with the world. + your_items_user_search_placeholder: meep a user… + your_items_user_search_submit: meep + infinite_closet_tagline: Meeping for something? + infinite_closet_description: Take a meep through our wearables meep! + infinite_closet_item_search_placeholder: meep an item… + infinite_closet_item_search_submit: meep + modeling_hub_tagline: Found somemeep? + modeling_hub_description: + Meep a pet's meep here and we'll meep a meep of what it's wearing. + Thanks so meep! + modeling_hub_load_pet_placeholder: meep a pet… + modeling_hub_load_pet_submit: meep + latest_contribution_header: Contribumeeps + latest_contribution_description_html: + "%{user_link} meeped us %{contributed_description}. + Meep, %{user_link}!" + blog_linkback: OpenNeo Meep + newest_items_header: New Meeps + + contributions: + contributed_description: + item_suffix: "for the first meep" + swf_asset_suffix: "on a new body meep" + pet_type_suffix: "for the first meep" + pet_state_prefix: "a new meep for" diff --git a/config/locales/en.yml b/config/locales/en.yml index a747bfa6..cfeea61e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,5 +1,68 @@ -# Sample localization file for English. Add more files in this directory for other locales. -# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. - en: - hello: "Hello world" + app_name: Dress to Impress + organization_name: OpenNeo + your_items: Your Items + infinite_closet: Infinite Closet + modeling_hub: Modeling Hub + + layouts: + application: + title_tagline: Preview customized Neopets' clothing and wearables + + userbar: + greeting: Hey, %{user_name}! + contributions_summary_html: You have %{contributions_link}. + contributions_link_content: "%{user_points} points" + items: Items + outfits: Outfits + settings: Settings + logout: Log out + login: Log in + + footer: + blog: Blog + source_code: Source Code + terms: Terms of Use + contact: Contact + suggestions: Suggestions + email: Questions, comments, bugs + copyright: + Images © 2000–%{year} Neopets, Inc. All Rights Reserved. + Used With Permission + + outfits: + new: + tagline: Neopets wearables made easy! + load_pet_legend: Enter your pet's name + load_pet_submit: Plan my outfit! + start_from_scratch_legend: Or start from scratch + start_from_scratch_submit: Go + your_items_tagline: Track and trade! + your_items_description: + Make lists of the items you own and want, + and share them with the world. + your_items_user_search_placeholder: find a user… + your_items_user_search_submit: search + infinite_closet_tagline: Looking for something? + infinite_closet_description: Take a look through our wearables database! + infinite_closet_item_search_placeholder: find an item… + infinite_closet_item_search_submit: search + modeling_hub_tagline: Found something? + modeling_hub_description: + Enter a pet's name here and we'll keep a copy of what it's wearing. + Thanks so much! + modeling_hub_load_pet_placeholder: model a pet… + modeling_hub_load_pet_submit: submit + latest_contribution_header: Contributions + latest_contribution_description_html: + "%{user_link} showed us %{contributed_description}. + Thanks, %{user_link}!" + blog_linkback: OpenNeo Blog + newest_items_header: New Items + + contributions: + contributed_description: + item_suffix: "for the first time" + swf_asset_suffix: "on a new body type" + pet_type_suffix: "for the first time" + pet_state_prefix: "a new pose for" diff --git a/public/stylesheets/compiled/screen.css b/public/stylesheets/compiled/screen.css index 5e6bfc2f..dee2bedc 100644 --- a/public/stylesheets/compiled/screen.css +++ b/public/stylesheets/compiled/screen.css @@ -3967,6 +3967,10 @@ body.outfits-new #latest-contribution #recent-contributions-link { margin-right: 0.5em; } /* line 207, ../../../app/stylesheets/outfits/_new.sass */ +body.outfits-new #latest-contribution #recent-contributions-link::after { + content: ":"; +} +/* line 210, ../../../app/stylesheets/outfits/_new.sass */ body.outfits-new #latest-contribution #latest-contribution-created-at { color: #448844; margin-left: 0.5em;