para-i18n 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 17b8bcf97585a99d6bdebb63fed2edb77b8ee5c8
4
- data.tar.gz: 0c273413dfd04f84dea954ebe616e57873771314
3
+ metadata.gz: 7ca0ae98159f636ad5cf97f4481d066819e4daf2
4
+ data.tar.gz: e2bd232647bd4edee8c06fc82a931fad40ef7211
5
5
  SHA512:
6
- metadata.gz: dfa24b1fc36ecb0f04a82c6ae428109ef6a2d0fdb2f0a8347b28704aba7b06577c70d85fcbe5bc49d0fa5a3bc2efe73f426e4e11a1175dcd6a8cad812db039f6
7
- data.tar.gz: c7c76befcdb61fec112409df695ddcde5523b3c5fb475bc39fb3220ef535cf516667192d2022663b5b4cc2a6c95201d570113079376b2a9b8cd981b32dd08420
6
+ metadata.gz: 79e0910d8bf7ec05871b8721f547ee29b4fd4e5192f2aaf1d8ae7bd1f825dd8a79040d144c538e3c2b1677d1cd8f420d2137d6db0d9ba37ffa6e907bdf876330
7
+ data.tar.gz: b19299a63bb9eff4690854f285eda67dab0c5df3bbd1cbbd703ab5139b71fede3425b49d97abc1bb048390b4b8c6b4372be2cb23cb3937db61de40cab638ad62
data/README.md CHANGED
@@ -57,7 +57,7 @@ end
57
57
  Now, any `crud` component managing the `Page` model will show a button to access
58
58
  the translation interface for existing entries.
59
59
 
60
- ## Overriding the translations form
60
+ ### Overriding the translations form
61
61
 
62
62
  The translation form can be overriden by generating it with the `para:i18n:form`
63
63
  generator. This is useful to delete or add fields to translate that are not
@@ -73,6 +73,82 @@ rails g para:i18n:form page
73
73
  This will create a partial at `app/views/admin/pages/_translations_form.html.haml`
74
74
  that you can override.
75
75
 
76
+ ### Translation fields customization
77
+
78
+ This gem uses a special [Simple Form](https://github.com/plataformatec/simple_form)
79
+ input to manage translations.
80
+
81
+ Example :
82
+
83
+ ```ruby
84
+ = form.input :title, as: :i18n, locale: @target_locale
85
+ ```
86
+
87
+ When generating the translations form, you can edit those inputs, remove or add
88
+ some inputs.
89
+
90
+ #### Managing nested resources
91
+
92
+ You can simply manage nested resources by using the `simple_fields_for` form
93
+ helper :
94
+
95
+ ```ruby
96
+ = form.simple_fields_for :nested_resources do |fields|
97
+ = fields.input :nested_title, as: :i18n, locale: @target_locale
98
+ ```
99
+
100
+ #### Passing options to the translation field
101
+
102
+ The I18n field generates a plain HTML section with the original contents of the
103
+ field (in the `I18n.default_locale` locale), and a sub-input, which uses the
104
+ default type that Para infers from the attribute.
105
+
106
+ You can override the generated field type or pass other options by using the
107
+ `:input_html` option :
108
+
109
+ ```ruby
110
+ = form.input :value, as: :i18n, locale: @target_locale, input_html: { as: :numeric, spinner: false }
111
+ ```
112
+
113
+ Also, if your original content contains useful HTML, you can use the `:html_safe`
114
+ to be applied to the original content by using the `:original_html` option :
115
+
116
+ ```ruby
117
+ = form.input :value, as: :i18n, locale: @target_locale, original_html: { html_safe: true }
118
+ ```
119
+
120
+ ### Locales fallbacks support
121
+
122
+ The gem fully supports Rails' I18n fallbacks and will try to fallback to any
123
+ available translation using the `I18n.fallbacks` hash if you enable them.
124
+
125
+ If not enabled, any untranslated content will be empty.
126
+
127
+ ### Friendly id support
128
+
129
+ The gem comes with friendly_id i18n support, with a dedicated `:i18n` module to
130
+ include instead of the `:slugged` module into your models friendly_id options.
131
+
132
+ For example, for the page :
133
+
134
+ ```ruby
135
+ class Page < ActiveRecord::Base
136
+ translates :title, :slug
137
+
138
+ friendly_id :title, use: [:finders, :i18n]
139
+ end
140
+ ```
141
+
142
+ > **Warning** : You always need to put the `:i18n` fiendly_id module after all
143
+ other modules, so the finder methods included in this module take the current
144
+ locale into account.
145
+
146
+ This will automatically build localized slugs when updating the `title` in any
147
+ locale, and will allow you to find the resources with the translated slugs.
148
+
149
+ Note that if I18n fallbacks are enabled, it will try to fall back to the next
150
+ locale if no resource was found for the given slug and locale.
151
+
76
152
  ## Contributing
77
153
 
78
154
  Bug reports and pull requests are welcome on GitHub at https://github.com/para-cms/para-i18n.
@@ -1,7 +1,11 @@
1
1
  module Para
2
2
  module Admin
3
- class TranslationsController < ::Para::Admin::CrudResourcesController
3
+ class TranslationsController < ::Para::Admin::ResourcesController
4
+ include Para::Admin::ResourceControllerConcerns
5
+
6
+ before_action :load_and_authorize_crud_resource
4
7
  before_action :load_locales
8
+ before_action :add_breadcrumbs
5
9
 
6
10
  def edit
7
11
  end
@@ -19,10 +23,14 @@ module Para
19
23
  private
20
24
 
21
25
  def load_and_authorize_crud_resource
22
- loader = self.class.cancan_resource_class.new(
23
- self, :resource, class: resource_model
24
- )
26
+ options = { class: resource_model }
27
+
28
+ options.merge!(
29
+ singleton: true,
30
+ through: :component
31
+ ) unless params.key?(:resource_id)
25
32
 
33
+ loader = self.class.cancan_resource_class.new(self, :resource, options)
26
34
  loader.load_and_authorize_resource
27
35
  end
28
36
 
@@ -36,7 +44,7 @@ module Para
36
44
  end
37
45
 
38
46
  def add_breadcrumbs
39
- super
47
+ add_breadcrumb(resource_title_for(resource), @component.relation_path(resource, action: :show)) if resource
40
48
  add_breadcrumb(t('para.i18n.translation'))
41
49
  end
42
50
  end
@@ -1,4 +1,4 @@
1
- class Translate<%= plural_class_name %> < ActiveRecord::Migration
1
+ class Translate<%= table_name.camelize %> < ActiveRecord::Migration
2
2
  def change
3
3
  add_column :<%= table_name %>, :_translations, :jsonb
4
4
  end
@@ -15,7 +15,7 @@ module Para
15
15
  desc "I18nAdmin translation generator"
16
16
 
17
17
  def generate_migration
18
- migration_template 'model_migration.rb.erb', "db/migrate/translate_#{ plural_file_name }.rb"
18
+ migration_template 'model_migration.rb.erb', "db/migrate/translate_#{ table_name }.rb"
19
19
  end
20
20
 
21
21
  def migrate
@@ -31,10 +31,6 @@ module Para
31
31
  def plural_class_name
32
32
  @plural_class_name ||= class_name.pluralize
33
33
  end
34
-
35
- def table_name
36
- @table_name ||= plural_class_name.underscore
37
- end
38
34
  end
39
35
  end
40
36
  end
@@ -9,7 +9,11 @@ module Para
9
9
 
10
10
  initializer 'para.i18n.extend_para_routes' do
11
11
  ::Para.config.routes.extend_routes_for(:crud_component) do
12
- resource :translation, only: [:edit, :update]
12
+ resource :translation, only: [:edit, :update], controller: '/para/admin/translations'
13
+ end
14
+
15
+ ::Para.config.routes.extend_routes_for(:form_component) do
16
+ resource :translation, only: [:edit, :update], controller: '/para/admin/translations'
13
17
  end
14
18
  end
15
19
 
@@ -26,12 +30,12 @@ module Para
26
30
  end
27
31
 
28
32
  initializer 'para.i18n.add_translate_actions' do
29
- Para.config.add_actions_for('crud/edit') do
33
+ Para.config.add_actions_for('crud/edit', 'form/show') do
30
34
  {
31
35
  icon: 'globe',
32
36
  label: ::I18n.t('para.i18n.translate'),
33
37
  url: @component.relation_path(resource, :translation, action: :edit)
34
- }
38
+ } if resource.class.translates?
35
39
  end
36
40
  end
37
41
  end
@@ -9,6 +9,15 @@ module FriendlyId
9
9
 
10
10
  def included(model_class)
11
11
  model_class.extend(ClassMethods)
12
+
13
+ # Support for friendly finds on associations for Rails 4.0.1 and above.
14
+ #
15
+ # Borrowed from FriendlyId::Finders module
16
+ #
17
+ if ::ActiveRecord.const_defined?('AssociationRelation')
18
+ association_relation_delegate_class = model_class.relation_delegate_class(::ActiveRecord::AssociationRelation)
19
+ association_relation_delegate_class.send(:include, ClassMethods)
20
+ end
12
21
  end
13
22
  end
14
23
 
@@ -31,21 +40,25 @@ module FriendlyId
31
40
  end
32
41
 
33
42
  module ClassMethods
34
- def exists_by_friendly_id?(id)
35
- if (exists = by_friendly_id(id).exists?)
43
+ def exists_by_friendly_id?(id, locale = ::I18n.locale)
44
+ if (exists = by_friendly_id(id, locale).exists?)
36
45
  exists
37
- elsif (fallback_locale = Para::I18n::Fallbacks.i18n_fallback_for(::I18n.locale))
38
- by_friendly_id(id, fallback_locale).exists?
46
+ elsif (fallback_locale = Para::I18n::Fallbacks.i18n_fallback_for(locale)) &&
47
+ fallback_locale != locale
48
+ then
49
+ exists_by_friendly_id?(id, fallback_locale)
39
50
  end
40
51
  end
41
52
 
42
53
  private
43
54
 
44
- def first_by_friendly_id(id)
45
- if (first = by_friendly_id(id).first)
55
+ def first_by_friendly_id(id, locale = ::I18n.locale)
56
+ if (first = by_friendly_id(id, locale).first)
46
57
  first
47
- elsif (fallback_locale = Para::I18n::Fallbacks.i18n_fallback_for(::I18n.locale))
48
- by_friendly_id(id, fallback_locale).first
58
+ elsif (fallback_locale = Para::I18n::Fallbacks.i18n_fallback_for(locale)) &&
59
+ fallback_locale != locale
60
+ then
61
+ first_by_friendly_id(id, fallback_locale)
49
62
  end
50
63
  end
51
64
 
@@ -53,7 +66,7 @@ module FriendlyId
53
66
  if locale == ::I18n.default_locale
54
67
  where(friendly_id_config.query_field => id)
55
68
  else
56
- json_path = "{#{ ::I18n.locale },#{ friendly_id_config.query_field }}"
69
+ json_path = "{#{ locale },#{ friendly_id_config.query_field }}"
57
70
  where("_translations#>>'#{ json_path }' = ?", id)
58
71
  end
59
72
  end
@@ -1,18 +1,26 @@
1
1
  module Para
2
2
  module Inputs
3
3
  class I18nInput < SimpleForm::Inputs::Base
4
+ delegate :content_tag, to: :template
5
+
4
6
  def input(wrapper_options = nil)
5
7
  model = object.class
8
+ render
9
+ end
6
10
 
7
- template.render(
8
- partial: 'para/inputs/i18n',
9
- locals: {
10
- form: @builder,
11
- resource: object,
12
- attribute_name: attribute_name,
13
- locale: locale
14
- }
15
- )
11
+ def render
12
+ content_tag(:div, class: 'row') do
13
+ content_tag(:div, class: 'col-md-6') do
14
+ ::I18n.with_locale(::I18n.default_locale) do
15
+ original_content
16
+ end
17
+ end +
18
+ content_tag(:div, class: 'col-md-6') do
19
+ ::I18n.with_locale(locale) do
20
+ @builder.input_field(attribute_name, input_options)
21
+ end
22
+ end
23
+ end
16
24
  end
17
25
 
18
26
  private
@@ -24,6 +32,23 @@ module Para
24
32
  raise 'Missing `:locale` option passed to :i18n input.'
25
33
  end
26
34
  end
35
+
36
+ def original_content
37
+ value = template.value_for(object, attribute_name)
38
+ value = value.html_safe if original_options[:html_safe]
39
+ value
40
+ end
41
+
42
+ def original_options
43
+ @original_options ||= options[:original_html] || {}
44
+ end
45
+
46
+ def input_options
47
+ @input_options ||= (options[:input_html] || {}).tap do |hash|
48
+ hash[:class] ||= ''
49
+ hash[:class] += ' form-control'
50
+ end
51
+ end
27
52
  end
28
53
  end
29
54
  end
@@ -4,11 +4,11 @@ module Para
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_attribute :translated_fields
7
+ class_attribute :translated_attribute_names
8
8
  end
9
9
 
10
10
  def read_translated_attribute(field, locale = ::I18n.locale)
11
- return read_attribute(field) if locale == ::I18n.default_locale
11
+ return read_plain_or_store_attribute(field) if locale == ::I18n.default_locale
12
12
 
13
13
  if model_translations[locale.to_s]
14
14
  if (translation = model_translations[locale.to_s][field.to_s])
@@ -23,7 +23,7 @@ module Para
23
23
  end
24
24
 
25
25
  def write_translated_attribute field, value, locale = ::I18n.locale
26
- return write_attribute(field, value) if locale == ::I18n.default_locale
26
+ return write_plain_or_store_attribute(field, value) if locale == ::I18n.default_locale
27
27
 
28
28
  model_translations[locale.to_s] ||= {}
29
29
  model_translations[locale.to_s][field.to_s] = value
@@ -40,12 +40,46 @@ module Para
40
40
  end
41
41
 
42
42
  def translation_for(locale)
43
- model_translations[locale] || {}
43
+ model_translations[locale.to_s] || {}
44
+ end
45
+
46
+ private
47
+
48
+ def read_plain_or_store_attribute(field)
49
+ if plain_column?(field)
50
+ read_attribute(field)
51
+ elsif (store_name = find_stored_column(field))
52
+ read_store_attribute(store_name, field)
53
+ else
54
+ raise ActiveRecord::UnknownAttributeError.new(self, field)
55
+ end
56
+ end
57
+
58
+ def write_plain_or_store_attribute(field, value)
59
+ if plain_column?(field)
60
+ write_attribute(field, value)
61
+ elsif (store_name = find_stored_column(field))
62
+ write_store_attribute(store_name, field, value)
63
+ else
64
+ raise ActiveRecord::UnknownAttributeError.new(self, field)
65
+ end
66
+ end
67
+
68
+ def plain_column?(field)
69
+ self.class.columns_hash.key?(field.to_s)
70
+ end
71
+
72
+ def find_stored_column(field)
73
+ store = self.class.stored_attributes.find do |name, attributes|
74
+ attributes.include?(field.to_sym)
75
+ end
76
+
77
+ store.first if store
44
78
  end
45
79
 
46
80
  module ClassMethods
47
81
  def translates(*fields)
48
- self.translated_fields = fields.map(&:to_sym)
82
+ self.translated_attribute_names = fields.map(&:to_sym)
49
83
 
50
84
  fields.each do |field|
51
85
  define_method field do
@@ -58,8 +92,8 @@ module Para
58
92
  end
59
93
  end
60
94
 
61
- def translatable?
62
- translated_fields.length > 0
95
+ def translates?
96
+ translated_attribute_names && translated_attribute_names.length > 0
63
97
  end
64
98
  end
65
99
  end
@@ -10,7 +10,7 @@ module Para
10
10
  end
11
11
 
12
12
  def translate_button(resource)
13
- return unless resource.class.translatable?
13
+ return unless resource.class.translates?
14
14
 
15
15
  path = component.relation_path(resource, :translation, action: :edit)
16
16
  options = { class: 'btn btn-info' }
@@ -4,7 +4,8 @@ module Para
4
4
  # TODO : Support images & co
5
5
  def translated_model_fields_for(model)
6
6
  model_field_mappings(model).fields.select do |field|
7
- model.translated_fields.include?(field.name.to_sym)
7
+ model.translated_attribute_names &&
8
+ model.translated_attribute_names.include?(field.name.to_sym)
8
9
  end
9
10
  end
10
11
  end
@@ -1,5 +1,5 @@
1
1
  module Para
2
2
  module I18n
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
data/lib/para/i18n.rb CHANGED
@@ -15,5 +15,8 @@ require 'para/i18n/engine'
15
15
  module Para
16
16
  module I18n
17
17
  # Your code goes here...
18
+ def self.method_missing(method, *args, &block)
19
+ ::I18n.send(method, *args, &block)
20
+ end
18
21
  end
19
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: para-i18n
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valentin Ballestrino
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-05-11 00:00:00.000000000 Z
11
+ date: 2016-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,7 +71,6 @@ files:
71
71
  - app/controllers/para/admin/translations_controller.rb
72
72
  - app/views/para/admin/resources/_translations_form.html.haml
73
73
  - app/views/para/admin/translations/edit.html.haml
74
- - app/views/para/inputs/_i18n.html.haml
75
74
  - bin/console
76
75
  - bin/setup
77
76
  - config/locales/para-i18n.fr.yml
@@ -1,7 +0,0 @@
1
- .row
2
- .col-md-6
3
- - I18n.with_locale(I18n.default_locale) do
4
- = field_value_for(resource, attribute_name)
5
- .col-md-6
6
- - I18n.with_locale(locale) do
7
- = form.input_field attribute_name, class: 'form-control'