csv2strings 0.2.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.
- checksums.yaml +15 -0
- data/.csvconverter.sample +5 -0
- data/.gitignore +11 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +20 -0
- data/README.md +28 -0
- data/Rakefile +9 -0
- data/bin/csv2strings +11 -0
- data/bin/strings2csv +10 -0
- data/csv2strings.gemspec +22 -0
- data/lib/command.rb +66 -0
- data/lib/csv2strings/converter.rb +130 -0
- data/lib/csvconverter.rb +16 -0
- data/lib/strings2csv/converter.rb +95 -0
- data/test/command_test.rb +90 -0
- data/test/csv2strings/converter_test.rb +22 -0
- data/test/data/test_data.csv +3 -0
- data/test/data/test_data.strings +2 -0
- data/test/data/test_data_multiple_langs.csv +3 -0
- data/test/data/test_en.strings +2 -0
- data/test/data/test_fr.strings +2 -0
- data/test/strings2csv/converter_test.rb +96 -0
- data/test/test_helper.rb +12 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YTA4ZWFmNjA3MzBiZjQ0YzU0MmJhMjk5YmE4MjVhMTA3NTBlNzFhMg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MzFmNDRlOGY2ZWQwNzc1MTg1MWU4NGQ4MjBkZjBlYWIzYTY2YTNmYg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTEyY2M2NmVkN2NiMzcyMzAyOWJkNzg3ODM3MDEwZGU3ZGFlOTM4ZDc5ZjNl
|
10
|
+
OGI5YjBkY2U4MTA3ZTMwOTE1NDU5ODNhNjEzNTY5NTJjMTkxY2IwNTY0Njk5
|
11
|
+
NjVjOTdiNWIzZmMzODI5Yzg4OWMwM2VhYWRjMmI4OGIzMGNlNGM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTI4YjZjMjIzMWVhZDc2MDI3NDFmYzBlNjE3OWVmYWNmZThkODg0M2EzZWQx
|
14
|
+
MGExY2ExMTkwYjExOTkxMzViOTNhYzA1YjQwMjg2NDMzMTQwODUyNWIwZDhj
|
15
|
+
NWFlNGVlNmY4YWM5OTk5MDdlMWQwM2I5NTFmY2RlMjI5MmEzM2Y=
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
csv2strings (0.2)
|
5
|
+
thor
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
metaclass (0.0.1)
|
11
|
+
mocha (0.10.5)
|
12
|
+
metaclass (~> 0.0.1)
|
13
|
+
multi_json (1.3.6)
|
14
|
+
rake (0.9.2.2)
|
15
|
+
simplecov (0.6.4)
|
16
|
+
multi_json (~> 1.0)
|
17
|
+
simplecov-html (~> 0.5.3)
|
18
|
+
simplecov-html (0.5.3)
|
19
|
+
test-unit (2.4.8)
|
20
|
+
thor (0.17.0)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
csv2strings!
|
27
|
+
mocha
|
28
|
+
rake
|
29
|
+
simplecov
|
30
|
+
test-unit
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 François Benaiteau
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
[](http://travis-ci.org/netbe/CSV-to-iOS-Localizable.strings-converter)
|
2
|
+
[](https://codeclimate.com/github/netbe/CSV-to-iOS-Localizable.strings-converter)
|
3
|
+
# Introduction
|
4
|
+
This script converts a csv file of translations into iOS .strings files and vice-versa.
|
5
|
+
|
6
|
+
# Setup
|
7
|
+
|
8
|
+
`gem install csvconverter`
|
9
|
+
|
10
|
+
# Usage
|
11
|
+
|
12
|
+
* Convert csv to localizable strings files:
|
13
|
+
|
14
|
+
`csv2strings <csvfilename>`
|
15
|
+
|
16
|
+
* Convert localizable strings files to csv:
|
17
|
+
|
18
|
+
`strings2csv <stringsfile1> <stringsfile2>`
|
19
|
+
|
20
|
+
* Use configuration file
|
21
|
+
|
22
|
+
You can use a configuration file to hold all your commandline arguments into a file (previous versions `i18n_config.rb`).
|
23
|
+
Place `.csvconverter` file (edit if needed) in the folder with your ``xx.lproj`` and call the script from there. See `.csvconverter.sample`
|
24
|
+
|
25
|
+
|
26
|
+
# Todo
|
27
|
+
|
28
|
+
See GitHub isssues
|
data/Rakefile
ADDED
data/bin/csv2strings
ADDED
data/bin/strings2csv
ADDED
data/csv2strings.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'csv2strings'
|
3
|
+
s.version = '0.2.0'
|
4
|
+
s.date = '2013-09-02'
|
5
|
+
s.summary = "CSV to iOS Localizable.strings converter"
|
6
|
+
s.description = "ruby script converts a CSV file of translations to Localizable.strings files and vice-versa"
|
7
|
+
s.authors = ["François Benaiteau"]
|
8
|
+
s.email = 'francois.benaiteau@gmail.com'
|
9
|
+
s.homepage = 'https://github.com/netbe/CSV-to-iOS-Localizable.strings-converter'
|
10
|
+
s.license = 'MIT'
|
11
|
+
s.add_dependency "thor"
|
12
|
+
s.add_dependency "fastercsv", :require => 'faster_csv' if RUBY_PLATFORM == 'ruby_19'
|
13
|
+
s.add_dependency "csv", :require => 'csv' if RUBY_PLATFORM == 'ruby_18'
|
14
|
+
s.add_development_dependency "rake"
|
15
|
+
s.add_development_dependency "mocha"
|
16
|
+
s.add_development_dependency "test-unit"
|
17
|
+
s.add_development_dependency "simplecov"
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_path = ['lib']
|
22
|
+
end
|
data/lib/command.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__)))
|
2
|
+
require 'yaml'
|
3
|
+
require 'thor'
|
4
|
+
require 'csvconverter'
|
5
|
+
|
6
|
+
class Command < Thor
|
7
|
+
|
8
|
+
desc "CSV_FILENAME", "convert CSV file to '.strings' file"
|
9
|
+
# required options but handled in method because of options read from yaml file
|
10
|
+
method_option :filename, :type => :string, :desc => "CSV file (CSV_FILENAME) to convert from"
|
11
|
+
method_option :langs, :type => :hash, :aliases => "-L", :desc => "Languages to convert"
|
12
|
+
# optional options
|
13
|
+
method_option :excluded_states, :type => :array, :aliases => "-x", :desc => "Exclude rows with given state"
|
14
|
+
method_option :state_column, :type => :numeric, :aliases => "-s", :desc => "Position of column for state if any"
|
15
|
+
method_option :keys_column, :type => :numeric, :aliases => "-k", :desc => "Position of column for keys"
|
16
|
+
method_option :default_lang, :type => :string, :aliases => "-l", :desc => "Default language to use for empty values if any"
|
17
|
+
method_option :default_path, :type => :string, :aliases => "-p", :desc => "Path of output files"
|
18
|
+
def csv2strings(filename = nil)
|
19
|
+
unless filename || options.has_key?('filename')
|
20
|
+
puts "No value provided for required options '--filename'"
|
21
|
+
help("csv2strings")
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
unless options.has_key?('langs')
|
25
|
+
puts "No value provided for required options '--langs'"
|
26
|
+
help("csv2strings")
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
filename ||= options['filename']
|
30
|
+
args = options.dup
|
31
|
+
args.delete(:langs)
|
32
|
+
args.delete(:filename)
|
33
|
+
converter = CSV2Strings::Converter.new(filename, options[:langs], args)
|
34
|
+
converter.csv_to_dotstrings
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "FILENAMES", "convert '.strings' files to CSV file"
|
38
|
+
# required options but handled in method because of options read from yaml file
|
39
|
+
method_option :filenames, :type => :array, :aliases => "-i", :desc => "location of strings files (FILENAMES)"
|
40
|
+
# optional options
|
41
|
+
method_option :csv_filename, :type => :string, :aliases => "-o", :desc => "location of output file"
|
42
|
+
method_option :headers, :type => :array, :aliases => "-h", :desc => "override headers of columns, default is name of input files and 'Variables' for reference"
|
43
|
+
method_option :dryrun, :type => :boolean, :aliases => "-n", :desc => "prints out content of hash without writing file"
|
44
|
+
def strings2csv
|
45
|
+
unless options.has_key?('filenames')
|
46
|
+
puts "No value provided for required options '--filenames'"
|
47
|
+
help("strings2csv")
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
converter = Strings2CSV::Converter.new(options)
|
51
|
+
debug_values = converter.dotstrings_to_csv(!options[:dryrun])
|
52
|
+
puts debug_values.inspect if options[:dryrun]
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def options
|
57
|
+
original_options = super
|
58
|
+
return original_options unless File.exists?(".csvconverter")
|
59
|
+
defaults = ::YAML::load_file(".csvconverter") || {}
|
60
|
+
# add default values for options here
|
61
|
+
defaults["csv_filename"] = "translations.csv" unless defaults.has_key?("csv_filename")
|
62
|
+
defaults["dryrun"] = false unless defaults.has_key?("dryrun")
|
63
|
+
defaults["keys_column"] = 0 unless defaults.has_key?("keys_column")
|
64
|
+
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module CSV2Strings
|
2
|
+
class Converter
|
3
|
+
attr_accessor :csv_filename, :output_file
|
4
|
+
attr_accessor :langs, :default_lang
|
5
|
+
attr_accessor :default_path
|
6
|
+
attr_accessor :excluded_states, :state_column, :keys_column
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(filename, langs, args = {})
|
10
|
+
args.merge!({
|
11
|
+
:excluded_states => [],
|
12
|
+
:state_column => nil,
|
13
|
+
:keys_column => 0})
|
14
|
+
|
15
|
+
@csv_filename = filename
|
16
|
+
@langs = langs
|
17
|
+
|
18
|
+
if !@langs.is_a?(Hash) || @langs.size == 0
|
19
|
+
raise "wrong format or/and languages parameter"
|
20
|
+
end
|
21
|
+
@output_file = (@langs.size == 1) ? args[:output_file] : nil
|
22
|
+
|
23
|
+
@default_path = args[:default_path].to_s
|
24
|
+
@excluded_states = args[:excluded_states]
|
25
|
+
@state_column = args[:state_column]
|
26
|
+
@keys_column = args[:keys_column]
|
27
|
+
@default_lang = args[:default_lang]
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_file_from_path(file_path)
|
31
|
+
path = File.dirname(file_path)
|
32
|
+
FileUtils.mkdir_p path
|
33
|
+
return File.new(file_path,"w")
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_header(excludedCols, files, row, index)
|
37
|
+
files[index] = []
|
38
|
+
lang_index = row[index]
|
39
|
+
|
40
|
+
# create output files here
|
41
|
+
if @output_file
|
42
|
+
# one single file
|
43
|
+
files[index] << self.create_file_from_path(@output_file)
|
44
|
+
else
|
45
|
+
# create one file for each languages
|
46
|
+
if self.langs[lang_index].is_a?(Array)
|
47
|
+
|
48
|
+
self.langs[lang_index].each do |locale|
|
49
|
+
filename = self.file_path_for_locale(locale)
|
50
|
+
files[index] << self.create_file_from_path(filename)
|
51
|
+
end
|
52
|
+
elsif self.langs[lang_index].is_a?(String)
|
53
|
+
locale = self.langs[lang_index]
|
54
|
+
filename = self.file_path_for_locale(locale)
|
55
|
+
files[index] << self.create_file_from_path(filename)
|
56
|
+
else
|
57
|
+
raise "wrong format or/and languages parameter"
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def file_path_for_locale(locale)
|
64
|
+
require 'pathname'
|
65
|
+
Pathname.new(self.default_path) + "#{locale}.lproj" + "Localizable.strings"
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_value(row_value, default_value)
|
69
|
+
value = row_value.nil? ? default_value : row_value
|
70
|
+
value = "" if value.nil?
|
71
|
+
value.gsub!(/\\*\"/, "\\\"") #escape double quotes
|
72
|
+
value.gsub!(/\s*(\n|\\\s*n)\s*/, "\\n") #replace new lines with \n + strip
|
73
|
+
value.gsub!(/%\s+([a-zA-Z@])([^a-zA-Z@]|$)/, "%\\1\\2") #repair string formats ("% d points" etc)
|
74
|
+
value.gsub!(/([^0-9\s\(\{\[^])%/, "\\1 %")
|
75
|
+
value.strip!
|
76
|
+
return value
|
77
|
+
end
|
78
|
+
|
79
|
+
# Convert csv file to multiple Localizable.strings files for each column
|
80
|
+
def csv_to_dotstrings(name = self.csv_filename)
|
81
|
+
files = {}
|
82
|
+
rowIndex = 0
|
83
|
+
excludedCols = []
|
84
|
+
defaultCol = 0
|
85
|
+
nb_translations = 0
|
86
|
+
CSVParserClass.foreach(name, :quote_char => '"', :col_sep =>',', :row_sep => :auto) do |row|
|
87
|
+
|
88
|
+
if rowIndex == 0
|
89
|
+
return unless row.count > 1 #check there's at least two columns
|
90
|
+
else
|
91
|
+
next if row == nil or row[self.keys_column].nil? #skip empty lines (or sections)
|
92
|
+
end
|
93
|
+
|
94
|
+
row.size.times do |i|
|
95
|
+
next if excludedCols.include? i
|
96
|
+
if rowIndex == 0 #header
|
97
|
+
# ignore all headers not listed in langs to create files
|
98
|
+
(excludedCols << i and next) unless self.langs.has_key?(row[i])
|
99
|
+
self.process_header(excludedCols, files, row, i)
|
100
|
+
# define defaultCol
|
101
|
+
defaultCol = i if self.default_lang == row[i]
|
102
|
+
elsif !self.state_column || (row[self.state_column].nil? or row[self.state_column] == '' or !self.excluded_states.include? row[self.state_column])
|
103
|
+
# TODO: add option to strip the constant or referenced language
|
104
|
+
key = row[self.keys_column].strip
|
105
|
+
value = self.process_value(row[i], row[defaultCol])
|
106
|
+
# files for a given language, i.e could group english US with english UK.
|
107
|
+
localized_files = files[i]
|
108
|
+
if localized_files
|
109
|
+
localized_files.each do |file|
|
110
|
+
nb_translations += 1
|
111
|
+
file.write "\"#{key}\" = \"#{value}\";\n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
rowIndex += 1
|
117
|
+
end
|
118
|
+
puts "\n>>>Created #{files.size} files. Content: #{nb_translations} translations\n"
|
119
|
+
|
120
|
+
# closing I/O
|
121
|
+
files.each do |key,locale_files|
|
122
|
+
locale_files.each do |file|
|
123
|
+
file.close
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end # end of method
|
128
|
+
|
129
|
+
end # end of class
|
130
|
+
end
|
data/lib/csvconverter.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__)))
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
CSVGEM = RUBY_VERSION.match(/^[0-1]\.[0-8]\./) ? 'faster_csv' : 'csv'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require CSVGEM
|
8
|
+
rescue LoadError
|
9
|
+
puts "Failed to load #{CSVGEM} (ruby #{RUBY_VERSION})"
|
10
|
+
puts "gem install #{CSVGEM}"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
CSVParserClass = CSVGEM == 'csv' ? CSV : FasterCSV
|
15
|
+
require "csv2strings/converter"
|
16
|
+
require "strings2csv/converter"
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Strings2CSV
|
2
|
+
class Converter
|
3
|
+
|
4
|
+
attr_accessor :csv_filename, :headers, :filenames, :default_lang
|
5
|
+
|
6
|
+
def initialize(args = {:filenames => []})
|
7
|
+
if args[:filenames] && args[:headers]
|
8
|
+
raise ArgumentError.new("number of headers and files don't match, don't forget the constant column") unless args[:headers].size == (args[:filenames].size + 1)
|
9
|
+
end
|
10
|
+
|
11
|
+
@filenames = args[:filenames]
|
12
|
+
|
13
|
+
@csv_filename = args[:csv_filename] || "translations.csv"
|
14
|
+
@default_lang = args[:default_lang]
|
15
|
+
@headers = args[:headers] || self.default_headers
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_headers
|
19
|
+
headers = ['Variables']
|
20
|
+
@filenames.each do |fname|
|
21
|
+
headers << basename(fname)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Load all strings of a given file
|
27
|
+
def load_strings(strings_filename)
|
28
|
+
strings = {}
|
29
|
+
File.open(strings_filename, 'r') do |strings_file|
|
30
|
+
strings_file.read.each_line do |line|
|
31
|
+
strings.merge!(self.parse_dotstrings_line(line))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
strings
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_dotstrings_line(line)
|
38
|
+
line.strip!
|
39
|
+
if (line[0] != ?# and line[0] != ?=)
|
40
|
+
m = line.match(/^[^\"]*\"(.+)\"[^=]+=[^\"]*\"(.*)\";/)
|
41
|
+
return {m[1] => m[2]} unless m.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# Convert Localizable.strings files to one CSV file
|
47
|
+
# output:
|
48
|
+
def dotstrings_to_csv(write_to_file = true)
|
49
|
+
# Parse .strings files
|
50
|
+
strings = {}
|
51
|
+
keys = nil
|
52
|
+
lang_order = []
|
53
|
+
|
54
|
+
@filenames.each do |fname|
|
55
|
+
header = basename(fname)
|
56
|
+
strings[header] = load_strings(fname)
|
57
|
+
lang_order << header
|
58
|
+
keys ||= strings[header].keys
|
59
|
+
end
|
60
|
+
|
61
|
+
if(write_to_file)
|
62
|
+
# Create csv file
|
63
|
+
puts "Creating #{@csv_filename}"
|
64
|
+
create_csv_file(keys, lang_order, strings)
|
65
|
+
else
|
66
|
+
return keys, lang_order, strings
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def basename(file_path)
|
71
|
+
filename = File.basename(file_path)
|
72
|
+
return filename.split('.')[0].to_sym if file_path
|
73
|
+
end
|
74
|
+
|
75
|
+
# Create the resulting file
|
76
|
+
def create_csv_file(keys, lang_order, strings)
|
77
|
+
raise "csv_filename must not be nil" unless self.csv_filename
|
78
|
+
CSVParserClass.open(self.csv_filename, "wb") do |csv|
|
79
|
+
csv << @headers
|
80
|
+
keys.each do |key|
|
81
|
+
line = [key]
|
82
|
+
default_val = strings[self.default_lang][key] if strings[self.default_lang]
|
83
|
+
lang_order.each do |lang|
|
84
|
+
current_val = strings[lang][key]
|
85
|
+
line << ((lang != self.default_lang and current_val == default_val) ? '' : current_val)
|
86
|
+
end
|
87
|
+
csv << line
|
88
|
+
end
|
89
|
+
puts "Done"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'command'
|
4
|
+
class CommandTest < Test::Unit::TestCase
|
5
|
+
def test_csv2strings_with_multiple_2_languages
|
6
|
+
command = "./bin/csv2strings"
|
7
|
+
command += " test/data/test_data_multiple_langs.csv"
|
8
|
+
command += " --langs=English:en French:fr"
|
9
|
+
system(command)
|
10
|
+
|
11
|
+
assert File.exist?("./en.lproj/Localizable.strings")
|
12
|
+
assert File.exist?("./fr.lproj/Localizable.strings")
|
13
|
+
|
14
|
+
#clean up
|
15
|
+
system("rm -rf ./en.lproj/")
|
16
|
+
system("rm -rf ./fr.lproj/")
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_csv2strings_with_default_path
|
20
|
+
command = "./bin/csv2strings"
|
21
|
+
command += " test/data/test_data_multiple_langs.csv"
|
22
|
+
command += " --langs=English:en French:fr"
|
23
|
+
command += " --default_path=mynewlocation"
|
24
|
+
system(command)
|
25
|
+
|
26
|
+
# testing
|
27
|
+
assert File.exist?("./mynewlocation/en.lproj/Localizable.strings"), "can't find output file for English"
|
28
|
+
assert File.exist?("./mynewlocation/fr.lproj/Localizable.strings"), "can't find output file for French"
|
29
|
+
|
30
|
+
#clean up
|
31
|
+
system("rm -rf ./mynewlocation")
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_strings2csv
|
35
|
+
command = "./bin/strings2csv"
|
36
|
+
command += " --filenames test/data/test_data.strings"
|
37
|
+
system(command)
|
38
|
+
|
39
|
+
assert File.exist?("translations.csv")
|
40
|
+
|
41
|
+
#clean up
|
42
|
+
system("rm -f translations.csv")
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_strings2csv_with_dryrun_option
|
46
|
+
command = "./bin/strings2csv"
|
47
|
+
command += " --filenames test/data/test_data.strings"
|
48
|
+
command += " --dryrun"
|
49
|
+
system(command)
|
50
|
+
|
51
|
+
assert !File.exist?("translations.csv")
|
52
|
+
|
53
|
+
#clean up
|
54
|
+
system("rm -f translations.csv")
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_strings2csv_with_output_file
|
58
|
+
command = "./bin/strings2csv"
|
59
|
+
command += " -i=test/data/test_data.strings"
|
60
|
+
command += " -o=myfile.csv"
|
61
|
+
system(command)
|
62
|
+
|
63
|
+
assert File.exist?("myfile.csv")
|
64
|
+
|
65
|
+
#clean up
|
66
|
+
system("rm -f myfile.csv")
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_strings2csv_with_headers
|
70
|
+
command = "./bin/csv2strings strings2csv"
|
71
|
+
command += " -i=test/data/test_data.strings"
|
72
|
+
command += " -h=constants english"
|
73
|
+
system(command)
|
74
|
+
|
75
|
+
#TODO assertion or move test on at lib level
|
76
|
+
|
77
|
+
#clean up
|
78
|
+
system("rm -f translations.csv")
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_strings2csv_with_two_files
|
82
|
+
command = "./bin/csv2strings strings2csv"
|
83
|
+
command += " --filenames=test/data/test_en.strings test/data/test_fr.strings"
|
84
|
+
command += " --headers=Constants English French"
|
85
|
+
command += " -o=enfr.csv"
|
86
|
+
system(command)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../../../lib/csv2strings/converter', __FILE__)
|
2
|
+
require File.expand_path('../../test_helper', __FILE__)
|
3
|
+
|
4
|
+
class CSV2Strings::ConverterTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_converting_csv_to_dotstrings
|
7
|
+
csv_file = "test/data/test_data.csv"
|
8
|
+
converter = CSV2Strings::Converter.new(csv_file, 'English' => [:en])
|
9
|
+
converter.csv_to_dotstrings
|
10
|
+
assert File.exists?("en.lproj/Localizable.strings"), "the ouptut file does not exist"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_converting_csv_to_dotstrings_one_output_option
|
14
|
+
csv_file = "test/data/test_data.csv"
|
15
|
+
single_file = 'myApp.strings'
|
16
|
+
converter = CSV2Strings::Converter.new(csv_file,
|
17
|
+
{'English' => [:en]},
|
18
|
+
:output_file => single_file)
|
19
|
+
converter.csv_to_dotstrings
|
20
|
+
assert File.exists?(single_file), "the ouptut file does not exist"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.expand_path('../../../lib/strings2csv/converter', __FILE__)
|
2
|
+
require File.expand_path('../../test_helper', __FILE__)
|
3
|
+
|
4
|
+
class Strings2CSV::ConverterTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_parse_dotstrings_line_with_good_string
|
7
|
+
input = String.new(<<-EOS)
|
8
|
+
"MY_CONSTANT" = "This is ok";
|
9
|
+
EOS
|
10
|
+
expected_output = {"MY_CONSTANT"=>"This is ok"}
|
11
|
+
|
12
|
+
output = Strings2CSV::Converter.new.parse_dotstrings_line input
|
13
|
+
assert_equal output, expected_output
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_parse_dotstrings_line_with_single_quote
|
17
|
+
input = String.new(<<-EOS)
|
18
|
+
"MY_CONSTANT" = "This 'is' ok";
|
19
|
+
EOS
|
20
|
+
expected_output = {"MY_CONSTANT"=>"This 'is' ok"}
|
21
|
+
|
22
|
+
output = Strings2CSV::Converter.new.parse_dotstrings_line input
|
23
|
+
assert_equal output, expected_output
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_parse_dotstrings_line_with_double_quotes
|
27
|
+
input = String.new(<<-EOS)
|
28
|
+
"MY_CONSTANT" = "This "is" ok";
|
29
|
+
EOS
|
30
|
+
expected_output = {"MY_CONSTANT"=>"This \"is\" ok"}
|
31
|
+
|
32
|
+
output = Strings2CSV::Converter.new.parse_dotstrings_line input
|
33
|
+
assert_equal output, expected_output
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_parse_dotstrings_line_with_wrong_string
|
37
|
+
input = String.new(<<-EOS)
|
38
|
+
"MY_CONSTANT" = "wrong syntax"
|
39
|
+
EOS
|
40
|
+
|
41
|
+
output = Strings2CSV::Converter.new.parse_dotstrings_line input
|
42
|
+
assert_nil output, "output should be nil with wrong syntax"
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_load_strings_with_wrong_file
|
46
|
+
assert_raise(Errno::ENOENT) do
|
47
|
+
output = Strings2CSV::Converter.new.load_strings "file that does not exist.strings"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_load_strings
|
52
|
+
expected_output = {"ERROR_HANDLER_WARNING_DISMISS" => "OK", "ANOTHER_STRING" => "hello"}
|
53
|
+
output = Strings2CSV::Converter.new.load_strings "test/data/test_data.strings"
|
54
|
+
assert_equal expected_output, output
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_dotstrings_to_csv
|
58
|
+
converter = Strings2CSV::Converter.new(:filenames => ["test/data/test_data.strings"])
|
59
|
+
keys, lang_order, strings = converter.dotstrings_to_csv(false)
|
60
|
+
assert_equal ["test_data".to_sym], lang_order
|
61
|
+
assert_equal ["ERROR_HANDLER_WARNING_DISMISS", "ANOTHER_STRING"], keys
|
62
|
+
expected_strings = {lang_order[0] => {"ERROR_HANDLER_WARNING_DISMISS" => "OK", "ANOTHER_STRING" => "hello"}}
|
63
|
+
assert_equal expected_strings, strings
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_create_csv_file
|
67
|
+
keys = ["ERROR_HANDLER_WARNING_DISMISS", "ANOTHER_STRING"]
|
68
|
+
lang_order = [:"test_data"]
|
69
|
+
strings = {lang_order[0] => {"ERROR_HANDLER_WARNING_DISMISS" => "OK", "ANOTHER_STRING" => "hello"}}
|
70
|
+
|
71
|
+
converter = Strings2CSV::Converter.new(:headers => %w{variables english})
|
72
|
+
|
73
|
+
converter.create_csv_file(keys, lang_order, strings)
|
74
|
+
assert File.exist?(converter.csv_filename)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_initialize
|
78
|
+
csv_filename = "file.csv"
|
79
|
+
filenames = %w{"french.strings english.strings"}
|
80
|
+
headers = %w{"constants french english"}
|
81
|
+
converter = Strings2CSV::Converter.new({
|
82
|
+
:csv_filename => csv_filename,
|
83
|
+
:headers => headers,
|
84
|
+
:filenames => filenames
|
85
|
+
})
|
86
|
+
|
87
|
+
assert_equal csv_filename, converter.csv_filename
|
88
|
+
assert_equal headers, converter.headers
|
89
|
+
assert_equal filenames, converter.filenames
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_initialize_with_default_values
|
93
|
+
converter = Strings2CSV::Converter.new
|
94
|
+
assert_not_nil converter.csv_filename
|
95
|
+
end
|
96
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: csv2strings
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- François Benaiteau
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mocha
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: test-unit
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: ruby script converts a CSV file of translations to Localizable.strings
|
84
|
+
files and vice-versa
|
85
|
+
email: francois.benaiteau@gmail.com
|
86
|
+
executables:
|
87
|
+
- csv2strings
|
88
|
+
- strings2csv
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- .csvconverter.sample
|
93
|
+
- .gitignore
|
94
|
+
- .travis.yml
|
95
|
+
- Gemfile
|
96
|
+
- Gemfile.lock
|
97
|
+
- LICENSE.txt
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
100
|
+
- bin/csv2strings
|
101
|
+
- bin/strings2csv
|
102
|
+
- csv2strings.gemspec
|
103
|
+
- lib/command.rb
|
104
|
+
- lib/csv2strings/converter.rb
|
105
|
+
- lib/csvconverter.rb
|
106
|
+
- lib/strings2csv/converter.rb
|
107
|
+
- test/command_test.rb
|
108
|
+
- test/csv2strings/converter_test.rb
|
109
|
+
- test/data/test_data.csv
|
110
|
+
- test/data/test_data.strings
|
111
|
+
- test/data/test_data_multiple_langs.csv
|
112
|
+
- test/data/test_en.strings
|
113
|
+
- test/data/test_fr.strings
|
114
|
+
- test/strings2csv/converter_test.rb
|
115
|
+
- test/test_helper.rb
|
116
|
+
homepage: https://github.com/netbe/CSV-to-iOS-Localizable.strings-converter
|
117
|
+
licenses:
|
118
|
+
- MIT
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- - lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 2.0.0
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: CSV to iOS Localizable.strings converter
|
140
|
+
test_files:
|
141
|
+
- test/command_test.rb
|
142
|
+
- test/csv2strings/converter_test.rb
|
143
|
+
- test/data/test_data.csv
|
144
|
+
- test/data/test_data.strings
|
145
|
+
- test/data/test_data_multiple_langs.csv
|
146
|
+
- test/data/test_en.strings
|
147
|
+
- test/data/test_fr.strings
|
148
|
+
- test/strings2csv/converter_test.rb
|
149
|
+
- test/test_helper.rb
|