sync-strings 0.0.0

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 (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/sync-strings +67 -0
  3. data/lib/sync-strings.rb +130 -0
  4. metadata +60 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 173b46f90fc054e72d5c6232cf60e6c64cf8fa8f1f5b060ca6597eccc08268ca
4
+ data.tar.gz: 337b2fcd5fe8d8a0f75ef61c712d15dcb7d202455c87165ab0bdb9d9c0bb90ff
5
+ SHA512:
6
+ metadata.gz: 3e37f09832ecbc46081bba33532e880498b4afa6060327459954747c9fa27772406f36054df76916f31b3a8eb04e4c195b11f484f3b107e72ecae7a720293acd
7
+ data.tar.gz: d667b9ccb9b2ec15ba22990330d460a2f9bd000937b2deb4bec6302603d942d210672c021ea600669083610e94d0bfe4ab86f4b3566c19e565d23ead0c263039
data/bin/sync-strings ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sync-strings'
4
+ require 'optparse'
5
+
6
+ @options = {}
7
+
8
+ option_parser = OptionParser.new do |parser|
9
+ parser.banner = "Usage: sync-string [paths]"
10
+ parser.on("-c", "--copy-mismatched-keys-as-values", "Copy Mismatched keys as values") do
11
+ @options[:copy_mismatched_keys_as_values] = true
12
+ end
13
+ parser.on("-s", "--scan[=PATH]", "Recursively scan for localization files relative to the current working directory") do |path|
14
+ @options[:scan] = true
15
+ if !path.nil?
16
+ @options[:scan_path] = path
17
+ end
18
+ end
19
+ parser.on("--paths x,y,z", Array, "Recursively scan for localization files relative to the current working directory") do |path_list|
20
+ @options[:path_list] = path_list
21
+ end
22
+ parser.on("--ignore x,y,z", Array, "When scanning, ignore paths containing these strings e.g. --ignore \"Pods/\",\"Carthage/\",\".build/\"") do |ignore_list|
23
+ @options[:ignore_list] = ignore_list
24
+ end
25
+ parser.on("-h", "--help", "Print the help menu") do
26
+ puts parser
27
+ exit
28
+ end
29
+ end.parse!
30
+
31
+ if @options[:scan]
32
+ files = StringFileFinder.new.find(@options[:scan_path] || Dir.pwd, @options[:ignore_list])
33
+
34
+ files.each { |group|
35
+ if group.length == 1
36
+ puts "Warning. Found a localization file with no matching translations #{group[0]}"
37
+ end
38
+ }
39
+ elsif @options[:path_list]
40
+ files = [@options[:path_list]]
41
+ else
42
+ puts "No files to synchronize. Either pass files using --paths=x,y,z or use --scan."
43
+ exit
44
+ end
45
+
46
+ puts "Starting synchronization"
47
+ puts "----------------------"
48
+
49
+ for groupIndex in 0 ... files.length do
50
+ for i in 1 ... files[groupIndex].length do
51
+ synchronizer = Synchronizer.new(files[groupIndex][i - 1], files[groupIndex][i])
52
+
53
+ synchronizer.print_stats
54
+
55
+ if @options[:copy_mismatched_keys_as_values]
56
+ synchronizer.sync()
57
+ elsif synchronizer.key_differences?
58
+ raise "Missing translations for keys: #{synchronizer.key_differences}"
59
+ end
60
+
61
+ puts "Done. Writing to file."
62
+ synchronizer.write()
63
+ puts "----------------------"
64
+ end
65
+ end
66
+
67
+ puts "Done synchronizing #{files.reduce(0) { |acc, curr| acc += curr.length}} files."
@@ -0,0 +1,130 @@
1
+ require 'apfel'
2
+
3
+ class HashStringFileWriter
4
+ def write(hash, to_filename)
5
+ file_format = file_format(hash)
6
+ File.open(to_filename,"w") do |file|
7
+ file.puts file_format
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def file_format(hash)
14
+ hash
15
+ .filter { |key, value|
16
+ !key.nil? && !key.empty?
17
+ }
18
+ .map { |key, value|
19
+ "\"#{key}\" = \"#{value}\";"
20
+ }
21
+ .sort
22
+ end
23
+ end
24
+
25
+ class Synchronizer
26
+ def initialize(a_filename, b_filename)
27
+ @a_filename = a_filename
28
+ @b_filename = b_filename
29
+
30
+ @a_hash = Apfel.parse(@a_filename).to_hash(with_comments: false)
31
+ @b_hash = Apfel.parse(@b_filename).to_hash(with_comments: false)
32
+
33
+ @a_subtract_b = difference(@a_hash, @b_hash)
34
+ @b_subtract_a = difference(@b_hash, @a_hash)
35
+ end
36
+
37
+ def print_stats()
38
+ puts "File A: #{@a_filename}"
39
+ puts "File B: #{@b_filename}"
40
+ puts "A - B = #{@a_subtract_b.length}"
41
+ puts "B - A = #{@b_subtract_a.length}"
42
+ end
43
+
44
+ def key_differences()
45
+ @a_subtract_b + @b_subtract_a
46
+ end
47
+
48
+ def key_differences?()
49
+ !@a_subtract_b.empty? || !@b_subtract_a.empty?
50
+ end
51
+
52
+ def sync()
53
+ add(@b_subtract_a, @a_hash)
54
+ add(@a_subtract_b, @b_hash)
55
+ end
56
+
57
+ def write()
58
+ writer = HashStringFileWriter.new
59
+ writer.write(@a_hash, @a_filename)
60
+ writer.write(@b_hash, @b_filename)
61
+ end
62
+
63
+ private
64
+
65
+ def difference(hash, subtracting_hash)
66
+ keys = hash.keys
67
+ subtracting_keys = subtracting_hash.keys
68
+ return keys.difference(subtracting_keys)
69
+ end
70
+
71
+ def add(keys, into_hash)
72
+ keys.each { |key|
73
+ into_hash[key] = key
74
+ }
75
+ end
76
+ end
77
+
78
+ require 'find'
79
+
80
+ class StringFileFinder
81
+ def find(dir, ignore_list)
82
+ paths = []
83
+ ignore_list = ignore_list || []
84
+
85
+ Find.find(dir) do |path|
86
+ paths << path if path =~ /.*\.strings$/ && ignore_list.none? { |ignore| path.include? ignore }
87
+ end
88
+
89
+ def split_path(path)
90
+ path.split(File::SEPARATOR).reject(&:empty?)
91
+ end
92
+
93
+ paths_hashed_by_base_path = paths.reduce({}) { |acc, path|
94
+ base_path = split_path(path)[0...-2].join("/")
95
+
96
+ if acc.key?(base_path)
97
+ acc[base_path].push(path)
98
+ elsif
99
+ acc[base_path] = [path]
100
+ end
101
+
102
+ acc
103
+ }
104
+
105
+ paths_by_length_and_name = []
106
+ paths_hashed_by_base_path.each { |key, same_length_paths|
107
+ same_name = []
108
+ same_length_paths.each { |same_length_path|
109
+ i = same_name.index { |array|
110
+ array.any? { |v|
111
+ name = split_path(v).last
112
+ same_length_path_name = split_path(same_length_path).last
113
+ name == same_length_path_name
114
+ }
115
+ }
116
+ if i.nil?
117
+ same_name.push([same_length_path])
118
+ elsif
119
+ same_name[i].push(same_length_path)
120
+ end
121
+ }
122
+
123
+ same_name.each{ |entry|
124
+ paths_by_length_and_name.push(entry)
125
+ }
126
+ }
127
+
128
+ paths_by_length_and_name
129
+ end
130
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sync-strings
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nico Richard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-10-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: apfel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.5
27
+ description: Adds missing values between .strings files then sorts and filters the
28
+ result so that line numbers always match between your languages.
29
+ email: nicorichard@gmail.com
30
+ executables:
31
+ - sync-strings
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - bin/sync-strings
36
+ - lib/sync-strings.rb
37
+ homepage: https://rubygems.org/gems/sync-strings
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubygems_version: 3.1.6
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Synchronize Xcode .strings files
60
+ test_files: []