platformos-check 0.4.9 → 0.4.10

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
  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