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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +18 -18
- data/config/default.yml +9 -0
- data/docs/checks/translation_files_match.md +70 -0
- data/docs/checks/translation_key_exists.md +44 -0
- data/docs/platformos-check.jpg +0 -0
- data/lib/platformos_check/app.rb +13 -0
- data/lib/platformos_check/app_file.rb +10 -3
- data/lib/platformos_check/checks/translation_files_match.rb +83 -0
- data/lib/platformos_check/checks/translation_key_exists.rb +48 -0
- data/lib/platformos_check/checks/undefined_object.rb +1 -1
- data/lib/platformos_check/ext/hash.rb +19 -0
- data/lib/platformos_check/language_server/constants.rb +18 -2
- data/lib/platformos_check/language_server/document_link_provider.rb +67 -10
- data/lib/platformos_check/language_server/document_link_providers/localize_document_link_provider.rb +38 -0
- data/lib/platformos_check/language_server/document_link_providers/theme_render_document_link_provider.rb +2 -1
- data/lib/platformos_check/language_server/document_link_providers/translation_document_link_provider.rb +36 -0
- data/lib/platformos_check/translation_file.rb +40 -0
- data/lib/platformos_check/version.rb +1 -1
- data/lib/platformos_check/yaml_file.rb +7 -2
- data/lib/platformos_check.rb +1 -0
- metadata +11 -4
- data/docs/preview.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 269921738309c4cc1a1d7afef16318af6a75f050fad261530d008f53c464ce62
|
4
|
+
data.tar.gz: 0a3c1b2704f40a8244a4608ff6d9f26b0cb7757f7e93182e875edb55344c6fd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/platformos_check/app.rb
CHANGED
@@ -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
|
-
|
38
|
-
return n if n.start_with?(prefix)
|
37
|
+
return n if n.start_with?(module_prefix)
|
39
38
|
|
40
|
-
"#{
|
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
|
@@ -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*(
|
41
|
-
^\s*(
|
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 =
|
45
|
-
buffer,
|
46
|
-
match.begin(:partial)
|
47
|
-
)
|
54
|
+
start_row, start_column = start_coordinates(buffer, match)
|
48
55
|
|
49
|
-
end_row, end_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
|
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
|
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
|
|
data/lib/platformos_check/language_server/document_link_providers/localize_document_link_provider.rb
ADDED
@@ -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(
|
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
|
@@ -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
|
-
|
38
|
-
@source = pretty
|
43
|
+
pretty.gsub("\n", @eol) + eof
|
39
44
|
end
|
40
45
|
|
41
46
|
def yaml?
|
data/lib/platformos_check.rb
CHANGED
@@ -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.
|
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-
|
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/
|
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.
|
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
|