platformos-check 0.4.9 → 0.4.10

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
  SHA256:
3
- metadata.gz: e41eee4e2aa717be9663df4efbc1acf79bc8b571784808b80177f9298fdad9e9
4
- data.tar.gz: 6bed8f9e1e3e22dbd02cee00a15d5e9ce1787f7826ee09e3e461fcf0161c681d
3
+ metadata.gz: 269921738309c4cc1a1d7afef16318af6a75f050fad261530d008f53c464ce62
4
+ data.tar.gz: 0a3c1b2704f40a8244a4608ff6d9f26b0cb7757f7e93182e875edb55344c6fd9
5
5
  SHA512:
6
- metadata.gz: 71a9feab2a3d5e31a2436652036bf1669c65424f24cfa550feedb49bd43e03264f2feb51b0367c84a79805c3cda0d24ed3ccccdd27d2b63a09dd15f3e26a94b4
7
- data.tar.gz: 56961261716dae13fce53dadb2c6e03d8c3f268195748e81783cfe4dd7162c18ffb05063530975939ad2805f2a2e33d5d63aab1da51de65f3c98e17e1e2df2b4
6
+ metadata.gz: 6de2d63705409c6b3d818f48799c5c65809c26304b64d675248c21a170562098af119d954c4d5ea77f0a09559c571e64ea10a8f3417fd406c7644999048e8916
7
+ data.tar.gz: bc35549e8dc86f7f924937a9abc7306dfb6629c05311e38b092219432b9984a855b5d1bbeda8e23503cbe903fbebeff2fb4e4538a012ec4a0dd2697a8bc6f183
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ v0.4.10 / 2024-02-19
2
+ ==================
3
+
4
+ * Add documentLink for translations (both for `| t` and `| l` filters)
5
+ * Add TranslationKeyExists, TranslationFilesMatch offenses to warn about issues with missing translation or inconsistency between multiple language translation files
6
+
1
7
  v0.4.9 / 2024-01-10
2
8
  ==================
3
9
 
data/README.md CHANGED
@@ -8,28 +8,28 @@ PlatformOS Check is a tool that helps you follow platformOS recommendations & be
8
8
 
9
9
  PlatformOS Check currently checks for the following:
10
10
 
11
- ✅ Liquid syntax errors
12
- ✅ JSON syntax errors
13
- ✅ Missing partials and graphqls
14
- ✅ Unused variables (via `{% assign var = ... %}`, {% function var = ... %} etc.)
15
- ✅ Unused partials
16
- ✅ Template length
17
- ✅ Deprecated tags
18
- ✅ Unknown tags
19
- ✅ Unknown filters
20
- ✅ Missing or extra spaces inside `{% ... %}` and `{{ ... }}`
21
- ✅ Using several `{% ... %}` instead of `{% liquid ... %}`
22
- ✅ Undefined objects
23
- ✅ Deprecated filters
24
- ✅ Missing `platformos-check-enable` comment
25
- ✅ Invalid arguments provided to `{% graphql %}` tags
26
- ✅ Missing `authenticity_token` in `<form>`
11
+ ✅ Liquid syntax errors
12
+ ✅ JSON syntax errors
13
+ ✅ Missing partials and graphqls
14
+ ✅ Unused variables (via `{% assign var = ... %}`, {% function var = ... %} etc.)
15
+ ✅ Unused partials
16
+ ✅ Template length
17
+ ✅ Deprecated tags
18
+ ✅ Unknown tags
19
+ ✅ Unknown filters
20
+ ✅ Missing or extra spaces inside `{% ... %}` and `{{ ... }}`
21
+ ✅ Using several `{% ... %}` instead of `{% liquid ... %}`
22
+ ✅ Undefined objects
23
+ ✅ Deprecated filters
24
+ ✅ Missing `platformos-check-enable` comment
25
+ ✅ Invalid arguments provided to `{% graphql %}` tags
26
+ ✅ Missing `authenticity_token` in `<form>`
27
27
  ✅ Unreachable code
28
28
 
29
29
  As well as checks that prevent easy to spot performance problems:
30
30
 
31
- ✅ [GraphQL in for loop](/docs/checks/graphql_in_for_loop.md)
32
- ✅ Use of [parser-blocking](/docs/checks/parser_blocking_javascript.md) JavaScript
31
+ ✅ [GraphQL in for loop](/docs/checks/graphql_in_for_loop.md)
32
+ ✅ Use of [parser-blocking](/docs/checks/parser_blocking_javascript.md) JavaScript
33
33
  ✅ [Missing width and height attributes on `img` tags](/docs/checks/img_width_and_height.md)
34
34
 
35
35
  For detailed descriptions and configuration options, [take a look at the complete list.](/docs/checks/)
data/config/default.yml CHANGED
@@ -110,7 +110,16 @@ HtmlParsingError:
110
110
  enabled: true
111
111
  ignore: []
112
112
 
113
+ TranslationKeyExists:
114
+ enabled: true
115
+ ignore: []
116
+
117
+ TranslationFilesMatch:
118
+ enabled: true
119
+ ignore: []
120
+
113
121
  ParseJsonFormat:
114
122
  enabled: false
115
123
  start_level: 0
116
124
  indent: ' '
125
+
@@ -0,0 +1,70 @@
1
+ # Translation Files Match (`TranslationFilesMatch`)
2
+
3
+ Checks if translation files for different language have the same keys.
4
+
5
+ ## Check Details
6
+
7
+ This check is aimed at avoiding inconsistences between translation files to avoid errors hard to spot and make maintenance easier.
8
+
9
+ :-1: Examples of **incorrect** code for this check:
10
+
11
+ ```yaml
12
+ # app/translations/en/item.yml
13
+ en:
14
+ app:
15
+ item:
16
+ title: "Item"
17
+ description: "Description"
18
+
19
+ # app/translations/de/item.yml
20
+ en:
21
+ app:
22
+ item:
23
+ description: "Beschreibung"
24
+ ```
25
+
26
+ Missing "title" in de/item.yml
27
+
28
+ :+1: Examples of **correct** code for this check:
29
+
30
+ ```yaml
31
+ # app/translations/en/item.yml
32
+ en:
33
+ app:
34
+ item:
35
+ title: "Item"
36
+ description: "Description"
37
+
38
+ # app/translations/de/item.yml
39
+ en:
40
+ app:
41
+ item:
42
+ title: "Artikel"
43
+ description: "Beschreibung"
44
+ ```
45
+
46
+ ## Check Options
47
+
48
+ The default configuration for this check is the following:
49
+
50
+ ```yaml
51
+ TranslationFilesMatch:
52
+ enabled: true
53
+ ```
54
+
55
+ ## When Not To Use It
56
+
57
+ There should be no cases where disabling this rule is needed. For keys that are set via UI, and hence should not be part of the codebase,
58
+ use proper configuration option in [app/config.yml](https://documentation.platformos.com/developer-guide/platformos-workflow/codebase/config)
59
+
60
+ ## Version
61
+
62
+ This check has been introduced in PlatformOS Check 0.4.10.
63
+
64
+ ## Resources
65
+
66
+ - [Rule Source][codesource]
67
+ - [Documentation Source][docsource]
68
+
69
+ [codesource]: /lib/platformos_check/checks/translation_files_match.rb
70
+ [docsource]: /docs/checks/translation_files_match.md
@@ -0,0 +1,44 @@
1
+ # Translation Key Exists (`TranslationKeyExists`)
2
+
3
+ Checks if translation key is defined in the default language
4
+
5
+ ## Check Details
6
+
7
+ This check is aimed at avoiding missing translation error.
8
+
9
+ :-1: Examples of **incorrect** code for this check:
10
+
11
+ ```liquid
12
+ {{ 'undefined.key' | t }}
13
+ ```
14
+
15
+ :+1: Examples of **correct** code for this check:
16
+
17
+ ```liquid
18
+ {{ 'defined.key' | t }}
19
+
20
+ ## Check Options
21
+
22
+ The default configuration for this check is the following:
23
+
24
+ ```yaml
25
+ TranslationKeyExists:
26
+ enabled: true
27
+ ```
28
+
29
+ ## When Not To Use It
30
+
31
+ There should be no cases where disabling this rule is needed. For keys that are set via UI, and hence should not be part of the codebase,
32
+ use proper configuration option in [app/config.yml](https://documentation.platformos.com/developer-guide/platformos-workflow/codebase/config)
33
+
34
+ ## Version
35
+
36
+ This check has been introduced in PlatformOS Check 0.4.10.
37
+
38
+ ## Resources
39
+
40
+ - [Rule Source][codesource]
41
+ - [Documentation Source][docsource]
42
+
43
+ [codesource]: /lib/platformos_check/checks/translation_key_exists.rb
44
+ [docsource]: /docs/checks/translation_key_exists.md
Binary file
@@ -39,6 +39,8 @@ module PlatformosCheck
39
39
  CONFIG_REGEX => ConfigFile
40
40
  }
41
41
 
42
+ DEFAULT_LANGUAGE = 'en'
43
+
42
44
  attr_reader :storage, :grouped_files
43
45
 
44
46
  def initialize(storage)
@@ -139,5 +141,16 @@ module PlatformosCheck
139
141
  all.find { |t| t.relative_path.to_s == name_or_relative_path }
140
142
  end
141
143
  end
144
+
145
+ def default_language
146
+ DEFAULT_LANGUAGE
147
+ end
148
+
149
+ def translations_hash
150
+ @translations_hash ||= translations.each_with_object({}) do |translation_file, hash|
151
+ hash.deep_merge!(translation_file.content)
152
+ hash
153
+ end
154
+ end
142
155
  end
143
156
  end
@@ -34,16 +34,19 @@ module PlatformosCheck
34
34
  n = remove_extension(relative_path.sub(dir_prefix, '')).to_s
35
35
  return n if module_name.nil?
36
36
 
37
- prefix = "modules#{File::SEPARATOR}#{module_name}#{File::SEPARATOR}"
38
- return n if n.start_with?(prefix)
37
+ return n if n.start_with?(module_prefix)
39
38
 
40
- "#{prefix}#{n}"
39
+ "#{module_prefix}#{n}"
41
40
  end
42
41
 
43
42
  def dir_prefix
44
43
  nil
45
44
  end
46
45
 
46
+ def module_prefix
47
+ @module_prefix ||= "modules#{File::SEPARATOR}#{module_name}#{File::SEPARATOR}"
48
+ end
49
+
47
50
  def module_name
48
51
  @module_name ||= begin
49
52
  dir_names = @relative_path.split(File::SEPARATOR).reject(&:empty?)
@@ -85,6 +88,10 @@ module PlatformosCheck
85
88
  false
86
89
  end
87
90
 
91
+ def translation?
92
+ false
93
+ end
94
+
88
95
  def ==(other)
89
96
  other.is_a?(self.class) && relative_path == other.relative_path
90
97
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ class TranslationFilesMatch < YamlCheck
5
+ severity :error
6
+ category :translation
7
+ doc docs_url(__FILE__)
8
+
9
+ PLURALIZATION_KEYS = Set.new(%w[zero one two few many other])
10
+
11
+ def on_file(file)
12
+ return unless file.translation?
13
+ return if file.parse_error
14
+ return add_offense_wrong_language_in_file(file) if file.language != file.language_from_path
15
+ return check_if_file_exists_for_all_other_languages(file) if file.language == @platformos_app.default_language
16
+
17
+ default_language_file = @platformos_app.grouped_files[PlatformosCheck::TranslationFile][file.name.sub(file.language, @platformos_app.default_language)]
18
+
19
+ return add_offense_missing_file(file) if default_language_file.nil?
20
+
21
+ add_offense_different_structure(file, default_language_file) unless same_structure?(default_language_file.content[@platformos_app.default_language], file.content[file.language])
22
+ end
23
+
24
+ protected
25
+
26
+ def add_offense_wrong_language_in_file(file)
27
+ add_offense("Mismatch detected - file inside #{file.language_from_path} directory defines translations for `#{file.language}`", app_file: file) do |_corrector|
28
+ file.update_contents(file.content[file.language_from_path] = file.content.delete(file.content[file.language]))
29
+ file.write
30
+ end
31
+ end
32
+
33
+ def add_offense_missing_file(file)
34
+ add_offense("Mismatch detected - missing `#{file.relative_path.to_s.sub(file.language, @platformos_app.default_language)}` to define translations the default language", app_file: file)
35
+ end
36
+
37
+ def check_if_file_exists_for_all_other_languages(file)
38
+ @platformos_app.translations_hash.each_key do |lang|
39
+ next if lang == @platformos_app.default_language
40
+
41
+ language_file = @platformos_app.grouped_files[PlatformosCheck::TranslationFile][file.name.sub(file.language, lang)]
42
+ add_offense_missing_translation_file(file, lang) if language_file.nil?
43
+ end
44
+ end
45
+
46
+ def add_offense_missing_translation_file(file, lang)
47
+ missing_file_path = file.relative_path.to_s.sub(file.language, lang)
48
+ add_offense("Mismatch detected - missing `#{missing_file_path}` file to define translations for `#{lang}`", app_file: file) do |corrector|
49
+ missing_file_content = file.content.clone
50
+ missing_file_content[lang] = missing_file_content.delete(file.language)
51
+ corrector.create_file(@platformos_app.storage, missing_file_path, YAML.dump(missing_file_content))
52
+ end
53
+ end
54
+
55
+ def same_structure?(hash1, hash2)
56
+ if !hash1.is_a?(Hash) && !hash2.is_a?(Hash)
57
+ true
58
+ elsif (hash1.is_a?(Hash) && !hash2.is_a?(Hash)) || (!hash1.is_a?(Hash) && hash2.is_a?(Hash))
59
+ false
60
+ elsif pluralization?(hash1) && pluralization?(hash2)
61
+ true
62
+ elsif hash1.keys.map(&:to_s).sort != hash2.keys.map(&:to_s).sort
63
+ false
64
+ else
65
+ hash1.keys.all? { |key| same_structure?(hash1[key], hash2[key]) }
66
+ end
67
+ end
68
+
69
+ def add_offense_different_structure(file, default_language_file)
70
+ add_offense("Mismatch detected - structure differs from the default language file #{default_language_file.relative_path}", app_file: file) do |_corrector|
71
+ file.content[file.language].transform_values! { |v| v.nil? ? {} : v }
72
+ file.content[file.language] = default_language_file.content[default_language_file.language].deep_merge(file.content[file.language])
73
+ file.write
74
+ end
75
+ end
76
+
77
+ def pluralization?(hash)
78
+ hash.all? do |key, value|
79
+ PLURALIZATION_KEYS.include?(key) && !value.is_a?(Hash)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ # Recommends using {% liquid ... %} if 5 or more consecutive {% ... %} are found.
5
+ class TranslationKeyExists < LiquidCheck
6
+ severity :error
7
+ categories :translation, :liquid
8
+ doc docs_url(__FILE__)
9
+
10
+ def on_variable(node)
11
+ return unless node.value.name.is_a?(String)
12
+ return unless node.filters.size == 1
13
+
14
+ translation_filter = node.filters.detect { |f| TranslationFile::TRANSLATION_FILTERS.include?(f[0]) }
15
+ return unless translation_filter
16
+ return unless translation_filter
17
+
18
+ filter_attributes = translation_filter[2] || {}
19
+
20
+ return unless filter_attributes['default'].nil?
21
+ return if !filter_attributes['scope'].nil? && !filter_attributes['scope'].is_a?(String)
22
+
23
+ lang = filter_attributes['language'].is_a?(String) ? filter_attributes['language'] : @platformos_app.default_language
24
+ translation_components = node.value.name.split('.')
25
+
26
+ translation_components = filter_attributes['scope'].split('.') + translation_components if filter_attributes['scope']
27
+
28
+ return add_translation_offense(node:, lang:) if @platformos_app.translations_hash.empty?
29
+
30
+ hash = @platformos_app.translations_hash[lang] || {}
31
+ index = 0
32
+ while translation_components[index]
33
+ hash = hash[translation_components[index]]
34
+ if hash.nil?
35
+ add_translation_offense(node:, lang:)
36
+ break
37
+ end
38
+ index += 1
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ def add_translation_offense(node:, lang:)
45
+ add_offense("Translation `#{lang}.#{node.value.name}` does not exists", node:)
46
+ end
47
+ end
48
+ end
@@ -64,7 +64,7 @@ module PlatformosCheck
64
64
  end
65
65
 
66
66
  def first_declaration(name)
67
- [all_assigns[name], all_captures[name]].compact.sort_by(&:line_number).first
67
+ [all_assigns[name], all_captures[name]].compact.min_by(&:line_number)
68
68
  end
69
69
  end
70
70
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ def deep_merge!(other_hash, &block)
5
+ merge!(other_hash) do |key, this_val, other_val|
6
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
7
+ this_val.deep_merge(other_val, &block)
8
+ elsif block
9
+ yield(key, this_val, other_val)
10
+ else
11
+ other_val
12
+ end
13
+ end
14
+ end
15
+
16
+ def deep_merge(other_hash, &)
17
+ dup.deep_merge!(other_hash, &)
18
+ end
19
+ end
@@ -32,13 +32,29 @@ module PlatformosCheck
32
32
  PARTIAL_GRAPHQL = partial_tag_with_result('graphql')
33
33
  PARTIAL_BACKGROUND = partial_tag_with_result('background')
34
34
 
35
+ TAGS_FOR_FILTERS = 'echo|print|log|hash_assign|assign'
36
+ TRANSLATION_FILTERS_NAMES = 'translate|t_escape|translate_escape|t[^\\w]'
37
+ OPTIONAL_SCOPE_ARGUMENT = %((:?([\\w:'"\\s]*)\\s*(scope:\\s*['"](?<scope>[^'"]*)['"]))?)
38
+
39
+ LOCALIZE_FILTERS_NAMES = ''
40
+
35
41
  ASSET_INCLUDE = /
36
42
  \{\{-?\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
37
43
  \{\{-?\s*"(?<partial>[^"]*)"\s*\|\s*asset_url|
38
44
 
39
45
  # in liquid tags the whole line is white space until the asset partial
40
- ^\s*(?:echo|assign[^=]*=)\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
41
- ^\s*(?:echo|assign[^=]*=)\s*"(?<partial>[^"]*)"\s*\|\s*asset_url
46
+ ^\s*(?:#{TAGS_FOR_FILTERS}[^=]*=)\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
47
+ ^\s*(?:#{TAGS_FOR_FILTERS}[^=]*=)\s*"(?<partial>[^"]*)"\s*\|\s*asset_url
48
+ /mix
49
+
50
+ TRANSLATION_FILTER = /
51
+ '(?<key>[^']*)'\s*\|\s*(#{TRANSLATION_FILTERS_NAMES})#{OPTIONAL_SCOPE_ARGUMENT}|
52
+ "(?<key>[^"]*)"\s*\|\s*(#{TRANSLATION_FILTERS_NAMES})#{OPTIONAL_SCOPE_ARGUMENT}
53
+ /mix
54
+
55
+ LOCALIZE_FILTER = /
56
+ [\s\w'"-:.]+\|\s*(localize|l):\s*'(?<key>[^']*)'|
57
+ [\s\w'"-:.]+\|\s*(localize|l):\s*"(?<key>[^"]*)"
42
58
  /mix
43
59
  end
44
60
  end
@@ -7,6 +7,16 @@ module PlatformosCheck
7
7
  include PositionHelper
8
8
  include URIHelper
9
9
 
10
+ class DefaultTranslationFile
11
+ def initialize(default_language)
12
+ @default_language = default_language
13
+ end
14
+
15
+ def relative_path
16
+ Pathname.new(@default_language, "#{@default_language}.yml")
17
+ end
18
+ end
19
+
10
20
  class << self
11
21
  attr_accessor :partial_regexp, :app_file_type, :default_dir, :default_extension
12
22
 
@@ -41,18 +51,12 @@ module PlatformosCheck
41
51
 
42
52
  def document_links(buffer, platformos_app)
43
53
  matches(buffer, partial_regexp).map do |match|
44
- start_row, start_column = from_index_to_row_column(
45
- buffer,
46
- match.begin(:partial)
47
- )
54
+ start_row, start_column = start_coordinates(buffer, match)
48
55
 
49
- end_row, end_column = from_index_to_row_column(
50
- buffer,
51
- match.end(:partial)
52
- )
56
+ end_row, end_column = end_coordinates(buffer, match)
53
57
 
54
58
  {
55
- target: file_link(match[:partial], platformos_app),
59
+ target: file_link(match, platformos_app),
56
60
  range: {
57
61
  start: {
58
62
  line: start_row,
@@ -67,13 +71,66 @@ module PlatformosCheck
67
71
  end
68
72
  end
69
73
 
70
- def file_link(partial, platformos_app)
74
+ def start_coordinates(buffer, match)
75
+ from_index_to_row_column(
76
+ buffer,
77
+ match.begin(:partial)
78
+ )
79
+ end
80
+
81
+ def end_coordinates(buffer, match)
82
+ from_index_to_row_column(
83
+ buffer,
84
+ match.end(:partial)
85
+ )
86
+ end
87
+
88
+ def file_link(match, platformos_app)
89
+ partial = match[:partial]
71
90
  relative_path = platformos_app.send(app_file_type).detect { |f| f.name == partial }&.relative_path
72
91
  relative_path ||= default_relative_path(partial)
73
92
 
74
93
  file_uri(@storage.path(relative_path))
75
94
  end
76
95
 
96
+ def translation_file_link(match, platformos_app)
97
+ @current_best_fit = platformos_app.translations.first || DefaultTranslationFile.new(platformos_app.default_language)
98
+ @current_best_fit_level = 0
99
+ array_of_translation_components = translation_components_for_match(match)
100
+ platformos_app.translations.each do |translation_file|
101
+ array_of_translation_components.each do |translation_components|
102
+ exact_match_level = translation_components.size
103
+ component_result = translation_file.content[platformos_app.default_language]
104
+ next if component_result.nil?
105
+
106
+ i = 0
107
+ while i < exact_match_level
108
+ component_result = yaml(component_result, translation_components[i])
109
+
110
+ break if component_result.nil?
111
+
112
+ i += 1
113
+ if i > @current_best_fit_level
114
+ @current_best_fit = translation_file
115
+ @current_best_fit_level = i
116
+ end
117
+
118
+ break unless component_result.is_a?(Hash)
119
+ end
120
+ end
121
+ end
122
+
123
+ file_uri(@storage.path(@current_best_fit&.relative_path))
124
+ end
125
+
126
+ def translation_components_for_match(match)
127
+ raise NotImplementedError
128
+ end
129
+
130
+ def yaml(component_result, component)
131
+ component_result[component]
132
+ end
133
+
77
134
  def default_relative_path(partial)
78
135
  return Pathname.new("app/#{default_dir}/#{partial}#{default_extension}") unless partial.start_with?('modules/')
79
136
 
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module LanguageServer
5
+ class LocalizeDocumentLinkProvider < DocumentLinkProvider
6
+ @partial_regexp = LOCALIZE_FILTER
7
+ @app_file_type = :translations
8
+ @default_dir = 'translations'
9
+ @default_extension = '.yml'
10
+
11
+ def file_link(match, platformos_app)
12
+ translation_file_link(match, platformos_app)
13
+ end
14
+
15
+ def translation_components_for_match(match)
16
+ key = match[:key].split('.')
17
+ [
18
+ %w[time formats] + key,
19
+ %w[date formats] + key
20
+ ]
21
+ end
22
+
23
+ def start_coordinates(buffer, match)
24
+ from_index_to_row_column(
25
+ buffer,
26
+ match.begin(:key)
27
+ )
28
+ end
29
+
30
+ def end_coordinates(buffer, match)
31
+ from_index_to_row_column(
32
+ buffer,
33
+ match.end(:key)
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -8,7 +8,8 @@ module PlatformosCheck
8
8
  @default_dir = 'views/partials'
9
9
  @default_extension = '.liquid'
10
10
 
11
- def file_link(partial, platformos_app)
11
+ def file_link(match, platformos_app)
12
+ partial = match[:partial]
12
13
  relative_path = nil
13
14
  path_prefixes(platformos_app).each do |prefix|
14
15
  prefix ||= ''
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module LanguageServer
5
+ class TranslationDocumentLinkProvider < DocumentLinkProvider
6
+ @partial_regexp = TRANSLATION_FILTER
7
+ @app_file_type = :translations
8
+ @default_dir = 'translations'
9
+ @default_extension = '.yml'
10
+
11
+ def file_link(match, platformos_app)
12
+ translation_file_link(match, platformos_app)
13
+ end
14
+
15
+ def translation_components_for_match(match)
16
+ translation_components = match[:key].split('.')
17
+ translation_components = match[:scope].split('.') + translation_components if match[:scope]
18
+ [translation_components]
19
+ end
20
+
21
+ def start_coordinates(buffer, match)
22
+ from_index_to_row_column(
23
+ buffer,
24
+ match.begin(:key)
25
+ )
26
+ end
27
+
28
+ def end_coordinates(buffer, match)
29
+ from_index_to_row_column(
30
+ buffer,
31
+ match.end(:key)
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
@@ -2,5 +2,45 @@
2
2
 
3
3
  module PlatformosCheck
4
4
  class TranslationFile < YamlFile
5
+ DIR_PREFIX = %r{\A/?((marketplace_builder|app)/(translations)/|modules/((\w|-)*)/(private|public)/(translations)/)}
6
+ TRANSLATION_FILTERS = Set.new(%w[t t_escape translate translate_escape]).freeze
7
+ attr_reader :language
8
+
9
+ def load!
10
+ before_load
11
+ super
12
+ after_load
13
+ end
14
+
15
+ def before_load
16
+ @storage.platformos_app.instance_variable_set(:@translations_hash, nil) unless @loaded
17
+ end
18
+
19
+ def after_load
20
+ @language = @content&.keys&.first
21
+ return if module_name.nil?
22
+
23
+ @content[@language].transform_keys! { |key| key.start_with?(module_prefix) ? key : "#{module_prefix}#{key}" }
24
+ end
25
+
26
+ def language_from_path
27
+ @language_from_path ||= name.sub(module_prefix, '').split(File::SEPARATOR).first
28
+ end
29
+
30
+ def update_contents(new_content = {})
31
+ before_load
32
+ super(new_content)
33
+ @loaded = true
34
+
35
+ after_load
36
+ end
37
+
38
+ def dir_prefix
39
+ DIR_PREFIX
40
+ end
41
+
42
+ def translation?
43
+ true
44
+ end
5
45
  end
6
46
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PlatformosCheck
4
- VERSION = "0.4.9"
4
+ VERSION = "0.4.10"
5
5
  end
@@ -28,14 +28,19 @@ module PlatformosCheck
28
28
  end
29
29
 
30
30
  def write
31
+ pretty = content_to_string
32
+ @storage.write(@relative_path, pretty)
33
+ @source = pretty
34
+ end
35
+
36
+ def content_to_string
31
37
  pretty = YAML.dump(@content)
32
38
  return unless source.rstrip != pretty.rstrip
33
39
 
34
40
  # Most editors add a trailing \n at the end of files. Here we
35
41
  # try to maintain the convention.
36
42
  eof = source.end_with?("\n") ? "\n" : ""
37
- @storage.write(@relative_path, pretty.gsub("\n", @eol) + eof)
38
- @source = pretty
43
+ pretty.gsub("\n", @eol) + eof
39
44
  end
40
45
 
41
46
  def yaml?
@@ -5,6 +5,7 @@ require "liquid"
5
5
  require_relative "platformos_check/version"
6
6
  require_relative "platformos_check/bug"
7
7
  require_relative "platformos_check/exceptions"
8
+ require_relative "platformos_check/ext/hash"
8
9
  require_relative "platformos_check/json_helper"
9
10
  require_relative "platformos_check/app_file_rewriter"
10
11
  require_relative "platformos_check/app_file"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: platformos-check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.9
4
+ version: 0.4.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Bliszczyk
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2024-01-10 00:00:00.000000000 Z
13
+ date: 2024-02-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: graphql
@@ -134,6 +134,8 @@ files:
134
134
  - docs/checks/space_inside_braces.md
135
135
  - docs/checks/syntax_error.md
136
136
  - docs/checks/template_length.md
137
+ - docs/checks/translation_files_match.md
138
+ - docs/checks/translation_key_exists.md
137
139
  - docs/checks/undefined_object.md
138
140
  - docs/checks/unknown_filter.md
139
141
  - docs/checks/unreachable_code.md
@@ -148,7 +150,7 @@ files:
148
150
  - docs/language_server/code-action-problem.png
149
151
  - docs/language_server/code-action-quickfix.png
150
152
  - docs/language_server/how_to_correct_code_with_code_actions_and_execute_command.md
151
- - docs/preview.png
153
+ - docs/platformos-check.jpg
152
154
  - exe/platformos-check
153
155
  - exe/platformos-check-language-server
154
156
  - lib/platformos_check.rb
@@ -181,6 +183,8 @@ files:
181
183
  - lib/platformos_check/checks/space_inside_braces.rb
182
184
  - lib/platformos_check/checks/syntax_error.rb
183
185
  - lib/platformos_check/checks/template_length.rb
186
+ - lib/platformos_check/checks/translation_files_match.rb
187
+ - lib/platformos_check/checks/translation_key_exists.rb
184
188
  - lib/platformos_check/checks/undefined_object.rb
185
189
  - lib/platformos_check/checks/unknown_filter.rb
186
190
  - lib/platformos_check/checks/unreachable_code.rb
@@ -196,6 +200,7 @@ files:
196
200
  - lib/platformos_check/disabled_checks.rb
197
201
  - lib/platformos_check/email_file.rb
198
202
  - lib/platformos_check/exceptions.rb
203
+ - lib/platformos_check/ext/hash.rb
199
204
  - lib/platformos_check/file_system_storage.rb
200
205
  - lib/platformos_check/form_file.rb
201
206
  - lib/platformos_check/graphql_file.rb
@@ -244,8 +249,10 @@ files:
244
249
  - lib/platformos_check/language_server/document_link_providers/graphql_document_link_provider.rb
245
250
  - lib/platformos_check/language_server/document_link_providers/include_document_link_provider.rb
246
251
  - lib/platformos_check/language_server/document_link_providers/include_form_document_link_provider.rb
252
+ - lib/platformos_check/language_server/document_link_providers/localize_document_link_provider.rb
247
253
  - lib/platformos_check/language_server/document_link_providers/render_document_link_provider.rb
248
254
  - lib/platformos_check/language_server/document_link_providers/theme_render_document_link_provider.rb
255
+ - lib/platformos_check/language_server/document_link_providers/translation_document_link_provider.rb
249
256
  - lib/platformos_check/language_server/execute_command_engine.rb
250
257
  - lib/platformos_check/language_server/execute_command_provider.rb
251
258
  - lib/platformos_check/language_server/execute_command_providers/correction_execute_command_provider.rb
@@ -366,7 +373,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
366
373
  - !ruby/object:Gem::Version
367
374
  version: '0'
368
375
  requirements: []
369
- rubygems_version: 3.4.22
376
+ rubygems_version: 3.5.6
370
377
  signing_key:
371
378
  specification_version: 4
372
379
  summary: A platformOS App Linter
data/docs/preview.png DELETED
Binary file