danger-localizationlinter 0.0.2

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