csv2strings 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/netbe/CSV-to-iOS-Localizable.strings-converter.png?branch=master)](http://travis-ci.org/netbe/CSV-to-iOS-Localizable.strings-converter)
|
2
|
+
[![Code Climate](https://codeclimate.com/badge.png)](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
|