decidim-comments 0.0.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 +7 -0
- data/README.md +42 -0
- data/Rakefile +2 -0
- data/app/assets/config/decidim_comments_manifest.js +1 -0
- data/app/assets/javascripts/decidim/comments/bundle.js +504 -0
- data/app/assets/javascripts/decidim/comments/bundle.js.map +1 -0
- data/app/assets/javascripts/decidim/comments/comments.js.erb +8 -0
- data/app/commands/decidim/comments/create_comment.rb +40 -0
- data/app/forms/decidim/comments/comment_form.rb +16 -0
- data/app/frontend/application/apollo_client.js +16 -0
- data/app/frontend/application/application.component.jsx +37 -0
- data/app/frontend/application/application.component.test.jsx +33 -0
- data/app/frontend/application/icon.component.jsx +21 -0
- data/app/frontend/application/icon.component.test.jsx +43 -0
- data/app/frontend/comments/add_comment_form.component.jsx +250 -0
- data/app/frontend/comments/add_comment_form.component.test.jsx +173 -0
- data/app/frontend/comments/add_comment_form.mutation.graphql +8 -0
- data/app/frontend/comments/comment.component.jsx +202 -0
- data/app/frontend/comments/comment.component.test.jsx +125 -0
- data/app/frontend/comments/comment.fragment.graphql +12 -0
- data/app/frontend/comments/comment_data.fragment.graphql +11 -0
- data/app/frontend/comments/comment_order_selector.component.jsx +28 -0
- data/app/frontend/comments/comment_order_selector.component.test.jsx +9 -0
- data/app/frontend/comments/comment_thread.component.jsx +64 -0
- data/app/frontend/comments/comment_thread.component.test.jsx +71 -0
- data/app/frontend/comments/comment_thread.fragment.graphql +9 -0
- data/app/frontend/comments/comments.component.jsx +139 -0
- data/app/frontend/comments/comments.component.test.jsx +106 -0
- data/app/frontend/comments/comments.query.graphql +10 -0
- data/app/frontend/comments/featured_comment.component.jsx +23 -0
- data/app/frontend/comments/featured_comment.component.test.jsx +15 -0
- data/app/frontend/entry.js +24 -0
- data/app/frontend/entry.test.js +29 -0
- data/app/frontend/support/asset_url.js +11 -0
- data/app/frontend/support/generate_comments_data.js +29 -0
- data/app/frontend/support/generate_current_user_data.js +13 -0
- data/app/frontend/support/load_translations.js +23 -0
- data/app/frontend/support/require_all.js +10 -0
- data/app/frontend/support/resolve_graphql_query.js +37 -0
- data/app/frontend/support/stub_component.js +29 -0
- data/app/helpers/decidim/comments/comments_helper.rb +51 -0
- data/app/models/decidim/comments/comment.rb +55 -0
- data/app/types/decidim/comments/add_comment_type.rb +12 -0
- data/app/types/decidim/comments/author_type.rb +15 -0
- data/app/types/decidim/comments/comment_type.rb +28 -0
- data/config/i18n-tasks.yml +124 -0
- data/config/locales/ca.yml +35 -0
- data/config/locales/en.yml +36 -0
- data/config/locales/es.yml +35 -0
- data/db/migrate/20161130143508_create_comments.rb +11 -0
- data/db/migrate/20161214082645_add_depth_to_comments.rb +5 -0
- data/db/migrate/20161216102820_add_alignment_to_comments.rb +5 -0
- data/db/seeds.rb +11 -0
- data/lib/decidim/comments.rb +10 -0
- data/lib/decidim/comments/engine.rb +34 -0
- data/lib/decidim/comments/mutation_extensions.rb +36 -0
- data/lib/decidim/comments/query_extensions.rb +33 -0
- metadata +228 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
import { Component } from 'react';
|
2
|
+
import { I18n } from 'react-i18nify';
|
3
|
+
|
4
|
+
import Comment from './comment.component';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* A wrapper component for a highlighted component.
|
8
|
+
* @class
|
9
|
+
* @augments Component
|
10
|
+
* @todo It's not used right now
|
11
|
+
*/
|
12
|
+
export default class FeaturedComment extends Component {
|
13
|
+
render() {
|
14
|
+
return (
|
15
|
+
<section className="comments">
|
16
|
+
<h4 className="section-heading">{ I18n.t("components.featured_comment.title") }</h4>
|
17
|
+
<div className="comment-thread comment--pinned">
|
18
|
+
<Comment />
|
19
|
+
</div>
|
20
|
+
</section>
|
21
|
+
);
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { shallow } from 'enzyme';
|
2
|
+
|
3
|
+
import FeaturedComment from './featured_comment.component';
|
4
|
+
import Comment from './comment.component';
|
5
|
+
|
6
|
+
import stubComponent from '../support/stub_component';
|
7
|
+
|
8
|
+
describe('<FeaturedComment />', () => {
|
9
|
+
stubComponent(Comment);
|
10
|
+
|
11
|
+
it("should render a section of class comments", () => {
|
12
|
+
const wrapper = shallow(<FeaturedComment />);
|
13
|
+
expect(wrapper.find('section.comments')).to.be.present();
|
14
|
+
});
|
15
|
+
});
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import ReactDOM from 'react-dom';
|
2
|
+
|
3
|
+
import loadTranslations from './support/load_translations';
|
4
|
+
import Comments from './comments/comments.component';
|
5
|
+
|
6
|
+
// Expose global components
|
7
|
+
window.DecidimComments.renderCommentsComponent = (nodeId, props) => {
|
8
|
+
var node = $(`#${nodeId}`)[0];
|
9
|
+
|
10
|
+
ReactDOM.render(
|
11
|
+
React.createElement(Comments,props),
|
12
|
+
node
|
13
|
+
);
|
14
|
+
|
15
|
+
function unmountComponent() {
|
16
|
+
ReactDOM.unmountComponentAtNode(node);
|
17
|
+
$(document).off('turbolinks:before-render', unmountComponent);
|
18
|
+
}
|
19
|
+
|
20
|
+
$(document).on('turbolinks:before-render', unmountComponent);
|
21
|
+
};
|
22
|
+
|
23
|
+
// Load component locales from yaml files
|
24
|
+
loadTranslations();
|
@@ -0,0 +1,29 @@
|
|
1
|
+
// ---------------------------------------
|
2
|
+
// Test Environment Setup
|
3
|
+
// ---------------------------------------
|
4
|
+
import sinon from 'sinon/pkg/sinon';
|
5
|
+
import chai from 'chai';
|
6
|
+
import sinonChai from 'sinon-chai';
|
7
|
+
import chaiAsPromised from 'chai-as-promised';
|
8
|
+
import chaiEnzyme from 'chai-enzyme';
|
9
|
+
import loadTranslations from './support/load_translations';
|
10
|
+
import requireAll from './support/require_all';
|
11
|
+
|
12
|
+
//
|
13
|
+
chai.use(sinonChai)
|
14
|
+
chai.use(chaiAsPromised)
|
15
|
+
chai.use(chaiEnzyme())
|
16
|
+
//
|
17
|
+
window.chai = chai
|
18
|
+
window.sinon = sinon
|
19
|
+
window.expect = chai.expect
|
20
|
+
window.should = chai.should()
|
21
|
+
|
22
|
+
// ---------------------------------------
|
23
|
+
// Require Tests
|
24
|
+
// ---------------------------------------
|
25
|
+
requireAll(require.context('./application/', true, /\.test\.jsx?$/));
|
26
|
+
requireAll(require.context('./comments/', true, /\.test\.jsx?$/));
|
27
|
+
|
28
|
+
// Load component locales from yaml files
|
29
|
+
loadTranslations();
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { random, name, date, image } from 'faker/locale/en';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Generate random comment data to emulate a database real content
|
5
|
+
* @param {number} num - The number of comments to generate random data
|
6
|
+
* @returns {Object[]} - An array of objects representing comments data
|
7
|
+
*/
|
8
|
+
const generateCommentsData = (num = 1) => {
|
9
|
+
let commentsData = [];
|
10
|
+
|
11
|
+
for (let idx = 0; idx < num; idx += 1) {
|
12
|
+
commentsData.push({
|
13
|
+
id: random.uuid(),
|
14
|
+
body: random.words(),
|
15
|
+
createdAt: date.past().toString(),
|
16
|
+
author: {
|
17
|
+
name: name.findName(),
|
18
|
+
avatarUrl: image.imageUrl()
|
19
|
+
},
|
20
|
+
replies: [],
|
21
|
+
canHaveReplies: true,
|
22
|
+
alignment: 0
|
23
|
+
})
|
24
|
+
}
|
25
|
+
|
26
|
+
return commentsData;
|
27
|
+
};
|
28
|
+
|
29
|
+
export default generateCommentsData;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { name } from 'faker/locale/en';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Generate random current user data to emulate a database real content
|
5
|
+
* @returns {Object} - An object representing current user data
|
6
|
+
*/
|
7
|
+
const generateCurrentUserData = () => {
|
8
|
+
return {
|
9
|
+
name: name.findName()
|
10
|
+
};
|
11
|
+
};
|
12
|
+
|
13
|
+
export default generateCurrentUserData;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
2
|
+
import { I18n } from 'react-i18nify';
|
3
|
+
import requireAll from './require_all';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Load components translations from yaml files and import them into
|
7
|
+
* react-i18ify system so they can be used via `I18n.t` method.
|
8
|
+
* @returns {Void} - Nothing
|
9
|
+
*/
|
10
|
+
const loadTranslations = () => {
|
11
|
+
const translationsContext = require.context('../../../config/locales/', true, /\.yml$/);
|
12
|
+
const translationFiles = requireAll(translationsContext);
|
13
|
+
|
14
|
+
const translations = translationsContext.keys().reduce((acc, key, index) => {
|
15
|
+
const locale = key.match(/\.\/(.*)\.yml/)[1];
|
16
|
+
acc[locale] = translationFiles[index][locale].decidim;
|
17
|
+
return acc;
|
18
|
+
}, {});
|
19
|
+
|
20
|
+
I18n.setTranslations(translations);
|
21
|
+
};
|
22
|
+
|
23
|
+
export default loadTranslations;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* Given a webpack require context it require all the files
|
3
|
+
* @param {Object} requireContext - A webpack require context
|
4
|
+
* @returns {Object[]} - An array of webpack modules
|
5
|
+
*/
|
6
|
+
const requireAll = (requireContext) => {
|
7
|
+
return requireContext.keys().map(requireContext);
|
8
|
+
};
|
9
|
+
|
10
|
+
export default requireAll;
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import graphql, { filter } from 'graphql-anywhere';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* A simple resolver which returns object properties to easily
|
5
|
+
* traverse a graphql response
|
6
|
+
* @param {String} fieldName - An object property
|
7
|
+
* @param {Object} root - An object
|
8
|
+
* @returns {any} - An object's property value
|
9
|
+
*/
|
10
|
+
const resolver = (fieldName, root) => root[fieldName];
|
11
|
+
|
12
|
+
/**
|
13
|
+
* A helper function to mock a graphql api request and return its
|
14
|
+
* result. The result can be filtered by the same query so it just
|
15
|
+
* returns a data subset.
|
16
|
+
* @param {String} document - A graphql query document
|
17
|
+
* @param {options} options - An object with optional options
|
18
|
+
* @returns {Object} - The result of the query filtered or not
|
19
|
+
*/
|
20
|
+
const resolveGraphQLQuery = (document, options = {}) => {
|
21
|
+
const { filterResult, rootValue, context, variables } = options;
|
22
|
+
|
23
|
+
let result = graphql(
|
24
|
+
resolver,
|
25
|
+
document,
|
26
|
+
rootValue,
|
27
|
+
context,
|
28
|
+
variables
|
29
|
+
);
|
30
|
+
|
31
|
+
if (filterResult) {
|
32
|
+
return filter(document, result);
|
33
|
+
}
|
34
|
+
return result;
|
35
|
+
}
|
36
|
+
|
37
|
+
export default resolveGraphQLQuery;
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
2
|
+
|
3
|
+
/**
|
4
|
+
* A helper function to stub the `propTypes` and `fragments` of a component.
|
5
|
+
* Useful for testing isolated components so the children propTypes are not
|
6
|
+
* evaluated.
|
7
|
+
* @param {ReactComponent} componentClass - A component constructor function or class
|
8
|
+
* @param {Object} options - An object which properties are used to stub component properties.
|
9
|
+
* @returns {ReactComponent} - A component with some properties stubbed
|
10
|
+
*/
|
11
|
+
const stubComponent = function(componentClass, options = {}) {
|
12
|
+
let originalPropTypes = {};
|
13
|
+
let originalFragments = {};
|
14
|
+
|
15
|
+
beforeEach(function() {
|
16
|
+
originalPropTypes = componentClass.propTypes;
|
17
|
+
originalFragments = componentClass.fragments;
|
18
|
+
|
19
|
+
componentClass.propTypes = options.propTypes || {};
|
20
|
+
componentClass.fragments = options.fragments || {};
|
21
|
+
});
|
22
|
+
|
23
|
+
afterEach(function() {
|
24
|
+
componentClass.propTypes = originalPropTypes;
|
25
|
+
componentClass.fragments = originalFragments;
|
26
|
+
});
|
27
|
+
};
|
28
|
+
|
29
|
+
export default stubComponent;
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Comments
|
4
|
+
# A helper to expose the comments component for a commentable
|
5
|
+
module CommentsHelper
|
6
|
+
# Creates a Comments component which is rendered using `react_ujs`
|
7
|
+
# from react-rails gem
|
8
|
+
#
|
9
|
+
# resource - A commentable resource
|
10
|
+
# options - A hash of options (default: {})
|
11
|
+
# :arguable - A boolean value to indicate if tihs option is available
|
12
|
+
#
|
13
|
+
# Returns a div which contain a RectComponent to be rendered by `react_ujs`
|
14
|
+
def comments_for(resource, options = {})
|
15
|
+
commentable_type = resource.class.name
|
16
|
+
commentable_id = resource.id.to_s
|
17
|
+
node_id = "comments-for-#{commentable_type.demodulize}-#{commentable_id}"
|
18
|
+
|
19
|
+
react_comments_component(node_id, commentableType: commentable_type,
|
20
|
+
commentableId: commentable_id,
|
21
|
+
options: options.slice(:arguable),
|
22
|
+
locale: I18n.locale)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Private: Render Comments component using inline javascript
|
26
|
+
#
|
27
|
+
# node_id - The id of the DOMElement to render the React component
|
28
|
+
# props - A hash corresponding to Comments component props
|
29
|
+
def react_comments_component(node_id, props)
|
30
|
+
content_tag("div", "", id: node_id) +
|
31
|
+
javascript_tag(%{
|
32
|
+
jQuery.ajax({
|
33
|
+
url: '#{asset_path("decidim/comments/comments.js")}',
|
34
|
+
dataType: 'script',
|
35
|
+
cache: true
|
36
|
+
}).then(function () {
|
37
|
+
window.DecidimComments.renderCommentsComponent(
|
38
|
+
'#{node_id}',
|
39
|
+
{
|
40
|
+
commentableType: "#{props[:commentableType]}",
|
41
|
+
commentableId: "#{props[:commentableId]}",
|
42
|
+
options: JSON.parse("#{j(props[:options].to_json)}"),
|
43
|
+
locale: "#{props[:locale]}"
|
44
|
+
}
|
45
|
+
);
|
46
|
+
});
|
47
|
+
})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Comments
|
4
|
+
# Some resources will be configured as commentable objects so users can
|
5
|
+
# comment on them. The will be able to create conversations between users
|
6
|
+
# to discuss or share their thoughts about the resource.
|
7
|
+
class Comment < ApplicationRecord
|
8
|
+
# Limit the max depth of a comment tree. If C is a comment and R is a reply:
|
9
|
+
# C (depth 0)
|
10
|
+
# |--R (depth 1)
|
11
|
+
# |--R (depth 1)
|
12
|
+
# |--R (depth 2)
|
13
|
+
# |--R (depth 3)
|
14
|
+
MAX_DEPTH = 3
|
15
|
+
|
16
|
+
belongs_to :author, class_name: Decidim::User
|
17
|
+
belongs_to :commentable, polymorphic: true
|
18
|
+
has_many :replies, as: :commentable, class_name: Comment
|
19
|
+
|
20
|
+
validates :author, :commentable, :body, presence: true
|
21
|
+
validate :commentable_can_have_replies
|
22
|
+
validates :depth, numericality: { greater_than_or_equal_to: 0 }
|
23
|
+
validates :alignment, inclusion: { in: [0, 1, -1] }
|
24
|
+
validate :same_organization
|
25
|
+
|
26
|
+
before_save :compute_depth
|
27
|
+
|
28
|
+
delegate :organization, to: :commentable
|
29
|
+
|
30
|
+
# Public: Define if a comment can have replies or not
|
31
|
+
#
|
32
|
+
# Returns a bool value to indicate if comment can have replies
|
33
|
+
def can_have_replies?
|
34
|
+
depth < MAX_DEPTH
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Private: Check if commentable can have replies and if not adds
|
40
|
+
# a validation error to the model
|
41
|
+
def commentable_can_have_replies
|
42
|
+
errors.add(:commentable, :cannot_have_replies) if commentable.respond_to?(:can_have_replies?) && !commentable.can_have_replies?
|
43
|
+
end
|
44
|
+
|
45
|
+
# Private: Compute comment depth inside the current comment tree
|
46
|
+
def compute_depth
|
47
|
+
self.depth = commentable.depth + 1 if commentable.respond_to?(:depth)
|
48
|
+
end
|
49
|
+
|
50
|
+
def same_organization
|
51
|
+
errors.add(:commentable, :invalid) unless author.organization == organization
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Comments
|
4
|
+
# This type represents a mutation to create new comments.
|
5
|
+
AddCommentType = GraphQL::ObjectType.define do
|
6
|
+
name "Add comment"
|
7
|
+
description "Add a new comment"
|
8
|
+
|
9
|
+
field :comment, CommentType, "The new created comment"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Comments
|
4
|
+
# This type represents an author who owns a resource
|
5
|
+
AuthorType = GraphQL::ObjectType.define do
|
6
|
+
name "Author"
|
7
|
+
description "An author"
|
8
|
+
|
9
|
+
field :name, !types.String, "The user's name"
|
10
|
+
field :avatarUrl, !types.String, "The user's avatar url" do
|
11
|
+
resolve ->(obj, _args, _ctx) { obj.avatar.url }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Comments
|
4
|
+
# This type represents a comment on a commentable object.
|
5
|
+
CommentType = GraphQL::ObjectType.define do
|
6
|
+
name "Comment"
|
7
|
+
description "A comment"
|
8
|
+
|
9
|
+
field :id, !types.ID, "The Comment's unique ID"
|
10
|
+
|
11
|
+
field :body, !types.String, "The comment message"
|
12
|
+
|
13
|
+
field :createdAt, !types.String, "The creation date of the comment" do
|
14
|
+
property :created_at
|
15
|
+
end
|
16
|
+
|
17
|
+
field :author, !AuthorType, "The comment's author"
|
18
|
+
|
19
|
+
field :replies, !types[CommentType], "The comment's replies"
|
20
|
+
|
21
|
+
field :canHaveReplies, !types.Boolean, "Define if a comment can or not have replies" do
|
22
|
+
property :can_have_replies?
|
23
|
+
end
|
24
|
+
|
25
|
+
field :alignment, types.Int, "The comment's alignment. Can be 0 (neutral), 1 (in favor) or -1 (against)'"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# i18n-tasks finds and manages missing and unused translations: https://github.com/glebm/i18n-tasks
|
2
|
+
|
3
|
+
# The "main" locale.
|
4
|
+
base_locale: en
|
5
|
+
## All available locales are inferred from the data by default. Alternatively, specify them explicitly:
|
6
|
+
locales: [en]
|
7
|
+
## Reporting locale, default: en. Available: en, ru.
|
8
|
+
# internal_locale: en
|
9
|
+
|
10
|
+
# Read and write translations.
|
11
|
+
data:
|
12
|
+
## Translations are read from the file system. Supported format: YAML, JSON.
|
13
|
+
## Provide a custom adapter:
|
14
|
+
# adapter: I18n::Tasks::Data::FileSystem
|
15
|
+
|
16
|
+
# Locale files or `File.find` patterns where translations are read from:
|
17
|
+
read:
|
18
|
+
## Default:
|
19
|
+
# - config/locales/%{locale}.yml
|
20
|
+
## More files:
|
21
|
+
# - config/locales/**/*.%{locale}.yml
|
22
|
+
## Another gem (replace %#= with %=):
|
23
|
+
# - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
|
24
|
+
|
25
|
+
# Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
|
26
|
+
# `i18n-tasks normalize -p` will force move the keys according to these rules
|
27
|
+
write:
|
28
|
+
## For example, write devise and simple form keys to their respective files:
|
29
|
+
# - ['{devise, simple_form}.*', 'config/locales/\1.%{locale}.yml']
|
30
|
+
## Catch-all default:
|
31
|
+
# - config/locales/%{locale}.yml
|
32
|
+
|
33
|
+
## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, or a custom class.
|
34
|
+
# router: convervative_router
|
35
|
+
|
36
|
+
yaml:
|
37
|
+
write:
|
38
|
+
# do not wrap lines at 80 characters
|
39
|
+
line_width: -1
|
40
|
+
|
41
|
+
## Pretty-print JSON:
|
42
|
+
# json:
|
43
|
+
# write:
|
44
|
+
# indent: ' '
|
45
|
+
# space: ' '
|
46
|
+
# object_nl: "\n"
|
47
|
+
# array_nl: "\n"
|
48
|
+
|
49
|
+
# Find translate calls
|
50
|
+
search:
|
51
|
+
## Paths or `File.find` patterns to search in:
|
52
|
+
# paths:
|
53
|
+
# - app/
|
54
|
+
|
55
|
+
## Root directories for relative keys resolution.
|
56
|
+
# relative_roots:
|
57
|
+
# - app/controllers
|
58
|
+
# - app/helpers
|
59
|
+
# - app/mailers
|
60
|
+
# - app/presenters
|
61
|
+
# - app/views
|
62
|
+
|
63
|
+
## Files or `File.fnmatch` patterns to exclude from search. Some files are always excluded regardless of this setting:
|
64
|
+
## %w(*.jpg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less *.yml *.json)
|
65
|
+
exclude:
|
66
|
+
- app/assets/images
|
67
|
+
- app/assets/fonts
|
68
|
+
- app/assets/javascripts/decidim/comments/bundle.js
|
69
|
+
- app/assets/javascripts/decidim/comments/bundle.js.map
|
70
|
+
|
71
|
+
## Alternatively, the only files or `File.fnmatch patterns` to search in `paths`:
|
72
|
+
## If specified, this settings takes priority over `exclude`, but `exclude` still applies.
|
73
|
+
# only: ["*.rb", "*.html.slim"]
|
74
|
+
|
75
|
+
## If `strict` is `false`, guess usages such as t("categories.#{category}.title"). The default is `true`.
|
76
|
+
# strict: true
|
77
|
+
|
78
|
+
## Multiple scanners can be used. Their results are merged.
|
79
|
+
## The options specified above are passed down to each scanner. Per-scanner options can be specified as well.
|
80
|
+
## See this example of a custom scanner: https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example
|
81
|
+
|
82
|
+
## Google Translate
|
83
|
+
# translation:
|
84
|
+
# # Get an API key and set billing info at https://code.google.com/apis/console to use Google Translate
|
85
|
+
# api_key: "AbC-dEf5"
|
86
|
+
|
87
|
+
## Do not consider these keys missing:
|
88
|
+
ignore_missing:
|
89
|
+
- 'components.*'
|
90
|
+
# - 'errors.messages.{accepted,blank,invalid,too_short,too_long}'
|
91
|
+
# - '{devise,simple_form}.*'
|
92
|
+
|
93
|
+
## Consider these keys used:
|
94
|
+
ignore_unused:
|
95
|
+
- 'activerecord.errors.messages.*'
|
96
|
+
- 'decidim.components.*'
|
97
|
+
# - '{devise,kaminari,will_paginate}.*'
|
98
|
+
# - 'simple_form.{yes,no}'
|
99
|
+
# - 'simple_form.{placeholders,hints,labels}.*'
|
100
|
+
# - 'simple_form.{error_notification,required}.:'
|
101
|
+
|
102
|
+
## Exclude these keys from the `i18n-tasks eq-base' report:
|
103
|
+
# ignore_eq_base:
|
104
|
+
# all:
|
105
|
+
# - common.ok
|
106
|
+
# fr,es:
|
107
|
+
# - common.brand
|
108
|
+
|
109
|
+
## Ignore these keys completely:
|
110
|
+
# ignore:
|
111
|
+
# - kaminari.*
|
112
|
+
|
113
|
+
## Sometimes, it isn't possible for i18n-tasks to match the key correctly,
|
114
|
+
## e.g. in case of a relative key defined in a helper method.
|
115
|
+
## In these cases you can use the built-in PatternMapper to map patterns to keys, e.g.:
|
116
|
+
#
|
117
|
+
# <%#= I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
|
118
|
+
# only: %w(*.html.haml *.html.slim),
|
119
|
+
# patterns: [['= title\b', '.page_title']] %>
|
120
|
+
#
|
121
|
+
# The PatternMapper can also match key literals via a special %{key} interpolation, e.g.:
|
122
|
+
#
|
123
|
+
# <%#= I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
|
124
|
+
# patterns: [['\bSpree\.t[( ]\s*%{key}', 'spree.%{key}']] %>
|