From f7486b92386ce4d50ef8e8411bf87666fb0c9b82 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 10 Apr 2018 13:15:36 +0200 Subject: [PATCH 1/9] Add Globalize to Milestones --- Gemfile | 2 + Gemfile.lock | 7 +++ app/assets/javascripts/application.js | 2 + app/assets/javascripts/globalize.js.coffee | 31 ++++++++++ ...budget_investment_milestones_controller.rb | 4 +- app/controllers/concerns/translatable.rb | 19 ++++++ app/helpers/globalize_helper.rb | 33 ++++++++++ app/models/budget/investment/milestone.rb | 3 + .../_form.html.erb | 20 +++++- .../budgets/investments/_milestones.html.erb | 8 ++- .../milestones/_globalize_locales.html.erb | 14 +++++ .../milestones/_milestone.html.erb | 42 +++++++++++++ ...20180323190027_add_translate_milestones.rb | 12 ++++ db/schema.rb | 12 ++++ spec/features/translations_spec.rb | 61 +++++++++++++++++++ 15 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/globalize.js.coffee create mode 100644 app/controllers/concerns/translatable.rb create mode 100644 app/helpers/globalize_helper.rb create mode 100644 app/views/budgets/investments/milestones/_globalize_locales.html.erb create mode 100644 app/views/budgets/investments/milestones/_milestone.html.erb create mode 100644 db/migrate/20180323190027_add_translate_milestones.rb create mode 100644 spec/features/translations_spec.rb diff --git a/Gemfile b/Gemfile index 8c3144058..f7f9c46ad 100644 --- a/Gemfile +++ b/Gemfile @@ -51,6 +51,8 @@ gem 'turnout', '~> 2.4.0' gem 'uglifier', '~> 4.1.2' gem 'unicorn', '~> 5.4.0' gem 'whenever', '~> 0.10.0', require: false +gem 'globalize', '~> 5.0.0' +gem 'globalize-accessors', '~> 0.2.1' source 'https://rails-assets.org' do gem 'rails-assets-leaflet' diff --git a/Gemfile.lock b/Gemfile.lock index 6c35b0776..aa4a00eab 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -176,6 +176,11 @@ GEM geocoder (1.4.4) globalid (0.4.1) activesupport (>= 4.2.0) + globalize (5.0.1) + activemodel (>= 4.2.0, < 4.3) + activerecord (>= 4.2.0, < 4.3) + globalize-accessors (0.2.1) + globalize (~> 5.0, >= 5.0.0) graphiql-rails (1.4.8) rails graphql (1.7.8) @@ -518,6 +523,8 @@ DEPENDENCIES faker (~> 1.8.7) foundation-rails (~> 6.2.4.0) foundation_rails_helper (~> 2.0.0) + globalize (~> 5.0.0) + globalize-accessors (~> 0.2.1) graphiql-rails (~> 1.4.1) graphql (~> 1.7.8) groupdate (~> 3.2.0) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 7a110aa18..1c97069b0 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -77,6 +77,7 @@ //= require investment_report_alert //= require send_newsletter_alert //= require managers +//= require globalize var initialize_modules = function() { App.Comments.initialize(); @@ -121,6 +122,7 @@ var initialize_modules = function() { App.InvestmentReportAlert.initialize(); App.SendNewsletterAlert.initialize(); App.Managers.initialize(); + App.Globalize.initialize(); }; $(function(){ diff --git a/app/assets/javascripts/globalize.js.coffee b/app/assets/javascripts/globalize.js.coffee new file mode 100644 index 000000000..6ac39e71c --- /dev/null +++ b/app/assets/javascripts/globalize.js.coffee @@ -0,0 +1,31 @@ +App.Globalize = + + display_locale: (locale) -> + $(".js-globalize-locale-link").each -> + if $(this).data("locale") == locale + $(this).show() + $(".js-globalize-locale option:selected").removeAttr("selected"); + return + + display_translations: (locale) -> + $(".js-globalize-attribute").each -> + console.log("In standard") + console.log($(this)) + if $(this).data("locale") == locale + $(this).show() + else + $(this).hide() + + highlight_locale: (element) -> + $('.js-globalize-locale-link').removeClass('highlight'); + element.addClass('highlight'); + + initialize: -> + $('.js-globalize-locale').on 'change', -> + App.Globalize.display_translations($(this).val()) + App.Globalize.display_locale($(this).val()) + + $('.js-globalize-locale-link').on 'click', -> + locale = $(this).data("locale") + App.Globalize.display_translations(locale) + App.Globalize.highlight_locale($(this)) \ No newline at end of file diff --git a/app/controllers/admin/budget_investment_milestones_controller.rb b/app/controllers/admin/budget_investment_milestones_controller.rb index e4aebd3cf..13bfb6460 100644 --- a/app/controllers/admin/budget_investment_milestones_controller.rb +++ b/app/controllers/admin/budget_investment_milestones_controller.rb @@ -1,4 +1,5 @@ class Admin::BudgetInvestmentMilestonesController < Admin::BaseController + include Translatable before_action :load_budget_investment, only: [:index, :new, :create, :edit, :update, :destroy] before_action :load_budget_investment_milestone, only: [:edit, :update, :destroy] @@ -46,7 +47,8 @@ class Admin::BudgetInvestmentMilestonesController < Admin::BaseController documents_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy] attributes = [:title, :description, :publication_date, :budget_investment_id, image_attributes: image_attributes, documents_attributes: documents_attributes] - params.require(:budget_investment_milestone).permit(*attributes) + + params.require(:budget_investment_milestone).permit(*attributes, translation_params) end def load_budget_investment diff --git a/app/controllers/concerns/translatable.rb b/app/controllers/concerns/translatable.rb new file mode 100644 index 000000000..2325fc013 --- /dev/null +++ b/app/controllers/concerns/translatable.rb @@ -0,0 +1,19 @@ +module Translatable + extend ActiveSupport::Concern + + included do + before_action :set_translation_locale + end + + private + + def translation_params + Budget::Investment::Milestone.globalize_attribute_names. + select { |k, v| params[:budget_investment_milestone].include?(k.to_sym) && params[:budget_investment_milestone][k].present? } + end + + def set_translation_locale + Globalize.locale = I18n.locale + end + +end \ No newline at end of file diff --git a/app/helpers/globalize_helper.rb b/app/helpers/globalize_helper.rb new file mode 100644 index 000000000..e0d11f611 --- /dev/null +++ b/app/helpers/globalize_helper.rb @@ -0,0 +1,33 @@ +module GlobalizeHelper + + def globalize_locale + params[:globalize_locale] || I18n.locale + end + + def options_for_locale_select + options_for_select(locale_options, nil) + end + + def locale_options + I18n.available_locales.map do |locale| + [name_for_locale(locale), locale] + end + end + + def display_translation?(locale) + I18n.locale == locale ? "" : "display: none" + end + + def css_to_display_translation?(resource, locale) + resource.translated_locales.include?(locale) || locale == I18n.locale ? "" : "display: none" + end + + def disable_translation?(locale) + locale == "en" ? "" : "disabled" + end + + def css_for_globalize_locale(locale) + globalize_locale == locale ? "highlight" : "" + end + +end \ No newline at end of file diff --git a/app/models/budget/investment/milestone.rb b/app/models/budget/investment/milestone.rb index 30acb5152..5553476ea 100644 --- a/app/models/budget/investment/milestone.rb +++ b/app/models/budget/investment/milestone.rb @@ -7,6 +7,9 @@ class Budget max_file_size: 3.megabytes, accepted_content_types: [ "application/pdf" ] + translates :description, touch: true + globalize_accessors locales: [:en, :es, :fr, :nl, :val] + belongs_to :investment validates :title, presence: true diff --git a/app/views/admin/budget_investment_milestones/_form.html.erb b/app/views/admin/budget_investment_milestones/_form.html.erb index 2b2af4b50..9dae79b6e 100644 --- a/app/views/admin/budget_investment_milestones/_form.html.erb +++ b/app/views/admin/budget_investment_milestones/_form.html.erb @@ -1,7 +1,23 @@ +
+ <%= render "budgets/investments/milestones/globalize_locales" %> +
+ <%= form_for [:admin, @investment.budget, @investment, @milestone] do |f| %> - <%= f.hidden_field :title, value: l(Time.current, format: :datetime), maxlength: Budget::Investment::Milestone.title_max_length %> - <%= f.text_area :description, rows: 5 %> + <%= f.hidden_field :title, value: l(Time.current, format: :datetime), + maxlength: Budget::Investment::Milestone.title_max_length %> + + <%= f.label :description, t("admin.milestones.new.description") %> + <% @milestone.globalize_locales.each do |locale| %> + <% Globalize.with_locale(locale) do %> + <%= f.text_area "description_#{locale}", rows: 5, + class: "js-globalize-attribute", + data: { locale: locale }, + style: display_translation?(locale), + label: false %> + <% end %> + <% end %> + <%= f.label :publication_date, t("admin.milestones.new.date") %> <%= f.text_field :publication_date, value: @milestone.publication_date.present? ? l(@milestone.publication_date.to_date) : nil, diff --git a/app/views/budgets/investments/_milestones.html.erb b/app/views/budgets/investments/_milestones.html.erb index 5fc984808..afada7fc2 100644 --- a/app/views/budgets/investments/_milestones.html.erb +++ b/app/views/budgets/investments/_milestones.html.erb @@ -23,7 +23,11 @@ { alt: milestone.image.title.unicode_normalize, class: "margin", id: "image_#{milestone.id}" }) if milestone.image.present? %> -

<%= text_with_links milestone.description %>

+

+ <% Globalize.with_locale(locale) do %> + <%= text_with_links milestone.description %> + <% end %> +

<% if milestone.documents.present? %> - + \ No newline at end of file diff --git a/app/views/budgets/investments/milestones/_globalize_locales.html.erb b/app/views/budgets/investments/milestones/_globalize_locales.html.erb new file mode 100644 index 000000000..fc291ea96 --- /dev/null +++ b/app/views/budgets/investments/milestones/_globalize_locales.html.erb @@ -0,0 +1,14 @@ +<% I18n.available_locales.each do |locale| %> + + <%= link_to name_for_locale(locale), "#", + style: css_to_display_translation?(@milestone, locale), + class: "js-globalize-locale-link", + data: { locale: locale }, + remote: true %> + +<% end %> + +<%= select_tag :translation_locale, + options_for_locale_select, + prompt: "Añadir idioma", + class: "js-globalize-locale" %> \ No newline at end of file diff --git a/app/views/budgets/investments/milestones/_milestone.html.erb b/app/views/budgets/investments/milestones/_milestone.html.erb new file mode 100644 index 000000000..2113fd06e --- /dev/null +++ b/app/views/budgets/investments/milestones/_milestone.html.erb @@ -0,0 +1,42 @@ +
  • +
    + + <% if milestone.publication_date.present? %> + + + <%= t("budgets.investments.show.milestone_publication_date", + publication_date: l(milestone.publication_date.to_date)) %> + + + <% end %> + + <% if milestone.image.present? %> + <%= image_tag(milestone.image_url(:large), + { id: "image_#{milestone.id}", + alt: milestone.image.title, + class: "margin" }) %> + <% end %> + +

    + <% Globalize.with_locale(locale) do %> + <%= text_with_links milestone.description %> + <% end %> +

    + + <% if milestone.documents.present? %> + + <% end %> + +
    +
  • \ No newline at end of file diff --git a/db/migrate/20180323190027_add_translate_milestones.rb b/db/migrate/20180323190027_add_translate_milestones.rb new file mode 100644 index 000000000..6e27583a8 --- /dev/null +++ b/db/migrate/20180323190027_add_translate_milestones.rb @@ -0,0 +1,12 @@ +class AddTranslateMilestones < ActiveRecord::Migration + def self.up + Budget::Investment::Milestone.create_translation_table!({ + title: :string, + description: :text + }) + end + + def self.down + Budget::Investment::Milestone.drop_translation_table! + end +end diff --git a/db/schema.rb b/db/schema.rb index c1ee88246..03a56453a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -118,6 +118,18 @@ ActiveRecord::Schema.define(version: 20180320104823) do add_index "budget_headings", ["group_id"], name: "index_budget_headings_on_group_id", using: :btree + create_table "budget_investment_milestone_translations", force: :cascade do |t| + t.integer "budget_investment_milestone_id", null: false + t.string "locale", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "title" + t.text "description" + end + + add_index "budget_investment_milestone_translations", ["budget_investment_milestone_id"], name: "index_6770e7675fe296cf87aa0fd90492c141b5269e0b", using: :btree + add_index "budget_investment_milestone_translations", ["locale"], name: "index_budget_investment_milestone_translations_on_locale", using: :btree + create_table "budget_investment_milestones", force: :cascade do |t| t.integer "investment_id" t.string "title", limit: 80 diff --git a/spec/features/translations_spec.rb b/spec/features/translations_spec.rb new file mode 100644 index 000000000..944f5bd22 --- /dev/null +++ b/spec/features/translations_spec.rb @@ -0,0 +1,61 @@ +require 'rails_helper' + +feature "Translations" do + + context "Milestones" do + + let(:investment) { create(:budget_investment) } + + background do + admin = create(:administrator) + login_as(admin.user) + end + + scenario "Add a translation", :js, :focus do + milestone = create(:budget_investment_milestone, description: "Description in English") + + edit_milestone_url = edit_admin_budget_budget_investment_budget_investment_milestone_path(investment.budget, investment, milestone) + visit edit_milestone_url + + select "Español", from: "translation_locale" + fill_in 'budget_investment_milestone_description_es', with: 'Descripción en Español' + + click_button 'Update milestone' + expect(page).to have_content "Milestone updated successfully" + + visit edit_milestone_url + expect(page).to have_field('budget_investment_milestone_description_en', with: 'Description in English') + + click_link "Español" + expect(page).to have_field('budget_investment_milestone_description_es', with: 'Descripción en Español') + end + + scenario "Update a translation", :js, :focus do + milestone = create(:budget_investment_milestone, + investment: investment, + description_en: "Description in English", + description_es: "Descripción en Español") + + edit_milestone_url = edit_admin_budget_budget_investment_budget_investment_milestone_path(investment.budget, investment, milestone) + visit edit_milestone_url + + select "Español", from: "translation_locale" + fill_in 'budget_investment_milestone_description_es', with: 'Descripción correcta en Español' + + click_button 'Update milestone' + expect(page).to have_content "Milestone updated successfully" + + visit budget_investment_path(investment.budget, investment) + + click_link("Milestones (1)") + expect(page).to have_content("Description in English") + + select('Español', from: 'locale-switcher') + click_link("Seguimiento (1)") + + expect(page).to have_content("Descripción correcta en Español") + end + + end + +end \ No newline at end of file From b318c2be460e76e2e71c8bcf1f22d1b98539aeee Mon Sep 17 00:00:00 2001 From: iagirre Date: Thu, 12 Apr 2018 09:58:02 +0200 Subject: [PATCH 2/9] Add feature to delete a translation To delete a translation, a link has been added. This link works for the selected language. It hides all the things related to a language (the tab and the text_area) and empties the text area, so that the value is blank in the param hash. A variable called `delete_translations[]` is changed. e.g. If admin wants to remove English language, delete_translations[:en] will be 1; if not, it will be 0. When the milestone is updated, there is a before_action callback that cleans the selected languages for deletion (looking the delete_translations[] variable). Because of the deleted translations are blank in param hash, them won't be saved in DB. --- app/assets/javascripts/globalize.js.coffee | 23 ++++++++++++++++--- app/controllers/concerns/translatable.rb | 14 ++++++++++- app/helpers/globalize_helper.rb | 6 ++++- .../_form.html.erb | 1 + .../milestones/_globalize_locales.html.erb | 11 ++++++--- config/locales/en/admin.yml | 4 ++++ config/locales/es/admin.yml | 4 ++++ 7 files changed, 55 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/globalize.js.coffee b/app/assets/javascripts/globalize.js.coffee index 6ac39e71c..82bb97a24 100644 --- a/app/assets/javascripts/globalize.js.coffee +++ b/app/assets/javascripts/globalize.js.coffee @@ -9,17 +9,28 @@ App.Globalize = display_translations: (locale) -> $(".js-globalize-attribute").each -> - console.log("In standard") - console.log($(this)) if $(this).data("locale") == locale $(this).show() else $(this).hide() + $('.delete-language').hide() + $('#delete-' + locale).show() highlight_locale: (element) -> $('.js-globalize-locale-link').removeClass('highlight'); element.addClass('highlight'); + remove_language: (locale) -> + $(".js-globalize-attribute[data-locale=" + locale + "]").val('') + $(".js-globalize-attribute[data-locale=" + locale + "]").hide() + $(".js-globalize-locale-link[data-locale=" + locale + "]").hide() + $("#delete-" + locale).hide() + next = $(".js-globalize-locale-link:visible").first() + App.Globalize.highlight_locale(next) + $(".js-globalize-attribute[data-locale=" + next.data("locale") + "]").show() + $("#delete-" + next.data("locale")).show() + $("#delete_translations_" + locale).val(1) + initialize: -> $('.js-globalize-locale').on 'change', -> App.Globalize.display_translations($(this).val()) @@ -28,4 +39,10 @@ App.Globalize = $('.js-globalize-locale-link').on 'click', -> locale = $(this).data("locale") App.Globalize.display_translations(locale) - App.Globalize.highlight_locale($(this)) \ No newline at end of file + App.Globalize.highlight_locale($(this)) + + $('.delete-language').on 'click', -> + locale = $(this).data("locale") + $(this).hide() + App.Globalize.remove_language(locale) + diff --git a/app/controllers/concerns/translatable.rb b/app/controllers/concerns/translatable.rb index 2325fc013..50cbf2cfe 100644 --- a/app/controllers/concerns/translatable.rb +++ b/app/controllers/concerns/translatable.rb @@ -3,6 +3,7 @@ module Translatable included do before_action :set_translation_locale + before_action :delete_translations, only: [:update] end private @@ -16,4 +17,15 @@ module Translatable Globalize.locale = I18n.locale end -end \ No newline at end of file + def delete_translations + locales = Budget::Investment::Milestone.globalize_locales. + select { |k, v| params[:delete_translations].include?(k.to_sym) && params[:delete_translations][k] == "1" } + milestone = Budget::Investment::Milestone.find(params[:id]) + locales.each do |l| + Globalize.with_locale(l) do + milestone.translation.destroy + end + end + end + +end diff --git a/app/helpers/globalize_helper.rb b/app/helpers/globalize_helper.rb index e0d11f611..bdfa1ac45 100644 --- a/app/helpers/globalize_helper.rb +++ b/app/helpers/globalize_helper.rb @@ -30,4 +30,8 @@ module GlobalizeHelper globalize_locale == locale ? "highlight" : "" end -end \ No newline at end of file + def show_delete?(locale) + I18n.locale == locale ? '' : 'display: none' + end + +end diff --git a/app/views/admin/budget_investment_milestones/_form.html.erb b/app/views/admin/budget_investment_milestones/_form.html.erb index 9dae79b6e..fedbbae7c 100644 --- a/app/views/admin/budget_investment_milestones/_form.html.erb +++ b/app/views/admin/budget_investment_milestones/_form.html.erb @@ -9,6 +9,7 @@ <%= f.label :description, t("admin.milestones.new.description") %> <% @milestone.globalize_locales.each do |locale| %> + <%= hidden_field_tag "delete_translations[#{locale}]", 0 %> <% Globalize.with_locale(locale) do %> <%= f.text_area "description_#{locale}", rows: 5, class: "js-globalize-attribute", diff --git a/app/views/budgets/investments/milestones/_globalize_locales.html.erb b/app/views/budgets/investments/milestones/_globalize_locales.html.erb index fc291ea96..1ef982591 100644 --- a/app/views/budgets/investments/milestones/_globalize_locales.html.erb +++ b/app/views/budgets/investments/milestones/_globalize_locales.html.erb @@ -5,10 +5,15 @@ class: "js-globalize-locale-link", data: { locale: locale }, remote: true %> + <%= link_to t("admin.milestones.form.remove_language"), "#", + id: "delete-#{locale}", + style: show_delete?(locale), + class: 'float-right delete-language', + data: { locale: locale } %> <% end %> -<%= select_tag :translation_locale, +<%= select_tag :translation_locale, options_for_locale_select, - prompt: "Añadir idioma", - class: "js-globalize-locale" %> \ No newline at end of file + prompt: t("admin.milestones.form.add_language"), + class: "js-globalize-locale" %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index c477fb875..0292df944 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -254,9 +254,13 @@ en: image: "Image" show_image: "Show image" documents: "Documents" + form: + add_language: Add language + remove_language: Remove language new: creating: Create milestone date: Date + description: Description edit: title: Edit milestone create: diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 496596ee3..5d44c9a17 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -254,9 +254,13 @@ es: image: "Imagen" show_image: "Mostrar imagen" documents: "Documentos" + form: + add_language: Añadir idioma + remove_language: Eliminar idioma new: creating: Crear hito date: Fecha + description: Descripción edit: title: Editar hito create: From 285e02ce9620d19e788726b96b42136568b67ab1 Mon Sep 17 00:00:00 2001 From: iagirre Date: Thu, 12 Apr 2018 10:00:43 +0200 Subject: [PATCH 3/9] Highlight current locale when changing locale from select When the locale changes the corresponding tab is highlighted automatically. When a language is added to the milestone, the tab is highlighted automatically. --- app/assets/javascripts/globalize.js.coffee | 1 + app/helpers/globalize_helper.rb | 4 ++++ .../investments/milestones/_globalize_locales.html.erb | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/globalize.js.coffee b/app/assets/javascripts/globalize.js.coffee index 82bb97a24..10c50b1c8 100644 --- a/app/assets/javascripts/globalize.js.coffee +++ b/app/assets/javascripts/globalize.js.coffee @@ -4,6 +4,7 @@ App.Globalize = $(".js-globalize-locale-link").each -> if $(this).data("locale") == locale $(this).show() + App.Globalize.highlight_locale($(this)) $(".js-globalize-locale option:selected").removeAttr("selected"); return diff --git a/app/helpers/globalize_helper.rb b/app/helpers/globalize_helper.rb index bdfa1ac45..6a637d8e0 100644 --- a/app/helpers/globalize_helper.rb +++ b/app/helpers/globalize_helper.rb @@ -30,6 +30,10 @@ module GlobalizeHelper globalize_locale == locale ? "highlight" : "" end + def highlight_current?(locale) + I18n.locale == locale ? 'highlight' : '' + end + def show_delete?(locale) I18n.locale == locale ? '' : 'display: none' end diff --git a/app/views/budgets/investments/milestones/_globalize_locales.html.erb b/app/views/budgets/investments/milestones/_globalize_locales.html.erb index 1ef982591..bacc1cd6d 100644 --- a/app/views/budgets/investments/milestones/_globalize_locales.html.erb +++ b/app/views/budgets/investments/milestones/_globalize_locales.html.erb @@ -2,7 +2,7 @@ <%= link_to name_for_locale(locale), "#", style: css_to_display_translation?(@milestone, locale), - class: "js-globalize-locale-link", + class: "js-globalize-locale-link #{highlight_current?(locale)}", data: { locale: locale }, remote: true %> <%= link_to t("admin.milestones.form.remove_language"), "#", From 8b9d1ebd33b2bee155d4ef4f6626f6c9359904f1 Mon Sep 17 00:00:00 2001 From: iagirre Date: Thu, 12 Apr 2018 10:03:45 +0200 Subject: [PATCH 4/9] Make portuguese locale work There was a problem with the portuguese locale. The locale was pt-BR, but `globalize_accessors` gem doesn't allow the creation of methods using locales with that format. To avoid transforming pt-BR to pt and lose the distinction of the different variations of the language, a function has been added to transform pt-BR into pt_br (without changing the locale itself). That way, when globalize uses the locales, all of them will have a valid format (downcased and underscored) AND they will be always the same (comparing pt-BR with pt_br doesn't work). --- app/helpers/globalize_helper.rb | 10 +++++++--- app/models/budget/investment/milestone.rb | 4 ++-- app/views/budgets/investments/_milestones.html.erb | 4 ++-- .../milestones/_globalize_locales.html.erb | 6 +++--- .../investments/milestones/_milestone.html.erb | 12 ++++++------ 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/app/helpers/globalize_helper.rb b/app/helpers/globalize_helper.rb index 6a637d8e0..5ef04361d 100644 --- a/app/helpers/globalize_helper.rb +++ b/app/helpers/globalize_helper.rb @@ -10,16 +10,16 @@ module GlobalizeHelper def locale_options I18n.available_locales.map do |locale| - [name_for_locale(locale), locale] + [name_for_locale(locale), neutral_locale(locale)] end end def display_translation?(locale) - I18n.locale == locale ? "" : "display: none" + neutral_locale(I18n.locale) == neutral_locale(locale) ? "" : "display: none" end def css_to_display_translation?(resource, locale) - resource.translated_locales.include?(locale) || locale == I18n.locale ? "" : "display: none" + resource.translated_locales.include?(neutral_locale(locale)) || locale == I18n.locale ? "" : "display: none" end def disable_translation?(locale) @@ -38,4 +38,8 @@ module GlobalizeHelper I18n.locale == locale ? '' : 'display: none' end + def neutral_locale(locale) + locale.to_s.downcase.underscore.to_sym + end + end diff --git a/app/models/budget/investment/milestone.rb b/app/models/budget/investment/milestone.rb index 5553476ea..3f8ba4f3a 100644 --- a/app/models/budget/investment/milestone.rb +++ b/app/models/budget/investment/milestone.rb @@ -7,8 +7,8 @@ class Budget max_file_size: 3.megabytes, accepted_content_types: [ "application/pdf" ] - translates :description, touch: true - globalize_accessors locales: [:en, :es, :fr, :nl, :val] + translates :title, :description, touch: true + globalize_accessors locales: [:en, :es, :fr, :nl, :val, :pt_br] belongs_to :investment diff --git a/app/views/budgets/investments/_milestones.html.erb b/app/views/budgets/investments/_milestones.html.erb index afada7fc2..4a17b066f 100644 --- a/app/views/budgets/investments/_milestones.html.erb +++ b/app/views/budgets/investments/_milestones.html.erb @@ -24,7 +24,7 @@ class: "margin", id: "image_#{milestone.id}" }) if milestone.image.present? %>

    - <% Globalize.with_locale(locale) do %> + <% Globalize.with_locale(neutral_locale(locale)) do %> <%= text_with_links milestone.description %> <% end %>

    @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/app/views/budgets/investments/milestones/_globalize_locales.html.erb b/app/views/budgets/investments/milestones/_globalize_locales.html.erb index bacc1cd6d..17aca653d 100644 --- a/app/views/budgets/investments/milestones/_globalize_locales.html.erb +++ b/app/views/budgets/investments/milestones/_globalize_locales.html.erb @@ -3,13 +3,13 @@ <%= link_to name_for_locale(locale), "#", style: css_to_display_translation?(@milestone, locale), class: "js-globalize-locale-link #{highlight_current?(locale)}", - data: { locale: locale }, + data: { locale: neutral_locale(locale) }, remote: true %> <%= link_to t("admin.milestones.form.remove_language"), "#", id: "delete-#{locale}", - style: show_delete?(locale), + style: show_delete?(neutral_locale(locale)), class: 'float-right delete-language', - data: { locale: locale } %> + data: { locale: neutral_locale(locale) } %>
    <% end %> diff --git a/app/views/budgets/investments/milestones/_milestone.html.erb b/app/views/budgets/investments/milestones/_milestone.html.erb index 2113fd06e..86adb26df 100644 --- a/app/views/budgets/investments/milestones/_milestone.html.erb +++ b/app/views/budgets/investments/milestones/_milestone.html.erb @@ -11,18 +11,18 @@ <% end %> <% if milestone.image.present? %> - <%= image_tag(milestone.image_url(:large), + <%= image_tag(milestone.image_url(:large), { id: "image_#{milestone.id}", - alt: milestone.image.title, + alt: milestone.image.title, class: "margin" }) %> <% end %> - +

    - <% Globalize.with_locale(locale) do %> + <% Globalize.with_locale(neutral_locale(locale)) do %> <%= text_with_links milestone.description %> <% end %>

    - + <% if milestone.documents.present? %> - \ No newline at end of file + From 951d1c16953b27a1fb9c44c0725f52270b0d3917 Mon Sep 17 00:00:00 2001 From: iagirre Date: Fri, 13 Apr 2018 09:25:56 +0200 Subject: [PATCH 5/9] Use a helper with yield Globalize.with_locale A helper has been created to encapsule the logic of Globalize.with_locale. Now, to call that function, globalize(locale) do is called. --- app/helpers/globalize_helper.rb | 6 ++++++ app/views/admin/budget_investment_milestones/_form.html.erb | 2 +- app/views/budgets/investments/_milestones.html.erb | 2 +- .../budgets/investments/milestones/_milestone.html.erb | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/helpers/globalize_helper.rb b/app/helpers/globalize_helper.rb index 5ef04361d..31b73e6ba 100644 --- a/app/helpers/globalize_helper.rb +++ b/app/helpers/globalize_helper.rb @@ -42,4 +42,10 @@ module GlobalizeHelper locale.to_s.downcase.underscore.to_sym end + def globalize(locale, &block) + Globalize.with_locale(locale) do + yield + end + end + end diff --git a/app/views/admin/budget_investment_milestones/_form.html.erb b/app/views/admin/budget_investment_milestones/_form.html.erb index fedbbae7c..28bb80537 100644 --- a/app/views/admin/budget_investment_milestones/_form.html.erb +++ b/app/views/admin/budget_investment_milestones/_form.html.erb @@ -10,7 +10,7 @@ <%= f.label :description, t("admin.milestones.new.description") %> <% @milestone.globalize_locales.each do |locale| %> <%= hidden_field_tag "delete_translations[#{locale}]", 0 %> - <% Globalize.with_locale(locale) do %> + <% globalize(locale) do %> <%= f.text_area "description_#{locale}", rows: 5, class: "js-globalize-attribute", data: { locale: locale }, diff --git a/app/views/budgets/investments/_milestones.html.erb b/app/views/budgets/investments/_milestones.html.erb index 4a17b066f..fa912393a 100644 --- a/app/views/budgets/investments/_milestones.html.erb +++ b/app/views/budgets/investments/_milestones.html.erb @@ -24,7 +24,7 @@ class: "margin", id: "image_#{milestone.id}" }) if milestone.image.present? %>

    - <% Globalize.with_locale(neutral_locale(locale)) do %> + <% globalize(neutral_locale(locale)) do %> <%= text_with_links milestone.description %> <% end %>

    diff --git a/app/views/budgets/investments/milestones/_milestone.html.erb b/app/views/budgets/investments/milestones/_milestone.html.erb index 86adb26df..c7d00f415 100644 --- a/app/views/budgets/investments/milestones/_milestone.html.erb +++ b/app/views/budgets/investments/milestones/_milestone.html.erb @@ -18,7 +18,7 @@ <% end %>

    - <% Globalize.with_locale(neutral_locale(locale)) do %> + <% globalize(neutral_locale(locale)) do %> <%= text_with_links milestone.description %> <% end %>

    From 4e5c9e2166d235132b282f8ffeb6000f5cf864a7 Mon Sep 17 00:00:00 2001 From: iagirre Date: Fri, 13 Apr 2018 13:25:35 +0200 Subject: [PATCH 6/9] Modify specs to work with new features Add specs to check that the translations are being deleted correctly and the current locale tab is highlighted when the admin visits the edit milestone page. --- .../budget_investment_milestones_spec.rb | 6 +- spec/features/budgets/investments_spec.rb | 12 ++- spec/features/translations_spec.rb | 94 +++++++++++++++---- 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/spec/features/admin/budget_investment_milestones_spec.rb b/spec/features/admin/budget_investment_milestones_spec.rb index 5967d71f5..81b9be430 100644 --- a/spec/features/admin/budget_investment_milestones_spec.rb +++ b/spec/features/admin/budget_investment_milestones_spec.rb @@ -39,7 +39,7 @@ feature 'Admin budget investment milestones' do click_link 'Create new milestone' - fill_in 'budget_investment_milestone_description', with: 'New description milestone' + fill_in 'budget_investment_milestone_description_en', with: 'New description milestone' fill_in 'budget_investment_milestone_publication_date', with: Date.current click_button 'Create milestone' @@ -53,7 +53,7 @@ feature 'Admin budget investment milestones' do click_link 'Create new milestone' - fill_in 'budget_investment_milestone_description', with: 'New description milestone' + fill_in 'budget_investment_milestone_description_en', with: 'New description milestone' click_button 'Create milestone' @@ -77,7 +77,7 @@ feature 'Admin budget investment milestones' do expect(page).to have_css("img[alt='#{milestone.image.title}']") - fill_in 'budget_investment_milestone_description', with: 'Changed description' + fill_in 'budget_investment_milestone_description_en', with: 'Changed description' fill_in 'budget_investment_milestone_publication_date', with: Date.current fill_in 'budget_investment_milestone_documents_attributes_0_title', with: 'New document title' diff --git a/spec/features/budgets/investments_spec.rb b/spec/features/budgets/investments_spec.rb index de15c44ee..b9b590a6f 100644 --- a/spec/features/budgets/investments_spec.rb +++ b/spec/features/budgets/investments_spec.rb @@ -995,7 +995,8 @@ feature 'Budget Investments' do user = create(:user) investment = create(:budget_investment) create(:budget_investment_milestone, investment: investment, - description: "Last milestone with a link to https://consul.dev", + description_en: "Last milestone with a link to https://consul.dev", + description_es: "Último hito con el link https://consul.dev", publication_date: Date.tomorrow) first_milestone = create(:budget_investment_milestone, investment: investment, description: "First milestone", @@ -1017,6 +1018,15 @@ feature 'Budget Investments' do expect(page).to have_link(document.title) expect(page).to have_link("https://consul.dev") end + + select('Español', from: 'locale-switcher') + + find("#tab-milestones-label").click + + within("#tab-milestones") do + expect(page).to have_content('Último hito con el link https://consul.dev') + expect(page).to have_link("https://consul.dev") + end end scenario "Show no_milestones text", :js do diff --git a/spec/features/translations_spec.rb b/spec/features/translations_spec.rb index 944f5bd22..55dcec4d1 100644 --- a/spec/features/translations_spec.rb +++ b/spec/features/translations_spec.rb @@ -5,41 +5,43 @@ feature "Translations" do context "Milestones" do let(:investment) { create(:budget_investment) } + let(:milestone) { create(:budget_investment_milestone, + investment: investment, + description_en: "Description in English", + description_es: "Descripción en Español") } background do admin = create(:administrator) login_as(admin.user) end - scenario "Add a translation", :js, :focus do - milestone = create(:budget_investment_milestone, description: "Description in English") + before do + @edit_milestone_url = edit_admin_budget_budget_investment_budget_investment_milestone_path(investment.budget, investment, milestone) + end - edit_milestone_url = edit_admin_budget_budget_investment_budget_investment_milestone_path(investment.budget, investment, milestone) - visit edit_milestone_url + scenario "Add a translation", :js do + visit @edit_milestone_url - select "Español", from: "translation_locale" - fill_in 'budget_investment_milestone_description_es', with: 'Descripción en Español' + select "Français", from: "translation_locale" + fill_in 'budget_investment_milestone_description_fr', with: 'Description en Français' click_button 'Update milestone' expect(page).to have_content "Milestone updated successfully" - visit edit_milestone_url + visit @edit_milestone_url expect(page).to have_field('budget_investment_milestone_description_en', with: 'Description in English') click_link "Español" expect(page).to have_field('budget_investment_milestone_description_es', with: 'Descripción en Español') + + click_link "Français" + expect(page).to have_field('budget_investment_milestone_description_fr', with: 'Description en Français') end - scenario "Update a translation", :js, :focus do - milestone = create(:budget_investment_milestone, - investment: investment, - description_en: "Description in English", - description_es: "Descripción en Español") + scenario "Update a translation", :js do + visit @edit_milestone_url - edit_milestone_url = edit_admin_budget_budget_investment_budget_investment_milestone_path(investment.budget, investment, milestone) - visit edit_milestone_url - - select "Español", from: "translation_locale" + click_link "Español" fill_in 'budget_investment_milestone_description_es', with: 'Descripción correcta en Español' click_button 'Update milestone' @@ -56,6 +58,64 @@ feature "Translations" do expect(page).to have_content("Descripción correcta en Español") end + scenario "Remove a translation", :js do + visit @edit_milestone_url + + click_link "Español" + click_link "Remove language" + + expect(page).not_to have_link "Español" + + click_button "Update milestone" + visit @edit_milestone_url + expect(page).not_to have_link "Español" + end + + context "Globalize javascript interface" do + + scenario "Highlight current locale", :js do + visit @edit_milestone_url + + expect(find("span .js-globalize-locale-link.highlight")).to have_content "English" + + select('Español', from: 'locale-switcher') + + expect(find("span .js-globalize-locale-link.highlight")).to have_content "Español" + end + + scenario "Highlight selected locale", :js do + visit @edit_milestone_url + + expect(find("span .js-globalize-locale-link.highlight")).to have_content "English" + + click_link "Español" + + expect(find("span .js-globalize-locale-link.highlight")).to have_content "Español" + end + + scenario "Show selected locale form", :js do + visit @edit_milestone_url + + expect(page).to have_field('budget_investment_milestone_description_en', with: 'Description in English') + + click_link "Español" + + expect(page).to have_field('budget_investment_milestone_description_es', with: 'Descripción en Español') + end + + scenario "Select a locale and add it to the milestone form", :js do + visit @edit_milestone_url + + select "Français", from: "translation_locale" + + expect(page).to have_link "Français" + + click_link "Français" + + expect(page).to have_field('budget_investment_milestone_description_fr') + end + end + end -end \ No newline at end of file +end From 747db0ea35c9e9fbe537b8a903bd27223d0544ce Mon Sep 17 00:00:00 2001 From: iagirre Date: Mon, 16 Apr 2018 09:30:21 +0200 Subject: [PATCH 7/9] Add dev_seeds for milestones with translations Add one milestone to each investment with translations for each locale defined in the app. --- db/dev_seeds/budgets.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/db/dev_seeds/budgets.rb b/db/dev_seeds/budgets.rb index 4740ed407..5d3294b46 100644 --- a/db/dev_seeds/budgets.rb +++ b/db/dev_seeds/budgets.rb @@ -109,3 +109,17 @@ section "Creating Valuation Assignments" do Budget::Investment.all.sample.valuators << Valuator.first end end + +section "Creating investment milestones" do + Budget::Investment.all.each do |investment| + milestone = Budget::Investment::Milestone.new(investment_id: investment.id, publication_date: Date.tomorrow) + I18n.available_locales.map do |locale| + neutral_locale = locale.to_s.downcase.underscore.to_sym + Globalize.with_locale(neutral_locale) do + milestone.description = "Description for locale #{locale}" + milestone.title = I18n.l(Time.current, format: :datetime) + milestone.save! + end + end + end +end From 471c9730ccfb428739eeb661ad87b8a5c1852bfe Mon Sep 17 00:00:00 2001 From: iagirre Date: Mon, 16 Apr 2018 17:48:53 +0200 Subject: [PATCH 8/9] Refactorings - Cleanup Translatable module (`translation_params` method too large) - Move globalize_helpers partial to admin folder - Use any class for method translation_params - Helpers in `GlobalizeHelpers` make sure all are in use and see if they can be more legible - Review js name clases and methods see if they can be more legible - Refactor milestone views into partials with nice spacing between attributes --- app/assets/javascripts/globalize.js.coffee | 7 ++-- ...budget_investment_milestones_controller.rb | 16 +++++++-- app/controllers/concerns/translatable.rb | 10 +++--- app/helpers/globalize_helper.rb | 22 ++++-------- .../_form.html.erb | 2 +- .../_globalize_locales.html.erb | 4 +-- .../budgets/investments/_milestones.html.erb | 36 +------------------ .../milestones/_milestone.html.erb | 15 +++----- db/schema.rb | 4 +-- 9 files changed, 38 insertions(+), 78 deletions(-) rename app/views/{budgets/investments/milestones => admin/budget_investment_milestones}/_globalize_locales.html.erb (87%) diff --git a/app/assets/javascripts/globalize.js.coffee b/app/assets/javascripts/globalize.js.coffee index 10c50b1c8..e53383ceb 100644 --- a/app/assets/javascripts/globalize.js.coffee +++ b/app/assets/javascripts/globalize.js.coffee @@ -22,14 +22,11 @@ App.Globalize = element.addClass('highlight'); remove_language: (locale) -> - $(".js-globalize-attribute[data-locale=" + locale + "]").val('') - $(".js-globalize-attribute[data-locale=" + locale + "]").hide() + $(".js-globalize-attribute[data-locale=" + locale + "]").val('').hide() $(".js-globalize-locale-link[data-locale=" + locale + "]").hide() - $("#delete-" + locale).hide() next = $(".js-globalize-locale-link:visible").first() App.Globalize.highlight_locale(next) - $(".js-globalize-attribute[data-locale=" + next.data("locale") + "]").show() - $("#delete-" + next.data("locale")).show() + App.Globalize.display_translations(next.data("locale")) $("#delete_translations_" + locale).val(1) initialize: -> diff --git a/app/controllers/admin/budget_investment_milestones_controller.rb b/app/controllers/admin/budget_investment_milestones_controller.rb index 13bfb6460..d2fdf5700 100644 --- a/app/controllers/admin/budget_investment_milestones_controller.rb +++ b/app/controllers/admin/budget_investment_milestones_controller.rb @@ -48,7 +48,7 @@ class Admin::BudgetInvestmentMilestonesController < Admin::BaseController attributes = [:title, :description, :publication_date, :budget_investment_id, image_attributes: image_attributes, documents_attributes: documents_attributes] - params.require(:budget_investment_milestone).permit(*attributes, translation_params) + params.require(:budget_investment_milestone).permit(*attributes, translation_params(params[:budget_investment_milestone])) end def load_budget_investment @@ -56,7 +56,19 @@ class Admin::BudgetInvestmentMilestonesController < Admin::BaseController end def load_budget_investment_milestone - @milestone = Budget::Investment::Milestone.find(params[:id]) + @milestone = get_milestone + end + + def get_milestone + Budget::Investment::Milestone.find(params[:id]) + end + + def resource_model + Budget::Investment::Milestone + end + + def resource + get_milestone end end diff --git a/app/controllers/concerns/translatable.rb b/app/controllers/concerns/translatable.rb index 50cbf2cfe..4b4b14d87 100644 --- a/app/controllers/concerns/translatable.rb +++ b/app/controllers/concerns/translatable.rb @@ -8,9 +8,8 @@ module Translatable private - def translation_params - Budget::Investment::Milestone.globalize_attribute_names. - select { |k, v| params[:budget_investment_milestone].include?(k.to_sym) && params[:budget_investment_milestone][k].present? } + def translation_params(params) + resource_model.globalize_attribute_names.select { |k, v| params.include?(k.to_sym) && params[k].present? } end def set_translation_locale @@ -18,12 +17,11 @@ module Translatable end def delete_translations - locales = Budget::Investment::Milestone.globalize_locales. + locales = resource_model.globalize_locales. select { |k, v| params[:delete_translations].include?(k.to_sym) && params[:delete_translations][k] == "1" } - milestone = Budget::Investment::Milestone.find(params[:id]) locales.each do |l| Globalize.with_locale(l) do - milestone.translation.destroy + resource.translation.destroy end end end diff --git a/app/helpers/globalize_helper.rb b/app/helpers/globalize_helper.rb index 31b73e6ba..97dfeab66 100644 --- a/app/helpers/globalize_helper.rb +++ b/app/helpers/globalize_helper.rb @@ -1,9 +1,5 @@ module GlobalizeHelper - def globalize_locale - params[:globalize_locale] || I18n.locale - end - def options_for_locale_select options_for_select(locale_options, nil) end @@ -15,27 +11,19 @@ module GlobalizeHelper end def display_translation?(locale) - neutral_locale(I18n.locale) == neutral_locale(locale) ? "" : "display: none" + same_locale?(neutral_locale(I18n.locale), neutral_locale(locale)) ? "" : "display: none" end def css_to_display_translation?(resource, locale) resource.translated_locales.include?(neutral_locale(locale)) || locale == I18n.locale ? "" : "display: none" end - def disable_translation?(locale) - locale == "en" ? "" : "disabled" - end - - def css_for_globalize_locale(locale) - globalize_locale == locale ? "highlight" : "" - end - def highlight_current?(locale) - I18n.locale == locale ? 'highlight' : '' + same_locale?(I18n.locale, locale) ? 'highlight' : '' end def show_delete?(locale) - I18n.locale == locale ? '' : 'display: none' + display_translation?(locale) end def neutral_locale(locale) @@ -48,4 +36,8 @@ module GlobalizeHelper end end + def same_locale?(locale1, locale2) + locale1 == locale2 + end + end diff --git a/app/views/admin/budget_investment_milestones/_form.html.erb b/app/views/admin/budget_investment_milestones/_form.html.erb index 28bb80537..d63942cc9 100644 --- a/app/views/admin/budget_investment_milestones/_form.html.erb +++ b/app/views/admin/budget_investment_milestones/_form.html.erb @@ -1,5 +1,5 @@
    - <%= render "budgets/investments/milestones/globalize_locales" %> + <%= render "globalize_locales" %>
    <%= form_for [:admin, @investment.budget, @investment, @milestone] do |f| %> diff --git a/app/views/budgets/investments/milestones/_globalize_locales.html.erb b/app/views/admin/budget_investment_milestones/_globalize_locales.html.erb similarity index 87% rename from app/views/budgets/investments/milestones/_globalize_locales.html.erb rename to app/views/admin/budget_investment_milestones/_globalize_locales.html.erb index 17aca653d..11bf985dc 100644 --- a/app/views/budgets/investments/milestones/_globalize_locales.html.erb +++ b/app/views/admin/budget_investment_milestones/_globalize_locales.html.erb @@ -6,8 +6,8 @@ data: { locale: neutral_locale(locale) }, remote: true %> <%= link_to t("admin.milestones.form.remove_language"), "#", - id: "delete-#{locale}", - style: show_delete?(neutral_locale(locale)), + id: "delete-#{neutral_locale(locale)}", + style: show_delete?(locale), class: 'float-right delete-language', data: { locale: neutral_locale(locale) } %> diff --git a/app/views/budgets/investments/_milestones.html.erb b/app/views/budgets/investments/_milestones.html.erb index fa912393a..60b52f46e 100644 --- a/app/views/budgets/investments/_milestones.html.erb +++ b/app/views/budgets/investments/_milestones.html.erb @@ -9,41 +9,7 @@
      <% @investment.milestones.order_by_publication_date.each do |milestone| %> -
    • -
      - <% if milestone.publication_date.present? %> - - - <%= t("budgets.investments.show.milestone_publication_date", - publication_date: l(milestone.publication_date.to_date)) %> - - - <% end %> - <%= image_tag(milestone.image_url(:large), - { alt: milestone.image.title.unicode_normalize, - class: "margin", - id: "image_#{milestone.id}" }) if milestone.image.present? %> -

      - <% globalize(neutral_locale(locale)) do %> - <%= text_with_links milestone.description %> - <% end %> -

      - <% if milestone.documents.present? %> - - <% end %> -
      -
    • + <%= render 'budgets/investments/milestones/milestone', milestone: milestone %> <% end %>
    diff --git a/app/views/budgets/investments/milestones/_milestone.html.erb b/app/views/budgets/investments/milestones/_milestone.html.erb index c7d00f415..774819d0c 100644 --- a/app/views/budgets/investments/milestones/_milestone.html.erb +++ b/app/views/budgets/investments/milestones/_milestone.html.erb @@ -10,18 +10,13 @@ <% end %> - <% if milestone.image.present? %> - <%= image_tag(milestone.image_url(:large), - { id: "image_#{milestone.id}", - alt: milestone.image.title, - class: "margin" }) %> - <% end %> + <%= image_tag(milestone.image_url(:large), { id: "image_#{milestone.id}", alt: milestone.image.title, class: "margin" }) if milestone.image.present? %> -

    - <% globalize(neutral_locale(locale)) do %> + <% globalize(neutral_locale(locale)) do %> +

    <%= text_with_links milestone.description %> - <% end %> -

    +

    + <% end %> <% if milestone.documents.present? %>