-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlangFilesTocsv.rb
More file actions
173 lines (132 loc) · 3.45 KB
/
langFilesTocsv.rb
File metadata and controls
173 lines (132 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/usr/bin/env ruby
# encoding: UTF-8
require 'fileutils'
require 'csv'
BASE_DIR = "CurrentLocalization"
OUTPUT_FOLDER = "Generated"
OUTPUT_FILE = OUTPUT_FOLDER + "/" + "localization.csv"
USAGE = <<END
Convert Localizable.strings files to translations.csv
Usage: put related *.lproj folders with Localizable.strings files to 'CurrentLocalization' folder
perform 'ruby langFiles2csv.rb'
check results in "Generated" folder
END
# ----------------------------
# Parse .strings file (fully robust)
# ----------------------------
def parse_strings_file(path)
data = {}
current_section = nil
buffer = ""
inside_string = false
File.readlines(path, encoding: "UTF-8").each do |line|
line.strip!
# Section start
if line.start_with?("//---")
current_section = line.gsub("//---", "").strip
data["##{current_section}"] = nil
next
end
# Ignore comments and empty lines
next if line.start_with?("//")
next if line.empty?
if inside_string
buffer += "\n" + line
# Check if this line ends the string
if buffer =~ /";\s*$/
inside_string = false
else
next
end
else
buffer = line
inside_string = !(buffer =~ /";\s*$/) # continue accumulating if multiline
next if inside_string
end
# Now buffer contains full key=value pair
if buffer =~ /^"((?:\\.|[^"\\])*)"\s*=\s*"((?:\\.|[^"\\])*)";\s*$/
key = Regexp.last_match(1)
val = Regexp.last_match(2)
# Unescape sequences
key = key.gsub(/\\n/, "\n").gsub(/\\t/, "\t").gsub(/\\"/, '"')
val = val.gsub(/\\n/, "\n").gsub(/\\t/, "\t").gsub(/\\"/, '"')
data[key] = val
else
puts "Warning: Could not parse key/value: #{buffer.strip}"
end
buffer = "" # reset buffer
end
data
end
# ----------------------------
# Find languages
# ----------------------------
def find_languages
langs = []
Dir.entries(BASE_DIR).each do |dir|
if dir.end_with?(".lproj")
langs << dir.gsub(".lproj", "")
end
end
langs.sort
end
# ----------------------------
# Main
# ----------------------------
languages = find_languages
if languages.empty?
abort "No .lproj folders found in #{BASE_DIR}"
end
puts "Found languages: #{languages.join(', ')}"
translations = {}
all_keys = []
# ----------------------------
# Read all .strings files
# ----------------------------
languages.each do |lang|
file = "#{BASE_DIR}/#{lang}.lproj/Localizable.strings"
unless File.exist?(file)
puts "Skipping missing: #{file}"
next
end
puts "Reading: #{file}"
parsed = parse_strings_file(file)
parsed.each do |key, val|
# Section header
if key.start_with?("#")
all_keys << key unless all_keys.include?(key)
next
end
all_keys << key unless all_keys.include?(key)
translations[key] ||= {}
translations[key][lang] = val
end
end
# ----------------------------
# Write CSV
# ----------------------------
FileUtils.mkdir_p(OUTPUT_FOLDER) # creates 'Generated' folder if it doesn't exist
CSV.open(OUTPUT_FILE, "w", encoding: "UTF-8") do |csv|
# Header
csv << ["KEY"] + languages
all_keys.each do |key|
# Section row
if key.start_with?("#")
csv << [key] # section headers remain unquoted
next
end
row = []
row << key # keys remain unquoted
languages.each do |lang|
value = translations.dig(key, lang)
# Only quote non-empty values, leave empty cells blank
if value.nil? || value.empty?
row << ""
else
row << '"' + value + '"'
end
end
csv << row
end
end
puts "CSV file created: #{OUTPUT_FILE}"