Complete basic I18n backend and frontend

This commit is contained in:
Raúl Fuentes
2018-07-20 13:54:52 +02:00
committed by Angel Perez
parent c18479e3ac
commit 6d6dc32c38
19 changed files with 490 additions and 99 deletions

View File

@@ -2,13 +2,28 @@ class Admin::SiteCustomization::InformationTextsController < Admin::SiteCustomiz
include Translatable include Translatable
def index def index
@contents = I18nContent.all existing_keys = {}
@tab = params[:tab] || :debates
I18nContent.begins_with_key(@tab).all.
map{|content| existing_keys[content.key] = content }
@content = {}
I18n.backend.send(:translations)[:en].each do |k,v|
@content[k.to_s] = flat_hash(v).keys.map{|s| existing_keys["#{k.to_s}.#{s}"].nil? ?
I18nContent.new(key: "#{k.to_s}.#{s}") :
existing_keys["#{k.to_s}.#{s}"] }
end
@content = @content[@tab.to_s]
end end
def update def update
content_params.each do |content| content_params.each do |content|
text = I18nContent.find(content[:id]) value = content[:values].slice(*translation_params(content[:values]))
text.update(content[:values].slice(*translation_params(content[:values]))) unless value.empty?
text = I18nContent.by_key(content[:id]).last || I18nContent.create(key: content[:id])
text.update(value)
text.save
end
end end
redirect_to admin_site_customization_information_texts_path redirect_to admin_site_customization_information_texts_path
end end
@@ -38,4 +53,11 @@ class Admin::SiteCustomization::InformationTextsController < Admin::SiteCustomiz
I18nContentTranslation.destroy_all(locale: locale) I18nContentTranslation.destroy_all(locale: locale)
end end
end end
def flat_hash(h, f=nil, g={})
return g.update({ f => h }) unless h.is_a? Hash
h.each { |k, r| flat_hash(r, [f,k].compact.join('.'), g) }
g
end
end end

View File

@@ -0,0 +1,11 @@
class I18nContent < ActiveRecord::Base
scope :by_key, -> (key){ where(key: key) }
scope :begins_with_key, -> (key){ where("key LIKE ?", "#{key}?%") }
validates :key, uniqueness: true
translates :value, touch: true
globalize_accessors locales: [:en, :es, :fr, :nl, :val, :pt_br]
end

View File

@@ -1,3 +0,0 @@
<%= render "globalize_locales" %>
<%= render "form", contents: [I18nContent.debates] %>

View File

@@ -1,3 +0,0 @@
<%= render "globalize_locales" %>
<%= render "form", contents: [I18nContent.emails] %>

View File

@@ -1,3 +1,5 @@
<%= render "globalize_locales" %>
<%= form_tag admin_site_customization_information_texts_path do %> <%= form_tag admin_site_customization_information_texts_path do %>
<% I18n.available_locales.each do |l| %> <% I18n.available_locales.each do |l| %>
<%= hidden_field_tag "delete_translations[#{l}]", 0 %> <%= hidden_field_tag "delete_translations[#{l}]", 0 %>
@@ -8,7 +10,6 @@
<% content.globalize_locales.each do |locale| %> <% content.globalize_locales.each do |locale| %>
<%= render 'form_field', content: content, locale: locale %> <%= render 'form_field', content: content, locale: locale %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<%= submit_tag "Save", class: "button" %> <%= submit_tag "Save", class: "button" %>

View File

@@ -1,6 +1,6 @@
<% globalize(locale) do %> <% globalize(locale) do %>
<%= hidden_field_tag "contents[content_#{content.id}][id]", content.id %> <%= hidden_field_tag "contents[content_#{content.key}][id]", content.key %>
<%= text_area_tag "contents[content_#{content.id}]values[value_#{locale}]", <%= text_area_tag "contents[content_#{content.key}]values[value_#{locale}]",
content.send("value_#{locale}"), content.send("value_#{locale}"),
{rows: 5, {rows: 5,
class: "js-globalize-attribute", class: "js-globalize-attribute",

View File

@@ -1,3 +0,0 @@
<%= render "globalize_locales" %>
<%= render "form", contents: [I18nContent.management, I18nContent.guides] %>

View File

@@ -1,3 +0,0 @@
<%= render "globalize_locales" %>
<%= render "form", contents: [I18nContent.devise_locales, I18nContent.devise_views, I18nContent.layouts, I18nContent.legislation] %>

View File

@@ -1,3 +0,0 @@
<%= render "globalize_locales" %>
<%= render "form", contents: [I18nContent.budgets] %>

View File

@@ -1,3 +0,0 @@
<%= render "globalize_locales" %>
<%= render "form", contents: [I18nContent.polls] %>

View File

@@ -1,3 +0,0 @@
<%= render "globalize_locales" %>
<%= render "form", contents: [I18nContent.community, I18nContent.proposals] %>

View File

@@ -1,23 +1,7 @@
<ul class="tabs" data-tabs id="information-texts-tabs"> <ul class="tabs" >
<li class="tabs-title is-active"> <% [:debates, :community, :proposals, :polls, :layouts, :emails, :management, :guides, :welcome].each do |tab| %>
<%= link_to t("admin.menu.site_customization.information_texts_menu.debates"), "#tab-debates" %> <li class="tabs-title <%= "is-active" if tab.to_s == @tab %>">
</li> <%= link_to t("admin.menu.site_customization.information_texts_menu.#{tab}"), admin_site_customization_information_texts_path(tab: tab) %>
<li class="tabs-title">
<%= link_to t("admin.menu.site_customization.information_texts_menu.proposals"), "#tab-proposals" %>
</li>
<li class="tabs-title">
<%= link_to t("admin.menu.site_customization.information_texts_menu.polls"), "#tab-polls" %>
</li>
<li class="tabs-title">
<%= link_to t("admin.menu.site_customization.information_texts_menu.participatory_budgets"), "#tab-participatory-budgets" %>
</li>
<li class="tabs-title">
<%= link_to t("admin.menu.site_customization.information_texts_menu.more_information"), "#tab-more-information" %>
</li>
<li class="tabs-title">
<%= link_to t("admin.menu.site_customization.information_texts_menu.emails"), "#tab-emails" %>
</li>
<li class="tabs-title">
<%= link_to t("admin.menu.site_customization.information_texts_menu.management"), "#tab-management" %>
</li> </li>
<% end %>
</ul> </ul>

View File

@@ -3,31 +3,8 @@
<div class="tabs-content" data-tabs-content="information-texts-tabs" role="tablist"> <div class="tabs-content" data-tabs-content="information-texts-tabs" role="tablist">
<%= render 'tabs' %> <%= render 'tabs' %>
<div class="tabs-panel is-active" id="tab-debates" role="tab"> <div class="tabs-panel is-active" role="tab">
<%= render "debates" %> <%= render "form", contents: [@content] %>
</div> </div>
<div class="tabs-panel" id="tab-proposals" role="tab">
<%= render "proposals" %>
</div>
<div class="tabs-panel" id="tab-polls" role="tab">
<%= render "polls" %>
</div>
<div class="tabs-panel" id="tab-participatory-budgets" role="tab">
<%= render "participatory_budgets" %>
</div>
<div class="tabs-panel" id="tab-more-information" role="tab">
<%= render "more_information" %>
</div>
<div class="tabs-panel" id="tab-emails" role="tab">
<%= render "emails" %>
</div>
<div class="tabs-panel" id="tab-management" role="tab">
<%= render "management" %>
</div>
</div> </div>

View File

@@ -3,7 +3,7 @@
<div class="user-permissions"> <div class="user-permissions">
<p><%= t("welcome.welcome.user_permission_info") %></p> <p><%= t("welcome.welcome.user_permission_info") %></p>
1
<ul> <ul>
<li><span class="icon-check"></span>&nbsp;<%= t("welcome.welcome.user_permission_debates") %></li> <li><span class="icon-check"></span>&nbsp;<%= t("welcome.welcome.user_permission_debates") %></li>
<li><span class="icon-check"></span>&nbsp;<%= t("welcome.welcome.user_permission_proposal") %></li> <li><span class="icon-check"></span>&nbsp;<%= t("welcome.welcome.user_permission_proposal") %></li>

View File

@@ -0,0 +1,25 @@
require 'action_view/helpers/tag_helper'
require 'i18n/exceptions'
module ActionView
# = Action View Translation Helpers
module Helpers
module TranslationHelper
include TagHelper
def t(key, options = {})
if translation = I18nContent.by_key(key).last
Globalize.with_locale(locale) do
string = I18nContent.where(key: key).first.value
options.each do |key, value|
string.sub! "%{#{key}}", (value || "%{#{key}}")
end
return string.html_safe
end
end
translate(key, options)
end
end
end
end

View File

@@ -0,0 +1,180 @@
en:
budgets:
index:
section_footer:
description: 'Debates section description'
help_text_1: 'Help text 1 for debates section'
help_text_2: 'Help text 2 for debates section'
help_text_3: 'Help text 3 for debates section'
help_text_4: 'Help text 4 for debates section'
community:
topic:
sidebar:
recommendation_one: 'Recommendation 1 when create a topic on a proposal community'
recommendation_two: 'Recommendation 2 when create a topic on a proposal community'
recommendation_three: 'Recommendation 3 when create a topic on a proposal community'
devise:
registrations:
destroyed: 'Message when deleting an account'
devise_views:
mailer:
confirmation_instructions:
title: 'Confirmation email title'
debates:
index:
section_footer:
description: 'Debates section description'
help_text_1: 'Help text 1 for debates section'
help_text_2: 'Help text 2 for debates section'
help_text_3: 'Help text 3 for debates section'
new:
recommendation_one: 'Recommendation 1 when creating debates'
recommendation_two: 'Recommendation 2 when creating debates'
recommendation_three: 'Recommendation 3 when creating debates'
recommendation_four: 'Recommendation 4 when creating debates'
layouts:
footer:
accessibility: 'Accessibility link name'
conditions: 'Conditions link name'
privacy: 'Privary policy link name'
proposals:
index:
section_footer:
description: 'Proposal page description'
help_text_1: 'Help text 1 of proposal page'
help_text_2: 'Help text 2 of proposal page'
help_text_3: 'Help text 3 of proposal page'
new:
form:
submit_button: '"Create proposal" button text'
more_info: '"More information" text in new proposal'
recommendation_one: 'Recommendation 1 in new proposal'
recommendation_two: 'Recommendation 2 in new proposal'
recommendation_three: 'Recommendation 3 in new proposal'
proposal:
reason_for_supports_necessary: 'Supports needed for proposal'
polls:
index:
section_footer:
description: 'Polls description'
help_text_1: 'Help text 1 of polls'
help_text_2: 'Help text 2 of polls'
legislation:
processes:
index:
section_footer:
description: 'Legislation processes description'
help_text_1: 'Help text 1 of legislation processes'
help_text_2: 'Help text 2 of legislation processes'
help_text_3: 'Help text 3 of legislation processes'
management:
print:
proposals_info: 'Printed proposal information'
proposals_note: 'Printed proposal note'
proposals_title: 'Printed proposal title'
budget_investments_info: 'Printed budget investments information'
budgets_investments_note: 'Printed budget investments note'
guides:
title: 'Guides titles'
subtitle: 'Guides subtitle'
budget_investment:
title: 'Budget investments guides title'
feature_1_html: 'Budget investments guides feature 1'
feature_2_html: 'Budget investments guides feature 2'
feature_3_html: 'Budget investments guides feature 3'
feature_4_html: 'Budget investments guides feature 4'
new_button: 'New investment button'
proposal:
title: 'Proposal guides title'
feature_1_html: 'Proposal guides feature 1'
feature_2_html: 'Proposal guides feature 2'
feature_3_html: 'Proposal guides feature 3'
feature_4_html: 'Proposal guides feature 4'
new_button: 'New proposal button'
mailers:
no_reply: 'No reply message'
comment:
hi: 'New comment email greetings'
new_comment_by_html: 'New comment text'
subject: 'New comment subject'
title: 'New comment title'
config:
manage_email_subscriptions: 'Manage email subscriptions'
email_verification:
click_here_to_verify: 'Link name to verify account'
instructions_2_html: 'Instructions to verify account 2'
instructions_html: 'Instructions to verify account'
subject: 'verify account subject'
thanks: 'Thanks'
title: 'Verify account title'
reply:
hi: 'New answer greetings'
new_reply_by_html: 'New answer text'
subject: 'New answer subject'
title: 'New answer title'
unfeasible_spending_proposal:
hi: 'Unfeasible spending proposal greetings'
reconsider_html: 'Unfeasible spending proposal reconsider text'
sincerely: 'Unfeasible spending proposal sincerely'
signatory: 'Unfeasible spending proposal signatory'
sorry: 'Unfeasible spending proposal sorry'
subject: 'Unfeasible spending proposal subject'
unfeasible_html: 'Unfeasible spending proposal text'
budget_investment_unfeasible:
hi: 'Budget investment unfeasible greetings'
reconsider_html: 'Budget investment unfeasible reconsider text'
sincerely: 'Budget investment unfeasible sincerely'
signatory: 'Budget investment unfeasible signatory'
sorry: 'Budget investment unfeasible sorry'
subject: 'Budget investment unfeasible subject'
unfeasible_html: 'Budget investment unfeasible text'
proposal_notification_digest:
info: 'Proposal notification digest information'
title: 'Proposal notification digest title'
share: 'Proposal notification digest share'
comment: 'Proposal notification digest share button'
unsubscribe: 'Proposal notification digest unsubscribe'
unsubscribe_account: 'Proposal notification digest unsubscribe link text'
direct_message_for_receiver:
subject: 'New direct message subject'
reply: 'Reply message subject button text'
unsubscribe: 'Unsubscribe direct messages emails'
unsubscribe_account: 'Unsubscribe direct message emails link text'
direct_message_for_sender:
subject: 'Direct message sender subject'
title_html: 'Direct message sender title'
user_invite:
ignore: 'User invitation ignore'
text: 'User invitation text'
thanks: 'User invitation thanks'
title: 'User invitation title'
button: 'User invitation button'
subject: 'User invitation subject'
budget_investment_created:
subject: 'Budget investment created subject'
title: 'Budget investment created title'
intro_html: 'Budget investment created intro'
text_html: 'Budget investment created text'
follow_html: 'Budget investment created follow'
follow_link: 'Budget investment created follow link'
sincerely: 'Budget investment created sincerely'
signatory: 'Budget investment created signatory'
share: 'Budget investment created share'
budget_investment_selected:
subject: 'Budget investment selected subject'
hi: 'Budget investment selected greetings'
selected_html: 'Budget investment selected text'
share: 'Budget investment selected share'
share_button: 'Budget investment selected share button'
thanks: 'Budget investment selected thanks'
sincerely: 'Budget investment selected sincerely'
signatory: 'Budget investment selected signatory'
budget_investment_unselected:
subject: 'Budget investment unselected subject'
hi: 'Budget investment unselected greetings'
unselected_html: 'Budget investment unselected text'
participate_html: 'Budget investment unselected participate'
participate_url: 'Budget investment unselected participate URL'
thanks: 'Budget investment unselected thanks'
sincerely: 'Budget investment unselected sincerely'
signatory: 'Budget investment unselected signatory'

View File

@@ -0,0 +1,180 @@
es:
budgets:
index:
section_footer:
description: 'Descripción de la sección de debates'
help_text_1: 'Texto de ayuda 1 para la sección de debates'
help_text_2: 'Texto de ayuda 2 para la sección de debates'
help_text_3: 'Texto de ayuda 3 para la sección de debates'
help_text_4: 'Texto de ayuda 4 para la sección de debates'
community:
topic:
sidebar:
recommendation_one: 'Recomendación 1 en la creación de un tema en la comunidad de propuestas'
recommendation_two: 'Recomendación 2 en la creación de un tema en la comunidad de propuestas'
recommendation_three: 'Recomendación 3 en la creación de un tema en la comunidad de propuestas'
devise:
registrations:
destroyed: 'Mensaje al eliminar una cuenta'
devise_views:
mailer:
confirmation_instructions:
title: 'Título del email de confimación'
debates:
index:
section_footer:
description: 'Descripción de la ayuda del índice de debates'
help_text_1: 'Texto de ayuda 1 del índice de debates'
help_text_2: 'Texto de ayuda 2 del índice de debates'
help_text_3: 'Texto de ayuda 3 del índice de debates'
new:
recommendation_one: 'Recomendación 1 en la creación de debates'
recommendation_two: 'Recomendación 2 en la creación de debates'
recommendation_three: 'Recomendación 3 en la creación de debates'
recommendation_four: 'Recomendación 4 en la creación de debates'
layouts:
footer:
accessibility: 'Nombre enlace accesibilidad'
conditions: 'Nombre enlace condiciones de uso'
privacy: 'Nombre enlace política de privacidad'
proposals:
index:
section_footer:
description: 'Descripción de la página de propuestas'
help_text_1: 'Texto de ayuda 1 en la página de propuestas'
help_text_2: 'Texto de ayuda 2 en la página de propuestas'
help_text_3: 'Texto de ayuda 3 en la página de propuestas'
new:
form:
submit_button: 'Texto del botón "Crear propuesta"'
more_info: 'Texto "Más información" de la página de nueva propuesta'
recommendation_one: 'Recomendación 1 de la página de nueva propuesta'
recommendation_two: 'Recomendación 2 de la página de nueva propuesta'
recommendation_three: 'Recomendación 3 de la página de nueva propuesta'
proposal:
reason_for_supports_necessary: 'Número de apoyos necesarios para las propuestas'
polls:
index:
section_footer:
description: 'Descripción de la página de votaciones'
help_text_1: 'Texto de ayuda 1 en la página de votaciones'
help_text_2: 'Texto de ayuda 2 en la página de votaciones'
legislation:
processes:
index:
section_footer:
description: 'Descripción procesos legislativos'
help_text_1: 'Texto ayuda 1 procesos legislativos'
help_text_2: 'Texto ayuda 2 procesos legislativos'
help_text_3: 'Texto ayuda 3 procesos legislativos'
management:
print:
proposals_info: 'Información de propuestas (impresas)'
proposals_note: 'Nota de propuetas (impresas)'
proposals_title: 'Título de propuestas (impresas)'
budget_investments_info: 'Información de proyectos (impresos)'
budgets_investments_note: 'Notas de proyectos (impresos)'
guides:
title: 'Título de las guías'
subtitle: 'Subtítulo de las guías'
budget_investment:
title: 'Título de guías de presupuestos'
feature_1_html: 'Característica 1 de presupuestos'
feature_2_html: 'Característica 2 de presupuestos'
feature_3_html: 'Característica 3 de presupuestos'
feature_4_html: 'Característica 4 de presupuestos'
new_button: 'Botón de nuevo presupuesto'
proposal:
title: 'Título de guías de propuestas'
feature_1_html: 'Característica 1 de propuestas'
feature_2_html: 'Característica 2 de propuestas'
feature_3_html: 'Característica 3 de propuestas'
feature_4_html: 'Característica 4 de propuestas'
new_button: 'Botón de nueva propuesta'
mailers:
no_reply: 'Mensaje de no contestar'
comment:
hi: 'Saludo email nuevo comentario'
new_comment_by_html: 'Texto nuevo comentario'
subject: 'Asunto nuevo comentario'
title: 'Título nuevo comentario'
config:
manage_email_subscriptions: 'Gestionar subscripciones de emails'
email_verification:
click_here_to_verify: 'Nombre enlace para verificar la cuenta'
instructions_2_html: 'Instrucciones para verificar la cuenta 2'
instructions_html: 'Instrucciones para verificar la cuenta'
subject: 'Asunto verificar la cuenta'
thanks: 'Agradecimientos'
title: 'Título email verificación'
reply:
hi: 'Saludo email nueva respuesta'
new_reply_by_html: 'Texto email nueva respuesta'
subject: 'Asunto email nueva respuesta'
title: 'Título email nueva respuesta'
unfeasible_spending_proposal:
hi: 'Saludo email proyecto inviable'
reconsider_html: 'Texto para reconsiderar el proyecto'
sincerely: 'Saludo final'
signatory: 'Firma'
sorry: 'Disculpas'
subject: 'Asunto'
unfeasible_html: 'Texto email inviable'
budget_investment_unfeasible:
hi: 'Saludo email proyecto inviable'
reconsider_html: 'Texto para reconsiderar la proyecto'
sincerely: 'Saludo final'
signatory: 'Firma'
sorry: 'Disculpas'
subject: 'Asunto'
unfeasible_html: 'Texto email inviable'
proposal_notification_digest:
info: 'Información resumen propuestas'
title: 'Título resumen propuestas'
share: 'Texto compartir propuesta'
comment: 'Texto comentar propuesta'
unsubscribe: 'Cancelar subscripción'
unsubscribe_account: 'Texto enlace cancelar subscripción'
direct_message_for_receiver:
subject: 'Asunto nuevo mensaje privado'
reply: 'Texto enlace responder mensaje privado'
unsubscribe: 'Cancelar avisos de mensajes privados'
unsubscribe_account: 'Texto enlace cancelar subscripción'
direct_message_for_sender:
subject: 'Asunto email envío mensaje privado'
title_html: 'Título email envío mensaje privado'
user_invite:
ignore: 'Ignorar correo de invitación'
text: 'Texto email invitación'
thanks: 'Agradecimiento email invitación'
title: 'Título email invitación'
button: 'Botón email invitación'
subject: 'Asunto email invitación'
budget_investment_created:
subject: 'Asunto email proyecto de inversión'
title: 'Título email proyecto de inversión'
intro_html: 'Saludo'
text_html: 'Texto'
follow_html: 'Texto seguir proceso'
follow_link: 'Texto link seguir proceso'
sincerely: 'Saludo final'
signatory: 'Firma'
share: 'Texto compartir'
budget_investment_selected:
subject: 'Asunto'
hi: 'Saludo'
selected_html: 'Texto seleccionado'
share: 'Compartir'
share_button: 'Botón compartir'
thanks: 'Agradecimientos'
sincerely: 'Saludo final'
signatory: 'Firma'
budget_investment_unselected:
subject: 'Asunto'
hi: 'Saludo'
unselected_html: 'Texto no seleccionado'
participate_html: 'Texto seguir participando'
participate_url: 'URL seguir participando'
thanks: 'Agradecimientos'
sincerely: 'Saludo final'
signatory: 'Firma'

View File

@@ -0,0 +1,17 @@
class CreateI18nContentTranslations < ActiveRecord::Migration
def change
create_table :i18n_contents do |t|
t.string :key
end
reversible do |dir|
dir.up do
I18nContent.create_translation_table! :value => :text
end
dir.down do
I18nContent.drop_translation_table!
end
end
end
end

View File

@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180711224810) do ActiveRecord::Schema.define(version: 20180718115545) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@@ -450,6 +450,21 @@ ActiveRecord::Schema.define(version: 20180711224810) do
add_index "geozones_polls", ["geozone_id"], name: "index_geozones_polls_on_geozone_id", using: :btree add_index "geozones_polls", ["geozone_id"], name: "index_geozones_polls_on_geozone_id", using: :btree
add_index "geozones_polls", ["poll_id"], name: "index_geozones_polls_on_poll_id", using: :btree add_index "geozones_polls", ["poll_id"], name: "index_geozones_polls_on_poll_id", using: :btree
create_table "i18n_content_translations", force: :cascade do |t|
t.integer "i18n_content_id", null: false
t.string "locale", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "value"
end
add_index "i18n_content_translations", ["i18n_content_id"], name: "index_i18n_content_translations_on_i18n_content_id", using: :btree
add_index "i18n_content_translations", ["locale"], name: "index_i18n_content_translations_on_locale", using: :btree
create_table "i18n_contents", force: :cascade do |t|
t.string "key"
end
create_table "identities", force: :cascade do |t| create_table "identities", force: :cascade do |t|
t.integer "user_id" t.integer "user_id"
t.string "provider" t.string "provider"