diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 79dfdcaa1..cdec8a25a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -45,4 +45,5 @@ submit the change with your pull request. - Marlena Compton / [Marlena](https://github.com/marlena) - Elizabeth A. Kari / [catfriend](https://github.com/catfriend) - Cheri Allen / [cherimarie](https://github.com/cherimarie) +- Maki Sugita / [macckii](https:://github.com/macckii) - Shiho Takagi / [oshiho3](https://github.com/oshiho3) diff --git a/Gemfile b/Gemfile index d789f6f0e..d463e130e 100644 --- a/Gemfile +++ b/Gemfile @@ -131,4 +131,5 @@ group :development, :test do gem 'coveralls', require: false # coverage analysis gem 'capybara' # integration tests gem 'poltergeist', '~> 1.5.1' # for headless JS testing + gem 'i18n-tasks' # adds tests for finding missing and unused translations end diff --git a/Gemfile.lock b/Gemfile.lock index 6f4b323df..fae16d6ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -110,6 +110,10 @@ GEM warden (~> 1.2.3) diff-lcs (1.1.3) docile (1.1.5) + easy_translate (0.5.0) + json + thread + thread_safe erubis (2.7.0) execjs (2.2.1) factory_girl (4.4.0) @@ -138,11 +142,21 @@ GEM haml (>= 3.1, < 4.1) railties (>= 3.1, < 4.1) hashie (3.3.1) + highline (1.6.21) hike (1.2.3) httparty (0.11.0) multi_json (~> 1.0) multi_xml (>= 0.5.2) i18n (0.6.1) + i18n-tasks (0.7.6) + activesupport + easy_translate (>= 0.5.0) + erubis + highline + i18n + slop (>= 3.5.0) + term-ansicolor + terminal-table journey (1.0.4) jquery-rails (3.1.1) railties (>= 3.0, < 5.0) @@ -272,10 +286,12 @@ GEM sprockets (>= 1.0.2) term-ansicolor (1.3.0) tins (~> 1.0) + terminal-table (1.4.5) therubyracer (0.12.1) libv8 (~> 3.16.14.0) ref thor (0.19.1) + thread (0.1.4) thread_safe (0.3.4) tilt (1.4.1) tins (1.3.2) @@ -331,6 +347,7 @@ DEPENDENCIES gravatar-ultimate haml haml-rails + i18n-tasks jquery-rails jquery-ui-rails js-routes diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0d07cb866..e417984fa 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,6 +4,7 @@ class ApplicationController < ActionController::Base include ApplicationHelper after_filter :store_location + before_filter :set_locale def store_location if (request.path != "/members/sign_in" && @@ -32,5 +33,14 @@ class ApplicationController < ActionController::Base rescue_from CanCan::AccessDenied do |exception| redirect_to request.referer || root_url, :alert => exception.message end + + def set_locale + I18n.locale = params[:locale] || extract_locale_from_subdomain || I18n.default_locale + end + + def extract_locale_from_subdomain + parsed_locale = request.subdomains.first + I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil + end end diff --git a/app/views/home/_blurb.html.haml b/app/views/home/_blurb.html.haml index 3e742b8d4..3e85b4496 100644 --- a/app/views/home/_blurb.html.haml +++ b/app/views/home/_blurb.html.haml @@ -4,19 +4,13 @@ .row .col-md-8.info - %p - #{ENV['GROWSTUFF_SITE_NAME']} is a community of food gardeners. - We're building an open source platform to help you learn about - growing food, track what you plant and harvest, and swap - seeds and produce with other gardeners near you. + %p= t('.intro', site_name: ENV['GROWSTUFF_SITE_NAME']) = render :partial => 'stats' .col-md-4.signup - %p Join now for your free garden journal, seed sharing, forums, and more. - %p= link_to 'Sign up', new_member_registration_path, :class => 'btn btn-primary btn-lg' + %p= t('.perks') + %p= link_to(t('.sign_up'), new_member_registration_path, :class => 'btn btn-primary btn-lg') %p %small - Or - = link_to "sign in", new_member_session_path - if you already have an account. + = t('.already_html', sign_in: link_to(t('.sign_in_linktext'), new_member_session_path)) diff --git a/app/views/home/_crops.html.haml b/app/views/home/_crops.html.haml index 62b6d9e4a..ce3c775ce 100644 --- a/app/views/home/_crops.html.haml +++ b/app/views/home/_crops.html.haml @@ -1,14 +1,14 @@ .row .col-md-6.hidden-xs - cache "interesting_crops", :expires_in => 1.day do - %h2 Some of our crops + %h2= t('.our_crops') - Crop.interesting.each do |c| .col-md-3{:style => 'margin:0px; padding: 3px'} = render :partial => 'crops/image_with_popover', :locals => { :crop => c } .col-md-6 - cache "interesting_plantings" do - %h2 Recently planted + %h2= t('.recently_planted') = render :partial => 'plantings/list', :locals => { :plantings => Planting.interesting.first(4) } .row @@ -16,8 +16,8 @@ - cache "recent_crops" do %p{ :style => 'margin-top: 11.25px' } %strong - Recently added crops: + #{t('.recently_added')}: != Crop.recent.limit(12).map {|c| link_to(c, c) }.join(", ") %p.text-right - =link_to "View all crops »", crops_path + =link_to "#{t('.view_all')} »", crops_path diff --git a/app/views/home/_discuss.html.haml b/app/views/home/_discuss.html.haml index 7da71a54d..22b9972b9 100644 --- a/app/views/home/_discuss.html.haml +++ b/app/views/home/_discuss.html.haml @@ -1,4 +1,4 @@ -%h2 Discussion +%h2= t('.discussion') - cache "recent_posts" do - posts = Post.limit(6) @@ -10,9 +10,9 @@ - if forums %ul.list-inline %li - %strong Forums: + %strong #{t('.forums')}: - forums.each do |f| %li= link_to f.name, f %p.text-right - = link_to "View all posts »", posts_path + = link_to "#{t('.view_all')} »", posts_path diff --git a/app/views/home/_keep_in_touch.html.haml b/app/views/home/_keep_in_touch.html.haml index 34365352b..5bc821887 100644 --- a/app/views/home/_keep_in_touch.html.haml +++ b/app/views/home/_keep_in_touch.html.haml @@ -1,17 +1,14 @@ -%h2 Keep in touch +%h2= t('.keep_in_touch') %p =image_tag("twitter_32.png", :alt => '') - Follow - =link_to '@growstufforg', 'http://twitter.com/growstufforg' - on Twitter. + = t('.twitter_html', link: link_to(t('.twitter_linktext'), 'http://twitter.com/growstufforg')) %p =image_tag("blog_32.png", :alt => '') - Subscribe to the - =link_to 'Growstuff Blog', 'http://blog.growstuff.org/' + = t('.blog_html', link: link_to(t('.blog_linktext'), 'http://blog.growstuff.org/')) %p =image_tag("email_32.png", :alt => '') - Subscribe to our - =link_to 'email newsletter', 'http://blog.growstuff.org/newsletter' + = t('.newsletter_html', link: link_to(t('.newsletter_linktext'), 'http://blog.growstuff.org/newsletter')) + diff --git a/app/views/home/_members.html.haml b/app/views/home/_members.html.haml index 6d3437302..25cb0867b 100644 --- a/app/views/home/_members.html.haml +++ b/app/views/home/_members.html.haml @@ -2,7 +2,7 @@ .hidden-xs - members = Member.interesting.first(6) - if members.present? - %h2 Some of our members + %h2= t('.title') .row - members.each do |m| @@ -10,4 +10,4 @@ = render :partial => "members/thumbnail", :locals => { :member => m } %p.text-right - = link_to "View all members »", members_path + = link_to "#{t('.view_all')} »", members_path diff --git a/app/views/home/_open.html.haml b/app/views/home/_open.html.haml index 0cc44700e..fa7c00f57 100644 --- a/app/views/home/_open.html.haml +++ b/app/views/home/_open.html.haml @@ -1,50 +1,29 @@ -%h2 Open Source +%h2= t('.open_source_title') %p - #{ENV['GROWSTUFF_SITE_NAME']} is open source software, - which means that we share this website's code for free with our - community and the world. We believe that openness, - sustainability, and social good go hand in hand. You can read more - about - =link_to "why Growstuff is open source", "http://blog.growstuff.org/2013/02/20/why-growstuff-is-open-source/" - or check out our code on - =succeed '.' do - = link_to 'Github', 'http://github.com/Growstuff/growstuff' + = t('.open_source_body_html', why: link_to(t('.why_linktext'), 'http://blog.growstuff.org/2013/02/20/why-growstuff-is-open-source/'), + github: link_to(t('.github_linktext'), 'http://github.com/Growstuff/growstuff'), + site_name: ENV['GROWSTUFF_SITE_NAME'] ) -%h2 Open Data and APIs + +%h2= t('.open_data_title') %p - We're building a database - of crops, planting advice, seed sources, and other information - that anyone can use for free, under a - = succeed '.' do - = link_to 'Creative Commons license', 'http://creativecommons.org/licenses/by-sa/3.0/' - You can use this data for research, to build apps, or for any - purpose at all. Read more about our + = t('.open_data_body_html', creative_commons_link: link_to(t('.creative_commons_linktext'), 'http://creativecommons.org/licenses/by-sa/3.0/'), + wiki_link: link_to(t('.wiki_linktext'), 'http://wiki.growstuff.org/index.php/Open_data'), + api_docs_link: link_to(t('.api_docs_linktext'), 'http://wiki.growstuff.org/index.php/API')) - = link_to 'open data', 'http://wiki.growstuff.org/index.php/Open_data' - and - = succeed '.' do - = link_to 'API documentation', 'http://wiki.growstuff.org/index.php/API' -%h2 Get Involved + +%h2= t('.get_involved_title') %p - We believe in collaboration, and work closely with our members - and the wider food-growing community. - Our team includes volunteers from all walks of life - and all skill levels. To get involved, visit - = link_to 'Growstuff Talk', 'http://talk.growstuff.org/' - or find more information on the - = succeed "." do - = link_to 'Growstuff Wiki', 'http://wiki.growstuff.org/' + = t('.get_involved_body_html', talk_link: link_to(t('.talk_linktext'), 'http://talk.growstuff.org/'), + wiki_link: link_to(t('.wiki_linktext'), 'http://wiki.growstuff.org/')) -%h2 Support Growstuff + +%h2= t('.support_title') %p - Growstuff is independent, - =succeed "," do - =link_to 'ad-free', 'http://wiki.growstuff.org/index.php/Why_no_ads%3F' - and we have no outside investment. You can support our work by - =succeed "." do - =link_to 'buying a paid account', shop_path + = t('.support_body_html', ad_free: link_to(t('.ad_free_linktext'), 'http://wiki.growstuff.org/index.php/Why_no_ads%3F'), + buy_account: link_to(t('.buy_account_linktext'), shop_path)) diff --git a/app/views/home/_seeds.html.haml b/app/views/home/_seeds.html.haml index ccacd0bf4..52d996373 100644 --- a/app/views/home/_seeds.html.haml +++ b/app/views/home/_seeds.html.haml @@ -1,18 +1,18 @@ - seeds = Seed.interesting.first(6) - if seeds.present? - %h2 Seeds available to trade + %h2= t('.title') - cache "interesting_seeds" do - if seeds.length > 0 %table.table.table-striped %tr - %th Owner - %th Crop - %th.hidden-xs.hidden-sm Description - %th Will trade to - %th From location + %th= t('.owner') + %th= t('.crop') + %th.hidden-xs.hidden-sm= t('.description') + %th= t('.trade_to') + %th= t('.from') %th - seeds.each do |seed| @@ -23,8 +23,8 @@ %td= seed.tradable? ? seed.tradable_to : '' %td - if seed.tradable? - = seed.owner.location.blank? ? "unspecified" : truncate(seed.owner.location, :length => 25, :separator => ', ') - %td= link_to 'Details', seed, :class => 'btn btn-default btn-xs' + = seed.owner.location.blank? ? t('.unspecified') : truncate(seed.owner.location, :length => 25, :separator => ', ') + %td= link_to t('.details'), seed, :class => 'btn btn-default btn-xs' %p.text-right - = link_to "View all seeds »", seeds_path + = link_to "#{t('.view_all')} »", seeds_path diff --git a/app/views/home/_stats.html.haml b/app/views/home/_stats.html.haml index 93c16327d..e13f74719 100644 --- a/app/views/home/_stats.html.haml +++ b/app/views/home/_stats.html.haml @@ -1,10 +1,8 @@ - cache("homepage_stats") do %p.stats - So far, - = link_to "#{Member.confirmed.count.to_i} members", members_path - have planted - = link_to "#{Crop.count.to_i} crops", crops_path - = link_to "#{Planting.count.to_i} times", plantings_path - in - = succeed "." do - = link_to "#{Garden.count.to_i} gardens", gardens_path + = t('.message_html', { member: link_to(t('.member_linktext', count: Member.confirmed.count.to_i), members_path), + number_crops: link_to(t('.number_crops_linktext', count: Crop.count.to_i), crops_path), + number_plantings: link_to(t('.number_plantings_linktext', count: Planting.count.to_i), plantings_path), + number_gardens: link_to(t('.number_gardens_linktext', count: Garden.count.to_i), gardens_path) }) + + diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 57fb6473f..f36a29aab 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,19 +1,16 @@ .row .col-md-12 - if member_signed_in? - %h1 - Welcome to - = succeed "," do - = ENV['GROWSTUFF_SITE_NAME'] - = current_member + %h1= t('.welcome', site_name: ENV['GROWSTUFF_SITE_NAME'], member_name: current_member) + = render :partial => 'stats' %p .btn-group - = link_to "Plant", new_planting_path, :class => 'btn btn-default' - = link_to "Harvest", new_harvest_path, :class => 'btn btn-default' - = link_to "Add seeds", new_seed_path, :class => 'btn btn-default' - = link_to "Post", new_post_path, :class => 'btn btn-default' - = link_to "Edit profile", edit_member_registration_path, :class => 'btn btn-default' + = link_to t('.plant'), new_planting_path, :class => 'btn btn-default' + = link_to t('.harvest'), new_harvest_path, :class => 'btn btn-default' + = link_to t('.add_seeds'), new_seed_path, :class => 'btn btn-default' + = link_to t('.post'), new_post_path, :class => 'btn btn-default' + = link_to t('.edit_profile'), edit_member_registration_path, :class => 'btn btn-default' - else .hidden-xs diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml new file mode 100644 index 000000000..e1a1c35d9 --- /dev/null +++ b/config/i18n-tasks.yml @@ -0,0 +1,90 @@ +# i18n-tasks finds and manages missing and unused translations https://github.com/glebm/i18n-tasks + +base_locale: en +## i18n-tasks detects locales automatically from the existing locale files +## uncomment to set locales explicitly +# locales: [en, es, fr] + +## i18n-tasks report locale, default: en, available: en, ru +# internal_locale: ru + +# Read and write locale data +data: + ## by default, translation data are read from the file system, or you can provide a custom data adapter + # adapter: I18n::Tasks::Data::FileSystem + + # Locale files to read from + read: + - config/locales/%{locale}.yml + # - config/locales/*.%{locale}.yml + # - config/locales/**/*.%{locale}.yml + + # key => file routes, matched top to bottom + write: + ## E.g., write devise and simple form keys to their respective files + # - ['{devise, simple_form}.*', 'config/locales/\1.%{locale}.yml'] + # Catch-all + - config/locales/%{locale}.yml + # `i18n-tasks normalize -p` will force move the keys according to these rules + + # YAML / JSON serializer options, passed to load / dump / parse / serialize + yaml: + write: + # do not wrap lines at 80 characters + line_width: -1 + json: + write: + # pretty print JSON + indent: ' ' + space: ' ' + object_nl: "\n" + array_nl: "\n" + +# Find translate calls +search: + ## Default scanner finds t() and I18n.t() calls + # scanner: I18n::Tasks::Scanners::PatternWithScopeScanner + + ## Paths to search in, passed to File.find + paths: + - app/ + + ## Root for resolving relative keys (default) + # relative_roots: + # - app/views + + ## File.fnmatch patterns to exclude from search (default) + # exclude: ["*.jpg", "*.png", "*.gif", "*.svg", "*.ico", "*.eot", "*.ttf", "*.woff", "*.pdf"] + + ## Or, File.fnmatch patterns to include + # include: ["*.rb", "*.html.slim"] + + ## Lines starting with # or / are ignored by default + # ignore_lines: + # - "^\\s*[#/](?!\\si18n-tasks-use)" + +## Google Translate +# translation: +# # Get an API key and set billing info at https://code.google.com/apis/console to use Google Translate +# api_key: "AbC-dEf5" + +## Consider these keys not missing +# ignore_missing: +# - pagination.views.* + +## Consider these keys used +# ignore_unused: +# - 'simple_form.{yes,no}' +# - 'simple_form.{placeholders,hints,labels}.*' +# - 'simple_form.{error_notification,required}.:' + +## Exclude these keys from `i18n-tasks eq-base' report +# ignore_eq_base: +# all: +# - common.ok +# fr,es: +# - common.brand + +## Exclude these keys from all of the reports +# ignore: +# - kaminari.* diff --git a/config/initializers/locale.rb b/config/initializers/locale.rb new file mode 100644 index 000000000..fb251b800 --- /dev/null +++ b/config/initializers/locale.rb @@ -0,0 +1,4 @@ +I18n.load_path += Dir[Rails.root.join('config', 'locales', '*.{rb,yml}')] +I18n.default_locale = :en + + \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 7a30c72fe..39bb974ff 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -13,3 +13,81 @@ en: all: "Please sign in or sign up to create a %{subject}." manage: all: "Not authorized to %{action} %{subject}." + + home: + blurb: + intro: "%{site_name} is a community of food gardeners. We're building an open source platform to help you learn about growing food, track what you plant and harvest, and swap seeds and produce with other gardeners near you." + perks: "Join now for your free garden journal, seed sharing, forums, and more." + sign_up: "Sign up" + already_html: "Or %{sign_in} if you already have an account" + sign_in_linktext: "sign in" + + crops: + our_crops: "Some of our crops" + recently_planted: "Recently planted" + recently_added: "Recently added crops" + view_all: "View all crops" + + discuss: + discussion: "Discussion" + forums: "Forums" + view_all: "View all posts" + + keep_in_touch: + keep_in_touch: "Keep in touch" + twitter_html: "Follow %{link} on Twitter" + twitter_linktext: "@growstufforg" + blog_html: "Subscribe to the %{link}" + blog_linktext: "Growstuff Blog" + newsletter_html: "Subscribe to our %{link}" + newsletter_linktext: "email newsletter" + + members: + title: "Some of our members" + view_all: "View all members" + + open: + open_source_title: "Open Source" + open_source_body_html: "%{site_name} is open source software, which means that we share this website's code for free with our community and the world. We believe that openness, sustainability, and social good go hand in hand. You can read more about %{why} or check out our code on %{github}." + why_linktext: "why Growstuff is open source" + github_linktext: "Github" + open_data_title: "Open Data and APIs" + open_data_body_html: "We're building a database of crops, planting advice, seed sources, and other information that anyone can use for free, under a %{creative_commons_link}. You can use this data for research, to build apps, or for any purpose at all. Read more about our %{wiki_link} and %{api_docs_link}." + creative_commons_linktext: "Creative Commons license" + wiki_linktext: "open data" + api_docs_linktext: "API documentation" + get_involved_title: "Get Involved" + get_involved_body_html: "We believe in collaboration, and work closely with our members and the wider food-growing community. Our team includes volunteers from all walks of life and all skill levels. To get involved, visit %{talk_link} or find more information on the %{wiki_link}." + talk_linktext: "Growstuff Talk" + wiki_linktext: "Growstuff Wiki" + support_title: "Support Growstuff" + support_body_html: "Growstuff is independent, %{ad_free} and we have no outside investment. You can support our work by %{buy_account}." + ad_free_linktext: "ad-free" + buy_account_linktext: "buying a paid account" + + seeds: + title: "Seeds available to trade" + owner: "Owner" + crop: "Crop" + description: "Description" + trade_to: "Will trade to" + from: "From location" + unspecified: "unspecified" + details: "Details" + view_all: "View all seeds" + + stats: + message_html: "So far, %{member} have planted %{number_crops} %{number_plantings} in %{number_gardens}." + member_linktext: "%{count} members" + number_crops_linktext: "%{count} crops" + number_plantings_linktext: "%{count} times" + number_gardens_linktext: "%{count} gardens" + + index: + welcome: "Welcome to %{site_name}, %{member_name}" + plant: "Plant" + harvest: "Harvest" + add_seeds: "Add seeds" + post: "Post" + edit_profile: "Edit profile" + diff --git a/config/locales/ja.yml b/config/locales/ja.yml new file mode 100644 index 000000000..1f2fe3247 --- /dev/null +++ b/config/locales/ja.yml @@ -0,0 +1,78 @@ +ja: + home: + blurb: + intro: "%{site_name}はガーデナーのコミュニティです。" + perks: "翻訳中" + sign_up: "翻訳中" + already_html: "翻訳中" + sign_in_linktext: "翻訳中" + + crops: + our_crops: "翻訳中" + recently_planted: "翻訳中" + recently_added: "翻訳中" + view_all: "翻訳中" + + discuss: + discussion: "翻訳中" + forums: "翻訳中" + view_all: "翻訳中" + + keep_in_touch: + keep_in_touch: "翻訳中" + twitter_html: "翻訳中" + twitter_linktext: "翻訳中" + blog_html: "翻訳中" + blog_linktext: "翻訳中" + newsletter_html: "翻訳中" + newsletter_linktext: "翻訳中" + + members: + title: "翻訳中" + view_all: "翻訳中" + + open: + open_source_title: "翻訳中" + open_source_body_html: "翻訳中" + why_linktext: "翻訳中" + github_linktext: "翻訳中" + open_data_title: "翻訳中" + open_data_body_html: "翻訳中" + creative_commons_linktext: "翻訳中" + wiki_linktext: "翻訳中" + api_docs_linktext: "翻訳中" + get_involved_title: "翻訳中" + get_involved_body_html: "翻訳中" + talk_linktext: "翻訳中" + wiki_linktext: "翻訳中" + support_title: "翻訳中" + support_body_html: "翻訳中" + ad_free_linktext: "翻訳中" + + + seeds: + title: "翻訳中" + owner: "翻訳中" + crop: "翻訳中" + description: "翻訳中" + trade_to: "翻訳中" + from: "翻訳中" + unspecified: "翻訳中" + details: "翻訳中" + view_all: "翻訳中" + + stats: + message_html: "翻訳中" + member_linktext: "翻訳中" + number_crops_linktext: "翻訳中" + number_plantings_linktext: "翻訳中" + number_gardens_linktext: "翻訳中" + + index: + welcome: "翻訳中" + plant: "翻訳中" + harvest: "翻訳中" + add_seeds: "翻訳中" + post: "翻訳中" + edit_profile: "翻訳中" + diff --git a/spec/features/locale_spec.rb b/spec/features/locale_spec.rb new file mode 100644 index 000000000..9a787f6cc --- /dev/null +++ b/spec/features/locale_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +feature "Changing locales" do + + after do + I18n.locale = :en + end + + scenario "Locale can be set with a query param" do + visit "/" + expect(page).to have_content("a community of food gardeners.") + visit "/?locale=ja" + expect(page).to have_content("はガーデナーのコミュニティです。") + end + +end \ No newline at end of file