danger-localizationlinter 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +167 -0
  6. data/Guardfile +21 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +62 -0
  9. data/Rakefile +25 -0
  10. data/danger-localizationlinter.gemspec +60 -0
  11. data/ext/localizationlinter/helper/validate_wording_cli.rb +21 -0
  12. data/ext/localizationlinter/localizationlinter.rb +331 -0
  13. data/ext/localizationlinter/swiftgen_localizables_check.stencil +36 -0
  14. data/lib/danger_localizationlinter.rb +3 -0
  15. data/lib/danger_plugin.rb +3 -0
  16. data/lib/localizationlinter/gem_version.rb +5 -0
  17. data/lib/localizationlinter/plugin.rb +96 -0
  18. data/spec/Test/Podfile +6 -0
  19. data/spec/Test/Podfile.lock +16 -0
  20. data/spec/Test/Pods/SwiftGen/CHANGELOG.md +1357 -0
  21. data/spec/Test/Pods/SwiftGen/LICENCE +21 -0
  22. data/spec/Test/Pods/SwiftGen/README.md +892 -0
  23. data/spec/Test/Pods/SwiftGen/bin/swiftgen +0 -0
  24. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Commander +0 -0
  25. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Resources/Info.plist +48 -0
  26. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Versions/A/Commander +0 -0
  27. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Versions/A/Resources/Info.plist +48 -0
  28. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Versions/A/_CodeSignature/CodeResources +132 -0
  29. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Versions/Current/Commander +0 -0
  30. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Versions/Current/Resources/Info.plist +48 -0
  31. data/spec/Test/Pods/SwiftGen/lib/Commander.framework/Versions/Current/_CodeSignature/CodeResources +132 -0
  32. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Kanna +0 -0
  33. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Resources/Info.plist +48 -0
  34. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Versions/A/Kanna +0 -0
  35. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Versions/A/Resources/Info.plist +48 -0
  36. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Versions/A/_CodeSignature/CodeResources +132 -0
  37. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Versions/Current/Kanna +0 -0
  38. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Versions/Current/Resources/Info.plist +48 -0
  39. data/spec/Test/Pods/SwiftGen/lib/Kanna.framework/Versions/Current/_CodeSignature/CodeResources +132 -0
  40. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/PathKit +0 -0
  41. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/Resources/Info.plist +48 -0
  42. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/Versions/A/PathKit +0 -0
  43. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/Versions/A/Resources/Info.plist +48 -0
  44. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/Versions/A/_CodeSignature/CodeResources +132 -0
  45. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/Versions/Current/PathKit +0 -0
  46. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/Versions/Current/Resources/Info.plist +48 -0
  47. data/spec/Test/Pods/SwiftGen/lib/PathKit.framework/Versions/Current/_CodeSignature/CodeResources +132 -0
  48. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Resources/Info.plist +48 -0
  49. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Stencil +0 -0
  50. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Versions/A/Resources/Info.plist +48 -0
  51. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Versions/A/Stencil +0 -0
  52. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Versions/A/_CodeSignature/CodeResources +132 -0
  53. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Versions/Current/Resources/Info.plist +48 -0
  54. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Versions/Current/Stencil +0 -0
  55. data/spec/Test/Pods/SwiftGen/lib/Stencil.framework/Versions/Current/_CodeSignature/CodeResources +132 -0
  56. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/Resources/Info.plist +48 -0
  57. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/StencilSwiftKit +0 -0
  58. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/Versions/A/Resources/Info.plist +48 -0
  59. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/Versions/A/StencilSwiftKit +0 -0
  60. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/Versions/A/_CodeSignature/CodeResources +132 -0
  61. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/Versions/Current/Resources/Info.plist +48 -0
  62. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/Versions/Current/StencilSwiftKit +0 -0
  63. data/spec/Test/Pods/SwiftGen/lib/StencilSwiftKit.framework/Versions/Current/_CodeSignature/CodeResources +132 -0
  64. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/Resources/Info.plist +48 -0
  65. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/SwiftGenKit +0 -0
  66. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/Versions/A/Resources/Info.plist +48 -0
  67. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/Versions/A/SwiftGenKit +0 -0
  68. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/Versions/A/_CodeSignature/CodeResources +132 -0
  69. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/Versions/Current/Resources/Info.plist +48 -0
  70. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/Versions/Current/SwiftGenKit +0 -0
  71. data/spec/Test/Pods/SwiftGen/lib/SwiftGenKit.framework/Versions/Current/_CodeSignature/CodeResources +132 -0
  72. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Resources/Info.plist +48 -0
  73. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Versions/A/Resources/Info.plist +48 -0
  74. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Versions/A/Yams +0 -0
  75. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Versions/A/_CodeSignature/CodeResources +132 -0
  76. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Versions/Current/Resources/Info.plist +48 -0
  77. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Versions/Current/Yams +0 -0
  78. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Versions/Current/_CodeSignature/CodeResources +132 -0
  79. data/spec/Test/Pods/SwiftGen/lib/Yams.framework/Yams +0 -0
  80. data/spec/Test/Pods/SwiftGen/templates/colors/literals-swift4.stencil +43 -0
  81. data/spec/Test/Pods/SwiftGen/templates/colors/literals-swift5.stencil +43 -0
  82. data/spec/Test/Pods/SwiftGen/templates/colors/swift4.stencil +84 -0
  83. data/spec/Test/Pods/SwiftGen/templates/colors/swift5.stencil +84 -0
  84. data/spec/Test/Pods/SwiftGen/templates/coredata/swift4.stencil +211 -0
  85. data/spec/Test/Pods/SwiftGen/templates/coredata/swift5.stencil +211 -0
  86. data/spec/Test/Pods/SwiftGen/templates/fonts/swift4.stencil +110 -0
  87. data/spec/Test/Pods/SwiftGen/templates/fonts/swift5.stencil +113 -0
  88. data/spec/Test/Pods/SwiftGen/templates/ib/scenes-swift4.stencil +157 -0
  89. data/spec/Test/Pods/SwiftGen/templates/ib/scenes-swift5.stencil +159 -0
  90. data/spec/Test/Pods/SwiftGen/templates/ib/segues-swift4.stencil +60 -0
  91. data/spec/Test/Pods/SwiftGen/templates/ib/segues-swift5.stencil +60 -0
  92. data/spec/Test/Pods/SwiftGen/templates/json/inline-swift4.stencil +82 -0
  93. data/spec/Test/Pods/SwiftGen/templates/json/inline-swift5.stencil +82 -0
  94. data/spec/Test/Pods/SwiftGen/templates/json/runtime-swift4.stencil +112 -0
  95. data/spec/Test/Pods/SwiftGen/templates/json/runtime-swift5.stencil +112 -0
  96. data/spec/Test/Pods/SwiftGen/templates/plist/inline-swift4.stencil +82 -0
  97. data/spec/Test/Pods/SwiftGen/templates/plist/inline-swift5.stencil +82 -0
  98. data/spec/Test/Pods/SwiftGen/templates/plist/runtime-swift4.stencil +117 -0
  99. data/spec/Test/Pods/SwiftGen/templates/plist/runtime-swift5.stencil +117 -0
  100. data/spec/Test/Pods/SwiftGen/templates/strings/flat-swift4.stencil +97 -0
  101. data/spec/Test/Pods/SwiftGen/templates/strings/flat-swift5.stencil +97 -0
  102. data/spec/Test/Pods/SwiftGen/templates/strings/objc-h.stencil +66 -0
  103. data/spec/Test/Pods/SwiftGen/templates/strings/objc-m.stencil +90 -0
  104. data/spec/Test/Pods/SwiftGen/templates/strings/structured-swift4.stencil +102 -0
  105. data/spec/Test/Pods/SwiftGen/templates/strings/structured-swift5.stencil +102 -0
  106. data/spec/Test/Pods/SwiftGen/templates/xcassets/swift4.stencil +266 -0
  107. data/spec/Test/Pods/SwiftGen/templates/xcassets/swift5.stencil +274 -0
  108. data/spec/Test/Pods/SwiftGen/templates/yaml/inline-swift4.stencil +92 -0
  109. data/spec/Test/Pods/SwiftGen/templates/yaml/inline-swift5.stencil +92 -0
  110. data/spec/Test/Test.xcodeproj/project.pbxproj +406 -0
  111. data/spec/Test/Test.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  112. data/spec/Test/Test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  113. data/spec/Test/Test.xcworkspace/contents.xcworkspacedata +10 -0
  114. data/spec/Test/Test.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  115. data/spec/Test/Test/AppDelegate.swift +36 -0
  116. data/spec/Test/Test/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  117. data/spec/Test/Test/Assets.xcassets/AppIcon.appiconset/Contents.json +98 -0
  118. data/spec/Test/Test/Assets.xcassets/Contents.json +6 -0
  119. data/spec/Test/Test/ContentView.swift +21 -0
  120. data/spec/Test/Test/Info.plist +62 -0
  121. data/spec/Test/Test/SceneDelegate.swift +63 -0
  122. data/spec/Test/Test/all_target_main_language.yml +1 -0
  123. data/spec/Test/Test/en.lproj/LaunchScreen.storyboard +25 -0
  124. data/spec/Test/Test/en.lproj/localizable.strings +3 -0
  125. data/spec/Test/Test/fr.lproj/LaunchScreen.storyboard +25 -0
  126. data/spec/Test/Test/fr.lproj/localizable.strings +3 -0
  127. data/spec/localizationofficer_spec.rb +116 -0
  128. data/spec/spec_helper.rb +67 -0
  129. data/spec/support/fixtures/all_target_main_language.yml +1 -0
  130. data/spec/support/fixtures/resources/TEST/en.lproj/Localizable.strings +8 -0
  131. data/spec/support/fixtures/resources/TEST/en.lproj/Localizable.stringsdict +30 -0
  132. data/spec/support/fixtures/resources/TEST_KO/en.lproj/Localizable.strings +11 -0
  133. data/spec/support/fixtures/resources/TEST_KO/en.lproj/Localizable.stringsdict +30 -0
  134. data/spec/support/fixtures/resources/TEST_KO/fr.lproj/Localizable.strings +11 -0
  135. data/spec/support/fixtures/resources/TEST_KO/fr.lproj/Localizable.stringsdict +30 -0
  136. metadata +491 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0a4afdaf597ba934bea40d09e3b0fd3648a224466512c2c24b676093cce3ee40
4
+ data.tar.gz: 4c69e69f4e56d1eaf50933469c97c18522b5d4db9347a1a3551144b75eeb4fe1
5
+ SHA512:
6
+ metadata.gz: 2a9728aa8b54a0120d00986eaa34321beb6109228f948cdff0c0c558465c7a7e0263811293c35a35cac3b302d727362887370dea1c0ec1ee5e055d17d0da48b0
7
+ data.tar.gz: ba9a05927c1ef36c54386dae856b16099cd202b9052c4319d075fb2744ff1a5f563f4f12f9216c098a253e2bf8764128e17f579bb7e3911ddca66d383c56af5b
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ .DS_Store
2
+ pkg
3
+ .idea/
4
+ .yardoc
5
+
6
+ ## Various iOS settings
7
+ *.pbxuser
8
+ !default.pbxuser
9
+ *.mode1v3
10
+ !default.mode1v3
11
+ *.mode2v3
12
+ !default.mode2v3
13
+ *.perspectivev3
14
+ !default.perspectivev3
15
+ xcuserdata
16
+ spec/Test/Pods/*
17
+ !spec/Test/Pods/SwiftGen/
18
+
19
+ ## Derived data
20
+ spec/support/fixtures/derived_data
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ cache:
3
+ directories:
4
+ - bundle
5
+
6
+ rvm:
7
+ - 2.0
8
+ - 2.1.3
9
+ - 2.3.1
10
+
11
+ script:
12
+ - bundle exec rake spec
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in danger-localizationlinter.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,167 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ danger-localizationlinter (0.0.1)
5
+ danger-plugin-api (~> 1.0)
6
+ json-diff (~> 0.4)
7
+ xcodeproj (~> 1.19)
8
+ yaml (~> 0.1)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ CFPropertyList (3.0.3)
14
+ addressable (2.7.0)
15
+ public_suffix (>= 2.0.2, < 5.0)
16
+ ast (2.4.2)
17
+ atomos (0.1.3)
18
+ claide (1.0.3)
19
+ claide-plugins (0.9.2)
20
+ cork
21
+ nap
22
+ open4 (~> 1.3)
23
+ coderay (1.1.3)
24
+ colored2 (3.1.2)
25
+ cork (0.3.0)
26
+ colored2 (~> 3.1)
27
+ danger (8.2.3)
28
+ claide (~> 1.0)
29
+ claide-plugins (>= 0.9.2)
30
+ colored2 (~> 3.1)
31
+ cork (~> 0.1)
32
+ faraday (>= 0.9.0, < 2.0)
33
+ faraday-http-cache (~> 2.0)
34
+ git (~> 1.7)
35
+ kramdown (~> 2.3)
36
+ kramdown-parser-gfm (~> 1.0)
37
+ no_proxy_fix
38
+ octokit (~> 4.7)
39
+ terminal-table (>= 1, < 4)
40
+ danger-plugin-api (1.0.0)
41
+ danger (> 2.0)
42
+ diff-lcs (1.4.4)
43
+ faraday (1.4.1)
44
+ faraday-excon (~> 1.1)
45
+ faraday-net_http (~> 1.0)
46
+ faraday-net_http_persistent (~> 1.1)
47
+ multipart-post (>= 1.2, < 3)
48
+ ruby2_keywords (>= 0.0.4)
49
+ faraday-excon (1.1.0)
50
+ faraday-http-cache (2.2.0)
51
+ faraday (>= 0.8)
52
+ faraday-net_http (1.0.1)
53
+ faraday-net_http_persistent (1.1.0)
54
+ ffi (1.15.0)
55
+ formatador (0.2.5)
56
+ git (1.8.1)
57
+ rchardet (~> 1.8)
58
+ guard (2.16.2)
59
+ formatador (>= 0.2.4)
60
+ listen (>= 2.7, < 4.0)
61
+ lumberjack (>= 1.0.12, < 2.0)
62
+ nenv (~> 0.1)
63
+ notiffany (~> 0.0)
64
+ pry (>= 0.9.12)
65
+ shellany (~> 0.0)
66
+ thor (>= 0.18.1)
67
+ guard-compat (1.2.1)
68
+ guard-rspec (4.7.3)
69
+ guard (~> 2.1)
70
+ guard-compat (~> 1.1)
71
+ rspec (>= 2.99.0, < 4.0)
72
+ json-diff (0.4.1)
73
+ kramdown (2.3.1)
74
+ rexml
75
+ kramdown-parser-gfm (1.1.0)
76
+ kramdown (~> 2.0)
77
+ listen (3.0.7)
78
+ rb-fsevent (>= 0.9.3)
79
+ rb-inotify (>= 0.9.7)
80
+ lumberjack (1.2.8)
81
+ method_source (1.0.0)
82
+ multipart-post (2.1.1)
83
+ nanaimo (0.3.0)
84
+ nap (1.1.0)
85
+ nenv (0.3.0)
86
+ no_proxy_fix (0.1.2)
87
+ notiffany (0.1.3)
88
+ nenv (~> 0.1)
89
+ shellany (~> 0.0)
90
+ octokit (4.21.0)
91
+ faraday (>= 0.9)
92
+ sawyer (~> 0.8.0, >= 0.5.3)
93
+ open4 (1.3.4)
94
+ parallel (1.20.1)
95
+ parser (3.0.0.0)
96
+ ast (~> 2.4.1)
97
+ pry (0.14.0)
98
+ coderay (~> 1.1)
99
+ method_source (~> 1.0)
100
+ public_suffix (4.0.6)
101
+ rainbow (3.0.0)
102
+ rake (10.5.0)
103
+ rb-fsevent (0.10.4)
104
+ rb-inotify (0.10.1)
105
+ ffi (~> 1.0)
106
+ rchardet (1.8.0)
107
+ regexp_parser (2.1.1)
108
+ rexml (3.2.4)
109
+ rspec (3.10.0)
110
+ rspec-core (~> 3.10.0)
111
+ rspec-expectations (~> 3.10.0)
112
+ rspec-mocks (~> 3.10.0)
113
+ rspec-core (3.10.1)
114
+ rspec-support (~> 3.10.0)
115
+ rspec-expectations (3.10.1)
116
+ diff-lcs (>= 1.2.0, < 2.0)
117
+ rspec-support (~> 3.10.0)
118
+ rspec-mocks (3.10.2)
119
+ diff-lcs (>= 1.2.0, < 2.0)
120
+ rspec-support (~> 3.10.0)
121
+ rspec-support (3.10.2)
122
+ rubocop (1.12.0)
123
+ parallel (~> 1.10)
124
+ parser (>= 3.0.0.0)
125
+ rainbow (>= 2.2.2, < 4.0)
126
+ regexp_parser (>= 1.8, < 3.0)
127
+ rexml
128
+ rubocop-ast (>= 1.2.0, < 2.0)
129
+ ruby-progressbar (~> 1.7)
130
+ unicode-display_width (>= 1.4.0, < 3.0)
131
+ rubocop-ast (1.4.1)
132
+ parser (>= 2.7.1.5)
133
+ ruby-progressbar (1.11.0)
134
+ ruby2_keywords (0.0.4)
135
+ sawyer (0.8.2)
136
+ addressable (>= 2.3.5)
137
+ faraday (> 0.8, < 2.0)
138
+ shellany (0.0.1)
139
+ terminal-table (1.6.0)
140
+ thor (1.1.0)
141
+ unicode-display_width (2.0.0)
142
+ xcodeproj (1.19.0)
143
+ CFPropertyList (>= 2.3.3, < 4.0)
144
+ atomos (~> 0.1.3)
145
+ claide (>= 1.0.2, < 2.0)
146
+ colored2 (~> 3.1)
147
+ nanaimo (~> 0.3.0)
148
+ yaml (0.1.1)
149
+ yard (0.9.26)
150
+
151
+ PLATFORMS
152
+ ruby
153
+
154
+ DEPENDENCIES
155
+ bundler (~> 2.0)
156
+ danger-localizationlinter!
157
+ guard (~> 2.14)
158
+ guard-rspec (~> 4.7)
159
+ listen (= 3.0.7)
160
+ pry
161
+ rake (~> 10.0)
162
+ rspec (~> 3.4)
163
+ rubocop
164
+ yard
165
+
166
+ BUNDLED WITH
167
+ 2.1.4
data/Guardfile ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A guardfile for making Danger Plugins
4
+ # For more info see https://github.com/guard/guard#readme
5
+
6
+ # To run, use `bundle exec guard`.
7
+
8
+ guard :rspec, cmd: 'bundle exec rspec' do
9
+ require 'guard/rspec/dsl'
10
+ dsl = Guard::RSpec::Dsl.new(self)
11
+
12
+ # RSpec files
13
+ rspec = dsl.rspec
14
+ watch(rspec.spec_helper) { rspec.spec_dir }
15
+ watch(rspec.spec_support) { rspec.spec_dir }
16
+ watch(rspec.spec_files)
17
+
18
+ # Ruby files
19
+ ruby = dsl.ruby
20
+ dsl.watch_spec_files_for(ruby.lib_files)
21
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2021 Benjamin Lavialle <benjamin.lavialle@fabernovel.com>
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # danger-localizationlinter
2
+
3
+ Verify consistency over your project localizable files (localizable.strings and Localizable.stringdict)
4
+ It uses Swiftgen to generate a localizables output per target and language and compare the output with
5
+ a reference language one.
6
+ The comparison ensure:
7
+ - missing keys
8
+ - additional keys
9
+ - wrong parameters in string format
10
+
11
+ ## Installation
12
+
13
+ $ gem install danger-localizationlinter
14
+
15
+ ## Usage
16
+
17
+ Methods and attributes from this plugin are available in your `Dangerfile` under the `localizationlinter` namespace.
18
+
19
+ localizationlinter.lint
20
+
21
+ #### Attributes
22
+
23
+ `clean_after_execution` - Keep or clean derived data after execution
24
+
25
+ `inline_mode` - Print inline or as markdown
26
+
27
+ `verbose` - Print execution logs
28
+
29
+ `language_configuration_path` - Path to language configuration yml, defining main language per target
30
+ example:
31
+ My_target: en
32
+ My_other_target: fr
33
+
34
+ default value is Configuration/Defaults/all_target_main_language.yml
35
+
36
+ `swift_gen_path` - Path to swift gen, default value is Pods/SwiftGen/bin/swiftgen
37
+
38
+ `derived_data_path` - Path to derived data, default value is localization_linter_derived_data
39
+
40
+ `resource_path` - Path to derived data, default value is Resources
41
+
42
+ `project_path` - Path to xcode project, default is taking the first project in root folder
43
+
44
+ #### Methods
45
+
46
+ `lint` - Verify the localizable files integrity over the project
47
+ rubocop:disable Metrics/MethodLength
48
+
49
+ `linter` - Returns the object in charge of verification
50
+
51
+ ## Development
52
+
53
+ 1. Clone this repo
54
+ 2. Run `bundle install` to setup dependencies.
55
+ 3. Run `bundle exec rake spec` to run the tests.
56
+ 4. Use `bundle exec guard` to automatically have tests run as you make changes.
57
+ 5. Make your changes.
58
+
59
+ To update swiftgen bin, use Test project in `spec`:
60
+ 1. `cd spec/Test`
61
+ 2. Open Podfile and update SwiftGen version
62
+ 3. `pod update SwiftGen`
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:specs)
8
+
9
+ task default: :specs
10
+
11
+ task :spec do
12
+ Rake::Task['specs'].invoke
13
+ Rake::Task['rubocop'].invoke
14
+ Rake::Task['spec_docs'].invoke
15
+ end
16
+
17
+ desc 'Run RuboCop on the lib/specs directory'
18
+ RuboCop::RakeTask.new(:rubocop) do |task|
19
+ task.patterns = ['lib/**/*.rb', 'spec/**/*.rb']
20
+ end
21
+
22
+ desc 'Ensure that the plugin passes `danger plugins lint`'
23
+ task :spec_docs do
24
+ sh 'bundle exec danger plugins lint'
25
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'localizationlinter/gem_version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'danger-localizationlinter'
9
+ spec.version = Localizationlinter::VERSION
10
+ spec.authors = ['Fabernovel']
11
+ spec.email = ['benjamin.lavialle@fabernovel.com', 'claire.peyron@fabernovel.com']
12
+ spec.description = 'This is a danger plugin checking the consistency of localizable folders in a xcode project'
13
+ spec.summary = 'Verify consistency over your project localizable files (localizable.strings and Localizable.stringdict).
14
+ It uses Swiftgen to generate a localizables output per target and language and compare the output with a reference language one.
15
+ The comparison ensure:
16
+ - missing keys
17
+ - additional keys
18
+ - wrong parameters in string format
19
+ '
20
+ spec.homepage = 'https://github.com/fabernovel/danger-localizationlinter'
21
+ spec.license = 'MIT'
22
+
23
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
24
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_runtime_dependency 'danger-plugin-api', '~> 1.0'
29
+
30
+ spec.add_dependency 'yaml', '~> 0.1'
31
+ spec.add_dependency 'json-diff', '~> 0.4'
32
+ spec.add_dependency 'xcodeproj', '~> 1.19'
33
+
34
+ # General ruby development
35
+ spec.add_development_dependency 'bundler', '~> 2.0'
36
+ spec.add_development_dependency 'rake', '~> 10.0'
37
+
38
+ # Testing support
39
+ spec.add_development_dependency 'rspec', '~> 3.4'
40
+
41
+ # Linting code and docs
42
+ spec.add_development_dependency 'rubocop'
43
+ spec.add_development_dependency 'yard'
44
+
45
+ # Makes testing easy via `bundle exec guard`
46
+ spec.add_development_dependency 'guard', '~> 2.14'
47
+ spec.add_development_dependency 'guard-rspec', '~> 4.7'
48
+
49
+ # If you want to work on older builds of ruby
50
+ spec.add_development_dependency 'listen', '3.0.7'
51
+
52
+ # This gives you the chance to run a REPL inside your tests
53
+ # via:
54
+ #
55
+ # require 'pry'
56
+ # binding.pry
57
+ #
58
+ # This will stop test execution and let you inspect the results
59
+ spec.add_development_dependency 'pry'
60
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../localizationlinter'
4
+
5
+ linter = LocalizationLinter.new(
6
+ ARGV[0],
7
+ ARGV[1],
8
+ ARGV[2],
9
+ ARGV[3],
10
+ ARGV[4],
11
+ ARGV[5],
12
+ ARGV[6]
13
+ )
14
+ message = linter.find_differences.map do |difference|
15
+ "#{difference[:filename]}:#{difference[:line]} #{difference[:message]}"
16
+ end
17
+ if message.empty?
18
+ puts 'Localization Linter ran with success'
19
+ else
20
+ puts "Localization Linter errors:\n#{message.join("\n")}"
21
+ end
@@ -0,0 +1,331 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json-diff'
4
+ require 'yaml'
5
+ require 'fileutils'
6
+ require 'xcodeproj'
7
+
8
+ class LocalizationLinter
9
+ def initialize(
10
+ verbose = nil,
11
+ clean_after_execution = nil,
12
+ language_configuration_path = nil,
13
+ swift_gen_path = nil,
14
+ derived_data_path = nil,
15
+ resource_path = nil,
16
+ project_path = nil
17
+ )
18
+ @verbose = !verbose.nil? || verbose == 'true' || verbose == true
19
+ @clean_after_execution = !clean_after_execution.nil? || clean_after_execution == 'true' || clean_after_execution == true
20
+ @language_configuration_path = language_configuration_path
21
+ @swift_gen_path = swift_gen_path
22
+ @derived_data_path = derived_data_path
23
+ @resource_path = resource_path
24
+ @project_path = project_path
25
+ end
26
+
27
+ def find_differences
28
+ clean_derived_data
29
+ create_derived_data_folder
30
+ log "Validating wording for targets: #{all_targets.join ', '}"
31
+ differences = all_targets.map { |target| check_missing_keys_in_target(target) }.compact.flatten
32
+ clean_derived_data if @clean_after_execution
33
+ differences
34
+ end
35
+
36
+ def log(text)
37
+ puts(text) if @verbose
38
+ end
39
+
40
+ def check_missing_keys_in_target(target)
41
+ log "Starting wording validation for #{target}"
42
+ main_region = main_region_for_target(target)
43
+ if main_region.nil?
44
+ return {
45
+ filename: 'General',
46
+ message: "Missing main region for target #{target}"
47
+ }
48
+ end
49
+
50
+ log "Found main region #{main_region} for #{target}"
51
+ unless known_regions.include? main_region
52
+ return {
53
+ filename: 'General',
54
+ message: "Missing main region #{main_region} in Project"
55
+ }
56
+ end
57
+ known_regions.each { |region| write_l10n_check_file(target, region) }
58
+ main_file_name = temporary_localizable_check_file_path(target, main_region)
59
+ log "Taking #{main_file_name} as reference for #{target}"
60
+ unless File.exist?(main_file_name)
61
+ return {
62
+ filename: 'General',
63
+ message: "Target #{target} doesn't localize for main region #{main_region}"
64
+ }
65
+ end
66
+ log "Found file for main region #{main_region} for #{target}"
67
+ main_file_hash = get_hash(main_file_name)
68
+ errors = known_regions.map do |region|
69
+ begin
70
+ check_missing_key(target, region, main_region, main_file_hash)
71
+ rescue CheckMissingKeyNotNeeded
72
+ next
73
+ end
74
+ end.compact
75
+ errors.flatten.compact unless errors.empty?
76
+ end
77
+
78
+ ## Tools
79
+
80
+ class CheckMissingKeyNotNeeded < StandardError
81
+ end
82
+
83
+ class IsMainRegion < CheckMissingKeyNotNeeded
84
+ end
85
+
86
+ class MissingFileForRegion < CheckMissingKeyNotNeeded
87
+ def initialize(msg="")
88
+ super
89
+ end
90
+ end
91
+
92
+ class RegionHashIdenticalAsMainOne < CheckMissingKeyNotNeeded
93
+ def initialize(msg="")
94
+ super
95
+ end
96
+ end
97
+
98
+ class RegionHasNoDiffError < CheckMissingKeyNotNeeded
99
+ def initialize(msg="")
100
+ super
101
+ end
102
+ end
103
+
104
+ def check_missing_key(target, region, main_region, main_file_hash)
105
+ raise IsMainRegion if region == main_region
106
+ message = "#{target}_#{region}"
107
+
108
+ file_name = temporary_localizable_check_file_path(target, region)
109
+ raise MissingFileForRegion, message unless File.exist?(file_name)
110
+ file_hash = get_hash(file_name)
111
+ raise RegionHashIdenticalAsMainOne, message unless file_hash != main_file_hash
112
+
113
+ log "Computing diff for #{region} with file: #{file_name}"
114
+ previous_diff = {}
115
+ previous_previous_diff = {}
116
+ diffs = JsonDiff.diff(file_hash, main_file_hash)
117
+ last_diff = diffs.last
118
+ current_errors = []
119
+ diffs.each do |diff|
120
+ current_errors << handle_previous_diff(diff, previous_diff, previous_previous_diff)
121
+ current_errors << handle_last_diff(diff, previous_diff) if diff == last_diff
122
+ previous_previous_diff = previous_diff
123
+ previous_diff = diff
124
+ end
125
+ if current_errors.empty?
126
+ log "Did not found any diff for #{region}"
127
+ else
128
+ log "Found #{current_errors.size} diffs:#{current_errors.join "\n"}"
129
+ end
130
+ raise RegionHasNoDiffError, message if current_errors.compact.empty?
131
+
132
+ current_errors.compact.map do |error|
133
+ parse_error_with_file_and_line(error, localizable_directory(target, region))
134
+ end
135
+ end
136
+
137
+ def parse_error_with_file_and_line(error, directory)
138
+ final_key = error[:key].gsub('/L10n/', '').split('/').first
139
+ line_vs_file = find_line(final_key, directory)
140
+ {
141
+ filename: line_vs_file[1],
142
+ key: final_key,
143
+ line: line_vs_file[0],
144
+ message: error[:message]
145
+ }
146
+ end
147
+
148
+ def find_line(key, directory)
149
+ result = [0, '']
150
+ Dir.foreach(directory) do |filename|
151
+ next if (filename == '.') || (filename == '..') || (result[0] != 0)
152
+
153
+ global = "#{directory}/#{filename}"
154
+ result = [0, global] unless result != [0, '']
155
+ File.foreach(global).with_index do |line, index|
156
+ next if result[0] != 0
157
+
158
+ result = [index + 1, global] if line.include? key
159
+ end
160
+ end
161
+ result
162
+ end
163
+
164
+ def write_l10n_check_file(target, language)
165
+ unless File.directory?(localizable_directory(target, language))
166
+ log "Target #{target} doesn't localize for region #{language}"
167
+ return
168
+ end
169
+ File.open(swiftgen_check_config_path, 'w') { |file| file.write(swiftgen_check_config(target, language).to_yaml) }
170
+ system("#{swift_gen_path} config run --config #{swiftgen_check_config_path}")
171
+ end
172
+
173
+ def get_hash(name)
174
+ trim = File.read(name).gsub(/[[:space:]]/, '').gsub(',}', '}')
175
+ JSON.parse(trim)
176
+ end
177
+
178
+ def main_region_for_target(target)
179
+ path = language_configuration_path
180
+ return "File #{path} does not exist." unless File.exist?(path)
181
+
182
+ languages = YAML.safe_load(File.read(path)) || {}
183
+ unless languages[target]
184
+ log "Missing main region for #{target} in file #{path}, put the same one as in Swiftgen Localizables build phase"
185
+ return nil
186
+ end
187
+
188
+ languages[target]
189
+ end
190
+
191
+ # Errors
192
+
193
+ def handle_previous_diff(diff, previous_diff, previous_previous_diff)
194
+ case previous_diff['op']
195
+ when 'remove'
196
+ useless_key(previous_diff) if previous_diff['path'] != diff['path']
197
+ when 'add'
198
+ if previous_diff['path'] != diff['path']
199
+ if (previous_previous_diff != {}) && (previous_previous_diff['path'] != previous_diff['path'])
200
+ missing_key(previous_diff)
201
+ else
202
+ bad_parameter(previous_diff)
203
+ end
204
+ else
205
+ bad_parameter(previous_diff)
206
+ end
207
+ end
208
+ end
209
+
210
+ def handle_last_diff(diff, previous_diff)
211
+ if previous_diff['path'] != diff['path']
212
+ if diff['op'] == 'remove'
213
+ useless_key(diff)
214
+ else
215
+ missing_key(diff)
216
+ end
217
+ else
218
+ bad_parameter(diff)
219
+ end
220
+ end
221
+
222
+ def useless_key(diff)
223
+ {
224
+ key: diff['path'],
225
+ message: "useless key: #{diff['path']}"
226
+ }
227
+ end
228
+
229
+ def missing_key(diff)
230
+ {
231
+ key: diff['path'],
232
+ message: "missing key: #{diff['path']}"
233
+ }
234
+ end
235
+
236
+ def bad_parameter(diff)
237
+ {
238
+ key: diff['path'],
239
+ message: "wrong parameters in key: #{diff['path']}/#{diff['value']}"
240
+ }
241
+ end
242
+
243
+ ## Paths
244
+
245
+ def localizable_directory(target, language)
246
+ "#{resource_path}/#{target}/#{language}.lproj"
247
+ end
248
+
249
+ def swiftgen_check_config_path
250
+ "#{derived_data_path}/swiftgen_check_config.yml"
251
+ end
252
+
253
+ def temporary_localizable_check_file_path(target, region)
254
+ "#{derived_data_path}/#{target}_#{region}"
255
+ end
256
+
257
+ def derived_data_path
258
+ @derived_data_path || 'localization_linter_derived_data'
259
+ end
260
+
261
+ def resource_path
262
+ @resource_path || 'Resources'
263
+ end
264
+
265
+ def swift_gen_path
266
+ @swift_gen_path || 'Pods/SwiftGen/bin/swiftgen'
267
+ end
268
+
269
+ def language_configuration_path
270
+ @language_configuration_path || 'Configuration/Defaults/all_target_main_language.yml'
271
+ end
272
+
273
+ def stencil_path
274
+ "#{File.expand_path(File.dirname(__FILE__))}/swiftgen_localizables_check.stencil"
275
+ end
276
+
277
+ def upper_path(path)
278
+ return "../#{path}" unless path.start_with?('/')
279
+ return path
280
+ end
281
+
282
+ ## Derived data
283
+
284
+ def create_derived_data_folder
285
+ Dir.mkdir derived_data_path
286
+ end
287
+
288
+ def clean_derived_data
289
+ FileUtils.rm_rf(derived_data_path)
290
+ end
291
+
292
+ ## config
293
+
294
+ def swiftgen_check_config(target, language)
295
+ {
296
+ 'strings' => {
297
+ 'inputs' => [
298
+ "#{upper_path(localizable_directory(target, language))}"
299
+ ],
300
+ 'outputs' => [{
301
+ 'templatePath' => stencil_path,
302
+ 'output' => "#{upper_path(temporary_localizable_check_file_path(target, language))}",
303
+ 'params' => {
304
+ 'publicAccess' => true
305
+ }
306
+ }]
307
+ }
308
+ }
309
+ end
310
+
311
+ ## project
312
+
313
+ def project_path
314
+ @project_path || Dir.glob('*.xcodeproj').first
315
+ end
316
+
317
+ def project
318
+ Xcodeproj::Project.open(project_path)
319
+ end
320
+
321
+ def all_targets
322
+ project
323
+ .targets
324
+ .filter { |t| t.product_type == 'com.apple.product-type.application' }
325
+ .map(&:name)
326
+ end
327
+
328
+ def known_regions
329
+ project.root_object.known_regions
330
+ end
331
+ end