para-i18n 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'